]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #20303 from andir/sysconfig-example
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 22 Aug 2021 06:40:39 +0000 (15:40 +0900)
committerGitHub <noreply@github.com>
Sun, 22 Aug 2021 06:40:39 +0000 (15:40 +0900)
{core, login}: respect install_sysconfdir_samples in meson file

636 files changed:
.github/workflows/mkosi.yml
.github/workflows/test_mkosi_boot.py
.lgtm.yml
.mkosi/mkosi.opensuse
README
TODO
catalog/systemd.catalog.in
docs/ENVIRONMENT.md
docs/HACKING.md
docs/PORTABLE_SERVICES.md
hwdb.d/60-autosuspend-fingerprint-reader.hwdb
hwdb.d/60-evdev.hwdb
hwdb.d/60-sensor.hwdb
hwdb.d/80-ieee1394-unit-function.hwdb
hwdb.d/meson.build
man/bootctl.xml
man/coredump.conf.xml
man/coredumpctl.xml
man/homectl.xml
man/hwdb-usb-device.c
man/loader.conf.xml
man/logind.conf.xml
man/machinectl.xml
man/meson.build
man/nss-myhostname.xml
man/nss-resolve.xml
man/nss-systemd.xml
man/org.freedesktop.systemd1.xml
man/os-release.xml
man/pam_systemd.xml
man/portablectl.xml
man/repart.d.xml
man/rules/meson.build
man/sd-id128.xml
man/sd_id128_randomize.xml
man/sd_id128_to_string.xml
man/systemctl.xml
man/systemd-analyze.xml
man/systemd-boot.xml
man/systemd-cryptenroll.xml
man/systemd-firstboot.xml
man/systemd-machine-id-setup.xml
man/systemd-nspawn.xml
man/systemd-portabled.service.xml
man/systemd-resolved.service.xml
man/systemd-run.xml
man/systemd-sysext.xml
man/systemd-system.conf.xml
man/systemd-sysusers.xml
man/systemd-timesyncd.service.xml
man/systemd-userdbd.service.xml
man/systemd-veritysetup@.service.xml
man/systemd.exec.xml
man/systemd.link.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.nspawn.xml
man/systemd.preset.xml
man/systemd.resource-control.xml
man/systemd.service.xml
man/systemd.special.xml
man/systemd.unit.xml
man/systemd.xml
man/timesyncd.conf.xml
man/tmpfiles.d.xml
man/udevadm.xml
man/userdbctl.xml
meson.build
meson_options.txt
network/meson.build
po/pt_BR.po
po/si.po
po/zh_TW.po
presets/90-systemd.preset
shell-completion/bash/meson.build
shell-completion/bash/systemd-analyze
shell-completion/bash/udevadm
shell-completion/zsh/_systemd-analyze
shell-completion/zsh/_udevadm
shell-completion/zsh/meson.build
src/ac-power/ac-power.c
src/activate/activate.c
src/analyze/analyze-condition.c
src/analyze/analyze-security.c
src/analyze/analyze-security.h
src/analyze/analyze-verify.c
src/analyze/analyze-verify.h
src/analyze/analyze.c
src/analyze/test-verify.c
src/ask-password/ask-password.c
src/backlight/backlight.c
src/basic/alloc-util.h
src/basic/architecture.c
src/basic/cgroup-util.c
src/basic/cgroup-util.h
src/basic/env-util.c
src/basic/env-util.h
src/basic/fd-util.c
src/basic/fd-util.h
src/basic/fileio.c
src/basic/fileio.h
src/basic/format-util.c
src/basic/fs-util.c
src/basic/hashmap.c
src/basic/hostname-util.c
src/basic/hostname-util.h
src/basic/in-addr-util.c
src/basic/linux/batman_adv.h
src/basic/linux/btrfs.h
src/basic/linux/btrfs_tree.h
src/basic/linux/can/netlink.h
src/basic/linux/if_bonding.h
src/basic/linux/if_bridge.h
src/basic/linux/if_ether.h
src/basic/linux/if_link.h
src/basic/linux/in.h
src/basic/linux/l2tp.h
src/basic/linux/netfilter/nf_tables.h
src/basic/linux/netfilter/nfnetlink.h
src/basic/linux/netlink.h
src/basic/linux/nexthop.h
src/basic/linux/nl80211.h
src/basic/linux/pkt_sched.h
src/basic/linux/rtnetlink.h
src/basic/list.h
src/basic/locale-util.c
src/basic/locale-util.h
src/basic/log.c
src/basic/log.h
src/basic/macro.h
src/basic/memory-util.h
src/basic/namespace-util.c
src/basic/os-util.c
src/basic/parse-util.c
src/basic/path-lookup.c
src/basic/path-util.c
src/basic/path-util.h
src/basic/process-util.c
src/basic/process-util.h
src/basic/set.h
src/basic/signal-util.c
src/basic/socket-util.h
src/basic/special.h
src/basic/stdio-util.h
src/basic/string-util.h
src/basic/strv.h
src/basic/time-util.c
src/basic/tmpfile-util.c
src/basic/xattr-util.c
src/basic/xattr-util.h
src/binfmt/binfmt.c
src/boot/bless-boot.c
src/boot/boot-check-no-failures.c
src/boot/bootctl.c
src/boot/efi/assert.c [new file with mode: 0644]
src/boot/efi/boot.c
src/boot/efi/console.c
src/boot/efi/console.h
src/boot/efi/crc32.c [deleted file]
src/boot/efi/crc32.h [deleted file]
src/boot/efi/disk.c
src/boot/efi/linux.c
src/boot/efi/linux.h
src/boot/efi/measure.c
src/boot/efi/meson.build
src/boot/efi/missing_efi.h
src/boot/efi/pe.c
src/boot/efi/pe.h
src/boot/efi/random-seed.c
src/boot/efi/sha256.c
src/boot/efi/shim.c
src/boot/efi/splash.c
src/boot/efi/stub.c
src/boot/efi/util.c
src/boot/efi/util.h
src/busctl/busctl-introspect.c
src/busctl/busctl.c
src/cgls/cgls.c
src/cgtop/cgtop.c
src/core/bpf-devices.c
src/core/bpf/restrict_ifaces/meson.build [new file with mode: 0644]
src/core/bpf/restrict_ifaces/restrict-ifaces.bpf.c [new file with mode: 0644]
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-cgroup.c
src/core/dbus-execute.c
src/core/dbus-socket.c
src/core/dbus-unit.c
src/core/device.c
src/core/dynamic-user.c
src/core/emergency-action.c
src/core/execute.c
src/core/job.c
src/core/load-fragment-gperf.gperf.in
src/core/load-fragment.c
src/core/load-fragment.h
src/core/locale-setup.c
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/meson.build
src/core/mount.c
src/core/namespace.c
src/core/restrict-ifaces.c [new file with mode: 0644]
src/core/restrict-ifaces.h [new file with mode: 0644]
src/core/scope.c
src/core/selinux-access.c
src/core/service.c
src/core/socket.c
src/core/swap.c
src/core/timer.c
src/core/unit-serialize.c
src/core/unit.c
src/core/unit.h
src/coredump/coredumpctl.c
src/coredump/stacktrace.c
src/creds/creds.c
src/cryptenroll/cryptenroll-password.c
src/cryptenroll/cryptenroll-tpm2.c
src/cryptenroll/cryptenroll-wipe.c
src/cryptenroll/cryptenroll.c
src/cryptsetup/cryptsetup-fido2.c
src/cryptsetup/cryptsetup-pkcs11.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c [new file with mode: 0644]
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c [new file with mode: 0644]
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-util.h
src/cryptsetup/cryptsetup-tokens/cryptsetup-token.h
src/cryptsetup/cryptsetup-tokens/cryptsetup-token.sym
src/cryptsetup/cryptsetup-tokens/luks2-fido2.c [new file with mode: 0644]
src/cryptsetup/cryptsetup-tokens/luks2-fido2.h [new file with mode: 0644]
src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c [new file with mode: 0644]
src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.h [new file with mode: 0644]
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
src/cryptsetup/cryptsetup-tokens/meson.build
src/cryptsetup/cryptsetup-tpm2.c
src/cryptsetup/cryptsetup-tpm2.h
src/cryptsetup/cryptsetup.c
src/delta/delta.c
src/detect-virt/detect-virt.c
src/dissect/dissect.c
src/escape/escape.c
src/firstboot/firstboot.c
src/fstab-generator/fstab-generator.c
src/fundamental/macro-fundamental.h
src/fundamental/meson.build
src/fundamental/string-util-fundamental.h
src/home/homectl.c
src/home/homed-home.c
src/home/homed-manager.c
src/home/homework-directory.c
src/home/homework-luks.c
src/home/homework.c
src/home/user-record-util.c
src/hostname/hostnamectl.c
src/hostname/hostnamed.c
src/hwdb/hwdb.c
src/id128/id128.c
src/import/curl-util.c
src/import/export-raw.c
src/import/export.c
src/import/import-common.c
src/import/import-common.h
src/import/import-compress.c
src/import/import-fs.c
src/import/import-raw.c
src/import/import-raw.h
src/import/import-tar.c
src/import/import.c
src/import/importd.c
src/import/pull-common.c
src/import/pull-common.h
src/import/pull-job.c
src/import/pull-job.h
src/import/pull-raw.c
src/import/pull-raw.h
src/import/pull-tar.c
src/import/pull-tar.h
src/import/pull.c
src/journal-remote/journal-gatewayd.c
src/journal-remote/journal-remote-main.c
src/journal-remote/journal-remote.c
src/journal-remote/journal-upload-journal.c
src/journal-remote/journal-upload.c
src/journal-remote/meson.build
src/journal/cat.c
src/journal/journalctl.c
src/journal/journald-server.c
src/journal/journald-stream.c
src/kernel-install/meson.build
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/fuzz-dhcp-server.c
src/libsystemd-network/icmp6-util.c
src/libsystemd-network/sd-dhcp-client.c
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/test-acd.c
src/libsystemd-network/test-dhcp-client.c
src/libsystemd-network/test-dhcp6-client.c
src/libsystemd-network/test-ipv4ll-manual.c
src/libsystemd/meson.build
src/libsystemd/sd-bus/bus-container.c
src/libsystemd/sd-bus/bus-dump.c
src/libsystemd/sd-bus/bus-gvariant.c
src/libsystemd/sd-bus/bus-match.c
src/libsystemd/sd-bus/bus-message.c
src/libsystemd/sd-bus/bus-slot.c
src/libsystemd/sd-bus/bus-socket.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-bus/test-bus-benchmark.c
src/libsystemd/sd-bus/test-bus-signature.c
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-event/test-event.c
src/libsystemd/sd-id128/id128-util.h
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-journal/test-compress-benchmark.c
src/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-slot.c
src/libsystemd/sd-netlink/rtnl-message.c
src/libsystemd/sd-network/sd-network.c
src/libsystemd/sd-resolve/sd-resolve.c
src/libsystemd/sd-resolve/test-resolve.c
src/libudev/meson.build
src/libudev/test-libudev.c
src/locale/keymap-util.c
src/locale/localectl.c
src/locale/meson.build
src/login/inhibit.c
src/login/loginctl.c
src/login/logind-action.c
src/login/logind-action.h
src/login/logind-button.c
src/login/logind-dbus.c
src/login/logind-session-device.c
src/login/pam_systemd.c
src/login/user-runtime-dir.c
src/machine-id-setup/machine-id-setup-main.c
src/machine/machinectl.c
src/machine/machined-varlink.c
src/modules-load/modules-load.c
src/mount/mount-tool.c
src/network/generator/main.c
src/network/netdev/bareudp.c
src/network/netdev/fou-tunnel.c
src/network/netdev/geneve.c
src/network/netdev/l2tp-tunnel.c
src/network/netdev/macsec.c
src/network/netdev/macvlan.c
src/network/netdev/netdev.c
src/network/netdev/netdev.h
src/network/netdev/tunnel.c
src/network/netdev/vxlan.c
src/network/networkctl.c
src/network/networkd-address.c
src/network/networkd-bridge-mdb.c
src/network/networkd-can.c
src/network/networkd-can.h
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-server-bus.c
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp-server.h
src/network/networkd-dhcp4.c
src/network/networkd-dhcp4.h
src/network/networkd-dhcp6.c
src/network/networkd-dhcp6.h
src/network/networkd-ipv4acd.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-lldp-rx.c
src/network/networkd-lldp-rx.h
src/network/networkd-lldp-tx.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-ndisc.h
src/network/networkd-neighbor.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-nexthop.c
src/network/networkd-queue.c
src/network/networkd-queue.h
src/network/networkd-radv.c
src/network/networkd-radv.h
src/network/networkd-route.c
src/network/networkd-route.h
src/network/networkd-routing-policy-rule.c
src/network/networkd-routing-policy-rule.h
src/network/networkd-setlink.c
src/network/networkd-sriov.c
src/network/tc/codel.c
src/network/tc/ets.c
src/network/tc/fifo.c
src/network/tc/fq-codel.c
src/network/tc/fq.c
src/network/tc/gred.c
src/network/tc/htb.c
src/network/tc/tbf.c
src/network/tc/tc.c
src/network/wait-online/wait-online.c
src/notify/notify.c
src/nspawn/nspawn-mount.c
src/nspawn/nspawn-patch-uid.c
src/nspawn/nspawn-stub-pid1.c
src/nspawn/nspawn.c
src/oom/oomctl.c
src/oom/oomd.c
src/partition/growfs.c
src/partition/repart.c
src/path/path.c
src/portable/meson.build
src/portable/portable.c
src/portable/portablectl.c
src/resolve/resolvconf-compat.c
src/resolve/resolvectl.c
src/resolve/resolved-bus.c
src/resolve/resolved-conf.c
src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-search-domain.c
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-stub.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-varlink.c
src/run/run.c
src/shared/acl-util.c
src/shared/btrfs-util.c
src/shared/btrfs-util.h
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/chown-recursive.c
src/shared/clock-util.c
src/shared/clock-util.h
src/shared/condition.c
src/shared/copy.c
src/shared/copy.h
src/shared/creds-util.c
src/shared/discover-image.c
src/shared/dissect-image.c
src/shared/ethtool-util.c
src/shared/ethtool-util.h
src/shared/format-table.c
src/shared/group-record.c
src/shared/hostname-setup.c
src/shared/hostname-setup.h
src/shared/import-util.c
src/shared/import-util.h
src/shared/install-file.c [new file with mode: 0644]
src/shared/install-file.h [new file with mode: 0644]
src/shared/install.c
src/shared/journal-importer.c
src/shared/json.c
src/shared/local-addresses.c
src/shared/logs-show.c
src/shared/meson.build
src/shared/mkfs-util.c
src/shared/mount-util.c
src/shared/nscd-flush.c
src/shared/pkcs11-util.c
src/shared/pkcs11-util.h
src/shared/rm-rf.c
src/shared/rm-rf.h
src/shared/seccomp-util.c
src/shared/selinux-util.c
src/shared/service-util.c
src/shared/smack-util.c
src/shared/socket-netlink.c
src/shared/tests.c
src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/shared/user-record.c
src/shared/userdb.c
src/shared/utmp-wtmp.c
src/shared/varlink.c
src/shared/web-util.c
src/shared/web-util.h
src/shared/xml.c
src/shutdown/shutdown.c
src/sleep/sleep.c
src/socket-proxy/socket-proxyd.c
src/sysctl/sysctl.c
src/sysext/sysext.c
src/systemctl/fuzz-systemctl-parse-argv.c
src/systemctl/systemctl-add-dependency.c
src/systemctl/systemctl-clean-or-freeze.c
src/systemctl/systemctl-compat-halt.c
src/systemctl/systemctl-compat-runlevel.c
src/systemctl/systemctl-compat-shutdown.c
src/systemctl/systemctl-compat-telinit.c
src/systemctl/systemctl-daemon-reload.c
src/systemctl/systemctl-enable.c
src/systemctl/systemctl-set-property.c
src/systemctl/systemctl-show.c
src/systemctl/systemctl-util.c
src/systemctl/systemctl.c
src/systemd/meson.build
src/systemd/sd-bus-vtable.h
src/systemd/sd-id128.h
src/systemd/sd-messages.h
src/sysusers/sysusers.c
src/test/meson.build
src/test/nss-test-util.c
src/test/test-bitmap.c
src/test/test-bpf-firewall.c
src/test/test-bpf-foreign-programs.c
src/test/test-cgroup-mask.c
src/test/test-cgroup-unit-default.c
src/test/test-chase-symlinks.c
src/test/test-condition.c
src/test/test-engine.c
src/test/test-env-util.c
src/test/test-execute.c
src/test/test-fd-util.c
src/test/test-format-util.c
src/test/test-import-util.c [new file with mode: 0644]
src/test/test-install-file.c [new file with mode: 0644]
src/test/test-install-root.c
src/test/test-load-fragment.c
src/test/test-locale-util.c
src/test/test-log.c
src/test/test-macro.c [new file with mode: 0644]
src/test/test-namespace.c
src/test/test-nss-hosts.c
src/test/test-path-util.c
src/test/test-path.c
src/test/test-proc-cmdline.c
src/test/test-sched-prio.c
src/test/test-set.c
src/test/test-sizeof.c
src/test/test-socket-bind.c
src/test/test-strv.c
src/test/test-util.c
src/test/test-watch-pid.c
src/test/test-xattr-util.c
src/timedate/timedatectl.c
src/timesync/timesyncd-gperf.gperf
src/timesync/timesyncd-manager.c
src/timesync/timesyncd-manager.h
src/timesync/timesyncd-server.c
src/timesync/timesyncd.c
src/timesync/timesyncd.conf.in
src/tmpfiles/test-offline-passwd.c
src/tmpfiles/tmpfiles.c
src/tty-ask-password-agent/tty-ask-password-agent.c
src/udev/cdrom_id/cdrom_id.c
src/udev/dmi_memory_id/dmi_memory_id.c
src/udev/meson.build
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
src/udev/scsi_id/scsi_id.c
src/udev/test-udev-event.c
src/udev/udev-builtin-blkid.c
src/udev/udev-builtin-hwdb.c
src/udev/udev-builtin-input_id.c
src/udev/udev-builtin-kmod.c
src/udev/udev-builtin-net_id.c
src/udev/udev-builtin-path_id.c
src/udev/udev-builtin-usb_id.c
src/udev/udev-ctrl.c
src/udev/udev-ctrl.h
src/udev/udev-event.c
src/udev/udev-rules.c
src/udev/udevadm-control.c
src/udev/udevadm-hwdb.c
src/udev/udevadm-info.c
src/udev/udevadm-monitor.c
src/udev/udevadm-settle.c
src/udev/udevadm-test-builtin.c
src/udev/udevadm-test.c
src/udev/udevadm-trigger.c
src/udev/udevadm-util.c
src/udev/udevadm-util.h
src/udev/udevadm.c
src/udev/udevd.c
src/udev/v4l_id/v4l_id.c
src/userdb/userdbctl.c
src/userdb/userwork.c
src/veritysetup/veritysetup-generator.c
src/veritysetup/veritysetup.c
src/xdg-autostart-generator/xdg-autostart-service.c
sysctl.d/meson.build
test/TEST-13-NSPAWN-SMOKE/test.sh
test/TEST-62-RESTRICT-IFACES/Makefile [new symlink]
test/TEST-62-RESTRICT-IFACES/test.sh [new file with mode: 0755]
test/create-busybox-container
test/fuzz/fuzz-link-parser/directives.link
test/fuzz/fuzz-network-parser/directives.network
test/fuzz/fuzz-unit-file/directives-all.service
test/fuzz/fuzz-unit-file/directives.mount
test/fuzz/fuzz-unit-file/directives.scope
test/fuzz/fuzz-unit-file/directives.service
test/fuzz/fuzz-unit-file/directives.slice
test/fuzz/fuzz-unit-file/directives.socket
test/fuzz/fuzz-unit-file/directives.swap
test/meson.build
test/test-functions
test/test-network/conf/25-vxlan-ipv6.netdev [new file with mode: 0644]
test/test-network/conf/26-bridge-issue-20373.netdev [new file with mode: 0644]
test/test-network/conf/26-bridge-vlan-master-issue-20373.network [new file with mode: 0644]
test/test-network/conf/26-bridge-vlan-slave-issue-20373.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-ipv4-dhcp-settings.network
test/test-network/conf/dhcp-server-static-lease.network
test/test-network/conf/dhcp-server-with-ipv6-prefix.network
test/test-network/conf/ipv6ra-prefix-client-deny-list.network
test/test-network/conf/ipv6ra-prefix-client.network
test/test-network/conf/ipv6ra-prefix.network
test/test-network/conf/ipv6ra-uplink.network [new file with mode: 0644]
test/test-network/conf/vxlan-ipv6.network [new file with mode: 0644]
test/test-network/conf/vxlan-test1.network
test/test-network/systemd-networkd-tests.py
test/units/testsuite-10.service
test/units/testsuite-13.sh
test/units/testsuite-29.sh
test/units/testsuite-50.sh
test/units/testsuite-62-1.service [new file with mode: 0644]
test/units/testsuite-62-2.service [new file with mode: 0644]
test/units/testsuite-62-3.service [new file with mode: 0644]
test/units/testsuite-62-4.service [new file with mode: 0644]
test/units/testsuite-62-5.service [new file with mode: 0644]
test/units/testsuite-62.service [new file with mode: 0644]
test/units/testsuite-62.sh [new file with mode: 0755]
tmpfiles.d/meson.build
units/factory-reset.target [new file with mode: 0644]
units/meson.build
units/systemd-boot-update.service [new file with mode: 0644]
units/systemd-tmpfiles-clean.service
units/systemd-tmpfiles-setup.service

index babdf7ae6ea73e85d6ab937a3becaf7f51b05992..75aa948aa9d2059f6e35fef839769764798cc477 100644 (file)
@@ -21,10 +21,11 @@ jobs:
           - debian
           - ubuntu
           - fedora
+          - opensuse
 
     steps:
     - uses: actions/checkout@v2
-    - uses: systemd/mkosi@v9
+    - uses: systemd/mkosi@v10
 
     - name: Install
       run: sudo apt-get update && sudo apt-get install --no-install-recommends python3-pexpect python3-jinja2
@@ -46,13 +47,13 @@ jobs:
         systemd-nspawn --version
 
     - name: Build ${{ matrix.distro }}
-      run: sudo python3 -m mkosi --password= --qemu-headless build
+      run: sudo python3 -m mkosi --password= --network-veth=no --qemu-headless build
 
     - name: Show ${{ matrix.distro }} image summary
       run: sudo python3 -m mkosi --password= --qemu-headless summary
 
     - name: Boot ${{ matrix.distro }} systemd-nspawn
-      run: sudo ./.github/workflows/test_mkosi_boot.py python3 -m mkosi --password= --qemu-headless boot
+      run: sudo ./.github/workflows/test_mkosi_boot.py python3 -m mkosi --password= --network-veth=no --qemu-headless boot
 
     - name: Boot ${{ matrix.distro }} QEMU
-      run: sudo ./.github/workflows/test_mkosi_boot.py python3 -m mkosi --password= --qemu-headless qemu
+      run: sudo ./.github/workflows/test_mkosi_boot.py python3 -m mkosi --password= --network-veth=no --qemu-headless qemu
index 3418fd3a51398c6ab877fa03ea2536ab8f10eace..3ea769a69f151011e16c897de3f897793d6940f0 100755 (executable)
@@ -2,13 +2,15 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
 import pexpect
+import re
 import sys
 
 
 def run() -> None:
     p = pexpect.spawnu(" ".join(sys.argv[1:]), logfile=sys.stdout, timeout=300)
 
-    p.expect("#")
+    # distro-independent root prompt
+    p.expect(re.compile("~[^#]{0,3}#"))
     p.sendline("systemctl poweroff")
 
     p.expect(pexpect.EOF)
index 93f8b1c0f471a9e642595a3ee869a5be67c22343..83ec5ecf116efda85d854260f44deec96e1cddbc 100644 (file)
--- a/.lgtm.yml
+++ b/.lgtm.yml
@@ -10,6 +10,9 @@ extraction:
         - libp11-kit-dev
         - libssl-dev
         - python3-jinja2
+    after_prepare:
+      - pip3 install meson==0.53.2
+      - export PATH="/opt/work/.local/bin:$PATH"
   python:
     python_setup:
       version: 3
index b468433f340a3b2efafedd560e45e916a0311a3f..7eb7b857ca0e230195033dbdeae57a788ccd4573 100644 (file)
@@ -36,7 +36,7 @@ BuildPackages=
         pcre-devel
         python3
         python3-lxml
-        python3-jinja2
+        python3-Jinja2
         qrencode-devel
         system-user-nobody
         systemd-sysvinit
@@ -60,6 +60,7 @@ Packages=
         libapparmor1
         libcrypt1
         libcryptsetup12
+        libgcrypt20
         libkmod2
         liblz4-1
         libmount1
diff --git a/README b/README
index a8f23a0d5b95d1c39dd6695c58cef3367906a175..11487420f7ce22589d66b5119db8ec2b28ed75b1 100644 (file)
--- a/README
+++ b/README
@@ -40,6 +40,7 @@ REQUIREMENTS:
         Linux kernel >= 4.17 for cgroup-bpf socket address hooks
         Linux kernel >= 5.3 for bounded-loops in BPF program
         Linux kernel >= 5.4 for signed Verity images support
+        Linux kernel >= 5.7 for BPF links
 
         Kernel Config Options:
           CONFIG_DEVTMPFS
@@ -108,7 +109,8 @@ REQUIREMENTS:
           CONFIG_HAVE_EBPF_JIT
           CONFIG_CGROUP_BPF
 
-        Required for SocketBind{Allow|Deny}= in resource control unit settings
+        Required for SocketBind{Allow|Deny}=, RestrictNetworkInterfaces= in
+        resource control unit settings
           CONFIG_BPF
           CONFIG_BPF_SYSCALL
           CONFIG_BPF_JIT
diff --git a/TODO b/TODO
index 3a29ccae2d1a00378c54349be52d1294d1783c8c..de27269d84b27de073c7c93ad49b937a5a967db1 100644 (file)
--- a/TODO
+++ b/TODO
@@ -83,6 +83,17 @@ Janitorial Clean-ups:
 
 Features:
 
+* tpm2: figure out if we need to do anything for TPM2 parameter encryption? And
+  if so, what precisely?
+
+* insert pkcs7 signature for verity gpt
+
+* when mounting disk images: if IMAGE_ID/IMAGE_VERSION is set in os-release
+  data in the image, make sure the image filename actually matches this, so
+  that images cannot be misused.
+
+* use credentials logic/TPM2 logic to store homed signing key
+
 * New udev block device symlink names:
   /dev/disk/by-parttypelabel/<pttype>/<ptlabel>. Use case: if pt label is used
   as partition image version string, this is a safe way to reference a specific
@@ -1017,10 +1028,6 @@ Features:
 
 * bootctl,sd-boot: actually honour the "architecture" key
 
-* sd-boot: add service that automatically runs "bootctl update" on every boot,
-  in a graceful way, so that updated /usr trees automatically propagate into
-  updated boot loaders on reboot.
-
 * bootctl:
   - teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation
   - teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host
index 3e08f564a64069200babc5fe76e18bb711bb7c8a..a5d7dc6f78d6636a36d94efc22af8f2db8f8a3f4 100644 (file)
@@ -188,6 +188,15 @@ Support: %SUPPORT_URL%
 System shutdown has been initiated. The shutdown has now begun and
 all system services are terminated and all file systems unmounted.
 
+-- c14aaf76ec284a5fa1f105f88dfb061c
+Subject: System factory reset initiated
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+System factory reset has been initiated. The precise operation this
+executes is implementation-defined, but typically has the effect of
+reverting the system's state and configuration to vendor defaults.
+
 -- 7d4958e842da4a758f6c1cdc7b36dcc5
 Subject: A start job for unit @UNIT@ has begun execution
 Defined-By: systemd
index 2cec3bdc1666d6344eb6ba39a1f18afcda9e4eb3..d2ab3baf68850d71b8f2fc3163a1df42a21c15fd 100644 (file)
@@ -316,3 +316,22 @@ fuzzers:
 
 Note that is may be also useful to set `$SYSTEMD_LOG_LEVEL`, since all logging
 is suppressed by default.
+
+systemd-importd:
+
+* `SYSTEMD_IMPORT_BTRFS_SUBVOL` – takes a boolean, which controls whether to
+  prefer creating btrfs subvolumes over plain directories for machine
+  images. Has no effect on non-btrfs file systems where subvolumes are not
+  available anyway. If not set, defaults to true.
+
+* `SYSTEMD_IMPORT_BTRFS_QUOTA` – takes a boolean, which controls whether to set
+  up quota automatically for created btrfs subvolumes for machine images. If
+  not set, defaults to true. Has no effect if machines are placed in regular
+  directories, because btrfs subvolumes are not supported or disabled. If
+  enabled, the quota group of the subvolume is automatically added to a
+  combined quota group for all such machine subvolumes.
+
+* `SYSTEMD_IMPORT_SYNC` – takes a boolean, which controls whether to
+  synchronize images to disk after installing them, before completing the
+  operation. If not set, defaults to true. If disabled installation of images
+  will be quicker, but not as safe.
index 198ca52328d75c6f0d785157026c85303b337504..7982be34d08604cdfe8a454c8387544f1f2c4b26 100644 (file)
@@ -245,3 +245,83 @@ the cached images are initialized (`mkosi -i`).
 
 Now, your editor will start clangd in the mkosi build image and all of clangd's features will work as
 expected.
+
+## Debugging systemd with mkosi + vscode
+
+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
+(either using QEMU or systemd-nspawn).
+
+To allow VSCode's debugger to attach to systemd running in a mkosi image, we have to make sure it can access
+the container/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. The easiest way to set the
+option is to create a file 20-local.conf in mkosi.default.d/ and add the following contents:
+
+```
+[Host]
+Ssh=yes
+```
+
+Next, make sure systemd-networkd is running on the host system so that it can configure the network interface
+connecting the host system to the container/VM spawned by mkosi. Once systemd-networkd is running, you should
+be able to connect to a running mkosi image by executing `mkosi ssh` in the systemd repo directory.
+
+Now we need to configure VSCode. First, make sure the C/C++ extension is installed. If you're already using
+a different extension for code completion and other IDE features for C in VSCode, make sure to disable the
+corresponding parts of the C/C++ extension in your VSCode user settings by adding the following entries:
+
+```json
+"C_Cpp.formatting": "Disabled",
+"C_Cpp.intelliSenseEngine": "Disabled",
+"C_Cpp.enhancedColorization": "Disabled",
+"C_Cpp.suggestSnippets": false,
+```
+
+With the extension set up, we can create the launch.json file in the .vscode/ directory to tell the VSCode
+debugger how to attach to the systemd instance running in our mkosi container/VM. Create the file and add the
+following contents:
+
+```json
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "cppdbg",
+            "program": "/usr/lib/systemd/systemd",
+            "processId": "${command:pickProcess}",
+            "request": "attach",
+            "name": "systemd",
+            "pipeTransport": {
+                "pipeProgram": "mkosi",
+                "pipeArgs": [
+                    "-C",
+                    "/path/to/systemd/repo/directory/on/host/system/",
+                    "ssh"
+                ],
+                "debuggerPath": "/usr/bin/gdb"
+            },
+            "MIMode": "gdb",
+            "sourceFileMap": {
+                "/root/build/../src": {
+                    "editorPath": "${workspaceFolder}",
+                    "useForBreakpoints": false
+                },
+                "/root/build/*": {
+                    "editorPath": "${workspaceFolder}/mkosi.builddir",
+                    "useForBreakpoints": false
+                }
+            }
+        }
+    ]
+}
+```
+
+Now that the debugger knows how to connect to our process in the container/VM and we've set up the necessary
+source mappings, go to the "Run and Debug" window and run the "systemd" debug configuration. If everything
+goes well, the debugger should now be attached to the systemd instance running in the container/VM. You can
+attach breakpoints from the editor and enjoy all the other features of VSCode's debugger.
+
+To debug systemd components other than PID 1, set "program" to the full path of the component you want to
+debug and set "processId" to "${command:pickProcess}". Now, when starting the debugger, VSCode will ask you
+the PID of the process you want to debug. Run `systemctl show --property MainPID --value <component>` in the
+container to figure out the PID and enter it when asked and VSCode will attach to that process instead.
index d9171c7b658fe387006f28eaa50484a3bd3fa75c..dc1171c736d10ed11b6fd8f240c566ba09eae4e7 100644 (file)
@@ -6,21 +6,17 @@ layout: default
 
 # Portable Services Introduction
 
-This systemd version includes a preview of the "portable service"
-concept. "Portable Services" are supposed to be an incremental improvement over
-traditional system services, making two specific facets of container management
-available to system services more readily. Specifically:
+systemd (since version 239) supports a concept of "Portable Services".
+"Portable Services" are a delivery method for system services that uses
+two specific features of container management:
 
-1. The bundling of applications, i.e. packing up multiple services, their
-   binaries and all their dependencies in a single image, and running them
-   directly from it.
+1. Applications are bundled. I.e. multiple services, their binaries and all
+   their dependencies are packaged in an image, and are run directly from it.
 
 2. Stricter default security policies, i.e. sand-boxing of applications.
 
-The primary tool for interfacing with "portable services" is the new
-"portablectl" program. It's currently shipped in /usr/lib/systemd/portablectl
-(i.e. not in the `$PATH`), since it's not yet considered part of the officially
-supported systemd interfaces — it's a preview still after all.
+The primary tool for interacting with Portable Services is `portablectl`,
+and they are managed by the `systemd-portabled` service.
 
 Portable services don't bring anything inherently new to the table. All they do
 is put together known concepts in a slightly nicer way to cover a specific set
@@ -29,12 +25,13 @@ of use-cases in a nicer way.
 ## So, what *is* a "Portable Service"?
 
 A portable service is ultimately just an OS tree, either inside of a directory
-tree, or inside a raw disk image containing a Linux file system. This tree is
-called the "image". It can be "attached" or "detached" from the system. When
-"attached" specific systemd units from the image are made available on the host
-system, then behaving pretty much exactly like locally installed system
-services. When "detached" these units are removed again from the host, leaving
-no artifacts around (except maybe messages they might have logged).
+tree, or inside a raw disk image (or a set of images that get layered, see
+[Layered Images](#layered-images)) containing a Linux file system. This tree is called the
+"image". It can be "attached" or "detached" from the system. When "attached"
+specific systemd units from the image are made available on the host system,
+then behaving pretty much exactly like locally installed system services. When
+"detached" these units are removed again from the host, leaving no artifacts
+around (except maybe messages they might have logged).
 
 The OS tree/image can be created with any tool of your choice. For example, you
 can use `dnf --installroot=` if you like, or `debootstrap`, the image format is
@@ -145,8 +142,14 @@ the drop-ins and the unit files associated with the image, and removes them
 again.
 
 Note that `portablectl attach` won't enable or start any of the units it copies
-out. This still has to take place in a second, separate step. (That said We
-might add options to do this automatically later on.).
+out by default, but `--enable` and `--now` parameter are available as shortcuts.
+The same is true for the opposite `detach` operation.
+
+A `portablectl reattach` command is made available to combine a `detach` with an
+`attach`, and it is useful in case an image gets upgraded, as it allows a to
+perform a `restart` operation on the unit(s) instead of `stop` plus `start`,
+thus providing lower downtime and avoiding losing runtime state associated with
+the unit such as the file descriptor store.
 
 ## Requirements on Images
 
@@ -243,6 +246,34 @@ image. To facility 3 and 4 you also need to include a boot loader in the
 image. As mentioned `mkosi -b` takes care of all of that for you, but any other
 image generator should work too.
 
+## Extension Images
+
+Portable services can be delivered as one or multiple images that extend the base
+image, and are combined with OverlayFS at runtime, when they are attached. This
+enables a workflow that splits the base 'runtime' from the daemon, so that multiple
+portable services can share the same 'runtime' image (libraries, tools) without
+having to include everything each time, with the layering happening only at runtime.
+The `--extension` parameter of `portablectl` can be used to specify as many upper
+layers as desired. On top of the requirements listed in the previous section, the
+following must be also be observed.
+
+1. The base/OS image must contain an os-release file, either in `/etc/os-release` or
+   `/usr/lib/os-release`. The file should follow the standard format.
+
+2. The upper extension(s) image(s) must contain an extension-release file in
+   `/usr/lib/extension-release.d/`, with an `ID=` and `SYSEXT_LEVEL=`/`VERSION_ID=`
+   matching the base image.
+
+3. The base/OS image does not need to have any unit files.
+
+4. The upper extension(s) image(s) must at least contain one matching unit file each,
+   with the right name prefix and suffix (see above).
+
+```
+# /usr/lib/systemd/portablectl attach --extension foobar_0.7.23.raw debian-runtime_11.1.raw foobar
+# /usr/lib/systemd/portablectl attach --extension barbaz_7.0.23.raw debian-runtime_11.1.raw barbaz
+```
+
 ## Execution Environment
 
 Note that the code in portable service images is run exactly like regular
index 7566798e2bbf375a244819be73eb1030f79316be..c1485e1256c74beeb95ad50aaca619298fdef790 100644 (file)
@@ -68,6 +68,11 @@ usb:v08FFp5731*
 usb:v5501p08FF*
  ID_AUTOSUSPEND=1
 
+# Supported by libfprint driver egis0570
+usb:v1C7Ap0570*
+usb:v1C7Ap0571*
+ ID_AUTOSUSPEND=1
+
 # Supported by libfprint driver elan
 usb:v04F3p0903*
 usb:v04F3p0907*
@@ -127,6 +132,11 @@ usb:v04F3p0C42*
 usb:v04F3p0C4D*
 usb:v04F3p0C4F*
 usb:v04F3p0C63*
+usb:v04F3p0C6E*
+ ID_AUTOSUSPEND=1
+
+# Supported by libfprint driver elanmoc
+usb:v04F3p0C7E*
  ID_AUTOSUSPEND=1
 
 # Supported by libfprint driver etes603
@@ -264,7 +274,6 @@ usb:v147Ep1002*
 usb:v1491p0088*
 usb:v16D1p1027*
 usb:v1C7Ap0300*
-usb:v1C7Ap0570*
 usb:v1C7Ap0575*
 usb:v27C6p5042*
 usb:v27C6p5110*
index 96c5da1104c6255b00d19d3302bc9ab0398c6d80..bd463a4215494bc5775fd58fc10248a9b6178f96 100644 (file)
@@ -567,9 +567,11 @@ evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnLENOVO:*pvrLenovoU41-70:*
  EVDEV_ABS_35=117:3958:36
  EVDEV_ABS_36=104:1960:26
 
-# Lenovo Thinkpad T490 and T14 Gen1
+# Lenovo Thinkpad T490 and T14/P14s Gen1/2
 evdev:name:SynPS/2 Synaptics TouchPad:dmi:*:svnLENOVO:*pvrThinkPadT490:*
 evdev:name:SynPS/2 Synaptics TouchPad:dmi:*:svnLENOVO:*pvrThinkPadT14Gen1:*
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:*:svnLENOVO:*pvrThinkPadP14sGen1:*
+evdev:name:SynPS/2 Synaptics TouchPad:dmi:*:svnLENOVO:*pvrThinkPadP14sGen2a:*
  EVDEV_ABS_00=::44
  EVDEV_ABS_01=::52
  EVDEV_ABS_35=::44
index 2b1386df75d5695d9740f635768014dc08828776..9859ce5b124097f811dc2fdb48f47dc467922c59 100644 (file)
@@ -289,10 +289,10 @@ sensor:modalias:acpi:*KIOX000A*:dmi:*svn*CytrixTechnology:*pn*Complex11t:*
 sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:pnVostro5581:*
  ACCEL_LOCATION=base
 
-sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:pnLatitude9520:*:ct10:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0A3E:*
  ACCEL_LOCATION=base
 
-sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:pnLatitude7420:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0B0B:*
  ACCEL_LOCATION=base
 
 # Dell Venue 8 Pro 3845
index aa007112112d2b0c4a5adef0e2650d8c9ada9411..2f879da34686b210457b32812b0759ce18e3cf79 100644 (file)
@@ -112,14 +112,6 @@ ieee1394:ven*sp0000A02Dver00000110
 # BridgeCo. Enhancement BreakOut Box (BeBoB) for DM1000, DM1100, and DM1500 ASICs.
 #
 
-# Match to eAR Master One, Eroica, Figaro, and Ciaccona.
-ieee1394:node:ven0x000aacmo0x000002units0x00a02d:0x010001
-ieee1394:ven00000AACmo00000002sp0000A02Dver00010001
- ID_VENDOR_FROM_DATABASE=Acoustic Reality
- ID_MODEL_FROM_DATABASE=eAR FireWire Audio
- IEEE1394_UNIT_FUNCTION_AUDIO=1
- IEEE1394_UNIT_FUNCTION_VIDEO=0
-
 ieee1394:node:ven0x0003dbmo0x01eeeeunits0x00a02d:0x010001
 ieee1394:ven000003DBmo0001EEEEsp0000A02Dver00010001
  ID_VENDOR_FROM_DATABASE=Apogee Electronics
@@ -322,7 +314,7 @@ ieee1394:ven00000FF2mo00010065sp0000A02Dver00010001
  IEEE1394_UNIT_FUNCTION_AUDIO=1
  IEEE1394_UNIT_FUNCTION_VIDEO=0
 
-# An extension card for Mackie d.2.
+# An extension card for Mackie d.2. Mackie d.2 Pro is preinstalled model.
 ieee1394:node:ven0x000ff2mo0x010067units0x00a02d:0x010001
 ieee1394:ven00000FF2mo00010067sp0000A02Dver00010001
  ID_VENDOR_FROM_DATABASE=Loud Technologies
@@ -330,8 +322,41 @@ ieee1394:ven00000FF2mo00010067sp0000A02Dver00010001
  IEEE1394_UNIT_FUNCTION_AUDIO=1
  IEEE1394_UNIT_FUNCTION_VIDEO=0
 
-# Match to FireFly 202, 302, 808, and 808 Universal.
-# Match to HelixBoard 12 mk II, 18 mk II, 24 mk II, 12 Universal, 18 Universal, and 24 Universal.
+ieee1394:node:ven0x001496mo0x050000units0x00a02d:0x010001
+ieee1394:ven00001496mo00050000sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=Phonic
+ ID_MODEL_FROM_DATABASE=Helixboard 12 FireWire MkII
+ IEEE1394_UNIT_FUNCTION_MIDI=1
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
+ieee1394:node:ven0x001496mo0x060000units0x00a02d:0x010001
+ieee1394:ven00001496mo00060000sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=Phonic
+ ID_MODEL_FROM_DATABASE=Helixboard 18 FireWire MkII
+ IEEE1394_UNIT_FUNCTION_MIDI=1
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
+ieee1394:node:ven0x001496mo0x070000units0x00a02d:0x010001
+ieee1394:ven00001496mo00070000sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=Phonic
+ ID_MODEL_FROM_DATABASE=Helixboard 24 FireWire MkII
+ IEEE1394_UNIT_FUNCTION_MIDI=1
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
+ieee1394:node:ven0x001496mo0x080000units0x00a02d:0x010001
+ieee1394:ven00001496mo00080000sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=Phonic
+ ID_MODEL_FROM_DATABASE=Firefly 808 FireWire
+ IEEE1394_UNIT_FUNCTION_MIDI=1
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
+# Match to FireFly 202, 302, 808 Universal.
+# Match to HelixBoard 12 FireWire, 18 FireWire, 24 FireWire.
+# Match to HelixBoard 12 Universal, 18 Universal, and 24 Universal.
 ieee1394:node:ven0x001496mo0x000000units0x00a02d:0x010001
 ieee1394:ven00001496mo00000000sp0000A02Dver00010001
  ID_VENDOR_FROM_DATABASE=Phonic
@@ -386,6 +411,15 @@ ieee1394:ven00010065mo00010067sp0000A02Dver00010001
  IEEE1394_UNIT_FUNCTION_AUDIO=1
  IEEE1394_UNIT_FUNCTION_VIDEO=0
 
+# Match to TerraTec Aureon 7.1 FireWire.
+# Match to eAR Master One, Eroica, Figaro, and Ciaccona. OEM by TerraTec perhaps.
+ieee1394:node:ven0x000aacmo0x000002units0x00a02d:0x010001
+ieee1394:ven00000AACmo00000002sp0000A02Dver00010001
+ ID_VENDOR_FROM_DATABASE=TerraTec
+ ID_MODEL_FROM_DATABASE=Aureon 7.1 FireWire
+ IEEE1394_UNIT_FUNCTION_AUDIO=1
+ IEEE1394_UNIT_FUNCTION_VIDEO=0
+
 ieee1394:node:ven0x000aacmo0x000003units0x00a02d:0x010001
 ieee1394:ven00000AACmo00000003sp0000A02Dver00010001
  ID_VENDOR_FROM_DATABASE=TerraTec Electronic
@@ -545,6 +579,7 @@ ieee1394:ven00000FF2mo00081216sp0000A02Dver00010001
  IEEE1394_UNIT_FUNCTION_AUDIO=1
  IEEE1394_UNIT_FUNCTION_VIDEO=0
 
+# Match to former model of Onyx 1640i.
 ieee1394:node:ven0x000ff2mo0x001640units0x00a02d:0x010001
 ieee1394:ven00000FF2mo00001640sp0000A02Dver00010001
  ID_VENDOR_FROM_DATABASE=Loud Technologies
@@ -707,7 +742,7 @@ ieee1394:ven00000FF2mo00000007sp00000FF2ver00000001
  ID_MODEL_FROM_DATABASE=Mackie Onyx Blackbird
  IEEE1394_UNIT_FUNCTION_AUDIO=1
 
-# Match to Onyx 1640i, and latter models of Onyx 820i, 1220i, and 1620i.
+# Match to latter models of Onyx 820i, 1220i, 1620i, and 1640i.
 ieee1394:node:ven0x000ff2mo0x000006units0x000ff2:0x000001
 ieee1394:ven00000FF2mo00000006sp00000FF2ver00000001
  ID_VENDOR_FROM_DATABASE=Loud Technologies
index 0a96b5553a540c4b10df8b9f088759fd01164428..4f3560e37f869ad8c405d7054d4ec30b6cb5e7d8 100644 (file)
@@ -49,7 +49,7 @@ if conf.get('ENABLE_HWDB') == 1
 
         if install_sysconfdir
                 meson.add_install_script('sh', '-c',
-                                         mkdir_p.format(join_paths(sysconfdir, 'udev/hwdb.d')))
+                                         mkdir_p.format(sysconfdir / 'udev/hwdb.d'))
 
                 meson.add_install_script('sh', '-c',
                                          'test -n "$DESTDIR" || @0@/systemd-hwdb update'.format(rootbindir))
index 81033158f64b3917f122d7843c911a660469285e..a958cde7df84d0d8f1285ced7e87fed182bb661d 100644 (file)
 
       <varlistentry>
         <term><option>--graceful</option></term>
-        <listitem><para>Ignore failure when the EFI System Partition cannot be found, or when EFI variables
-        cannot be written. Currently only applies to random seed operations.</para></listitem>
+        <listitem><para>Ignore failure when the EFI System Partition cannot be found, when EFI variables
+        cannot be written, or a different or newer boot loader is already installed. Currently only applies
+        to random seed and update operations.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--make-machine-id-directory=yes|no|auto</option></term>
         <listitem><para>Control creation and deletion of the top-level machine ID directory on the file
-        system containing boot loader entries (i.e. beneath the file system returned by
-        <option>--print-boot-path</option> above) during <option>install</option> and
+        system containing boot loader entries (i.e. beneath the file system returned by the
+        <option>--print-boot-path</option> option, see above) during <option>install</option> and
         <option>remove</option>, respectively.  <literal>auto</literal> is equivalent to
         <literal>yes</literal> if <filename>/etc/machine-id</filename> resides on a filesystem other than
         tmpfs and <literal>no</literal> otherwise (in the latter case the machine ID is likely transient and
index 28e6017c7db28664569c26b22e17dfa02a4b8e97..1751191ec0cb12cc8b899c78295f98f8ccf64ed4 100644 (file)
       <varlistentry>
         <term><varname>ProcessSizeMax=</varname></term>
 
-        <listitem><para>The maximum size in bytes of a core
-        which will be processed. Core dumps exceeding this size
-        may be stored, but the backtrace will not be generated.
-        Like other sizes in this same config file, the usual
-        suffixes to the base of 1024 are allowed (B, K, M,
-        G, T, P, and E.)</para>
+        <listitem><para>The maximum size in bytes of a core which will be processed. Core dumps exceeding
+        this size may be stored, but the backtrace will not be generated.  Like other sizes in this same
+        config file, the usual suffixes to the base of 1024 are allowed (B, K, M, G, T, P, and E).</para>
 
         <para>Setting <varname>Storage=none</varname> and <varname>ProcessSizeMax=0</varname>
         disables all coredump handling except for a log entry.</para>
@@ -99,9 +96,8 @@
         <term><varname>ExternalSizeMax=</varname></term>
         <term><varname>JournalSizeMax=</varname></term>
 
-        <listitem><para>The maximum (compressed or uncompressed) size in bytes of a
-        core to be saved. Unit suffixes are allowed just as in
-        <option>ProcessSizeMax=</option></para></listitem>.
+        <listitem><para>The maximum (compressed or uncompressed) size in bytes of a core to be saved. Unit
+        suffixes are allowed just as in <option>ProcessSizeMax=</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         by core dumps might temporarily exceed these limits while
         core dumps are processed. Note that old core dumps are also
         removed based on time via
-        <citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>8</manvolnum></citerefentry>. Set
-        either value to 0 to turn off size-based
-        clean-up.</para></listitem>
+        <citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+        Set either value to 0 to turn off size-based cleanup.</para></listitem>
       </varlistentry>
     </variablelist>
 
index 6ceed41b0598e0f720bb993235ccb3e44ff4ec71..d45ed753b25502c368efbfef7953215bd08c2493 100644 (file)
         <term><option>-1</option></term>
 
         <listitem><para>Show information of the most recent core dump only, instead of listing all known core
-        dumps. (Equivalent to <option>--reverse -n 1</option></para></listitem>
+        dumps. Equivalent to <option>--reverse -n 1</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 4b0b120ca8c23ba7abc0c62b2cd4fec6cba2766f..245ebcee003f5af76db050e2b58d6ec6911402a9 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>--setenv=</option><replaceable>VARIABLE</replaceable>=<replaceable>VALUE</replaceable></term>
+        <term><option>--setenv=</option><replaceable>VARIABLE</replaceable>[=<replaceable>VALUE</replaceable>]</term>
 
-        <listitem><para>Takes an environment variable assignment to set for all user processes. Note that a
-        number of other settings also result in environment variables to be set for the user, including
-        <option>--email=</option>, <option>--timezone=</option> and <option>--language=</option>. May be used
-        multiple times to set multiple environment variables.</para></listitem>
+        <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
+        <replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
+        program environment will be used.</para>
+
+        <para>Note that a number of other settings also result in environment variables to be set for the
+        user, including <option>--email=</option>, <option>--timezone=</option> and
+        <option>--language=</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 8a4b86e7bd12823c3082fd9bc26c9c8e74949fbe..a85c3bcf103886696aa6ffe0667dbf96ff4171f8 100644 (file)
@@ -3,13 +3,13 @@
 #include <sd-hwdb.h>
 
 int print_usb_properties(uint16_t vid, uint16_t pid) {
-  char match[15];
+  char match[STRLEN("usb:vp") + DECIMAL_STR_MAX(uint16_t) * 2];
   sd_hwdb *hwdb;
   const char *key, *value;
   int r;
 
   /* Match this USB vendor and product ID combination */
-  snprintf(match, sizeof match, "usb:v%04Xp%04X", vid, pid);
+  xsprintf(match, "usb:v%04Xp%04X", vid, pid);
 
   r = sd_hwdb_new(&hwdb);
   if (r < 0)
index 29315ceb1743a3353460a5a1aa42749c881f9018..ffbd897a1fdd467d59c594579222c6c3d0ecfa84 100644 (file)
@@ -41,8 +41,8 @@
     a comment line. Empty and comment lines are ignored.</para>
 
     <para>Boolean arguments may be written as
-    <literal>yes</literal>/<literal>y</literal>/<literal>true</literal>/<literal>1</literal> or
-    <literal>no</literal>/<literal>n</literal>/<literal>false</literal>/<literal>0</literal>.
+    <literal>yes</literal>/<literal>y</literal>/<literal>true</literal>/<literal>t</literal>/<literal>on</literal>/<literal>1</literal> or
+    <literal>no</literal>/<literal>n</literal>/<literal>false</literal>/<literal>f</literal>/<literal>off</literal>/<literal>0</literal>.
     </para>
   </refsect1>
 
         <listitem><para>A glob pattern to select the default entry. The default entry
         may be changed in the boot menu itself, in which case the name of the
         selected entry will be stored as an EFI variable, overriding this option.
-        </para></listitem>
+        </para>
+
+        <table>
+          <title>Automatically detected entries will use the following names:</title>
+
+          <tgroup cols='2'>
+            <colspec colname='name' />
+            <colspec colname='expl' />
+            <thead>
+              <row>
+                <entry>Name</entry>
+                <entry>Description</entry>
+              </row>
+            </thead>
+            <tbody>
+              <row>
+                <entry>auto-efi-default</entry>
+                <entry>EFI Default Loader</entry>
+              </row>
+              <row>
+                <entry>auto-efi-shell</entry>
+                <entry>EFI Shell</entry>
+              </row>
+              <row>
+                <entry>auto-osx</entry>
+                <entry>macOS</entry>
+              </row>
+              <row>
+                <entry>auto-reboot-to-firmware-setup</entry>
+                <entry>Reboot Into Firmware Interface</entry>
+              </row>
+              <row>
+                <entry>auto-windows</entry>
+                <entry>Windows Boot Manager</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table></listitem>
       </varlistentry>
 
       <varlistentry>
index 5dc24a3f05306a2a8cb74af604d21255f74ede3a..3045c1b9ba675970e8341d9e86eac4415474d94a 100644 (file)
         <literal>suspend</literal>,
         <literal>hibernate</literal>,
         <literal>hybrid-sleep</literal>,
-        <literal>suspend-then-hibernate</literal>, and
-        <literal>lock</literal>.
+        <literal>suspend-then-hibernate</literal>,
+        <literal>lock</literal>, and
+        <literal>factory-reset</literal>.
         If <literal>ignore</literal>, logind will never handle these
         keys. If <literal>lock</literal>, all running sessions will be
         screen-locked; otherwise, the specified action will be taken
index ad47b6102e342af99ef8d1355d7812eeb66e3d3f..7ecb8885a25a5e93e54ca2c37aeeff8d529f29a4 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
-        <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
-
-         <listitem><para>When used with the <command>shell</command> command, sets an environment
-         variable to pass to the executed shell. Takes an environment variable name and value,
-         separated by <literal>=</literal>. This switch may be used multiple times to set multiple
-         environment variables. Note that this switch is not supported for the
-         <command>login</command> command (see below).</para></listitem>
+        <term><option>-E <replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+        <term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+
+        <listitem><para>When used with the <command>shell</command> command, sets an environment variable for
+        the executed shell. This option may be used more than once to set multiple variables. When
+        <literal>=</literal> and <replaceable>VALUE</replaceable> are omitted, the value of the variable with
+        the same name in the program environment will be used.</para>
+
+        <para>Note that this option is not supported for the <command>login</command> command.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
index 900bb2a3afce62f84b33eeec72f3f476e32f1e2a..4132f826a8962de7784f35dbcf02f45578ce2a0a 100644 (file)
@@ -37,7 +37,7 @@ man_pages = []
 html_pages = []
 source_xml_files = []
 dbus_docs = []
-foreach tuple : xsltproc.found() ? manpages : []
+foreach tuple : manpages
         stem = tuple[0]
         section = tuple[1]
         aliases = tuple[2]
@@ -54,49 +54,50 @@ foreach tuple : xsltproc.found() ? manpages : []
                 htmlaliases += alias + '.html'
         endforeach
 
-        mandirn = join_paths(get_option('mandir'), 'man' + section)
+        mandirn = get_option('mandir') / ('man' + section)
 
         if condition == '' or conf.get(condition) == 1
-                p1 = custom_target(
-                        man,
-                        input : xml,
-                        output : [man] + manaliases,
-                        command : xslt_cmd + [custom_man_xsl, '@INPUT@'],
-                        depends : custom_entities_ent,
-                        install : want_man,
-                        install_dir : mandirn)
-                man_pages += p1
-
-                p2 = []
-                foreach htmlalias : htmlaliases
-                        link = custom_target(
-                                htmlalias,
-                                output : htmlalias,
-                                command : [ln, '-fs', html, '@OUTPUT@'])
-                        if want_html
-                                dst = join_paths(docdir, 'html', htmlalias)
-                                cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
-                                meson.add_install_script('sh', '-c', cmd)
-                                p2 += link
-                        endif
-                        html_pages += link
-                endforeach
-
-                p3 = custom_target(
-                        html,
-                        input : xml,
-                        output : html,
-                        command : xslt_cmd + [custom_html_xsl, '@INPUT@'],
-                        depends : [custom_entities_ent, p2],
-                        install : want_html,
-                        install_dir : join_paths(docdir, 'html'))
-                html_pages += p3
-
                 file = files(tuple[0] + '.xml')
-                source_xml_files += file
                 if tuple[0].startswith('org.freedesktop.')
                         dbus_docs += file
                 endif
+
+                if xsltproc.found()
+                        p1 = custom_target(
+                                man,
+                                input : xml,
+                                output : [man] + manaliases,
+                                command : xslt_cmd + [custom_man_xsl, '@INPUT@'],
+                                depends : custom_entities_ent,
+                                install : want_man,
+                                install_dir : mandirn)
+                        man_pages += p1
+
+                        p2 = []
+                        foreach htmlalias : htmlaliases
+                                link = custom_target(
+                                        htmlalias,
+                                        output : htmlalias,
+                                        command : [ln, '-fs', html, '@OUTPUT@'])
+                                if want_html
+                                        dst = docdir / 'html' / htmlalias
+                                        cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
+                                        meson.add_install_script('sh', '-c', cmd)
+                                        p2 += link
+                                endif
+                                html_pages += link
+                        endforeach
+
+                        p3 = custom_target(
+                                html,
+                                input : xml,
+                                output : html,
+                                command : xslt_cmd + [custom_html_xsl, '@INPUT@'],
+                                depends : [custom_entities_ent, p2],
+                                install : want_html,
+                                install_dir : docdir / 'html')
+                        html_pages += p3
+                endif
         else
                 message('Skipping @0@.@1@ because @2@ is false'.format(stem, section, condition))
         endif
@@ -132,7 +133,7 @@ foreach tuple : xsltproc.found() ? [['systemd.directives', '7', systemd_directiv
         html = stem + '.html'
         man = stem + '.' + section
 
-        mandirn = join_paths(get_option('mandir'), 'man' + section)
+        mandirn = get_option('mandir') / ('man' + section)
 
         p1 = custom_target(
                 man,
@@ -152,7 +153,7 @@ foreach tuple : xsltproc.found() ? [['systemd.directives', '7', systemd_directiv
                         output : htmlalias,
                         command : [ln, '-fs', html, '@OUTPUT@'])
                 if want_html
-                        dst = join_paths(docdir, 'html', htmlalias)
+                        dst = docdir / 'html' / htmlalias
                         cmd = 'ln -fs @0@ $DESTDIR@1@'.format(html, dst)
                         meson.add_install_script('sh', '-c', cmd)
                         p2 += link
@@ -167,7 +168,7 @@ foreach tuple : xsltproc.found() ? [['systemd.directives', '7', systemd_directiv
                 command : xslt_cmd + [custom_html_xsl, '@INPUT@'],
                 depends : [custom_entities_ent, p2],
                 install : want_html and have_lxml,
-                install_dir : join_paths(docdir, 'html'))
+                install_dir : docdir / 'html')
         html_pages += p3
 endforeach
 
@@ -213,3 +214,26 @@ configure_file(
         input : 'html.in',
         output : 'html',
         configuration : buildroot_substs)
+
+############################################################
+
+update_dbus_docs = custom_target(
+        'update-dbus-docs',
+        output : 'update-dbus-docs',
+        command : [update_dbus_docs_py, '--build-dir', project_build_root, '@INPUT@'],
+        input : dbus_docs)
+
+if conf.get('BUILD_MODE_DEVELOPER') == 1
+        test('dbus-docs-fresh',
+             update_dbus_docs_py,
+             args : ['--build-dir', project_build_root, '--test', dbus_docs])
+endif
+
+update_man_rules = custom_target(
+        'update-man-rules',
+        output : 'update-man-rules',
+        command : [sh, '-c',
+                   'cd @0@ && '.format(project_build_root) +
+                   'python3 @0@/tools/update-man-rules.py $(find @0@ -wholename "*/man/*.xml") >t && '.format(project_source_root) +
+                   'mv t @0@/rules/meson.build'.format(meson.current_source_dir())],
+        depends : custom_entities_ent)
index f9d0ff43f4399463e07f31b2211c67711c4dd08b..b6544cf65d5cbd82377163779d0d61b9bc8fb15d 100644 (file)
@@ -80,7 +80,7 @@
     <filename>/etc/hosts</filename>.</para>
 
     <para>Please keep in mind that <command>nss-myhostname</command> (and <command>nss-resolve</command>) also resolve
-    in the other direction — from locally attached IP adresses to
+    in the other direction — from locally attached IP addresses to
     hostnames. If you rely on that lookup being provided by DNS, you might
     want to order things differently.
     </para>
index 4f9e1f9c5a58f27e2702216df5bb0f93ec3ec17f..81f9fdae8c330393563e30c6014220a303ca78d6 100644 (file)
@@ -54,7 +54,7 @@
     <command>systemd-resolved</command> is not running.</para>
 
     <para>Please keep in mind that <command>nss-myhostname</command> (and <command>nss-resolve</command>) also resolve
-    in the other direction — from locally attached IP adresses to
+    in the other direction — from locally attached IP addresses to
     hostnames. If you rely on that lookup being provided by DNS, you might
     want to order things differently.
     </para>
index 734a9a4a0726a8ca166ad8732c24e820acd6f808..b7b453825e303f660be2b8b1887ce1884d56df1d 100644 (file)
@@ -67,7 +67,7 @@
 
     <para>This is a simple mechanism to provide static user and group records via JSON drop-in files. Such
     user records should be defined in the format described by the <ulink
-    url="https://systemd.io/USER_RECORD">JSON User Record</ulink> specification and be placed in one of the
+    url="https://systemd.io/USER_RECORD">JSON User Records</ulink> specification and be placed in one of the
     aforementioned directories under a file name composed of the user name suffixed with
     <filename>.user</filename>, with a world-readable access mode. A symlink named after the user record's
     UID formatted in decimal and suffixed with <filename>.user</filename> pointing to the primary record file
index 3a54375400223b6abec49b14ea3186252053ba0a..09cb2335539dcbd3f81be9c07d93db594fb5df79 100644 (file)
@@ -1706,7 +1706,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b DefaultDependencies = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
-      readonly s OnSuccesJobMode = '...';
+      readonly s OnSuccessJobMode = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s OnFailureJobMode = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -1815,7 +1815,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property CanFreeze is not documented!-->
 
-    <!--property OnSuccesJobMode is not documented!-->
+    <!--property OnSuccessJobMode is not documented!-->
 
     <!--property OnFailureJobMode is not documented!-->
 
@@ -2019,7 +2019,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="DefaultDependencies"/>
 
-    <variablelist class="dbus-property" generated="True" extra-ref="OnSuccesJobMode"/>
+    <variablelist class="dbus-property" generated="True" extra-ref="OnSuccessJobMode"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="OnFailureJobMode"/>
 
@@ -2528,6 +2528,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       readonly a(iiqq) SocketBindAllow = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly a(iiqq) SocketBindDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (bas) RestrictNetworkInterfaces = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly as Environment = ['...', ...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -3070,6 +3072,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property SocketBindDeny is not documented!-->
 
+    <!--property RestrictNetworkInterfaces is not documented!-->
+
     <!--property EnvironmentFiles is not documented!-->
 
     <!--property PassEnvironment is not documented!-->
@@ -3638,6 +3642,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@@ -4340,6 +4346,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       readonly a(iiqq) SocketBindAllow = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly a(iiqq) SocketBindDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (bas) RestrictNetworkInterfaces = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly as Environment = ['...', ...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -4910,6 +4918,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property SocketBindDeny is not documented!-->
 
+    <!--property RestrictNetworkInterfaces is not documented!-->
+
     <!--property EnvironmentFiles is not documented!-->
 
     <!--property PassEnvironment is not documented!-->
@@ -5476,6 +5486,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@@ -6075,6 +6087,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       readonly a(iiqq) SocketBindAllow = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly a(iiqq) SocketBindDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (bas) RestrictNetworkInterfaces = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly as Environment = ['...', ...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -6573,6 +6587,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property SocketBindDeny is not documented!-->
 
+    <!--property RestrictNetworkInterfaces is not documented!-->
+
     <!--property EnvironmentFiles is not documented!-->
 
     <!--property PassEnvironment is not documented!-->
@@ -7057,6 +7073,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@@ -7777,6 +7795,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       readonly a(iiqq) SocketBindAllow = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly a(iiqq) SocketBindDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (bas) RestrictNetworkInterfaces = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly as Environment = ['...', ...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -8261,6 +8281,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property SocketBindDeny is not documented!-->
 
+    <!--property RestrictNetworkInterfaces is not documented!-->
+
     <!--property EnvironmentFiles is not documented!-->
 
     <!--property PassEnvironment is not documented!-->
@@ -8731,6 +8753,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@@ -9304,6 +9328,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
       readonly a(iiqq) SocketBindAllow = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly a(iiqq) SocketBindDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (bas) RestrictNetworkInterfaces = ...;
   };
   interface org.freedesktop.DBus.Peer { ... };
   interface org.freedesktop.DBus.Introspectable { ... };
@@ -9448,6 +9474,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <!--property SocketBindDeny is not documented!-->
 
+    <!--property RestrictNetworkInterfaces is not documented!-->
+
     <!--Autogenerated cross-references for systemd.directives, do not edit-->
 
     <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
@@ -9598,6 +9626,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
     <!--End of Autogenerated section-->
 
     <refsect2>
@@ -9767,6 +9797,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
       readonly a(iiqq) SocketBindAllow = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly a(iiqq) SocketBindDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (bas) RestrictNetworkInterfaces = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s KillMode = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -9927,6 +9959,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <!--property SocketBindDeny is not documented!-->
 
+    <!--property RestrictNetworkInterfaces is not documented!-->
+
     <!--property KillMode is not documented!-->
 
     <!--property KillSignal is not documented!-->
@@ -10103,6 +10137,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
index fb24eda18252c94102eeca6bdde5f3c433149ff5..6be96cf8bc7a8d5b2d8384a9ac6fd2c2ee9339fa 100644 (file)
@@ -17,6 +17,7 @@
   <refnamediv>
     <refname>os-release</refname>
     <refname>initrd-release</refname>
+    <refname>extension-release</refname>
     <refpurpose>Operating system identification</refpurpose>
   </refnamediv>
 
@@ -24,6 +25,7 @@
     <para><filename>/etc/os-release</filename></para>
     <para><filename>/usr/lib/os-release</filename></para>
     <para><filename>/etc/initrd-release</filename></para>
+    <para><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename></para>
   </refsynopsisdiv>
 
   <refsect1>
       above) work correctly. The rest of this document that talks about <filename>os-release</filename>
       should be understood to apply to <filename>initrd-release</filename> too.</para>
     </refsect2>
+
+    <refsect2>
+      <title><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename></title>
+
+      <para><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename>
+      for extension images plays the same role as <filename>os-release</filename> in the main system, and follows the
+      same syntax and rules as described in the <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services Documentation</ulink>.
+      The purpose of this file is to allow the operating system to correctly match an extension image
+      to a base OS image, This is typically implemented by first checking that the <varname>ID=</varname>
+      options match, and if they do either <varname>SYSEXT_LEVEL=</varname> has to match too (preferred), or
+      as a fallback if that is not present <varname>VERSION_ID=</varname> is checked. This ensures that ABI/API
+      between the layers matches and no incompatible images are merged in an overlay.
+      It is preferred that the <filename>extension-release.<replaceable>IMAGE</replaceable></filename> filename is suffixed
+      with the exact file name of the image that contains it, so that all such files in every layer of an overlay are visible.
+      But for the purpose of parsing metadata, in case it is not possible to guarantee that an image file name is stable
+      and doesn't change between the build and the deployment phases, the first and only file which name starts with
+      <filename>extension-release.</filename>, is located in the same directory and is tagged with a
+      <varname>user.extension-release.strict</varname> <citerefentry><refentrytitle>xattr</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+      set to the string <literal>0</literal>, will be parsed instead, if the one with the expected name cannot be found.
+      The rest of this document that talks about <filename>os-release</filename> should be understood to apply to
+      <filename>extension-release</filename> too.</para>
+    </refsect2>
   </refsect1>
 
   <refsect1>
 
           <listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 0–9,
           a–z, ".", "_" and "-") identifying the operating system extensions support level, to indicate which
-          extension images are supported. See:
+          extension images are supported. See <filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename>,
+          <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/initrd.html">initrd</ulink> and
           <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
           for more information.</para>
 
@@ -436,6 +461,13 @@ VARIANT="Workstation Edition"
 VARIANT_ID=workstation</programlisting>
     </example>
 
+    <example>
+      <title><filename>extension-release</filename> file for an extension for Fedora Workstation 32</title>
+
+      <programlisting>ID=fedora
+VERSION_ID=32</programlisting>
+    </example>
+
     <example>
       <title>Reading <filename>os-release</filename> in
       <citerefentry><refentrytitle>sh</refentrytitle><manvolnum>1</manvolnum></citerefentry></title>
index 24174213e2831a4c74f9339087db7679b87b1a31..5e3761ac50e4dddfdca43327633d7802a495d15a 100644 (file)
@@ -33,7 +33,7 @@
     and hence the systemd control group hierarchy.</para>
 
     <para>The module also applies various resource management and runtime parameters to the new session, as
-    configured in the <ulink url="https://systemd.io/USER_RECORD">JSON User Record</ulink> of the user, when
+    configured in the <ulink url="https://systemd.io/USER_RECORD">JSON User Records</ulink> of the user, when
     one is defined.</para>
 
     <para>On login, this module — in conjunction with <filename>systemd-logind.service</filename> — ensures the
index d798219d459f6661f85490a5fac23bb2008bcdcc..c5404db0bae59d6de07b4a47b978433beb69bca6 100644 (file)
         top of <replaceable>IMAGE</replaceable> when attaching/detaching. This argument can be specified
         multiple times, in which case the order in which images are laid down follows the rules specified in
         <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        for the <varname>ExtensionImages=</varname> directive.</para>
+        for the <varname>ExtensionImages=</varname> directive. The image(s) must contain an
+        <filename>extension-release</filename> file with metadata that matches what is defined in the
+        <filename>os-release</filename> of <replaceable>IMAGE</replaceable>. See:
+        <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+        </para>
 
         <para>Note that the same extensions have to be specified, in the same order, when attaching
         and detaching.</para></listitem>
index 0305e255c77141f407e34dd368ecbf3489dd35ea..d3059e9ec60d3cef9b4d01e6a7039fd97349f4a2 100644 (file)
         <para>If the special value <literal>auto</literal> is specified, the source to copy from is
         automatically picked up from the running system (or the image specified with
         <option>--image=</option> — if used). A partition that matches both the configured partition type (as
-        declared with <varname>Type=</varname> above), and the currently mounted directory appropriate for
-        that partition type is determined. For example, if the partition type is set to
+        declared with <varname>Type=</varname> described above), and the currently mounted directory
+        appropriate for that partition type is determined. For example, if the partition type is set to
         <literal>root</literal> the partition backing the root directory (<filename>/</filename>) is used as
         source to copy from — if its partition type is set to <literal>root</literal> as well. If the
         declared type is <literal>usr</literal> the partition backing <filename>/usr/</filename> is used as
       <varlistentry>
         <term><varname>MakeDirectories=</varname></term>
 
-        <listitem><para>akes one or more absolute paths, separated by whitespace, each declaring a directory
+        <listitem><para>Takes one or more absolute paths, separated by whitespace, each declaring a directory
         to create within the new file system. Behaviour is similar to <varname>CopyFiles=</varname>, but
         instead of copying in a set of files this just creates the specified directories with the default
         mode of 0755 owned by the root user and group, plus all their parent directories (with the same
         are copied in or the file system configured with <varname>Format=</varname> is created.</para>
 
         <para>The LUKS2 UUID is automatically derived from the partition UUID in a stable fashion. If
-        <literal>key-file</literal> or <literal>key-file+tpm2</literal> is used a key is added to the LUKS2
-        superblock, configurable with the <option>--key-file=</option> switch to
+        <literal>key-file</literal> or <literal>key-file+tpm2</literal> is used, a key is added to the LUKS2
+        superblock, configurable with the <option>--key-file=</option> option to
         <command>systemd-repart</command>. If <literal>tpm2</literal> or <literal>key-file+tpm2</literal> is
-        used a key is added to the LUKS2 superblock that is enrolled to the local TPM2 chip, as configured
+        used, a key is added to the LUKS2 superblock that is enrolled to the local TPM2 chip, as configured
         with the <option>--tpm2-device=</option> and <option>--tpm2-pcrs=</option> options to
         <command>systemd-repart</command>.</para>
 
         has no effect on explicit mounts, such as those done via <citerefentry
         project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
         <citerefentry
-        project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry></para>
+        project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
 
         <para>If both bit 50 and 59 are set for a partition (i.e. the partition is marked both read-only and
         marked for file system growing) the latter is typically without effect: the read-only flag takes
index 6dea1b76fbc3e54caf3ab941a5ac5014e25f60d0..c6f343d8e5a78d81aa9dfa14d3a50014224f9f0f 100644 (file)
@@ -61,7 +61,7 @@ manpages = [
  ['org.freedesktop.resolve1', '5', [], 'ENABLE_RESOLVE'],
  ['org.freedesktop.systemd1', '5', [], ''],
  ['org.freedesktop.timedate1', '5', [], 'ENABLE_TIMEDATED'],
- ['os-release', '5', ['initrd-release'], ''],
+ ['os-release', '5', ['extension-release', 'initrd-release'], ''],
  ['pam_systemd', '8', [], 'HAVE_PAM'],
  ['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'],
  ['portablectl', '1', [], 'ENABLE_PORTABLED'],
@@ -633,7 +633,10 @@ manpages = [
    'sd_id128_get_machine_app_specific'],
   ''],
  ['sd_id128_randomize', '3', [], ''],
- ['sd_id128_to_string', '3', ['sd_id128_from_string'], ''],
+ ['sd_id128_to_string',
+  '3',
+  ['SD_ID128_STRING_MAX', 'SD_ID128_TO_STRING', 'sd_id128_from_string'],
+  ''],
  ['sd_is_fifo',
   '3',
   ['sd_is_mq',
index 891a4585c619e92bc0d0843feda5c6d640f7f8fc..b6e50a559e97b5e13274430cd9acd95bd642be41 100644 (file)
     <programlisting>#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)</programlisting>
 
     <para><constant>SD_ID128_NULL</constant> may be used to refer to the 128-bit ID consisting of only
-    <constant>NUL</constant> bytes.</para>
+    <constant>NUL</constant> bytes (i.e. all bits off).</para>
+
+    <para><constant>SD_ID128_ALLF</constant> may be used to refer to the 128-bit ID consisting of only
+    <constant>0xFF</constant> bytes (i.e. all bits on).</para>
 
     <para><function>SD_ID128_MAKE_STR()</function> is similar to <function>SD_ID128_MAKE()</function>, but creates a
     <type>const char*</type> expression that can be conveniently used in message formats and such:</para>
index 52fa089ec35c9e3d92e5d1b3eae7a0d1b16505a3..2df3547d40783cc8f939f253dc63dff0efda7cb2 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_id128_randomize()</function> generates a new
-    randomized 128-bit ID and returns it in
-    <parameter>ret</parameter>. Every invocation returns a new
-    randomly generated ID. This uses the
-    <filename>/dev/urandom</filename> kernel random number
-    generator.</para>
+    <para><function>sd_id128_randomize()</function> generates a new randomized 128-bit ID and returns it in
+    <parameter>ret</parameter>. Every invocation returns a new randomly generated ID. This uses the
+    <citerefentry
+    project='man-pages'><refentrytitle>getrandom</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    kernel random number generator.</para>
 
     <para>Note that <function>sd_id128_randomize()</function> always returns a UUID Variant 1 Version 4
     compatible ID. It is hence guaranteed that this function will never return the ID consisting of all zero
@@ -72,7 +71,8 @@
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>getrandom</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_id128_get_machine</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
index 469768050bdae74a2837b1962301d03d83f022e1..db64bc018d1b3a2b225e097df60b33129b51af2f 100644 (file)
@@ -17,7 +17,9 @@
 
   <refnamediv>
     <refname>sd_id128_to_string</refname>
+    <refname>SD_ID128_TO_STRING</refname>
     <refname>sd_id128_from_string</refname>
+    <refname>SD_ID128_STRING_MAX</refname>
     <refpurpose>Format or parse 128-bit IDs as strings</refpurpose>
   </refnamediv>
 
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-id128.h&gt;</funcsynopsisinfo>
 
+      <funcsynopsisinfo>#define SD_ID128_STRING_MAX 33U</funcsynopsisinfo>
+
+      <funcsynopsisinfo>#define SD_ID128_TO_STRING(id) …</funcsynopsisinfo>
+
       <funcprototype>
         <funcdef>char *<function>sd_id128_to_string</function></funcdef>
-        <paramdef>sd_id128_t <parameter>id</parameter>, char <parameter>s</parameter>[33]</paramdef>
+        <paramdef>sd_id128_t <parameter>id</parameter>, char <parameter>s</parameter>[static SD_ID128_STRING_MAX]</paramdef>
       </funcprototype>
 
       <funcprototype>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_id128_to_string()</function> formats a 128-bit
-    ID as a character string. It expects the ID and a string array
-    capable of storing 33 characters. The ID will be formatted as 32
-    lowercase hexadecimal digits and be terminated by a
-    <constant>NUL</constant> byte.</para>
+    <para><function>sd_id128_to_string()</function> formats a 128-bit ID as a character string. It expects
+    the ID and a string array capable of storing 33 characters
+    (<constant>SD_ID128_STRING_MAX</constant>). The ID will be formatted as 32 lowercase hexadecimal digits
+    and be terminated by a <constant>NUL</constant> byte.</para>
+
+    <para><function>SD_ID128_TO_STRING()</function> is a macro that wraps
+    <function>sd_id128_to_string()</function> and passes an appropriately sized buffer as second argument,
+    allocated as C99 compound literal. Each use will thus implicitly acquire a suitable buffer on the stack
+    which remains valid until the end of the current code block. This is usually the simplest way to acquire
+    a string representation of a 128-bit ID in a buffer that is valid in the current code block.</para>
 
-    <para><function>sd_id128_from_string()</function> implements the reverse operation: it takes a 33 character string
-    with 32 hexadecimal digits (either lowercase or uppercase, terminated by <constant>NUL</constant>) and parses them
-    back into a 128-bit ID returned in <parameter>ret</parameter>. Alternatively, this call can also parse a
-    37-character string with a 128-bit ID formatted as RFC UUID. If <parameter>ret</parameter> is passed as
-    <constant>NULL</constant> the function will validate the passed ID string, but not actually return it in parsed
-    form.</para>
+    <para><function>sd_id128_from_string()</function> implements the reverse operation: it takes a 33
+    character string with 32 hexadecimal digits (either lowercase or uppercase, terminated by
+    <constant>NUL</constant>) and parses them back into a 128-bit ID returned in
+    <parameter>ret</parameter>. Alternatively, this call can also parse a 37-character string with a 128-bit
+    ID formatted as RFC UUID. If <parameter>ret</parameter> is passed as <constant>NULL</constant> the
+    function will validate the passed ID string, but not actually return it in parsed form.</para>
 
     <para>Note that when parsing 37 character UUIDs this is done strictly in Big Endian byte order,
-    i.e. according to <ulink url="https://tools.ietf.org/html/rfc4122">RFC4122</ulink> Variant 1
-    rules, even if the UUID encodes a different variant. This matches behaviour in various other Linux
-    userspace tools. It's probably wise to avoid UUIDs of other variant types.</para>
-
-    <para>For more information about the <literal>sd_id128_t</literal>
-    type see
-    <citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
-    Note that these calls operate the same way on all architectures,
-    i.e. the results do not depend on endianness.</para>
-
-    <para>When formatting a 128-bit ID into a string, it is often
-    easier to use a format string for
-    <citerefentry project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
-    This is easily done using the
-    <constant>SD_ID128_FORMAT_STR</constant> and <function>SD_ID128_FORMAT_VAL()</function> macros. For
-    more information see
+    i.e. according to <ulink url="https://tools.ietf.org/html/rfc4122">RFC4122</ulink> Variant 1 rules, even
+    if the UUID encodes a different variant. This matches behaviour in various other Linux userspace
+    tools. It's probably wise to avoid UUIDs of other variant types.</para>
+
+    <para>For more information about the <literal>sd_id128_t</literal> type see
+    <citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>.  Note that
+    these calls operate the same way on all architectures, i.e. the results do not depend on
+    endianness.</para>
+
+    <para>When formatting a 128-bit ID into a string, it is often easier to use a format string for
+    <citerefentry
+    project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>.  This
+    is easily done using the <constant>SD_ID128_FORMAT_STR</constant> and
+    <function>SD_ID128_FORMAT_VAL()</function> macros. For more information see
     <citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para><function>sd_id128_to_string()</function> always succeeds
-    and returns a pointer to the string array passed in.
-    <function>sd_id128_from_string()</function> returns 0 on success, in
-    which case <parameter>ret</parameter> is filled in, or a negative
-    errno-style error code.</para>
+    <para><function>sd_id128_to_string()</function> always succeeds and returns a pointer to the string array
+    passed in.  <function>sd_id128_from_string()</function> returns 0 on success, in which case
+    <parameter>ret</parameter> is filled in, or a negative errno-style error code.</para>
   </refsect1>
 
   <xi:include href="libsystemd-pkgconfig.xml" />
index 8402b9508a0e2eeb658ec2f67bca60bc03923ab5..5a133dea75b158cf621f45126c713a4042e32000 100644 (file)
@@ -552,38 +552,62 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         </varlistentry>
 
         <varlistentry>
-          <term><command>bind</command> <replaceable>UNIT</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
-
-          <listitem><para>Bind mounts a file or directory from the host into the specified unit's view. The first path
-          argument is the source file or directory on the host, the second path argument is the destination file or
-          directory in the unit's view. When the latter is omitted, the destination path in the unit's view is the same as
-          the source path on the host. When combined with the <option>--read-only</option> switch, a ready-only bind
-          mount is created. When combined with the <option>--mkdir</option> switch, the destination path is first created
-          before the mount is applied. Note that this option is currently only supported for units that run within a mount
-          namespace (e.g.: with <option>RootImage=</option>, <option>PrivateMounts=</option>, etc.). This command supports bind
-          mounting directories, regular files, device nodes, <constant>AF_UNIX</constant> socket nodes, as well as FIFOs.
-          The bind mount is ephemeral, and it is undone as soon as the current unit process exists.
-          Note that the namespace mentioned here, where the bind mount will be added to, is the one where the main service
-          process runs, as other processes run in distinct namespaces (e.g.: <option>ExecReload=</option>,
-          <option>ExecStartPre=</option>, etc.) </para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><command>mount-image</command> <replaceable>UNIT</replaceable> <replaceable>IMAGE</replaceable> [<replaceable>PATH</replaceable> [<replaceable>PARTITION_NAME</replaceable>:<replaceable>MOUNT_OPTIONS</replaceable>]]</term>
-
-          <listitem><para>Mounts an image from the host into the specified unit's view. The first path argument is the source
-          image on the host, the second path argument is the destination directory in the unit's view (ie: inside
-          <option>RootImage=</option>/<option>RootDirectory=</option>). Any following argument is interpreted as a
-          colon-separated tuple of partition name and comma-separated list of mount options for that partition. The format is the
-          same as the service <option>MountImages=</option> setting. When combined with the <option>--read-only</option> switch, a
-          ready-only mount is created. When combined with the <option>--mkdir</option> switch, the destination path is first
-          created before the mount is applied. Note that this option is currently only supported for units that run within a mount
-          namespace (e.g.: with <option>RootImage=</option>, <option>PrivateMounts=</option>, etc.).
-          Note that the namespace mentioned here, where the image mount will be added to, is the one where the main service
-          process runs, as other processes run in distinct namespaces (e.g.: <option>ExecReload=</option>,
-          <option>ExecStartPre=</option>, etc.). Example:
+          <term>
+            <command>bind</command>
+            <replaceable>UNIT</replaceable>
+            <replaceable>PATH</replaceable>
+            [<replaceable>PATH</replaceable>]
+          </term>
+
+          <listitem><para>Bind-mounts a file or directory from the host into the specified unit's mount
+          namespace. The first path argument is the source file or directory on the host, the second path
+          argument is the destination file or directory in the unit's mount namespace. When the latter is
+          omitted, the destination path in the unit's mount namespace is the same as the source path on the
+          host. When combined with the <option>--read-only</option> switch, a ready-only bind mount is
+          created. When combined with the <option>--mkdir</option> switch, the destination path is first
+          created before the mount is applied.</para>
+
+          <para>Note that this option is currently only supported for units that run within a mount namespace
+          (e.g.: with <option>RootImage=</option>, <option>PrivateMounts=</option>, etc.). This command
+          supports bind-mounting directories, regular files, device nodes, <constant>AF_UNIX</constant>
+          socket nodes, as well as FIFOs.  The bind mount is ephemeral, and it is undone as soon as the
+          current unit process exists. Note that the namespace mentioned here, where the bind mount will be
+          added to, is the one where the main service process runs. Other processes (those exececuted by
+          <option>ExecReload=</option>, <option>ExecStartPre=</option>, etc.) run in distinct namespaces.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>
+            <command>mount-image</command>
+            <replaceable>UNIT</replaceable>
+            <replaceable>IMAGE</replaceable>
+            [<replaceable>PATH</replaceable>
+            [<replaceable>PARTITION_NAME</replaceable>:<replaceable>MOUNT_OPTIONS</replaceable>]]
+          </term>
+
+          <listitem><para>Mounts an image from the host into the specified unit's mount namespace. The first
+          path argument is the source image on the host, the second path argument is the destination
+          directory in the unit's mount namespace (i.e. inside
+          <option>RootImage=</option>/<option>RootDirectory=</option>). The following argument, if any, is
+          interpreted as a colon-separated tuple of partition name and comma-separated list of mount options
+          for that partition. The format is the same as the service <option>MountImages=</option>
+          setting. When combined with the <option>--read-only</option> switch, a ready-only mount is
+          created. When combined with the <option>--mkdir</option> switch, the destination path is first
+          created before the mount is applied.</para>
+
+          <para>Note that this option is currently only supported for units that run within a mount namespace
+          (i.e. with <option>RootImage=</option>, <option>PrivateMounts=</option>, etc.). Note that the
+          namespace mentioned here where the image mount will be added to, is the one where the main service
+          process runs. Note that the namespace mentioned here, where the bind mount will be
+          added to, is the one where the main service process runs. Other processes (those exececuted by
+          <option>ExecReload=</option>, <option>ExecStartPre=</option>, etc.) run in distinct namespaces.
+          </para>
+
+          <para>Example:
           <programlisting>systemctl mount-image foo.service /tmp/img.raw /var/lib/image root:ro,nosuid</programlisting>
-          <programlisting>systemctl mount-image --mkdir bar.service /tmp/img.raw /var/lib/baz/img</programlisting></para></listitem>
+          <programlisting>systemctl mount-image --mkdir bar.service /tmp/img.raw /var/lib/baz/img</programlisting>
+          </para></listitem>
         </varlistentry>
 
         <varlistentry>
@@ -2185,9 +2209,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <term><option>--lines=</option></term>
 
         <listitem>
-          <para>When used with <command>status</command>, controls the number of journal lines to show, counting from
-          the most recent ones. Takes a positive integer argument, or 0 to disable journal output. Defaults to
-          10.</para>
+          <para>When used with <command>status</command>, controls the number of journal lines to show,
+          counting from the most recent ones. Takes a positive integer argument, or 0 to disable journal
+          output. Defaults to 10.</para>
         </listitem>
       </varlistentry>
 
@@ -2208,8 +2232,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <term><option>--firmware-setup</option></term>
 
         <listitem>
-          <para>When used with the <command>reboot</command> command, indicate to the system's firmware to reboot into
-          the firmware setup interface. Note that this functionality is not available on all systems.</para>
+          <para>When used with the <command>reboot</command> command, indicate to the system's firmware to
+          reboot into the firmware setup interface. Note that this functionality is not available on all
+          systems.</para>
         </listitem>
       </varlistentry>
 
@@ -2217,10 +2242,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <term><option>--boot-loader-menu=</option></term>
 
         <listitem>
-          <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to show the
-          boot loader menu on the following boot. Takes a time value as parameter — indicating the menu timeout. Pass
-          zero in order to disable the menu timeout. Note that not all boot loaders support this
-          functionality.</para>
+          <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to
+          show the boot loader menu on the following boot. Takes a time value as parameter — indicating the
+          menu timeout. Pass zero in order to disable the menu timeout. Note that not all boot loaders
+          support this functionality.</para>
         </listitem>
       </varlistentry>
 
@@ -2228,10 +2253,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <term><option>--boot-loader-entry=</option></term>
 
         <listitem>
-          <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to boot into
-          a specific boot loader entry on the following boot. Takes a boot loader entry identifier as argument, or
-          <literal>help</literal> in order to list available entries. Note that not all boot loaders support this
-          functionality.</para>
+          <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to
+          boot into a specific boot loader entry on the following boot. Takes a boot loader entry identifier
+          as argument, or <literal>help</literal> in order to list available entries. Note that not all boot
+          loaders support this functionality.</para>
         </listitem>
       </varlistentry>
 
index 4da066e05c26854a032e0b0e5b0fcccc4a9f2380..a2f915479149c1daeeda04445ce6f542d1f69a95 100644 (file)
@@ -744,11 +744,52 @@ Service b@0.service not loaded, b.socket cannot be started.
         generators enabled will generally result in some warnings.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--recursive-errors=<replaceable>MODE</replaceable></option></term>
+
+        <listitem><para>Control verification of units and their dependencies and whether
+        <command>systemd-analyze verify</command> exits with a non-zero process exit status or not. With
+        <command>yes</command>, return a non-zero process exit status when warnings arise during verification
+        of either the specified unit or any of its associated dependencies. This is the default. With
+        <command>no</command>, return a non-zero process exit status when warnings arise during verification
+        of only the specified unit. With <command>one</command>, return a non-zero process exit status when
+        warnings arise during verification of either the specified unit or its immediate dependencies. </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--root=<replaceable>PATH</replaceable></option></term>
 
-        <listitem><para>With <command>cat-files</command>, show config files underneath
-        the specified root path <replaceable>PATH</replaceable>.</para></listitem>
+        <listitem><para>With <command>cat-files</command> and <command>verify</command>,
+        operate on files underneath the specified root path <replaceable>PATH</replaceable>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--image=<replaceable>PATH</replaceable></option></term>
+
+        <listitem><para>With <command>cat-files</command> and <command>verify</command>,
+        operate on files inside the specified image path <replaceable>PATH</replaceable>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--offline=<replaceable>BOOL</replaceable></option></term>
+
+        <listitem><para>With <command>security</command>, perform an offline security review
+        of the specified unit file(s), i.e. does not have to rely on PID 1 to acquire security
+        information for the files like the <command>security</command> verb when used by itself does.
+        This means that <option>--offline=</option> can be used with <option>--root=</option> and
+        <option>--image=</option> as well. If a unit's overall exposure level is above that set by
+        <option>--threshold=</option> (default value is 100), <option>--offline=</option> will return
+        an error.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--threshold=<replaceable>NUMBER</replaceable></option></term>
+
+        <listitem><para>With <command>security</command>, allow the user to set a custom value
+        to compare the overall exposure level with, for the specified unit file(s). If a unit's
+        overall exposure level, is greater than that set by the user, <command>security</command>
+        will return an error. <option>--threshold=</option> can be used with <option>--offline=</option>
+        as well and its default value is 100.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 5169bbbd0f60d4e3019b1ffc4ed9ca9b02d7d8c7..d3306593e413f86886421f71f9284dafb8cfcdad 100644 (file)
     <title>Key bindings</title>
     <para>The following keys may be used in the boot menu:</para>
 
+    <!-- Developer commands Q/v/Ctrl+l deliberately not advertised. -->
+
     <variablelist>
       <varlistentry>
         <term><keycap>↑</keycap> (Up)</term>
       </varlistentry>
 
       <varlistentry>
-        <term><keycap>v</keycap></term>
-        <listitem><para>Show systemd-boot, UEFI, and firmware versions</para></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><keycap>P</keycap></term>
+        <term><keycap>p</keycap></term>
         <listitem><para>Print status</para></listitem>
       </varlistentry>
 
-      <varlistentry>
-        <term><keycap>Q</keycap></term>
-        <listitem><para>Quit</para></listitem>
-      </varlistentry>
-
       <varlistentry>
         <term><keycap>h</keycap></term>
         <term><keycap>?</keycap></term>
         <term><keycap>F1</keycap></term>
         <listitem><para>Show a help screen</para></listitem>
       </varlistentry>
-
-      <varlistentry>
-        <term><keycombo><keycap>Ctrl</keycap><keycap>l</keycap></keycombo></term>
-        <listitem><para>Reprint the screen</para></listitem>
-      </varlistentry>
     </variablelist>
 
     <para>The following keys may be pressed during bootup or in the boot menu to directly boot a specific
index d5719bc463586c8ad2ffc479382a80c7e66f97e0..c6d6c5bd6ca30ea8f87aea0b011dd2dabc527021 100644 (file)
@@ -84,7 +84,7 @@
         <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
 
         <listitem><para>Enroll a PKCS#11 security token or smartcard (e.g. a YubiKey). Expects a PKCS#11
-        smart card URI referring to the token. Alternatively the special value <literal>auto</literal> may
+        smartcard URI referring to the token. Alternatively the special value <literal>auto</literal> may
         be specified, in order to automatically determine the URI of a currently plugged in security token
         (of which there must be exactly one). The special value <literal>list</literal> may be used to
         enumerate all suitable PKCS#11 tokens currently plugged in. The security token must contain an RSA
index 87cf98c31a0142c2d774b6ea575f860ebdb47a89..66d829941b89e7ace7228614b975be1275bb33bf 100644 (file)
       <varlistentry>
         <term><literal>passwd.shell.root</literal></term>
 
-        <listitem><para>Specifies the shell binary to use for the specified account when creating it.
+        <listitem><para>Specifies the shell binary to use for the specified account.
         Equivalent to the credential of the same name defined for the
         <citerefentry><refentrytitle>systemd-sysusers.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         service.</para></listitem>
index a9c52d842c54bb9c16c800a35090832ededa14cb..177fe892a650c89a239118393eb8cebedfd11a59 100644 (file)
@@ -90,7 +90,7 @@
 
       <varlistentry>
         <term><option>--image=<replaceable>path</replaceable></option></term>
-        <listitem><para>Takes a path to a device node or refular file as argument. This is similar to
+        <listitem><para>Takes a path to a device node or regular file as argument. This is similar to
         <option>--root=</option> as described above, but operates on a disk image instead of a directory
         tree.</para></listitem>
       </varlistentry>
index e929d32f62b6a90987acca2817628fafbf1afa74..e84ac6ae42bc1c2e4ca081b64807485656f04115 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
-        <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
-
-        <listitem><para>Specifies an environment variable assignment
-        to pass to the init process in the container, in the format
-        <literal>NAME=VALUE</literal>. This may be used to override
-        the default variables or to set additional variables. This
-        parameter may be used more than once.</para></listitem>
+        <term><option>-E <replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+        <term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+
+        <listitem><para>Specifies an environment variable to pass to the init process in the container. This
+        may be used to override the default variables or to set additional variables. It may be used more
+        than once to set multiple variables. When <literal>=</literal> and <replaceable>VALUE</replaceable>
+        are omitted, the value of the variable with the same name in the program environment will be used.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -1375,12 +1375,12 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
         </orderedlist>
 
         <para>The combination of the three operations above ensures that it is possible to log into the
-        host's user account inside the container as if it was local to the container. The user is only mapped
-        transiently, while the container is running and the mapping itself does not result in persistent
-        changes to the container (except maybe for generated log messages at login time, and similar). Note
-        that in particular the UID/GID assignment in the container is not made persistently. If the user is
-        mapped transiently, it is best to not allow the user to make persistent changes to the container. If
-        the user leaves files or directories owned by the user, and those UIDs/GIDs are recycled during later
+        container using the same account information as on the host. The user is only mapped transiently,
+        while the container is running, and the mapping itself does not result in persistent changes to the
+        container (except maybe for log messages generated at login time, and similar). Note that in
+        particular the UID/GID assignment in the container is not made persistently. If the user is mapped
+        transiently, it is best to not allow the user to make persistent changes to the container. If the
+        user leaves files or directories owned by the user, and those UIDs/GIDs are reused during later
         container invocations (possibly with a different <option>--bind-user=</option> mapping), those files
         and directories will be accessible to the "new" user.</para>
 
@@ -1581,9 +1581,9 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
         -b</programlisting>
 
         <para>The above command line will invoke the specified image file <filename>image.raw</filename> in
-        volatile mode, i.e with an empty <filename>/etc/</filename> and <filename>/var/</filename>, so that
-        the container's payload recognizes this as first boot condition, and will invoke
-        <filename>systemd-firstboot.service</filename>, which then read the two passed credentials to
+        volatile mode, i.e. with empty <filename>/etc/</filename> and <filename>/var/</filename>.  The
+        container payload will recognize this as a first boot, and will invoke
+        <filename>systemd-firstboot.service</filename>, which then reads the two passed credentials to
         configure the system's initial locale and root password.</para>
         </listitem>
         </varlistentry>
index 23906f2663aa56700cf9ef77a8682c321ae41112..61f2c5ca9e59747a4946b48a3cfe75049a23a190 100644 (file)
@@ -35,8 +35,8 @@
     <para>Most of <command>systemd-portabled</command>'s functionality is accessible through the
     <citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> command.</para>
 
-    <para>See the <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable
-    Services Documentation</ulink> for details about the concepts this service implements.</para>
+    <para>See <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> for details about
+    the concepts this service implements.</para>
   </refsect1>
 
   <refsect1>
index 7dbbfa0001fc7e742b5676d2855da834fc861fe9..34c1257ab007778688f303a3f2872ae3b6fbd6d8 100644 (file)
@@ -275,7 +275,7 @@ search foobar.com barbar.com
       fragility in both directions: a valid global name could be obscured by a local name, and resolution of
       a relative local name could suddenly break when a new top-level domain is created, or when a new
       subdomain of a top-level domain in registered. Resolving any given name as either relative or absolute
-      avoids this ambiguity.)</para></footnote></para></listitem>
+      avoids this ambiguity.</para></footnote></para></listitem>
 
       <listitem><para>This resolver has a notion of the special <literal>.local</literal> domain used for
       MulticastDNS, and will not route queries with that suffix to unicast DNS servers unless explicitly
index fc8716ea79d304ce2f0d4e5b655f9d7ab601d866..99726dabfbbc73c0071d5267cc029beb43545b45 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
-        <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
+        <term><option>-E <replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+        <term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
 
-        <listitem><para>Runs the service process with the specified environment variable set.
-        Also see <varname>Environment=</varname> in
+        <listitem><para>Runs the service process with the specified environment variable set. This parameter
+        may be used more than once to set multiple variables. When <literal>=</literal> and
+        <replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
+        program environment will be used.</para>
+
+        <para>Also see <varname>Environment=</varname> in
         <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
         </listitem>
       </varlistentry>
index ad6a401c7a221ab4edd5241e44823c7bce8d5178..f8a9f47539f1c817c50a8546bf66b423774d051b 100644 (file)
@@ -46,7 +46,7 @@
     operating system tree. When one or more system extension images are activated, their
     <filename>/usr/</filename> and <filename>/opt/</filename> hierarchies are combined via
     <literal>overlayfs</literal> with the same hierarchies of the host OS, and the host
-    <filename>/usr/</filename> and <filename>/opt</filename> overmounted with it ("merging"). When they are
+    <filename>/usr/</filename> and <filename>/opt/</filename> overmounted with it ("merging"). When they are
     deactivated, the mount point is disassembled — again revealing the unmodified original host version of
     the hierarchy ("unmerging"). Merging thus makes the extension's resources suddenly appear below the
     <filename>/usr/</filename> and <filename>/opt/</filename> hierarchies as if they were included in the
@@ -71,7 +71,7 @@
     <orderedlist>
       <listitem><para>Plain directories or btrfs subvolumes containing the OS tree</para></listitem>
       <listitem><para>Disk images with a GPT disk label, following the <ulink
-      url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partition Specification</ulink></para></listitem>
+      url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink></para></listitem>
       <listitem><para>Disk images lacking a partition table, with a naked Linux file system (e.g. squashfs or ext4)</para></listitem>
     </orderedlist>
 
     <title>Uses</title>
 
     <para>The primary use case for system images are immutable environments where debugging and development
-    tools shall optionally be made available, but not included in the immutable base OS image itself
-    (e.g. <filename>strace</filename> and <filename>gdb</filename> shall be an optionally installable
-    addition in order to make debugging/development easier). System extension images should not be
-    misunderstood as a generic software packaging framework, as no dependency scheme is available: system
-    extensions should carry all files they need themselves, except for those already shipped in the
-    underlying host system image. Typically, system extension images are built at the same time as the base
-    OS image — within the same build system.</para>
+    tools shall optionally be made available, but not included in the immutable base OS image itself (e.g.
+    <citerefentry project='man-pages'><refentrytitle>strace</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    and
+    <citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    shall be an optionally installable addition in order to make debugging/development easier). System
+    extension images should not be misunderstood as a generic software packaging framework, as no dependency
+    scheme is available: system extensions should carry all files they need themselves, except for those
+    already shipped in the underlying host system image. Typically, system extension images are built at the
+    same time as the base OS image — within the same build system.</para>
 
     <para>Another use case for the system extension concept is temporarily overriding OS supplied resources
     with newer ones, for example to install a locally compiled development version of some low-level
index c11dd461439ae6f9f5f641e32f253322dc4bed0b..5824e01e0c826207d00be9ba999fe2acc5f09111 100644 (file)
         names in status messages (e.g. <literal>systemd-journald.service</literal>), instead of the longer
         and more informative descriptions set with <varname>Description=</varname> (e.g. <literal>Journal
         Logging Service</literal>). If <option>combined</option>, the system manager will use both unit names
-        and descriptions in status messages (e.g. <literal>systemdmd-jouranld.service - Journal Logging
+        and descriptions in status messages (e.g. <literal>systemd-journald.service - Journal Logging
         Service</literal>).</para>
 
         <para>See
index b4500d1448a9c1660241eab6c79c24aab5de4429..e0a89b071221d8af36b630172378fbe97a0960a1 100644 (file)
     for the first time it is possible to configure the root user's password to be <literal>systemd</literal>
     like this:</para>
 
-    <para><programlisting># systemd-nspawn --image=… --set-credential=password.hashed-password.root:'$y$j9T$yAuRJu1o5HioZAGDYPU5d.$F64ni6J2y2nNQve90M/p0ZP0ECP/qqzipNyaY9fjGpC' …</programlisting></para>
+    <para><programlisting># systemd-nspawn --image=… --set-credential=passwd.hashed-password.root:'$y$j9T$yAuRJu1o5HioZAGDYPU5d.$F64ni6J2y2nNQve90M/p0ZP0ECP/qqzipNyaY9fjGpC' …</programlisting></para>
 
     <para>Note again that the data specified in these credentials is consulted only when creating an account
     for the first time, it may not be used for changing the password or shell of an account that already
index 9ab4af9763bef47b5d48aa443ec96b2a3d7278f9..34e0a674720078d1f5cf132573adee8e34008bda 100644 (file)
         <term><filename>/var/lib/systemd/timesync/clock</filename></term>
 
         <listitem>
-          <para>The modification time ("mtime") of this file indicates the timestamp of the last successful
-          synchronization (or at least the systemd build date, in case synchronization was not possible). It
-          is used to ensure that the system clock remains roughly monotonic across reboots, in case no local
-          RTC is available.</para>
+          <para>The modification time ("mtime") of this file is updated on each successful NTP synchronization
+          or after each <varname>SaveIntervalSec=</varname> time interval, as specified in
+          <citerefentry><refentrytitle>timesyncd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          At the minimum, it will be set to the systemd build date. It is used to ensure that the system clock
+          remains roughly monotonic across reboots, in case no local RTC is available.</para>
         </listitem>
       </varlistentry>
 
index fbbc740040fb6c1c18b68f5beda37eb2e41975e7..9d8b62c45248aa0af0eebcb02f65d3c3af1cb26c 100644 (file)
     JSON user/group records from classic UNIX/glibc NSS user/group records in order to provide full backwards
     compatibility. It may also pick up statically defined JSON user/group records from drop-in files in
     <filename>/etc/userdb/</filename>, <filename>/run/userdb/</filename>,
-    <filename>/run/host/userdb/</filename> and <filename>/use/lib/userdb/</filename>.</para>
+    <filename>/run/host/userdb/</filename> and <filename>/usr/lib/userdb/</filename>.</para>
 
     <para>Most of <command>systemd-userdbd</command>'s functionality is accessible through the
     <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     command.</para>
 
     <para>The user and group records this service provides access to follow the <ulink
-    url="https://systemd.io/USER_RECORD">JSON User Record</ulink> and <ulink
+    url="https://systemd.io/USER_RECORD">JSON User Records</ulink> and <ulink
     url="https://systemd.io/GROUP_RECORD">JSON Group Record</ulink> definitions. This service implements the
     <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>, and
     multiplexes access other services implementing this API, too. It is thus both server and client of this
index c9554b087aebb35bfd0a33670f459e36d83c5c93..70f08374e228a963297640ee6c1a04d8c88f7727 100644 (file)
     <para>At early boot and when the system manager configuration is reloaded kernel command line configuration for
     integrity protected block devices is translated into <filename>systemd-veritysetup@.service</filename> units by
     <citerefentry><refentrytitle>systemd-veritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+    <para><filename>systemd-veritysetup@.service</filename> calls <command>systemd-veritysetup</command>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Commands</title>
+
+    <para>The following commands are understood by <command>systemd-veritysetup</command>:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>
+          <option>attach</option>
+          <replaceable>volume</replaceable>
+          <replaceable>datadevice</replaceable>
+          <replaceable>hashdevice</replaceable>
+          <replaceable>roothash</replaceable>
+          [<replaceable>option</replaceable>...]
+        </term>
+
+        <listitem><para>Create a block device <replaceable>volume</replaceable> using
+        <replaceable>datadevice</replaceable> and <replaceable>hashdevice</replaceable> as the backing
+        devices. <replaceable>roothash</replaceable> forms the root of the tree of hashes stored on
+        <replaceable>hashdevice</replaceable>. See
+        <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/verity.html">
+          Kernel dm-verity</ulink> documentation for details.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>detach</option>
+          <replaceable>volume</replaceable>
+        </term>
+
+        <listitem><para>Detach (destroy) the block device
+        <replaceable>volume</replaceable>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>help</option>
+        </term>
+
+        <listitem><para>Print short information about command syntax.</para></listitem>
+      </varlistentry>
+    </variablelist>
   </refsect1>
 
   <refsect1>
index ccb1567900ef290e4c028827ed4501d1bfaf8db6..eadfc024213ca2327b6fb87efc0ca00903eaf4df 100644 (file)
         <para>This option is supported only for disk images that contain a single file system, without an
         enveloping partition table. Images that contain a GPT partition table should instead include both
         root file system and matching Verity data in the same image, implementing the <ulink
-        url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partition Specification</ulink>.</para>
+        url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>.</para>
 
         <xi:include href="system-only.xml" xpointer="singular"/></listitem>
       </varlistentry>
         <term><varname>ExtensionImages=</varname></term>
 
         <listitem><para>This setting is similar to <varname>MountImages=</varname> in that it mounts a file
-        system hierarchy from a block device node or loopback file, but instead of providing a destination path,
-        an overlay will be set up. This option expects a whitespace separated list of mount definitions. Each
-        definition consists of a source path, optionally followed by a colon and a list of mount options.</para>
+        system hierarchy from a block device node or loopback file, but instead of providing a destination
+        path, an overlay will be set up. This option expects a whitespace separated list of mount
+        definitions. Each definition consists of a source path, optionally followed by a colon and a list of
+        mount options.</para>
 
         <para>A read-only OverlayFS will be set up on top of <filename>/usr/</filename> and
-        <filename>/opt/</filename> hierarchies from the root. The order in which the images are listed
-        will determine the order in which the overlay is laid down: images specified first to last will result
-        in overlayfs layers bottom to top.</para>
+        <filename>/opt/</filename> hierarchies. The order in which the images are listed will determine the
+        order in which the overlay is laid down: images specified first to last will result in overlayfs
+        layers bottom to top.</para>
 
         <para>Mount options may be defined as a single comma-separated list of options, in which case they
         will be implicitly applied to the root partition on the image, or a series of colon-separated tuples
         paths. If the empty string is assigned, the entire list of mount paths defined prior to this is
         reset.</para>
 
+        <para>Each image must carry a <filename>/usr/lib/extension-release.d/extension-release.IMAGE</filename>
+        file, with the appropriate metadata which matches <varname>RootImage=</varname>/<varname>RootDirectory=</varname>
+        or the host. See:
+        <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
         <para>When <varname>DevicePolicy=</varname> is set to <literal>closed</literal> or
         <literal>strict</literal>, or set to <literal>auto</literal> and <varname>DeviceAllow=</varname> is
         set, then this setting adds <filename>/dev/loop-control</filename> with <constant>rw</constant> mode,
@@ -2304,7 +2310,7 @@ SystemCallErrorNumber=EPERM</programlisting>
 
         <listitem><para>Sets environment variables for executed processes. Each line is unquoted using the
         rules described in "Quoting" section in
-        <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
         and becomes a list of variable assignments. If you need to assign a value containing spaces or the
         equals sign to a variable, put quotes around the whole assignment. Variable expansion is not
         performed inside the strings and the <literal>$</literal> character has no special meaning. Specifier
index 1093e2e0b84f7c058261e904668fb60e42717a2e..638a1522cd3875fa7ec8539b586e807b5f9baef6 100644 (file)
         <term><varname>MTUBytes=</varname></term>
         <listitem>
           <para>The maximum transmission unit in bytes to set for the
-          device. The usual suffixes K, M, G, are supported and are
+          device. The usual suffixes K, M, G are supported and are
           understood to the base of 1024.</para>
         </listitem>
       </varlistentry>
         <term><varname>BitsPerSecond=</varname></term>
         <listitem>
           <para>The speed to set for the device, the value is rounded
-          down to the nearest Mbps. The usual suffixes K, M, G, are
+          down to the nearest Mbps. The usual suffixes K, M, G are
           supported and are understood to the base of 1000.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>ReceiveChecksumOffload=</varname></term>
         <listitem>
-          <para>Takes a boolean. If set to true, the hardware offload for checksumming of ingress
+          <para>Takes a boolean. If set to true, hardware offload for checksumming of ingress
           network packets is enabled. When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>TransmitChecksumOffload=</varname></term>
         <listitem>
-          <para>Takes a boolean. If set to true, the hardware offload for checksumming of egress
+          <para>Takes a boolean. If set to true, hardware offload for checksumming of egress
           network packets is enabled. When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>TCPSegmentationOffload=</varname></term>
         <listitem>
-          <para>Takes a boolean. If set to true, the TCP Segmentation Offload (TSO) is enabled.
+          <para>Takes a boolean. If set to true, TCP Segmentation Offload (TSO) is enabled.
           When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
        <varlistentry>
       <term><varname>TCP6SegmentationOffload=</varname></term>
         <listitem>
-          <para>Takes a boolean. If set to true, the TCP6 Segmentation Offload (tx-tcp6-segmentation) is enabled.
+          <para>Takes a boolean. If set to true, TCP6 Segmentation Offload (tx-tcp6-segmentation) is enabled.
           When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>GenericSegmentationOffload=</varname></term>
         <listitem>
-          <para>Takes a boolean. If set to true, the Generic Segmentation Offload (GSO) is enabled.
+          <para>Takes a boolean. If set to true, Generic Segmentation Offload (GSO) is enabled.
           When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>GenericReceiveOffload=</varname></term>
         <listitem>
-          <para>Takes a boolean. If set to true, the Generic Receive Offload (GRO) is enabled.
+          <para>Takes a boolean. If set to true, Generic Receive Offload (GRO) is enabled.
           When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>GenericReceiveOffloadHardware=</varname></term>
+        <listitem>
+          <para>Takes a boolean. If set to true, hardware accelerated Generic Receive Offload (GRO) is
+          enabled. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>LargeReceiveOffload=</varname></term>
         <listitem>
-          <para>Takes a boolean. If set to true, the Large Receive Offload (LRO) is enabled.
+          <para>Takes a boolean. If set to true, Large Receive Offload (LRO) is enabled.
           When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>RxChannels=</varname></term>
+        <term><varname>TxChannels=</varname></term>
+        <term><varname>OtherChannels=</varname></term>
+        <term><varname>CombinedChannels=</varname></term>
         <listitem>
-          <para>Sets the number of receive channels (a number between 1 and 4294967295) .</para>
+          <para>Specifies the number of receive, transmit, other, or combined channels, respectively.
+          Takes an unsigned integer in the range 1…4294967295 or <literal>max</literal>. If set to
+          <literal>max</literal>, the advertised maximum value of the hardware will be used. When
+          unset, the number will not be changed. Defaults to unset.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>TxChannels=</varname></term>
+        <term><varname>RxBufferSize=</varname></term>
+        <term><varname>RxMiniBufferSize=</varname></term>
+        <term><varname>RxJumboBufferSize=</varname></term>
+        <term><varname>TxBufferSize=</varname></term>
         <listitem>
-          <para>Sets the number of transmit channels (a number between 1 and 4294967295).</para>
+          <para>Specifies the maximum number of pending packets in the NIC receive buffer, mini receive
+          buffer, jumbo receive buffer, or transmit buffer, respectively. Takes an unsigned integer in
+          the range 1…4294967295 or <literal>max</literal>. If set to <literal>max</literal>, the
+          advertised maximum value of the hardware will be used. When unset, the number will not be
+          changed. Defaults to unset.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>OtherChannels=</varname></term>
+        <term><varname>RxFlowControl=</varname></term>
         <listitem>
-          <para>Sets the number of other channels (a number between 1 and 4294967295).</para>
+          <para>Takes a boolean. When set, enables receive flow control, also known as the ethernet
+          receive PAUSE message (generate and send ethernet PAUSE frames). When unset, the kernel's
+          default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>CombinedChannels=</varname></term>
+        <term><varname>TxFlowControl=</varname></term>
         <listitem>
-          <para>Sets the number of combined set channels (a number between 1 and 4294967295).</para>
+          <para>Takes a boolean. When set, enables transmit flow control, also known as the ethernet
+          transmit PAUSE message (respond to received ethernet PAUSE frames). When unset, the kernel's
+          default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>RxBufferSize=</varname></term>
+        <term><varname>AutoNegotiationFlowControl=</varname></term>
         <listitem>
-          <para>Takes an integer. Specifies the maximum number of pending packets in the NIC receive buffer.
-          When unset, the kernel's default will be used.</para>
+          <para>Takes a boolean. When set, auto negotiation enables the interface to exchange state
+          advertisements with the connected peer so that the two devices can agree on the ethernet
+          PAUSE configuration. When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>RxMiniBufferSize=</varname></term>
+        <term><varname>GenericSegmentOffloadMaxBytes=</varname></term>
         <listitem>
-          <para>Takes an integer. Specifies the maximum number of pending packets in the NIC mini receive buffer.
-          When unset, the kernel's default will be used.</para>
+          <para>Specifies the maximum size of a Generic Segment Offload (GSO) packet the
+          device should accept. The usual suffixes K, M, G are supported and are
+          understood to the base of 1024. An unsigned integer in the range 1…65536.
+          Defaults to unset.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>RxJumboBufferSize=</varname></term>
+        <term><varname>GenericSegmentOffloadMaxSegments=</varname></term>
         <listitem>
-          <para>Takes an integer. Specifies the maximum number of pending packets in the NIC jumbo receive buffer.
-          When unset, the kernel's default will be used.</para>
+          <para>Specifies the maximum number of Generic Segment Offload (GSO) segments the device should
+          accept. An unsigned integer in the range 1…65535. Defaults to unset.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>TxBufferSize=</varname></term>
+        <term><varname>UseAdaptiveRxCoalesce=</varname></term>
+        <term><varname>UseAdaptiveTxCoalesce=</varname></term>
         <listitem>
-          <para>Takes an integer. Specifies the maximum number of pending packets in the NIC transmit buffer.
-          When unset, the kernel's default will be used.</para>
+          <para>Boolean properties that, when set, enable/disable adaptive Rx/Tx coalescing if the hardware
+          supports it. When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>RxFlowControl=</varname></term>
+        <term><varname>RxCoalesceSec=</varname></term>
+        <term><varname>RxCoalesceIrqSec=</varname></term>
+        <term><varname>RxCoalesceLowSec=</varname></term>
+        <term><varname>RxCoalesceHighSec=</varname></term>
+        <term><varname>TxCoalesceSec=</varname></term>
+        <term><varname>TxCoalesceIrqSec=</varname></term>
+        <term><varname>TxCoalesceLowSec=</varname></term>
+        <term><varname>TxCoalesceHighSec=</varname></term>
         <listitem>
-          <para>Takes a boolean. When set, enables the receive flow control, also known as the ethernet
-          receive PAUSE message (generate and send ethernet PAUSE frames). When unset, the kernel's
-          default will be used.</para>
+          <para>These properties configure the delay before Rx/Tx interrupts are generated after a packet is
+          sent/received. The <literal>Irq</literal> properties come into effect when the host is servicing an
+          IRQ. The <literal>Low</literal> and <literal>High</literal> properties come into effect when the
+          packet rate drops below the low packet rate threshold or exceeds the high packet rate threshold
+          respectively if adaptive Rx/Tx coalescing is enabled. When unset, the kernel's defaults will be
+          used.</para>
         </listitem>
       </varlistentry>
-      <varlistentry>
-        <term><varname>TxFlowControl=</varname></term>
+        <varlistentry>
+        <term><varname>RxMaxCoalescedFrames=</varname></term>
+        <term><varname>RxMaxCoalescedIrqFrames=</varname></term>
+        <term><varname>RxMaxCoalescedLowFrames=</varname></term>
+        <term><varname>RxMaxCoalescedHighFrames=</varname></term>
+        <term><varname>TxMaxCoalescedFrames=</varname></term>
+        <term><varname>TxMaxCoalescedIrqFrames=</varname></term>
+        <term><varname>TxMaxCoalescedLowFrames=</varname></term>
+        <term><varname>TxMaxCoalescedHighFrames=</varname></term>
         <listitem>
-          <para>Takes a boolean. When set, enables the transmit flow control, also known as the ethernet
-          transmit PAUSE message (respond to received ethernet PAUSE frames). When unset, the kernel's
-          default will be used.</para>
+          <para>These properties configure the maximum number of frames that are sent/received before a Rx/Tx
+          interrupt is generated. The <literal>Irq</literal> properties come into effect when the host is
+          servicing an IRQ. The <literal>Low</literal> and <literal>High</literal> properties come into
+          effect when the packet rate drops below the low packet rate threshold or exceeds the high packet
+          rate threshold respectively if adaptive Rx/Tx coalescing is enabled. When unset, the kernel's
+          defaults will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>AutoNegotiationFlowControl=</varname></term>
+        <term><varname>CoalescePacketRateLow=</varname></term>
+        <term><varname>CoalescePacketRateHigh=</varname></term>
         <listitem>
-          <para>Takes a boolean. When set, the auto negotiation enables the interface to exchange state
-          advertisements with the connected peer so that the two devices can agree on the ethernet
-          PAUSE configuration. When unset, the kernel's default will be used.</para>
+          <para>These properties configure the low and high packet rate (expressed in packets per second)
+          threshold respectively and are used to determine when the corresponding coalescing settings for low
+          and high packet rates come into effect if adaptive Rx/Tx coalescing is enabled. If unset, the
+          kernel's defaults will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>GenericSegmentOffloadMaxBytes=</varname></term>
+        <term><varname>CoalescePacketRateSampleIntervalSec=</varname></term>
         <listitem>
-          <para>Specifies the maximum size of a Generic Segment Offload (GSO) packet the
-          device should accept. The usual suffixes K, M, G, are supported and are
-          understood to the base of 1024. An unsigned integer in the range 1…65536.
-          Defaults to unset.</para>
+          <para>Configures how often to sample the packet rate used for adaptive Rx/Tx coalescing. This
+          property cannot be zero. This lowest time granularity supported by this property is seconds.
+          Partial seconds will be rounded up before being passed to the kernel. If unset, the kernel's
+          default will be used.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
-        <term><varname>GenericSegmentOffloadMaxSegments=</varname></term>
+        <term><varname>StatisticsBlockCoalesceSec=</varname></term>
         <listitem>
-          <para>Specifies the maximum number of a Generic Segment Offload (GSO) segments the device should
-          accept.  An unsigned integer in the range 1…65535. Defaults to unset.</para>
+          <para>How long to delay driver in-memory statistics block updates. If the driver does not have an
+          in-memory statistic block, this property is ignored. This property cannot be zero. If unset, the
+          kernel's default will be used.</para>
         </listitem>
       </varlistentry>
 
index 1d1d71786fa78c1427a34c865f6157e50f1dcb32..f62b4cb4004356dc0e4b7b75b444b20621878889 100644 (file)
         <term><varname>DefaultPVID=</varname></term>
         <listitem>
           <para>This specifies the default port VLAN ID of a newly attached bridge port.
-          Set this to an integer in the range 1â\80\934094 or <literal>none</literal> to disable the PVID.</para>
+          Set this to an integer in the range 1â\80¦4094 or <literal>none</literal> to disable the PVID.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>Id=</varname></term>
         <listitem>
-          <para>The VLAN ID to use. An integer in the range 0â\80\934094.
+          <para>The VLAN ID to use. An integer in the range 0â\80¦4094.
           This setting is compulsory.</para>
         </listitem>
       </varlistentry>
         <term><varname>TTL=</varname></term>
         <listitem>
           <para>A fixed Time To Live N on Virtual eXtensible Local Area Network packets.
-          Takes <literal>inherit</literal> or a number in the range 0â\80\93255. 0 is a special
+          Takes <literal>inherit</literal> or a number in the range 0â\80¦255. 0 is a special
           value meaning inherit the inner protocol's TTL value. <literal>inherit</literal>
           means that it will inherit the outer protocol's TTL value.</para>
         </listitem>
       <varlistentry>
         <term><varname>TunnelId=</varname></term>
         <listitem>
-          <para>Specifies the tunnel identifier. Takes an number in the range 1â\80\934294967295. The value used
+          <para>Specifies the tunnel identifier. Takes an number in the range 1â\80¦4294967295. The value used
           must match the <literal>PeerTunnelId=</literal> value being used at the peer. This setting is
           compulsory.</para>
         </listitem>
       <varlistentry>
         <term><varname>SessionId=</varname></term>
         <listitem>
-          <para>Specifies the session identifier. Takes an number in the range 1â\80\934294967295. The value used
+          <para>Specifies the session identifier. Takes an number in the range 1â\80¦4294967295. The value used
           must match the <literal>SessionId=</literal> value being used at the peer. This setting is
           compulsory.</para>
         </listitem>
       <varlistentry>
         <term><varname>PeerSessionId=</varname></term>
         <listitem>
-          <para>Specifies the peer session identifier. Takes an number in the range 1â\80\934294967295.
+          <para>Specifies the peer session identifier. Takes an number in the range 1â\80¦4294967295.
           The value used must match the <literal>PeerSessionId=</literal> value being used at the peer.
           This setting is compulsory.</para>
         </listitem>
         <term><varname>TTL=</varname></term>
         <listitem>
           <para>A fixed Time To Live N on tunneled packets. N is a
-          number in the range 1â\80\93255. 0 is a special value meaning that
+          number in the range 1â\80¦255. 0 is a special value meaning that
           packets inherit the TTL value. The default value for IPv4
           tunnels is 0 (inherit). The default value for IPv6 tunnels is
           64.</para>
           It is only used for IPv6 tunnels.
           A flow label of zero is used to indicate packets that have
           not been labeled.
-          It can be configured to a value in the range 0â\80\930xFFFFF, or be
+          It can be configured to a value in the range 0â\80¦0xFFFFF, or be
           set to <literal>inherit</literal>, in which case the original flowlabel is used.</para>
         </listitem>
       </varlistentry>
           <para>Sets a comma-separated list of IP (v4 or v6) addresses with CIDR masks
           from which this peer is allowed to send incoming traffic and to
           which outgoing traffic for this peer is directed.</para>
+
           <para>The catch-all 0.0.0.0/0 may be specified for matching all IPv4 addresses,
           and ::/0 may be specified for matching all IPv6 addresses.</para>
-          <para>Note that this only affects "routing inside the network interface itself",
-          as in, which wireguard peer packets with a specific destination address are sent to,
-          and what source addresses are accepted from which peer.</para>
-          <para>To cause packets to be sent via wireguard in first place, a route needs
-          to be added, as well - either in the <literal>[Routes]</literal> section on the
-          <literal>.network</literal> matching the wireguard interface, or outside of networkd.
-          </para>
+
+          <para>Note that this only affects <emphasis>routing inside the network interface itself</emphasis>,
+          i.e. the packets that pass through the tunnel itself. To cause packets to be sent via the tunnel in
+          the first place, an appropriate route needs to be added as well — either in the
+          <literal>[Routes]</literal> section on the <literal>.network</literal> matching the wireguard
+          interface, or externally to <filename>systemd-networkd</filename>.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>AdUserPortKey=</varname></term>
         <listitem>
           <para>Specifies the 802.3ad user defined portion of the port key. Takes a number in the range
-          0â\80\931023.</para>
+          0â\80¦1023.</para>
         </listitem>
       </varlistentry>
 
 
   <refsect1>
     <title>[BatmanAdvanced] Section Options</title>
-    <para>The [BatmanAdvanced] section only applies for
-    netdevs of kind <literal>batadv</literal> and accepts the
-    following keys:</para>
+
+    <para>The [BatmanAdvanced] section only applies for netdevs of kind <literal>batadv</literal> and accepts
+    the following keys:</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
index 242d4ea0668e8834efe92e87b2c087e5d6594301..573ba959eb4d15240cf39c21cc2421848247bb64 100644 (file)
         <listitem>
           <para>Takes a boolean. If set to true, promiscuous mode of the interface is enabled.
           Defaults to unset.</para>
+          <para>If this is set to false for the underlying link of a <literal>passthru</literal> mode MACVLAN/MACVTAP,
+          the virtual interface will be created with the <literal>nopromisc</literal> flag set.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
@@ -1053,7 +1055,8 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
         <varlistentry>
           <term><varname>Label=</varname></term>
           <listitem>
-            <para>An address label.</para>
+            <para>Specifies the label for the IPv4 address. The label must be a 7-bit ASCII string with
+            a length of 1…15 characters. Defaults to unset.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
@@ -1238,7 +1241,9 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
           <term><varname>Priority=</varname></term>
           <listitem>
             <para>Specifies the priority of this rule. <varname>Priority=</varname> is an unsigned
-            integer. Higher number means lower priority, and rules get processed in order of increasing number.</para>
+            integer in the range 0…4294967295. Higher number means lower priority, and rules get
+            processed in order of increasing number. Defaults to unset, and the kernel will pick
+            a value dynamically.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
@@ -1423,7 +1428,7 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
           <term><varname>Metric=</varname></term>
           <listitem>
             <para>The metric of the route. Takes an unsigned integer in the range 0…4294967295.
-            Defaluts to unset, and the kernel's default will be used.</para>
+            Defaults to unset, and the kernel's default will be used.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
@@ -1615,9 +1620,10 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
           <term><varname>SendHostname=</varname></term>
           <listitem>
             <para>When true (the default), the machine's hostname (or the value specified with
-            <varname>Hostname=</varname> below) will be sent to the DHCP server. Note that the hostname must
-            consist only of 7-bit ASCII lower-case characters and no spaces or dots, and be formatted as a
-            valid DNS domain name. Otherwise, the hostname is not sent even if this option is true.</para>
+            <varname>Hostname=</varname>, described below) will be sent to the DHCP server. Note that the
+            hostname must consist only of 7-bit ASCII lower-case characters and no spaces or dots, and be
+            formatted as a valid DNS domain name. Otherwise, the hostname is not sent even if this option is
+            true.</para>
           </listitem>
         </varlistentry>
 
@@ -1769,6 +1775,15 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
 
         <!-- How to use the DHCP lease -->
 
+        <varlistentry>
+          <term><varname>Label=</varname></term>
+          <listitem>
+            <para>Specifies the label for the IPv4 address received from the DHCP server.
+            The label must be a 7-bit ASCII string with a length of 1…15 characters.
+            Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>UseDNS=</varname></term>
           <listitem>
@@ -1868,8 +1883,9 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
         <varlistentry>
           <term><varname>RouteMetric=</varname></term>
           <listitem>
-            <para>Set the routing metric for routes specified by the DHCP server. Takes an unsigned
-            integer in the range 0…4294967295. Defaults to 1024.</para>
+            <para>Set the routing metric for routes specified by the DHCP server (including the prefix
+            route added for the specified prefix). Takes an unsigned integer in the range 0…4294967295.
+            Defaults to 1024.</para>
           </listitem>
         </varlistentry>
 
@@ -1911,8 +1927,8 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
           <term><varname>FallbackLeaseLifetimeSec=</varname></term>
           <listitem>
             <para>Allows to set DHCPv4 lease lifetime when DHCPv4 server does not send the lease lifetime.
-            Takes one of <literal>forever</literal> or <literal>infinity</literal> means that the address
-            never expires. Defaults to unset.</para>
+            Takes one of <literal>forever</literal> or <literal>infinity</literal>. The latter means that the
+            address never expires. Defaults to unset.</para>
           </listitem>
         </varlistentry>
 
@@ -2249,6 +2265,14 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>UseMTU=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When true, the MTU received in the Router Advertisement will be
+            used. Defaults to true.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>UseAutonomousPrefix=</varname></term>
           <listitem>
@@ -2339,9 +2363,9 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
       <varlistentry>
         <term><varname>ServerAddress=</varname></term>
         <listitem><para>Specifies server address for the DHCP server. Takes an IPv4 address with prefix
-        length, e.g., <literal>192.168.0.1/24</literal>. This setting may be useful when the link which
-        DHCP server running on has multiple static addresses. When unset, one of static addresses in
-        the link will be automatically selected. Defaults to unset.</para></listitem>
+        length, for example <literal>192.168.0.1/24</literal>. This setting may be useful when the link on
+        which the DHCP server is running has multiple static addresses. When unset, one of static addresses
+        in the link will be automatically selected. Defaults to unset.</para></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -2381,12 +2405,12 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
 
       <varlistentry>
         <term><varname>UplinkInterface=</varname></term>
-        <listitem><para>Specifies name or index of uplink interface, or one of the special values
-        <literal>:none</literal> and <literal>:auto</literal>. When emitting DNS, NTP, or SIP servers
-        are enabled but no servers are specified, the servers configured in the uplink interface will
-        be emitted. When <literal>:auto</literal>, the link which has default gateway with higher
-        priority will be automatically selected. When <literal>:none</literal>, no uplink interface
-        will be selected. Defaults to <literal>:auto</literal>.</para></listitem>
+        <listitem><para>Specifies the name or the index of the uplink interface, or one of the special
+        values <literal>:none</literal> and <literal>:auto</literal>. When emitting DNS, NTP, or SIP
+        servers is enabled but no servers are specified, the servers configured in the uplink interface
+        will be emitted. When <literal>:auto</literal>, the link which has a default gateway with the
+        highest priority will be automatically selected. When <literal>:none</literal>, no uplink
+        interface will be selected. Defaults to <literal>:auto</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -2521,23 +2545,22 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
 
   <refsect1>
     <title>[DHCPServerStaticLease] Section Options</title>
-    <para>The <literal>[DHCPServerStaticLease]</literal> section configures a static DHCP lease to
-    assign a pre-set IPv4 address to a specific device based on its MAC address. This section can be
-    specified multiple times.</para>
+    <para>The <literal>[DHCPServerStaticLease]</literal> section configures a static DHCP lease to assign a
+    fixed IPv4 address to a specific device based on its MAC address. This section can be specified multiple
+    times.</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
         <term><varname>MACAddress=</varname></term>
 
-        <listitem><para>The hardware address of a device which should be assigned IPv4 address
-        specified in <varname>Address=</varname>. This key is mandatory.</para></listitem>
+        <listitem><para>The hardware address of a device to match. This key is mandatory.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>Address=</varname></term>
 
-        <listitem><para>IPv4 address that should be assigned to a device with a hardware address
-        specified in <varname>MACAddress=</varname>. This key is mandatory.</para></listitem>
+        <listitem><para>The IPv4 address that should be assigned to the device that was matched with
+        <varname>MACAddress=</varname>. This key is mandatory.</para></listitem>
       </varlistentry>
     </variablelist>
   </refsect1>
@@ -2586,18 +2609,28 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
         for details. Defaults to <literal>medium</literal>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>UplinkInterface=</varname></term>
+        <listitem><para>Specifies the name or the index of the uplink interface, or one of the special
+        values <literal>:none</literal> and <literal>:auto</literal>. When emitting DNS servers or
+        search domains is enabled but no servers are specified, the servers configured in the uplink
+        interface will be emitted. When <literal>:auto</literal>, the link which has a default gateway
+        with the highest priority will be automatically selected. When <literal>:none</literal>, no
+        uplink interface will be selected. Defaults to <literal>:auto</literal>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>EmitDNS=</varname></term>
         <term><varname>DNS=</varname></term>
 
-        <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses that
-        are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is
-        true. <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that case
-        the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS servers are read
-        from the [Network] section. If the [Network] section does not contain any DNS servers either, DNS
-        servers from the uplink with the highest priority default route are used. When
-        <varname>EmitDNS=</varname> is false, no DNS server information is sent in Router Advertisement
-        messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem>
+        <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses
+        that are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is true.
+        <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that case
+        the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS servers are
+        read from the [Network] section. If the [Network] section does not contain any DNS servers
+        either, DNS servers from the uplink interface specified in <varname>UplinkInterface=</varname>
+        will be used. When <varname>EmitDNS=</varname> is false, no DNS server information is sent in
+        Router Advertisement messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -2605,11 +2638,12 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
         <term><varname>Domains=</varname></term>
 
         <listitem><para>A list of DNS search domains distributed via Router Advertisement messages when
-        <varname>EmitDomains=</varname> is true. If <varname>Domains=</varname> is empty, DNS search domains
-        are read from the [Network] section. If the [Network] section does not contain any DNS search domains
-        either, DNS search domains from the uplink with the highest priority default route are used. When
-        <varname>EmitDomains=</varname> is false, no DNS search domain information is sent in Router
-        Advertisement messages. <varname>EmitDomains=</varname> defaults to true.</para></listitem>
+        <varname>EmitDomains=</varname> is true. If <varname>Domains=</varname> is empty, DNS search
+        domains are read from the [Network] section. If the [Network] section does not contain any DNS
+        search domains either, DNS search domains from the uplink interface specified in
+        <varname>UplinkInterface=</varname> will be used. When <varname>EmitDomains=</varname> is false,
+        no DNS search domain information is sent in Router Advertisement messages.
+        <varname>EmitDomains=</varname> defaults to true.</para></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -2946,7 +2980,27 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
           <term><varname>SamplePoint=</varname></term>
           <listitem>
             <para>Optional sample point in percent with one decimal (e.g. <literal>75%</literal>,
-            <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>).</para>
+            <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>). This will be ignored when
+            <varname>BitRate=</varname> is unspecified.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>TimeQuantaNSec=</varname></term>
+          <term><varname>PropagationSegment=</varname></term>
+          <term><varname>PhaseBufferSegment1=</varname></term>
+          <term><varname>PhaseBufferSegment2=</varname></term>
+          <term><varname>SyncJumpWidth=</varname></term>
+          <listitem>
+            <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
+            synchronization jump width, which allow to define the CAN bit-timing in a hardware
+            independent format as proposed by the Bosch CAN 2.0 Specification.
+            <varname>TimeQuantaNSec=</varname> takes a timespan in nanoseconds.
+            <varname>PropagationSegment=</varname>, <varname>PhaseBufferSegment1=</varname>,
+            <varname>PhaseBufferSegment2=</varname>, and <varname>SyncJumpWidth=</varname> take number
+            of time quantum specified in <varname>TimeQuantaNSec=</varname> and must be an unsigned
+            integer in the range 0…4294967295. These settings except for
+            <varname>SyncJumpWidth=</varname> will be ignored when <varname>BitRate=</varname> is
+            specified.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
@@ -2957,12 +3011,25 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
             analogous to the <varname>BitRate=</varname> and <varname>SamplePoint=</varname> keys.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>DataTimeQuantaNSec=</varname></term>
+          <term><varname>DataPropagationSegment=</varname></term>
+          <term><varname>DataPhaseBufferSegment1=</varname></term>
+          <term><varname>DataPhaseBufferSegment2=</varname></term>
+          <term><varname>DataSyncJumpWidth=</varname></term>
+          <listitem>
+            <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
+            synchronization jump width for the data phase, if CAN-FD is used. These settings are
+            analogous to the <varname>TimeQuantaNSec=</varname> or related settings.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><varname>FDMode=</varname></term>
           <listitem>
             <para>Takes a boolean. When <literal>yes</literal>, CAN-FD mode is enabled for the interface.
             Note, that a bitrate and optional sample point should also be set for the CAN-FD data phase using
-            the <varname>DataBitRate=</varname> and <varname>DataSamplePoint=</varname> keys.</para>
+            the <varname>DataBitRate=</varname> and <varname>DataSamplePoint=</varname> keys, or
+            <varname>DataTimeQuanta=</varname> and related settings.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
@@ -2985,8 +3052,10 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
         <varlistentry>
           <term><varname>Termination=</varname></term>
           <listitem>
-            <para>Takes a boolean. When <literal>yes</literal>, the termination resistor will be selected for
-            the bias network. When unset, the kernel's default will be used.</para>
+            <para>Takes a boolean or a termination resistor value in ohm in the range 0–65535. When
+            <literal>yes</literal>, the termination resistor is set to 120 ohm. When
+            <literal>no</literal> or <literal>0</literal> is set, the termination resistor is disabled.
+            When unset, the kernel's default will be used.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
@@ -3016,6 +3085,36 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
             </para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>Loopback=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When <literal>yes</literal>, loopback mode is enabled. When the
+            loopback mode is enabled, the interface treats messages transmitted by itself as received
+            messages. The loopback mode is important to debug CAN networks. When unset, the kernel's
+            default will be used.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>OneShot=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When <literal>yes</literal>, one-shot mode is enabled. When unset,
+            the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>PresumeAck=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When <literal>yes</literal>, the interface will ignore missing CAN
+            ACKs. When unset, the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>ClassicDataLengthCode=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When <literal>yes</literal>, the interface will handle the 4bit data
+            length code (DLC). When unset, the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
index 7ba8e361b4214424dd8a3b1f924cd4de7a2bd323..dc0e2f9fd2141c9cfb58c59ecabd0efe9fe28392 100644 (file)
         capabilities (see
         <citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
         for details). The <varname>AmbientCapability=</varname> setting
-        specifies capability which will be passed to to started program
+        specifies capability which will be passed to the started program
         in the inheritable and ambient capability sets. This will grant
         these capabilities to this process. This setting correspond to
         the <option>--ambient-capability=</option> command line switch.
index 64333a3216f0830a4850c123026b515996356426..9e6db28536b6dd5ebda9d53bf47dd12dab52950c 100644 (file)
@@ -207,7 +207,7 @@ disable *</programlisting>
       <citerefentry><refentrytitle>systemd-delta</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     </para>
 
-    <para><citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    <para><citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     has a discussion of packaging scriptlets.</para>
 
     <para>Fedora page introducing the use of presets:
index f5cbe688ee86c3ce9daff38d2329431fa299c934..15b7f314a37ee129987d47d00c673e23af77c143 100644 (file)
@@ -855,6 +855,52 @@ SocketBindDeny=any
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RestrictNetworkInterfaces=</varname></term>
+
+        <listitem>
+          <para>Takes a list of space-separated network interface names. This option restricts the network
+          interfaces that processes of this unit can use. By default processes can only use the network interfaces
+          listed (allow-list). If the first character of the rule is <literal>~</literal>, the effect is inverted:
+          the processes can only use network interfaces not listed (deny-list).
+          </para>
+
+          <para>This option can appear multiple times, in which case the network interface names are merged. If the
+          empty string is assigned the set is reset, all prior assigments will have not effect.
+          </para>
+
+          <para>If you specify both types of this option (i.e. allow-listing and deny-listing), the first encountered
+          will take precedence and will dictate the default action (allow vs deny). Then the next occurrences of this
+          option will add or delete the listed network interface names from the set, depending of its type and the
+          default action.
+          </para>
+
+          <para>The loopback interface ("lo") is not treated in any special way, you have to configure it explicitly
+          in the unit file.
+          </para>
+          <para>Example 1: allow-list
+          <programlisting>
+RestrictNetworkInterfaces=eth1
+RestrictNetworkInterfaces=eth2</programlisting>
+          Programs in the unit will be only able to use the eth1 and eth2 network
+          interfaces.
+          </para>
+
+          <para>Example 2: deny-list
+          <programlisting>
+RestrictNetworkInterfaces=~eth1 eth2</programlisting>
+          Programs in the unit will be able to use any network interface but eth1 and eth2.
+          </para>
+
+          <para>Example 3: mixed
+          <programlisting>
+RestrictNetworkInterfaces=eth1 eth2
+RestrictNetworkInterfaces=~eth1</programlisting>
+          Programs in the unit will be only able to use the eth2 network interface.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>DeviceAllow=</varname></term>
 
index 350bc5f8e5bca5054c2702ed046ab9bfbe2d65f4..884260a2159ce0e4a10062b31cda6772dc4a1bb9 100644 (file)
     <literal>\;</literal>.</para>
 
     <para>Each command line is unquoted using the rules described in "Quoting" section in
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>. The
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>. The
     first item becomes the command to execute, and the subsequent items the arguments.</para>
 
     <para>This syntax is inspired by shell syntax, but only the meta-characters and expansions
index b09c4e9fa2ee48fa59f3a36057934574b7f64967..8755b523ae2b7204c1a4bacb650799d41453bffc 100644 (file)
@@ -33,6 +33,7 @@
     <filename>default.target</filename>,
     <filename>emergency.target</filename>,
     <filename>exit.target</filename>,
+    <filename>factory-reset.target</filename>,
     <filename>final.target</filename>,
     <filename>first-boot-complete.target</filename>,
     <filename>getty.target</filename>,
             shutdown when the service manager starts to exit.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><filename>factory-reset.target</filename></term>
+          <listitem>
+            <para>A special target to trigger a factory reset.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><filename>final.target</filename></term>
           <listitem>
index 9a4010c4527c7ccb3eba2f66fd9b51d588397fb8..b6e4a2233c4552888f9ff4dba169999293c98513 100644 (file)
         <varlistentry>
           <term><varname>ConditionControlGroupController=</varname></term>
 
-          <listitem><para>Check whether given cgroup controllers (eg. <literal>cpu</literal>) are available
+          <listitem><para>Check whether given cgroup controllers (e.g. <literal>cpu</literal>) are available
           for use on the system or whether the legacy v1 cgroup or the modern v2 cgroup hierarchy is used.
           </para>
 
index fe1719fc1814dcea390c914b72d09e3db5654999..468edfb2d3953e73caa06e428536f83401952f48 100644 (file)
         for --user instances).</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><constant>SIGRTMIN+25</constant></term>
+
+        <listitem><para>Upon receiving this signal the systemd manager will reexecute itself. This
+        is mostly equivalent to <command>systemctl daemon-reexec</command> except that it will be
+        done asynchronously.</para>
+
+        <para>The systemd system manager treats this signal the same way as
+        <constant>SIGTERM</constant>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><constant>SIGRTMIN+26</constant></term>
 
index 3fd11cea04c7d2337bcfa96d252cd90cf847bf52..07472cdb39b0d921104c35316b14c8614029136a 100644 (file)
         Defaults to 30 seconds and must not be smaller than 1 second.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>SaveIntervalSec=</varname></term>
+        <listitem><para>The interval at which the current time is periodically saved to disk, in the absence
+        of any recent synchronisation from an NTP server. This is especially useful for offline systems
+        with no local RTC, as it will guarantee that the system clock remains roughly monotonic across
+        reboots.</para>
+
+        <para>Takes a time interval value. The default unit is seconds, but other units may be specified, see
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+        Defaults to 60 seconds.</para></listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index 261de5902d57d691078577f77f9ebd4315dba45d..305033b672e606d6e6d36d122bdf8ed94cfdfb46 100644 (file)
@@ -550,6 +550,7 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
 
     <refsect2>
       <title>Age</title>
+
       <para>The date field, when set, is used to decide what files to
       delete when cleaning. If a file or directory is older than the
       current time minus the age field, it is deleted. The field
@@ -582,10 +583,9 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
       and <varname>X</varname>. If omitted or set to
       <literal>-</literal>, no automatic clean-up is done.</para>
 
-      <para>If the age field starts with a tilde character
-      <literal>~</literal>, the clean-up is only applied to files and
-      directories one level inside the directory specified, but not
-      the files and directories immediately inside it.</para>
+      <para>If the age field starts with a tilde character <literal>~</literal>, clean-up is only applied to
+      files and directories one level inside the directory specified, but not the files and directories
+      immediately inside it.</para>
 
       <para>The age of a file system entry is determined from its last
       modification timestamp (mtime), its last access timestamp (atime),
@@ -595,30 +595,25 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
       the age field. To restrict the deletion based on particular type
       of file timestamps, the age-by argument can be used.</para>
 
-      <para>The age-by argument, when (optionally) specified along
-      with age will check if the file system entry has aged by the
-      type of file timestamp(s) provided. It can be specified by
-      prefixing the age argument with a set of file timestamp types
-      followed by a colon character <literal>:</literal>, i.e.,
-      <literal><replaceable>age-by</replaceable>:<replaceable>cleanup-age</replaceable></literal>.
-      The argument can be a set of:
-      <constant>a</constant> (<constant>A</constant> for directories),
-      <constant>b</constant> (<constant>B</constant> for directories),
-      <constant>c</constant> (<constant>C</constant> for directories; ignored by default), or
-      <constant>m</constant> (<constant>M</constant> for directories),
-      indicating access, creation, last status change, and last
-      modification times of a file system entry respectively. See
-      <citerefentry project='man-pages'><refentrytitle>statx</refentrytitle><manvolnum>2</manvolnum></citerefentry>
-      file timestamp fields for more details.</para>
-
-      <para>If unspecified, the age-by field defaults to
-      <constant>abcmABM</constant>,
-      i.e., by default all file timestamps are taken into consideration,
-      with the exception of the last status change timestamp (ctime) for
-      directories. This is because the aging logic itself will alter the
-      ctime whenever it deletes a file inside it. To ensure that running
-      the aging logic does not feed back into the next iteration of it,
-      ctime for directories is ignored by default.</para>
+      <para>The age-by argument overrides the timestamp types to be used for the age check. It can be
+      specified by prefixing the age argument with a sequence of characters to specify the timestamp types
+      and a colon (<literal>:</literal>):
+      <literal><replaceable>age-by</replaceable>...:<replaceable>cleanup-age</replaceable></literal>.  The
+      argument can consist of <constant>a</constant> (<constant>A</constant> for directories),
+      <constant>b</constant> (<constant>B</constant> for directories), <constant>c</constant>
+      (<constant>C</constant> for directories), or <constant>m</constant> (<constant>M</constant> for
+      directories). Those respectively indicate access, creation, last status change, and last modification
+      time of a file system entry. The lower-case letter signifies that the given timestamp type should be
+      considered for files, while the upper-case letter signifies that the given timestamp type should be
+      considered for directories. See <citerefentry
+      project='man-pages'><refentrytitle>statx</refentrytitle><manvolnum>2</manvolnum></citerefentry> file
+      timestamp fields for more details about timestamp types.</para>
+
+      <para>If not specified, the age-by field defaults to <constant>abcmABM</constant>, i.e. by default all
+      file timestamps are taken into consideration, with the exception of the last status change timestamp
+      (ctime) for directories. This is because the aging logic itself will alter the ctime whenever it
+      deletes a file inside it. To ensure that running the aging logic does not feed back into the next
+      iteration of itself, ctime for directories is ignored by default.</para>
 
       <para>For example:<programlisting>
 # Files created and modified, and directories accessed more than
index 90adc64543a003c751a73ca1ce35bee94267daf9..2704156840c384bb9a28af3907db32a70f87cf01 100644 (file)
       for device <replaceable>DEVPATH</replaceable>, and print debug
       output.</para>
       <variablelist>
+        <varlistentry>
+          <term><option>-a</option></term>
+          <term><option>--action=<replaceable>ACTION</replaceable></option></term>
+          <listitem>
+            <para>Type of event to be simulated. Possible actions are <literal>add</literal>,
+            <literal>remove</literal>, <literal>change</literal>, <literal>move</literal>,
+            <literal>online</literal>, <literal>offline</literal>, <literal>bind</literal>,
+            and <literal>unbind</literal>. Also, the special value <literal>help</literal> can be used
+            to list the possible actions. The default value is <literal>add</literal>.</para>
+          </listitem>
+        </varlistentry>
+
         <xi:include href="standard-options.xml" xpointer="help" />
       </variablelist>
     </refsect2>
index 0871306b202b6aff3f4f77cd613a122394dde4e4..611c425b795cc94bb7b1ef5f82ea73a3bbd00214 100644 (file)
@@ -42,7 +42,7 @@
     url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via Varlink</ulink>, and may also
     pick up drop-in JSON user and group records from <filename>/etc/userdb/</filename>,
     <filename>/run/userdb/</filename>, <filename>/run/host/userdb/</filename>,
-    <filename>/use/lib/userdb/</filename>.</para>
+    <filename>/usr/lib/userdb/</filename>.</para>
   </refsect1>
 
   <refsect1>
 
         <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>,
-        <filename>/run/host/userdb/</filename>, <filename>/use/lib/userdb/</filename>. If
+        <filename>/run/host/userdb/</filename>, <filename>/usr/lib/userdb/</filename>. If
         <option>--with-dropin=no</option> is used these records are suppressed. If
         <option>--with-dropin=yes</option> is specified such users/groups are included in the output (which
         is the default).</para></listitem>
         <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         and picks up JSON user/group records from <filename>/etc/userdb/</filename>,
         <filename>/run/userdb/</filename>, <filename>/run/host/userdb/</filename>,
-        <filename>/use/lib/userdb/</filename>.</para></listitem>
+        <filename>/usr/lib/userdb/</filename>.</para></listitem>
       </varlistentry>
 
     </variablelist>
index cb023f7f132722b65748d279b80ff8f349c3f575..f7b18c6b98ee9a8d5f75c23f26675ef481484ec9 100644 (file)
@@ -10,7 +10,7 @@ project('systemd', 'c',
                 'localstatedir=/var',
                 'warning_level=2',
         ],
-        meson_version : '>= 0.47',
+        meson_version : '>= 0.53.2',
        )
 
 libsystemd_version = '0.32.0'
@@ -101,9 +101,9 @@ conf.set10('BUMP_PROC_SYS_FS_FILE_MAX', get_option('bump-proc-sys-fs-file-max'))
 conf.set10('BUMP_PROC_SYS_FS_NR_OPEN',  get_option('bump-proc-sys-fs-nr-open'))
 conf.set('HIGH_RLIMIT_NOFILE',          512*1024)
 
-# join_paths ignores the preceding arguments if an absolute component is
-# encountered, so this should canonicalize various paths when they are
-# absolute or relative.
+# Meson ignores the preceding arguments when joining paths if an absolute
+# component is encountered, so this should canonicalize various paths when they
+# are absolute or relative.
 prefixdir = get_option('prefix')
 if not prefixdir.startswith('/')
         error('Prefix is not absolute: "@0@"'.format(prefixdir))
@@ -113,105 +113,105 @@ if prefixdir != rootprefixdir and rootprefixdir != '/' and not prefixdir.strip('
                 rootprefixdir, prefixdir))
 endif
 
-bindir = join_paths(prefixdir, get_option('bindir'))
-libdir = join_paths(prefixdir, get_option('libdir'))
-sysconfdir = join_paths(prefixdir, get_option('sysconfdir'))
-includedir = join_paths(prefixdir, get_option('includedir'))
-datadir = join_paths(prefixdir, get_option('datadir'))
-localstatedir = join_paths('/', get_option('localstatedir'))
+bindir = prefixdir / get_option('bindir')
+libdir = prefixdir / get_option('libdir')
+sysconfdir = prefixdir / get_option('sysconfdir')
+includedir = prefixdir / get_option('includedir')
+datadir = prefixdir / get_option('datadir')
+localstatedir = '/' / get_option('localstatedir')
 
-rootbindir = join_paths(rootprefixdir, 'bin')
-rootsbindir = join_paths(rootprefixdir, split_bin ? 'sbin' : 'bin')
-rootlibexecdir = join_paths(rootprefixdir, 'lib/systemd')
+rootbindir = rootprefixdir / 'bin'
+rootsbindir = rootprefixdir / (split_bin ? 'sbin' : 'bin')
+rootlibexecdir = rootprefixdir / 'lib/systemd'
 
 rootlibdir = get_option('rootlibdir')
 if rootlibdir == ''
-        rootlibdir = join_paths(rootprefixdir, libdir.split('/')[-1])
+        rootlibdir = rootprefixdir / libdir.split('/')[-1]
 endif
 
 install_sysconfdir = get_option('install-sysconfdir') != 'false'
 install_sysconfdir_samples = get_option('install-sysconfdir') == 'true'
 # Dirs of external packages
-pkgconfigdatadir = get_option('pkgconfigdatadir') == '' ? join_paths(datadir, 'pkgconfig') : get_option('pkgconfigdatadir')
-pkgconfiglibdir = get_option('pkgconfiglibdir') == '' ? join_paths(libdir, 'pkgconfig') : get_option('pkgconfiglibdir')
-polkitpolicydir = join_paths(datadir, 'polkit-1/actions')
-polkitrulesdir = join_paths(datadir, 'polkit-1/rules.d')
-polkitpkladir = join_paths(localstatedir, 'lib/polkit-1/localauthority/10-vendor.d')
-xinitrcdir = get_option('xinitrcdir') == '' ? join_paths(sysconfdir, 'X11/xinit/xinitrc.d') : get_option('xinitrcdir')
+pkgconfigdatadir = get_option('pkgconfigdatadir') != '' ? get_option('pkgconfigdatadir') : datadir / 'pkgconfig'
+pkgconfiglibdir = get_option('pkgconfiglibdir') != '' ? get_option('pkgconfiglibdir') : libdir / 'pkgconfig'
+polkitpolicydir = datadir / 'polkit-1/actions'
+polkitrulesdir = datadir / 'polkit-1/rules.d'
+polkitpkladir = localstatedir / 'lib/polkit-1/localauthority/10-vendor.d'
+xinitrcdir = get_option('xinitrcdir') != '' ? get_option('xinitrcdir') : sysconfdir / 'X11/xinit/xinitrc.d'
 rpmmacrosdir = get_option('rpmmacrosdir')
 if rpmmacrosdir != 'no'
-        rpmmacrosdir = join_paths(prefixdir, rpmmacrosdir)
+        rpmmacrosdir = prefixdir / rpmmacrosdir
 endif
-modprobedir = join_paths(rootprefixdir, 'lib/modprobe.d')
+modprobedir = rootprefixdir / 'lib/modprobe.d'
 
 # Our own paths
-pkgdatadir = join_paths(datadir, 'systemd')
-environmentdir = join_paths(prefixdir, 'lib/environment.d')
-pkgsysconfdir = join_paths(sysconfdir, 'systemd')
-userunitdir = join_paths(prefixdir, 'lib/systemd/user')
-userpresetdir = join_paths(prefixdir, 'lib/systemd/user-preset')
-tmpfilesdir = join_paths(prefixdir, 'lib/tmpfiles.d')
-sysusersdir = join_paths(prefixdir, 'lib/sysusers.d')
-sysctldir = join_paths(prefixdir, 'lib/sysctl.d')
-binfmtdir = join_paths(prefixdir, 'lib/binfmt.d')
-modulesloaddir = join_paths(prefixdir, 'lib/modules-load.d')
-networkdir = join_paths(rootprefixdir, 'lib/systemd/network')
-pkgincludedir = join_paths(includedir, 'systemd')
-systemgeneratordir = join_paths(rootlibexecdir, 'system-generators')
-usergeneratordir = join_paths(prefixdir, 'lib/systemd/user-generators')
-systemenvgeneratordir = join_paths(prefixdir, 'lib/systemd/system-environment-generators')
-userenvgeneratordir = join_paths(prefixdir, 'lib/systemd/user-environment-generators')
-systemshutdowndir = join_paths(rootlibexecdir, 'system-shutdown')
-systemsleepdir = join_paths(rootlibexecdir, 'system-sleep')
-systemunitdir = join_paths(rootprefixdir, 'lib/systemd/system')
-systempresetdir = join_paths(rootprefixdir, 'lib/systemd/system-preset')
-udevlibexecdir = join_paths(rootprefixdir, 'lib/udev')
-udevrulesdir = join_paths(udevlibexecdir, 'rules.d')
-udevhwdbdir = join_paths(udevlibexecdir, 'hwdb.d')
-catalogdir = join_paths(prefixdir, 'lib/systemd/catalog')
-kernelinstalldir = join_paths(prefixdir, 'lib/kernel/install.d')
-factorydir = join_paths(datadir, 'factory')
-bootlibdir = join_paths(prefixdir, 'lib/systemd/boot/efi')
-testsdir = join_paths(prefixdir, 'lib/systemd/tests')
-systemdstatedir = join_paths(localstatedir, 'lib/systemd')
-catalogstatedir = join_paths(systemdstatedir, 'catalog')
-randomseeddir = join_paths(localstatedir, 'lib/systemd')
-profiledir = join_paths(rootlibexecdir, 'portable', 'profile')
-ntpservicelistdir = join_paths(rootprefixdir, 'lib/systemd/ntp-units.d')
+pkgdatadir = datadir / 'systemd'
+environmentdir = prefixdir / 'lib/environment.d'
+pkgsysconfdir = sysconfdir / 'systemd'
+userunitdir = prefixdir / 'lib/systemd/user'
+userpresetdir = prefixdir / 'lib/systemd/user-preset'
+tmpfilesdir = prefixdir / 'lib/tmpfiles.d'
+sysusersdir = prefixdir / 'lib/sysusers.d'
+sysctldir = prefixdir / 'lib/sysctl.d'
+binfmtdir = prefixdir / 'lib/binfmt.d'
+modulesloaddir = prefixdir / 'lib/modules-load.d'
+networkdir = rootprefixdir / 'lib/systemd/network'
+pkgincludedir = includedir / 'systemd'
+systemgeneratordir = rootlibexecdir / 'system-generators'
+usergeneratordir = prefixdir / 'lib/systemd/user-generators'
+systemenvgeneratordir = prefixdir / 'lib/systemd/system-environment-generators'
+userenvgeneratordir = prefixdir / 'lib/systemd/user-environment-generators'
+systemshutdowndir = rootlibexecdir / 'system-shutdown'
+systemsleepdir = rootlibexecdir / 'system-sleep'
+systemunitdir = rootprefixdir / 'lib/systemd/system'
+systempresetdir = rootprefixdir / 'lib/systemd/system-preset'
+udevlibexecdir = rootprefixdir / 'lib/udev'
+udevrulesdir = udevlibexecdir / 'rules.d'
+udevhwdbdir = udevlibexecdir / 'hwdb.d'
+catalogdir = prefixdir / 'lib/systemd/catalog'
+kernelinstalldir = prefixdir / 'lib/kernel/install.d'
+factorydir = datadir / 'factory'
+bootlibdir = prefixdir / 'lib/systemd/boot/efi'
+testsdir = prefixdir / 'lib/systemd/tests'
+systemdstatedir = localstatedir / 'lib/systemd'
+catalogstatedir = systemdstatedir / 'catalog'
+randomseeddir = localstatedir / 'lib/systemd'
+profiledir = rootlibexecdir / 'portable' / 'profile'
+ntpservicelistdir = rootprefixdir / 'lib/systemd/ntp-units.d'
 
 docdir = get_option('docdir')
 if docdir == ''
-        docdir = join_paths(datadir, 'doc/systemd')
+        docdir = datadir / 'doc/systemd'
 endif
 
 dbuspolicydir = get_option('dbuspolicydir')
 if dbuspolicydir == ''
-        dbuspolicydir = join_paths(datadir, 'dbus-1/system.d')
+        dbuspolicydir = datadir / 'dbus-1/system.d'
 endif
 
 dbussessionservicedir = get_option('dbussessionservicedir')
 if dbussessionservicedir == ''
-        dbussessionservicedir = join_paths(datadir, 'dbus-1/services')
+        dbussessionservicedir = datadir / 'dbus-1/services'
 endif
 
 dbussystemservicedir = get_option('dbussystemservicedir')
 if dbussystemservicedir == ''
-        dbussystemservicedir = join_paths(datadir, 'dbus-1/system-services')
+        dbussystemservicedir = datadir / 'dbus-1/system-services'
 endif
 
 pamlibdir = get_option('pamlibdir')
 if pamlibdir == ''
-        pamlibdir = join_paths(rootlibdir, 'security')
+        pamlibdir = rootlibdir / 'security'
 endif
 
 pamconfdir = get_option('pamconfdir')
 if pamconfdir == ''
-        pamconfdir = join_paths(prefixdir, 'lib/pam.d')
+        pamconfdir = prefixdir / 'lib/pam.d'
 endif
 
 libcryptsetup_plugins_dir = get_option('libcryptsetup-plugins-dir')
 if libcryptsetup_plugins_dir == ''
-        libcryptsetup_plugins_dir = join_paths(rootlibdir, 'cryptsetup')
+        libcryptsetup_plugins_dir = rootlibdir / 'cryptsetup'
 endif
 
 memory_accounting_default = get_option('memory-accounting-default')
@@ -219,19 +219,19 @@ status_unit_format_default = get_option('status-unit-format-default')
 
 conf.set_quoted('BINFMT_DIR',                                 binfmtdir)
 conf.set_quoted('BOOTLIBDIR',                                 bootlibdir)
-conf.set_quoted('CATALOG_DATABASE',                           join_paths(catalogstatedir, 'database'))
+conf.set_quoted('CATALOG_DATABASE',                           catalogstatedir / 'database')
 conf.set_quoted('CERTIFICATE_ROOT',                           get_option('certificate-root'))
 conf.set_quoted('DOC_DIR',                                    docdir)
-conf.set_quoted('DOCUMENT_ROOT',                              join_paths(pkgdatadir, 'gatewayd'))
+conf.set_quoted('DOCUMENT_ROOT',                              pkgdatadir / 'gatewayd')
 conf.set_quoted('ENVIRONMENT_DIR',                            environmentdir)
 conf.set_quoted('INCLUDE_DIR',                                includedir)
 conf.set_quoted('LIBDIR',                                     libdir)
 conf.set_quoted('MODPROBE_DIR',                               modprobedir)
 conf.set_quoted('MODULESLOAD_DIR',                            modulesloaddir)
 conf.set_quoted('PKGSYSCONFDIR',                              pkgsysconfdir)
-conf.set_quoted('POLKIT_AGENT_BINARY_PATH',                   join_paths(bindir, 'pkttyagent'))
+conf.set_quoted('POLKIT_AGENT_BINARY_PATH',                   bindir / 'pkttyagent')
 conf.set_quoted('PREFIX',                                     prefixdir)
-conf.set_quoted('RANDOM_SEED',                                join_paths(randomseeddir, 'random-seed'))
+conf.set_quoted('RANDOM_SEED',                                randomseeddir / 'random-seed')
 conf.set_quoted('RANDOM_SEED_DIR',                            randomseeddir)
 conf.set_quoted('RC_LOCAL_PATH',                              get_option('rc-local'))
 conf.set_quoted('ROOTBINDIR',                                 rootbindir)
@@ -241,29 +241,29 @@ conf.set_quoted('ROOTPREFIX',                                 rootprefixdir)
 conf.set_quoted('ROOTPREFIX_NOSLASH',                         rootprefixdir_noslash)
 conf.set_quoted('SYSCONF_DIR',                                sysconfdir)
 conf.set_quoted('SYSCTL_DIR',                                 sysctldir)
-conf.set_quoted('SYSTEMCTL_BINARY_PATH',                      join_paths(rootbindir, 'systemctl'))
-conf.set_quoted('SYSTEMD_BINARY_PATH',                        join_paths(rootlibexecdir, 'systemd'))
+conf.set_quoted('SYSTEMCTL_BINARY_PATH',                      rootbindir / 'systemctl')
+conf.set_quoted('SYSTEMD_BINARY_PATH',                        rootlibexecdir / 'systemd')
 conf.set_quoted('SYSTEMD_CATALOG_DIR',                        catalogdir)
-conf.set_quoted('SYSTEMD_CGROUPS_AGENT_PATH',                 join_paths(rootlibexecdir, 'systemd-cgroups-agent'))
-conf.set_quoted('SYSTEMD_CRYPTSETUP_PATH',                    join_paths(rootlibexecdir, 'systemd-cryptsetup'))
-conf.set_quoted('SYSTEMD_EXPORT_PATH',                        join_paths(rootlibexecdir, 'systemd-export'))
-conf.set_quoted('SYSTEMD_FSCK_PATH',                          join_paths(rootlibexecdir, 'systemd-fsck'))
-conf.set_quoted('SYSTEMD_GROWFS_PATH',                        join_paths(rootlibexecdir, 'systemd-growfs'))
-conf.set_quoted('SYSTEMD_HOMEWORK_PATH',                      join_paths(rootlibexecdir, 'systemd-homework'))
-conf.set_quoted('SYSTEMD_IMPORT_FS_PATH',                     join_paths(rootlibexecdir, 'systemd-import-fs'))
-conf.set_quoted('SYSTEMD_IMPORT_PATH',                        join_paths(rootlibexecdir, 'systemd-import'))
-conf.set_quoted('SYSTEMD_KBD_MODEL_MAP',                      join_paths(pkgdatadir, 'kbd-model-map'))
-conf.set_quoted('SYSTEMD_LANGUAGE_FALLBACK_MAP',              join_paths(pkgdatadir, 'language-fallback-map'))
-conf.set_quoted('SYSTEMD_MAKEFS_PATH',                        join_paths(rootlibexecdir, 'systemd-makefs'))
-conf.set_quoted('SYSTEMD_PULL_PATH',                          join_paths(rootlibexecdir, 'systemd-pull'))
-conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH',               join_paths(rootlibexecdir, 'systemd-shutdown'))
-conf.set_quoted('SYSTEMD_STDIO_BRIDGE_BINARY_PATH',           join_paths(bindir, 'systemd-stdio-bridge'))
-conf.set_quoted('SYSTEMD_TEST_DATA',                          join_paths(testsdir, 'testdata'))
-conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', join_paths(rootbindir, 'systemd-tty-ask-password-agent'))
-conf.set_quoted('SYSTEMD_UPDATE_HELPER_PATH',                 join_paths(rootlibexecdir, 'systemd-update-helper'))
-conf.set_quoted('SYSTEMD_USERWORK_PATH',                      join_paths(rootlibexecdir, 'systemd-userwork'))
-conf.set_quoted('SYSTEMD_VERITYSETUP_PATH',                   join_paths(rootlibexecdir, 'systemd-veritysetup'))
-conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR',                     join_paths(pkgsysconfdir, 'system'))
+conf.set_quoted('SYSTEMD_CGROUPS_AGENT_PATH',                 rootlibexecdir / 'systemd-cgroups-agent')
+conf.set_quoted('SYSTEMD_CRYPTSETUP_PATH',                    rootlibexecdir / 'systemd-cryptsetup')
+conf.set_quoted('SYSTEMD_EXPORT_PATH',                        rootlibexecdir / 'systemd-export')
+conf.set_quoted('SYSTEMD_FSCK_PATH',                          rootlibexecdir / 'systemd-fsck')
+conf.set_quoted('SYSTEMD_GROWFS_PATH',                        rootlibexecdir / 'systemd-growfs')
+conf.set_quoted('SYSTEMD_HOMEWORK_PATH',                      rootlibexecdir / 'systemd-homework')
+conf.set_quoted('SYSTEMD_IMPORT_FS_PATH',                     rootlibexecdir / 'systemd-import-fs')
+conf.set_quoted('SYSTEMD_IMPORT_PATH',                        rootlibexecdir / 'systemd-import')
+conf.set_quoted('SYSTEMD_KBD_MODEL_MAP',                      pkgdatadir / 'kbd-model-map')
+conf.set_quoted('SYSTEMD_LANGUAGE_FALLBACK_MAP',              pkgdatadir / 'language-fallback-map')
+conf.set_quoted('SYSTEMD_MAKEFS_PATH',                        rootlibexecdir / 'systemd-makefs')
+conf.set_quoted('SYSTEMD_PULL_PATH',                          rootlibexecdir / 'systemd-pull')
+conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH',               rootlibexecdir / 'systemd-shutdown')
+conf.set_quoted('SYSTEMD_STDIO_BRIDGE_BINARY_PATH',           bindir / 'systemd-stdio-bridge')
+conf.set_quoted('SYSTEMD_TEST_DATA',                          testsdir / 'testdata')
+conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', rootbindir / 'systemd-tty-ask-password-agent')
+conf.set_quoted('SYSTEMD_UPDATE_HELPER_PATH',                 rootlibexecdir / 'systemd-update-helper')
+conf.set_quoted('SYSTEMD_USERWORK_PATH',                      rootlibexecdir / 'systemd-userwork')
+conf.set_quoted('SYSTEMD_VERITYSETUP_PATH',                   rootlibexecdir / 'systemd-veritysetup')
+conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR',                     pkgsysconfdir / 'system')
 conf.set_quoted('SYSTEM_DATA_UNIT_DIR',                       systemunitdir)
 conf.set_quoted('SYSTEM_ENV_GENERATOR_DIR',                   systemenvgeneratordir)
 conf.set_quoted('SYSTEM_GENERATOR_DIR',                       systemgeneratordir)
@@ -278,13 +278,13 @@ conf.set_quoted('UDEVLIBEXECDIR',                             udevlibexecdir)
 conf.set_quoted('UDEV_HWDB_DIR',                              udevhwdbdir)
 conf.set_quoted('UDEV_RULES_DIR',                             udevrulesdir)
 conf.set_quoted('UPDATE_HELPER_USER_TIMEOUT',                 get_option('update-helper-user-timeout'))
-conf.set_quoted('USER_CONFIG_UNIT_DIR',                       join_paths(pkgsysconfdir, 'user'))
+conf.set_quoted('USER_CONFIG_UNIT_DIR',                       pkgsysconfdir / 'user')
 conf.set_quoted('USER_DATA_UNIT_DIR',                         userunitdir)
 conf.set_quoted('USER_ENV_GENERATOR_DIR',                     userenvgeneratordir)
 conf.set_quoted('USER_GENERATOR_DIR',                         usergeneratordir)
-conf.set_quoted('USER_KEYRING_PATH',                          join_paths(pkgsysconfdir, 'import-pubring.gpg'))
+conf.set_quoted('USER_KEYRING_PATH',                          pkgsysconfdir / 'import-pubring.gpg')
 conf.set_quoted('USER_PRESET_DIR',                            userpresetdir)
-conf.set_quoted('VENDOR_KEYRING_PATH',                        join_paths(rootlibexecdir, 'import-pubring.gpg'))
+conf.set_quoted('VENDOR_KEYRING_PATH',                        rootlibexecdir / 'import-pubring.gpg')
 
 conf.set('ANSI_OK_COLOR',                                     'ANSI_' + get_option('ok-color').underscorify().to_upper())
 conf.set10('ENABLE_URLIFY',                                   get_option('urlify'))
@@ -345,6 +345,7 @@ possible_common_cc_flags = [
         '-Werror=shift-count-overflow',
         '-Werror=shift-overflow=2',
         '-Werror=undef',
+        '-Werror=unused-function',
         '-Wfloat-equal',
         '-Wimplicit-fallthrough=5',
         '-Winit-self',
@@ -382,7 +383,7 @@ if cc.get_id() == 'gcc'
 endif
 
 # --as-needed and --no-undefined are provided by meson by default,
-# run mesonconf to see what is enabled
+# run 'meson configure' to see what is enabled
 possible_link_flags = [
         '-Wl,-z,relro',
         '-Wl,-z,now',
@@ -597,7 +598,7 @@ test_efi_create_disk_sh = find_program('test/test-efi-create-disk.sh')
 mkdir_p = 'mkdir -p $DESTDIR/@0@'
 splash_bmp = files('test/splash.bmp')
 
-# if -Dxxx-path option is found, use that. Otherwise, check in $PATH,
+# If -Dxxx-path option is found, use that. Otherwise, check in $PATH,
 # /usr/sbin, /sbin, and fall back to the default from middle column.
 progs = [['quotaon',    '/usr/sbin/quotaon'    ],
          ['quotacheck', '/usr/sbin/quotacheck' ],
@@ -722,6 +723,8 @@ if time_epoch == -1
 endif
 conf.set('TIME_EPOCH', time_epoch)
 
+conf.set('CLOCK_VALID_RANGE_USEC_MAX', get_option('clock-valid-range-usec-max'))
+
 foreach tuple : [['system-alloc-uid-min', 'SYS_UID_MIN', 1],  # Also see login.defs(5).
                  ['system-uid-max',       'SYS_UID_MAX', 999],
                  ['system-alloc-gid-min', 'SYS_GID_MIN', 1],
@@ -909,10 +912,6 @@ conf.set10('LOG_TRACE', get_option('log-trace'))
 default_user_path = get_option('user-path')
 if default_user_path != ''
         conf.set_quoted('DEFAULT_USER_PATH', default_user_path)
-        default_user_path_display = default_user_path
-else
-        # meson 0.49 fails when ?: is used in .format()
-        default_user_path_display = '(same as system services)'
 endif
 
 
@@ -952,9 +951,11 @@ if want_bpf_framework == 'false'
 else
         clang = find_program('clang', required : bpf_framework_required)
         llvm_strip = find_program('llvm-strip', required : bpf_framework_required)
-        # Debian installs this in /usr/sbin/ which is not in $PATH
-        # FIXME: use the 'dirs' parameter once we bump Meson version to >= 0.53
+
+        # Debian installs this in /usr/sbin/ which is not in $PATH.
+        # We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
         bpftool = find_program('bpftool', '/usr/sbin/bpftool', required : bpf_framework_required)
+
         bpf_arches = ['x86_64']
         deps_found = libbpf.found() and clang.found() and llvm_strip.found() and bpftool.found()
         # Can build BPF program from source code in restricted C
@@ -1778,7 +1779,7 @@ if conf.get('HAVE_LIBCRYPTSETUP_PLUGINS') == 1
                         'cryptsetup-token-systemd-tpm2',
                         link_args : ['-shared',
                                      '-Wl,--version-script=' + cryptsetup_token_sym_path],
-                        dependencies : libshared_deps + [libcryptsetup],
+                        dependencies : libshared_deps + [libcryptsetup, versiondep],
                         link_with : [libshared],
                         link_whole : [cryptsetup_token_systemd_tpm2_static],
                         link_depends : cryptsetup_token_sym,
@@ -1786,6 +1787,34 @@ if conf.get('HAVE_LIBCRYPTSETUP_PLUGINS') == 1
                         install : true,
                         install_dir : libcryptsetup_plugins_dir)
         endif
+
+        if conf.get('HAVE_LIBFIDO2') == 1
+                cryptsetup_token_systemd_fido2 = shared_library(
+                        'cryptsetup-token-systemd-fido2',
+                        link_args : ['-shared',
+                                     '-Wl,--version-script=' + cryptsetup_token_sym_path],
+                        dependencies : libshared_deps + [libcryptsetup, versiondep],
+                        link_with : [libshared],
+                        link_whole : [cryptsetup_token_systemd_fido2_static],
+                        link_depends : cryptsetup_token_sym,
+                        install_rpath : rootlibexecdir,
+                        install : true,
+                        install_dir : libcryptsetup_plugins_dir)
+        endif
+
+        if conf.get('HAVE_P11KIT') == 1
+                cryptsetup_token_systemd_pkcs11 = shared_library(
+                        'cryptsetup-token-systemd-pkcs11',
+                        link_args : ['-shared',
+                                     '-Wl,--version-script=' + cryptsetup_token_sym_path],
+                        dependencies : libshared_deps + [libcryptsetup, versiondep],
+                        link_with : [libshared],
+                        link_whole : [cryptsetup_token_systemd_pkcs11_static],
+                        link_depends : cryptsetup_token_sym,
+                        install_rpath : rootlibexecdir,
+                        install : true,
+                        install_dir : libcryptsetup_plugins_dir)
+        endif
 endif
 
 ############################################################
@@ -1859,7 +1888,7 @@ foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'],
                 module = tuple[0]
 
                 sym = 'src/nss-@0@/nss-@0@.sym'.format(module)
-                version_script_arg = join_paths(project_source_root, sym)
+                version_script_arg = project_source_root / sym
 
                 sources = ['src/nss-@0@/nss-@0@.c'.format(module)]
                 if tuple.length() > 2
@@ -1924,8 +1953,8 @@ executable(
         install_dir : rootlibexecdir)
 
 meson.add_install_script(meson_make_symlink,
-                         join_paths(rootlibexecdir, 'systemd'),
-                         join_paths(rootsbindir, 'init'))
+                         rootlibexecdir / 'systemd',
+                         rootsbindir / 'init')
 
 public_programs += executable(
         'systemd-analyze',
@@ -2030,8 +2059,8 @@ if conf.get('ENABLE_ENVIRONMENT_D') == 1
                 install_dir : userenvgeneratordir)
 
         meson.add_install_script(meson_make_symlink,
-                                 join_paths(sysconfdir, 'environment'),
-                                 join_paths(environmentdir, '99-environment.conf'))
+                                 sysconfdir / 'environment',
+                                 environmentdir / '99-environment.conf')
 endif
 
 if conf.get('ENABLE_HIBERNATE') == 1
@@ -2102,12 +2131,12 @@ if conf.get('ENABLE_RESOLVE') == 1
                 install : true)
 
         meson.add_install_script(meson_make_symlink,
-                                 join_paths(bindir, 'resolvectl'),
-                                 join_paths(rootsbindir, 'resolvconf'))
+                                 bindir / 'resolvectl',
+                                 rootsbindir / 'resolvconf')
 
         meson.add_install_script(meson_make_symlink,
-                                 join_paths(bindir, 'resolvectl'),
-                                 join_paths(bindir, 'systemd-resolve'))
+                                 bindir / 'resolvectl',
+                                 bindir / 'systemd-resolve')
 endif
 
 if conf.get('ENABLE_LOGIND') == 1
@@ -2146,7 +2175,7 @@ if conf.get('ENABLE_LOGIND') == 1
                 install_dir : rootbindir)
 
         if conf.get('HAVE_PAM') == 1
-                version_script_arg = join_paths(project_source_root, pam_systemd_sym)
+                version_script_arg = project_source_root / pam_systemd_sym
                 pam_systemd = shared_library(
                         'pam_systemd',
                         pam_systemd_c,
@@ -2364,7 +2393,7 @@ if conf.get('ENABLE_HOMED') == 1
                 install_dir : rootbindir)
 
         if conf.get('HAVE_PAM') == 1
-                version_script_arg = join_paths(project_source_root, pam_systemd_home_sym)
+                version_script_arg = project_source_root / pam_systemd_home_sym
                 pam_systemd = shared_library(
                         'pam_systemd_home',
                         pam_systemd_home_c,
@@ -2387,13 +2416,13 @@ endif
 foreach alias : (['halt', 'poweroff', 'reboot', 'shutdown'] +
                  (conf.get('HAVE_SYSV_COMPAT') == 1 ? ['runlevel', 'telinit'] : []))
         meson.add_install_script(meson_make_symlink,
-                                 join_paths(rootbindir, 'systemctl'),
-                                 join_paths(rootsbindir, alias))
+                                 rootbindir / 'systemctl',
+                                 rootsbindir / alias)
 endforeach
 
 meson.add_install_script(meson_make_symlink,
-                         join_paths(rootbindir, 'udevadm'),
-                         join_paths(rootlibexecdir, 'systemd-udevd'))
+                         rootbindir / 'udevadm',
+                         rootlibexecdir / 'systemd-udevd')
 
 if conf.get('ENABLE_BACKLIGHT') == 1
         executable(
@@ -2830,7 +2859,7 @@ if conf.get('ENABLE_BINFMT') == 1
                                  mkdir_p.format(binfmtdir))
         if install_sysconfdir
                 meson.add_install_script('sh', '-c',
-                                         mkdir_p.format(join_paths(sysconfdir, 'binfmt.d')))
+                                         mkdir_p.format(sysconfdir / 'binfmt.d'))
         endif
 endif
 
@@ -3107,7 +3136,7 @@ public_programs += executable(
         install : true)
 
 meson.add_install_script(meson_make_symlink,
-                         'systemd-mount', join_paths(bindir, 'systemd-umount'))
+                         'systemd-mount', bindir / 'systemd-umount')
 
 public_programs += executable(
         'systemd-run',
@@ -3309,7 +3338,7 @@ if conf.get('HAVE_KMOD') == 1
                                  mkdir_p.format(modulesloaddir))
         if install_sysconfdir
                 meson.add_install_script('sh', '-c',
-                                         mkdir_p.format(join_paths(sysconfdir, 'modules-load.d')))
+                                         mkdir_p.format(sysconfdir / 'modules-load.d'))
         endif
 endif
 
@@ -3389,8 +3418,8 @@ custom_target(
         output : 'systemd-runtest.env',
         command : [sh, '-c',
                    '{ echo SYSTEMD_TEST_DATA=@0@; echo SYSTEMD_CATALOG_DIR=@1@; } >@OUTPUT@'.format(
-                           join_paths(project_source_root, 'test'),
-                           join_paths(project_build_root, 'catalog'))],
+                           project_source_root / 'test',
+                           project_build_root / 'catalog')],
         build_by_default : true)
 
 test_cflags = ['-DTEST_CODE=1']
@@ -3432,7 +3461,7 @@ foreach tuple : tests
                         build_by_default : want_tests != 'false',
                         install_rpath : rootlibexecdir,
                         install : install_tests,
-                        install_dir : join_paths(testsdir, type))
+                        install_dir : testsdir / type)
 
                 if type == 'manual'
                         message('@0@ is a manual test'.format(name))
@@ -3548,16 +3577,13 @@ foreach tuple : fuzzers
                         if b == name
                                 test('@0@_@1@'.format(b, c),
                                      exe,
-                                     args : [join_paths(project_source_root, p)])
+                                     args : [project_source_root / p])
                         endif
                 endforeach
         endif
 endforeach
 
-run_target(
-        'fuzzers',
-        depends : fuzzer_exes,
-        command : ['true'])
+alias_target('fuzzers', fuzzer_exes)
 
 ############################################################
 
@@ -3610,8 +3636,8 @@ if get_option('mode') == 'developer' and want_tests != 'false' and jekyll.found(
         test('github-pages',
              jekyll,
              args : ['build',
-                     '--source', join_paths(project_source_root, 'docs'),
-                     '--destination', join_paths(project_build_root, '_site')])
+                     '--source', project_source_root / 'docs',
+                     '--destination', project_build_root / '_site'])
 endif
 
 ############################################################
@@ -3661,7 +3687,7 @@ foreach tuple : sanitizers
                                                 output : name,
                                                 depends : build,
                                                 command : [ln, '-fs',
-                                                           join_paths(build.full_path(), b),
+                                                           build.full_path() / b,
                                                            '@OUTPUT@'],
                                                 build_by_default : true)
                                 else
@@ -3676,7 +3702,7 @@ foreach tuple : sanitizers
                                      env : ['UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1'],
                                      timeout : 60,
                                      args : [exe.full_path(),
-                                             join_paths(project_source_root, p)])
+                                             project_source_root / p])
                         endif
                 endforeach
         endif
@@ -3736,100 +3762,67 @@ run_target(
         depends : [man, libsystemd, libudev],
         command : [check_api_docs_sh, libsystemd.full_path(), libudev.full_path()])
 
-############################################################
-
-if dbus_docs.length() > 0
-        custom_target(
-                'update-dbus-docs',
-                output : 'update-dbus-docs',
-                command : [update_dbus_docs_py,
-                           '--build-dir=@0@'.format(project_build_root),
-                           '@INPUT@'],
-                input : dbus_docs)
-
-        if conf.get('BUILD_MODE_DEVELOPER') == 1
-                test('dbus-docs-fresh',
-                     update_dbus_docs_py,
-                     args : ['--build-dir=@0@'.format(project_build_root),
-                             '--test'] + dbus_docs)
-        endif
-endif
-
-custom_target(
-        'update-man-rules',
-        output : 'update-man-rules',
-        command : [sh, '-c',
-                   'cd @0@ && '.format(meson.build_root()) +
-                   'python3 @0@/tools/update-man-rules.py $(find @0@ -wholename "*/man/*.xml") >t && '.format(project_source_root) +
-                   'mv t @0@/man/rules/meson.build'.format(meson.current_source_dir())],
-        depends : custom_entities_ent)
+alias_target('update-dbus-docs', update_dbus_docs)
+alias_target('update-man-rules', update_man_rules)
 
 ############################################################
-watchdog_opt = service_watchdog == '' ? 'disabled' : service_watchdog
-
-status = [
-        '@0@ @1@'.format(meson.project_name(), meson.project_version()),
-
-        'build mode:                        @0@'.format(get_option('mode')),
-        'split /usr:                        @0@'.format(split_usr),
-        'split bin-sbin:                    @0@'.format(split_bin),
-        'prefix directory:                  @0@'.format(prefixdir),
-        'rootprefix directory:              @0@'.format(rootprefixdir),
-        'sysconf directory:                 @0@'.format(sysconfdir),
-        'include directory:                 @0@'.format(includedir),
-        'lib directory:                     @0@'.format(libdir),
-        'rootlib directory:                 @0@'.format(rootlibdir),
-        'SysV init scripts:                 @0@'.format(sysvinit_path),
-        'SysV rc?.d directories:            @0@'.format(sysvrcnd_path),
-        'PAM modules directory:             @0@'.format(pamlibdir),
-        'PAM configuration directory:       @0@'.format(pamconfdir),
-        'libcryptsetup plugins directory:   @0@'.format(libcryptsetup_plugins_dir),
-        'RPM macros directory:              @0@'.format(rpmmacrosdir),
-        'modprobe.d directory:              @0@'.format(modprobedir),
-        'D-Bus policy directory:            @0@'.format(dbuspolicydir),
-        'D-Bus session directory:           @0@'.format(dbussessionservicedir),
-        'D-Bus system directory:            @0@'.format(dbussystemservicedir),
-        'bash completions directory:        @0@'.format(bashcompletiondir),
-        'zsh completions directory:         @0@'.format(zshcompletiondir),
-        'extra start script:                @0@'.format(get_option('rc-local')),
-        'debug shell:                       @0@ @ @1@'.format(get_option('debug-shell'),
-                                                              get_option('debug-tty')),
-        'system UIDs:                       <=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_UID_MAX'),
-                                                                        conf.get('SYSTEM_ALLOC_UID_MIN')),
-        'system GIDs:                       <=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_GID_MAX'),
-                                                                        conf.get('SYSTEM_ALLOC_GID_MIN')),
-        'dynamic UIDs:                      @0@…@1@'.format(dynamic_uid_min, dynamic_uid_max),
-        'container UID bases:               @0@…@1@'.format(container_uid_base_min, container_uid_base_max),
-        'static UID/GID allocations:        @0@'.format(' '.join(static_ugids)),
-        '/dev/kvm access mode:              @0@'.format(get_option('dev-kvm-mode')),
-        'render group access mode:          @0@'.format(get_option('group-render-mode')),
-        'certificate root directory:        @0@'.format(get_option('certificate-root')),
-        'support URL:                       @0@'.format(support_url),
-        'nobody user name:                  @0@'.format(nobody_user),
-        'nobody group name:                 @0@'.format(nobody_group),
-        'fallback hostname:                 @0@'.format(get_option('fallback-hostname')),
-
-        'default DNSSEC mode:               @0@'.format(default_dnssec),
-        'default DNS-over-TLS mode:         @0@'.format(default_dns_over_tls),
-        'default mDNS mode:                 @0@'.format(default_mdns),
-        'default LLMNR mode:                @0@'.format(default_llmnr),
-        'default cgroup hierarchy:          @0@'.format(default_hierarchy),
-        'default net.naming-scheme setting: @0@'.format(default_net_naming_scheme),
-        'default KillUserProcesses setting: @0@'.format(kill_user_processes),
-        'default locale:                    @0@'.format(default_locale),
-        'default user $PATH:                @0@'.format(default_user_path_display),
-        'systemd service watchdog:          @0@'.format(watchdog_opt)]
-
-alt_dns_servers = '\n                                            '.join(dns_servers.split(' '))
-alt_ntp_servers = '\n                                            '.join(ntp_servers.split(' '))
-status += [
-        'default DNS servers:               @0@'.format(alt_dns_servers),
-        'default NTP servers:               @0@'.format(alt_ntp_servers)]
 
 alt_time_epoch = run_command('date', '-Is', '-u', '-d',
                              '@@0@'.format(time_epoch)).stdout().strip()
-status += [
-        'time epoch:                        @0@ (@1@)'.format(time_epoch, alt_time_epoch)]
+
+summary({
+        'build mode' :                      get_option('mode'),
+        'split /usr' :                      split_usr,
+        'split bin-sbin' :                  split_bin,
+        'prefix directory' :                prefixdir,
+        'rootprefix directory' :            rootprefixdir,
+        'sysconf directory' :               sysconfdir,
+        'include directory' :               includedir,
+        'lib directory' :                   libdir,
+        'rootlib directory' :               rootlibdir,
+        'SysV init scripts' :               sysvinit_path,
+        'SysV rc?.d directories' :          sysvrcnd_path,
+        'PAM modules directory' :           pamlibdir,
+        'PAM configuration directory' :     pamconfdir,
+        'libcryptsetup plugins directory' : libcryptsetup_plugins_dir,
+        'RPM macros directory' :            rpmmacrosdir,
+        'modprobe.d directory' :            modprobedir,
+        'D-Bus policy directory' :          dbuspolicydir,
+        'D-Bus session directory' :         dbussessionservicedir,
+        'D-Bus system directory' :          dbussystemservicedir,
+        'bash completions directory' :      bashcompletiondir,
+        'zsh completions directory' :       zshcompletiondir,
+        'extra start script' :              get_option('rc-local'),
+        'debug shell' :                     '@0@ @ @1@'.format(get_option('debug-shell'),
+                                                               get_option('debug-tty')),
+        'system UIDs' :                     '<=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_UID_MAX'),
+                                                                         conf.get('SYSTEM_ALLOC_UID_MIN')),
+        'system GIDs' :                     '<=@0@ (alloc >=@1@)'.format(conf.get('SYSTEM_GID_MAX'),
+                                                                         conf.get('SYSTEM_ALLOC_GID_MIN')),
+        'dynamic UIDs' :                    '@0@…@1@'.format(dynamic_uid_min, dynamic_uid_max),
+        'container UID bases' :             '@0@…@1@'.format(container_uid_base_min, container_uid_base_max),
+        'static UID/GID allocations' :      ' '.join(static_ugids),
+        '/dev/kvm access mode' :            get_option('dev-kvm-mode'),
+        'render group access mode' :        get_option('group-render-mode'),
+        'certificate root directory' :      get_option('certificate-root'),
+        'support URL' :                     support_url,
+        'nobody user name' :                nobody_user,
+        'nobody group name' :               nobody_group,
+        'fallback hostname' :               get_option('fallback-hostname'),
+        'default DNSSEC mode' :             default_dnssec,
+        'default DNS-over-TLS mode' :       default_dns_over_tls,
+        'default mDNS mode' :               default_mdns,
+        'default LLMNR mode' :              default_llmnr,
+        'default DNS servers' :             dns_servers.split(' '),
+        'default NTP servers' :             ntp_servers.split(' '),
+        'default cgroup hierarchy' :        default_hierarchy,
+        'default net.naming-scheme value' : default_net_naming_scheme,
+        'default KillUserProcesses value' : kill_user_processes,
+        'default locale' :                  default_locale,
+        'default user $PATH' :
+                default_user_path != '' ? default_user_path : '(same as system services)',
+        'systemd service watchdog' :        service_watchdog == '' ? 'disabled' : service_watchdog,
+        'time epoch' :                      '@0@ (@1@)'.format(time_epoch, alt_time_epoch)})
 
 # TODO:
 # CFLAGS:   ${OUR_CFLAGS} ${CFLAGS}
@@ -3837,15 +3830,17 @@ status += [
 # LDFLAGS:  ${OUR_LDFLAGS} ${LDFLAGS}
 
 if conf.get('ENABLE_EFI') == 1
-        status += 'efi arch:                          @0@'.format(efi_arch)
+        summary({'efi arch' : efi_arch},
+                section : 'Extensible Firmware Interface')
 
         if have_gnu_efi
-                status += [
-                        'EFI machine type:                  @0@'.format(EFI_MACHINE_TYPE_NAME),
-                        'EFI CC                             @0@'.format(' '.join(efi_cc)),
-                        'EFI lds:                           @0@'.format(efi_lds),
-                        'EFI crt0:                          @0@'.format(efi_crt0),
-                        'EFI include directory:             @0@'.format(efi_incdir)]
+                summary({
+                        'EFI machine type' :                EFI_MACHINE_TYPE_NAME,
+                        'EFI CC' :                          '@0@'.format(' '.join(efi_cc)),
+                        'EFI lds' :                         efi_lds,
+                        'EFI crt0' :                        efi_crt0,
+                        'EFI include directory' :           efi_incdir},
+                        section : 'Extensible Firmware Interface')
         endif
 endif
 
@@ -4000,13 +3995,10 @@ else
         missing += 'DNS-over-TLS'
 endif
 
-status += [
-        '',
-        'enabled features: @0@'.format(', '.join(found)),
-        '',
-        'disabled features: @0@'.format(', '.join(missing)),
-        '']
-message('\n         '.join(status))
+summary({
+        'enabled' :  ', '.join(found),
+        'disabled' : ', '.join(missing)},
+        section : 'Features')
 
 if rootprefixdir != rootprefix_default
         warning('\n' +
index 0b01fb2cbf2665edcf2d88acfb731dca2f721ffb..b122f275da215c4ca332ebde5472a4233146b25d 100644 (file)
@@ -208,6 +208,8 @@ option('status-unit-format-default', type : 'combo',
        description : 'use unit name or description in messages by default')
 option('time-epoch', type : 'integer', value : '-1',
        description : 'time epoch for time clients')
+option('clock-valid-range-usec-max', type : 'integer', value : '473364000000000', # 15 years
+       description : 'maximum value in microseconds for the difference between RTC and epoch, exceeding which is considered an RTC error')
 
 option('system-alloc-uid-min', type : 'integer', value : '-1',
        description : 'minimum system UID used when allocating')
@@ -428,6 +430,14 @@ option('sbat-distro-version', type : 'string',
        description : 'SBAT distribution package version, e.g. 248-7.fc34')
 option('sbat-distro-url', type : 'string',
        description : 'SBAT distribution URL, e.g. https://src.fedoraproject.org/rpms/systemd')
+option('efi-color-normal', type : 'string', value : 'lightgray,black',
+       description : 'general boot loader color in "foreground,background" form, see constants from eficon.h')
+option('efi-color-entry', type : 'string', value : 'lightgray,black',
+       description : 'boot loader color for entries')
+option('efi-color-highlight', type : 'string', value : 'black,lightgray',
+       description : 'boot loader color for selected entries')
+option('efi-color-edit', type : 'string', value : 'black,lightgray',
+       description : 'boot loader color for option line edit')
 
 option('bashcompletiondir', type : 'string',
        description : 'directory for bash completion scripts ["no" disables]')
index b0e60f72176b38716565ae5e784a4da0517aba2c..ccfd79e8dabad0a8052ab02f095eee544b7acc4c 100644 (file)
@@ -12,7 +12,7 @@ if conf.get('ENABLE_NETWORKD') == 1
 
         if install_sysconfdir
                 meson.add_install_script('sh', '-c',
-                                         mkdir_p.format(join_paths(sysconfdir, 'systemd/network')))
+                                         mkdir_p.format(sysconfdir / 'systemd/network'))
         endif
 endif
 
index 8cdd49fe174a35ec1394c242db8b7cf623b54aa0..89d8467e973bdfedc0e0e8e70a29e6746d76ee8a 100644 (file)
@@ -10,7 +10,7 @@ msgstr ""
 "Project-Id-Version: systemd\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2021-02-25 18:41+0000\n"
+"PO-Revision-Date: 2021-08-17 07:04+0000\n"
 "Last-Translator: Gustavo Costa <xfgusta@gmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://translate.fedoraproject.org/"
 "projects/systemd/master/pt_BR/>\n"
@@ -19,7 +19,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.4.2\n"
+"X-Generator: Weblate 4.7.2\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -316,7 +316,7 @@ msgstr ""
 #: src/login/org.freedesktop.login1.policy:117
 msgid "Allow applications to inhibit system handling of the reboot key"
 msgstr ""
-"Permitir que aplicativos inibam o sistema de gerenciar o botão de "
+"Permitir que os aplicativos inibam o gerenciamento do sistema da tecla de "
 "reinicialização"
 
 #: src/login/org.freedesktop.login1.policy:118
@@ -324,8 +324,8 @@ msgid ""
 "Authentication is required for an application to inhibit system handling of "
 "the reboot key."
 msgstr ""
-"É necessária autenticação para que um aplicativo iniba o sistema de "
-"gerenciar o botão de reinicialização."
+"A autenticação é necessária para um aplicativo para inibir o gerenciamento "
+"do sistema da tecla de reinicialização."
 
 #: src/login/org.freedesktop.login1.policy:128
 msgid "Allow non-logged-in user to run programs"
index 1d6b64e6606772dd61ec5c23a92a65e0ad2ae5b0..18eec40018926df8213acc4e0a47bdcc1575767d 100644 (file)
--- a/po/si.po
+++ b/po/si.po
@@ -7,13 +7,16 @@ msgstr ""
 "Project-Id-Version: systemd\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
+"PO-Revision-Date: 2021-08-19 07:04+0000\n"
+"Last-Translator: Hela Basa <r45xveza@pm.me>\n"
+"Language-Team: Sinhala <https://translate.fedoraproject.org/projects/systemd/"
+"master/si/>\n"
 "Language: si\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n > 1;\n"
+"X-Generator: Weblate 4.7.2\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -773,7 +776,7 @@ msgstr ""
 
 #: src/timedate/org.freedesktop.timedate1.policy:22
 msgid "Set system time"
-msgstr ""
+msgstr "පද්ධතියේ වේලාව සකසන්න"
 
 #: src/timedate/org.freedesktop.timedate1.policy:23
 msgid "Authentication is required to set the system time."
index f47d9491325c4f070fea58b4414cab7dff1c3abe..4b297aa43da6e989660cc7df31212d5a90b7e2b0 100644 (file)
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2019-10-23 19:19+0800\n"
+"PO-Revision-Date: 2021-08-10 11:36+0800\n"
 "Last-Translator: pan93412 <pan93412@gmail.com>\n"
 "Language-Team: Chinese <chinese-l10n@googlegroups.com>\n"
 "Language: zh_TW\n"
@@ -16,7 +16,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Lokalize 19.08.2\n"
+"X-Generator: Poedit 2.2.1\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -63,67 +63,53 @@ msgstr "重新載入 systemd 狀態需要驗證。"
 
 #: src/home/org.freedesktop.home1.policy:13
 msgid "Create a home area"
-msgstr ""
+msgstr "創建一個家區域"
 
 #: src/home/org.freedesktop.home1.policy:14
-#, fuzzy
-#| msgid "Authentication is required to set NTP servers."
 msgid "Authentication is required to create a user's home area."
-msgstr "設定 NTP 伺服器需要身份驗證。"
+msgstr "創建用戶家區域需要身份驗證。"
 
 #: src/home/org.freedesktop.home1.policy:23
 msgid "Remove a home area"
-msgstr ""
+msgstr "移除一個家區域"
 
 #: src/home/org.freedesktop.home1.policy:24
-#, fuzzy
-#| msgid "Authentication is required to set NTP servers."
 msgid "Authentication is required to remove a user's home area."
-msgstr "設定 NTP 伺服器需要身份驗證。"
+msgstr "移除用戶家區域需要身份驗證。"
 
 #: src/home/org.freedesktop.home1.policy:33
 msgid "Check credentials of a home area"
-msgstr ""
+msgstr "檢查家區域憑證"
 
 #: src/home/org.freedesktop.home1.policy:34
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to attach or detach a portable service image."
 msgid ""
 "Authentication is required to check credentials against a user's home area."
-msgstr "連結或取消連結可攜式服務映像需要身份驗證。"
+msgstr "根據用戶家區域檢查憑證需要認證。"
 
 #: src/home/org.freedesktop.home1.policy:43
 msgid "Update a home area"
-msgstr ""
+msgstr "更新一個家區域"
 
 #: src/home/org.freedesktop.home1.policy:44
-#, fuzzy
-#| msgid "Authentication is required to attach a device to a seat."
 msgid "Authentication is required to update a user's home area."
-msgstr "將設備連接到座位需要驗證。"
+msgstr "更新用戶家區域需要認證。"
 
 #: src/home/org.freedesktop.home1.policy:53
 msgid "Resize a home area"
-msgstr ""
+msgstr "調整家區域大小"
 
 #: src/home/org.freedesktop.home1.policy:54
-#, fuzzy
-#| msgid "Authentication is required to set NTP servers."
 msgid "Authentication is required to resize a user's home area."
-msgstr "設å®\9a NTP ä¼ºæ\9c\8då\99¨é\9c\80è¦\81身份é©\97證。"
+msgstr "調æ\95´å®¶å\8d\80å\9f\9f大å°\8fé\9c\80è¦\81èª\8d證。"
 
 #: src/home/org.freedesktop.home1.policy:63
 msgid "Change password of a home area"
-msgstr ""
+msgstr "更改家區域的密碼"
 
 #: src/home/org.freedesktop.home1.policy:64
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to manage active sessions, users and seats."
 msgid ""
 "Authentication is required to change the password of a user's home area."
-msgstr "管理活躍的工作階段、使用者與座位需要驗證。"
+msgstr "更改家區域密碼需要認證。"
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set hostname"
@@ -283,20 +269,14 @@ msgid ""
 msgstr "要讓應用程式阻止系統處理上蓋開關需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:117
-#, fuzzy
-#| msgid "Allow applications to inhibit system handling of the power key"
 msgid "Allow applications to inhibit system handling of the reboot key"
-msgstr "å\85\81許æ\87\89ç\94¨ç¨\8bå¼\8fé\98»æ­¢ç³»çµ±è\99\95ç\90\86é\9b»æº\90鍵"
+msgstr "å\85\81許æ\87\89ç\94¨ç¨\8bå¼\8fé\98»æ­¢ç³»çµ±è\99\95ç\90\86é\87\8då\95\93鍵"
 
 #: src/login/org.freedesktop.login1.policy:118
-#, fuzzy
-#| msgid ""
-#| "Authentication is required for an application to inhibit system handling "
-#| "of the power key."
 msgid ""
 "Authentication is required for an application to inhibit system handling of "
 "the reboot key."
-msgstr "è¦\81è®\93æ\87\89ç\94¨ç¨\8bå¼\8fé\98»æ­¢ç³»çµ±è\99\95ç\90\86é\9b»æº\90鍵需要驗證。"
+msgstr "è¦\81è®\93æ\87\89ç\94¨ç¨\8bå¼\8fé\98»æ­¢ç³»çµ±è\99\95ç\90\86é\87\8då\95\93鍵需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:128
 msgid "Allow non-logged-in user to run programs"
@@ -409,14 +389,10 @@ msgid "Halt the system while an application is inhibiting this"
 msgstr "在應用程式阻止時停止系統"
 
 #: src/login/org.freedesktop.login1.policy:258
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to hibernate the system while an application "
-#| "is inhibiting this."
 msgid ""
 "Authentication is required to halt the system while an application is "
 "inhibiting this."
-msgstr "當應用程式阻止系統冬眠時將系統冬眠需要驗證。"
+msgstr "當應用程式阻止停止系統時需要驗證。"
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid "Suspend the system"
@@ -538,13 +514,11 @@ msgstr "設定 wall 訊息需要身份驗證"
 
 #: src/login/org.freedesktop.login1.policy:406
 msgid "Change Session"
-msgstr ""
+msgstr "更改會話"
 
 #: src/login/org.freedesktop.login1.policy:407
-#, fuzzy
-#| msgid "Authentication is required to halt the system."
 msgid "Authentication is required to change the virtual terminal."
-msgstr "停止系統需要身份驗證。"
+msgstr "更改虛擬終端需要身份驗證。"
 
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
@@ -720,13 +694,11 @@ msgstr "重設 DNS 設定需要身份驗證。"
 
 #: src/network/org.freedesktop.network1.policy:143
 msgid "DHCP server sends force renew message"
-msgstr ""
+msgstr "DHCP 服務器發送強制更新訊息"
 
 #: src/network/org.freedesktop.network1.policy:144
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
 msgid "Authentication is required to send force renew message."
-msgstr "設定 wall 訊息需要身份驗證"
+msgstr "發送強制更新訊息需要身份驗證。"
 
 #: src/network/org.freedesktop.network1.policy:154
 msgid "Renew dynamic addresses"
@@ -737,26 +709,20 @@ msgid "Authentication is required to renew dynamic addresses."
 msgstr "重新產生動態位址需要身份驗證。"
 
 #: src/network/org.freedesktop.network1.policy:165
-#, fuzzy
-#| msgid "Revert NTP settings"
 msgid "Reload network settings"
-msgstr "é\82\84å\8e\9f NTP 設定"
+msgstr "é\87\8dæ\96°å\8a è¼\89網絡設定"
 
 #: src/network/org.freedesktop.network1.policy:166
-#, fuzzy
-#| msgid "Authentication is required to reset NTP settings."
 msgid "Authentication is required to reload network settings."
-msgstr "重設 NTP 設定需要身份驗證。"
+msgstr "重新加載網絡設定需要身份驗證。"
 
 #: src/network/org.freedesktop.network1.policy:176
 msgid "Reconfigure network interface"
-msgstr ""
+msgstr "重新配置網絡接口"
 
 #: src/network/org.freedesktop.network1.policy:177
-#, fuzzy
-#| msgid "Authentication is required to reboot the system."
 msgid "Authentication is required to reconfigure network interface."
-msgstr "重新啟動系統需要驗證。"
+msgstr "重新配置網絡接口需要驗證。"
 
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
@@ -881,13 +847,9 @@ msgid ""
 msgstr "刪除與 '$(unit)' 相關的檔案及目錄需要身份驗證。"
 
 #: src/core/dbus-unit.c:757
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to send a UNIX signal to the processes of "
-#| "'$(unit)'."
 msgid ""
 "Authentication is required to freeze or thaw the processes of '$(unit)' unit."
-msgstr "å\82³é\80\81 UNIX ä¿¡è\99\9fè\87³「$(unit)」的程序需要身份驗證。"
+msgstr "å\87\8dçµ\90æ\88\96解å\87\8d「$(unit)」的程序需要身份驗證。"
 
 #~ msgid ""
 #~ "Authentication is required to halt the system while an application asked "
index d26087445c9695ded99f3b620e7985a1a64f4099..8a1a08210cfe269be1bc54c0c06635e7c2295927 100644 (file)
@@ -22,6 +22,7 @@ enable systemd-resolved.service
 enable systemd-homed.service
 enable systemd-userdbd.socket
 enable systemd-pstore.service
+enable systemd-boot-update.service
 
 disable console-getty.service
 disable debug-shell.service
index c26b413d926d9ea4d5d06cceb213e00533f57553..0946bc5b54302d1be4e7d14b3d8c4c6c80e5a0c7 100644 (file)
@@ -6,7 +6,7 @@ if bashcompletiondir == ''
         if bash_completion.found()
                 bashcompletiondir = bash_completion.get_pkgconfig_variable('completionsdir')
         else
-                bashcompletiondir = join_paths(datadir, 'bash-completion/completions')
+                bashcompletiondir = datadir / 'bash-completion/completions'
         endif
 endif
 
index 36fcf432ff91b92ed675862802cbeca0ee523feb..6f33d53cfc43278a35596bf80dd68505f7d81a03 100644 (file)
@@ -125,7 +125,7 @@ _systemd_analyze() {
 
     elif __contains_word "$verb" ${VERBS[VERIFY]}; then
         if [[ $cur = -* ]]; then
-            comps='--help --version --system --user --global --man=no --generators=yes'
+            comps='--help --version --system --user --global --man=no --generators=yes --root --image --recursive-errors=no --recursive-errors=yes --recursive-errors=one'
         else
             comps=$( compgen -A file -- "$cur" )
             compopt -o filenames
@@ -144,7 +144,7 @@ _systemd_analyze() {
 
     elif __contains_word "$verb" ${VERBS[SECURITY]}; then
         if [[ $cur = -* ]]; then
-            comps='--help --version --no-pager --system --user -H --host -M --machine'
+            comps='--help --version --no-pager --system --user -H --host -M --machine --offline --threshold'
         else
             if __contains_word "--user" ${COMP_WORDS[*]}; then
                 mode=--user
index 81036f35883726cac2364d831968487a1b6811dc..42103668084f203f4ace50fb85296dc035593a98 100644 (file)
@@ -61,6 +61,7 @@ _udevadm() {
         [MONITOR_STANDALONE]='-k --kernel -u --udev -p --property'
         [MONITOR_ARG]='-s --subsystem-match -t --tag-match'
         [TEST]='-a --action -N --resolve-names'
+        [TEST_BUILTIN]='-a --action'
     )
 
     local verbs=(info trigger settle control monitor test-builtin test)
@@ -215,6 +216,16 @@ _udevadm() {
             ;;
 
         'test-builtin')
+            if __contains_word "$prev" ${OPTS[TEST_BUILTIN]}; then
+                case $prev in
+                    -a|--action)
+                        comps=$( udevadm test-builtin --action help )
+                        ;;
+                esac
+                COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
+                return 0
+            fi
+
             for ((i=0; i < COMP_CWORD; i++)); do
                 if __contains_word "${COMP_WORDS[i]}" "${builtins[@]}"; then
                     builtin=${COMP_WORDS[i]}
@@ -225,7 +236,7 @@ _udevadm() {
             if [[ -z $builtin ]]; then
                 comps="${builtins[@]}"
             elif [[ $cur = -* ]]; then
-                comps="${OPTS[COMMON]}"
+                comps="${OPTS[COMMON]} ${OPTS[TEST_BUILTIN]}"
             else
                 comps=$( __get_all_sysdevs )
                 local IFS=$'\n'
index ce8e6162e810cfa9878b38c5f4595286e74b08be..f91357cb61e03a5d02564d8d9c194f874b9d4cf4 100644 (file)
@@ -87,6 +87,11 @@ _arguments \
     '--system[Operate on system systemd instance]' \
     '--user[Operate on user systemd instance]' \
     '--global[Show global user instance config]' \
+    '--root=[Add support for root argument]:PATH' \
+    '--image=[Add support for discrete images]:PATH' \
+    '--recursive-errors=[When verifying a unit, control dependency verification]:MODE' \
+    '--offline=[Perform a security review of the specified unit file(s)]:BOOL' \
+    '--threshold=[Set a value to compare the overall security exposure level with]: NUMBER' \
     '--no-pager[Do not pipe output into a pager]' \
     '--man=[Do (not) check for existence of man pages]:boolean:(1 0)' \
     '--order[When generating graph for dot, show only order]' \
index a19829c1dee2fa12e0b38d76248166199659a90a..1179f81a8bfa1a30898b7de21d284ac1656518aa 100644 (file)
@@ -23,7 +23,7 @@ _udevadm_trigger(){
         '--dry-run[Do not actually trigger the event.]' \
         '--quiet[Suppress error logging in triggering events.]' \
         '--type=[Trigger a specific type of devices.]:types:(devices subsystems failed)' \
-        '--action=[Type of event to be triggered.]:actions:(add change remove)' \
+        '--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.]' \
@@ -74,7 +74,7 @@ _udevadm_monitor(){
 (( $+functions[_udevadm_test] )) ||
 _udevadm_test(){
     _arguments \
-        '--action=[The action string.]:actions:(add change remove)' \
+        '--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'
@@ -84,14 +84,17 @@ _udevadm_test(){
 _udevadm_test-builtin(){
     if (( CURRENT == 2 )); then
     _arguments \
+        '--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]' \
             '*::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]'
     fi
 }
index f5f9b0f993f044376f74627ecc5a4474bf1b3d57..ce3a304d45c23954c10ca9b903baa582401493d2 100644 (file)
@@ -2,7 +2,7 @@
 
 zshcompletiondir = get_option('zshcompletiondir')
 if zshcompletiondir == ''
-        zshcompletiondir = join_paths(datadir, 'zsh/site-functions')
+        zshcompletiondir = datadir / 'zsh/site-functions'
 endif
 
 custom_target(
index 905da4ad879161230e106b83e433f03620f04ad2..c4bfe7cb18effc8dcdc2b10b6bf79d5207b864f3 100644 (file)
@@ -53,7 +53,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind < argc)
index 8c61c3ca7fc2eb091bfd30e6215343bf3fa921b7..0c321526719fd30b2cf7b3010f11892ab229ca83 100644 (file)
@@ -9,6 +9,7 @@
 #include "sd-daemon.h"
 
 #include "alloc-util.h"
+#include "env-util.h"
 #include "errno-util.h"
 #include "escape.h"
 #include "fd-util.h"
@@ -121,11 +122,9 @@ static int open_sockets(int *epoll_fd, bool accept) {
         return count;
 }
 
-static int exec_process(const char *name, char **argv, char **env, int start_fd, size_t n_fds) {
+static int exec_process(const char *name, char **argv, int start_fd, size_t n_fds) {
         _cleanup_strv_free_ char **envp = NULL;
-        _cleanup_free_ char *joined = NULL;
-        size_t n_env = 0, length;
-        const char *tocopy;
+        const char *var;
         char **s;
         int r;
 
@@ -133,55 +132,16 @@ static int exec_process(const char *name, char **argv, char **env, int start_fd,
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "--inetd only supported for single file descriptors.");
 
-        length = strv_length(arg_setenv);
-
-        /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
-        envp = new0(char *, length + 8);
-        if (!envp)
-                return log_oom();
-
-        STRV_FOREACH(s, arg_setenv) {
-
-                if (strchr(*s, '=')) {
-                        char *k;
-
-                        k = strdup(*s);
-                        if (!k)
-                                return log_oom();
-
-                        envp[n_env++] = k;
-                } else {
-                        _cleanup_free_ char *p = NULL;
-                        const char *n;
-
-                        p = strjoin(*s, "=");
-                        if (!p)
-                                return log_oom();
-
-                        n = strv_find_prefix(env, p);
-                        if (!n)
-                                continue;
-
-                        envp[n_env] = strdup(n);
-                        if (!envp[n_env])
-                                return log_oom();
-
-                        n_env++;
-                }
-        }
-
-        FOREACH_STRING(tocopy, "TERM=", "PATH=", "USER=", "HOME=") {
+        FOREACH_STRING(var, "TERM", "PATH", "USER", "HOME") {
                 const char *n;
 
-                n = strv_find_prefix(env, tocopy);
+                n = strv_find_prefix(environ, var);
                 if (!n)
                         continue;
 
-                envp[n_env] = strdup(n);
-                if (!envp[n_env])
-                        return log_oom();
-
-                n_env++;
+                r = strv_extend(&envp, n);
+                if (r < 0)
+                        return r;
         }
 
         if (arg_inetd) {
@@ -201,16 +161,17 @@ static int exec_process(const char *name, char **argv, char **env, int start_fd,
                         safe_close(start_fd);
                 }
 
-                if (asprintf((char **) (envp + n_env++), "LISTEN_FDS=%zu", n_fds) < 0)
-                        return log_oom();
+                r = strv_extendf(&envp, "LISTEN_FDS=%zu", n_fds);
+                if (r < 0)
+                        return r;
 
-                if (asprintf((char **) (envp + n_env++), "LISTEN_PID=" PID_FMT, getpid_cached()) < 0)
-                        return log_oom();
+                r = strv_extendf(&envp, "LISTEN_PID=" PID_FMT, getpid_cached());
+                if (r < 0)
+                        return r;
 
                 if (arg_fdnames) {
                         _cleanup_free_ char *names = NULL;
                         size_t len;
-                        char *e;
 
                         len = strv_length(arg_fdnames);
                         if (len == 1)
@@ -226,15 +187,23 @@ static int exec_process(const char *name, char **argv, char **env, int start_fd,
                         if (!names)
                                 return log_oom();
 
-                        e = strjoin("LISTEN_FDNAMES=", names);
-                        if (!e)
+                        char *t = strjoin("LISTEN_FDNAMES=", names);
+                        if (!t)
                                 return log_oom();
 
-                        envp[n_env++] = e;
+                        r = strv_consume(&envp, t);
+                        if (r < 0)
+                                return r;
                 }
         }
 
-        joined = strv_join(argv, " ");
+        STRV_FOREACH(s, arg_setenv) {
+                r = strv_env_replace_strdup(&envp, *s);
+                if (r < 0)
+                        return r;
+        }
+
+        _cleanup_free_ char *joined = strv_join(argv, " ");
         if (!joined)
                 return log_oom();
 
@@ -244,7 +213,7 @@ static int exec_process(const char *name, char **argv, char **env, int start_fd,
         return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
 }
 
-static int fork_and_exec_process(const char *child, char **argv, char **env, int fd) {
+static int fork_and_exec_process(const char *child, char **argv, int fd) {
         _cleanup_free_ char *joined = NULL;
         pid_t child_pid;
         int r;
@@ -260,7 +229,7 @@ static int fork_and_exec_process(const char *child, char **argv, char **env, int
                 return r;
         if (r == 0) {
                 /* In the child */
-                exec_process(child, argv, env, fd, 1);
+                exec_process(child, argv, fd, 1);
                 _exit(EXIT_FAILURE);
         }
 
@@ -268,7 +237,7 @@ static int fork_and_exec_process(const char *child, char **argv, char **env, int
         return 0;
 }
 
-static int do_accept(const char *name, char **argv, char **envp, int fd) {
+static int do_accept(const char *name, char **argv, int fd) {
         _cleanup_free_ char *local = NULL, *peer = NULL;
         _cleanup_close_ int fd_accepted = -1;
 
@@ -284,7 +253,7 @@ static int do_accept(const char *name, char **argv, char **envp, int fd) {
         (void) getpeername_pretty(fd_accepted, true, &peer);
         log_info("Connection from %s to %s", strna(peer), strna(local));
 
-        return fork_and_exec_process(name, argv, envp, fd_accepted);
+        return fork_and_exec_process(name, argv, fd_accepted);
 }
 
 /* SIGCHLD handler. */
@@ -414,10 +383,9 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'E':
-                        r = strv_extend(&arg_setenv, optarg);
+                        r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
                         if (r < 0)
-                                return log_oom();
-
+                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
                         break;
 
                 case ARG_FDNAME: {
@@ -453,7 +421,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind == argc)
@@ -471,7 +439,7 @@ static int parse_argv(int argc, char *argv[]) {
         return 1 /* work to do */;
 }
 
-int main(int argc, char **argv, char **envp) {
+int main(int argc, char **argv) {
         int r, n;
         int epoll_fd = -1;
 
@@ -508,14 +476,14 @@ int main(int argc, char **argv, char **envp) {
 
                 log_info("Communication attempt on fd %i.", event.data.fd);
                 if (arg_accept) {
-                        r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
+                        r = do_accept(argv[optind], argv + optind, event.data.fd);
                         if (r < 0)
                                 return EXIT_FAILURE;
                 } else
                         break;
         }
 
-        exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, (size_t) n);
+        exec_process(argv[optind], argv + optind, SD_LISTEN_FDS_START, (size_t) n);
 
         return EXIT_SUCCESS;
 }
index 241c188ed6d187a46198ebd2b56843af4fed99d3..09870b95ec497b1e28e34feb6dcad05eb7a45467 100644 (file)
@@ -83,7 +83,7 @@ int verify_conditions(char **lines, UnitFileScope scope) {
                 return log_error_errno(r, "Failed to initialize manager: %m");
 
         log_debug("Starting manager...");
-        r = manager_startup(m, NULL, NULL);
+        r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, /* root= */ NULL);
         if (r < 0)
                 return r;
 
index 4d41834c3f85fc5e7739e2c7311f59817740bb3a..24500e3a5b9fd1af8ecf64216e33300db8c1f189 100644 (file)
@@ -2,7 +2,9 @@
 
 #include <sys/utsname.h>
 
+#include "af-list.h"
 #include "analyze-security.h"
+#include "analyze-verify.h"
 #include "bus-error.h"
 #include "bus-map-properties.h"
 #include "bus-unit-util.h"
@@ -12,6 +14,7 @@
 #include "in-addr-util.h"
 #include "locale-util.h"
 #include "macro.h"
+#include "manager.h"
 #include "missing_capability.h"
 #include "missing_sched.h"
 #include "nulstr-util.h"
 #if HAVE_SECCOMP
 #  include "seccomp-util.h"
 #endif
+#include "service.h"
 #include "set.h"
 #include "stdio-util.h"
 #include "strv.h"
 #include "terminal-util.h"
 #include "unit-def.h"
 #include "unit-name.h"
+#include "unit-serialize.h"
 
-struct security_info {
+typedef struct SecurityInfo {
         char *id;
         char *type;
         char *load_state;
@@ -81,7 +86,7 @@ struct security_info {
         bool restrict_address_family_packet;
         bool restrict_address_family_other;
 
-        uint64_t restrict_namespaces;
+        unsigned long long restrict_namespaces;
         bool restrict_realtime;
         bool restrict_suid_sgid;
 
@@ -92,13 +97,13 @@ struct security_info {
         char *device_policy;
         bool device_allow_non_empty;
 
-        char **system_call_architectures;
+        Set *system_call_architectures;
 
         bool system_call_filter_allow_list;
-        Set *system_call_filter;
+        Hashmap *system_call_filter;
 
-        uint32_t _umask;
-};
+        mode_t _umask;
+} SecurityInfo;
 
 struct security_assessor {
         const char *id;
@@ -110,7 +115,7 @@ struct security_assessor {
         uint64_t range;
         int (*assess)(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description);
@@ -119,9 +124,24 @@ struct security_assessor {
         bool default_dependencies_only;
 };
 
-static void security_info_free(struct security_info *i) {
+static SecurityInfo *security_info_new(void) {
+        SecurityInfo *info = new(SecurityInfo, 1);
+        if (!info)
+                return NULL;
+
+        *info = (SecurityInfo) {
+                .default_dependencies = true,
+                .capability_bounding_set = UINT64_MAX,
+                .restrict_namespaces = UINT64_MAX,
+                ._umask = 0002,
+        };
+
+        return info;
+}
+
+static SecurityInfo *security_info_free(SecurityInfo *i) {
         if (!i)
-                return;
+                return NULL;
 
         free(i->id);
         free(i->type);
@@ -144,12 +164,16 @@ static void security_info_free(struct security_info *i) {
         free(i->device_policy);
 
         strv_free(i->supplementary_groups);
-        strv_free(i->system_call_architectures);
+        set_free(i->system_call_architectures);
 
-        set_free(i->system_call_filter);
+        hashmap_free(i->system_call_filter);
+
+        return mfree(i);
 }
 
-static bool security_info_runs_privileged(const struct security_info *i)  {
+DEFINE_TRIVIAL_CLEANUP_FUNC(SecurityInfo*, security_info_free);
+
+static bool security_info_runs_privileged(const SecurityInfo *i)  {
         assert(i);
 
         if (STRPTR_IN_SET(i->user, "0", "root"))
@@ -163,7 +187,7 @@ static bool security_info_runs_privileged(const struct security_info *i)  {
 
 static int assess_bool(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -182,7 +206,7 @@ static int assess_bool(
 
 static int assess_user(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -219,7 +243,7 @@ static int assess_user(
 
 static int assess_protect_home(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -261,7 +285,7 @@ static int assess_protect_home(
 
 static int assess_protect_system(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -303,7 +327,7 @@ static int assess_protect_system(
 
 static int assess_root_directory(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -321,7 +345,7 @@ static int assess_root_directory(
 
 static int assess_capability_bounding_set(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -337,7 +361,7 @@ static int assess_capability_bounding_set(
 
 static int assess_umask(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -378,7 +402,7 @@ static int assess_umask(
 
 static int assess_keyring_mode(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -394,7 +418,7 @@ static int assess_keyring_mode(
 
 static int assess_protect_proc(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -416,7 +440,7 @@ static int assess_protect_proc(
 
 static int assess_proc_subset(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -432,7 +456,7 @@ static int assess_proc_subset(
 
 static int assess_notify_access(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -448,7 +472,7 @@ static int assess_notify_access(
 
 static int assess_remove_ipc(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -467,7 +491,7 @@ static int assess_remove_ipc(
 
 static int assess_supplementary_groups(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -486,7 +510,7 @@ static int assess_supplementary_groups(
 
 static int assess_restrict_namespaces(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -502,7 +526,7 @@ static int assess_restrict_namespaces(
 
 static int assess_system_call_architectures(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -513,10 +537,11 @@ static int assess_system_call_architectures(
         assert(ret_badness);
         assert(ret_description);
 
-        if (strv_isempty(info->system_call_architectures)) {
+        if (set_isempty(info->system_call_architectures)) {
                 b = 10;
                 d = strdup("Service may execute system calls with all ABIs");
-        } else if (strv_equal(info->system_call_architectures, STRV_MAKE("native"))) {
+        } else if (set_contains(info->system_call_architectures, "native") &&
+                   set_size(info->system_call_architectures) == 1) {
                 b = 0;
                 d = strdup("Service may execute system calls only with native ABI");
         } else {
@@ -535,7 +560,7 @@ static int assess_system_call_architectures(
 
 #if HAVE_SECCOMP
 
-static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
+static bool syscall_names_in_filter(Hashmap *s, bool allow_list, const SyscallFilterSet *f, const char **ret_offending_syscall) {
         const char *syscall;
 
         NULSTR_FOREACH(syscall, f->value) {
@@ -556,7 +581,7 @@ static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilter
                 if (id < 0)
                         continue;
 
-                if (set_contains(s, syscall) == allow_list) {
+                if (hashmap_contains(s, syscall) == allow_list) {
                         log_debug("Offending syscall filter item: %s", syscall);
                         if (ret_offending_syscall)
                                 *ret_offending_syscall = syscall;
@@ -570,7 +595,7 @@ static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilter
 
 static int assess_system_call_filter(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -587,7 +612,7 @@ static int assess_system_call_filter(
         uint64_t b;
         int r;
 
-        if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) {
+        if (!info->system_call_filter_allow_list && hashmap_isempty(info->system_call_filter)) {
                 r = free_and_strdup(&d, "Service does not filter system calls");
                 b = 10;
         } else {
@@ -635,7 +660,7 @@ static int assess_system_call_filter(
 
 static int assess_ip_address_allow(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -675,7 +700,7 @@ static int assess_ip_address_allow(
 
 static int assess_device_allow(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -712,7 +737,7 @@ static int assess_device_allow(
 
 static int assess_ambient_capabilities(
                 const struct security_assessor *a,
-                const struct security_info *info,
+                const SecurityInfo *info,
                 const void *data,
                 uint64_t *ret_badness,
                 char **ret_description) {
@@ -753,7 +778,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, private_devices),
+                .offset = offsetof(SecurityInfo, private_devices),
         },
         {
                 .id = "PrivateMounts=",
@@ -763,7 +788,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, private_mounts),
+                .offset = offsetof(SecurityInfo, private_mounts),
         },
         {
                 .id = "PrivateNetwork=",
@@ -773,7 +798,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 2500,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, private_network),
+                .offset = offsetof(SecurityInfo, private_network),
         },
         {
                 .id = "PrivateTmp=",
@@ -783,7 +808,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, private_tmp),
+                .offset = offsetof(SecurityInfo, private_tmp),
                 .default_dependencies_only = true,
         },
         {
@@ -794,7 +819,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, private_users),
+                .offset = offsetof(SecurityInfo, private_users),
         },
         {
                 .id = "ProtectControlGroups=",
@@ -804,7 +829,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, protect_control_groups),
+                .offset = offsetof(SecurityInfo, protect_control_groups),
         },
         {
                 .id = "ProtectKernelModules=",
@@ -814,7 +839,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, protect_kernel_modules),
+                .offset = offsetof(SecurityInfo, protect_kernel_modules),
         },
         {
                 .id = "ProtectKernelTunables=",
@@ -824,7 +849,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, protect_kernel_tunables),
+                .offset = offsetof(SecurityInfo, protect_kernel_tunables),
         },
         {
                 .id = "ProtectKernelLogs=",
@@ -834,7 +859,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, protect_kernel_logs),
+                .offset = offsetof(SecurityInfo, protect_kernel_logs),
         },
         {
                 .id = "ProtectClock=",
@@ -844,7 +869,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, protect_clock),
+                .offset = offsetof(SecurityInfo, protect_clock),
         },
         {
                 .id = "ProtectHome=",
@@ -862,7 +887,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 50,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, protect_hostname),
+                .offset = offsetof(SecurityInfo, protect_hostname),
         },
         {
                 .id = "ProtectSystem=",
@@ -890,7 +915,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 100,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, lock_personality),
+                .offset = offsetof(SecurityInfo, lock_personality),
         },
         {
                 .id = "MemoryDenyWriteExecute=",
@@ -900,7 +925,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 100,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, memory_deny_write_execute),
+                .offset = offsetof(SecurityInfo, memory_deny_write_execute),
         },
         {
                 .id = "NoNewPrivileges=",
@@ -910,7 +935,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, no_new_privileges),
+                .offset = offsetof(SecurityInfo, no_new_privileges),
         },
         {
                 .id = "CapabilityBoundingSet=~CAP_SYS_ADMIN",
@@ -1227,7 +1252,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 100,
                 .range = 1,
                 .assess = assess_remove_ipc,
-                .offset = offsetof(struct security_info, remove_ipc),
+                .offset = offsetof(SecurityInfo, remove_ipc),
         },
         {
                 .id = "Delegate=",
@@ -1237,7 +1262,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 100,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, delegate),
+                .offset = offsetof(SecurityInfo, delegate),
                 .parameter = true, /* invert! */
         },
         {
@@ -1248,7 +1273,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 500,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, restrict_realtime),
+                .offset = offsetof(SecurityInfo, restrict_realtime),
         },
         {
                 .id = "RestrictSUIDSGID=",
@@ -1258,7 +1283,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, restrict_suid_sgid),
+                .offset = offsetof(SecurityInfo, restrict_suid_sgid),
         },
         {
                 .id = "RestrictNamespaces=~CLONE_NEWUSER",
@@ -1338,7 +1363,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1500,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, restrict_address_family_inet),
+                .offset = offsetof(SecurityInfo, restrict_address_family_inet),
         },
         {
                 .id = "RestrictAddressFamilies=~AF_UNIX",
@@ -1348,7 +1373,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 25,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, restrict_address_family_unix),
+                .offset = offsetof(SecurityInfo, restrict_address_family_unix),
         },
         {
                 .id = "RestrictAddressFamilies=~AF_NETLINK",
@@ -1358,7 +1383,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 200,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, restrict_address_family_netlink),
+                .offset = offsetof(SecurityInfo, restrict_address_family_netlink),
         },
         {
                 .id = "RestrictAddressFamilies=~AF_PACKET",
@@ -1368,7 +1393,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1000,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, restrict_address_family_packet),
+                .offset = offsetof(SecurityInfo, restrict_address_family_packet),
         },
         {
                 .id = "RestrictAddressFamilies=~…",
@@ -1378,7 +1403,7 @@ static const struct security_assessor security_assessor_table[] = {
                 .weight = 1250,
                 .range = 1,
                 .assess = assess_bool,
-                .offset = offsetof(struct security_info, restrict_address_family_other),
+                .offset = offsetof(SecurityInfo, restrict_address_family_other),
         },
         {
                 .id = "SystemCallArchitectures=",
@@ -1502,7 +1527,7 @@ static const struct security_assessor security_assessor_table[] = {
         },
 };
 
-static int assess(const struct security_info *info, Table *overview_table, AnalyzeSecurityFlags flags) {
+static int assess(const SecurityInfo *info, Table *overview_table, AnalyzeSecurityFlags flags, unsigned threshold) {
         static const struct {
                 uint64_t exposure;
                 const char *name;
@@ -1698,6 +1723,60 @@ static int assess(const struct security_info *info, Table *overview_table, Analy
                         return table_log_add_error(r);
         }
 
+        /* Return error when overall exposure level is over threshold */
+        if (exposure > threshold)
+                return -EINVAL;
+
+        return 0;
+}
+
+static int property_read_restrict_namespaces(
+                sd_bus *bus,
+                const char *member,
+                sd_bus_message *m,
+                sd_bus_error *error,
+                void *userdata) {
+
+        SecurityInfo *info = userdata;
+        int r;
+        uint64_t namespaces;
+
+        assert(bus);
+        assert(member);
+        assert(m);
+        assert(info);
+
+        r = sd_bus_message_read(m, "t", &namespaces);
+        if (r < 0)
+                return r;
+
+        info->restrict_namespaces = (unsigned long long) namespaces;
+
+        return 0;
+}
+
+static int property_read_umask(
+                sd_bus *bus,
+                const char *member,
+                sd_bus_message *m,
+                sd_bus_error *error,
+                void *userdata) {
+
+        SecurityInfo *info = userdata;
+        int r;
+        uint32_t umask;
+
+        assert(bus);
+        assert(member);
+        assert(m);
+        assert(info);
+
+        r = sd_bus_message_read(m, "u", &umask);
+        if (r < 0)
+                return r;
+
+        info->_umask = (mode_t) umask;
+
         return 0;
 }
 
@@ -1708,7 +1787,7 @@ static int property_read_restrict_address_families(
                 sd_bus_error *error,
                 void *userdata) {
 
-        struct security_info *info = userdata;
+        SecurityInfo *info = userdata;
         int allow_list, r;
 
         assert(bus);
@@ -1761,6 +1840,42 @@ static int property_read_restrict_address_families(
         return sd_bus_message_exit_container(m);
 }
 
+static int property_read_syscall_archs(
+                sd_bus *bus,
+                const char *member,
+                sd_bus_message *m,
+                sd_bus_error *error,
+                void *userdata) {
+
+        SecurityInfo *info = userdata;
+        int r;
+
+        assert(bus);
+        assert(member);
+        assert(m);
+        assert(info);
+
+        r = sd_bus_message_enter_container(m, 'a', "s");
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                const char *name;
+
+                r = sd_bus_message_read(m, "s", &name);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                r = set_put_strdup(&info->system_call_architectures, name);
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_bus_message_exit_container(m);
+}
+
 static int property_read_system_call_filter(
                 sd_bus *bus,
                 const char *member,
@@ -1768,7 +1883,7 @@ static int property_read_system_call_filter(
                 sd_bus_error *error,
                 void *userdata) {
 
-        struct security_info *info = userdata;
+        SecurityInfo *info = userdata;
         int allow_list, r;
 
         assert(bus);
@@ -1798,7 +1913,9 @@ static int property_read_system_call_filter(
                 if (r == 0)
                         break;
 
-                r = set_put_strdup(&info->system_call_filter, name);
+                /* The actual ExecContext stores the system call id as the map value, which we don't
+                 * need. So we assign NULL to all values here. */
+                r = hashmap_put_strdup(&info->system_call_filter, name, NULL);
                 if (r < 0)
                         return r;
         }
@@ -1817,7 +1934,7 @@ static int property_read_ip_address_allow(
                 sd_bus_error *error,
                 void *userdata) {
 
-        struct security_info *info = userdata;
+        SecurityInfo *info = userdata;
         bool deny_ipv4 = false, deny_ipv6 = false;
         int r;
 
@@ -1895,7 +2012,7 @@ static int property_read_ip_filters(
                 sd_bus_error *error,
                 void *userdata) {
 
-        struct security_info *info = userdata;
+        SecurityInfo *info = userdata;
         _cleanup_(strv_freep) char **l = NULL;
         int r;
 
@@ -1910,7 +2027,7 @@ static int property_read_ip_filters(
         if (streq(member, "IPIngressFilterPath"))
                 info->ip_filters_custom_ingress = !strv_isempty(l);
         else if (streq(member, "IPEgressFilterPath"))
-                info->ip_filters_custom_ingress = !strv_isempty(l);
+                info->ip_filters_custom_egress = !strv_isempty(l);
 
         return 0;
 }
@@ -1922,7 +2039,7 @@ static int property_read_device_allow(
                 sd_bus_error *error,
                 void *userdata) {
 
-        struct security_info *info = userdata;
+        SecurityInfo *info = userdata;
         size_t n = 0;
         int r;
 
@@ -1951,56 +2068,56 @@ static int property_read_device_allow(
         return sd_bus_message_exit_container(m);
 }
 
-static int acquire_security_info(sd_bus *bus, const char *name, struct security_info *info, AnalyzeSecurityFlags flags) {
+static int acquire_security_info(sd_bus *bus, const char *name, SecurityInfo *info, AnalyzeSecurityFlags flags) {
 
         static const struct bus_properties_map security_map[] = {
-                { "AmbientCapabilities",     "t",       NULL,                                    offsetof(struct security_info, ambient_capabilities)      },
-                { "CapabilityBoundingSet",   "t",       NULL,                                    offsetof(struct security_info, capability_bounding_set)   },
-                { "DefaultDependencies",     "b",       NULL,                                    offsetof(struct security_info, default_dependencies)      },
-                { "Delegate",                "b",       NULL,                                    offsetof(struct security_info, delegate)                  },
-                { "DeviceAllow",             "a(ss)",   property_read_device_allow,              0                                                         },
-                { "DevicePolicy",            "s",       NULL,                                    offsetof(struct security_info, device_policy)             },
-                { "DynamicUser",             "b",       NULL,                                    offsetof(struct security_info, dynamic_user)              },
-                { "FragmentPath",            "s",       NULL,                                    offsetof(struct security_info, fragment_path)             },
-                { "IPAddressAllow",          "a(iayu)", property_read_ip_address_allow,          0                                                         },
-                { "IPAddressDeny",           "a(iayu)", property_read_ip_address_allow,          0                                                         },
-                { "IPIngressFilterPath",     "as",      property_read_ip_filters,                0                                                         },
-                { "IPEgressFilterPath",      "as",      property_read_ip_filters,                0                                                         },
-                { "Id",                      "s",       NULL,                                    offsetof(struct security_info, id)                        },
-                { "KeyringMode",             "s",       NULL,                                    offsetof(struct security_info, keyring_mode)              },
-                { "ProtectProc",             "s",       NULL,                                    offsetof(struct security_info, protect_proc)              },
-                { "ProcSubset",              "s",       NULL,                                    offsetof(struct security_info, proc_subset)               },
-                { "LoadState",               "s",       NULL,                                    offsetof(struct security_info, load_state)                },
-                { "LockPersonality",         "b",       NULL,                                    offsetof(struct security_info, lock_personality)          },
-                { "MemoryDenyWriteExecute",  "b",       NULL,                                    offsetof(struct security_info, memory_deny_write_execute) },
-                { "NoNewPrivileges",         "b",       NULL,                                    offsetof(struct security_info, no_new_privileges)         },
-                { "NotifyAccess",            "s",       NULL,                                    offsetof(struct security_info, notify_access)             },
-                { "PrivateDevices",          "b",       NULL,                                    offsetof(struct security_info, private_devices)           },
-                { "PrivateMounts",           "b",       NULL,                                    offsetof(struct security_info, private_mounts)            },
-                { "PrivateNetwork",          "b",       NULL,                                    offsetof(struct security_info, private_network)           },
-                { "PrivateTmp",              "b",       NULL,                                    offsetof(struct security_info, private_tmp)               },
-                { "PrivateUsers",            "b",       NULL,                                    offsetof(struct security_info, private_users)             },
-                { "ProtectControlGroups",    "b",       NULL,                                    offsetof(struct security_info, protect_control_groups)    },
-                { "ProtectHome",             "s",       NULL,                                    offsetof(struct security_info, protect_home)              },
-                { "ProtectHostname",         "b",       NULL,                                    offsetof(struct security_info, protect_hostname)          },
-                { "ProtectKernelModules",    "b",       NULL,                                    offsetof(struct security_info, protect_kernel_modules)    },
-                { "ProtectKernelTunables",   "b",       NULL,                                    offsetof(struct security_info, protect_kernel_tunables)   },
-                { "ProtectKernelLogs",       "b",       NULL,                                    offsetof(struct security_info, protect_kernel_logs)       },
-                { "ProtectClock",            "b",       NULL,                                    offsetof(struct security_info, protect_clock)             },
-                { "ProtectSystem",           "s",       NULL,                                    offsetof(struct security_info, protect_system)            },
-                { "RemoveIPC",               "b",       NULL,                                    offsetof(struct security_info, remove_ipc)                },
-                { "RestrictAddressFamilies", "(bas)",   property_read_restrict_address_families, 0                                                         },
-                { "RestrictNamespaces",      "t",       NULL,                                    offsetof(struct security_info, restrict_namespaces)       },
-                { "RestrictRealtime",        "b",       NULL,                                    offsetof(struct security_info, restrict_realtime)         },
-                { "RestrictSUIDSGID",        "b",       NULL,                                    offsetof(struct security_info, restrict_suid_sgid)        },
-                { "RootDirectory",           "s",       NULL,                                    offsetof(struct security_info, root_directory)            },
-                { "RootImage",               "s",       NULL,                                    offsetof(struct security_info, root_image)                },
-                { "SupplementaryGroups",     "as",      NULL,                                    offsetof(struct security_info, supplementary_groups)      },
-                { "SystemCallArchitectures", "as",      NULL,                                    offsetof(struct security_info, system_call_architectures) },
-                { "SystemCallFilter",        "(as)",    property_read_system_call_filter,        0                                                         },
-                { "Type",                    "s",       NULL,                                    offsetof(struct security_info, type)                      },
-                { "UMask",                   "u",       NULL,                                    offsetof(struct security_info, _umask)                    },
-                { "User",                    "s",       NULL,                                    offsetof(struct security_info, user)                      },
+                { "AmbientCapabilities",     "t",       NULL,                                    offsetof(SecurityInfo, ambient_capabilities)      },
+                { "CapabilityBoundingSet",   "t",       NULL,                                    offsetof(SecurityInfo, capability_bounding_set)   },
+                { "DefaultDependencies",     "b",       NULL,                                    offsetof(SecurityInfo, default_dependencies)      },
+                { "Delegate",                "b",       NULL,                                    offsetof(SecurityInfo, delegate)                  },
+                { "DeviceAllow",             "a(ss)",   property_read_device_allow,              0                                                 },
+                { "DevicePolicy",            "s",       NULL,                                    offsetof(SecurityInfo, device_policy)             },
+                { "DynamicUser",             "b",       NULL,                                    offsetof(SecurityInfo, dynamic_user)              },
+                { "FragmentPath",            "s",       NULL,                                    offsetof(SecurityInfo, fragment_path)             },
+                { "IPAddressAllow",          "a(iayu)", property_read_ip_address_allow,          0                                                 },
+                { "IPAddressDeny",           "a(iayu)", property_read_ip_address_allow,          0                                                 },
+                { "IPIngressFilterPath",     "as",      property_read_ip_filters,                0                                                 },
+                { "IPEgressFilterPath",      "as",      property_read_ip_filters,                0                                                 },
+                { "Id",                      "s",       NULL,                                    offsetof(SecurityInfo, id)                        },
+                { "KeyringMode",             "s",       NULL,                                    offsetof(SecurityInfo, keyring_mode)              },
+                { "ProtectProc",             "s",       NULL,                                    offsetof(SecurityInfo, protect_proc)              },
+                { "ProcSubset",              "s",       NULL,                                    offsetof(SecurityInfo, proc_subset)               },
+                { "LoadState",               "s",       NULL,                                    offsetof(SecurityInfo, load_state)                },
+                { "LockPersonality",         "b",       NULL,                                    offsetof(SecurityInfo, lock_personality)          },
+                { "MemoryDenyWriteExecute",  "b",       NULL,                                    offsetof(SecurityInfo, memory_deny_write_execute) },
+                { "NoNewPrivileges",         "b",       NULL,                                    offsetof(SecurityInfo, no_new_privileges)         },
+                { "NotifyAccess",            "s",       NULL,                                    offsetof(SecurityInfo, notify_access)             },
+                { "PrivateDevices",          "b",       NULL,                                    offsetof(SecurityInfo, private_devices)           },
+                { "PrivateMounts",           "b",       NULL,                                    offsetof(SecurityInfo, private_mounts)            },
+                { "PrivateNetwork",          "b",       NULL,                                    offsetof(SecurityInfo, private_network)           },
+                { "PrivateTmp",              "b",       NULL,                                    offsetof(SecurityInfo, private_tmp)               },
+                { "PrivateUsers",            "b",       NULL,                                    offsetof(SecurityInfo, private_users)             },
+                { "ProtectControlGroups",    "b",       NULL,                                    offsetof(SecurityInfo, protect_control_groups)    },
+                { "ProtectHome",             "s",       NULL,                                    offsetof(SecurityInfo, protect_home)              },
+                { "ProtectHostname",         "b",       NULL,                                    offsetof(SecurityInfo, protect_hostname)          },
+                { "ProtectKernelModules",    "b",       NULL,                                    offsetof(SecurityInfo, protect_kernel_modules)    },
+                { "ProtectKernelTunables",   "b",       NULL,                                    offsetof(SecurityInfo, protect_kernel_tunables)   },
+                { "ProtectKernelLogs",       "b",       NULL,                                    offsetof(SecurityInfo, protect_kernel_logs)       },
+                { "ProtectClock",            "b",       NULL,                                    offsetof(SecurityInfo, protect_clock)             },
+                { "ProtectSystem",           "s",       NULL,                                    offsetof(SecurityInfo, protect_system)            },
+                { "RemoveIPC",               "b",       NULL,                                    offsetof(SecurityInfo, remove_ipc)                },
+                { "RestrictAddressFamilies", "(bas)",   property_read_restrict_address_families, 0                                                 },
+                { "RestrictNamespaces",      "t",       property_read_restrict_namespaces,       0                                                 },
+                { "RestrictRealtime",        "b",       NULL,                                    offsetof(SecurityInfo, restrict_realtime)         },
+                { "RestrictSUIDSGID",        "b",       NULL,                                    offsetof(SecurityInfo, restrict_suid_sgid)        },
+                { "RootDirectory",           "s",       NULL,                                    offsetof(SecurityInfo, root_directory)            },
+                { "RootImage",               "s",       NULL,                                    offsetof(SecurityInfo, root_image)                },
+                { "SupplementaryGroups",     "as",      NULL,                                    offsetof(SecurityInfo, supplementary_groups)      },
+                { "SystemCallArchitectures", "as",      property_read_syscall_archs,             0                                                 },
+                { "SystemCallFilter",        "(as)",    property_read_system_call_filter,        0                                                 },
+                { "Type",                    "s",       NULL,                                    offsetof(SecurityInfo, type)                      },
+                { "UMask",                   "u",       property_read_umask,                     0                                                 },
+                { "User",                    "s",       NULL,                                    offsetof(SecurityInfo, user)                      },
                 {}
         };
 
@@ -2075,37 +2192,306 @@ static int acquire_security_info(sd_bus *bus, const char *name, struct security_
         return 0;
 }
 
-static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_table, AnalyzeSecurityFlags flags) {
-        _cleanup_(security_info_free) struct security_info info = {
-                .default_dependencies = true,
-                .capability_bounding_set = UINT64_MAX,
-                .restrict_namespaces = UINT64_MAX,
-                ._umask = 0002,
-        };
+static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_table,
+                                AnalyzeSecurityFlags flags, unsigned threshold) {
+
+        _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
+        if (!info)
+                return log_oom();
+
         int r;
 
         assert(bus);
         assert(name);
 
-        r = acquire_security_info(bus, name, &info, flags);
+        r = acquire_security_info(bus, name, info, flags);
         if (r == -EMEDIUMTYPE) /* Ignore this one because not loaded or Type is oneshot */
                 return 0;
         if (r < 0)
                 return r;
 
-        r = assess(&info, overview_table, flags);
+        r = assess(info, overview_table, flags, threshold);
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags) {
+/* Refactoring SecurityInfo so that it can make use of existing struct variables instead of reading from dbus */
+static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, SecurityInfo **ret_info) {
+        assert(ret_info);
+
+        _cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
+        if (!info)
+                return log_oom();
+
+        if (u) {
+                if (u->id) {
+                        info->id = strdup(u->id);
+                        if (!info->id)
+                                return log_oom();
+                }
+                if (unit_type_to_string(u->type)) {
+                        info->type = strdup(unit_type_to_string(u->type));
+                        if (!info->type)
+                                return log_oom();
+                }
+                if (unit_load_state_to_string(u->load_state)) {
+                        info->load_state = strdup(unit_load_state_to_string(u->load_state));
+                        if (!info->load_state)
+                                return log_oom();
+                }
+                if (u->fragment_path) {
+                        info->fragment_path = strdup(u->fragment_path);
+                        if (!info->fragment_path)
+                                return log_oom();
+                }
+                info->default_dependencies = u->default_dependencies;
+                if (u->type == UNIT_SERVICE && notify_access_to_string(SERVICE(u)->notify_access)) {
+                        info->notify_access = strdup(notify_access_to_string(SERVICE(u)->notify_access));
+                        if (!info->notify_access)
+                                return log_oom();
+                }
+        }
+
+        if (c) {
+                info->ambient_capabilities = c->capability_ambient_set;
+                info->capability_bounding_set = c->capability_bounding_set;
+                if (c->user) {
+                        info->user = strdup(c->user);
+                        if (!info->user)
+                                return log_oom();
+                }
+                if (c->supplementary_groups) {
+                        info->supplementary_groups = strv_copy(c->supplementary_groups);
+                        if (!info->supplementary_groups)
+                                return log_oom();
+                }
+                info->dynamic_user = c->dynamic_user;
+                if (exec_keyring_mode_to_string(c->keyring_mode)) {
+                        info->keyring_mode = strdup(exec_keyring_mode_to_string(c->keyring_mode));
+                        if (!info->keyring_mode)
+                                return log_oom();
+                }
+                if (protect_proc_to_string(c->protect_proc)) {
+                        info->protect_proc = strdup(protect_proc_to_string(c->protect_proc));
+                        if (!info->protect_proc)
+                                return log_oom();
+                }
+                if (proc_subset_to_string(c->proc_subset)) {
+                        info->proc_subset = strdup(proc_subset_to_string(c->proc_subset));
+                        if (!info->proc_subset)
+                                return log_oom();
+                }
+                info->lock_personality = c->lock_personality;
+                info->memory_deny_write_execute = c->memory_deny_write_execute;
+                info->no_new_privileges = c->no_new_privileges;
+                info->protect_hostname = c->protect_hostname;
+                info->private_devices = c->private_devices;
+                info->private_mounts = c->private_mounts;
+                info->private_network = c->private_network;
+                info->private_tmp = c->private_tmp;
+                info->private_users = c->private_users;
+                info->protect_control_groups = c->protect_control_groups;
+                info->protect_kernel_modules = c->protect_kernel_modules;
+                info->protect_kernel_tunables = c->protect_kernel_tunables;
+                info->protect_kernel_logs = c->protect_kernel_logs;
+                info->protect_clock = c->protect_clock;
+                if (protect_home_to_string(c->protect_home)) {
+                        info->protect_home = strdup(protect_home_to_string(c->protect_home));
+                        if (!info->protect_home)
+                                return log_oom();
+                }
+                if (protect_system_to_string(c->protect_system)) {
+                        info->protect_system = strdup(protect_system_to_string(c->protect_system));
+                        if (!info->protect_system)
+                                return log_oom();
+                }
+                info->remove_ipc = c->remove_ipc;
+                info->restrict_address_family_inet =
+                        info->restrict_address_family_unix =
+                        info->restrict_address_family_netlink =
+                        info->restrict_address_family_packet =
+                        info->restrict_address_family_other =
+                        c->address_families_allow_list;
+
+                void *key;
+                SET_FOREACH(key, c->address_families) {
+                        int family = PTR_TO_INT(key);
+                        if (family == 0)
+                                continue;
+                        if (IN_SET(family, AF_INET, AF_INET6))
+                                info->restrict_address_family_inet = !c->address_families_allow_list;
+                        else if (family == AF_UNIX)
+                                info->restrict_address_family_unix = !c->address_families_allow_list;
+                        else if (family == AF_NETLINK)
+                                info->restrict_address_family_netlink = !c->address_families_allow_list;
+                        else if (family == AF_PACKET)
+                                info->restrict_address_family_packet = !c->address_families_allow_list;
+                        else
+                                info->restrict_address_family_other = !c->address_families_allow_list;
+                }
+
+                info->restrict_namespaces = c->restrict_namespaces;
+                info->restrict_realtime = c->restrict_realtime;
+                info->restrict_suid_sgid = c->restrict_suid_sgid;
+                if (c->root_directory) {
+                        info->root_directory = strdup(c->root_directory);
+                        if (!info->root_directory)
+                                return log_oom();
+                }
+                if (c->root_image) {
+                        info->root_image = strdup(c->root_image);
+                        if (!info->root_image)
+                                return log_oom();
+                }
+                info->_umask = c->umask;
+                if (c->syscall_archs) {
+                        info->system_call_architectures = set_copy(c->syscall_archs);
+                        if (!info->system_call_architectures)
+                                return log_oom();
+                }
+                info->system_call_filter_allow_list = c->syscall_allow_list;
+                if (c->syscall_filter) {
+                        info->system_call_filter = hashmap_copy(c->syscall_filter);
+                        if (!info->system_call_filter)
+                                return log_oom();
+                }
+        }
+
+        if (g) {
+                info->delegate = g->delegate;
+                if (cgroup_device_policy_to_string(g->device_policy)) {
+                        info->device_policy = strdup(cgroup_device_policy_to_string(g->device_policy));
+                        if (!info->device_policy)
+                                return log_oom();
+                }
+
+                IPAddressAccessItem *i;
+                bool deny_ipv4 = false, deny_ipv6 = false;
+
+                LIST_FOREACH(items, i, g->ip_address_deny) {
+                        if (i->family == AF_INET && i->prefixlen == 0)
+                                deny_ipv4 = true;
+                        else if (i->family == AF_INET6 && i->prefixlen == 0)
+                                deny_ipv6 = true;
+                }
+                info->ip_address_deny_all = deny_ipv4 && deny_ipv6;
+
+                info->ip_address_allow_localhost = info->ip_address_allow_other = false;
+                LIST_FOREACH(items, i, g->ip_address_allow) {
+                        if (in_addr_is_localhost(i->family, &i->address))
+                                info->ip_address_allow_localhost = true;
+                        else
+                                info->ip_address_allow_other = true;
+                }
+
+                info->ip_filters_custom_ingress = !strv_isempty(g->ip_filters_ingress);
+                info->ip_filters_custom_egress = !strv_isempty(g->ip_filters_egress);
+                info->device_allow_non_empty = !LIST_IS_EMPTY(g->device_allow);
+        }
+
+        *ret_info = TAKE_PTR(info);
+
+        return 0;
+}
+
+static int offline_security_check(Unit *u, unsigned threshold) {
+        _cleanup_(table_unrefp) Table *overview_table = NULL;
+        AnalyzeSecurityFlags flags = 0;
+        _cleanup_(security_info_freep) SecurityInfo *info = NULL;
+        int r;
+
+        assert(u);
+
+        if (DEBUG_LOGGING)
+                unit_dump(u, stdout, "\t");
+
+        r = get_security_info(u, unit_get_exec_context(u), unit_get_cgroup_context(u), &info);
+        if (r < 0)
+              return r;
+
+        return assess(info, overview_table, flags, threshold);
+}
+
+static int offline_security_checks(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, unsigned threshold, const char *root) {
+        const ManagerTestRunFlags flags =
+                MANAGER_TEST_RUN_MINIMAL |
+                MANAGER_TEST_RUN_ENV_GENERATORS |
+                run_generators * MANAGER_TEST_RUN_GENERATORS;
+
+        _cleanup_(manager_freep) Manager *m = NULL;
+        Unit *units[strv_length(filenames)];
+        _cleanup_free_ char *var = NULL;
+        int r, k;
+        size_t count = 0;
+        char **filename;
+
+        if (strv_isempty(filenames))
+                return 0;
+
+        /* set the path */
+        r = verify_generate_path(&var, filenames);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate unit load path: %m");
+
+        assert_se(set_unit_path(var) >= 0);
+
+        r = manager_new(scope, flags, &m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to initialize manager: %m");
+
+        log_debug("Starting manager...");
+
+        r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root);
+        if (r < 0)
+                return r;
+
+        log_debug("Loading remaining units from the command line...");
+
+        STRV_FOREACH(filename, filenames) {
+                _cleanup_free_ char *prepared = NULL;
+
+                log_debug("Handling %s...", *filename);
+
+                k = verify_prepare_filename(*filename, &prepared);
+                if (k < 0) {
+                        log_warning_errno(k, "Failed to prepare filename %s: %m", *filename);
+                        if (r == 0)
+                                r = k;
+                        continue;
+                }
+
+                k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]);
+                if (k < 0) {
+                        if (r == 0)
+                                r = k;
+                        continue;
+                }
+
+                count++;
+        }
+
+        for (size_t i = 0; i < count; i++) {
+                k = offline_security_check(units[i], threshold);
+                if (k < 0 && r == 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators,
+                     bool offline, unsigned threshold, const char *root, AnalyzeSecurityFlags flags) {
+
         _cleanup_(table_unrefp) Table *overview_table = NULL;
         int ret = 0, r;
 
         assert(bus);
 
+        if (offline)
+                return offline_security_checks(units, scope, check_man, run_generators, threshold, root);
+
         if (strv_length(units) != 1) {
                 overview_table = table_new("unit", "exposure", "predicate", "happy");
                 if (!overview_table)
@@ -2164,7 +2550,7 @@ int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags) {
                 flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING;
 
                 STRV_FOREACH(i, list) {
-                        r = analyze_security_one(bus, *i, overview_table, flags);
+                        r = analyze_security_one(bus, *i, overview_table, flags, threshold);
                         if (r < 0 && ret >= 0)
                                 ret = r;
                 }
@@ -2199,7 +2585,7 @@ int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags) {
                         } else
                                 name = mangled;
 
-                        r = analyze_security_one(bus, name, overview_table, flags);
+                        r = analyze_security_one(bus, name, overview_table, flags, threshold);
                         if (r < 0 && ret >= 0)
                                 ret = r;
                 }
index e8de39f3bc2f76b3adcb68fe14b7198b1a48b895..57a93afbef416fbe3e6482d2865c947d34cd692b 100644 (file)
@@ -1,12 +1,17 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <stdbool.h>
+
 #include "sd-bus.h"
 
+#include "unit-file.h"
+
 typedef enum AnalyzeSecurityFlags {
         ANALYZE_SECURITY_SHORT             = 1 << 0,
         ANALYZE_SECURITY_ONLY_LOADED       = 1 << 1,
         ANALYZE_SECURITY_ONLY_LONG_RUNNING = 1 << 2,
 } AnalyzeSecurityFlags;
 
-int analyze_security(sd_bus *bus, char **units, AnalyzeSecurityFlags flags);
+int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators,
+                     bool offline, unsigned threshold, const char *root, AnalyzeSecurityFlags flags);
index bb5bdf998a35cfcc32ce0a85188b9f5c17754aca..2a436c545ef210c4ed7dc3e67f07d9855bf8a456 100644 (file)
 #include "manager.h"
 #include "pager.h"
 #include "path-util.h"
+#include "string-table.h"
 #include "strv.h"
 #include "unit-name.h"
 #include "unit-serialize.h"
 
-static int prepare_filename(const char *filename, char **ret) {
+static void log_syntax_callback(const char *unit, int level, void *userdata) {
+        Set **s = userdata;
+        int r;
+
+        assert(userdata);
+        assert(unit);
+
+        if (level > LOG_WARNING)
+                return;
+
+        r = set_put_strdup(s, unit);
+        if (r < 0) {
+                set_free_free(*s);
+                *s = POINTER_MAX;
+        }
+}
+
+int verify_prepare_filename(const char *filename, char **ret) {
         int r;
         const char *name;
         _cleanup_free_ char *abspath = NULL;
@@ -52,7 +70,7 @@ static int prepare_filename(const char *filename, char **ret) {
         return 0;
 }
 
-static int generate_path(char **var, char **filenames) {
+int verify_generate_path(char **var, char **filenames) {
         const char *old;
         char **filename;
 
@@ -115,7 +133,7 @@ static int verify_socket(Unit *u) {
         return 0;
 }
 
-int verify_executable(Unit *u, const ExecCommand *exec) {
+int verify_executable(Unit *u, const ExecCommand *exec, const char *root) {
         int r;
 
         if (!exec)
@@ -124,14 +142,14 @@ int verify_executable(Unit *u, const ExecCommand *exec) {
         if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE)
                 return 0;
 
-        r = find_executable_full(exec->path, false, NULL, NULL);
+        r = find_executable_full(exec->path, root, false, NULL, NULL);
         if (r < 0)
                 return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path);
 
         return 0;
 }
 
-static int verify_executables(Unit *u) {
+static int verify_executables(Unit *u, const char *root) {
         ExecCommand *exec;
         int r = 0, k;
         unsigned i;
@@ -141,20 +159,20 @@ static int verify_executables(Unit *u) {
         exec =  u->type == UNIT_SOCKET ? SOCKET(u)->control_command :
                 u->type == UNIT_MOUNT ? MOUNT(u)->control_command :
                 u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL;
-        k = verify_executable(u, exec);
+        k = verify_executable(u, exec, root);
         if (k < 0 && r == 0)
                 r = k;
 
         if (u->type == UNIT_SERVICE)
                 for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) {
-                        k = verify_executable(u, SERVICE(u)->exec_command[i]);
+                        k = verify_executable(u, SERVICE(u)->exec_command[i], root);
                         if (k < 0 && r == 0)
                                 r = k;
                 }
 
         if (u->type == UNIT_SOCKET)
                 for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) {
-                        k = verify_executable(u, SOCKET(u)->exec_command[i]);
+                        k = verify_executable(u, SOCKET(u)->exec_command[i], root);
                         if (k < 0 && r == 0)
                                 r = k;
                 }
@@ -189,7 +207,7 @@ static int verify_documentation(Unit *u, bool check_man) {
         return r;
 }
 
-static int verify_unit(Unit *u, bool check_man) {
+static int verify_unit(Unit *u, bool check_man, const char *root) {
         _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
         int r, k;
 
@@ -207,7 +225,7 @@ static int verify_unit(Unit *u, bool check_man) {
         if (k < 0 && r == 0)
                 r = k;
 
-        k = verify_executables(u);
+        k = verify_executables(u, root);
         if (k < 0 && r == 0)
                 r = k;
 
@@ -218,13 +236,22 @@ static int verify_unit(Unit *u, bool check_man) {
         return r;
 }
 
-int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators) {
+static void set_destroy_ignore_pointer_max(Set** s) {
+        if (*s == POINTER_MAX)
+                return;
+        set_free_free(*s);
+}
+
+int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) {
         const ManagerTestRunFlags flags =
                 MANAGER_TEST_RUN_MINIMAL |
                 MANAGER_TEST_RUN_ENV_GENERATORS |
+                (recursive_errors == RECURSIVE_ERRORS_NO) * MANAGER_TEST_RUN_IGNORE_DEPENDENCIES |
                 run_generators * MANAGER_TEST_RUN_GENERATORS;
 
         _cleanup_(manager_freep) Manager *m = NULL;
+        _cleanup_(set_destroy_ignore_pointer_max) Set *s = NULL;
+        _unused_ _cleanup_(clear_log_syntax_callback) dummy_t dummy;
         Unit *units[strv_length(filenames)];
         _cleanup_free_ char *var = NULL;
         int r, k, i, count = 0;
@@ -233,8 +260,13 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
         if (strv_isempty(filenames))
                 return 0;
 
+        /* Allow systemd-analyze to hook in a callback function so that it can get
+         * all the required log data from the function itself without having to rely
+         * on a global set variable for the same */
+        set_log_syntax_callback(log_syntax_callback, &s);
+
         /* set the path */
-        r = generate_path(&var, filenames);
+        r = verify_generate_path(&var, filenames);
         if (r < 0)
                 return log_error_errno(r, "Failed to generate unit load path: %m");
 
@@ -246,7 +278,7 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
 
         log_debug("Starting manager...");
 
-        r = manager_startup(m, NULL, NULL);
+        r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root);
         if (r < 0)
                 return r;
 
@@ -259,7 +291,7 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
 
                 log_debug("Handling %s...", *filename);
 
-                k = prepare_filename(*filename, &prepared);
+                k = verify_prepare_filename(*filename, &prepared);
                 if (k < 0) {
                         log_error_errno(k, "Failed to prepare filename %s: %m", *filename);
                         if (r == 0)
@@ -278,10 +310,39 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
         }
 
         for (i = 0; i < count; i++) {
-                k = verify_unit(units[i], check_man);
+                k = verify_unit(units[i], check_man, root);
                 if (k < 0 && r == 0)
                         r = k;
         }
 
-        return r;
+        if (s == POINTER_MAX)
+                return log_oom();
+
+        if (set_isempty(s) || r != 0)
+                return r;
+
+        /* If all previous verifications succeeded, then either the recursive parsing of all the
+         * associated dependencies with RECURSIVE_ERRORS_YES or the parsing of the specified unit file
+         * with RECURSIVE_ERRORS_NO must have yielded a syntax warning and hence, a non-empty set. */
+        if (IN_SET(recursive_errors, RECURSIVE_ERRORS_YES, RECURSIVE_ERRORS_NO))
+                return -ENOTRECOVERABLE;
+
+        /* If all previous verifications succeeded, then the non-empty set could have resulted from
+         * a syntax warning encountered during the recursive parsing of the specified unit file and
+         * its direct dependencies. Hence, search for any of the filenames in the set and if found,
+         * return a non-zero process exit status. */
+        if (recursive_errors == RECURSIVE_ERRORS_ONE)
+                STRV_FOREACH(filename, filenames)
+                        if (set_contains(s, basename(*filename)))
+                                return -ENOTRECOVERABLE;
+
+        return 0;
 }
+
+static const char* const recursive_errors_table[_RECURSIVE_ERRORS_MAX] = {
+        [RECURSIVE_ERRORS_NO]  = "no",
+        [RECURSIVE_ERRORS_YES] = "yes",
+        [RECURSIVE_ERRORS_ONE] = "one",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(recursive_errors, RecursiveErrors);
index 43bfbcbc8c4eaa35bb8f6b04d3c7ae7f550ec42f..47b78a8158963f75cd441b70cdba4cad7aa85479 100644 (file)
@@ -6,5 +6,18 @@
 #include "execute.h"
 #include "path-lookup.h"
 
-int verify_executable(Unit *u, const ExecCommand *exec);
-int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators);
+typedef enum RecursiveErrors {
+        RECURSIVE_ERRORS_YES,               /* Look for errors in all associated units */
+        RECURSIVE_ERRORS_NO,                /* Don't look for errors in any but the selected unit */
+        RECURSIVE_ERRORS_ONE,               /* Look for errors in the selected unit and its direct dependencies */
+        _RECURSIVE_ERRORS_MAX,
+        _RECURSIVE_ERRORS_INVALID = -EINVAL,
+} RecursiveErrors;
+
+int verify_generate_path(char **var, char **filenames);
+int verify_prepare_filename(const char *filename, char **ret);
+int verify_executable(Unit *u, const ExecCommand *exec, const char *root);
+int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root);
+
+const char* recursive_errors_to_string(RecursiveErrors i) _const_;
+RecursiveErrors recursive_errors_from_string(const char *s) _pure_;
index c4685752f6401af15a86863401239ff6f4cbfaf1..9bc7e606e82e97d33cfc82e450f1b96e3c6cf03a 100644 (file)
@@ -34,6 +34,7 @@
 #include "locale-util.h"
 #include "log.h"
 #include "main-func.h"
+#include "mount-util.h"
 #include "nulstr-util.h"
 #include "pager.h"
 #include "parse-argument.h"
@@ -45,6 +46,7 @@
 #endif
 #include "sort-util.h"
 #include "special.h"
+#include "string-table.h"
 #include "strv.h"
 #include "strxcpyx.h"
 #include "terminal-util.h"
@@ -84,14 +86,20 @@ static PagerFlags arg_pager_flags = 0;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static const char *arg_host = NULL;
 static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
+static RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES;
 static bool arg_man = true;
 static bool arg_generators = false;
-static const char *arg_root = NULL;
+static char *arg_root = NULL;
+static char *arg_image = NULL;
+static bool arg_offline = false;
+static unsigned arg_threshold = 100;
 static unsigned arg_iterations = 1;
 static usec_t arg_base_time = USEC_INFINITY;
 
 STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 
 typedef struct BootTimes {
         usec_t firmware_time;
@@ -2141,7 +2149,7 @@ static int do_condition(int argc, char *argv[], void *userdata) {
 }
 
 static int do_verify(int argc, char *argv[], void *userdata) {
-        return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
+        return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
 }
 
 static int do_security(int argc, char *argv[], void *userdata) {
@@ -2154,7 +2162,7 @@ static int do_security(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        return analyze_security(bus, strv_skip(argv, 1), 0);
+        return analyze_security(bus, strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_offline, arg_threshold, arg_root, 0);
 }
 
 static int help(int argc, char *argv[], void *userdata) {
@@ -2175,43 +2183,55 @@ static int help(int argc, char *argv[], void *userdata) {
         printf("%s [OPTIONS...] COMMAND ...\n\n"
                "%sProfile systemd, show unit dependencies, check unit files.%s\n"
                "\nCommands:\n"
-               "  [time]                   Print time required to boot the machine\n"
-               "  blame                    Print list of running units ordered by time to init\n"
-               "  critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
-               "  plot                     Output SVG graphic showing service initialization\n"
-               "  dot [UNIT...]            Output dependency graph in %s format\n"
-               "  dump                     Output state serialization of service manager\n"
-               "  cat-config               Show configuration file and drop-ins\n"
-               "  unit-files               List files and symlinks for units\n"
-               "  unit-paths               List load directories for units\n"
-               "  exit-status [STATUS...]  List exit status definitions\n"
-               "  capability [CAP...]      List capability definitions\n"
-               "  syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
-               "  condition CONDITION...   Evaluate conditions and asserts\n"
-               "  verify FILE...           Check unit files for correctness\n"
-               "  calendar SPEC...         Validate repetitive calendar time events\n"
-               "  timestamp TIMESTAMP...   Validate a timestamp\n"
-               "  timespan SPAN...         Validate a time span\n"
-               "  security [UNIT...]       Analyze security of unit\n"
+               "  [time]                     Print time required to boot the machine\n"
+               "  blame                      Print list of running units ordered by\n"
+               "                             time to init\n"
+               "  critical-chain [UNIT...]   Print a tree of the time critical chain\n"
+               "                             of units\n"
+               "  plot                       Output SVG graphic showing service\n"
+               "                             initialization\n"
+               "  dot [UNIT...]              Output dependency graph in %s format\n"
+               "  dump                       Output state serialization of service\n"
+               "                             manager\n"
+               "  cat-config                 Show configuration file and drop-ins\n"
+               "  unit-files                 List files and symlinks for units\n"
+               "  unit-paths                 List load directories for units\n"
+               "  exit-status [STATUS...]    List exit status definitions\n"
+               "  capability [CAP...]        List capability definitions\n"
+               "  syscall-filter [NAME...]   Print list of syscalls in seccomp\n"
+               "                             filter\n"
+               "  condition CONDITION...     Evaluate conditions and asserts\n"
+               "  verify FILE...             Check unit files for correctness\n"
+               "  calendar SPEC...           Validate repetitive calendar time\n"
+               "                             events\n"
+               "  timestamp TIMESTAMP...     Validate a timestamp\n"
+               "  timespan SPAN...           Validate a time span\n"
+               "  security [UNIT...]         Analyze security of unit\n"
                "\nOptions:\n"
-               "  -h --help                Show this help\n"
-               "     --version             Show package version\n"
-               "     --no-pager            Do not pipe output into a pager\n"
-               "     --system              Operate on system systemd instance\n"
-               "     --user                Operate on user systemd instance\n"
-               "     --global              Operate on global user configuration\n"
-               "  -H --host=[USER@]HOST    Operate on remote host\n"
-               "  -M --machine=CONTAINER   Operate on local container\n"
-               "     --order               Show only order in the graph\n"
-               "     --require             Show only requirement in the graph\n"
-               "     --from-pattern=GLOB   Show only origins in the graph\n"
-               "     --to-pattern=GLOB     Show only destinations in the graph\n"
-               "     --fuzz=SECONDS        Also print services which finished SECONDS earlier\n"
-               "                           than the latest in the branch\n"
-               "     --man[=BOOL]          Do [not] check for existence of man pages\n"
-               "     --generators[=BOOL]   Do [not] run unit generators (requires privileges)\n"
-               "     --iterations=N        Show the specified number of iterations\n"
-               "     --base-time=TIMESTAMP Calculate calendar times relative to specified time\n"
+               "  -h --help                  Show this help\n"
+               "     --recursive-errors=MODE Control which units are verified\n"
+               "     --offline=BOOL          Perform a security review on unit file(s)\n"
+               "     --threshold=N           Exit with a non-zero status when overall\n"
+               "                             exposure level is over threshold value\n"
+               "     --version               Show package version\n"
+               "     --no-pager              Do not pipe output into a pager\n"
+               "     --system                Operate on system systemd instance\n"
+               "     --user                  Operate on user systemd instance\n"
+               "     --global                Operate on global user configuration\n"
+               "  -H --host=[USER@]HOST      Operate on remote host\n"
+               "  -M --machine=CONTAINER     Operate on local container\n"
+               "     --order                 Show only order in the graph\n"
+               "     --require               Show only requirement in the graph\n"
+               "     --from-pattern=GLOB     Show only origins in the graph\n"
+               "     --to-pattern=GLOB       Show only destinations in the graph\n"
+               "     --fuzz=SECONDS          Also print services which finished SECONDS\n"
+               "                             earlier than the latest in the branch\n"
+               "     --man[=BOOL]            Do [not] check for existence of man pages\n"
+               "     --generators[=BOOL]     Do [not] run unit generators\n"
+               "                             (requires privileges)\n"
+               "     --iterations=N          Show the specified number of iterations\n"
+               "     --base-time=TIMESTAMP   Calculate calendar times relative to\n"
+               "                             specified time\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -2231,6 +2251,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_ORDER,
                 ARG_REQUIRE,
                 ARG_ROOT,
+                ARG_IMAGE,
                 ARG_SYSTEM,
                 ARG_USER,
                 ARG_GLOBAL,
@@ -2242,27 +2263,34 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_GENERATORS,
                 ARG_ITERATIONS,
                 ARG_BASE_TIME,
+                ARG_RECURSIVE_ERRORS,
+                ARG_OFFLINE,
+                ARG_THRESHOLD,
         };
 
         static const struct option options[] = {
-                { "help",         no_argument,       NULL, 'h'                  },
-                { "version",      no_argument,       NULL, ARG_VERSION          },
-                { "order",        no_argument,       NULL, ARG_ORDER            },
-                { "require",      no_argument,       NULL, ARG_REQUIRE          },
-                { "root",         required_argument, NULL, ARG_ROOT             },
-                { "system",       no_argument,       NULL, ARG_SYSTEM           },
-                { "user",         no_argument,       NULL, ARG_USER             },
-                { "global",       no_argument,       NULL, ARG_GLOBAL           },
-                { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
-                { "to-pattern",   required_argument, NULL, ARG_DOT_TO_PATTERN   },
-                { "fuzz",         required_argument, NULL, ARG_FUZZ             },
-                { "no-pager",     no_argument,       NULL, ARG_NO_PAGER         },
-                { "man",          optional_argument, NULL, ARG_MAN              },
-                { "generators",   optional_argument, NULL, ARG_GENERATORS       },
-                { "host",         required_argument, NULL, 'H'                  },
-                { "machine",      required_argument, NULL, 'M'                  },
-                { "iterations",   required_argument, NULL, ARG_ITERATIONS       },
-                { "base-time",    required_argument, NULL, ARG_BASE_TIME        },
+                { "help",             no_argument,       NULL, 'h'                  },
+                { "version",          no_argument,       NULL, ARG_VERSION          },
+                { "order",            no_argument,       NULL, ARG_ORDER            },
+                { "require",          no_argument,       NULL, ARG_REQUIRE          },
+                { "root",             required_argument, NULL, ARG_ROOT             },
+                { "image",            required_argument, NULL, ARG_IMAGE            },
+                { "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
+                { "offline",          required_argument, NULL, ARG_OFFLINE          },
+                { "threshold",        required_argument, NULL, ARG_THRESHOLD        },
+                { "system",           no_argument,       NULL, ARG_SYSTEM           },
+                { "user",             no_argument,       NULL, ARG_USER             },
+                { "global",           no_argument,       NULL, ARG_GLOBAL           },
+                { "from-pattern",     required_argument, NULL, ARG_DOT_FROM_PATTERN },
+                { "to-pattern",       required_argument, NULL, ARG_DOT_TO_PATTERN   },
+                { "fuzz",             required_argument, NULL, ARG_FUZZ             },
+                { "no-pager",         no_argument,       NULL, ARG_NO_PAGER         },
+                { "man",              optional_argument, NULL, ARG_MAN              },
+                { "generators",       optional_argument, NULL, ARG_GENERATORS       },
+                { "host",             required_argument, NULL, 'H'                  },
+                { "machine",          required_argument, NULL, 'M'                  },
+                { "iterations",       required_argument, NULL, ARG_ITERATIONS       },
+                { "base-time",        required_argument, NULL, ARG_BASE_TIME        },
                 {}
         };
 
@@ -2277,11 +2305,31 @@ static int parse_argv(int argc, char *argv[]) {
                 case 'h':
                         return help(0, NULL, NULL);
 
+                case ARG_RECURSIVE_ERRORS:
+                        if (streq(optarg, "help")) {
+                                DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX);
+                                return 0;
+                        }
+                        r = recursive_errors_from_string(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Unknown mode passed to --recursive-errors='%s'.", optarg);
+
+                        arg_recursive_errors = r;
+                        break;
+
                 case ARG_VERSION:
                         return version();
 
                 case ARG_ROOT:
-                        arg_root = optarg;
+                        r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_IMAGE:
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
+                        if (r < 0)
+                                return r;
                         break;
 
                 case ARG_SYSTEM:
@@ -2348,6 +2396,19 @@ static int parse_argv(int argc, char *argv[]) {
                                 return r;
                         break;
 
+                case ARG_OFFLINE:
+                        r = parse_boolean_argument("--offline", optarg, &arg_offline);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_THRESHOLD:
+                        r = safe_atou(optarg, &arg_threshold);
+                        if (r < 0 || arg_threshold > 100)
+                                return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse threshold: %s", optarg);
+
+                        break;
+
                 case ARG_ITERATIONS:
                         r = safe_atou(optarg, &arg_iterations);
                         if (r < 0)
@@ -2366,9 +2427,17 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option code.");
+                        assert_not_reached();
                 }
 
+        if (arg_offline && !streq_ptr(argv[optind], "security"))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Option --offline= is only supported for security right now.");
+
+        if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Option --threshold= is only supported for security right now.");
+
         if (arg_scope == UNIT_FILE_GLOBAL &&
             !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -2378,14 +2447,22 @@ static int parse_argv(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Option --user is not supported for cat-config right now.");
 
-        if (arg_root && !streq_ptr(argv[optind], "cat-config"))
+        if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify")) &&
+           (!(streq_ptr(argv[optind], "security") && arg_offline)))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Option --root is only supported for cat-config right now.");
+                                       "Options --root= and --image= are only supported for cat-config, verify and security when used with --offline= right now.");
+
+        /* Having both an image and a root is not supported by the code */
+        if (arg_root && arg_image)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
 
         return 1; /* work to do */
 }
 
 static int run(int argc, char *argv[]) {
+        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+        _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+        _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
 
         static const Verb verbs[] = {
                 { "help",              VERB_ANY, VERB_ANY, 0,            help                   },
@@ -2429,6 +2506,26 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
+        /* Open up and mount the image */
+        if (arg_image) {
+                assert(!arg_root);
+
+                r = mount_image_privately_interactively(
+                                arg_image,
+                                DISSECT_IMAGE_GENERIC_ROOT |
+                                DISSECT_IMAGE_RELAX_VAR_CHECK |
+                                DISSECT_IMAGE_READ_ONLY,
+                                &unlink_dir,
+                                &loop_device,
+                                &decrypted_image);
+                if (r < 0)
+                        return r;
+
+                arg_root = strdup(unlink_dir);
+                if (!arg_root)
+                        return log_oom();
+        }
+
         return dispatch_verb(argc, argv, verbs, NULL);
 }
 
index 12c32159e56e9c0cd937344357171a06f1d965ed..eaf5e0b39b41d15c16b39bd8228cbaf1a16a1012 100644 (file)
@@ -4,12 +4,12 @@
 
 static void test_verify_nonexistent(void) {
         /* Negative cases */
-        assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/non/existent"}) == 0);
-        assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/non/existent"}) < 0);
+        assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/non/existent"}, NULL) == 0);
+        assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/non/existent"}, NULL) < 0);
 
         /* Ordinary cases */
-        assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/bin/echo"}) == 0);
-        assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/bin/echo"}) == 0);
+        assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/bin/echo"}, NULL) == 0);
+        assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/bin/echo"}, NULL) == 0);
 }
 
 int main(int argc, char *argv[]) {
index 45305dec1f0dc39f278a35ac55b200f66c767402..a100679af211f58e37488d6717ba038fc1badc16 100644 (file)
@@ -188,7 +188,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (isempty(emoji) || streq(emoji, "auto"))
index 7c0970a60c24502a587784fd52ce05ce609934b7..6fcf58466752385bd3eeb77eb09658951cae6ec6 100644 (file)
@@ -495,7 +495,7 @@ static int run(int argc, char *argv[]) {
                         return log_device_error_errno(device, r, "Failed to write %s: %m", saved);
 
         } else
-                assert_not_reached("Unknown verb.");
+                assert_not_reached();
 
         return 0;
 }
index 3c33308f481c9748687db42dcfb7e0eeea1bad54..e587fe79e7a90903fc4fa477b5ca04351d4b8912 100644 (file)
@@ -44,16 +44,19 @@ typedef void (*free_func_t)(void *p);
 
 #define malloc0(n) (calloc(1, (n) ?: 1))
 
-static inline void *mfree(void *memory) {
-        free(memory);
-        return NULL;
-}
+#define mfree(memory)                           \
+        ({                                      \
+                free(memory);                   \
+                (typeof(memory)) NULL;          \
+        })
 
 #define free_and_replace(a, b)                  \
         ({                                      \
-                free(a);                        \
-                (a) = (b);                      \
-                (b) = NULL;                     \
+                typeof(a)* _a = &(a);           \
+                typeof(b)* _b = &(b);           \
+                free(*_a);                      \
+                (*_a) = (*_b);                  \
+                (*_b) = NULL;                   \
                 0;                              \
         })
 
index 409632c3f49d6f0e11d678dc4afc95fc6b39f9aa..e5dafb98141e59aa0632ff94381095def1f5d251 100644 (file)
@@ -136,7 +136,7 @@ int uname_architecture(void) {
                 if (streq(arch_map[i].machine, u.machine))
                         return cached = arch_map[i].arch;
 
-        assert_not_reached("Couldn't identify architecture. You need to patch systemd.");
+        assert_not_reached();
         return _ARCHITECTURE_INVALID;
 }
 
index 1ff6160dc88d86ad606a6ba7f45b07d0c3b31ac7..95891f68aa9fede1f250a5671edee79cd9f28825 100644 (file)
@@ -160,6 +160,24 @@ bool cg_freezer_supported(void) {
         return supported;
 }
 
+bool cg_kill_supported(void) {
+        static thread_local int supported = -1;
+
+        if (supported >= 0)
+                return supported;
+
+        if (cg_all_unified() <= 0)
+                supported = false;
+        else if (access("/sys/fs/cgroup/init.scope/cgroup.kill", F_OK) < 0) {
+                if (errno != ENOENT)
+                        log_debug_errno(errno, "Failed to check if cgroup.kill is available, assuming not: %m");
+                supported = false;
+        } else
+                supported = true;
+
+        return supported;
+}
+
 int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
         _cleanup_free_ char *fs = NULL;
         int r;
@@ -358,6 +376,29 @@ int cg_kill(
         return cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.threads");
 }
 
+int cg_kill_kernel_sigkill(const char *controller, const char *path) {
+        /* Kills the cgroup at `path` directly by writing to its cgroup.kill file.
+         * This sends SIGKILL to all processes in the cgroup and has the advantage of
+         * being completely atomic, unlike cg_kill_items. */
+        int r;
+        _cleanup_free_ char *killfile = NULL;
+
+        assert(path);
+
+        if (!cg_kill_supported())
+                return -EOPNOTSUPP;
+
+        r = cg_get_path(controller, path, "cgroup.kill", &killfile);
+        if (r < 0)
+                return r;
+
+        r = write_string_file(killfile, "1", WRITE_STRING_FILE_DISABLE_BUFFER);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int cg_kill_recursive(
                 const char *controller,
                 const char *path,
@@ -375,38 +416,46 @@ int cg_kill_recursive(
         assert(path);
         assert(sig >= 0);
 
-        if (!s) {
-                s = allocated_set = set_new(NULL);
-                if (!s)
-                        return -ENOMEM;
-        }
+        if (sig == SIGKILL && cg_kill_supported() &&
+            !FLAGS_SET(flags, CGROUP_IGNORE_SELF) && !s && !log_kill) {
+                /* ignore CGROUP_SIGCONT, since this is a no-op alongside SIGKILL */
+                ret = cg_kill_kernel_sigkill(controller, path);
+                if (ret < 0)
+                        return ret;
+        } else {
+                if (!s) {
+                        s = allocated_set = set_new(NULL);
+                        if (!s)
+                                return -ENOMEM;
+                }
 
-        ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata);
+                ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata);
 
-        r = cg_enumerate_subgroups(controller, path, &d);
-        if (r < 0) {
-                if (ret >= 0 && r != -ENOENT)
-                        return r;
+                r = cg_enumerate_subgroups(controller, path, &d);
+                if (r < 0) {
+                        if (ret >= 0 && r != -ENOENT)
+                                return r;
 
-                return ret;
-        }
+                        return ret;
+                }
 
-        while ((r = cg_read_subgroup(d, &fn)) > 0) {
-                _cleanup_free_ char *p = NULL;
+                while ((r = cg_read_subgroup(d, &fn)) > 0) {
+                        _cleanup_free_ char *p = NULL;
 
-                p = path_join(empty_to_root(path), fn);
-                free(fn);
-                if (!p)
-                        return -ENOMEM;
+                        p = path_join(empty_to_root(path), fn);
+                        free(fn);
+                        if (!p)
+                                return -ENOMEM;
 
-                r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata);
-                if (r != 0 && ret >= 0)
+                        r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata);
+                        if (r != 0 && ret >= 0)
+                                ret = r;
+                }
+                if (ret >= 0 && r < 0)
                         ret = r;
         }
-        if (ret >= 0 && r < 0)
-                ret = r;
 
-        if (flags & CGROUP_REMOVE) {
+        if (FLAGS_SET(flags, CGROUP_REMOVE)) {
                 r = cg_rmdir(controller, path);
                 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
                         return r;
@@ -2165,6 +2214,7 @@ static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
         [CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
         [CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
         [CGROUP_CONTROLLER_BPF_SOCKET_BIND] = "bpf-socket-bind",
+        [CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES] = "bpf-restrict-network-interfaces",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
index ce2f4c6589bddbd8998c3884fec05c9ce853a3e0..90ccd2c032652b1cde79b9f8a9001f36880cb06f 100644 (file)
@@ -32,6 +32,7 @@ typedef enum CGroupController {
         CGROUP_CONTROLLER_BPF_DEVICES,
         CGROUP_CONTROLLER_BPF_FOREIGN,
         CGROUP_CONTROLLER_BPF_SOCKET_BIND,
+        CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
 
         _CGROUP_CONTROLLER_MAX,
         _CGROUP_CONTROLLER_INVALID = -EINVAL,
@@ -53,6 +54,7 @@ typedef enum CGroupMask {
         CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
         CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
         CGROUP_MASK_BPF_SOCKET_BIND = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_SOCKET_BIND),
+        CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES),
 
         /* All real cgroup v1 controllers */
         CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
@@ -61,7 +63,7 @@ typedef enum CGroupMask {
         CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
 
         /* All cgroup v2 BPF pseudo-controllers */
-        CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND,
+        CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
 
         _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
 } CGroupMask;
@@ -172,6 +174,7 @@ typedef enum CGroupFlags {
 typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
 
 int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
+int cg_kill_kernel_sigkill(const char *controller, const char *path);
 int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
 
 int cg_split_spec(const char *spec, char **ret_controller, char **ret_path);
@@ -272,6 +275,7 @@ int cg_kernel_controllers(Set **controllers);
 
 bool cg_ns_supported(void);
 bool cg_freezer_supported(void);
+bool cg_kill_supported(void);
 
 int cg_all_unified(void);
 int cg_hybrid_unified(void);
index 81b1e3f10e5c883e1aecf409cb5c23b661a6e647..b42ca50b2505ae81c01b79bdbc7040925c031532 100644 (file)
@@ -183,39 +183,51 @@ static int env_append(char **r, char ***k, char **a) {
         return 0;
 }
 
-char **strv_env_merge(size_t n_lists, ...) {
-        _cleanup_strv_free_ char **ret = NULL;
-        size_t n = 0;
-        char **l, **k;
+char** _strv_env_merge(char **first, ...) {
+        _cleanup_strv_free_ char **merged = NULL;
+        char **k;
         va_list ap;
 
         /* Merges an arbitrary number of environment sets */
 
-        va_start(ap, n_lists);
-        for (size_t i = 0; i < n_lists; i++) {
+        size_t n = strv_length(first);
+
+        va_start(ap, first);
+        for (;;) {
+                char **l;
+
                 l = va_arg(ap, char**);
+                if (l == POINTER_MAX)
+                        break;
+
                 n += strv_length(l);
         }
         va_end(ap);
 
-        ret = new(char*, n+1);
-        if (!ret)
+        k = merged = new(char*, n + 1);
+        if (!merged)
+                return NULL;
+        merged[0] = NULL;
+
+        if (env_append(merged, &k, first) < 0)
                 return NULL;
 
-        *ret = NULL;
-        k = ret;
+        va_start(ap, first);
+        for (;;) {
+                char **l;
 
-        va_start(ap, n_lists);
-        for (size_t i = 0; i < n_lists; i++) {
                 l = va_arg(ap, char**);
-                if (env_append(ret, &k, l) < 0) {
+                if (l == POINTER_MAX)
+                        break;
+
+                if (env_append(merged, &k, l) < 0) {
                         va_end(ap);
                         return NULL;
                 }
         }
         va_end(ap);
 
-        return TAKE_PTR(ret);
+        return TAKE_PTR(merged);
 }
 
 static bool env_match(const char *t, const char *pattern) {
@@ -409,6 +421,32 @@ int strv_env_replace_strdup(char ***l, const char *assignment) {
         return strv_env_replace_consume(l, p);
 }
 
+int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
+        /* Like strv_env_replace_strdup(), but pulls the variable from the environment of
+         * the calling program, if a variable name without value is specified.
+         */
+        char *p;
+
+        if (strchr(assignment, '=')) {
+                if (!env_assignment_is_valid(assignment))
+                        return -EINVAL;
+
+                p = strdup(assignment);
+        } else {
+                if (!env_name_is_valid(assignment))
+                        return -EINVAL;
+
+                /* If we can't find the variable in our environment, we will use
+                 * the empty string. This way "passthrough" is equivalent to passing
+                 * --setenv=FOO=$FOO in the shell. */
+                p = strjoin(assignment, "=", secure_getenv(assignment));
+        }
+        if (!p)
+                return -ENOMEM;
+
+        return strv_env_replace_consume(l, p);
+}
+
 int strv_env_assign(char ***l, const char *key, const char *value) {
         if (!env_name_is_valid(key))
                 return -EINVAL;
@@ -832,3 +870,20 @@ int getenv_path_list(const char *name, char ***ret_paths) {
         *ret_paths = TAKE_PTR(l);
         return 1;
 }
+
+int unsetenv_erase(const char *name) {
+        char *p;
+
+        assert(name);
+
+        p = getenv(name);
+        if (!p)
+                return 0;
+
+        string_erase(p);
+
+        if (unsetenv(name) < 0)
+                return -errno;
+
+        return 1;
+}
index 1fbe7e4270992144bd0e85530291ff4c006ac08e..38bfc8a3f2c18df98393404835329bdca16f5664 100644 (file)
@@ -39,13 +39,15 @@ char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const cha
 bool strv_env_name_is_valid(char **l);
 bool strv_env_name_or_assignment_is_valid(char **l);
 
-char **strv_env_merge(size_t n_lists, ...);
+char** _strv_env_merge(char **first, ...);
+#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
 char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
 
 char **strv_env_unset(char **l, const char *p); /* In place ... */
 char **strv_env_unset_many(char **l, ...) _sentinel_;
 int strv_env_replace_consume(char ***l, char *p); /* In place ... */
 int strv_env_replace_strdup(char ***l, const char *assignment);
+int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
 int strv_env_assign(char ***l, const char *key, const char *value);
 
 char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
@@ -66,3 +68,5 @@ int setenv_systemd_exec_pid(bool update_only);
 /* Parses and does sanity checks on an environment variable containing
  * PATH-like colon-separated absolute paths */
 int getenv_path_list(const char *name, char ***ret_paths);
+
+int unsetenv_erase(const char *name);
index 6b6457dbc22a6baffb77b27c71ccef5b37e3cfe0..d7cf85e0ee8e4826ae46b0c0be6fb520ce3ecaf6 100644 (file)
@@ -433,11 +433,9 @@ bool fdname_is_valid(const char *s) {
 }
 
 int fd_get_path(int fd, char **ret) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         int r;
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        r = readlink_malloc(procfs_path, ret);
+        r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret);
         if (r == -ENOENT) {
                 /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
                  * things debuggable and distinguish the two. */
@@ -647,7 +645,6 @@ finish:
 }
 
 int fd_reopen(int fd, int flags) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         int new_fd;
 
         /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
@@ -657,8 +654,7 @@ int fd_reopen(int fd, int flags) {
          *
          * This implicitly resets the file read index to 0. */
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        new_fd = open(procfs_path, flags);
+        new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
         if (new_fd < 0) {
                 if (errno != ENOENT)
                         return -errno;
index 61b6684cb3c156a8d9040bf6acce590d406d1c44..2382d52d40ce6fd8000477f5011f03ed43a223bf 100644 (file)
@@ -7,6 +7,7 @@
 #include <sys/socket.h>
 
 #include "macro.h"
+#include "stdio-util.h"
 
 /* maximum length of fdname */
 #define FDNAME_MAX 255
@@ -104,7 +105,20 @@ static inline int make_null_stdio(void) {
                 0;                              \
         })
 
-
 int fd_reopen(int fd, int flags);
 int read_nr_open(void);
 int btrfs_defrag_fd(int fd);
+
+/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
+#define PROC_FD_PATH_MAX \
+        (STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int))
+
+static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int fd) {
+        assert(buf);
+        assert(fd >= 0);
+        assert_se(snprintf_ok(buf, PROC_FD_PATH_MAX, "/proc/self/fd/%i", fd));
+        return buf;
+}
+
+#define FORMAT_PROC_FD_PATH(fd) \
+        format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd))
index 19fe6e214c978d8b6bedc0e8fbcc99abaed4a591..f44fa57de47219314898f845642e7bca6efdfa98 100644 (file)
@@ -721,8 +721,6 @@ int read_full_file_full(
                 if (dir_fd == AT_FDCWD)
                         r = sockaddr_un_set_path(&sa.un, filename);
                 else {
-                        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
                         /* If we shall operate relative to some directory, then let's use O_PATH first to
                          * open the socket inode, and then connect to it via /proc/self/fd/. We have to do
                          * this since there's not connectat() that takes a directory fd as first arg. */
@@ -731,8 +729,7 @@ int read_full_file_full(
                         if (dfd < 0)
                                 return -errno;
 
-                        xsprintf(procfs_path, "/proc/self/fd/%i", dfd);
-                        r = sockaddr_un_set_path(&sa.un, procfs_path);
+                        r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd));
                 }
                 if (r < 0)
                         return r;
index af797cfafdbf5372a1a8f946c8efdf09d2fb0f77..4295b84a85c59494a091795dc65035471a19164e 100644 (file)
@@ -2,11 +2,11 @@
 #pragma once
 
 #include <dirent.h>
+#include <fcntl.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <sys/stat.h>
-#include <sys/fcntl.h>
 #include <sys/types.h>
 
 #include "macro.h"
index 9920604f3abe8b7c346289a4293f883958d97e56..e2c7b134d01855c3156096c62aa424f6fbb6c02c 100644 (file)
@@ -15,9 +15,9 @@ char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIf
                 return NULL;
 
         if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
-                snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex);
+                assert(snprintf_ok(buf, IF_NAMESIZE + 1, "%%%d", ifindex));
         else
-                snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex);
+                assert(snprintf_ok(buf, IF_NAMESIZE + 1, "%d", ifindex));
 
         return buf;
 }
@@ -56,23 +56,23 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
         for (size_t i = 0; i < n; i++)
                 if (t >= table[i].factor) {
                         if (flag & FORMAT_BYTES_BELOW_POINT) {
-                                snprintf(buf, l,
-                                         "%" PRIu64 ".%" PRIu64 "%s",
-                                         t / table[i].factor,
-                                         i != n - 1 ?
-                                         (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
-                                         (t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
-                                         table[i].suffix);
+                                (void) snprintf(buf, l,
+                                                "%" PRIu64 ".%" PRIu64 "%s",
+                                                t / table[i].factor,
+                                                i != n - 1 ?
+                                                (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
+                                                (t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
+                                                table[i].suffix);
                         } else
-                                snprintf(buf, l,
-                                         "%" PRIu64 "%s",
-                                         t / table[i].factor,
-                                         table[i].suffix);
+                                (void) snprintf(buf, l,
+                                                "%" PRIu64 "%s",
+                                                t / table[i].factor,
+                                                table[i].suffix);
 
                         goto finish;
                 }
 
-        snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
+        (void) snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
 
 finish:
         buf[l-1] = 0;
index 28c7247e0526f58a46353b9ee7aef34bfc55a14a..027f86fc0b05df975d983ede3d81b6b894ac4b1b 100644 (file)
@@ -308,14 +308,11 @@ int fchmod_umask(int fd, mode_t m) {
 }
 
 int fchmod_opath(int fd, mode_t m) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
         /* This function operates also on fd that might have been opened with
          * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
          * fchownat() does. */
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        if (chmod(procfs_path, m) < 0) {
+        if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) {
                 if (errno != ENOENT)
                         return -errno;
 
@@ -329,12 +326,9 @@ int fchmod_opath(int fd, mode_t m) {
 }
 
 int futimens_opath(int fd, const struct timespec ts[2]) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
         /* Similar to fchmod_path() but for futimens() */
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) {
+        if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) {
                 if (errno != ENOENT)
                         return -errno;
 
@@ -380,7 +374,6 @@ int fd_warn_permissions(const char *path, int fd) {
 }
 
 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
-        char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         _cleanup_close_ int fd = -1;
         int r, ret = 0;
 
@@ -412,8 +405,6 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
         /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
          * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
          * something fchown(), fchmod(), futimensat() don't allow. */
-        xsprintf(fdpath, "/proc/self/fd/%i", fd);
-
         ret = fchmod_and_chown(fd, mode, uid, gid);
 
         if (stamp != USEC_INFINITY) {
@@ -421,9 +412,9 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
 
                 timespec_store(&ts[0], stamp);
                 ts[1] = ts[0];
-                r = utimensat(AT_FDCWD, fdpath, ts, 0);
+                r = utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0);
         } else
-                r = utimensat(AT_FDCWD, fdpath, NULL, 0);
+                r = utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), NULL, 0);
         if (r < 0 && ret >= 0)
                 return -errno;
 
@@ -703,13 +694,10 @@ int unlink_or_warn(const char *filename) {
 }
 
 int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
-        char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
         int wd;
 
         /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
-        xsprintf(path, "/proc/self/fd/%i", what);
-
-        wd = inotify_add_watch(fd, path, mask);
+        wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
         if (wd < 0)
                 return -errno;
 
@@ -1156,7 +1144,6 @@ int chase_symlinks_and_opendir(
                 char **ret_path,
                 DIR **ret_dir) {
 
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         _cleanup_close_ int path_fd = -1;
         _cleanup_free_ char *p = NULL;
         DIR *d;
@@ -1182,8 +1169,7 @@ int chase_symlinks_and_opendir(
                 return r;
         assert(path_fd >= 0);
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", path_fd);
-        d = opendir(procfs_path);
+        d = opendir(FORMAT_PROC_FD_PATH(path_fd));
         if (!d)
                 return -errno;
 
@@ -1237,12 +1223,9 @@ int chase_symlinks_and_stat(
 }
 
 int access_fd(int fd, int mode) {
-        char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
-
         /* Like access() but operates on an already open fd */
 
-        xsprintf(p, "/proc/self/fd/%i", fd);
-        if (access(p, mode) < 0) {
+        if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
                 if (errno != ENOENT)
                         return -errno;
 
index 0decbb04e10fdba6e458b033c5f34044f8516fdb..b51d70bc87928d0a708ba7b079110dde7f6d8be0 100644 (file)
@@ -401,7 +401,7 @@ static struct hashmap_base_entry* bucket_at_virtual(HashmapBase *h, struct swap_
         if (idx < _IDX_SWAP_END)
                 return &bucket_at_swap(swap, idx)->p.b;
 
-        assert_not_reached("Invalid index");
+        assert_not_reached();
 }
 
 static dib_raw_t* dib_raw_ptr(HashmapBase *h) {
@@ -513,7 +513,7 @@ static void* entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
                 return (void*) e->key;
 
         default:
-                assert_not_reached("Unknown hashmap type");
+                assert_not_reached();
         }
 }
 
@@ -1747,7 +1747,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h  HASHMAP_DEBUG_PARAMS) {
                 r = set_merge((Set*)copy, (Set*)h);
                 break;
         default:
-                assert_not_reached("Unknown hashmap type");
+                assert_not_reached();
         }
 
         if (r < 0)
index 29d69910d2486cc1e431d00c0503a52279be0812..1d0640e075685f5143115d409910aa1cf90fc29f 100644 (file)
@@ -36,66 +36,39 @@ char* get_default_hostname(void) {
         return strdup(FALLBACK_HOSTNAME);
 }
 
-char* gethostname_malloc(void) {
+int gethostname_full(GetHostnameFlags flags, char **ret) {
+        _cleanup_free_ char *buf = NULL, *fallback = NULL;
         struct utsname u;
         const char *s;
 
-        /* This call tries to return something useful, either the actual hostname
-         * or it makes something up. The only reason it might fail is OOM.
-         * It might even return "localhost" if that's set. */
+        assert(ret);
 
         assert_se(uname(&u) >= 0);
 
         s = u.nodename;
-        if (isempty(s) || streq(s, "(none)"))
-                return get_default_hostname();
-
-        return strdup(s);
-}
-
-char* gethostname_short_malloc(void) {
-        struct utsname u;
-        const char *s;
-        _cleanup_free_ char *f = NULL;
-
-        /* Like above, but kills the FQDN part if present. */
-
-        assert_se(uname(&u) >= 0);
-
-        s = u.nodename;
-        if (isempty(s) || streq(s, "(none)") || s[0] == '.') {
-                s = f = get_default_hostname();
+        if (isempty(s) ||
+            (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_NONE) && streq(s, "(none)")) ||
+            (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) ||
+            (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
+                if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))
+                        return -ENXIO;
+
+                s = fallback = get_default_hostname();
                 if (!s)
-                        return NULL;
+                        return -ENOMEM;
 
-                assert(s[0] != '.');
+                if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')
+                        return -ENXIO;
         }
 
-        return strndup(s, strcspn(s, "."));
-}
-
-int gethostname_strict(char **ret) {
-        struct utsname u;
-        char *k;
-
-        /* This call will rather fail than make up a name. It will not return "localhost" either. */
-
-        assert_se(uname(&u) >= 0);
-
-        if (isempty(u.nodename))
-                return -ENXIO;
-
-        if (streq(u.nodename, "(none)"))
-                return -ENXIO;
-
-        if (is_localhost(u.nodename))
-                return -ENXIO;
-
-        k = strdup(u.nodename);
-        if (!k)
+        if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
+                buf = strndup(s, strcspn(s, "."));
+        else
+                buf = strdup(s);
+        if (!buf)
                 return -ENOMEM;
 
-        *ret = k;
+        *ret = TAKE_PTR(buf);
         return 0;
 }
 
index c3fc6752cbae43369ebfdbfcb64274da817bd358..28975c879203f6af1467a5319a136ed1a8d9d34e 100644 (file)
@@ -7,10 +7,37 @@
 #include "macro.h"
 #include "strv.h"
 
+typedef enum GetHostnameFlags {
+        GET_HOSTNAME_ALLOW_NONE       = 1 << 0, /* accepts "(none)". */
+        GET_HOSTNAME_ALLOW_LOCALHOST  = 1 << 1, /* accepts "localhost" or friends. */
+        GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 2, /* use default hostname if no hostname is set. */
+        GET_HOSTNAME_SHORT            = 1 << 3, /* kills the FQDN part if present. */
+} GetHostnameFlags;
+
+int gethostname_full(GetHostnameFlags flags, char **ret);
+static inline int gethostname_strict(char **ret) {
+        return gethostname_full(0, ret);
+}
+
+static inline char* gethostname_malloc(void) {
+        char *s;
+
+        if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0)
+                return NULL;
+
+        return s;
+}
+
+static inline char* gethostname_short_malloc(void) {
+        char *s;
+
+        if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0)
+                return NULL;
+
+        return s;
+}
+
 char* get_default_hostname(void);
-char* gethostname_malloc(void);
-char* gethostname_short_malloc(void);
-int gethostname_strict(char **ret);
 
 bool valid_ldh_char(char c) _const_;
 
index 9cc92a1c767bf83746bb2129ce75860a0857ac0d..2187bd0cbacf2522ea78cfa28ff144f5a5b687ef 100644 (file)
@@ -794,7 +794,7 @@ int in_addr_prefix_from_string_auto_internal(
                                 k = 0;
                         break;
                 default:
-                        assert_not_reached("Invalid prefixlen mode");
+                        assert_not_reached();
                 }
 
         if (ret_family)
index d035e4cf344f1c327f95ace85a2f3e8f474f7cdc..35dc016c9bb41b91e59ab2b698c1097e6396cbfb 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: MIT */
-/* Copyright (C) 2016-2020  B.A.T.M.A.N. contributors:
+/* Copyright (C) B.A.T.M.A.N. contributors:
  *
  * Matthias Schiffer
  */
  * in the TT CRC computation.
  */
 enum batadv_tt_client_flags {
-        /**
-         * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
-         */
-        BATADV_TT_CLIENT_DEL     = (1 << 0),
-
-        /**
-         * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and
-         * the new update telling its new real location has not been
-         * received/sent yet
-         */
-        BATADV_TT_CLIENT_ROAM    = (1 << 1),
-
-        /**
-         * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi
-         * interface. This information is used by the "AP Isolation" feature
-         */
-        BATADV_TT_CLIENT_WIFI    = (1 << 4),
-
-        /**
-         * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
-         * information is used by the Extended Isolation feature
-         */
-        BATADV_TT_CLIENT_ISOLA  = (1 << 5),
-
-        /**
-         * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from
-         * the table
-         */
-        BATADV_TT_CLIENT_NOPURGE = (1 << 8),
-
-        /**
-         * @BATADV_TT_CLIENT_NEW: this client has been added to the local table
-         * but has not been announced yet
-         */
-        BATADV_TT_CLIENT_NEW     = (1 << 9),
-
-        /**
-         * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it
-         * is kept in the table for one more originator interval for consistency
-         * purposes
-         */
-        BATADV_TT_CLIENT_PENDING = (1 << 10),
-
-        /**
-         * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be
-         * part of the network but no node has already announced it
-         */
-        BATADV_TT_CLIENT_TEMP   = (1 << 11),
+       /**
+        * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
+        */
+       BATADV_TT_CLIENT_DEL     = (1 << 0),
+
+       /**
+        * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and
+        * the new update telling its new real location has not been
+        * received/sent yet
+        */
+       BATADV_TT_CLIENT_ROAM    = (1 << 1),
+
+       /**
+        * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi
+        * interface. This information is used by the "AP Isolation" feature
+        */
+       BATADV_TT_CLIENT_WIFI    = (1 << 4),
+
+       /**
+        * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
+        * information is used by the Extended Isolation feature
+        */
+       BATADV_TT_CLIENT_ISOLA   = (1 << 5),
+
+       /**
+        * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from
+        * the table
+        */
+       BATADV_TT_CLIENT_NOPURGE = (1 << 8),
+
+       /**
+        * @BATADV_TT_CLIENT_NEW: this client has been added to the local table
+        * but has not been announced yet
+        */
+       BATADV_TT_CLIENT_NEW     = (1 << 9),
+
+       /**
+        * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it
+        * is kept in the table for one more originator interval for consistency
+        * purposes
+        */
+       BATADV_TT_CLIENT_PENDING = (1 << 10),
+
+       /**
+        * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be
+        * part of the network but no node has already announced it
+        */
+       BATADV_TT_CLIENT_TEMP    = (1 << 11),
 };
 
 /**
@@ -88,615 +88,615 @@ enum batadv_tt_client_flags {
  * related flags are undefined.
  */
 enum batadv_mcast_flags_priv {
-        /**
-         * @BATADV_MCAST_FLAGS_BRIDGED: There is a bridge on top of the mesh
-         * interface.
-         */
-        BATADV_MCAST_FLAGS_BRIDGED                     = (1 << 0),
-
-        /**
-         * @BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS: Whether an IGMP querier
-         * exists in the mesh
-         */
-        BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS         = (1 << 1),
-
-        /**
-         * @BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS: Whether an MLD querier
-         * exists in the mesh
-         */
-        BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS         = (1 << 2),
-
-        /**
-         * @BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING: If an IGMP querier
-         * exists, whether it is potentially shadowing multicast listeners
-         * (i.e. querier is behind our own bridge segment)
-         */
-        BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING      = (1 << 3),
-
-        /**
-         * @BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING: If an MLD querier
-         * exists, whether it is potentially shadowing multicast listeners
-         * (i.e. querier is behind our own bridge segment)
-         */
-        BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING      = (1 << 4),
+       /**
+        * @BATADV_MCAST_FLAGS_BRIDGED: There is a bridge on top of the mesh
+        * interface.
+        */
+       BATADV_MCAST_FLAGS_BRIDGED                      = (1 << 0),
+
+       /**
+        * @BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS: Whether an IGMP querier
+        * exists in the mesh
+        */
+       BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS          = (1 << 1),
+
+       /**
+        * @BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS: Whether an MLD querier
+        * exists in the mesh
+        */
+       BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS          = (1 << 2),
+
+       /**
+        * @BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING: If an IGMP querier
+        * exists, whether it is potentially shadowing multicast listeners
+        * (i.e. querier is behind our own bridge segment)
+        */
+       BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING       = (1 << 3),
+
+       /**
+        * @BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING: If an MLD querier
+        * exists, whether it is potentially shadowing multicast listeners
+        * (i.e. querier is behind our own bridge segment)
+        */
+       BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING       = (1 << 4),
 };
 
 /**
  * enum batadv_gw_modes - gateway mode of node
  */
 enum batadv_gw_modes {
-        /** @BATADV_GW_MODE_OFF: gw mode disabled */
-        BATADV_GW_MODE_OFF,
+       /** @BATADV_GW_MODE_OFF: gw mode disabled */
+       BATADV_GW_MODE_OFF,
 
-        /** @BATADV_GW_MODE_CLIENT: send DHCP requests to gw servers */
-        BATADV_GW_MODE_CLIENT,
+       /** @BATADV_GW_MODE_CLIENT: send DHCP requests to gw servers */
+       BATADV_GW_MODE_CLIENT,
 
-        /** @BATADV_GW_MODE_SERVER: announce itself as gateway server */
-        BATADV_GW_MODE_SERVER,
+       /** @BATADV_GW_MODE_SERVER: announce itself as gateway server */
+       BATADV_GW_MODE_SERVER,
 };
 
 /**
  * enum batadv_nl_attrs - batman-adv netlink attributes
  */
 enum batadv_nl_attrs {
-        /**
-         * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors
-         */
-        BATADV_ATTR_UNSPEC,
-
-        /**
-         * @BATADV_ATTR_VERSION: batman-adv version string
-         */
-        BATADV_ATTR_VERSION,
-
-        /**
-         * @BATADV_ATTR_ALGO_NAME: name of routing algorithm
-         */
-        BATADV_ATTR_ALGO_NAME,
-
-        /**
-         * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface
-         */
-        BATADV_ATTR_MESH_IFINDEX,
-
-        /**
-         * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface
-         */
-        BATADV_ATTR_MESH_IFNAME,
-
-        /**
-         * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface
-         */
-        BATADV_ATTR_MESH_ADDRESS,
-
-        /**
-         * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface
-         */
-        BATADV_ATTR_HARD_IFINDEX,
-
-        /**
-         * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface
-         */
-        BATADV_ATTR_HARD_IFNAME,
-
-        /**
-         * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv
-         * interface
-         */
-        BATADV_ATTR_HARD_ADDRESS,
-
-        /**
-         * @BATADV_ATTR_ORIG_ADDRESS: originator mac address
-         */
-        BATADV_ATTR_ORIG_ADDRESS,
-
-        /**
-         * @BATADV_ATTR_TPMETER_RESULT: result of run (see
-         * batadv_tp_meter_status)
-         */
-        BATADV_ATTR_TPMETER_RESULT,
-
-        /**
-         * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took
-         */
-        BATADV_ATTR_TPMETER_TEST_TIME,
-
-        /**
-         * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run
-         */
-        BATADV_ATTR_TPMETER_BYTES,
-
-        /**
-         * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session
-         */
-        BATADV_ATTR_TPMETER_COOKIE,
-
-        /**
-         * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment
-         */
-        BATADV_ATTR_PAD,
-
-        /**
-         * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active
-         */
-        BATADV_ATTR_ACTIVE,
-
-        /**
-         * @BATADV_ATTR_TT_ADDRESS: Client MAC address
-         */
-        BATADV_ATTR_TT_ADDRESS,
-
-        /**
-         * @BATADV_ATTR_TT_TTVN: Translation table version
-         */
-        BATADV_ATTR_TT_TTVN,
-
-        /**
-         * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version
-         */
-        BATADV_ATTR_TT_LAST_TTVN,
-
-        /**
-         * @BATADV_ATTR_TT_CRC32: CRC32 over translation table
-         */
-        BATADV_ATTR_TT_CRC32,
-
-        /**
-         * @BATADV_ATTR_TT_VID: VLAN ID
-         */
-        BATADV_ATTR_TT_VID,
-
-        /**
-         * @BATADV_ATTR_TT_FLAGS: Translation table client flags
-         */
-        BATADV_ATTR_TT_FLAGS,
-
-        /**
-         * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best
-         */
-        BATADV_ATTR_FLAG_BEST,
-
-        /**
-         * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen
-         */
-        BATADV_ATTR_LAST_SEEN_MSECS,
-
-        /**
-         * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address
-         */
-        BATADV_ATTR_NEIGH_ADDRESS,
-
-        /**
-         * @BATADV_ATTR_TQ: TQ to neighbour
-         */
-        BATADV_ATTR_TQ,
-
-        /**
-         * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour
-         */
-        BATADV_ATTR_THROUGHPUT,
-
-        /**
-         * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth
-         */
-        BATADV_ATTR_BANDWIDTH_UP,
-
-        /**
-         * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth
-         */
-        BATADV_ATTR_BANDWIDTH_DOWN,
-
-        /**
-         * @BATADV_ATTR_ROUTER: Gateway router MAC address
-         */
-        BATADV_ATTR_ROUTER,
-
-        /**
-         * @BATADV_ATTR_BLA_OWN: Flag indicating own originator
-         */
-        BATADV_ATTR_BLA_OWN,
-
-        /**
-         * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address
-         */
-        BATADV_ATTR_BLA_ADDRESS,
-
-        /**
-         * @BATADV_ATTR_BLA_VID: BLA VLAN ID
-         */
-        BATADV_ATTR_BLA_VID,
-
-        /**
-         * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address
-         */
-        BATADV_ATTR_BLA_BACKBONE,
-
-        /**
-         * @BATADV_ATTR_BLA_CRC: BLA CRC
-         */
-        BATADV_ATTR_BLA_CRC,
-
-        /**
-         * @BATADV_ATTR_DAT_CACHE_IP4ADDRESS: Client IPv4 address
-         */
-        BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
-
-        /**
-         * @BATADV_ATTR_DAT_CACHE_HWADDRESS: Client MAC address
-         */
-        BATADV_ATTR_DAT_CACHE_HWADDRESS,
-
-        /**
-         * @BATADV_ATTR_DAT_CACHE_VID: VLAN ID
-         */
-        BATADV_ATTR_DAT_CACHE_VID,
-
-        /**
-         * @BATADV_ATTR_MCAST_FLAGS: Per originator multicast flags
-         */
-        BATADV_ATTR_MCAST_FLAGS,
-
-        /**
-         * @BATADV_ATTR_MCAST_FLAGS_PRIV: Private, own multicast flags
-         */
-        BATADV_ATTR_MCAST_FLAGS_PRIV,
-
-        /**
-         * @BATADV_ATTR_VLANID: VLAN id on top of soft interface
-         */
-        BATADV_ATTR_VLANID,
-
-        /**
-         * @BATADV_ATTR_AGGREGATED_OGMS_ENABLED: whether the batman protocol
-         *  messages of the mesh interface shall be aggregated or not.
-         */
-        BATADV_ATTR_AGGREGATED_OGMS_ENABLED,
-
-        /**
-         * @BATADV_ATTR_AP_ISOLATION_ENABLED: whether the data traffic going
-         *  from a wireless client to another wireless client will be silently
-         *  dropped.
-         */
-        BATADV_ATTR_AP_ISOLATION_ENABLED,
-
-        /**
-         * @BATADV_ATTR_ISOLATION_MARK: the isolation mark which is used to
-         *  classify clients as "isolated" by the Extended Isolation feature.
-         */
-        BATADV_ATTR_ISOLATION_MARK,
-
-        /**
-         * @BATADV_ATTR_ISOLATION_MASK: the isolation (bit)mask which is used to
-         *  classify clients as "isolated" by the Extended Isolation feature.
-         */
-        BATADV_ATTR_ISOLATION_MASK,
-
-        /**
-         * @BATADV_ATTR_BONDING_ENABLED: whether the data traffic going through
-         *  the mesh will be sent using multiple interfaces at the same time.
-         */
-        BATADV_ATTR_BONDING_ENABLED,
-
-        /**
-         * @BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED: whether the bridge loop
-         *  avoidance feature is enabled. This feature detects and avoids loops
-         *  between the mesh and devices bridged with the soft interface
-         */
-        BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED,
-
-        /**
-         * @BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED: whether the distributed
-         *  arp table feature is enabled. This feature uses a distributed hash
-         *  table to answer ARP requests without flooding the request through
-         *  the whole mesh.
-         */
-        BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED,
-
-        /**
-         * @BATADV_ATTR_FRAGMENTATION_ENABLED: whether the data traffic going
-         *  through the mesh will be fragmented or silently discarded if the
-         *  packet size exceeds the outgoing interface MTU.
-         */
-        BATADV_ATTR_FRAGMENTATION_ENABLED,
-
-        /**
-         * @BATADV_ATTR_GW_BANDWIDTH_DOWN: defines the download bandwidth which
-         *  is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
-         *  to 'server'.
-         */
-        BATADV_ATTR_GW_BANDWIDTH_DOWN,
-
-        /**
-         * @BATADV_ATTR_GW_BANDWIDTH_UP: defines the upload bandwidth which
-         *  is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
-         *  to 'server'.
-         */
-        BATADV_ATTR_GW_BANDWIDTH_UP,
-
-        /**
-         * @BATADV_ATTR_GW_MODE: defines the state of the gateway features.
-         * Possible values are specified in enum batadv_gw_modes
-         */
-        BATADV_ATTR_GW_MODE,
-
-        /**
-         * @BATADV_ATTR_GW_SEL_CLASS: defines the selection criteria this node
-         *  will use to choose a gateway if gw_mode was set to 'client'.
-         */
-        BATADV_ATTR_GW_SEL_CLASS,
-
-        /**
-         * @BATADV_ATTR_HOP_PENALTY: defines the penalty which will be applied
-         *  to an originator message's tq-field on every hop and/or per
-         *  hard interface
-         */
-        BATADV_ATTR_HOP_PENALTY,
-
-        /**
-         * @BATADV_ATTR_LOG_LEVEL: bitmask with to define which debug messages
-         *  should be send to the debug log/trace ring buffer
-         */
-        BATADV_ATTR_LOG_LEVEL,
-
-        /**
-         * @BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED: whether multicast
-         *  optimizations should be replaced by simple broadcast-like flooding
-         *  of multicast packets. If set to non-zero then all nodes in the mesh
-         *  are going to use classic flooding for any multicast packet with no
-         *  optimizations.
-         */
-        BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED,
-
-        /**
-         * @BATADV_ATTR_NETWORK_CODING_ENABLED: whether Network Coding (using
-         *  some magic to send fewer wifi packets but still the same content) is
-         *  enabled or not.
-         */
-        BATADV_ATTR_NETWORK_CODING_ENABLED,
-
-        /**
-         * @BATADV_ATTR_ORIG_INTERVAL: defines the interval in milliseconds in
-         *  which batman sends its protocol messages.
-         */
-        BATADV_ATTR_ORIG_INTERVAL,
-
-        /**
-         * @BATADV_ATTR_ELP_INTERVAL: defines the interval in milliseconds in
-         *  which batman emits probing packets for neighbor sensing (ELP).
-         */
-        BATADV_ATTR_ELP_INTERVAL,
-
-        /**
-         * @BATADV_ATTR_THROUGHPUT_OVERRIDE: defines the throughput value to be
-         *  used by B.A.T.M.A.N. V when estimating the link throughput using
-         *  this interface. If the value is set to 0 then batman-adv will try to
-         *  estimate the throughput by itself.
-         */
-        BATADV_ATTR_THROUGHPUT_OVERRIDE,
-
-        /**
-         * @BATADV_ATTR_MULTICAST_FANOUT: defines the maximum number of packet
-         * copies that may be generated for a multicast-to-unicast conversion.
-         * Once this limit is exceeded distribution will fall back to broadcast.
-         */
-        BATADV_ATTR_MULTICAST_FANOUT,
-
-        /* add attributes above here, update the policy in netlink.c */
-
-        /**
-         * @__BATADV_ATTR_AFTER_LAST: internal use
-         */
-        __BATADV_ATTR_AFTER_LAST,
-
-        /**
-         * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available
-         */
-        NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST,
-
-        /**
-         * @BATADV_ATTR_MAX: highest attribute number currently defined
-         */
-        BATADV_ATTR_MAX = __BATADV_ATTR_AFTER_LAST - 1
+       /**
+        * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors
+        */
+       BATADV_ATTR_UNSPEC,
+
+       /**
+        * @BATADV_ATTR_VERSION: batman-adv version string
+        */
+       BATADV_ATTR_VERSION,
+
+       /**
+        * @BATADV_ATTR_ALGO_NAME: name of routing algorithm
+        */
+       BATADV_ATTR_ALGO_NAME,
+
+       /**
+        * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface
+        */
+       BATADV_ATTR_MESH_IFINDEX,
+
+       /**
+        * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface
+        */
+       BATADV_ATTR_MESH_IFNAME,
+
+       /**
+        * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface
+        */
+       BATADV_ATTR_MESH_ADDRESS,
+
+       /**
+        * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface
+        */
+       BATADV_ATTR_HARD_IFINDEX,
+
+       /**
+        * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface
+        */
+       BATADV_ATTR_HARD_IFNAME,
+
+       /**
+        * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv
+        * interface
+        */
+       BATADV_ATTR_HARD_ADDRESS,
+
+       /**
+        * @BATADV_ATTR_ORIG_ADDRESS: originator mac address
+        */
+       BATADV_ATTR_ORIG_ADDRESS,
+
+       /**
+        * @BATADV_ATTR_TPMETER_RESULT: result of run (see
+        * batadv_tp_meter_status)
+        */
+       BATADV_ATTR_TPMETER_RESULT,
+
+       /**
+        * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took
+        */
+       BATADV_ATTR_TPMETER_TEST_TIME,
+
+       /**
+        * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run
+        */
+       BATADV_ATTR_TPMETER_BYTES,
+
+       /**
+        * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session
+        */
+       BATADV_ATTR_TPMETER_COOKIE,
+
+       /**
+        * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment
+        */
+       BATADV_ATTR_PAD,
+
+       /**
+        * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active
+        */
+       BATADV_ATTR_ACTIVE,
+
+       /**
+        * @BATADV_ATTR_TT_ADDRESS: Client MAC address
+        */
+       BATADV_ATTR_TT_ADDRESS,
+
+       /**
+        * @BATADV_ATTR_TT_TTVN: Translation table version
+        */
+       BATADV_ATTR_TT_TTVN,
+
+       /**
+        * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version
+        */
+       BATADV_ATTR_TT_LAST_TTVN,
+
+       /**
+        * @BATADV_ATTR_TT_CRC32: CRC32 over translation table
+        */
+       BATADV_ATTR_TT_CRC32,
+
+       /**
+        * @BATADV_ATTR_TT_VID: VLAN ID
+        */
+       BATADV_ATTR_TT_VID,
+
+       /**
+        * @BATADV_ATTR_TT_FLAGS: Translation table client flags
+        */
+       BATADV_ATTR_TT_FLAGS,
+
+       /**
+        * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best
+        */
+       BATADV_ATTR_FLAG_BEST,
+
+       /**
+        * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen
+        */
+       BATADV_ATTR_LAST_SEEN_MSECS,
+
+       /**
+        * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address
+        */
+       BATADV_ATTR_NEIGH_ADDRESS,
+
+       /**
+        * @BATADV_ATTR_TQ: TQ to neighbour
+        */
+       BATADV_ATTR_TQ,
+
+       /**
+        * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour
+        */
+       BATADV_ATTR_THROUGHPUT,
+
+       /**
+        * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth
+        */
+       BATADV_ATTR_BANDWIDTH_UP,
+
+       /**
+        * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth
+        */
+       BATADV_ATTR_BANDWIDTH_DOWN,
+
+       /**
+        * @BATADV_ATTR_ROUTER: Gateway router MAC address
+        */
+       BATADV_ATTR_ROUTER,
+
+       /**
+        * @BATADV_ATTR_BLA_OWN: Flag indicating own originator
+        */
+       BATADV_ATTR_BLA_OWN,
+
+       /**
+        * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address
+        */
+       BATADV_ATTR_BLA_ADDRESS,
+
+       /**
+        * @BATADV_ATTR_BLA_VID: BLA VLAN ID
+        */
+       BATADV_ATTR_BLA_VID,
+
+       /**
+        * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address
+        */
+       BATADV_ATTR_BLA_BACKBONE,
+
+       /**
+        * @BATADV_ATTR_BLA_CRC: BLA CRC
+        */
+       BATADV_ATTR_BLA_CRC,
+
+       /**
+        * @BATADV_ATTR_DAT_CACHE_IP4ADDRESS: Client IPv4 address
+        */
+       BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
+
+       /**
+        * @BATADV_ATTR_DAT_CACHE_HWADDRESS: Client MAC address
+        */
+       BATADV_ATTR_DAT_CACHE_HWADDRESS,
+
+       /**
+        * @BATADV_ATTR_DAT_CACHE_VID: VLAN ID
+        */
+       BATADV_ATTR_DAT_CACHE_VID,
+
+       /**
+        * @BATADV_ATTR_MCAST_FLAGS: Per originator multicast flags
+        */
+       BATADV_ATTR_MCAST_FLAGS,
+
+       /**
+        * @BATADV_ATTR_MCAST_FLAGS_PRIV: Private, own multicast flags
+        */
+       BATADV_ATTR_MCAST_FLAGS_PRIV,
+
+       /**
+        * @BATADV_ATTR_VLANID: VLAN id on top of soft interface
+        */
+       BATADV_ATTR_VLANID,
+
+       /**
+        * @BATADV_ATTR_AGGREGATED_OGMS_ENABLED: whether the batman protocol
+        *  messages of the mesh interface shall be aggregated or not.
+        */
+       BATADV_ATTR_AGGREGATED_OGMS_ENABLED,
+
+       /**
+        * @BATADV_ATTR_AP_ISOLATION_ENABLED: whether the data traffic going
+        *  from a wireless client to another wireless client will be silently
+        *  dropped.
+        */
+       BATADV_ATTR_AP_ISOLATION_ENABLED,
+
+       /**
+        * @BATADV_ATTR_ISOLATION_MARK: the isolation mark which is used to
+        *  classify clients as "isolated" by the Extended Isolation feature.
+        */
+       BATADV_ATTR_ISOLATION_MARK,
+
+       /**
+        * @BATADV_ATTR_ISOLATION_MASK: the isolation (bit)mask which is used to
+        *  classify clients as "isolated" by the Extended Isolation feature.
+        */
+       BATADV_ATTR_ISOLATION_MASK,
+
+       /**
+        * @BATADV_ATTR_BONDING_ENABLED: whether the data traffic going through
+        *  the mesh will be sent using multiple interfaces at the same time.
+        */
+       BATADV_ATTR_BONDING_ENABLED,
+
+       /**
+        * @BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED: whether the bridge loop
+        *  avoidance feature is enabled. This feature detects and avoids loops
+        *  between the mesh and devices bridged with the soft interface
+        */
+       BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED,
+
+       /**
+        * @BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED: whether the distributed
+        *  arp table feature is enabled. This feature uses a distributed hash
+        *  table to answer ARP requests without flooding the request through
+        *  the whole mesh.
+        */
+       BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED,
+
+       /**
+        * @BATADV_ATTR_FRAGMENTATION_ENABLED: whether the data traffic going
+        *  through the mesh will be fragmented or silently discarded if the
+        *  packet size exceeds the outgoing interface MTU.
+        */
+       BATADV_ATTR_FRAGMENTATION_ENABLED,
+
+       /**
+        * @BATADV_ATTR_GW_BANDWIDTH_DOWN: defines the download bandwidth which
+        *  is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
+        *  to 'server'.
+        */
+       BATADV_ATTR_GW_BANDWIDTH_DOWN,
+
+       /**
+        * @BATADV_ATTR_GW_BANDWIDTH_UP: defines the upload bandwidth which
+        *  is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
+        *  to 'server'.
+        */
+       BATADV_ATTR_GW_BANDWIDTH_UP,
+
+       /**
+        * @BATADV_ATTR_GW_MODE: defines the state of the gateway features.
+        * Possible values are specified in enum batadv_gw_modes
+        */
+       BATADV_ATTR_GW_MODE,
+
+       /**
+        * @BATADV_ATTR_GW_SEL_CLASS: defines the selection criteria this node
+        *  will use to choose a gateway if gw_mode was set to 'client'.
+        */
+       BATADV_ATTR_GW_SEL_CLASS,
+
+       /**
+        * @BATADV_ATTR_HOP_PENALTY: defines the penalty which will be applied
+        *  to an originator message's tq-field on every hop and/or per
+        *  hard interface
+        */
+       BATADV_ATTR_HOP_PENALTY,
+
+       /**
+        * @BATADV_ATTR_LOG_LEVEL: bitmask with to define which debug messages
+        *  should be send to the debug log/trace ring buffer
+        */
+       BATADV_ATTR_LOG_LEVEL,
+
+       /**
+        * @BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED: whether multicast
+        *  optimizations should be replaced by simple broadcast-like flooding
+        *  of multicast packets. If set to non-zero then all nodes in the mesh
+        *  are going to use classic flooding for any multicast packet with no
+        *  optimizations.
+        */
+       BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED,
+
+       /**
+        * @BATADV_ATTR_NETWORK_CODING_ENABLED: whether Network Coding (using
+        *  some magic to send fewer wifi packets but still the same content) is
+        *  enabled or not.
+        */
+       BATADV_ATTR_NETWORK_CODING_ENABLED,
+
+       /**
+        * @BATADV_ATTR_ORIG_INTERVAL: defines the interval in milliseconds in
+        *  which batman sends its protocol messages.
+        */
+       BATADV_ATTR_ORIG_INTERVAL,
+
+       /**
+        * @BATADV_ATTR_ELP_INTERVAL: defines the interval in milliseconds in
+        *  which batman emits probing packets for neighbor sensing (ELP).
+        */
+       BATADV_ATTR_ELP_INTERVAL,
+
+       /**
+        * @BATADV_ATTR_THROUGHPUT_OVERRIDE: defines the throughput value to be
+        *  used by B.A.T.M.A.N. V when estimating the link throughput using
+        *  this interface. If the value is set to 0 then batman-adv will try to
+        *  estimate the throughput by itself.
+        */
+       BATADV_ATTR_THROUGHPUT_OVERRIDE,
+
+       /**
+        * @BATADV_ATTR_MULTICAST_FANOUT: defines the maximum number of packet
+        * copies that may be generated for a multicast-to-unicast conversion.
+        * Once this limit is exceeded distribution will fall back to broadcast.
+        */
+       BATADV_ATTR_MULTICAST_FANOUT,
+
+       /* add attributes above here, update the policy in netlink.c */
+
+       /**
+        * @__BATADV_ATTR_AFTER_LAST: internal use
+        */
+       __BATADV_ATTR_AFTER_LAST,
+
+       /**
+        * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available
+        */
+       NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST,
+
+       /**
+        * @BATADV_ATTR_MAX: highest attribute number currently defined
+        */
+       BATADV_ATTR_MAX = __BATADV_ATTR_AFTER_LAST - 1
 };
 
 /**
  * enum batadv_nl_commands - supported batman-adv netlink commands
  */
 enum batadv_nl_commands {
-        /**
-         * @BATADV_CMD_UNSPEC: unspecified command to catch errors
-         */
-        BATADV_CMD_UNSPEC,
-
-        /**
-         * @BATADV_CMD_GET_MESH: Get attributes from softif/mesh
-         */
-        BATADV_CMD_GET_MESH,
-
-        /**
-         * @BATADV_CMD_GET_MESH_INFO: Alias for @BATADV_CMD_GET_MESH
-         */
-        BATADV_CMD_GET_MESH_INFO = BATADV_CMD_GET_MESH,
-
-        /**
-         * @BATADV_CMD_TP_METER: Start a tp meter session
-         */
-        BATADV_CMD_TP_METER,
-
-        /**
-         * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session
-         */
-        BATADV_CMD_TP_METER_CANCEL,
-
-        /**
-         * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms.
-         */
-        BATADV_CMD_GET_ROUTING_ALGOS,
-
-        /**
-         * @BATADV_CMD_GET_HARDIF: Get attributes from a hardif of the
-         *  current softif
-         */
-        BATADV_CMD_GET_HARDIF,
-
-        /**
-         * @BATADV_CMD_GET_HARDIFS: Alias for @BATADV_CMD_GET_HARDIF
-         */
-        BATADV_CMD_GET_HARDIFS = BATADV_CMD_GET_HARDIF,
-
-        /**
-         * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
-         */
-        BATADV_CMD_GET_TRANSTABLE_LOCAL,
-
-        /**
-         * @BATADV_CMD_GET_TRANSTABLE_GLOBAL: Query list of global translations
-         */
-        BATADV_CMD_GET_TRANSTABLE_GLOBAL,
-
-        /**
-         * @BATADV_CMD_GET_ORIGINATORS: Query list of originators
-         */
-        BATADV_CMD_GET_ORIGINATORS,
-
-        /**
-         * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours
-         */
-        BATADV_CMD_GET_NEIGHBORS,
-
-        /**
-         * @BATADV_CMD_GET_GATEWAYS: Query list of gateways
-         */
-        BATADV_CMD_GET_GATEWAYS,
-
-        /**
-         * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims
-         */
-        BATADV_CMD_GET_BLA_CLAIM,
-
-        /**
-         * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance
-         * backbones
-         */
-        BATADV_CMD_GET_BLA_BACKBONE,
-
-        /**
-         * @BATADV_CMD_GET_DAT_CACHE: Query list of DAT cache entries
-         */
-        BATADV_CMD_GET_DAT_CACHE,
-
-        /**
-         * @BATADV_CMD_GET_MCAST_FLAGS: Query list of multicast flags
-         */
-        BATADV_CMD_GET_MCAST_FLAGS,
-
-        /**
-         * @BATADV_CMD_SET_MESH: Set attributes for softif/mesh
-         */
-        BATADV_CMD_SET_MESH,
-
-        /**
-         * @BATADV_CMD_SET_HARDIF: Set attributes for hardif of the
-         *  current softif
-         */
-        BATADV_CMD_SET_HARDIF,
-
-        /**
-         * @BATADV_CMD_GET_VLAN: Get attributes from a VLAN of the
-         *  current softif
-         */
-        BATADV_CMD_GET_VLAN,
-
-        /**
-         * @BATADV_CMD_SET_VLAN: Set attributes for VLAN of the
-         *  current softif
-         */
-        BATADV_CMD_SET_VLAN,
-
-        /* add new commands above here */
-
-        /**
-         * @__BATADV_CMD_AFTER_LAST: internal use
-         */
-        __BATADV_CMD_AFTER_LAST,
-
-        /**
-         * @BATADV_CMD_MAX: highest used command number
-         */
-        BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1
+       /**
+        * @BATADV_CMD_UNSPEC: unspecified command to catch errors
+        */
+       BATADV_CMD_UNSPEC,
+
+       /**
+        * @BATADV_CMD_GET_MESH: Get attributes from softif/mesh
+        */
+       BATADV_CMD_GET_MESH,
+
+       /**
+        * @BATADV_CMD_GET_MESH_INFO: Alias for @BATADV_CMD_GET_MESH
+        */
+       BATADV_CMD_GET_MESH_INFO = BATADV_CMD_GET_MESH,
+
+       /**
+        * @BATADV_CMD_TP_METER: Start a tp meter session
+        */
+       BATADV_CMD_TP_METER,
+
+       /**
+        * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session
+        */
+       BATADV_CMD_TP_METER_CANCEL,
+
+       /**
+        * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms.
+        */
+       BATADV_CMD_GET_ROUTING_ALGOS,
+
+       /**
+        * @BATADV_CMD_GET_HARDIF: Get attributes from a hardif of the
+        *  current softif
+        */
+       BATADV_CMD_GET_HARDIF,
+
+       /**
+        * @BATADV_CMD_GET_HARDIFS: Alias for @BATADV_CMD_GET_HARDIF
+        */
+       BATADV_CMD_GET_HARDIFS = BATADV_CMD_GET_HARDIF,
+
+       /**
+        * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
+        */
+       BATADV_CMD_GET_TRANSTABLE_LOCAL,
+
+       /**
+        * @BATADV_CMD_GET_TRANSTABLE_GLOBAL: Query list of global translations
+        */
+       BATADV_CMD_GET_TRANSTABLE_GLOBAL,
+
+       /**
+        * @BATADV_CMD_GET_ORIGINATORS: Query list of originators
+        */
+       BATADV_CMD_GET_ORIGINATORS,
+
+       /**
+        * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours
+        */
+       BATADV_CMD_GET_NEIGHBORS,
+
+       /**
+        * @BATADV_CMD_GET_GATEWAYS: Query list of gateways
+        */
+       BATADV_CMD_GET_GATEWAYS,
+
+       /**
+        * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims
+        */
+       BATADV_CMD_GET_BLA_CLAIM,
+
+       /**
+        * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance
+        * backbones
+        */
+       BATADV_CMD_GET_BLA_BACKBONE,
+
+       /**
+        * @BATADV_CMD_GET_DAT_CACHE: Query list of DAT cache entries
+        */
+       BATADV_CMD_GET_DAT_CACHE,
+
+       /**
+        * @BATADV_CMD_GET_MCAST_FLAGS: Query list of multicast flags
+        */
+       BATADV_CMD_GET_MCAST_FLAGS,
+
+       /**
+        * @BATADV_CMD_SET_MESH: Set attributes for softif/mesh
+        */
+       BATADV_CMD_SET_MESH,
+
+       /**
+        * @BATADV_CMD_SET_HARDIF: Set attributes for hardif of the
+        *  current softif
+        */
+       BATADV_CMD_SET_HARDIF,
+
+       /**
+        * @BATADV_CMD_GET_VLAN: Get attributes from a VLAN of the
+        *  current softif
+        */
+       BATADV_CMD_GET_VLAN,
+
+       /**
+        * @BATADV_CMD_SET_VLAN: Set attributes for VLAN of the
+        *  current softif
+        */
+       BATADV_CMD_SET_VLAN,
+
+       /* add new commands above here */
+
+       /**
+        * @__BATADV_CMD_AFTER_LAST: internal use
+        */
+       __BATADV_CMD_AFTER_LAST,
+
+       /**
+        * @BATADV_CMD_MAX: highest used command number
+        */
+       BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1
 };
 
 /**
  * enum batadv_tp_meter_reason - reason of a tp meter test run stop
  */
 enum batadv_tp_meter_reason {
-        /**
-         * @BATADV_TP_REASON_COMPLETE: sender finished tp run
-         */
-        BATADV_TP_REASON_COMPLETE              = 3,
-
-        /**
-         * @BATADV_TP_REASON_CANCEL: sender was stopped during run
-         */
-        BATADV_TP_REASON_CANCEL                        = 4,
-
-        /* error status >= 128 */
-
-        /**
-         * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or
-         * didn't answer
-         */
-        BATADV_TP_REASON_DST_UNREACHABLE       = 128,
-
-        /**
-         * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit
-         */
-        BATADV_TP_REASON_RESEND_LIMIT          = 129,
-
-        /**
-         * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node
-         * already ongoing
-         */
-        BATADV_TP_REASON_ALREADY_ONGOING       = 130,
-
-        /**
-         * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory
-         */
-        BATADV_TP_REASON_MEMORY_ERROR          = 131,
-
-        /**
-         * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface
-         */
-        BATADV_TP_REASON_CANT_SEND             = 132,
-
-        /**
-         * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions
-         */
-        BATADV_TP_REASON_TOO_MANY              = 133,
+       /**
+        * @BATADV_TP_REASON_COMPLETE: sender finished tp run
+        */
+       BATADV_TP_REASON_COMPLETE               = 3,
+
+       /**
+        * @BATADV_TP_REASON_CANCEL: sender was stopped during run
+        */
+       BATADV_TP_REASON_CANCEL                 = 4,
+
+       /* error status >= 128 */
+
+       /**
+        * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or
+        * didn't answer
+        */
+       BATADV_TP_REASON_DST_UNREACHABLE        = 128,
+
+       /**
+        * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit
+        */
+       BATADV_TP_REASON_RESEND_LIMIT           = 129,
+
+       /**
+        * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node
+        * already ongoing
+        */
+       BATADV_TP_REASON_ALREADY_ONGOING        = 130,
+
+       /**
+        * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory
+        */
+       BATADV_TP_REASON_MEMORY_ERROR           = 131,
+
+       /**
+        * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface
+        */
+       BATADV_TP_REASON_CANT_SEND              = 132,
+
+       /**
+        * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions
+        */
+       BATADV_TP_REASON_TOO_MANY               = 133,
 };
 
 /**
  * enum batadv_ifla_attrs - batman-adv ifla nested attributes
  */
 enum batadv_ifla_attrs {
-        /**
-         * @IFLA_BATADV_UNSPEC: unspecified attribute which is not parsed by
-         *  rtnetlink
-         */
-        IFLA_BATADV_UNSPEC,
-
-        /**
-         * @IFLA_BATADV_ALGO_NAME: routing algorithm (name) which should be
-         *  used by the newly registered batadv net_device.
-         */
-        IFLA_BATADV_ALGO_NAME,
-
-        /* add attributes above here, update the policy in soft-interface.c */
-
-        /**
-         * @__IFLA_BATADV_MAX: internal use
-         */
-        __IFLA_BATADV_MAX,
+       /**
+        * @IFLA_BATADV_UNSPEC: unspecified attribute which is not parsed by
+        *  rtnetlink
+        */
+       IFLA_BATADV_UNSPEC,
+
+       /**
+        * @IFLA_BATADV_ALGO_NAME: routing algorithm (name) which should be
+        *  used by the newly registered batadv net_device.
+        */
+       IFLA_BATADV_ALGO_NAME,
+
+       /* add attributes above here, update the policy in soft-interface.c */
+
+       /**
+        * @__IFLA_BATADV_MAX: internal use
+        */
+       __IFLA_BATADV_MAX,
 };
 
 #define IFLA_BATADV_MAX (__IFLA_BATADV_MAX - 1)
index d22f1978ce292b1ea9caf6f13a44658115e3dd58..0f8306fdea645c49805990fcd757d4053957fd91 100644 (file)
@@ -154,7 +154,7 @@ struct btrfs_scrub_progress {
        __u64 tree_bytes_scrubbed;      /* # of tree bytes scrubbed */
        __u64 read_errors;              /* # of read errors encountered (EIO) */
        __u64 csum_errors;              /* # of failed csum checks */
-       __u64 verify_errors;            /* # of occurences, where the metadata
+       __u64 verify_errors;            /* # of occurrences, where the metadata
                                         * of a tree block did not match the
                                         * expected values, like generation or
                                         * logical */
@@ -174,7 +174,7 @@ struct btrfs_scrub_progress {
        __u64 last_physical;            /* last physical address scrubbed. In
                                         * case a scrub was aborted, this can
                                         * be used to restart the scrub */
-       __u64 unverified_errors;        /* # of occurences where a read for a
+       __u64 unverified_errors;        /* # of occurrences where a read for a
                                         * full (64k) bio failed, but the re-
                                         * check succeeded for each 4k piece.
                                         * Intermittent error. */
@@ -307,6 +307,7 @@ struct btrfs_ioctl_fs_info_args {
 #define BTRFS_FEATURE_INCOMPAT_NO_HOLES                (1ULL << 9)
 #define BTRFS_FEATURE_INCOMPAT_METADATA_UUID   (1ULL << 10)
 #define BTRFS_FEATURE_INCOMPAT_RAID1C34                (1ULL << 11)
+#define BTRFS_FEATURE_INCOMPAT_ZONED           (1ULL << 12)
 
 struct btrfs_ioctl_feature_flags {
        __u64 compat_flags;
index 6b885982ece68775a97941b5b495d5b551609d2e..ccdb40fe40dc254e120e86007b586cee548c2735 100644 (file)
@@ -59,7 +59,7 @@
 /* for storing balance parameters in the root tree */
 #define BTRFS_BALANCE_OBJECTID -4ULL
 
-/* orhpan objectid for tracking unlinked/truncated files */
+/* orphan objectid for tracking unlinked/truncated files */
 #define BTRFS_ORPHAN_OBJECTID -5ULL
 
 /* does write ahead logging to speed up fsyncs */
 #define BTRFS_PERSISTENT_ITEM_KEY      249
 
 /*
- * Persistantly stores the device replace state in the device tree.
+ * Persistently stores the device replace state in the device tree.
  * The key is built like this: (0, BTRFS_DEV_REPLACE_KEY, 0).
  */
 #define BTRFS_DEV_REPLACE_KEY  250
  */
 #define BTRFS_STRING_ITEM_KEY  253
 
-
+/* Maximum metadata block size (nodesize) */
+#define BTRFS_MAX_METADATA_BLOCKSIZE                   65536
 
 /* 32 bytes in various csum fields */
 #define BTRFS_CSUM_SIZE 32
index 6f598b73839e0851437811abf52ce73678797b70..f730d443b91846a3692b0fe7e68eb31b00ef75e5 100644 (file)
@@ -100,6 +100,7 @@ struct can_ctrlmode {
 #define CAN_CTRLMODE_FD                        0x20    /* CAN FD mode */
 #define CAN_CTRLMODE_PRESUME_ACK       0x40    /* Ignore missing CAN ACKs */
 #define CAN_CTRLMODE_FD_NON_ISO                0x80    /* CAN FD in non-ISO mode */
+#define CAN_CTRLMODE_CC_LEN8_DLC       0x100   /* Classic CAN DLC option */
 
 /*
  * CAN device statistics
index 45f3750aa861b5bfbaedf9c0b643b3d6ac5888ae..d174914a837dbfaa55409dfe682eec3148bb53a0 100644 (file)
@@ -94,6 +94,7 @@
 #define BOND_XMIT_POLICY_LAYER23       2 /* layer 2+3 (IP ^ MAC) */
 #define BOND_XMIT_POLICY_ENCAP23       3 /* encapsulated layer 2+3 */
 #define BOND_XMIT_POLICY_ENCAP34       4 /* encapsulated layer 3+4 */
+#define BOND_XMIT_POLICY_VLAN_SRCMAC   5 /* vlan + source MAC */
 
 /* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */
 #define LACP_STATE_LACP_ACTIVITY   0x1
@@ -152,14 +153,3 @@ enum {
 #define BOND_3AD_STAT_MAX (__BOND_3AD_STAT_MAX - 1)
 
 #endif /* _LINUX_IF_BONDING_H */
-
-/*
- * Local variables:
- *  version-control: t
- *  kept-new-versions: 5
- *  c-indent-level: 8
- *  c-basic-offset: 8
- *  tab-width: 8
- * End:
- */
-
index 4c687686aa8f73f8429346208a7223cf6febed54..6b56a7549531c42da3e9884958ac664cdcf75638 100644 (file)
@@ -121,6 +121,7 @@ enum {
        IFLA_BRIDGE_VLAN_INFO,
        IFLA_BRIDGE_VLAN_TUNNEL_INFO,
        IFLA_BRIDGE_MRP,
+       IFLA_BRIDGE_CFM,
        __IFLA_BRIDGE_MAX,
 };
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
@@ -328,6 +329,130 @@ struct br_mrp_start_in_test {
        __u16 in_id;
 };
 
+enum {
+       IFLA_BRIDGE_CFM_UNSPEC,
+       IFLA_BRIDGE_CFM_MEP_CREATE,
+       IFLA_BRIDGE_CFM_MEP_DELETE,
+       IFLA_BRIDGE_CFM_MEP_CONFIG,
+       IFLA_BRIDGE_CFM_CC_CONFIG,
+       IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD,
+       IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE,
+       IFLA_BRIDGE_CFM_CC_RDI,
+       IFLA_BRIDGE_CFM_CC_CCM_TX,
+       IFLA_BRIDGE_CFM_MEP_CREATE_INFO,
+       IFLA_BRIDGE_CFM_MEP_CONFIG_INFO,
+       IFLA_BRIDGE_CFM_CC_CONFIG_INFO,
+       IFLA_BRIDGE_CFM_CC_RDI_INFO,
+       IFLA_BRIDGE_CFM_CC_CCM_TX_INFO,
+       IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO,
+       IFLA_BRIDGE_CFM_MEP_STATUS_INFO,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO,
+       __IFLA_BRIDGE_CFM_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MAX (__IFLA_BRIDGE_CFM_MAX - 1)
+
+enum {
+       IFLA_BRIDGE_CFM_MEP_CREATE_UNSPEC,
+       IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE,
+       IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN,
+       IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION,
+       IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX,
+       __IFLA_BRIDGE_CFM_MEP_CREATE_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_CREATE_MAX (__IFLA_BRIDGE_CFM_MEP_CREATE_MAX - 1)
+
+enum {
+       IFLA_BRIDGE_CFM_MEP_DELETE_UNSPEC,
+       IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE,
+       __IFLA_BRIDGE_CFM_MEP_DELETE_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_DELETE_MAX (__IFLA_BRIDGE_CFM_MEP_DELETE_MAX - 1)
+
+enum {
+       IFLA_BRIDGE_CFM_MEP_CONFIG_UNSPEC,
+       IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE,
+       IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC,
+       IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL,
+       IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID,
+       __IFLA_BRIDGE_CFM_MEP_CONFIG_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_CONFIG_MAX (__IFLA_BRIDGE_CFM_MEP_CONFIG_MAX - 1)
+
+enum {
+       IFLA_BRIDGE_CFM_CC_CONFIG_UNSPEC,
+       IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE,
+       IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE,
+       IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL,
+       IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID,
+       __IFLA_BRIDGE_CFM_CC_CONFIG_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_CONFIG_MAX (__IFLA_BRIDGE_CFM_CC_CONFIG_MAX - 1)
+
+enum {
+       IFLA_BRIDGE_CFM_CC_PEER_MEP_UNSPEC,
+       IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE,
+       IFLA_BRIDGE_CFM_CC_PEER_MEPID,
+       __IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX (__IFLA_BRIDGE_CFM_CC_PEER_MEP_MAX - 1)
+
+enum {
+       IFLA_BRIDGE_CFM_CC_RDI_UNSPEC,
+       IFLA_BRIDGE_CFM_CC_RDI_INSTANCE,
+       IFLA_BRIDGE_CFM_CC_RDI_RDI,
+       __IFLA_BRIDGE_CFM_CC_RDI_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_RDI_MAX (__IFLA_BRIDGE_CFM_CC_RDI_MAX - 1)
+
+enum {
+       IFLA_BRIDGE_CFM_CC_CCM_TX_UNSPEC,
+       IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE,
+       IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC,
+       IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE,
+       IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD,
+       IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV,
+       IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE,
+       IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV,
+       IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE,
+       __IFLA_BRIDGE_CFM_CC_CCM_TX_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_CCM_TX_MAX (__IFLA_BRIDGE_CFM_CC_CCM_TX_MAX - 1)
+
+enum {
+       IFLA_BRIDGE_CFM_MEP_STATUS_UNSPEC,
+       IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE,
+       IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN,
+       IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN,
+       IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN,
+       __IFLA_BRIDGE_CFM_MEP_STATUS_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_MEP_STATUS_MAX (__IFLA_BRIDGE_CFM_MEP_STATUS_MAX - 1)
+
+enum {
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_UNSPEC,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN,
+       IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN,
+       __IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX,
+};
+
+#define IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX (__IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX - 1)
+
 struct bridge_stp_xstats {
        __u64 transition_blk;
        __u64 transition_fwd;
@@ -502,6 +627,8 @@ enum {
        MDBA_ROUTER_PATTR_UNSPEC,
        MDBA_ROUTER_PATTR_TIMER,
        MDBA_ROUTER_PATTR_TYPE,
+       MDBA_ROUTER_PATTR_INET_TIMER,
+       MDBA_ROUTER_PATTR_INET6_TIMER,
        __MDBA_ROUTER_PATTR_MAX
 };
 #define MDBA_ROUTER_PATTR_MAX (__MDBA_ROUTER_PATTR_MAX - 1)
@@ -526,6 +653,7 @@ struct br_mdb_entry {
                union {
                        __be32  ip4;
                        struct in6_addr ip6;
+                       unsigned char mac_addr[ETH_ALEN];
                } u;
                __be16          proto;
        } addr;
index d6de2b167448fe592b73da2dda68e99f355078e5..a0b637911d3cf97fa9c0785d20b31ab00bffc372 100644 (file)
@@ -99,6 +99,7 @@
 #define ETH_P_1588     0x88F7          /* IEEE 1588 Timesync */
 #define ETH_P_NCSI     0x88F8          /* NCSI protocol                */
 #define ETH_P_PRP      0x88FB          /* IEC 62439-3 PRP/HSRv0        */
+#define ETH_P_CFM      0x8902          /* Connectivity Fault Management */
 #define ETH_P_FCOE     0x8906          /* Fibre Channel over Ethernet  */
 #define ETH_P_IBOE     0x8915          /* Infiniband over Ethernet     */
 #define ETH_P_TDLS     0x890D          /* TDLS */
index 82708c6db432251e7a680c3dc657cc048c6f58e9..4882e81514b664f5cb95f6a51d94a862efcd4a5c 100644 (file)
@@ -341,6 +341,13 @@ enum {
        IFLA_ALT_IFNAME, /* Alternative ifname */
        IFLA_PERM_ADDRESS,
        IFLA_PROTO_DOWN_REASON,
+
+       /* device (sysfs) name as parent, used instead
+        * of IFLA_LINK where there's no parent netdev
+        */
+       IFLA_PARENT_DEV_NAME,
+       IFLA_PARENT_DEV_BUS_NAME,
+
        __IFLA_MAX
 };
 
@@ -525,6 +532,8 @@ enum {
        IFLA_BRPORT_BACKUP_PORT,
        IFLA_BRPORT_MRP_RING_OPEN,
        IFLA_BRPORT_MRP_IN_OPEN,
+       IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
+       IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
        __IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
@@ -612,6 +621,7 @@ enum macvlan_macaddr_mode {
 };
 
 #define MACVLAN_FLAG_NOPROMISC 1
+#define MACVLAN_FLAG_NODST     2 /* skip dst macvlan if matching src macvlan */
 
 /* VRF section */
 enum {
@@ -1233,6 +1243,8 @@ enum {
 #define RMNET_FLAGS_INGRESS_MAP_COMMANDS          (1U << 1)
 #define RMNET_FLAGS_INGRESS_MAP_CKSUMV4           (1U << 2)
 #define RMNET_FLAGS_EGRESS_MAP_CKSUMV4            (1U << 3)
+#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5           (1U << 4)
+#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5            (1U << 5)
 
 enum {
        IFLA_RMNET_UNSPEC,
index 7d6687618d80802ce10da1aa4e326b55bd9af014..d1b327036ae43686d66019c18533ed1091ccc65b 100644 (file)
@@ -289,6 +289,9 @@ struct sockaddr_in {
 /* Address indicating an error return. */
 #define        INADDR_NONE             ((unsigned long int) 0xffffffff)
 
+/* Dummy address for src of ICMP replies if no real address is set (RFC7600). */
+#define        INADDR_DUMMY            ((unsigned long int) 0xc0000008)
+
 /* Network number for local host loopback. */
 #define        IN_LOOPBACKNET          127
 
index 30c80d5ba4bfcba26a2bdd14045cfe0b936fdbfb..bab8c9708611139357eb888c9ad367935f380844 100644 (file)
@@ -145,6 +145,7 @@ enum {
        L2TP_ATTR_RX_ERRORS,            /* u64 */
        L2TP_ATTR_STATS_PAD,
        L2TP_ATTR_RX_COOKIE_DISCARDS,   /* u64 */
+       L2TP_ATTR_RX_INVALID,           /* u64 */
        __L2TP_ATTR_STATS_MAX,
 };
 
index 4565456c0ef447e44e59791b23be2e702b828054..e94d1fa554cb224da089742468739b7f36f38395 100644 (file)
@@ -133,7 +133,7 @@ enum nf_tables_msg_types {
  * @NFTA_LIST_ELEM: list element (NLA_NESTED)
  */
 enum nft_list_attributes {
-       NFTA_LIST_UNPEC,
+       NFTA_LIST_UNSPEC,
        NFTA_LIST_ELEM,
        __NFTA_LIST_MAX
 };
@@ -164,7 +164,10 @@ enum nft_hook_attributes {
  */
 enum nft_table_flags {
        NFT_TABLE_F_DORMANT     = 0x1,
+       NFT_TABLE_F_OWNER       = 0x2,
 };
+#define NFT_TABLE_F_MASK       (NFT_TABLE_F_DORMANT | \
+                                NFT_TABLE_F_OWNER)
 
 /**
  * enum nft_table_attributes - nf_tables table netlink attributes
@@ -172,6 +175,8 @@ enum nft_table_flags {
  * @NFTA_TABLE_NAME: name of the table (NLA_STRING)
  * @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32)
  * @NFTA_TABLE_USE: number of chains in this table (NLA_U32)
+ * @NFTA_TABLE_USERDATA: user data (NLA_BINARY)
+ * @NFTA_TABLE_OWNER: owner of this table through netlink portID (NLA_U32)
  */
 enum nft_table_attributes {
        NFTA_TABLE_UNSPEC,
@@ -180,10 +185,21 @@ enum nft_table_attributes {
        NFTA_TABLE_USE,
        NFTA_TABLE_HANDLE,
        NFTA_TABLE_PAD,
+       NFTA_TABLE_USERDATA,
+       NFTA_TABLE_OWNER,
        __NFTA_TABLE_MAX
 };
 #define NFTA_TABLE_MAX         (__NFTA_TABLE_MAX - 1)
 
+enum nft_chain_flags {
+       NFT_CHAIN_BASE          = (1 << 0),
+       NFT_CHAIN_HW_OFFLOAD    = (1 << 1),
+       NFT_CHAIN_BINDING       = (1 << 2),
+};
+#define NFT_CHAIN_FLAGS                (NFT_CHAIN_BASE         | \
+                                NFT_CHAIN_HW_OFFLOAD   | \
+                                NFT_CHAIN_BINDING)
+
 /**
  * enum nft_chain_attributes - nf_tables chain netlink attributes
  *
@@ -196,6 +212,8 @@ enum nft_table_attributes {
  * @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING)
  * @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes)
  * @NFTA_CHAIN_FLAGS: chain flags
+ * @NFTA_CHAIN_ID: uniquely identifies a chain in a transaction (NLA_U32)
+ * @NFTA_CHAIN_USERDATA: user data (NLA_BINARY)
  */
 enum nft_chain_attributes {
        NFTA_CHAIN_UNSPEC,
@@ -209,6 +227,8 @@ enum nft_chain_attributes {
        NFTA_CHAIN_COUNTERS,
        NFTA_CHAIN_PAD,
        NFTA_CHAIN_FLAGS,
+       NFTA_CHAIN_ID,
+       NFTA_CHAIN_USERDATA,
        __NFTA_CHAIN_MAX
 };
 #define NFTA_CHAIN_MAX         (__NFTA_CHAIN_MAX - 1)
@@ -238,6 +258,7 @@ enum nft_rule_attributes {
        NFTA_RULE_PAD,
        NFTA_RULE_ID,
        NFTA_RULE_POSITION_ID,
+       NFTA_RULE_CHAIN_ID,
        __NFTA_RULE_MAX
 };
 #define NFTA_RULE_MAX          (__NFTA_RULE_MAX - 1)
@@ -277,6 +298,7 @@ enum nft_rule_compat_attributes {
  * @NFT_SET_EVAL: set can be updated from the evaluation path
  * @NFT_SET_OBJECT: set contains stateful objects
  * @NFT_SET_CONCAT: set contains a concatenation
+ * @NFT_SET_EXPR: set contains expressions
  */
 enum nft_set_flags {
        NFT_SET_ANONYMOUS               = 0x1,
@@ -287,6 +309,7 @@ enum nft_set_flags {
        NFT_SET_EVAL                    = 0x20,
        NFT_SET_OBJECT                  = 0x40,
        NFT_SET_CONCAT                  = 0x80,
+       NFT_SET_EXPR                    = 0x100,
 };
 
 /**
@@ -345,6 +368,7 @@ enum nft_set_field_attributes {
  * @NFTA_SET_OBJ_TYPE: stateful object type (NLA_U32: NFT_OBJECT_*)
  * @NFTA_SET_HANDLE: set handle (NLA_U64)
  * @NFTA_SET_EXPR: set expression (NLA_NESTED: nft_expr_attributes)
+ * @NFTA_SET_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes)
  */
 enum nft_set_attributes {
        NFTA_SET_UNSPEC,
@@ -365,6 +389,7 @@ enum nft_set_attributes {
        NFTA_SET_OBJ_TYPE,
        NFTA_SET_HANDLE,
        NFTA_SET_EXPR,
+       NFTA_SET_EXPRESSIONS,
        __NFTA_SET_MAX
 };
 #define NFTA_SET_MAX           (__NFTA_SET_MAX - 1)
@@ -373,9 +398,11 @@ enum nft_set_attributes {
  * enum nft_set_elem_flags - nf_tables set element flags
  *
  * @NFT_SET_ELEM_INTERVAL_END: element ends the previous interval
+ * @NFT_SET_ELEM_CATCHALL: special catch-all element
  */
 enum nft_set_elem_flags {
        NFT_SET_ELEM_INTERVAL_END       = 0x1,
+       NFT_SET_ELEM_CATCHALL           = 0x2,
 };
 
 /**
@@ -390,6 +417,7 @@ enum nft_set_elem_flags {
  * @NFTA_SET_ELEM_EXPR: expression (NLA_NESTED: nft_expr_attributes)
  * @NFTA_SET_ELEM_OBJREF: stateful object reference (NLA_STRING)
  * @NFTA_SET_ELEM_KEY_END: closing key value (NLA_NESTED: nft_data)
+ * @NFTA_SET_ELEM_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes)
  */
 enum nft_set_elem_attributes {
        NFTA_SET_ELEM_UNSPEC,
@@ -403,6 +431,7 @@ enum nft_set_elem_attributes {
        NFTA_SET_ELEM_PAD,
        NFTA_SET_ELEM_OBJREF,
        NFTA_SET_ELEM_KEY_END,
+       NFTA_SET_ELEM_EXPRESSIONS,
        __NFTA_SET_ELEM_MAX
 };
 #define NFTA_SET_ELEM_MAX      (__NFTA_SET_ELEM_MAX - 1)
@@ -468,11 +497,13 @@ enum nft_data_attributes {
  *
  * @NFTA_VERDICT_CODE: nf_tables verdict (NLA_U32: enum nft_verdicts)
  * @NFTA_VERDICT_CHAIN: jump target chain name (NLA_STRING)
+ * @NFTA_VERDICT_CHAIN_ID: jump target chain ID (NLA_U32)
  */
 enum nft_verdict_attributes {
        NFTA_VERDICT_UNSPEC,
        NFTA_VERDICT_CODE,
        NFTA_VERDICT_CHAIN,
+       NFTA_VERDICT_CHAIN_ID,
        __NFTA_VERDICT_MAX
 };
 #define NFTA_VERDICT_MAX       (__NFTA_VERDICT_MAX - 1)
@@ -684,6 +715,7 @@ enum nft_dynset_ops {
 
 enum nft_dynset_flags {
        NFT_DYNSET_F_INV        = (1 << 0),
+       NFT_DYNSET_F_EXPR       = (1 << 1),
 };
 
 /**
@@ -697,6 +729,7 @@ enum nft_dynset_flags {
  * @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64)
  * @NFTA_DYNSET_EXPR: expression (NLA_NESTED: nft_expr_attributes)
  * @NFTA_DYNSET_FLAGS: flags (NLA_U32)
+ * @NFTA_DYNSET_EXPRESSIONS: list of expressions (NLA_NESTED: nft_list_attributes)
  */
 enum nft_dynset_attributes {
        NFTA_DYNSET_UNSPEC,
@@ -709,6 +742,7 @@ enum nft_dynset_attributes {
        NFTA_DYNSET_EXPR,
        NFTA_DYNSET_PAD,
        NFTA_DYNSET_FLAGS,
+       NFTA_DYNSET_EXPRESSIONS,
        __NFTA_DYNSET_MAX,
 };
 #define NFTA_DYNSET_MAX                (__NFTA_DYNSET_MAX - 1)
@@ -731,10 +765,12 @@ enum nft_payload_bases {
  *
  * @NFT_PAYLOAD_CSUM_NONE: no checksumming
  * @NFT_PAYLOAD_CSUM_INET: internet checksum (RFC 791)
+ * @NFT_PAYLOAD_CSUM_SCTP: CRC-32c, for use in SCTP header (RFC 3309)
  */
 enum nft_payload_csum_types {
        NFT_PAYLOAD_CSUM_NONE,
        NFT_PAYLOAD_CSUM_INET,
+       NFT_PAYLOAD_CSUM_SCTP,
 };
 
 enum nft_payload_csum_flags {
@@ -777,11 +813,13 @@ enum nft_exthdr_flags {
  * @NFT_EXTHDR_OP_IPV6: match against ipv6 extension headers
  * @NFT_EXTHDR_OP_TCP: match against tcp options
  * @NFT_EXTHDR_OP_IPV4: match against ipv4 options
+ * @NFT_EXTHDR_OP_SCTP: match against sctp chunks
  */
 enum nft_exthdr_op {
        NFT_EXTHDR_OP_IPV6,
        NFT_EXTHDR_OP_TCPOPT,
        NFT_EXTHDR_OP_IPV4,
+       NFT_EXTHDR_OP_SCTP,
        __NFT_EXTHDR_OP_MAX
 };
 #define NFT_EXTHDR_OP_MAX      (__NFT_EXTHDR_OP_MAX - 1)
@@ -980,11 +1018,13 @@ enum nft_rt_attributes {
  *
  * @NFTA_SOCKET_KEY: socket key to match
  * @NFTA_SOCKET_DREG: destination register
+ * @NFTA_SOCKET_LEVEL: cgroups2 ancestor level (only for cgroupsv2)
  */
 enum nft_socket_attributes {
        NFTA_SOCKET_UNSPEC,
        NFTA_SOCKET_KEY,
        NFTA_SOCKET_DREG,
+       NFTA_SOCKET_LEVEL,
        __NFTA_SOCKET_MAX
 };
 #define NFTA_SOCKET_MAX                (__NFTA_SOCKET_MAX - 1)
@@ -994,10 +1034,14 @@ enum nft_socket_attributes {
  *
  * @NFT_SOCKET_TRANSPARENT: Value of the IP(V6)_TRANSPARENT socket option
  * @NFT_SOCKET_MARK: Value of the socket mark
+ * @NFT_SOCKET_WILDCARD: Whether the socket is zero-bound (e.g. 0.0.0.0 or ::0)
+ * @NFT_SOCKET_CGROUPV2: Match on cgroups version 2
  */
 enum nft_socket_keys {
        NFT_SOCKET_TRANSPARENT,
        NFT_SOCKET_MARK,
+       NFT_SOCKET_WILDCARD,
+       NFT_SOCKET_CGROUPV2,
        __NFT_SOCKET_MAX
 };
 #define NFT_SOCKET_MAX (__NFT_SOCKET_MAX - 1)
@@ -1151,6 +1195,21 @@ enum nft_counter_attributes {
 };
 #define NFTA_COUNTER_MAX       (__NFTA_COUNTER_MAX - 1)
 
+/**
+ * enum nft_last_attributes - nf_tables last expression netlink attributes
+ *
+ * @NFTA_LAST_SET: last update has been set, zero means never updated (NLA_U32)
+ * @NFTA_LAST_MSECS: milliseconds since last update (NLA_U64)
+ */
+enum nft_last_attributes {
+       NFTA_LAST_UNSPEC,
+       NFTA_LAST_SET,
+       NFTA_LAST_MSECS,
+       NFTA_LAST_PAD,
+       __NFTA_LAST_MAX
+};
+#define NFTA_LAST_MAX  (__NFTA_LAST_MAX - 1)
+
 /**
  * enum nft_log_attributes - nf_tables log expression netlink attributes
  *
@@ -1541,6 +1600,7 @@ enum nft_ct_expectation_attributes {
  * @NFTA_OBJ_DATA: stateful object data (NLA_NESTED)
  * @NFTA_OBJ_USE: number of references to this expression (NLA_U32)
  * @NFTA_OBJ_HANDLE: object handle (NLA_U64)
+ * @NFTA_OBJ_USERDATA: user data (NLA_BINARY)
  */
 enum nft_object_attributes {
        NFTA_OBJ_UNSPEC,
@@ -1551,6 +1611,7 @@ enum nft_object_attributes {
        NFTA_OBJ_USE,
        NFTA_OBJ_HANDLE,
        NFTA_OBJ_PAD,
+       NFTA_OBJ_USERDATA,
        __NFTA_OBJ_MAX
 };
 #define NFTA_OBJ_MAX           (__NFTA_OBJ_MAX - 1)
index a89f3a56a33e5f256c953555820334ecb6387f05..6cd58cd2a6f00fe485563a2350528d916295852e 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _NFNETLINK_H
-#define _NFNETLINK_H
+#ifndef _UAPI_NFNETLINK_H
+#define _UAPI_NFNETLINK_H
 #include <linux/types.h>
 #include <linux/netfilter/nfnetlink_compat.h>
 
@@ -60,7 +60,8 @@ struct nfgenmsg {
 #define NFNL_SUBSYS_CTHELPER           9
 #define NFNL_SUBSYS_NFTABLES           10
 #define NFNL_SUBSYS_NFT_COMPAT         11
-#define NFNL_SUBSYS_COUNT              12
+#define NFNL_SUBSYS_HOOK               12
+#define NFNL_SUBSYS_COUNT              13
 
 /* Reserved control nfnetlink messages */
 #define NFNL_MSG_BATCH_BEGIN           NLMSG_MIN_TYPE
@@ -78,4 +79,4 @@ enum nfnl_batch_attributes {
 };
 #define NFNL_BATCH_MAX                 (__NFNL_BATCH_MAX - 1)
 
-#endif /* _NFNETLINK_H */
+#endif /* _UAPI_NFNETLINK_H */
index c3816ff7bfc32f4bd68315f92cb6c6a01f8cc6fc..4c0cde075c2779ee79acb811bd968e99326a42b5 100644 (file)
@@ -2,7 +2,7 @@
 #ifndef _UAPI__LINUX_NETLINK_H
 #define _UAPI__LINUX_NETLINK_H
 
-#include <linux/kernel.h>
+#include <linux/const.h>
 #include <linux/socket.h> /* for __kernel_sa_family_t */
 #include <linux/types.h>
 
@@ -91,9 +91,10 @@ struct nlmsghdr {
 #define NLMSG_HDRLEN    ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
 #define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
 #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
-#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_DATA(nlh)  ((void *)(((char *)nlh) + NLMSG_HDRLEN))
 #define NLMSG_NEXT(nlh,len)     ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
-                                 (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+                                 (struct nlmsghdr *)(((char *)(nlh)) + \
+                                 NLMSG_ALIGN((nlh)->nlmsg_len)))
 #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
                           (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
                           (nlh)->nlmsg_len <= (len))
index 2d4a1e784cf04a30fed6d4d28628212c80b1dde9..d8ffa8c9ca78492ec1f18c2634f20eb8f79b6ee7 100644 (file)
@@ -21,7 +21,10 @@ struct nexthop_grp {
 };
 
 enum {
-       NEXTHOP_GRP_TYPE_MPATH,  /* default type if not specified */
+       NEXTHOP_GRP_TYPE_MPATH,  /* hash-threshold nexthop group
+                                 * default type if not specified
+                                 */
+       NEXTHOP_GRP_TYPE_RES,    /* resilient nexthop group */
        __NEXTHOP_GRP_TYPE_MAX,
 };
 
@@ -52,8 +55,50 @@ enum {
        NHA_FDB,        /* flag; nexthop belongs to a bridge fdb */
        /* if NHA_FDB is added, OIF, BLACKHOLE, ENCAP cannot be set */
 
+       /* nested; resilient nexthop group attributes */
+       NHA_RES_GROUP,
+       /* nested; nexthop bucket attributes */
+       NHA_RES_BUCKET,
+
        __NHA_MAX,
 };
 
 #define NHA_MAX        (__NHA_MAX - 1)
+
+enum {
+       NHA_RES_GROUP_UNSPEC,
+       /* Pad attribute for 64-bit alignment. */
+       NHA_RES_GROUP_PAD = NHA_RES_GROUP_UNSPEC,
+
+       /* u16; number of nexthop buckets in a resilient nexthop group */
+       NHA_RES_GROUP_BUCKETS,
+       /* clock_t as u32; nexthop bucket idle timer (per-group) */
+       NHA_RES_GROUP_IDLE_TIMER,
+       /* clock_t as u32; nexthop unbalanced timer */
+       NHA_RES_GROUP_UNBALANCED_TIMER,
+       /* clock_t as u64; nexthop unbalanced time */
+       NHA_RES_GROUP_UNBALANCED_TIME,
+
+       __NHA_RES_GROUP_MAX,
+};
+
+#define NHA_RES_GROUP_MAX      (__NHA_RES_GROUP_MAX - 1)
+
+enum {
+       NHA_RES_BUCKET_UNSPEC,
+       /* Pad attribute for 64-bit alignment. */
+       NHA_RES_BUCKET_PAD = NHA_RES_BUCKET_UNSPEC,
+
+       /* u16; nexthop bucket index */
+       NHA_RES_BUCKET_INDEX,
+       /* clock_t as u64; nexthop bucket idle time */
+       NHA_RES_BUCKET_IDLE_TIME,
+       /* u32; nexthop id assigned to the nexthop bucket */
+       NHA_RES_BUCKET_NH_ID,
+
+       __NHA_RES_BUCKET_MAX,
+};
+
+#define NHA_RES_BUCKET_MAX     (__NHA_RES_BUCKET_MAX - 1)
+
 #endif
index 47700a2b9af962f387a7759af4092e2a6a04412a..db474994fa732a8590bf80b6022727e92e3b19a0 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-2020 Intel Corporation
+ * Copyright (C) 2018-2021 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
  *     When a security association was established on an 802.1X network using
  *     fast transition, this event should be followed by an
  *     %NL80211_CMD_PORT_AUTHORIZED event.
+ *     Following a %NL80211_CMD_ROAM event userspace can issue
+ *     %NL80211_CMD_GET_SCAN in order to obtain the scan information for the
+ *     new BSS the card/driver roamed to.
  * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
  *     userspace that a connection was dropped by the AP or due to other
  *     reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
  *     of any other interfaces, and other interfaces will again take
  *     precedence when they are used.
  *
- * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
+ * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface
+ *     (no longer supported).
  *
  * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform
  *     multicast to unicast conversion. When enabled, all multicast packets
  *     includes the contents of the frame. %NL80211_ATTR_ACK flag is included
  *     if the recipient acknowledged the frame.
  *
+ * @NL80211_CMD_SET_SAR_SPECS: SAR power limitation configuration is
+ *     passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to
+ *     specify the wiphy index to be applied to.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1407,6 +1415,8 @@ enum nl80211_commands {
 
        NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS,
 
+       NL80211_CMD_SET_SAR_SPECS,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1750,8 +1760,9 @@ enum nl80211_commands {
  *     specify just a single bitrate, which is to be used for the beacon.
  *     The driver must also specify support for this with the extended
  *     features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
- *     NL80211_EXT_FEATURE_BEACON_RATE_HT and
- *     NL80211_EXT_FEATURE_BEACON_RATE_VHT.
+ *     NL80211_EXT_FEATURE_BEACON_RATE_HT,
+ *     NL80211_EXT_FEATURE_BEACON_RATE_VHT and
+ *     NL80211_EXT_FEATURE_BEACON_RATE_HE.
  *
  * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
  *     at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
@@ -1955,8 +1966,15 @@ enum nl80211_commands {
  * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire
  *     probe-response frame. The DA field in the 802.11 header is zero-ed out,
  *     to be filled by the FW.
- * @NL80211_ATTR_DISABLE_HT:  Force HT capable interfaces to disable
- *      this feature.  Currently, only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable
+ *      this feature during association. This is a flag attribute.
+ *     Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_VHT: Force VHT capable interfaces to disable
+ *      this feature during association. This is a flag attribute.
+ *     Currently only supported in mac80211 drivers.
+ * @NL80211_ATTR_DISABLE_HE: Force HE capable interfaces to disable
+ *      this feature during association. This is a flag attribute.
+ *     Currently only supported in mac80211 drivers.
  * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
  *      ATTR_HT_CAPABILITY to which attention should be paid.
  *      Currently, only mac80211 NICs support this feature.
@@ -2077,7 +2095,8 @@ enum nl80211_commands {
  *     until the channel switch event.
  * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
  *     must be blocked on the current channel (before the channel switch
- *     operation).
+ *     operation). Also included in the channel switch started event if quiet
+ *     was requested by the AP.
  * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
  *     for the time while performing a channel switch.
  * @NL80211_ATTR_CNTDWN_OFFS_BEACON: An array of offsets (u16) to the channel
@@ -2527,6 +2546,20 @@ enum nl80211_commands {
  *     override mask. Used with NL80211_ATTR_S1G_CAPABILITY in
  *     NL80211_CMD_ASSOCIATE or NL80211_CMD_CONNECT.
  *
+ * @NL80211_ATTR_SAE_PWE: Indicates the mechanism(s) allowed for SAE PWE
+ *     derivation in WPA3-Personal networks which are using SAE authentication.
+ *     This is a u8 attribute that encapsulates one of the values from
+ *     &enum nl80211_sae_pwe_mechanism.
+ *
+ * @NL80211_ATTR_SAR_SPEC: SAR power limitation specification when
+ *     used with %NL80211_CMD_SET_SAR_SPECS. The message contains fields
+ *     of %nl80211_sar_attrs which specifies the sar type and related
+ *     sar specs. Sar specs contains array of %nl80211_sar_specs_attrs.
+ *
+ * @NL80211_ATTR_RECONNECT_REQUESTED: flag attribute, used with deauth and
+ *     disassoc events to indicate that an immediate reconnect to the AP
+ *     is desired.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3016,6 +3049,14 @@ enum nl80211_attrs {
        NL80211_ATTR_S1G_CAPABILITY,
        NL80211_ATTR_S1G_CAPABILITY_MASK,
 
+       NL80211_ATTR_SAE_PWE,
+
+       NL80211_ATTR_RECONNECT_REQUESTED,
+
+       NL80211_ATTR_SAR_SPEC,
+
+       NL80211_ATTR_DISABLE_HE,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3613,6 +3654,8 @@ enum nl80211_mpath_info {
  *     defined
  * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
  *     given for all 6 GHz band channels
+ * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are
+ *     advertised on this band/for this iftype (binary)
  * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_band_iftype_attr {
@@ -3624,6 +3667,7 @@ enum nl80211_band_iftype_attr {
        NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET,
        NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE,
        NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
+       NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS,
 
        /* keep last */
        __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
@@ -5896,6 +5940,19 @@ enum nl80211_feature_flags {
  * @NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP: Driver/device supports
  *     unsolicited broadcast probe response transmission
  *
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HE: Driver supports beacon rate
+ *     configuration (AP/mesh) with HE rates.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_LTF: Device supports secure LTF measurement
+ *      exchange protocol.
+ *
+ * @NL80211_EXT_FEATURE_SECURE_RTT: Device supports secure RTT measurement
+ *      exchange protocol.
+ *
+ * @NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE: Device supports management
+ *      frame protection for all management frames exchanged during the
+ *      negotiation and range measurement procedure.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -5956,6 +6013,10 @@ enum nl80211_ext_feature_index {
        NL80211_EXT_FEATURE_SAE_OFFLOAD_AP,
        NL80211_EXT_FEATURE_FILS_DISCOVERY,
        NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP,
+       NL80211_EXT_FEATURE_BEACON_RATE_HE,
+       NL80211_EXT_FEATURE_SECURE_LTF,
+       NL80211_EXT_FEATURE_SECURE_RTT,
+       NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
 
        /* add new features before the definition below */
        NUM_NL80211_EXT_FEATURES,
@@ -6253,11 +6314,13 @@ struct nl80211_vendor_cmd_info {
  * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
  * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
  * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ * @NL80211_TDLS_PEER_HE: TDLS peer is HE capable.
  */
 enum nl80211_tdls_peer_capability {
        NL80211_TDLS_PEER_HT = 1<<0,
        NL80211_TDLS_PEER_VHT = 1<<1,
        NL80211_TDLS_PEER_WMM = 1<<2,
+       NL80211_TDLS_PEER_HE = 1<<3,
 };
 
 /**
@@ -6849,6 +6912,12 @@ enum nl80211_peer_measurement_ftm_capa {
  *      if neither %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED nor
  *     %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set, EDCA based
  *     ranging will be used.
+ * @NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK: negotiate for LMR feedback. Only
+ *     valid if either %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED or
+ *     %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED is set.
+ * @NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR: optional. The BSS color of the
+ *     responder. Only valid if %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED
+ *     or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED is set.
  *
  * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
  * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
@@ -6867,6 +6936,8 @@ enum nl80211_peer_measurement_ftm_req {
        NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC,
        NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED,
        NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED,
+       NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK,
+       NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR,
 
        /* keep last */
        NUM_NL80211_PMSR_FTM_REQ_ATTR,
@@ -7124,4 +7195,115 @@ enum nl80211_unsol_bcast_probe_resp_attributes {
        NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX =
                __NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_LAST - 1
 };
+
+/**
+ * enum nl80211_sae_pwe_mechanism - The mechanism(s) allowed for SAE PWE
+ *     derivation. Applicable only when WPA3-Personal SAE authentication is
+ *     used.
+ *
+ * @NL80211_SAE_PWE_UNSPECIFIED: not specified, used internally to indicate that
+ *     attribute is not present from userspace.
+ * @NL80211_SAE_PWE_HUNT_AND_PECK: hunting-and-pecking loop only
+ * @NL80211_SAE_PWE_HASH_TO_ELEMENT: hash-to-element only
+ * @NL80211_SAE_PWE_BOTH: both hunting-and-pecking loop and hash-to-element
+ *     can be used.
+ */
+enum nl80211_sae_pwe_mechanism {
+       NL80211_SAE_PWE_UNSPECIFIED,
+       NL80211_SAE_PWE_HUNT_AND_PECK,
+       NL80211_SAE_PWE_HASH_TO_ELEMENT,
+       NL80211_SAE_PWE_BOTH,
+};
+
+/**
+ * enum nl80211_sar_type - type of SAR specs
+ *
+ * @NL80211_SAR_TYPE_POWER: power limitation specified in 0.25dBm unit
+ *
+ */
+enum nl80211_sar_type {
+       NL80211_SAR_TYPE_POWER,
+
+       /* add new type here */
+
+       /* Keep last */
+       NUM_NL80211_SAR_TYPE,
+};
+
+/**
+ * enum nl80211_sar_attrs - Attributes for SAR spec
+ *
+ * @NL80211_SAR_ATTR_TYPE: the SAR type as defined in &enum nl80211_sar_type.
+ *
+ * @NL80211_SAR_ATTR_SPECS: Nested array of SAR power
+ *     limit specifications. Each specification contains a set
+ *     of %nl80211_sar_specs_attrs.
+ *
+ *     For SET operation, it contains array of %NL80211_SAR_ATTR_SPECS_POWER
+ *     and %NL80211_SAR_ATTR_SPECS_RANGE_INDEX.
+ *
+ *     For sar_capa dump, it contains array of
+ *     %NL80211_SAR_ATTR_SPECS_START_FREQ
+ *     and %NL80211_SAR_ATTR_SPECS_END_FREQ.
+ *
+ * @__NL80211_SAR_ATTR_LAST: Internal
+ * @NL80211_SAR_ATTR_MAX: highest sar attribute
+ *
+ * These attributes are used with %NL80211_CMD_SET_SAR_SPEC
+ */
+enum nl80211_sar_attrs {
+       __NL80211_SAR_ATTR_INVALID,
+
+       NL80211_SAR_ATTR_TYPE,
+       NL80211_SAR_ATTR_SPECS,
+
+       __NL80211_SAR_ATTR_LAST,
+       NL80211_SAR_ATTR_MAX = __NL80211_SAR_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_sar_specs_attrs - Attributes for SAR power limit specs
+ *
+ * @NL80211_SAR_ATTR_SPECS_POWER: Required (s32)value to specify the actual
+ *     power limit value in units of 0.25 dBm if type is
+ *     NL80211_SAR_TYPE_POWER. (i.e., a value of 44 represents 11 dBm).
+ *     0 means userspace doesn't have SAR limitation on this associated range.
+ *
+ * @NL80211_SAR_ATTR_SPECS_RANGE_INDEX: Required (u32) value to specify the
+ *     index of exported freq range table and the associated power limitation
+ *     is applied to this range.
+ *
+ *     Userspace isn't required to set all the ranges advertised by WLAN driver,
+ *     and userspace can skip some certain ranges. These skipped ranges don't
+ *     have SAR limitations, and they are same as setting the
+ *     %NL80211_SAR_ATTR_SPECS_POWER to any unreasonable high value because any
+ *     value higher than regulatory allowed value just means SAR power
+ *     limitation is removed, but it's required to set at least one range.
+ *     It's not allowed to set duplicated range in one SET operation.
+ *
+ *     Every SET operation overwrites previous SET operation.
+ *
+ * @NL80211_SAR_ATTR_SPECS_START_FREQ: Required (u32) value to specify the start
+ *     frequency of this range edge when registering SAR capability to wiphy.
+ *     It's not a channel center frequency. The unit is kHz.
+ *
+ * @NL80211_SAR_ATTR_SPECS_END_FREQ: Required (u32) value to specify the end
+ *     frequency of this range edge when registering SAR capability to wiphy.
+ *     It's not a channel center frequency. The unit is kHz.
+ *
+ * @__NL80211_SAR_ATTR_SPECS_LAST: Internal
+ * @NL80211_SAR_ATTR_SPECS_MAX: highest sar specs attribute
+ */
+enum nl80211_sar_specs_attrs {
+       __NL80211_SAR_ATTR_SPECS_INVALID,
+
+       NL80211_SAR_ATTR_SPECS_POWER,
+       NL80211_SAR_ATTR_SPECS_RANGE_INDEX,
+       NL80211_SAR_ATTR_SPECS_START_FREQ,
+       NL80211_SAR_ATTR_SPECS_END_FREQ,
+
+       __NL80211_SAR_ATTR_SPECS_LAST,
+       NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1,
+};
+
 #endif /* __LINUX_NL80211_H */
index 9e7c2c6078456bdc8dad240e844cfd42144bb024..79a699f106b14ef36afe459b955ab136326e36a0 100644 (file)
@@ -434,6 +434,7 @@ enum {
        TCA_HTB_RATE64,
        TCA_HTB_CEIL64,
        TCA_HTB_PAD,
+       TCA_HTB_OFFLOAD,
        __TCA_HTB_MAX,
 };
 
index 9b814c92de123911aad41564cb290fa099bb0c0c..5888492a5257b450d6518718a80c82a4d6c17b93 100644 (file)
@@ -178,6 +178,13 @@ enum {
        RTM_GETVLAN,
 #define RTM_GETVLAN    RTM_GETVLAN
 
+       RTM_NEWNEXTHOPBUCKET = 116,
+#define RTM_NEWNEXTHOPBUCKET   RTM_NEWNEXTHOPBUCKET
+       RTM_DELNEXTHOPBUCKET,
+#define RTM_DELNEXTHOPBUCKET   RTM_DELNEXTHOPBUCKET
+       RTM_GETNEXTHOPBUCKET,
+#define RTM_GETNEXTHOPBUCKET   RTM_GETNEXTHOPBUCKET
+
        __RTM_MAX,
 #define RTM_MAX                (((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -283,6 +290,7 @@ enum {
 #define RTPROT_MROUTED         17      /* Multicast daemon */
 #define RTPROT_KEEPALIVED      18      /* Keepalived daemon */
 #define RTPROT_BABEL           42      /* Babel daemon */
+#define RTPROT_OPENR           99      /* Open Routing (Open/R) Routes */
 #define RTPROT_BGP             186     /* BGP Routes */
 #define RTPROT_ISIS            187     /* ISIS Routes */
 #define RTPROT_OSPF            188     /* OSPF Routes */
@@ -319,6 +327,11 @@ enum rt_scope_t {
 #define RTM_F_FIB_MATCH                0x2000  /* return full fib lookup match */
 #define RTM_F_OFFLOAD          0x4000  /* route is offloaded */
 #define RTM_F_TRAP             0x8000  /* route is trapping packets */
+#define RTM_F_OFFLOAD_FAILED   0x20000000 /* route offload failed, this value
+                                           * is chosen to avoid conflicts with
+                                           * other flags defined in
+                                           * include/uapi/linux/ipv6_route.h
+                                           */
 
 /* Reserved table identifiers */
 
@@ -396,11 +409,13 @@ struct rtnexthop {
 #define RTNH_F_DEAD            1       /* Nexthop is dead (used by multipath)  */
 #define RTNH_F_PERVASIVE       2       /* Do recursive gateway lookup  */
 #define RTNH_F_ONLINK          4       /* Gateway is forced on link    */
-#define RTNH_F_OFFLOAD         8       /* offloaded route */
+#define RTNH_F_OFFLOAD         8       /* Nexthop is offloaded */
 #define RTNH_F_LINKDOWN                16      /* carrier-down on nexthop */
 #define RTNH_F_UNRESOLVED      32      /* The entry is unresolved (ipmr) */
+#define RTNH_F_TRAP            64      /* Nexthop is trapping packets */
 
-#define RTNH_COMPARE_MASK      (RTNH_F_DEAD | RTNH_F_LINKDOWN | RTNH_F_OFFLOAD)
+#define RTNH_COMPARE_MASK      (RTNH_F_DEAD | RTNH_F_LINKDOWN | \
+                                RTNH_F_OFFLOAD | RTNH_F_TRAP)
 
 /* Macros to handle hexthops */
 
@@ -766,12 +781,18 @@ enum {
 #define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg))
 /* tcamsg flags stored in attribute TCA_ROOT_FLAGS
  *
- * TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO
- * actions in a dump. All dump responses will contain the number of actions
- * being dumped stored in for user app's consumption in TCA_ROOT_COUNT
+ * TCA_ACT_FLAG_LARGE_DUMP_ON user->kernel to request for larger than
+ * TCA_ACT_MAX_PRIO actions in a dump. All dump responses will contain the
+ * number of actions being dumped stored in for user app's consumption in
+ * TCA_ROOT_COUNT
+ *
+ * TCA_ACT_FLAG_TERSE_DUMP user->kernel to request terse (brief) dump that only
+ * includes essential action info (kind, index, etc.)
  *
  */
 #define TCA_FLAG_LARGE_DUMP_ON         (1 << 0)
+#define TCA_ACT_FLAG_LARGE_DUMP_ON     TCA_FLAG_LARGE_DUMP_ON
+#define TCA_ACT_FLAG_TERSE_DUMP                (1 << 1)
 
 /* New extended info filters for IFLA_EXT_MASK */
 #define RTEXT_FILTER_VF                (1 << 0)
@@ -779,6 +800,8 @@ enum {
 #define RTEXT_FILTER_BRVLAN_COMPRESSED (1 << 2)
 #define        RTEXT_FILTER_SKIP_STATS (1 << 3)
 #define RTEXT_FILTER_MRP       (1 << 4)
+#define RTEXT_FILTER_CFM_CONFIG        (1 << 5)
+#define RTEXT_FILTER_CFM_STATUS        (1 << 6)
 
 /* End of information exported to user level */
 
index 256b7187c2e8ed54a64e549b711850bbaef441c3..e488fff9f020604e6aaba71808707ad75945f313 100644 (file)
 #define LIST_FOREACH_SAFE(name,i,n,head)                                \
         for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
 
-#define LIST_FOREACH_BEFORE(name,i,p)                                   \
-        for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev)
-
-#define LIST_FOREACH_AFTER(name,i,p)                                    \
-        for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next)
+#define LIST_FOREACH_BACKWARDS(name,i,p)                                \
+        for ((i) = (p); (i); (i) = (i)->name##_prev)
 
 /* Iterate through all the members of the list p is included in, but skip over p */
 #define LIST_FOREACH_OTHERS(name,i,p)                                   \
index fd6b01cfaade6f0441b61222964322e667ede998..007e3a091ecd38df4375814a75a0c918391b3f4e 100644 (file)
@@ -375,6 +375,9 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_DEPRESSED_SMILEY]        = ":-[",
                         [SPECIAL_GLYPH_LOCK_AND_KEY]            = "o-,",
                         [SPECIAL_GLYPH_TOUCH]                   = "O=",    /* Yeah, not very convincing, can you do it better? */
+                        [SPECIAL_GLYPH_RECYCLING]               = "~",
+                        [SPECIAL_GLYPH_DOWNLOAD]                = "\\",
+                        [SPECIAL_GLYPH_SPARKLES]                = "*",
                 },
 
                 /* UTF-8 */
@@ -421,7 +424,12 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_LOCK_AND_KEY]            = "\360\237\224\220",         /* 🔐 (actually called: CLOSED LOCK WITH KEY) */
 
                         /* This emoji is a single character cell glyph in Unicode, and two in ASCII */
-                        [SPECIAL_GLYPH_TOUCH]                   = "\360\237\221\206",         /* 👆 (actually called: BACKHAND INDEX POINTING UP */
+                        [SPECIAL_GLYPH_TOUCH]                   = "\360\237\221\206",         /* 👆 (actually called: BACKHAND INDEX POINTING UP) */
+
+                        /* These three emojis are single character cell glyphs in Unicode and also in ASCII. */
+                        [SPECIAL_GLYPH_RECYCLING]               = "\u267B\uFE0F ",            /* ♻️  (actually called: UNIVERSAL RECYCLNG SYMBOL) */
+                        [SPECIAL_GLYPH_DOWNLOAD]                = "\u2935\uFE0F ",            /* ⤵️  (actually called: RIGHT ARROW CURVING DOWN) */
+                        [SPECIAL_GLYPH_SPARKLES]                = "\u2728",                   /* ✨ */
                 },
         };
 
index df259d1bbd9f169907e45382d0800dca67a3d6eb..3430eb6ee1ff98e62b64d31222abb2993d135d50 100644 (file)
@@ -69,6 +69,9 @@ typedef enum SpecialGlyph {
         SPECIAL_GLYPH_DEPRESSED_SMILEY,
         SPECIAL_GLYPH_LOCK_AND_KEY,
         SPECIAL_GLYPH_TOUCH,
+        SPECIAL_GLYPH_RECYCLING,
+        SPECIAL_GLYPH_DOWNLOAD,
+        SPECIAL_GLYPH_SPARKLES,
         _SPECIAL_GLYPH_MAX,
         _SPECIAL_GLYPH_INVALID = -EINVAL,
 } SpecialGlyph;
@@ -95,3 +98,7 @@ static inline void locale_variables_freep(char*(*l)[_VARIABLE_LC_MAX]) {
 static inline const char *special_glyph_check_mark(bool b) {
         return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
 }
+
+static inline const char *special_glyph_check_mark_space(bool b) {
+        return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " ";
+}
index fb183ea9e752b62ec81aa66acffd1714e1101a3c..5fd2c5dcb4d3f755d529b95d7242cd4dc8d47361 100644 (file)
@@ -39,6 +39,9 @@
 
 #define SNDBUF_SIZE (8*1024*1024)
 
+static log_syntax_callback_t log_syntax_callback = NULL;
+static void *log_syntax_callback_userdata = NULL;
+
 static LogTarget log_target = LOG_TARGET_CONSOLE;
 static int log_max_level = LOG_INFO;
 static int log_facility = LOG_DAEMON;
@@ -476,7 +479,7 @@ static int write_to_syslog(
                 IOVEC_MAKE_STRING(header_pid),
                 IOVEC_MAKE_STRING(buffer),
         };
-        struct msghdr msghdr = {
+        const struct msghdr msghdr = {
                 .msg_iov = iovec,
                 .msg_iovlen = ELEMENTSOF(iovec),
         };
@@ -608,23 +611,24 @@ static int write_to_journal(
                 const char *buffer) {
 
         char header[LINE_MAX];
-        struct iovec iovec[4] = {};
-        struct msghdr mh = {};
 
         if (journal_fd < 0)
                 return 0;
 
         log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra);
 
-        iovec[0] = IOVEC_MAKE_STRING(header);
-        iovec[1] = IOVEC_MAKE_STRING("MESSAGE=");
-        iovec[2] = IOVEC_MAKE_STRING(buffer);
-        iovec[3] = IOVEC_MAKE_STRING("\n");
-
-        mh.msg_iov = iovec;
-        mh.msg_iovlen = ELEMENTSOF(iovec);
+        struct iovec iovec[4] = {
+                IOVEC_MAKE_STRING(header),
+                IOVEC_MAKE_STRING("MESSAGE="),
+                IOVEC_MAKE_STRING(buffer),
+                IOVEC_MAKE_STRING("\n"),
+        };
+        const struct msghdr msghdr = {
+                .msg_iov = iovec,
+                .msg_iovlen = ELEMENTSOF(iovec),
+        };
 
-        if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
+        if (sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL) < 0)
                 return -errno;
 
         return 1;
@@ -863,12 +867,11 @@ _noreturn_ void log_assert_failed(
 }
 
 _noreturn_ void log_assert_failed_unreachable(
-                const char *text,
                 const char *file,
                 int line,
                 const char *func) {
-        log_assert(LOG_CRIT, text, file, line, func,
-                   "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
+        log_assert(LOG_CRIT, "Code should not be reached", file, line, func,
+                   "%s at %s:%u, function %s(). Aborting. 💥");
         abort();
 }
 
@@ -919,11 +922,8 @@ int log_format_iovec(
                 VA_FORMAT_ADVANCE(format, ap);
 
                 iovec[(*n)++] = IOVEC_MAKE_STRING(m);
-
-                if (newline_separator) {
-                        iovec[*n] = IOVEC_MAKE((char *)&nl, 1);
-                        (*n)++;
-                }
+                if (newline_separator)
+                        iovec[(*n)++] = IOVEC_MAKE((char *)&nl, 1);
 
                 format = va_arg(ap, char *);
         }
@@ -960,12 +960,9 @@ int log_struct_internal(
 
                 if (journal_fd >= 0) {
                         char header[LINE_MAX];
-                        struct iovec iovec[17] = {};
+                        struct iovec iovec[17];
                         size_t n = 0;
                         int r;
-                        struct msghdr mh = {
-                                .msg_iov = iovec,
-                        };
                         bool fallback = false;
 
                         /* If the journal is available do structured logging.
@@ -978,8 +975,12 @@ int log_struct_internal(
                         if (r < 0)
                                 fallback = true;
                         else {
-                                mh.msg_iovlen = n;
-                                (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL);
+                                const struct msghdr msghdr = {
+                                        .msg_iov = iovec,
+                                        .msg_iovlen = n,
+                                };
+
+                                (void) sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL);
                         }
 
                         va_end(ap);
@@ -1038,8 +1039,6 @@ int log_struct_iovec_internal(
                 size_t n_input_iovec) {
 
         PROTECT_ERRNO;
-        size_t i;
-        char *m;
 
         if (_likely_(LOG_PRI(level) > log_max_level) ||
             log_target == LOG_TARGET_NULL)
@@ -1053,36 +1052,35 @@ int log_struct_iovec_internal(
                                LOG_TARGET_JOURNAL) &&
             journal_fd >= 0) {
 
-                struct iovec iovec[1 + n_input_iovec*2];
                 char header[LINE_MAX];
-                struct msghdr mh = {
-                        .msg_iov = iovec,
-                        .msg_iovlen = 1 + n_input_iovec*2,
-                };
-
                 log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
-                iovec[0] = IOVEC_MAKE_STRING(header);
 
-                for (i = 0; i < n_input_iovec; i++) {
+                struct iovec iovec[1 + n_input_iovec*2];
+                iovec[0] = IOVEC_MAKE_STRING(header);
+                for (size_t i = 0; i < n_input_iovec; i++) {
                         iovec[1+i*2] = input_iovec[i];
                         iovec[1+i*2+1] = IOVEC_MAKE_STRING("\n");
                 }
 
-                if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) >= 0)
+                const struct msghdr msghdr = {
+                        .msg_iov = iovec,
+                        .msg_iovlen = 1 + n_input_iovec*2,
+                };
+
+                if (sendmsg(journal_fd, &msghdr, MSG_NOSIGNAL) >= 0)
                         return -ERRNO_VALUE(error);
         }
 
-        for (i = 0; i < n_input_iovec; i++)
-                if (memory_startswith(input_iovec[i].iov_base, input_iovec[i].iov_len, "MESSAGE="))
-                        break;
+        for (size_t i = 0; i < n_input_iovec; i++)
+                if (memory_startswith(input_iovec[i].iov_base, input_iovec[i].iov_len, "MESSAGE=")) {
+                        char *m = strndupa(input_iovec[i].iov_base + STRLEN("MESSAGE="),
+                                           input_iovec[i].iov_len - STRLEN("MESSAGE="));
 
-        if (_unlikely_(i >= n_input_iovec)) /* Couldn't find MESSAGE=? */
-                return -ERRNO_VALUE(error);
-
-        m = strndupa(input_iovec[i].iov_base + STRLEN("MESSAGE="),
-                     input_iovec[i].iov_len - STRLEN("MESSAGE="));
+                        return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, m);
+                }
 
-        return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, m);
+        /* Couldn't find MESSAGE=. */
+        return -ERRNO_VALUE(error);
 }
 
 int log_set_target_from_string(const char *e) {
@@ -1346,6 +1344,14 @@ void log_received_signal(int level, const struct signalfd_siginfo *si) {
                          signal_to_string(si->ssi_signo));
 }
 
+void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata) {
+        assert(!log_syntax_callback || !cb);
+        assert(!log_syntax_callback_userdata || !userdata);
+
+        log_syntax_callback = cb;
+        log_syntax_callback_userdata = userdata;
+}
+
 int log_syntax_internal(
                 const char *unit,
                 int level,
@@ -1357,6 +1363,9 @@ int log_syntax_internal(
                 const char *func,
                 const char *format, ...) {
 
+        if (log_syntax_callback)
+                log_syntax_callback(unit, level, log_syntax_callback_userdata);
+
         PROTECT_ERRNO;
         char buffer[LINE_MAX];
         va_list ap;
index 738c181070337cf20b3eff5d20cf8879b989afe9..b34bdffd1b8fed1811e2bb874520e4d56c9c4b76 100644 (file)
@@ -32,6 +32,15 @@ typedef enum LogTarget{
 #define IS_SYNTHETIC_ERRNO(val)             ((val) >> 30 & 1)
 #define ERRNO_VALUE(val)                    (abs(val) & 255)
 
+/* The callback function to be invoked when syntax warnings are seen
+ * in the unit files. */
+typedef void (*log_syntax_callback_t)(const char *unit, int level, void *userdata);
+void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata);
+
+static inline void clear_log_syntax_callback(dummy_t *dummy) {
+          set_log_syntax_callback(/* cb= */ NULL, /* userdata= */ NULL);
+}
+
 const char *log_target_to_string(LogTarget target) _const_;
 LogTarget log_target_from_string(const char *s) _pure_;
 void log_set_target(LogTarget target);
@@ -174,7 +183,6 @@ _noreturn_ void log_assert_failed(
                 const char *func);
 
 _noreturn_ void log_assert_failed_unreachable(
-                const char *text,
                 const char *file,
                 int line,
                 const char *func);
index a8476184c2d8dce06e641a4a696cde2d60003e6d..6977a1ddd94b47be46c85772a922574bbedc1291 100644 (file)
 #define _sentinel_ __attribute__((__sentinel__))
 #define _destructor_ __attribute__((__destructor__))
 #define _deprecated_ __attribute__((__deprecated__))
-#define _packed_ __attribute__((__packed__))
 #define _malloc_ __attribute__((__malloc__))
 #define _weak_ __attribute__((__weak__))
-#define _likely_(x) (__builtin_expect(!!(x), 1))
-#define _unlikely_(x) (__builtin_expect(!!(x), 0))
 #define _public_ __attribute__((__visibility__("default")))
 #define _hidden_ __attribute__((__visibility__("hidden")))
 #define _weakref_(x) __attribute__((__weakref__(#x)))
 #define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
 #define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
 #define _warn_unused_result_ __attribute__((__warn_unused_result__))
-#if __GNUC__ >= 7
-#define _fallthrough_ __attribute__((__fallthrough__))
-#else
-#define _fallthrough_
-#endif
-/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
- * compiler versions */
-#ifndef _noreturn_
-#if __STDC_VERSION__ >= 201112L
-#define _noreturn_ _Noreturn
-#else
-#define _noreturn_ __attribute__((__noreturn__))
-#endif
-#endif
 
 #if !defined(HAS_FEATURE_MEMORY_SANITIZER)
 #  if defined(__has_feature)
 #define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
 
 static inline size_t ALIGN_TO(size_t l, size_t ali) {
+        /* Check that alignment is exponent of 2 */
+#if SIZE_MAX == UINT_MAX
+        assert(__builtin_popcount(ali) == 1);
+#elif SIZE_MAX == ULONG_MAX
+        assert(__builtin_popcountl(ali) == 1);
+#elif SIZE_MAX == ULONGLONG_MAX
+        assert(__builtin_popcountll(ali) == 1);
+#else
+#error "Unexpected size_t"
+#endif
+
+        if (l > SIZE_MAX - (ali - 1))
+                return SIZE_MAX; /* indicate overflow */
+
         return ((l + ali - 1) & ~(ali - 1));
 }
 
@@ -209,13 +206,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
         return m;
 }
 
-/*
- * STRLEN - return the length of a string literal, minus the trailing NUL byte.
- *          Contrary to strlen(), this is a constant expression.
- * @x: a string literal.
- */
-#define STRLEN(x) ((unsigned) sizeof(""x"") - 1)
-
 /*
  * container_of - cast a member of a structure out to the containing structure
  * @ptr: the pointer to the member.
@@ -282,8 +272,8 @@ static inline int __coverity_check_and_return__(int condition) {
 #define assert(expr) assert_message_se(expr, #expr)
 #endif
 
-#define assert_not_reached(t)                                           \
-        log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
+#define assert_not_reached(                                           \
+        log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
 
 #define assert_return(expr, r)                                          \
         do {                                                            \
@@ -345,12 +335,12 @@ static inline int __coverity_check_and_return__(int condition) {
         (2U+(sizeof(type) <= 1 ? 3U :                                   \
              sizeof(type) <= 2 ? 5U :                                   \
              sizeof(type) <= 4 ? 10U :                                  \
-             sizeof(type) <= 8 ? 20U : (unsigned) sizeof(int[-2*(sizeof(type) > 8)])))
+             sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)])))
 
 #define DECIMAL_STR_WIDTH(x)                            \
         ({                                              \
                 typeof(x) _x_ = (x);                    \
-                unsigned ans = 1;                       \
+                size_t ans = 1;                         \
                 while ((_x_ /= 10) != 0)                \
                         ans++;                          \
                 ans;                                    \
@@ -483,4 +473,10 @@ static inline size_t size_add(size_t x, size_t y) {
         return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
 }
 
+typedef struct {
+        int _empty[0];
+} dummy_t;
+
+assert_cc(sizeof(dummy_t) == 0);
+
 #include "log.h"
index 0b04278ab47a6deaa6a7e89732a7fe8a5a78e1e6..e2b543cb8f26a95120dee151388468f347749223 100644 (file)
@@ -71,6 +71,16 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
         return memmem(haystack, haystacklen, needle, needlelen);
 }
 
+static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+        const uint8_t *p;
+
+        p = memmem_safe(haystack, haystacklen, needle, needlelen);
+        if (!p)
+                return NULL;
+
+        return (uint8_t*) p + needlelen;
+}
+
 #if HAVE_EXPLICIT_BZERO
 static inline void* explicit_bzero_safe(void *p, size_t l) {
         if (l > 0)
index a55b76df97b98c5f70f6e1c6f621773ff36a8a0d..d1fee952458e5349c5e261e09bf737a00c13bffd 100644 (file)
@@ -89,9 +89,7 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
                 /* Can't setns to your own userns, since then you could escalate from non-root to root in
                  * your own namespace, so check if namespaces are equal before attempting to enter. */
 
-                char userns_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-                xsprintf(userns_fd_path, "/proc/self/fd/%d", userns_fd);
-                r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
+                r = files_same(FORMAT_PROC_FD_PATH(userns_fd), "/proc/self/ns/user", 0);
                 if (r < 0)
                         return r;
                 if (r)
index 51c685bc6a87e2c85a6de76bb730471fac5507ab..1d69ecc58622c0616c3dceb083ae3adbce6e3a87 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "alloc-util.h"
+#include "dirent-util.h"
 #include "env-file.h"
 #include "env-util.h"
 #include "fd-util.h"
@@ -8,10 +9,13 @@
 #include "fs-util.h"
 #include "macro.h"
 #include "os-util.h"
+#include "parse-util.h"
 #include "path-util.h"
+#include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "utf8.h"
+#include "xattr-util.h"
 
 bool image_name_is_valid(const char *s) {
         if (!filename_is_valid(s))
@@ -41,8 +45,8 @@ int path_is_extension_tree(const char *path, const char *extension) {
         if (laccess(path, F_OK) < 0)
                 return -errno;
 
-        /* We use /usr/lib/extension-release.d/extension-release.NAME as flag file if something is a system extension,
-         * and {/etc|/usr/lib}/os-release as flag file if something is an OS (in case extension == NULL) */
+        /* We use /usr/lib/extension-release.d/extension-release[.NAME] as flag for something being a system extension,
+         * and {/etc|/usr/lib}/os-release as a flag for something being an OS (when not an extension). */
         r = open_extension_release(path, extension, NULL, NULL);
         if (r == -ENOENT) /* We got nothing */
                 return 0;
@@ -67,6 +71,91 @@ int open_extension_release(const char *root, const char *extension, char **ret_p
                 r = chase_symlinks(extension_full_path, root, CHASE_PREFIX_ROOT,
                                    ret_path ? &q : NULL,
                                    ret_fd ? &fd : NULL);
+                /* Cannot find the expected extension-release file? The image filename might have been
+                 * mangled on deployment, so fallback to checking for any file in the extension-release.d
+                 * directory, and return the first one with a user.extension-release xattr instead.
+                 * The user.extension-release.strict xattr is checked to ensure the author of the image
+                 * considers it OK if names do not match. */
+                if (r == -ENOENT) {
+                        _cleanup_free_ char *extension_release_dir_path = NULL;
+                        _cleanup_closedir_ DIR *extension_release_dir = NULL;
+
+                        r = chase_symlinks_and_opendir("/usr/lib/extension-release.d/", root, CHASE_PREFIX_ROOT,
+                                                       &extension_release_dir_path, &extension_release_dir);
+                        if (r < 0)
+                                return r;
+
+                        r = -ENOENT;
+                        struct dirent *de;
+                        FOREACH_DIRENT(de, extension_release_dir, return -errno) {
+                                int k;
+
+                                if (!IN_SET(de->d_type, DT_REG, DT_UNKNOWN))
+                                        continue;
+
+                                const char *image_name = startswith(de->d_name, "extension-release.");
+                                if (!image_name)
+                                        continue;
+
+                                if (!image_name_is_valid(image_name))
+                                        continue;
+
+                                /* We already chased the directory, and checked that
+                                 * this is a real file, so we shouldn't fail to open it. */
+                                _cleanup_close_ int extension_release_fd = openat(dirfd(extension_release_dir),
+                                                                                  de->d_name,
+                                                                                  O_PATH|O_CLOEXEC|O_NOFOLLOW);
+                                if (extension_release_fd < 0)
+                                        return log_debug_errno(errno,
+                                                               "Failed to open extension-release file %s/%s: %m",
+                                                               extension_release_dir_path,
+                                                               de->d_name);
+
+                                /* Really ensure it is a regular file after we open it. */
+                                if (fd_verify_regular(extension_release_fd) < 0)
+                                        continue;
+
+                                /* No xattr or cannot parse it? Then skip this. */
+                                _cleanup_free_ char *extension_release_xattr = NULL;
+                                k = fgetxattrat_fake_malloc(extension_release_fd, NULL, "user.extension-release.strict", AT_EMPTY_PATH, &extension_release_xattr);
+                                if (k < 0 && !ERRNO_IS_NOT_SUPPORTED(k) && k != -ENODATA)
+                                        log_debug_errno(k,
+                                                        "Failed to read 'user.extension-release.strict' extended attribute from extension-release file %s/%s: %m",
+                                                        extension_release_dir_path,
+                                                        de->d_name);
+                                if (k < 0)
+                                        continue;
+
+                                /* Explicitly set to request strict matching? Skip it. */
+                                k = parse_boolean(extension_release_xattr);
+                                if (k < 0)
+                                        log_debug_errno(k,
+                                                        "Failed to parse 'user.extension-release.strict' extended attribute value from extension-release file %s/%s: %m",
+                                                        extension_release_dir_path,
+                                                        de->d_name);
+                                if (k < 0 || k > 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 fallback path.*/
+                                if (r == 0) {
+                                        r = -ENOTUNIQ;
+                                        break;
+                                }
+
+                                r = 0; /* Found it! */
+
+                                if (ret_fd)
+                                        fd = TAKE_FD(extension_release_fd);
+
+                                if (ret_path) {
+                                        q = path_join(extension_release_dir_path, de->d_name);
+                                        if (!q)
+                                                return -ENOMEM;
+                                }
+                        }
+                }
         } else {
                 const char *p;
 
index b79c885dfd21292bf10115f413f8ae93aa0ad7f6..70062eba2aa8a59bb5872f73fe0933ce246c4eb5 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <errno.h>
 #include <inttypes.h>
-#include <linux/oom.h>
 #include <net/if.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -731,7 +730,7 @@ int parse_oom_score_adjust(const char *s, int *ret) {
         if (r < 0)
                 return r;
 
-        if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX)
+        if (!oom_score_adjust_is_valid(v))
                 return -ERANGE;
 
         *ret = v;
index 05eb17d66ccbb5f83056a5daa161c7776b31858f..987834b0d55ac1929d8b1b4a18ac9fb84d94cca7 100644 (file)
@@ -347,7 +347,7 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru
                 return 0;
 
         default:
-                assert_not_reached("Hmm, unexpected scope value.");
+                assert_not_reached();
         }
 
         if (!a || !b)
@@ -405,7 +405,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r
                 return -EOPNOTSUPP;
 
         default:
-                assert_not_reached("Hmm, unexpected scope value.");
+                assert_not_reached();
         }
 
         *persistent = TAKE_PTR(a);
@@ -657,7 +657,7 @@ int lookup_paths_init(
                         break;
 
                 default:
-                        assert_not_reached("Hmm, unexpected scope?");
+                        assert_not_reached();
                 }
 
                 if (!add)
@@ -807,7 +807,7 @@ char **generator_binary_paths(UnitFileScope scope) {
                         break;
 
                 default:
-                        assert_not_reached("Hmm, unexpected scope.");
+                        assert_not_reached();
                 }
 
                 if (!add)
index 4aebb6541ea153c6832fd445881cffb225fca9e4..fe799da20b6ce73071f965fcf19a224f60d76ab2 100644 (file)
@@ -639,7 +639,7 @@ static int check_x_access(const char *path, int *ret_fd) {
         return 0;
 }
 
-int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) {
+int find_executable_full(const char *name, const char *root, bool use_path_envvar, char **ret_filename, int *ret_fd) {
         int last_error, r;
         const char *p = NULL;
 
@@ -647,6 +647,25 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
 
         if (is_path(name)) {
                 _cleanup_close_ int fd = -1;
+                _cleanup_free_ char *path_name = NULL;
+
+                /* Function chase_symlinks() is invoked only when root is not NULL,
+                 * as using it regardless of root value would alter the behavior
+                 * of existing callers for example: /bin/sleep would become
+                 * /usr/bin/sleep when find_executables is called. Hence, this function
+                 * should be invoked when needed to avoid unforeseen regression or other
+                 * complicated changes. */
+                if (root) {
+                        r = chase_symlinks(name,
+                                           root,
+                                           CHASE_PREFIX_ROOT,
+                                           &path_name,
+                                           /* ret_fd= */ NULL); /* prefix root to name in case full paths are not specified */
+                        if (r < 0)
+                                return r;
+
+                        name = path_name;
+                }
 
                 r = check_x_access(name, ret_fd ? &fd : NULL);
                 if (r < 0)
@@ -690,6 +709,23 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
                 if (!path_extend(&element, name))
                         return -ENOMEM;
 
+                if (root) {
+                        char *path_name;
+
+                        r = chase_symlinks(element,
+                                           root,
+                                           CHASE_PREFIX_ROOT,
+                                           &path_name,
+                                           /* ret_fd= */ NULL);
+                        if (r < 0) {
+                                if (r != -EACCES)
+                                        last_error = r;
+                                continue;
+                        }
+
+                        free_and_replace(element, path_name);
+                }
+
                 r = check_x_access(element, ret_fd ? &fd : NULL);
                 if (r < 0) {
                         /* PATH entries which we don't have access to are ignored, as per tradition. */
index 26e7362d1f5865bb0aa5c596a524e40f7813bf33..0d69145497d50a51d6d44856355a629087e71a6e 100644 (file)
@@ -99,9 +99,9 @@ int path_strv_make_absolute_cwd(char **l);
 char** path_strv_resolve(char **l, const char *root);
 char** path_strv_resolve_uniq(char **l, const char *root);
 
-int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd);
+int find_executable_full(const char *name, const char *root, bool use_path_envvar, char **ret_filename, int *ret_fd);
 static inline int find_executable(const char *name, char **ret_filename) {
-        return find_executable_full(name, true, ret_filename, NULL);
+        return find_executable_full(name, /* root= */ NULL, true, ret_filename, NULL);
 }
 
 bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
index ce4bfb783da121534f67162db335badf97b3f379..ecb14306f8b1ca64a1003115c6b4dc4cc598f3fd 100644 (file)
@@ -1037,6 +1037,10 @@ bool is_main_thread(void) {
         return cached > 0;
 }
 
+bool oom_score_adjust_is_valid(int oa) {
+        return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
+}
+
 unsigned long personality_from_string(const char *p) {
         int architecture;
 
index a591fc32a4ad3a29261415887ccc4fc87eb15422..c9536495023855d5e9a363818d2eeac87ba9f160 100644 (file)
@@ -82,6 +82,8 @@ int pid_from_same_root_fs(pid_t pid);
 
 bool is_main_thread(void);
 
+bool oom_score_adjust_is_valid(int oa);
+
 #ifndef PERSONALITY_INVALID
 /* personality(7) documents that 0xffffffffUL is used for querying the
  * current personality, hence let's use that here as error
index 0f8673934f118d72a45de0c028be154be42a48f0..5cae13160bc84999adb00326956d982e7c77cb26 100644 (file)
@@ -26,7 +26,7 @@ static inline Set* set_free_free(Set *s) {
 
 /* no set_free_free_free */
 
-#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h)  HASHMAP_DEBUG_SRC_ARGS))
+#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(s)  HASHMAP_DEBUG_SRC_ARGS))
 
 int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
 #define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
index b06b5ce7744d8d444a2561097effdd455cff6398..34b2b279189230a8247abd5db0ee1c62f48c6661 100644 (file)
@@ -265,7 +265,7 @@ int pop_pending_signal_internal(int sig, ...) {
         if (sigemptyset(&ss) < 0)
                 return -errno;
 
-        /* Add first signal (if the signal is zero, we'll silently skip it, to make it easiert to build
+        /* Add first signal (if the signal is zero, we'll silently skip it, to make it easier to build
          * parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles
          * this.) */
         if (sig > 0 && sigaddset(&ss, sig) < 0)
index f92e425fd68b738febfd9c57efa269cc09039fba..e857ae4341925fb3c8195b77432a8a11d01c29de 100644 (file)
@@ -245,7 +245,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
                         _len = sizeof(struct sockaddr_vm);              \
                         break;                                          \
                 default:                                                \
-                        assert_not_reached("invalid socket family");    \
+                        assert_not_reached();                           \
                 }                                                       \
                 _len;                                                   \
         })
index 78f22f1ac918d4ef844a2230d5b85911ff1b0751..5d1111fd712be76261241da9358c02cd0695a474 100644 (file)
@@ -20,6 +20,7 @@
 #define SPECIAL_HIBERNATE_TARGET "hibernate.target"
 #define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target"
 #define SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET "suspend-then-hibernate.target"
+#define SPECIAL_FACTORY_RESET_TARGET "factory-reset.target"
 
 /* Special boot targets */
 #define SPECIAL_RESCUE_TARGET "rescue.target"
index 6dc1e72312c8dfc3b02193e59e2fffbb5df6a5e8..69d7062ec6f38eeac130c4a25c51e629053deba5 100644 (file)
@@ -9,8 +9,13 @@
 #include "macro.h"
 #include "memory-util.h"
 
-#define snprintf_ok(buf, len, fmt, ...) \
-        ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len))
+#define snprintf_ok(buf, len, fmt, ...)                                \
+        ({                                                             \
+                char *_buf = (buf);                                    \
+                size_t _len = (len);                                   \
+                int _snpf = snprintf(_buf, _len, (fmt), __VA_ARGS__);  \
+                _snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL;     \
+        })
 
 #define xsprintf(buf, fmt, ...) \
         assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough")
@@ -58,7 +63,7 @@ do {                                                                    \
                         (void) va_arg(ap, long double);                 \
                         break;                                          \
                 default:                                                \
-                        assert_not_reached("Unknown format string argument."); \
+                        assert_not_reached();                           \
                 }                                                       \
         }                                                               \
 } while (false)
index 9155e50ba8927c3af2f2d39c40e422638048ef08..5c11adf393f52172bbc4e66f9f8e201967145b84 100644 (file)
@@ -189,22 +189,6 @@ static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) {
 }
 REENABLE_WARNING;
 
-/* Like startswith(), but operates on arbitrary memory blocks */
-static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
-        assert(token);
-
-        size_t n = strlen(token);
-        if (sz < n)
-                return NULL;
-
-        assert(p);
-
-        if (memcmp(p, token, n) != 0)
-                return NULL;
-
-        return (uint8_t*) p + n;
-}
-
 /* Like startswith_no_case(), but operates on arbitrary memory blocks.
  * It works only for ASCII strings.
  */
index 911528fab495a9d761211ccfd8f7116f168a526b..e7654c0c0ff6f09417c4c135e0220016502a5df1 100644 (file)
@@ -233,9 +233,11 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
 
 #define strv_free_and_replace(a, b)             \
         ({                                      \
-                strv_free(a);                   \
-                (a) = (b);                      \
-                (b) = NULL;                     \
+                char ***_a = &(a);              \
+                char ***_b = &(b);              \
+                strv_free(*_a);                 \
+                (*_a) = (*_b);                  \
+                (*_b) = NULL;                   \
                 0;                              \
         })
 
index c3b175a192bffd942e6610e4fecb871a1d3af8e9..f4022f7c86984a1f59fb3a074e8451b4a334c362 100644 (file)
@@ -433,62 +433,62 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
                 usec_t years = d / USEC_PER_YEAR;
                 usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
 
-                snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
-                         years,
-                         years == 1 ? "year" : "years",
-                         months,
-                         months == 1 ? "month" : "months",
-                         s);
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                                years,
+                                years == 1 ? "year" : "years",
+                                months,
+                                months == 1 ? "month" : "months",
+                                s);
         } else if (d >= USEC_PER_MONTH) {
                 usec_t months = d / USEC_PER_MONTH;
                 usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
 
-                snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
-                         months,
-                         months == 1 ? "month" : "months",
-                         days,
-                         days == 1 ? "day" : "days",
-                         s);
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                                months,
+                                months == 1 ? "month" : "months",
+                                days,
+                                days == 1 ? "day" : "days",
+                                s);
         } else if (d >= USEC_PER_WEEK) {
                 usec_t weeks = d / USEC_PER_WEEK;
                 usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
 
-                snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
-                         weeks,
-                         weeks == 1 ? "week" : "weeks",
-                         days,
-                         days == 1 ? "day" : "days",
-                         s);
+                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s",
+                                weeks,
+                                weeks == 1 ? "week" : "weeks",
+                                days,
+                                days == 1 ? "day" : "days",
+                                s);
         } else if (d >= 2*USEC_PER_DAY)
-                snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
+                (void) snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
         else if (d >= 25*USEC_PER_HOUR)
-                snprintf(buf, l, "1 day " USEC_FMT "h %s",
-                         (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
+                (void) snprintf(buf, l, "1 day " USEC_FMT "h %s",
+                                (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
         else if (d >= 6*USEC_PER_HOUR)
-                snprintf(buf, l, USEC_FMT "h %s",
-                         d / USEC_PER_HOUR, s);
+                (void) snprintf(buf, l, USEC_FMT "h %s",
+                                d / USEC_PER_HOUR, s);
         else if (d >= USEC_PER_HOUR)
-                snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
-                         d / USEC_PER_HOUR,
-                         (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
+                (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
+                                d / USEC_PER_HOUR,
+                                (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
         else if (d >= 5*USEC_PER_MINUTE)
-                snprintf(buf, l, USEC_FMT "min %s",
-                         d / USEC_PER_MINUTE, s);
+                (void) snprintf(buf, l, USEC_FMT "min %s",
+                                d / USEC_PER_MINUTE, s);
         else if (d >= USEC_PER_MINUTE)
-                snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
-                         d / USEC_PER_MINUTE,
-                         (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
+                (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
+                                d / USEC_PER_MINUTE,
+                                (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
         else if (d >= USEC_PER_SEC)
-                snprintf(buf, l, USEC_FMT "s %s",
-                         d / USEC_PER_SEC, s);
+                (void) snprintf(buf, l, USEC_FMT "s %s",
+                                d / USEC_PER_SEC, s);
         else if (d >= USEC_PER_MSEC)
-                snprintf(buf, l, USEC_FMT "ms %s",
-                         d / USEC_PER_MSEC, s);
+                (void) snprintf(buf, l, USEC_FMT "ms %s",
+                                d / USEC_PER_MSEC, s);
         else if (d > 0)
-                snprintf(buf, l, USEC_FMT"us %s",
-                         d, s);
+                (void) snprintf(buf, l, USEC_FMT"us %s",
+                                d, s);
         else
-                snprintf(buf, l, "now");
+                (void) snprintf(buf, l, "now");
 
         buf[l-1] = 0;
         return buf;
index 91c8ff172549a992b30e3cf3a22354df4b6bb0ca..36930cb34b5ed0e2f7e7bf287f5a658ed8f92450 100644 (file)
@@ -300,11 +300,7 @@ int link_tmpfile(int fd, const char *path, const char *target) {
                 if (r < 0)
                         return r;
         } else {
-                char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
-
-                xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
-
-                if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
+                if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
                         return -errno;
         }
 
index c175ce1fbcc79c91aef0dab7aa9c1af917880996..3d5eb6c378fd4ed83d8c724e039f7acaf970d727 100644 (file)
@@ -103,17 +103,17 @@ int fgetxattr_malloc(
         }
 }
 
-int fgetxattrat_fake(
+/* Note: ret_fn should already be allocated for the usual xsprintf and /proc/self/fd/%i pattern. */
+static int getxattrat_fake_prepare(
                 int dirfd,
                 const char *filename,
-                const char *attribute,
-                void *value, size_t size,
                 int flags,
-                size_t *ret_size) {
+                char ret_fn[static PROC_FD_PATH_MAX],
+                int *ret_fd) {
 
-        char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
         _cleanup_close_ int fd = -1;
-        ssize_t l;
+        assert(ret_fn);
+        assert(ret_fd);
 
         /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
 
@@ -124,15 +124,40 @@ int fgetxattrat_fake(
                 if (!(flags & AT_EMPTY_PATH))
                         return -EINVAL;
 
-                xsprintf(fn, "/proc/self/fd/%i", dirfd);
+                assert(dirfd >= 0);
+
+                format_proc_fd_path(ret_fn, dirfd);
         } else {
                 fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
                 if (fd < 0)
                         return -errno;
 
-                xsprintf(fn, "/proc/self/fd/%i", fd);
+                format_proc_fd_path(ret_fn, fd);
         }
 
+        /* Pass the FD to the caller, since in case we do openat() the filename depends on it. */
+        *ret_fd = TAKE_FD(fd);
+
+        return 0;
+}
+
+int fgetxattrat_fake(
+                int dirfd,
+                const char *filename,
+                const char *attribute,
+                void *value, size_t size,
+                int flags,
+                size_t *ret_size) {
+
+        _cleanup_close_ int fd = -1;
+        char fn[PROC_FD_PATH_MAX];
+        ssize_t l;
+        int r;
+
+        r = getxattrat_fake_prepare(dirfd, filename, flags, fn, &fd);
+        if (r < 0)
+                return r;
+
         l = getxattr(fn, attribute, value, size);
         if (l < 0)
                 return -errno;
@@ -141,6 +166,24 @@ int fgetxattrat_fake(
         return 0;
 }
 
+int fgetxattrat_fake_malloc(
+                int dirfd,
+                const char *filename,
+                const char *attribute,
+                int flags,
+                char **value) {
+
+        _cleanup_close_ int fd = -1;
+        char fn[PROC_FD_PATH_MAX];
+        int r;
+
+        r = getxattrat_fake_prepare(dirfd, filename, flags, fn, &fd);
+        if (r < 0)
+                return r;
+
+        return getxattr_malloc(fn, attribute, value, false);
+}
+
 static int parse_crtime(le64_t le, usec_t *usec) {
         uint64_t u;
 
index 560e34babda4dc4c20da1ada690dcd8dee5e1d9b..ac64f4a544e8cd8d0fd69ed02874c795a680fffd 100644 (file)
@@ -17,6 +17,12 @@ int fgetxattrat_fake(
                 void *value, size_t size,
                 int flags,
                 size_t *ret_size);
+int fgetxattrat_fake_malloc(
+                int dirfd,
+                const char *filename,
+                const char *attribute,
+                int flags,
+                char **value);
 
 int fd_setcrtime(int fd, usec_t usec);
 
index 29530bb691c6c22c398a3067836ea6c4c7a0a0ed..bf1861981a3aa6816180289827ed036588132f56 100644 (file)
@@ -174,7 +174,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if ((arg_unregister || arg_cat_config) && argc > optind)
index 3fc319ca27325391214472a0b42ca6476dda0e3e..7d5a52841ee107190ba4fd47d8d7fd98f0fd55bb 100644 (file)
@@ -89,7 +89,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         return 1;
index cb4f758cb41603eb463d7ccb95d21202170d51a4..78642063c4a61c7fc4198d13d03e81077c384782 100644 (file)
@@ -67,7 +67,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         return 1;
index fa8c6003218d2e436e939d3b5bef23a2e9508e33..d8a66616b0ffc94611e7466f3e35c1e8652692a9 100644 (file)
@@ -65,6 +65,7 @@ static const char *arg_dollar_boot_path(void) {
 
 static int acquire_esp(
                 bool unprivileged_mode,
+                bool graceful,
                 uint32_t *ret_part,
                 uint64_t *ret_pstart,
                 uint64_t *ret_psize,
@@ -80,10 +81,14 @@ static int acquire_esp(
          * this). */
 
         r = find_esp_and_warn(arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid);
-        if (r == -ENOKEY)
+        if (r == -ENOKEY) {
+                if (graceful)
+                        return log_info_errno(r, "Couldn't find EFI system partition, skipping.");
+
                 return log_error_errno(r,
                                        "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
                                        "Alternatively, use --esp-path= to specify path to mount point.");
+        }
         if (r < 0)
                 return r;
 
@@ -154,12 +159,11 @@ static int get_file_version(int fd, char **v) {
         if (buf == MAP_FAILED)
                 return log_error_errno(errno, "Failed to memory map EFI binary: %m");
 
-        s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
+        s = mempmem_safe(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
         if (!s)
                 goto finish;
-        s += 17;
 
-        e = memmem(s, st.st_size - (s - buf), " ####", 5);
+        e = memmem_safe(s, st.st_size - (s - buf), " ####", 5);
         if (!e || e - s < 3) {
                 r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed version string.");
                 goto finish;
@@ -500,7 +504,7 @@ static int version_check(int fd_from, const char *from, int fd_to, const char *t
         if (r < 0)
                 return r;
         if (r == 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE),
                                        "Source file \"%s\" does not carry version information!",
                                        from);
 
@@ -508,12 +512,15 @@ static int version_check(int fd_from, const char *from, int fd_to, const char *t
         if (r < 0)
                 return r;
         if (r == 0 || compare_product(a, b) != 0)
-                return log_notice_errno(SYNTHETIC_ERRNO(EEXIST),
+                return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE),
                                         "Skipping \"%s\", since it's owned by another boot loader.",
                                         to);
 
-        if (compare_version(a, b) < 0)
-                return log_warning_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since a newer boot loader version exists already.", to);
+        r = compare_version(a, b);
+        if (r < 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since newer boot loader version in place already.", to);
+        else if (r == 0)
+                return log_info_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since same boot loader version in place already.", to);
 
         return 0;
 }
@@ -665,6 +672,10 @@ static int install_binaries(const char *esp_path, bool force) {
                         continue;
 
                 k = copy_one_file(esp_path, de->d_name, force);
+                /* Don't propagate an error code if no update necessary, installed version already equal or
+                 * newer version, or other boot loader in place. */
+                if (arg_graceful && IN_SET(k, -ESTALE, -EREMOTE))
+                        continue;
                 if (k < 0 && r == 0)
                         r = k;
         }
@@ -907,7 +918,6 @@ static int remove_subdirs(const char *root, const char *const *subdirs) {
 
 static int remove_machine_id_directory(const char *root) {
         sd_id128_t machine_id;
-        char buf[SD_ID128_STRING_MAX];
         int r;
 
         assert(root);
@@ -920,7 +930,7 @@ static int remove_machine_id_directory(const char *root) {
         if (r < 0)
                 return log_error_errno(r, "Failed to get machine id: %m");
 
-        return rmdir_one(root, sd_id128_to_string(machine_id, buf));
+        return rmdir_one(root, SD_ID128_TO_STRING(machine_id));
 }
 
 static int remove_binaries(const char *esp_path) {
@@ -1009,8 +1019,6 @@ static int install_loader_config(const char *esp_path) {
         _cleanup_(unlink_and_freep) char *t = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_close_ int fd = -1;
-        sd_id128_t machine_id;
-        char machine_string[SD_ID128_STRING_MAX];
         const char *p;
         int r;
 
@@ -1030,12 +1038,15 @@ static int install_loader_config(const char *esp_path) {
 
         fprintf(f, "#timeout 3\n"
                    "#console-mode keep\n");
+
         if (arg_make_machine_id_directory) {
+                sd_id128_t machine_id;
+
                 r = sd_id128_get_machine(&machine_id);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get machine id: %m");
 
-                fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
+                fprintf(f, "default %s-*\n", SD_ID128_TO_STRING(machine_id));
         }
 
         r = fflush_sync_and_check(f);
@@ -1054,7 +1065,6 @@ static int install_loader_config(const char *esp_path) {
 
 static int install_machine_id_directory(const char *root) {
         sd_id128_t machine_id;
-        char buf[SD_ID128_STRING_MAX];
         int r;
 
         assert(root);
@@ -1067,7 +1077,7 @@ static int install_machine_id_directory(const char *root) {
         if (r < 0)
                 return log_error_errno(r, "Failed to get machine id: %m");
 
-        return mkdir_one(root, sd_id128_to_string(machine_id, buf));
+        return mkdir_one(root, SD_ID128_TO_STRING(machine_id));
 }
 
 static int help(int argc, char *argv[], void *userdata) {
@@ -1216,7 +1226,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         return 1;
@@ -1243,7 +1253,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
         sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL;
         int r, k;
 
-        r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &esp_uuid);
+        r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid);
         if (arg_print_esp_path) {
                 if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
                                    * error the find_esp_and_warn() won't log on its own) */
@@ -1254,7 +1264,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                 puts(arg_esp_path);
         }
 
-        r = acquire_xbootldr(geteuid() != 0, &xbootldr_uuid);
+        r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid);
         if (arg_print_dollar_boot_path) {
                 if (r == -EACCES)
                         return log_error_errno(r, "Failed to determine XBOOTLDR location: %m");
@@ -1402,13 +1412,13 @@ static int verb_list(int argc, char *argv[], void *userdata) {
          * off logging about access errors and turn off potentially privileged device probing. Here we're interested in
          * the latter but not the former, hence request the mode, and log about EACCES. */
 
-        r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, NULL);
+        r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, NULL);
         if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
                 return log_error_errno(r, "Failed to determine ESP: %m");
         if (r < 0)
                 return r;
 
-        r = acquire_xbootldr(geteuid() != 0, NULL);
+        r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL);
         if (r == -EACCES)
                 return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
         if (r < 0)
@@ -1600,21 +1610,24 @@ static int verb_install(int argc, char *argv[], void *userdata) {
         sd_id128_t uuid = SD_ID128_NULL;
         uint64_t pstart = 0, psize = 0;
         uint32_t part = 0;
-        bool install;
+        bool install, graceful;
         int r;
 
-        r = acquire_esp(false, &part, &pstart, &psize, &uuid);
+        install = streq(argv[0], "install");
+        graceful = !install && arg_graceful; /* support graceful mode for updates */
+
+        r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid);
+        if (graceful && r == -ENOKEY)
+                return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
         if (r < 0)
                 return r;
 
-        r = acquire_xbootldr(false, NULL);
+        r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL);
         if (r < 0)
                 return r;
 
         settle_make_machine_id_directory();
 
-        install = streq(argv[0], "install");
-
         RUN_WITH_UMASK(0002) {
                 if (install) {
                         /* Don't create any of these directories when we are just updating. When we update
@@ -1663,11 +1676,11 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
         sd_id128_t uuid = SD_ID128_NULL;
         int r, q;
 
-        r = acquire_esp(false, NULL, NULL, NULL, &uuid);
+        r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, &uuid);
         if (r < 0)
                 return r;
 
-        r = acquire_xbootldr(false, NULL);
+        r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL);
         if (r < 0)
                 return r;
 
@@ -1726,7 +1739,7 @@ static int verb_is_installed(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *p = NULL;
         int r;
 
-        r = acquire_esp(false, NULL, NULL, NULL, NULL);
+        r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL);
         if (r < 0)
                 return r;
 
diff --git a/src/boot/efi/assert.c b/src/boot/efi/assert.c
new file mode 100644 (file)
index 0000000..350b2b0
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#ifndef NDEBUG
+
+#include <efi.h>
+#include <efilib.h>
+#include "util.h"
+
+void efi_assert(const char *expr, const char *file, unsigned line, const char *function) {
+      log_error_stall(L"systemd-boot assertion '%a' failed at %a:%u, function %a(). Halting.", expr, file, line, function);
+      for (;;)
+            uefi_call_wrapper(BS->Stall, 1, 60 * 1000 * 1000);
+}
+
+#endif
index 3249171ec127ea8230a37fbbbc69a6cfcbc2ce20..ecd18f592d8431c380149a437846a8cdc35582e9 100644 (file)
@@ -5,7 +5,6 @@
 #include <efilib.h>
 
 #include "console.h"
-#include "crc32.h"
 #include "disk.h"
 #include "efi-loader-features.h"
 #include "graphics.h"
 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
 #endif
 
+#define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111)
+
 /* magic string to find in the binary image */
-static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
+static const char _used_ _section_(".sdmagic") magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
 
 enum loader_type {
         LOADER_UNDEFINED,
@@ -72,6 +73,9 @@ typedef struct {
 } Config;
 
 static VOID cursor_left(UINTN *cursor, UINTN *first) {
+        assert(cursor);
+        assert(first);
+
         if ((*cursor) > 0)
                 (*cursor)--;
         else if ((*first) > 0)
@@ -84,6 +88,9 @@ static VOID cursor_right(
                 UINTN x_max,
                 UINTN len) {
 
+        assert(cursor);
+        assert(first);
+
         if ((*cursor)+1 < x_max)
                 (*cursor)++;
         else if ((*first) + (*cursor) < len)
@@ -100,6 +107,8 @@ static BOOLEAN line_edit(
         UINTN size, len, first, cursor, clear;
         BOOLEAN exit, enter;
 
+        assert(line_out);
+
         if (!line_in)
                 line_in = L"";
         size = StrLen(line_in) + 1024;
@@ -108,8 +117,6 @@ static BOOLEAN line_edit(
         len = StrLen(line);
         print = AllocatePool((x_max+1) * sizeof(CHAR16));
 
-        uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
-
         first = 0;
         cursor = 0;
         clear = 0;
@@ -119,24 +126,29 @@ static BOOLEAN line_edit(
                 EFI_STATUS err;
                 UINT64 key;
                 UINTN j;
+                UINTN cursor_color = TEXT_ATTR_SWAP(COLOR_EDIT);
 
-                j = len - first;
-                if (j >= x_max-1)
-                        j = x_max-1;
+                j = MIN(len - first, x_max);
                 CopyMem(print, line + first, j * sizeof(CHAR16));
-                while (clear > 0 && j < x_max-1) {
+                while (clear > 0 && j < x_max) {
                         clear--;
                         print[j++] = ' ';
                 }
                 print[j] = '\0';
 
-                uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_pos);
-                uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
-                uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
+                /* See comment at edit_line() call site for why we start at 1. */
+                print_at(1, y_pos, COLOR_EDIT, print);
 
-                err = console_key_read(&key, TRUE);
-                if (EFI_ERROR(err))
-                        continue;
+                if (!print[cursor])
+                        print[cursor] = ' ';
+                print[cursor+1] = '\0';
+                do {
+                        print_at(cursor + 1, y_pos, cursor_color, print + cursor);
+                        cursor_color = TEXT_ATTR_SWAP(cursor_color);
+
+                        err = console_key_read(&key, 750 * 1000);
+                        print_at(cursor + 1, y_pos, COLOR_EDIT, print + cursor);
+                } while (EFI_ERROR(err));
 
                 switch (key) {
                 case KEYPRESS(0, SCAN_ESC, 0):
@@ -174,7 +186,6 @@ static BOOLEAN line_edit(
                                 cursor_right(&cursor, &first, x_max, len);
                         while (line[first + cursor] && line[first + cursor] != ' ')
                                 cursor_right(&cursor, &first, x_max, len);
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
                         continue;
 
                 case KEYPRESS(0, SCAN_UP, 0):
@@ -188,7 +199,6 @@ static BOOLEAN line_edit(
                         }
                         while ((first + cursor) > 0 && line[first + cursor-1] != ' ')
                                 cursor_left(&cursor, &first);
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
                         continue;
 
                 case KEYPRESS(0, SCAN_RIGHT, 0):
@@ -198,7 +208,6 @@ static BOOLEAN line_edit(
                         if (first + cursor == len)
                                 continue;
                         cursor_right(&cursor, &first, x_max, len);
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
                         continue;
 
                 case KEYPRESS(0, SCAN_LEFT, 0):
@@ -206,7 +215,6 @@ static BOOLEAN line_edit(
                 case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('b')):
                         /* backward-char */
                         cursor_left(&cursor, &first);
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
                         continue;
 
                 case KEYPRESS(EFI_ALT_PRESSED, 0, 'd'):
@@ -242,7 +250,6 @@ static BOOLEAN line_edit(
                                 cursor_left(&cursor, &first);
                                 clear++;
                         }
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
 
                         for (UINTN i = first + cursor; i + clear < len; i++)
                                 line[i] = line[i + clear];
@@ -327,11 +334,12 @@ static BOOLEAN line_edit(
                 }
         }
 
-        uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
         return enter;
 }
 
 static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
+        assert(config);
+
         if (key == 0)
                 return -1;
 
@@ -362,7 +370,10 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
         _cleanup_freepool_ CHAR16 *partstr = NULL, *defaultstr = NULL;
         UINTN x, y;
 
-        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+        assert(config);
+        assert(loaded_image_path);
+
+        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, COLOR_NORMAL);
         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
 
         Print(L"systemd-boot version:   " GIT_VERSION "\n");
@@ -387,7 +398,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
                 Print(L"OsIndicationsSupported: %d\n", indvar);
 
         Print(L"\n--- press key ---\n\n");
-        console_key_read(&key, TRUE);
+        console_key_read(&key, 0);
 
         Print(L"timeout:                %u\n", config->timeout_sec);
         if (config->timeout_sec_efivar >= 0)
@@ -432,7 +443,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
                 Print(L"LoaderEntryDefault:     %s\n", defaultstr);
 
         Print(L"\n--- press key ---\n\n");
-        console_key_read(&key, TRUE);
+        console_key_read(&key, 0);
 
         for (UINTN i = 0; i < config->entry_count; i++) {
                 ConfigEntry *entry;
@@ -482,7 +493,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
                               entry->path, entry->next_name);
 
                 Print(L"\n--- press key ---\n\n");
-                console_key_read(&key, TRUE);
+                console_key_read(&key, 0);
         }
 
         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@@ -493,41 +504,45 @@ static BOOLEAN menu_run(
                 ConfigEntry **chosen_entry,
                 CHAR16 *loaded_image_path) {
 
+        assert(config);
+        assert(chosen_entry);
+        assert(loaded_image_path);
+
         EFI_STATUS err;
         UINTN visible_max;
-        UINTN idx_highlight;
-        UINTN idx_highlight_prev;
+        UINTN idx_highlight = config->idx_default;
+        UINTN idx_highlight_prev = 0;
         UINTN idx_first;
         UINTN idx_last;
-        BOOLEAN refresh;
-        BOOLEAN highlight;
-        UINTN line_width;
+        BOOLEAN refresh = TRUE;
+        BOOLEAN highlight = FALSE;
+        UINTN line_width = 0;
+        UINTN entry_padding = 3;
         CHAR16 **lines;
         UINTN x_start;
         UINTN y_start;
         UINTN x_max;
         UINTN y_max;
-        CHAR16 *status;
+        CHAR16 *status = NULL;
         CHAR16 *clearline;
-        INTN timeout_remain;
+        UINTN timeout_remain = config->timeout_sec;
         INT16 idx;
         BOOLEAN exit = FALSE;
         BOOLEAN run = TRUE;
-        BOOLEAN wait = FALSE;
 
         graphics_mode(FALSE);
         uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
         uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE);
-        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
+        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, COLOR_NORMAL);
 
         /* draw a single character to make ClearScreen work on some firmware */
-        uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*) L" ");
+        Print(L" ");
 
         if (config->console_mode_change != CONSOLE_MODE_KEEP) {
                 err = console_set_mode(&config->console_mode, config->console_mode_change);
                 if (EFI_ERROR(err)) {
                         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
-                        Print(L"Error switching console mode to %ld: %r.\r", (UINT64)config->console_mode, err);
+                        log_error_stall(L"Error switching console mode to %lu: %r", (UINT64)config->console_mode, err);
                 }
         } else
                 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@@ -538,38 +553,24 @@ static BOOLEAN menu_run(
                 y_max = 25;
         }
 
-        /* we check 10 times per second for a keystroke */
-        if (config->timeout_sec > 0)
-                timeout_remain = config->timeout_sec * 10;
-        else
-                timeout_remain = -1;
-
-        idx_highlight = config->idx_default;
-        idx_highlight_prev = 0;
-
         visible_max = y_max - 2;
 
-        if ((UINTN)config->idx_default >= visible_max)
-                idx_first = config->idx_default-1;
-        else
+        /* Drawing entries starts at idx_first until idx_last. We want to make
+         * sure that idx_highlight is centered, but not if we are close to the
+         * beginning/end of the entry list. Otherwise we would have a half-empty
+         * screen. */
+        if (config->entry_count <= visible_max || idx_highlight <= visible_max / 2)
                 idx_first = 0;
-
-        idx_last = idx_first + visible_max-1;
-
-        refresh = TRUE;
-        highlight = FALSE;
+        else if (idx_highlight >= config->entry_count - (visible_max / 2))
+                idx_first = config->entry_count - visible_max;
+        else
+                idx_first = idx_highlight - (visible_max / 2);
+        idx_last = idx_first + visible_max - 1;
 
         /* length of the longest entry */
-        line_width = 5;
-        for (UINTN i = 0; i < config->entry_count; i++) {
-                UINTN entry_len;
-
-                entry_len = StrLen(config->entries[i]->title_show);
-                if (line_width < entry_len)
-                        line_width = entry_len;
-        }
-        if (line_width > x_max-6)
-                line_width = x_max-6;
+        for (UINTN i = 0; i < config->entry_count; i++)
+                line_width = MAX(line_width, StrLen(config->entries[i]->title_show));
+        line_width = MIN(line_width + 2 * entry_padding, x_max);
 
         /* offsets to center the entries on the screen */
         x_start = (x_max - (line_width)) / 2;
@@ -583,19 +584,20 @@ static BOOLEAN menu_run(
         for (UINTN i = 0; i < config->entry_count; i++) {
                 UINTN j;
 
-                lines[i] = AllocatePool(((x_max+1) * sizeof(CHAR16)));
-                for (j = 0; j < x_start; j++)
+                lines[i] = AllocatePool(((line_width + 1) * sizeof(CHAR16)));
+                UINTN padding = (line_width - MIN(StrLen(config->entries[i]->title_show), line_width)) / 2;
+
+                for (j = 0; j < padding; j++)
                         lines[i][j] = ' ';
 
-                for (UINTN k = 0; config->entries[i]->title_show[k] != '\0' && j < x_max; j++, k++)
+                for (UINTN k = 0; config->entries[i]->title_show[k] != '\0' && j < line_width; j++, k++)
                         lines[i][j] = config->entries[i]->title_show[k];
 
-                for (; j < x_max; j++)
+                for (; j < line_width; j++)
                         lines[i][j] = ' ';
-                lines[i][x_max] = '\0';
+                lines[i][line_width] = '\0';
         }
 
-        status = NULL;
         clearline = AllocatePool((x_max+1) * sizeof(CHAR16));
         for (UINTN i = 0; i < x_max; i++)
                 clearline[i] = ' ';
@@ -608,42 +610,28 @@ static BOOLEAN menu_run(
                         for (UINTN i = 0; i < config->entry_count; i++) {
                                 if (i < idx_first || i > idx_last)
                                         continue;
-                                uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + i - idx_first);
-                                if (i == idx_highlight)
-                                        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
-                                                          EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
-                                else
-                                        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
-                                                          EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
-                                uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[i]);
-                                if ((INTN)i == config->idx_default_efivar) {
-                                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + i - idx_first);
-                                        uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*) L"=>");
-                                }
+                                print_at(x_start, y_start + i - idx_first,
+                                         (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
+                                         lines[i]);
+                                if ((INTN)i == config->idx_default_efivar)
+                                        print_at(x_start, y_start + i - idx_first,
+                                                 (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY,
+                                                 (CHAR16*) L"=>");
                         }
                         refresh = FALSE;
                 } else if (highlight) {
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight_prev - idx_first);
-                        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
-                        uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight_prev]);
-                        if ((INTN)idx_highlight_prev == config->idx_default_efivar) {
-                                uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight_prev - idx_first);
-                                uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*) L"=>");
-                        }
-
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_start + idx_highlight - idx_first);
-                        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_BLACK|EFI_BACKGROUND_LIGHTGRAY);
-                        uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, lines[idx_highlight]);
-                        if ((INTN)idx_highlight == config->idx_default_efivar) {
-                                uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x_start-3, y_start + idx_highlight - idx_first);
-                                uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*) L"=>");
-                        }
+                        print_at(x_start, y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, lines[idx_highlight_prev]);
+                        print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, lines[idx_highlight]);
+                        if ((INTN)idx_highlight_prev == config->idx_default_efivar)
+                                print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) L"=>");
+                        if ((INTN)idx_highlight == config->idx_default_efivar)
+                                print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) L"=>");
                         highlight = FALSE;
                 }
 
                 if (timeout_remain > 0) {
                         FreePool(status);
-                        status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10);
+                        status = PoolPrint(L"Boot in %d s.", timeout_remain);
                 }
 
                 /* print status at last line of screen */
@@ -657,42 +645,29 @@ static BOOLEAN menu_run(
                                 x = (x_max - len) / 2;
                         else
                                 x = 0;
-                        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
-                        uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline + (x_max - x));
+                        print_at(0, y_max - 1, COLOR_NORMAL, clearline + (x_max - x));
                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, status);
                         uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
                 }
 
-                err = console_key_read(&key, wait);
-                if (EFI_ERROR(err)) {
-                        /* timeout reached */
+                err = console_key_read(&key, timeout_remain > 0 ? 1000 * 1000 : 0);
+                if (err == EFI_TIMEOUT) {
+                        timeout_remain--;
                         if (timeout_remain == 0) {
                                 exit = TRUE;
                                 break;
                         }
 
-                        /* sleep and update status */
-                        if (timeout_remain > 0) {
-                                uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
-                                timeout_remain--;
-                                continue;
-                        }
-
-                        /* timeout disabled, wait for next key */
-                        wait = TRUE;
+                        /* update status */
                         continue;
-                }
-
-                timeout_remain = -1;
+                } else
+                        timeout_remain = 0;
 
                 /* clear status after keystroke */
                 if (status) {
                         FreePool(status);
                         status = NULL;
-                        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
-                        uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+                        print_at(0, y_max - 1, COLOR_NORMAL, clearline + 1);
                 }
 
                 idx_highlight_prev = idx_highlight;
@@ -700,12 +675,14 @@ static BOOLEAN menu_run(
                 switch (key) {
                 case KEYPRESS(0, SCAN_UP, 0):
                 case KEYPRESS(0, 0, 'k'):
+                case KEYPRESS(0, 0, 'K'):
                         if (idx_highlight > 0)
                                 idx_highlight--;
                         break;
 
                 case KEYPRESS(0, SCAN_DOWN, 0):
                 case KEYPRESS(0, 0, 'j'):
+                case KEYPRESS(0, 0, 'J'):
                         if (idx_highlight < config->entry_count-1)
                                 idx_highlight++;
                         break;
@@ -749,8 +726,10 @@ static BOOLEAN menu_run(
 
                 case KEYPRESS(0, SCAN_F1, 0):
                 case KEYPRESS(0, 0, 'h'):
+                case KEYPRESS(0, 0, 'H'):
                 case KEYPRESS(0, 0, '?'):
-                        status = StrDuplicate(L"(d)efault, (t/T)timeout, (e)dit, (v)ersion (Q)uit (P)rint (h)elp");
+                        /* This must stay below 80 characters! Q/v/Ctrl+l deliberately not advertised. */
+                        status = StrDuplicate(L"(d)efault (t/T)timeout (e)dit (p)rint (h)elp");
                         break;
 
                 case KEYPRESS(0, 0, 'Q'):
@@ -759,6 +738,7 @@ static BOOLEAN menu_run(
                         break;
 
                 case KEYPRESS(0, 0, 'd'):
+                case KEYPRESS(0, 0, 'D'):
                         if (config->idx_default_efivar != (INTN)idx_highlight) {
                                 /* store the selected entry in a persistent EFI variable */
                                 efivar_set(
@@ -787,7 +767,7 @@ static BOOLEAN menu_run(
                                         config->timeout_sec_efivar,
                                         EFI_VARIABLE_NON_VOLATILE);
                                 if (config->timeout_sec_efivar > 0)
-                                        status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar);
+                                        status = PoolPrint(L"Menu timeout set to %d s.", config->timeout_sec_efivar);
                                 else
                                         status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
                         } else if (config->timeout_sec_efivar <= 0){
@@ -795,7 +775,7 @@ static BOOLEAN menu_run(
                                 efivar_set(
                                         LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
                                 if (config->timeout_sec_config > 0)
-                                        status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.",
+                                        status = PoolPrint(L"Menu timeout of %d s is defined by configuration file.",
                                                            config->timeout_sec_config);
                                 else
                                         status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
@@ -813,23 +793,25 @@ static BOOLEAN menu_run(
                                 config->timeout_sec_efivar,
                                 EFI_VARIABLE_NON_VOLATILE);
                         if (config->timeout_sec_efivar > 0)
-                                status = PoolPrint(L"Menu timeout set to %d sec.",
+                                status = PoolPrint(L"Menu timeout set to %d s.",
                                                    config->timeout_sec_efivar);
                         else
                                 status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
                         break;
 
                 case KEYPRESS(0, 0, 'e'):
+                case KEYPRESS(0, 0, 'E'):
                         /* only the options of configured entries can be edited */
                         if (!config->editor || config->entries[idx_highlight]->type == LOADER_UNDEFINED)
                                 break;
-                        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
-                        uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
-                        if (line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-1, y_max-1))
-                                exit = TRUE;
-                        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, y_max-1);
-                        uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1);
+                        /* The edit line may end up on the last line of the screen. And even though we're
+                         * not telling the firmware to advance the line, it still does in this one case,
+                         * causing a scroll to happen that screws with our beautiful boot loader output.
+                         * Since we cannot paint the last character of the edit line, we simply start
+                         * at x-offset 1 for symmetry. */
+                        print_at(1, y_max - 1, COLOR_EDIT, clearline + 2);
+                        exit = line_edit(config->entries[idx_highlight]->options, &config->options_edit, x_max-2, y_max-1);
+                        print_at(1, y_max - 1, COLOR_NORMAL, clearline + 2);
                         break;
 
                 case KEYPRESS(0, 0, 'v'):
@@ -838,6 +820,7 @@ static BOOLEAN menu_run(
                                            ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
                         break;
 
+                case KEYPRESS(0, 0, 'p'):
                 case KEYPRESS(0, 0, 'P'):
                         print_status(config, loaded_image_path);
                         refresh = TRUE;
@@ -878,12 +861,15 @@ static BOOLEAN menu_run(
         FreePool(lines);
         FreePool(clearline);
 
-        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_WHITE|EFI_BACKGROUND_BLACK);
+        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, COLOR_NORMAL);
         uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
         return run;
 }
 
 static VOID config_add_entry(Config *config, ConfigEntry *entry) {
+        assert(config);
+        assert(entry);
+
         if ((config->entry_count & 15) == 0) {
                 UINTN i;
 
@@ -924,6 +910,12 @@ static CHAR8 *line_get_key_value(
         CHAR8 *line, *value;
         UINTN linelen;
 
+        assert(content);
+        assert(sep);
+        assert(pos);
+        assert(key_ret);
+        assert(value_ret);
+
 skip:
         line = content + *pos;
         if (*line == '\0')
@@ -985,6 +977,10 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
         CHAR8 *line;
         UINTN pos = 0;
         CHAR8 *key, *value;
+        EFI_STATUS err;
+
+        assert(config);
+        assert(content);
 
         while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
                 if (strcmpa((CHAR8 *)"timeout", key) == 0) {
@@ -1004,32 +1000,23 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
                 }
 
                 if (strcmpa((CHAR8 *)"editor", key) == 0) {
-                        BOOLEAN on;
-
-                        if (EFI_ERROR(parse_boolean(value, &on)))
-                                continue;
-
-                        config->editor = on;
+                        err = parse_boolean(value, &config->editor);
+                        if (EFI_ERROR(err))
+                                log_error_stall(L"Error parsing 'editor' config option: %a", value);
                         continue;
                 }
 
                 if (strcmpa((CHAR8 *)"auto-entries", key) == 0) {
-                        BOOLEAN on;
-
-                        if (EFI_ERROR(parse_boolean(value, &on)))
-                                continue;
-
-                        config->auto_entries = on;
+                        err = parse_boolean(value, &config->auto_entries);
+                        if (EFI_ERROR(err))
+                                log_error_stall(L"Error parsing 'auto-entries' config option: %a", value);
                         continue;
                 }
 
                 if (strcmpa((CHAR8 *)"auto-firmware", key) == 0) {
-                        BOOLEAN on;
-
-                        if (EFI_ERROR(parse_boolean(value, &on)))
-                                continue;
-
-                        config->auto_firmware = on;
+                        err = parse_boolean(value, &config->auto_firmware);
+                        if (EFI_ERROR(err))
+                                log_error_stall(L"Error parsing 'auto-firmware' config option: %a", value);
                         continue;
                 }
 
@@ -1061,8 +1048,11 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
                         else {
                                 BOOLEAN on;
 
-                                if (EFI_ERROR(parse_boolean(value, &on)))
+                                err = parse_boolean(value, &on);
+                                if (EFI_ERROR(err)) {
+                                        log_error_stall(L"Error parsing 'random-seed-mode' config option: %a", value);
                                         continue;
+                                }
 
                                 config->random_seed_mode = on ? RANDOM_SEED_ALWAYS : RANDOM_SEED_OFF;
                         }
@@ -1079,6 +1069,10 @@ static VOID config_entry_parse_tries(
         UINTN left = UINTN_MAX, done = UINTN_MAX, factor = 1, i, next_left, next_done;
         _cleanup_freepool_ CHAR16 *prefix = NULL;
 
+        assert(entry);
+        assert(path);
+        assert(file);
+
         /*
          * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
          * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
@@ -1196,6 +1190,9 @@ static VOID config_entry_bump_counters(
         UINTN file_info_size, a, b;
         EFI_STATUS r;
 
+        assert(entry);
+        assert(root_dir);
+
         if (entry->tries_left == UINTN_MAX)
                 return;
 
@@ -1221,8 +1218,7 @@ static VOID config_entry_bump_counters(
                         break;
 
                 if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) {
-                        Print(L"\nFailed to get file info for '%s': %r\n", old_path, r);
-                        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+                        log_error_stall(L"Failed to get file info for '%s': %r", old_path, r);
                         return;
                 }
 
@@ -1234,8 +1230,7 @@ static VOID config_entry_bump_counters(
         StrCpy(file_info->FileName, entry->next_name);
         r = uefi_call_wrapper(handle->SetInfo, 4, handle, &EfiFileInfoGuid, file_info_size, file_info);
         if (EFI_ERROR(r)) {
-                Print(L"\nFailed to rename '%s' to '%s', ignoring: %r\n", old_path, entry->next_name, r);
-                uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+                log_error_stall(L"Failed to rename '%s' to '%s', ignoring: %r", old_path, entry->next_name, r);
                 return;
         }
 
@@ -1271,6 +1266,14 @@ static VOID config_entry_add_from_file(
         EFI_FILE_HANDLE handle;
         _cleanup_freepool_ CHAR16 *initrd = NULL;
 
+        assert(config);
+        assert(device);
+        assert(root_dir);
+        assert(path);
+        assert(file);
+        assert(content);
+        assert(loaded_image_path);
+
         entry = AllocatePool(sizeof(ConfigEntry));
 
         *entry = (ConfigEntry) {
@@ -1399,11 +1402,14 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
         UINTN sec;
         EFI_STATUS err;
 
+        assert(root_dir);
+
         *config = (Config) {
                 .editor = TRUE,
                 .auto_entries = TRUE,
                 .auto_firmware = TRUE,
                 .random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN,
+                .idx_default_efivar = -1,
         };
 
         err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
@@ -1436,6 +1442,11 @@ static VOID config_load_entries(
         EFI_FILE_HANDLE entries_dir;
         EFI_STATUS err;
 
+        assert(config);
+        assert(device);
+        assert(root_dir);
+        assert(loaded_image_path);
+
         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, (CHAR16*) L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
         if (!EFI_ERROR(err)) {
                 for (;;) {
@@ -1471,6 +1482,9 @@ static VOID config_load_entries(
 static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
         INTN r;
 
+        assert(a);
+        assert(b);
+
         /* Order entries that have no tries left to the beginning of the list */
         if (a->tries_left != 0 && b->tries_left == 0)
                 return 1;
@@ -1501,6 +1515,8 @@ static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
 }
 
 static VOID config_sort_entries(Config *config) {
+        assert(config);
+
         for (UINTN i = 1; i < config->entry_count; i++) {
                 BOOLEAN more;
 
@@ -1522,6 +1538,9 @@ static VOID config_sort_entries(Config *config) {
 }
 
 static INTN config_entry_find(Config *config, CHAR16 *id) {
+        assert(config);
+        assert(id);
+
         for (UINTN i = 0; i < config->entry_count; i++)
                 if (StrCmp(config->entries[i]->id, id) == 0)
                         return (INTN) i;
@@ -1530,21 +1549,21 @@ static INTN config_entry_find(Config *config, CHAR16 *id) {
 }
 
 static VOID config_default_entry_select(Config *config) {
-        _cleanup_freepool_ CHAR16 *entry_oneshot = NULL, *entry_default = NULL;
+        _cleanup_freepool_ CHAR16 *entry_default = NULL;
         EFI_STATUS err;
         INTN i;
 
+        assert(config);
+
         /*
          * The EFI variable to specify a boot entry for the next, and only the
          * next reboot. The variable is always cleared directly after it is read.
          */
-        err = efivar_get(LOADER_GUID, L"LoaderEntryOneShot", &entry_oneshot);
+        err = efivar_get(LOADER_GUID, L"LoaderEntryOneShot", &config->entry_oneshot);
         if (!EFI_ERROR(err)) {
-
-                config->entry_oneshot = StrDuplicate(entry_oneshot);
                 efivar_set(LOADER_GUID, L"LoaderEntryOneShot", NULL, EFI_VARIABLE_NON_VOLATILE);
 
-                i = config_entry_find(config, entry_oneshot);
+                i = config_entry_find(config, config->entry_oneshot);
                 if (i >= 0) {
                         config->idx_default = i;
                         return;
@@ -1554,12 +1573,10 @@ static VOID config_default_entry_select(Config *config) {
         /*
          * The EFI variable to select the default boot entry overrides the
          * configured pattern. The variable can be set and cleared by pressing
-         * the 'd' key in the loader selection menu, the entry is marked with
-         * an '*'.
+         * the 'd' key in the loader selection menu.
          */
         err = efivar_get(LOADER_GUID, L"LoaderEntryDefault", &entry_default);
         if (!EFI_ERROR(err)) {
-
                 i = config_entry_find(config, entry_default);
                 if (i >= 0) {
                         config->idx_default = i;
@@ -1567,7 +1584,6 @@ static VOID config_default_entry_select(Config *config) {
                         return;
                 }
         }
-        config->idx_default_efivar = -1;
 
         if (config->entry_count == 0)
                 return;
@@ -1579,8 +1595,6 @@ static VOID config_default_entry_select(Config *config) {
         if (config->entry_default_pattern) {
                 i = config->entry_count;
                 while (i--) {
-                        if (config->entries[i]->no_autoselect)
-                                continue;
                         if (MetaiMatch(config->entries[i]->id, config->entry_default_pattern)) {
                                 config->idx_default = i;
                                 return;
@@ -1604,6 +1618,8 @@ static VOID config_default_entry_select(Config *config) {
 static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
         BOOLEAN non_unique = FALSE;
 
+        assert(entries);
+
         for (UINTN i = 0; i < entry_count; i++)
                 entries[i]->non_unique = FALSE;
 
@@ -1622,6 +1638,8 @@ static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
 
 /* generate a unique title, avoiding non-distinguishable menu entries */
 static VOID config_title_generate(Config *config) {
+        assert(config);
+
         /* set title */
         for (UINTN i = 0; i < config->entry_count; i++) {
                 CHAR16 *title;
@@ -1694,6 +1712,11 @@ static BOOLEAN config_entry_add_call(
 
         ConfigEntry *entry;
 
+        assert(config);
+        assert(id);
+        assert(title);
+        assert(call);
+
         entry = AllocatePool(sizeof(ConfigEntry));
         *entry = (ConfigEntry) {
                 .id = StrDuplicate(id),
@@ -1720,11 +1743,17 @@ static ConfigEntry *config_entry_add_loader(
 
         ConfigEntry *entry;
 
+        assert(config);
+        assert(device);
+        assert(id);
+        assert(title);
+        assert(loader);
+
         entry = AllocatePool(sizeof(ConfigEntry));
         *entry = (ConfigEntry) {
                 .type = type,
                 .title = StrDuplicate(title),
-                .version = StrDuplicate(version),
+                .version = version ? StrDuplicate(version) : NULL,
                 .device = device,
                 .loader = StrDuplicate(loader),
                 .id = StrDuplicate(id),
@@ -1739,6 +1768,29 @@ static ConfigEntry *config_entry_add_loader(
         return entry;
 }
 
+static BOOLEAN is_sd_boot(EFI_FILE *root_dir, const CHAR16 *loader_path) {
+        EFI_STATUS err;
+        const CHAR8 *sections[] = {
+                (CHAR8 *)".sdmagic",
+                NULL
+        };
+        UINTN offset = 0, size = 0, read;
+        _cleanup_freepool_ CHAR8 *content = NULL;
+
+        assert(root_dir);
+        assert(loader_path);
+
+        err = pe_file_locate_sections(root_dir, loader_path, sections, &offset, &size);
+        if (EFI_ERROR(err) || size != sizeof(magic))
+                return FALSE;
+
+        err = file_read(root_dir, loader_path, offset, size, &content, &read);
+        if (EFI_ERROR(err) || size != read)
+                return FALSE;
+
+        return CompareMem(content, magic, sizeof(magic)) == 0;
+}
+
 static BOOLEAN config_entry_add_loader_auto(
                 Config *config,
                 EFI_HANDLE *device,
@@ -1753,23 +1805,28 @@ static BOOLEAN config_entry_add_loader_auto(
         ConfigEntry *entry;
         EFI_STATUS err;
 
+        assert(config);
+        assert(device);
+        assert(root_dir);
+        assert(id);
+        assert(title);
+        assert(loader || loaded_image_path);
+
         if (!config->auto_entries)
                 return FALSE;
 
-        /* do not add an entry for ourselves */
         if (loaded_image_path) {
-                UINTN len;
-                _cleanup_freepool_ CHAR8 *content = NULL;
-
-                if (StriCmp(loader, loaded_image_path) == 0)
+                loader = L"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi";
+
+                /* We are trying to add the default EFI loader here,
+                 * but we do not want to do that if that would be us.
+                 *
+                 * If the default loader is not us, it might be shim. It would
+                 * chainload GRUBX64.EFI in that case, which might be us.*/
+                if (StriCmp(loader, loaded_image_path) == 0 ||
+                    is_sd_boot(root_dir, loader) ||
+                    is_sd_boot(root_dir, L"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L".EFI"))
                         return FALSE;
-
-                /* look for systemd-boot magic string */
-                err = file_read(root_dir, loader, 0, 100*1024, &content, &len);
-                if (!EFI_ERROR(err))
-                        for (CHAR8 *start = content; start <= content + len - sizeof(magic) - 1; start++)
-                                if (start[0] == magic[0] && CompareMem(start, magic, sizeof(magic) - 1) == 0)
-                                        return FALSE;
         }
 
         /* check existence */
@@ -1793,6 +1850,8 @@ static VOID config_entry_add_osx(Config *config) {
         UINTN handle_count = 0;
         _cleanup_freepool_ EFI_HANDLE *handles = NULL;
 
+        assert(config);
+
         if (!config->auto_entries)
                 return;
 
@@ -1814,6 +1873,55 @@ static VOID config_entry_add_osx(Config *config) {
         }
 }
 
+static VOID config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir) {
+        _cleanup_freepool_ CHAR8 *bcd = NULL;
+        const CHAR16 *title = NULL;
+        EFI_STATUS err;
+        UINTN len;
+
+        assert(config);
+        assert(device);
+        assert(root_dir);
+
+        if (!config->auto_entries)
+                return;
+
+        /* Try to find a better title. */
+        err = file_read(root_dir, L"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd, &len);
+        if (!EFI_ERROR(err)) {
+                static const CHAR16 *versions[] = {
+                        L"Windows 11",
+                        L"Windows 10",
+                        L"Windows 8.1",
+                        L"Windows 8",
+                        L"Windows 7",
+                        L"Windows Vista",
+                };
+
+                CHAR8 *p = bcd;
+                while (!title) {
+                        CHAR8 *q = mempmem_safe(p, len, versions[0], STRLEN(L"Windows "));
+                        if (!q)
+                                break;
+
+                        len -= q - p;
+                        p = q;
+
+                        /* We found the prefix, now try all the version strings. */
+                        for (UINTN i = 0; i < ELEMENTSOF(versions); i++) {
+                                if (memory_startswith(p, len, versions[i] + STRLEN("Windows "))) {
+                                        title = versions[i];
+                                        break;
+                                }
+                        }
+                }
+        }
+
+        config_entry_add_loader_auto(config, device, root_dir, NULL,
+                                     L"auto-windows", 'w', title ?: L"Windows Boot Manager",
+                                     L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
+}
+
 static VOID config_entry_add_linux(
                 Config *config,
                 EFI_HANDLE *device,
@@ -1823,6 +1931,10 @@ static VOID config_entry_add_linux(
         EFI_STATUS err;
         ConfigEntry *entry;
 
+        assert(config);
+        assert(device);
+        assert(root_dir);
+
         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, (CHAR16*) L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
         if (EFI_ERROR(err))
                 return;
@@ -1831,14 +1943,13 @@ static VOID config_entry_add_linux(
                 CHAR16 buf[256];
                 UINTN bufsize = sizeof buf;
                 EFI_FILE_INFO *f;
-                CHAR8 *sections[] = {
+                const CHAR8 *sections[] = {
                         (CHAR8 *)".osrel",
                         (CHAR8 *)".cmdline",
                         NULL
                 };
                 UINTN offs[ELEMENTSOF(sections)-1] = {};
                 UINTN szs[ELEMENTSOF(sections)-1] = {};
-                UINTN addrs[ELEMENTSOF(sections)-1] = {};
                 CHAR8 *content = NULL;
                 CHAR8 *line;
                 UINTN pos = 0;
@@ -1865,7 +1976,7 @@ static VOID config_entry_add_linux(
                         continue;
 
                 /* look for .osrel and .cmdline sections in the .efi binary */
-                err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
+                err = pe_file_locate_sections(linux_dir, f->FileName, sections, offs, szs);
                 if (EFI_ERROR(err))
                         continue;
 
@@ -1957,6 +2068,9 @@ static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node
         EFI_DEVICE_PATH *parent;
         UINTN len;
 
+        assert(path);
+        assert(node);
+
         len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
         parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH));
         CopyMem(parent, path, len);
@@ -1978,6 +2092,9 @@ static VOID config_load_xbootldr(
         EFI_FILE *root_dir;
         EFI_STATUS r;
 
+        assert(config);
+        assert(device);
+
         partition_path = DevicePathFromHandle(device);
         if (!partition_path)
                 return;
@@ -2018,10 +2135,10 @@ static VOID config_load_xbootldr(
                                 EFI_PARTITION_TABLE_HEADER gpt_header;
                                 uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512];
                         } gpt_header_buffer;
-                        const EFI_PARTITION_TABLE_HEADER *h = &gpt_header_buffer.gpt_header;
+                        EFI_PARTITION_TABLE_HEADER *h = &gpt_header_buffer.gpt_header;
                         UINT64 where;
                         UINTN sz;
-                        UINT32 c;
+                        UINT32 crc32, crc32_saved;
 
                         if (nr == 0)
                                 /* Read the first copy at LBA 1 */
@@ -2040,8 +2157,7 @@ static VOID config_load_xbootldr(
                                 continue;
 
                         /* Some superficial validation of the GPT header */
-                        c = CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature));
-                        if (c != 0)
+                        if(CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature) != 0))
                                 continue;
 
                         if (h->Header.HeaderSize < 92 ||
@@ -2052,12 +2168,11 @@ static VOID config_load_xbootldr(
                                 continue;
 
                         /* Calculate CRC check */
-                        c = ~crc32_exclude_offset(UINT32_MAX,
-                                                  (const UINT8*) &gpt_header_buffer,
-                                                  h->Header.HeaderSize,
-                                                  OFFSETOF(EFI_PARTITION_TABLE_HEADER, Header.CRC32),
-                                                  sizeof(h->Header.CRC32));
-                        if (c != h->Header.CRC32)
+                        crc32_saved = h->Header.CRC32;
+                        h->Header.CRC32 = 0;
+                        r = BS->CalculateCrc32(&gpt_header_buffer, h->Header.HeaderSize, &crc32);
+                        h->Header.CRC32 = crc32_saved;
+                        if (EFI_ERROR(r) || crc32 != crc32_saved)
                                 continue;
 
                         if (h->MyLBA != where)
@@ -2086,8 +2201,8 @@ static VOID config_load_xbootldr(
                                 continue;
 
                         /* Calculate CRC of entries array, too */
-                        c = ~crc32(UINT32_MAX, entries, sz);
-                        if (c != h->PartitionEntryArrayCRC32)
+                        r = BS->CalculateCrc32(&entries, sz, &crc32);
+                        if (EFI_ERROR(r) || crc32 != h->PartitionEntryArrayCRC32)
                                 continue;
 
                         for (UINTN i = 0; i < h->NumberOfPartitionEntries; i++) {
@@ -2164,19 +2279,16 @@ static EFI_STATUS image_start(
         CHAR16 *options;
         EFI_STATUS err;
 
+        assert(config);
+        assert(entry);
+
         path = FileDevicePath(entry->device, entry->loader);
-        if (!path) {
-                Print(L"Error getting device path.");
-                uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
-                return EFI_INVALID_PARAMETER;
-        }
+        if (!path)
+                return log_error_status_stall(EFI_INVALID_PARAMETER, L"Error getting device path.");
 
         err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
-        if (EFI_ERROR(err)) {
-                Print(L"Error loading %s: %r", entry->loader, err);
-                uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
-                return err;
-        }
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
 
         if (config->options_edit)
                 options = config->options_edit;
@@ -2190,22 +2302,19 @@ static EFI_STATUS image_start(
                 err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
                                         parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
                 if (EFI_ERROR(err)) {
-                        Print(L"Error getting LoadedImageProtocol handle: %r", err);
-                        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+                        log_error_stall(L"Error getting LoadedImageProtocol handle: %r", err);
                         goto out_unload;
                 }
                 loaded_image->LoadOptions = options;
-                loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16);
+                loaded_image->LoadOptionsSize = StrSize(loaded_image->LoadOptions);
 
 #if ENABLE_TPM
                 /* Try to log any options to the TPM, especially to catch manually edited options */
                 err = tpm_log_event(SD_TPM_PCR,
                                     (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
                                     loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
-                if (EFI_ERROR(err)) {
-                        Print(L"Unable to add image options measurement: %r", err);
-                        uefi_call_wrapper(BS->Stall, 1, 200 * 1000);
-                }
+                if (EFI_ERROR(err))
+                        log_error_stall(L"Unable to add image options measurement: %r", err);
 #endif
         }
 
@@ -2231,12 +2340,11 @@ static EFI_STATUS reboot_into_firmware(VOID) {
                 return err;
 
         err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
-        Print(L"Error calling ResetSystem: %r", err);
-        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
-        return err;
+        return log_error_status_stall(err, L"Error calling ResetSystem: %r", err);
 }
 
 static VOID config_free(Config *config) {
+        assert(config);
         for (UINTN i = 0; i < config->entry_count; i++)
                 config_entry_free(config->entries[i]);
         FreePool(config->entries);
@@ -2246,26 +2354,30 @@ static VOID config_free(Config *config) {
 }
 
 static VOID config_write_entries_to_variable(Config *config) {
-        _cleanup_freepool_ CHAR16 *buffer = NULL;
+        _cleanup_freepool_ CHAR8 *buffer = NULL;
         UINTN sz = 0;
-        CHAR16 *p;
+        CHAR8 *p;
+
+        assert(config);
 
         for (UINTN i = 0; i < config->entry_count; i++)
-                sz += StrLen(config->entries[i]->id) + 1;
+                sz += StrSize(config->entries[i]->id);
 
-        p = buffer = AllocatePool(sz * sizeof(CHAR16));
+        p = buffer = AllocatePool(sz);
 
         for (UINTN i = 0; i < config->entry_count; i++) {
                 UINTN l;
 
-                l = StrLen(config->entries[i]->id) + 1;
-                CopyMem(p, config->entries[i]->id, l * sizeof(CHAR16));
+                l = StrSize(config->entries[i]->id);
+                CopyMem(p, config->entries[i]->id, l);
 
                 p += l;
         }
 
+        assert(p == buffer + sz);
+
         /* Store the full list of discovered entries. */
-        (void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, (UINT8 *) p - (UINT8 *) buffer, 0);
+        (void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, sz, 0);
 }
 
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
@@ -2305,30 +2417,21 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
 
         err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
                                 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
-        if (EFI_ERROR(err)) {
-                Print(L"Error getting a LoadedImageProtocol handle: %r", err);
-                uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
-                return err;
-        }
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
 
         /* export the device path this image is started from */
         if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
                 efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
 
         root_dir = LibOpenRoot(loaded_image->DeviceHandle);
-        if (!root_dir) {
-                Print(L"Unable to open root directory.");
-                uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
-                return EFI_LOAD_ERROR;
-        }
+        if (!root_dir)
+                return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory.", EFI_LOAD_ERROR);
 
         if (secure_boot_enabled() && shim_loaded()) {
                 err = security_policy_install();
-                if (EFI_ERROR(err)) {
-                        Print(L"Error installing security policy: %r ", err);
-                        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
-                        return err;
-                }
+                if (EFI_ERROR(err))
+                        return log_error_status_stall(err, L"Error installing security policy: %r", err);
         }
 
         /* the filesystem path to this image, to prevent adding ourselves to the menu */
@@ -2350,13 +2453,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         config_sort_entries(&config);
 
         /* if we find some well-known loaders, add them to the end of the list */
-        config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL,
-                                     L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
+        config_entry_add_osx(&config);
+        config_entry_add_windows(&config, loaded_image->DeviceHandle, root_dir);
         config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL,
                                      L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
         config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
-                                     L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi");
-        config_entry_add_osx(&config);
+                                     L"auto-efi-default", '\0', L"EFI Default Loader", NULL);
 
         if (config.auto_firmware && efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind) == EFI_SUCCESS) {
                 if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)
@@ -2367,8 +2469,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         }
 
         if (config.entry_count == 0) {
-                Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
-                uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+                log_error_stall(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
                 goto out;
         }
 
@@ -2392,13 +2493,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         else {
                 UINT64 key;
 
-                err = console_key_read(&key, FALSE);
-
-                if (err == EFI_NOT_READY) {
-                        uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
-                        err = console_key_read(&key, FALSE);
-                }
-
+                /* Block up to 100ms to give firmware time to get input working. */
+                err = console_key_read(&key, 100 * 1000);
                 if (!EFI_ERROR(err)) {
                         INT16 idx;
 
@@ -2440,8 +2536,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 err = image_start(image, &config, entry);
                 if (EFI_ERROR(err)) {
                         graphics_mode(FALSE);
-                        Print(L"\nFailed to execute %s (%s): %r\n", entry->title, entry->loader, err);
-                        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+                        log_error_stall(L"Failed to execute %s (%s): %r", entry->title, entry->loader, err);
                         goto out;
                 }
 
index 83619d2147f3b49af9c207105c8a7f8ace7fe30a..3f405113bdb341111597c88d4eab2db0080938a7 100644 (file)
 
 #define EFI_SIMPLE_TEXT_INPUT_EX_GUID &(EFI_GUID) EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID
 
-EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) {
+static inline void EventClosep(EFI_EVENT *event) {
+        if (!*event)
+                return;
+
+        uefi_call_wrapper(BS->CloseEvent, 1, *event);
+}
+
+/*
+ * Reading input from the console sounds like an easy task to do, but thanks to broken
+ * firmware it is actually a nightmare.
+ *
+ * There is a ConIn and TextInputEx API for this. Ideally we want to use TextInputEx,
+ * because that gives us Ctrl/Alt/Shift key state information. Unfortunately, it is not
+ * always available and sometimes just non-functional.
+ *
+ * On the other hand we have ConIn, where some firmware likes to just freeze on us
+ * if we call ReadKeyStroke on it.
+ *
+ * Therefore, we use WaitForEvent on both ConIn and TextInputEx (if available) along
+ * with a timer event. The timer ensures there is no need to call into functions
+ * that might freeze on us, while still allowing us to show a timeout counter.
+ */
+EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec) {
         static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx;
         static BOOLEAN checked;
         UINTN index;
         EFI_INPUT_KEY k;
         EFI_STATUS err;
+        _cleanup_(EventClosep) EFI_EVENT timer = NULL;
+        EFI_EVENT events[3] = { ST->ConIn->WaitForKey };
+        UINTN n_events = 1;
+
+        assert(key);
 
         if (!checked) {
                 err = LibLocateProtocol(EFI_SIMPLE_TEXT_INPUT_EX_GUID, (VOID **)&TextInputEx);
-                if (EFI_ERROR(err))
+                if (EFI_ERROR(err) ||
+                    uefi_call_wrapper(BS->CheckEvent, 1, TextInputEx->WaitForKeyEx) == EFI_INVALID_PARAMETER)
+                        /* If WaitForKeyEx fails here, the firmware pretends it talks this
+                         * protocol, but it really doesn't. */
                         TextInputEx = NULL;
+                else
+                        events[n_events++] = TextInputEx->WaitForKeyEx;
 
                 checked = TRUE;
         }
 
-        /* wait until key is pressed */
-        if (wait)
-                uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index);
+        if (timeout_usec > 0) {
+                err = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL, &timer);
+                if (EFI_ERROR(err))
+                        return log_error_status_stall(err, L"Error creating timer event: %r", err);
+
+                /* SetTimer expects 100ns units for some reason. */
+                err = uefi_call_wrapper(BS->SetTimer, 3, timer, TimerRelative, timeout_usec * 10);
+                if (EFI_ERROR(err))
+                        return log_error_status_stall(err, L"Error arming timer event: %r", err);
+
+                events[n_events++] = timer;
+        }
+
+        err = uefi_call_wrapper(BS->WaitForEvent, 3, n_events, events, &index);
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Error waiting for events: %r", err);
+
+        if (timeout_usec > 0 && timer == events[index])
+                return EFI_TIMEOUT;
 
-        if (TextInputEx) {
+        /* TextInputEx might be ready too even if ConIn got to signal first. */
+        if (TextInputEx && !EFI_ERROR(uefi_call_wrapper(BS->CheckEvent, 1, TextInputEx->WaitForKeyEx))) {
                 EFI_KEY_DATA keydata;
                 UINT64 keypress;
+                UINT32 shift = 0;
 
                 err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
-                if (!EFI_ERROR(err)) {
-                        UINT32 shift = 0;
-
-                        /* do not distinguish between left and right keys */
-                        if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
-                                if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
-                                        shift |= EFI_CONTROL_PRESSED;
-                                if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
-                                        shift |= EFI_ALT_PRESSED;
-                        };
-
-                        /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
-                        keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
-                        if (keypress > 0) {
-                                *key = keypress;
-                                return 0;
-                        }
+                if (EFI_ERROR(err))
+                        return err;
+
+                /* do not distinguish between left and right keys */
+                if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
+                        if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
+                                shift |= EFI_CONTROL_PRESSED;
+                        if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
+                                shift |= EFI_ALT_PRESSED;
+                };
+
+                /* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
+                keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
+                if (keypress > 0) {
+                        *key = keypress;
+                        return EFI_SUCCESS;
                 }
+
+                return EFI_NOT_READY;
         }
 
-        /* fallback for firmware which does not support SimpleTextInputExProtocol
-         *
-         * This is also called in case ReadKeyStrokeEx did not return a key, because
-         * some broken firmwares offer SimpleTextInputExProtocol, but never actually
-         * handle any key. */
         err  = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
         if (EFI_ERROR(err))
                 return err;
 
         *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
-        return 0;
+        return EFI_SUCCESS;
 }
 
 static EFI_STATUS change_mode(UINTN mode) {
@@ -108,6 +154,8 @@ static EFI_STATUS mode_auto(UINTN *mode) {
         EFI_STATUS err;
         BOOLEAN keep = FALSE;
 
+        assert(mode);
+
         err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
         if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
                 Info = GraphicsOutput->Mode->Info;
@@ -156,6 +204,8 @@ static EFI_STATUS mode_auto(UINTN *mode) {
 }
 
 EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how) {
+        assert(mode);
+
         if (how == CONSOLE_MODE_AUTO)
                 return mode_auto(mode);
 
index 2c69af552a6f469620f6c8fc8bd010a658b87d1b..23848a9c58a0a4bbcba6a5e2c2dc3238e4710c65 100644 (file)
@@ -16,5 +16,5 @@ enum console_mode_change_type {
         CONSOLE_MODE_MAX,
 };
 
-EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait);
+EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec);
 EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how);
diff --git a/src/boot/efi/crc32.c b/src/boot/efi/crc32.c
deleted file mode 100644 (file)
index c9e5501..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/* SPDX-License-Identifier: LicenseRef-crc32-no-restriction */
-/* This is copied from util-linux, which in turn copied in the version from Gary S. Brown */
-
-/*
- *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
- *  code or tables extracted from it, as desired without restriction.
- *
- *  First, the polynomial itself and its table of feedback terms.  The
- *  polynomial is
- *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
- *
- *  Note that we take it "backwards" and put the highest-order term in
- *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
- *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
- *  the MSB being 1.
- *
- *  Note that the usual hardware shift register implementation, which
- *  is what we're using (we're merely optimizing it by doing eight-bit
- *  chunks at a time) shifts bits into the lowest-order term.  In our
- *  implementation, that means shifting towards the right.  Why do we
- *  do it this way?  Because the calculated CRC must be transmitted in
- *  order from highest-order term to lowest-order term.  UARTs transmit
- *  characters in order from LSB to MSB.  By storing the CRC this way,
- *  we hand it to the UART in the order low-byte to high-byte; the UART
- *  sends each low-bit to high-bit; and the result is transmission bit
- *  by bit from highest- to lowest-order term without requiring any bit
- *  shuffling on our part.  Reception works similarly.
- *
- *  The feedback terms table consists of 256, 32-bit entries.  Notes
- *
- *      The table can be generated at runtime if desired; code to do so
- *      is shown later.  It might not be obvious, but the feedback
- *      terms simply represent the results of eight shift/xor opera-
- *      tions for all combinations of data and CRC register values.
- *
- *      The values must be right-shifted by eight bits by the "updcrc"
- *      logic; the shift must be unsigned (bring in zeroes).  On some
- *      hardware you could probably optimize the shift in assembler by
- *      using byte-swap instructions.
- *      polynomial $edb88320
- *
- */
-
-#include "crc32.h"
-
-static const UINT32 crc32_tab[] = {
-        0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
-        0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
-        0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
-        0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
-        0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
-        0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
-        0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
-        0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
-        0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
-        0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
-        0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
-        0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
-        0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
-        0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
-        0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
-        0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
-        0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
-        0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
-        0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
-        0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
-        0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
-        0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
-        0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
-        0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
-        0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
-        0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
-        0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
-        0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
-        0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
-        0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
-        0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
-        0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
-        0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
-        0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
-        0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
-        0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
-        0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
-        0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
-        0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
-        0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
-        0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
-        0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
-        0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
-        0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
-        0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
-        0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
-        0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
-        0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
-        0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
-        0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
-        0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
-        0x2d02ef8dL
-};
-
-static inline UINT32 crc32_add_char(UINT32 crc, UINT8 c) {
-        return crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8);
-}
-
-/*
- * This a generic crc32() function, it takes seed as an argument,
- * and does __not__ xor at the end. Then individual users can do
- * whatever they need.
- */
-UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len) {
-        const UINT8 *p = buf;
-        UINT32 crc = seed;
-
-        while (len > 0) {
-                crc = crc32_add_char(crc, *p++);
-                len--;
-        }
-
-        return crc;
-}
-
-UINT32 crc32_exclude_offset(
-                UINT32 seed,
-                const VOID *buf,
-                UINTN len,
-                UINTN exclude_off,
-                UINTN exclude_len) {
-
-        const UINT8 *p = buf;
-        UINT32 crc = seed;
-
-        for (UINTN i = 0; i < len; i++) {
-                UINT8 x = *p++;
-
-                if (i >= exclude_off && i < exclude_off + exclude_len)
-                        x = 0;
-
-                crc = crc32_add_char(crc, x);
-        }
-
-        return crc;
-}
diff --git a/src/boot/efi/crc32.h b/src/boot/efi/crc32.h
deleted file mode 100644 (file)
index 3af543b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-/* SPDX-License-Identifier: LicenseRef-crc32-no-restriction */
-#pragma once
-
-#include <efi.h>
-#include <efilib.h>
-
-UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len);
-UINT32 crc32_exclude_offset(UINT32 seed, const VOID *buf, UINTN len, UINTN exclude_off, UINTN exclude_len);
index 122f08dd5cd151308322e7c96bea1fe1fb2c005f..196dc52be58d80b36324ff75594a8e167acc87db 100644 (file)
@@ -9,6 +9,8 @@
 EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[static 37]) {
         EFI_DEVICE_PATH *device_path;
 
+        assert(handle);
+
         /* export the device path this image is started from */
         device_path = DevicePathFromHandle(handle);
         if (device_path) {
index 4d44671315f858adbd6a45ce425b309defaf16ec..529325fef9916ecf10bde9aed07e4a478c0c6654 100644 (file)
@@ -17,6 +17,8 @@ static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
         handover_f handover;
         UINTN start = (UINTN)params->hdr.code32_start;
 
+        assert(params);
+
 #ifdef __x86_64__
         asm volatile ("cli");
         start += 512;
@@ -25,7 +27,7 @@ static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
         handover(image, ST, params);
 }
 
-EFI_STATUS linux_exec(EFI_HANDLE *image,
+EFI_STATUS linux_exec(EFI_HANDLE image,
                       CHAR8 *cmdline, UINTN cmdline_len,
                       UINTN linux_addr,
                       UINTN initrd_addr, UINTN initrd_size) {
@@ -35,6 +37,9 @@ EFI_STATUS linux_exec(EFI_HANDLE *image,
         EFI_PHYSICAL_ADDRESS addr;
         EFI_STATUS err;
 
+        assert(image);
+        assert(cmdline);
+
         image_params = (struct boot_params *) linux_addr;
 
         if (image_params->hdr.boot_flag != 0xAA55 ||
index 09be2de27b36a9a4cc7655d748dbe7bbe48eab18..773c260b7ef81c1f779f9de2b5e2b9f15587b78b 100644 (file)
@@ -2,6 +2,7 @@
 #pragma once
 
 #include <efi.h>
+#include "macro-fundamental.h"
 
 #define SETUP_MAGIC             0x53726448      /* "HdrS" */
 
@@ -44,7 +45,7 @@ struct setup_header {
         UINT64 pref_address;
         UINT32 init_size;
         UINT32 handover_offset;
-} __attribute__((packed));
+} _packed_;
 
 /* adapted from linux' bootparam.h */
 struct boot_params {
@@ -81,9 +82,9 @@ struct boot_params {
         UINT8  _pad8[48];
         UINT8  eddbuf[6*82];            // was: struct edd_info eddbuf[EDDMAXNR]
         UINT8  _pad9[276];
-} __attribute__((packed));
+} _packed_;
 
-EFI_STATUS linux_exec(EFI_HANDLE *image,
+EFI_STATUS linux_exec(EFI_HANDLE image,
                       CHAR8 *cmdline, UINTN cmdline_size,
                       UINTN linux_addr,
                       UINTN initrd_addr, UINTN initrd_size);
index c272d0855379753ea5039abfab9d3777a3e885ab..1d19366f73a27f3c1953a1252a00c8a1c70a5ea4 100644 (file)
@@ -5,6 +5,7 @@
 #include <efi.h>
 #include <efilib.h>
 
+#include "macro-fundamental.h"
 #include "measure.h"
 
 #define EFI_TCG_GUID \
@@ -132,13 +133,13 @@ typedef struct {
         UINT16 HeaderVersion;
         UINT32 PCRIndex;
         UINT32 EventType;
-} __attribute__((packed)) EFI_TCG2_EVENT_HEADER;
+} _packed_ EFI_TCG2_EVENT_HEADER;
 
 typedef struct tdEFI_TCG2_EVENT {
         UINT32 Size;
         EFI_TCG2_EVENT_HEADER Header;
         UINT8 Event[1];
-} __attribute__((packed)) EFI_TCG2_EVENT;
+} _packed_ EFI_TCG2_EVENT;
 
 typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_CAPABILITY) (IN EFI_TCG2_PROTOCOL * This,
                                                       IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY * ProtocolCapability);
@@ -184,8 +185,10 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p
         EFI_PHYSICAL_ADDRESS event_log_last;
         UINTN desc_len;
 
-        desc_len = (StrLen(description) + 1) * sizeof(CHAR16);
+        assert(tcg);
+        assert(description);
 
+        desc_len = StrSize(description);
         tcg_event = AllocateZeroPool(desc_len + sizeof(TCG_PCR_EVENT));
 
         if (!tcg_event)
@@ -215,14 +218,16 @@ static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32
         EFI_TCG2_EVENT *tcg_event;
         UINTN desc_len;
 
-        desc_len = StrLen(description) * sizeof(CHAR16);
+        assert(tcg);
+        assert(description);
 
-        tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1);
+        desc_len = StrSize(description);
+        tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len);
 
         if (!tcg_event)
                 return EFI_OUT_OF_RESOURCES;
 
-        tcg_event->Size = sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1;
+        tcg_event->Size = sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len;
         tcg_event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER);
         tcg_event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION;
         tcg_event->Header.PCRIndex = pcrindex;
@@ -302,6 +307,8 @@ EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UIN
         EFI_TCG *tpm1;
         EFI_TCG2 *tpm2;
 
+        assert(description);
+
         tpm2 = tcg2_interface_check();
         if (tpm2)
                 return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
index 62d826bec6762277658a529c32b0b2113e1bb6f5..a556471e0071d8a8bb0333e4ee3d90aaa8b45b2f 100644 (file)
@@ -2,7 +2,6 @@
 
 efi_headers = files('''
         console.h
-        crc32.h
         disk.h
         graphics.h
         linux.h
@@ -17,6 +16,7 @@ efi_headers = files('''
 '''.split())
 
 common_sources = '''
+        assert.c
         disk.c
         graphics.c
         measure.c
@@ -28,7 +28,6 @@ common_sources = '''
 systemd_boot_sources = '''
         boot.c
         console.c
-        crc32.c
         random-seed.c
         sha256.c
         shim.c
@@ -71,14 +70,14 @@ if conf.get('ENABLE_EFI') == 1 and get_option('gnu-efi') != 'false'
         efi_libdir = get_option('efi-libdir')
         if efi_libdir == ''
                 # New location first introduced with gnu-efi 3.0.11
-                efi_libdir = join_paths('/usr/lib/gnuefi', EFI_MACHINE_TYPE_NAME)
+                efi_libdir = '/usr/lib/gnuefi' / EFI_MACHINE_TYPE_NAME
                 cmd = run_command(test, '-e', efi_libdir)
 
                 if cmd.returncode() != 0
                         # Fall back to the old approach
                         cmd = run_command(efi_cc + ['-print-multi-os-directory'])
                         if cmd.returncode() == 0
-                                path = join_paths('/usr/lib', cmd.stdout().strip())
+                                path = '/usr/lib' / cmd.stdout().strip()
                                 cmd = run_command(env, 'realpath', '-e', path)
                                 if cmd.returncode() == 0
                                         efi_libdir = cmd.stdout().strip()
@@ -102,6 +101,13 @@ if have_gnu_efi
         efi_conf.set10('ENABLE_TPM', get_option('tpm'))
         efi_conf.set('SD_TPM_PCR', get_option('tpm-pcrindex'))
 
+        foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit']
+                c = get_option('efi-' + ctype).split(',')
+                fg = 'EFI_' + c[0].strip().underscorify().to_upper()
+                bg = 'EFI_BACKGROUND_' + c[1].strip().underscorify().to_upper()
+                efi_conf.set(ctype.underscorify().to_upper(), '(' + fg + '|' + bg + ')')
+        endforeach
+
         if get_option('sbat-distro') != ''
                 efi_conf.set_quoted('SBAT_PROJECT', meson.project_name())
                 efi_conf.set_quoted('PROJECT_VERSION', meson.project_version())
@@ -148,13 +154,13 @@ if have_gnu_efi
 
         efi_location_map = [
                 # New locations first introduced with gnu-efi 3.0.11
-                [join_paths(efi_libdir, 'efi.lds'),
-                 join_paths(efi_libdir, 'crt0.o')],
+                [efi_libdir / 'efi.lds',
+                 efi_libdir / 'crt0.o'],
                 # Older locations...
-                [join_paths(efi_libdir, 'gnuefi', 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)),
-                 join_paths(efi_libdir, 'gnuefi', 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))],
-                [join_paths(efi_libdir, 'elf_@0@_efi.lds'.format(gnu_efi_path_arch)),
-                 join_paths(efi_libdir, 'crt0-efi-@0@.o'.format(gnu_efi_path_arch))]]
+                [efi_libdir / 'gnuefi' / 'elf_@0@_efi.lds'.format(gnu_efi_path_arch),
+                 efi_libdir / 'gnuefi' / 'crt0-efi-@0@.o'.format(gnu_efi_path_arch)],
+                [efi_libdir / 'elf_@0@_efi.lds'.format(gnu_efi_path_arch),
+                 efi_libdir / 'crt0-efi-@0@.o'.format(gnu_efi_path_arch)]]
         efi_lds = ''
         foreach location : efi_location_map
                 if efi_lds == ''
@@ -191,7 +197,7 @@ if have_gnu_efi
                 '-nostdlib',
                 '-std=gnu99',
                 '-isystem', efi_incdir,
-                '-isystem', join_paths(efi_incdir, gnu_efi_path_arch),
+                '-isystem', efi_incdir / gnu_efi_path_arch,
                 '-I', fundamental_path,
                 '-DSD_BOOT',
                 '-include', efi_config_h,
@@ -219,12 +225,16 @@ if have_gnu_efi
                 compile_args += ['-Werror']
         endif
         if get_option('buildtype') == 'debug'
-                compile_args += ['-ggdb', '-O0']
+                compile_args += ['-ggdb', '-O0', '-DEFI_DEBUG']
         elif get_option('buildtype') == 'debugoptimized'
-                compile_args += ['-ggdb', '-Og']
+                compile_args += ['-ggdb', '-Og', '-DEFI_DEBUG']
         else
                 compile_args += ['-O2']
         endif
+        if get_option('b_ndebug') == 'true' or (
+           get_option('b_ndebug') == 'if-release' and ['plain', 'release'].contains(get_option('buildtype')))
+                compile_args += ['-DNDEBUG']
+        endif
 
         efi_ldflags = ['-T', efi_lds,
                        '-shared',
@@ -287,6 +297,7 @@ if have_gnu_efi
                                    '-j', '.text',
                                    '-j', '.sdata',
                                    '-j', '.sbat',
+                                   '-j', '.sdmagic',
                                    '-j', '.data',
                                    '-j', '.dynamic',
                                    '-j', '.dynsym',
index b983931348b2dc1dc1ea6e7cf4992cd36dfe761a..37c468f2ab077a654a901f8688d65d04b9bddb18 100644 (file)
@@ -120,3 +120,7 @@ typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
 } EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
 
 #endif
+
+#ifndef EFI_IMAGE_MACHINE_RISCV64
+        #define EFI_IMAGE_MACHINE_RISCV64 0x5064
+#endif
index e65701734307a56e12b6b6143609e87d6192d3f1..04b4504c2ee3babdbd6ff2714c8b8445b55ea804 100644 (file)
@@ -6,6 +6,24 @@
 #include "pe.h"
 #include "util.h"
 
+#define DOS_FILE_MAGIC "MZ"
+#define PE_FILE_MAGIC  "PE\0\0"
+#define MAX_SECTIONS 96
+
+#if defined(__i386__)
+        #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
+#elif defined(__x86_64__)
+        #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
+#elif defined(__aarch64__)
+        #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
+#elif defined(__arm__)
+        #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
+#elif defined(__riscv) && __riscv_xlen == 64
+        #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
+#else
+        #error Unknown EFI arch
+#endif
+
 struct DosFileHeader {
         UINT8   Magic[2];
         UINT16  LastSize;
@@ -26,14 +44,9 @@ struct DosFileHeader {
         UINT16  OEMInfo;
         UINT16  reserved2[10];
         UINT32  ExeHeader;
-} __attribute__((packed));
+} _packed_;
 
-#define PE_HEADER_MACHINE_I386          0x014c
-#define PE_HEADER_MACHINE_X64           0x8664
-#define PE_HEADER_MACHINE_ARM64         0xaa64
-#define PE_HEADER_MACHINE_ARM           0x01c2
-#define PE_HEADER_MACHINE_RISCV64       0x5064
-struct PeFileHeader {
+struct CoffFileHeader {
         UINT16  Machine;
         UINT16  NumberOfSections;
         UINT32  TimeDateStamp;
@@ -41,12 +54,13 @@ struct PeFileHeader {
         UINT32  NumberOfSymbols;
         UINT16  SizeOfOptionalHeader;
         UINT16  Characteristics;
-} __attribute__((packed));
+} _packed_;
 
-struct PeHeader {
+struct PeFileHeader {
         UINT8   Magic[4];
-        struct PeFileHeader FileHeader;
-} __attribute__((packed));
+        struct CoffFileHeader FileHeader;
+        /* OptionalHeader omitted */
+} _packed_;
 
 struct PeSectionHeader {
         UINT8   Name[8];
@@ -59,114 +73,143 @@ struct PeSectionHeader {
         UINT16  NumberOfRelocations;
         UINT16  NumberOfLinenumbers;
         UINT32  Characteristics;
-} __attribute__((packed));
-
-EFI_STATUS pe_memory_locate_sections(CHAR8 *base, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
-        struct DosFileHeader *dos;
-        struct PeHeader *pe;
-        UINTN offset;
+} _packed_;
 
-        dos = (struct DosFileHeader *)base;
-
-        if (CompareMem(dos->Magic, "MZ", 2) != 0)
-                return EFI_LOAD_ERROR;
+static inline BOOLEAN verify_dos(const struct DosFileHeader *dos) {
+        assert(dos);
+        return CompareMem(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
+}
 
-        pe = (struct PeHeader *)&base[dos->ExeHeader];
-        if (CompareMem(pe->Magic, "PE\0\0", 4) != 0)
-                return EFI_LOAD_ERROR;
+static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) {
+        assert(pe);
+        return CompareMem(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
+               pe->FileHeader.Machine == TARGET_MACHINE_TYPE &&
+               pe->FileHeader.NumberOfSections > 0 &&
+               pe->FileHeader.NumberOfSections <= MAX_SECTIONS;
+}
 
-        /* PE32+ Subsystem type */
-        if (pe->FileHeader.Machine != PE_HEADER_MACHINE_X64 &&
-            pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM64 &&
-            pe->FileHeader.Machine != PE_HEADER_MACHINE_I386 &&
-            pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM &&
-            pe->FileHeader.Machine != PE_HEADER_MACHINE_RISCV64)
-                return EFI_LOAD_ERROR;
+static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
+        assert(dos);
+        assert(pe);
+        return dos->ExeHeader + sizeof(struct PeFileHeader) + pe->FileHeader.SizeOfOptionalHeader;
+}
 
-        if (pe->FileHeader.NumberOfSections > 96)
-                return EFI_LOAD_ERROR;
+static VOID locate_sections(
+                const struct PeSectionHeader section_table[],
+                UINTN n_table,
+                const CHAR8 **sections,
+                UINTN *addrs,
+                UINTN *offsets,
+                UINTN *sizes) {
 
-        offset = dos->ExeHeader + sizeof(*pe) + pe->FileHeader.SizeOfOptionalHeader;
+        assert(section_table);
+        assert(sections);
+        assert(sizes);
 
-        for (UINTN i = 0; i < pe->FileHeader.NumberOfSections; i++) {
-                struct PeSectionHeader *sect;
+        for (UINTN i = 0; i < n_table; i++) {
+                const struct PeSectionHeader *sect = section_table + i;
 
-                sect = (struct PeSectionHeader *)&base[offset];
                 for (UINTN j = 0; sections[j]; j++) {
                         if (CompareMem(sect->Name, sections[j], strlena(sections[j])) != 0)
                                 continue;
 
                         if (addrs)
-                                addrs[j] = (UINTN)sect->VirtualAddress;
+                                addrs[j] = sect->VirtualAddress;
                         if (offsets)
-                                offsets[j] = (UINTN)sect->PointerToRawData;
-                        if (sizes)
-                                sizes[j] = (UINTN)sect->VirtualSize;
+                                offsets[j] = sect->PointerToRawData;
+                        sizes[j] = sect->VirtualSize;
                 }
-                offset += sizeof(*sect);
         }
+}
+
+EFI_STATUS pe_memory_locate_sections(
+                const CHAR8 *base,
+                const CHAR8 **sections,
+                UINTN *addrs,
+                UINTN *sizes) {
+        const struct DosFileHeader *dos;
+        const struct PeFileHeader *pe;
+        UINTN offset;
+
+        assert(base);
+        assert(sections);
+        assert(addrs);
+        assert(sizes);
+
+        dos = (const struct DosFileHeader*)base;
+        if (!verify_dos(dos))
+                return EFI_LOAD_ERROR;
+
+        pe = (const struct PeFileHeader*)&base[dos->ExeHeader];
+        if (!verify_pe(pe))
+                return EFI_LOAD_ERROR;
+
+        offset = section_table_offset(dos, pe);
+        locate_sections((struct PeSectionHeader*)&base[offset], pe->FileHeader.NumberOfSections,
+                        sections, addrs, NULL, sizes);
 
         return EFI_SUCCESS;
 }
 
-EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
-        EFI_FILE_HANDLE handle;
+EFI_STATUS pe_file_locate_sections(
+                EFI_FILE *dir,
+                const CHAR16 *path,
+                const CHAR8 **sections,
+                UINTN *offsets,
+                UINTN *sizes) {
+        _cleanup_freepool_ struct PeSectionHeader *section_table = NULL;
+        _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
         struct DosFileHeader dos;
-        struct PeHeader pe;
-        UINTN len;
-        UINTN headerlen;
+        struct PeFileHeader pe;
+        UINTN len, section_table_len;
         EFI_STATUS err;
-        _cleanup_freepool_ CHAR8 *header = NULL;
 
-        err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL);
+        assert(dir);
+        assert(path);
+        assert(sections);
+        assert(offsets);
+        assert(sizes);
+
+        err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*)path, EFI_FILE_MODE_READ, 0ULL);
         if (EFI_ERROR(err))
                 return err;
 
-        /* MS-DOS stub */
         len = sizeof(dos);
         err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos);
         if (EFI_ERROR(err))
-                goto out;
-        if (len != sizeof(dos)) {
-                err = EFI_LOAD_ERROR;
-                goto out;
-        }
+                return err;
+        if (len != sizeof(dos) || !verify_dos(&dos))
+                return EFI_LOAD_ERROR;
 
         err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader);
         if (EFI_ERROR(err))
-                goto out;
+                return err;
 
         len = sizeof(pe);
         err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe);
         if (EFI_ERROR(err))
-                goto out;
-        if (len != sizeof(pe)) {
-                err = EFI_LOAD_ERROR;
-                goto out;
-        }
+                return err;
+        if (len != sizeof(pe) || !verify_pe(&pe))
+                return EFI_LOAD_ERROR;
 
-        headerlen = sizeof(dos) + sizeof(pe) + pe.FileHeader.SizeOfOptionalHeader + pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
-        header = AllocatePool(headerlen);
-        if (!header) {
-                err = EFI_OUT_OF_RESOURCES;
-                goto out;
-        }
-        len = headerlen;
-        err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0);
+        section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
+        section_table = AllocatePool(section_table_len);
+        if (!section_table)
+                return EFI_OUT_OF_RESOURCES;
+
+        err = uefi_call_wrapper(handle->SetPosition, 2, handle, section_table_offset(&dos, &pe));
         if (EFI_ERROR(err))
-                goto out;
+                return err;
 
-        err = uefi_call_wrapper(handle->Read, 3, handle, &len, header);
+        len = section_table_len;
+        err = uefi_call_wrapper(handle->Read, 3, handle, &len, section_table);
         if (EFI_ERROR(err))
-                goto out;
+                return err;
+        if (len != section_table_len)
+                return EFI_LOAD_ERROR;
 
-        if (len != headerlen) {
-                err = EFI_LOAD_ERROR;
-                goto out;
-        }
+        locate_sections(section_table, pe.FileHeader.NumberOfSections,
+                        sections, NULL, offsets, sizes);
 
-        err = pe_memory_locate_sections(header, sections, addrs, offsets, sizes);
-out:
-        uefi_call_wrapper(handle->Close, 1, handle);
-        return err;
+        return EFI_SUCCESS;
 }
index 06a0fcd70c6b9c3dc6703ac52d13432183175041..e35521122353571a63e7b85b64586b5f09b4547f 100644 (file)
@@ -1,9 +1,17 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include <efi.h>
+#include <efidef.h>
 
-EFI_STATUS pe_memory_locate_sections(CHAR8 *base,
-                                     CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
-EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path,
-                                   CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
+EFI_STATUS pe_memory_locate_sections(
+                const CHAR8 *base,
+                const CHAR8 **sections,
+                UINTN *addrs,
+                UINTN *sizes);
+
+EFI_STATUS pe_file_locate_sections(
+                EFI_FILE *dir,
+                const CHAR16 *path,
+                const CHAR8 **sections,
+                UINTN *offsets,
+                UINTN *sizes);
index ff364695f352982871e37bda4480b6b89f893c76..55348a64ecf2f2816d1ba8d666efd630406854e6 100644 (file)
@@ -22,6 +22,8 @@ static EFI_STATUS acquire_rng(UINTN size, VOID **ret) {
         EFI_RNG_PROTOCOL *rng;
         EFI_STATUS err;
 
+        assert(ret);
+
         /* Try to acquire the specified number of bytes from the UEFI RNG */
 
         err = LibLocateProtocol(EFI_RNG_GUID, (VOID**) &rng);
@@ -35,10 +37,8 @@ static EFI_STATUS acquire_rng(UINTN size, VOID **ret) {
                 return log_oom();
 
         err = uefi_call_wrapper(rng->GetRNG, 3, rng, NULL, size, data);
-        if (EFI_ERROR(err)) {
-                Print(L"Failed to acquire RNG data: %r\n", err);
-                return err;
-        }
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to acquire RNG data: %r", err);
 
         *ret = TAKE_PTR(data);
         return EFI_SUCCESS;
@@ -65,6 +65,10 @@ static VOID hash_once(
 
         struct sha256_ctx hash;
 
+        assert(old_seed);
+        assert(rng);
+        assert(system_token);
+
         sha256_init_ctx(&hash);
         sha256_process_bytes(old_seed, size, &hash);
         if (rng)
@@ -87,6 +91,11 @@ static EFI_STATUS hash_many(
 
         _cleanup_freepool_ VOID *output = NULL;
 
+        assert(old_seed);
+        assert(rng);
+        assert(system_token);
+        assert(ret);
+
         /* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the
          * range counter_start…counter_start+n-1. */
 
@@ -117,6 +126,12 @@ static EFI_STATUS mangle_random_seed(
         EFI_STATUS err;
         UINTN n;
 
+        assert(old_seed);
+        assert(rng);
+        assert(system_token);
+        assert(ret_new_seed);
+        assert(ret_for_kernel);
+
         /* This takes the old seed file contents, an (optional) random number acquired from the UEFI RNG, an
          * (optional) system 'token' installed once by the OS installer in an EFI variable, and hashes them
          * together in counter mode, generating a new seed (to replace the file on disk) and the seed for the
@@ -146,17 +161,18 @@ static EFI_STATUS acquire_system_token(VOID **ret, UINTN *ret_size) {
         EFI_STATUS err;
         UINTN size;
 
+        assert(ret);
+        assert(ret_size);
+
         err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size);
         if (EFI_ERROR(err)) {
                 if (err != EFI_NOT_FOUND)
-                        Print(L"Failed to read LoaderSystemToken EFI variable: %r", err);
+                        log_error_stall(L"Failed to read LoaderSystemToken EFI variable: %r", err);
                 return err;
         }
 
-        if (size <= 0) {
-                Print(L"System token too short, ignoring.");
-                return EFI_NOT_FOUND;
-        }
+        if (size <= 0)
+                return log_error_status_stall(EFI_NOT_FOUND, L"System token too short, ignoring.");
 
         *ret = TAKE_PTR(data);
         *ret_size = size;
@@ -166,7 +182,7 @@ static EFI_STATUS acquire_system_token(VOID **ret, UINTN *ret_size) {
 
 static VOID validate_sha256(void) {
 
-#ifndef __OPTIMIZE__
+#ifdef EFI_DEBUG
         /* Let's validate our SHA256 implementation. We stole it from glibc, and converted it to UEFI
          * style. We better check whether it does the right stuff. We use the simpler test vectors from the
          * SHA spec. Note that we strip this out in optimization builds. */
@@ -208,14 +224,9 @@ static VOID validate_sha256(void) {
                 sha256_process_bytes(array[i].string, strlena((const CHAR8*) array[i].string), &hash);
                 sha256_finish_ctx(&hash, result);
 
-                if (CompareMem(result, array[i].hash, HASH_VALUE_SIZE) != 0) {
-                        Print(L"SHA256 failed validation.\n");
-                        uefi_call_wrapper(BS->Stall, 1, 120 * 1000 * 1000);
-                        return;
-                }
+                assert(CompareMem(result, array[i].hash, HASH_VALUE_SIZE) == 0);
         }
 
-        Print(L"SHA256 validated\n");
 #endif
 }
 
@@ -226,6 +237,8 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
         _cleanup_freepool_ EFI_FILE_INFO *info = NULL;
         EFI_STATUS err;
 
+        assert(root_dir);
+
         validate_sha256();
 
         if (mode == RANDOM_SEED_OFF)
@@ -245,8 +258,8 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
 
         err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, (CHAR16*) L"\\loader\\random-seed", EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
         if (EFI_ERROR(err)) {
-                if (err != EFI_NOT_FOUND)
-                        Print(L"Failed to open random seed file: %r\n", err);
+                if (err != EFI_NOT_FOUND && err != EFI_WRITE_PROTECTED)
+                        log_error_stall(L"Failed to open random seed file: %r", err);
                 return err;
         }
 
@@ -255,15 +268,11 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
                 return log_oom();
 
         size = info->FileSize;
-        if (size < RANDOM_MAX_SIZE_MIN) {
-                Print(L"Random seed file is too short?\n");
-                return EFI_INVALID_PARAMETER;
-        }
+        if (size < RANDOM_MAX_SIZE_MIN)
+                return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too short.");
 
-        if (size > RANDOM_MAX_SIZE_MAX) {
-                Print(L"Random seed file is too large?\n");
-                return EFI_INVALID_PARAMETER;
-        }
+        if (size > RANDOM_MAX_SIZE_MAX)
+                return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
 
         seed = AllocatePool(size);
         if (!seed)
@@ -271,20 +280,14 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
 
         rsize = size;
         err = uefi_call_wrapper(handle->Read, 3, handle, &rsize, seed);
-        if (EFI_ERROR(err)) {
-                Print(L"Failed to read random seed file: %r\n", err);
-                return err;
-        }
-        if (rsize != size) {
-                Print(L"Short read on random seed file\n");
-                return EFI_PROTOCOL_ERROR;
-        }
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to read random seed file: %r", err);
+        if (rsize != size)
+                return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short read on random seed file.");
 
         err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0);
-        if (EFI_ERROR(err)) {
-                Print(L"Failed to seek to beginning of random seed file: %r\n", err);
-                return err;
-        }
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
 
         /* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
          * idea to use it because it helps us for cases where users mistakenly include a random seed in
@@ -299,27 +302,19 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
         /* Update the random seed on disk before we use it */
         wsize = size;
         err = uefi_call_wrapper(handle->Write, 3, handle, &wsize, new_seed);
-        if (EFI_ERROR(err)) {
-                Print(L"Failed to write random seed file: %r\n", err);
-                return err;
-        }
-        if (wsize != size) {
-                Print(L"Short write on random seed file\n");
-                return EFI_PROTOCOL_ERROR;
-        }
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
+        if (wsize != size)
+                return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
 
         err = uefi_call_wrapper(handle->Flush, 1, handle);
-        if (EFI_ERROR(err)) {
-                Print(L"Failed to flush random seed file: %r\n");
-                return err;
-        }
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
 
         /* We are good to go */
         err = efivar_set_raw(LOADER_GUID, L"LoaderRandomSeed", for_kernel, size, 0);
-        if (EFI_ERROR(err)) {
-                Print(L"Failed to write random seed to EFI variable: %r\n", err);
-                return err;
-        }
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Failed to write random seed to EFI variable: %r", err);
 
         return EFI_SUCCESS;
 }
index 6585fdb9b2388f07eaff5e7e0685d3e3dc12b6ac..5df7e5316e0ffc0a28cc010ee6b882f191e15a8c 100644 (file)
@@ -23,6 +23,7 @@
 
 /* Written by Ulrich Drepper <drepper@redhat.com>, 2007.  */
 
+#include "macro-fundamental.h"
 #include "sha256.h"
 
 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@@ -73,6 +74,8 @@ static void sha256_process_block(const void *, UINTN, struct sha256_ctx *);
 /* Initialize structure containing state of computation.
    (FIPS 180-2:5.3.2)  */
 void sha256_init_ctx(struct sha256_ctx *ctx) {
+        assert(ctx);
+
         ctx->H[0] = 0x6a09e667;
         ctx->H[1] = 0xbb67ae85;
         ctx->H[2] = 0x3c6ef372;
@@ -96,6 +99,9 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
         UINT32 bytes = ctx->buflen;
         UINTN pad;
 
+        assert(ctx);
+        assert(resbuf);
+
         /* Now count remaining bytes.  */
         ctx->total64 += bytes;
 
@@ -118,6 +124,9 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
 }
 
 void sha256_process_bytes(const void *buffer, UINTN len, struct sha256_ctx *ctx) {
+        assert(buffer);
+        assert(ctx);
+
         /* When we already have some bits in our internal buffer concatenate
            both inputs first.  */
 
@@ -191,6 +200,10 @@ void sha256_process_bytes(const void *buffer, UINTN len, struct sha256_ctx *ctx)
 static void sha256_process_block(const void *buffer, UINTN len, struct sha256_ctx *ctx) {
         const UINT32 *words = buffer;
         UINTN nwords = len / sizeof (UINT32);
+
+        assert(buffer);
+        assert(ctx);
+
         UINT32 a = ctx->H[0];
         UINT32 b = ctx->H[1];
         UINT32 c = ctx->H[2];
index 48602627bb7eda8e7e6ce5a6e460812a5e5ab74b..9bcbb347693d65291a8b89f29106fe8f904f0b0c 100644 (file)
@@ -111,6 +111,10 @@ static EFIAPI EFI_STATUS security2_policy_authentication (const EFI_SECURITY2_PR
                                                           VOID *file_buffer, UINTN file_size, BOOLEAN boot_policy) {
         EFI_STATUS status;
 
+        assert(this);
+        assert(device_path);
+        assert(file_buffer);
+
         /* Chain original security policy */
         status = uefi_call_wrapper(es2fa, 5, this, device_path, file_buffer, file_size, boot_policy);
 
@@ -143,6 +147,9 @@ static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROT
         _cleanup_freepool_ CHAR8 *file_buffer = NULL;
         UINTN file_size;
 
+        assert(this);
+        assert(device_path_const);
+
         if (!device_path_const)
                 return EFI_INVALID_PARAMETER;
 
index 5e085ec45c2289fb5fd03f5f4327e5e02221b7f2..23627a794e7c803b5a8f211c5acb6581452870a3 100644 (file)
@@ -12,7 +12,7 @@ struct bmp_file {
         UINT32 size;
         UINT16 reserved[2];
         UINT32 offset;
-} __attribute__((packed));
+} _packed_;
 
 /* we require at least BITMAPINFOHEADER, later versions are
    accepted, but their features ignored */
@@ -28,14 +28,14 @@ struct bmp_dib {
         INT32 y_pixel_meter;
         UINT32 colors_used;
         UINT32 colors_important;
-} __attribute__((packed));
+} _packed_;
 
 struct bmp_map {
         UINT8 blue;
         UINT8 green;
         UINT8 red;
         UINT8 reserved;
-} __attribute__((packed));
+} _packed_;
 
 static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib,
                             struct bmp_map **ret_map, UINT8 **pixmap) {
@@ -44,6 +44,11 @@ static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_
         struct bmp_map *map;
         UINTN row_size;
 
+        assert(bmp);
+        assert(ret_dib);
+        assert(ret_map);
+        assert(pixmap);
+
         if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib))
                 return EFI_INVALID_PARAMETER;
 
@@ -128,6 +133,8 @@ static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_
 static VOID pixel_blend(UINT32 *dst, const UINT32 source) {
         UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
 
+        assert(dst);
+
         alpha = (source & 0xff);
 
         /* convert src from RGBA to XRGB */
@@ -152,6 +159,11 @@ static EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
                       UINT8 *pixmap) {
         UINT8 *in;
 
+        assert(buf);
+        assert(dib);
+        assert(map);
+        assert(pixmap);
+
         /* transform and copy pixels */
         in = pixmap;
         for (UINTN y = 0; y < dib->y; y++) {
@@ -247,6 +259,8 @@ EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_
         UINTN y_pos = 0;
         EFI_STATUS err;
 
+        assert(content);
+
         if (!background) {
                 if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) {
                         pixel.Red = 0xc0;
index 082fe91c9e5fd56599119d8a9e8f1a3980f2e86b..5758b5cd9481e3021bde2427598ec766addf2ccb 100644 (file)
@@ -17,7 +17,7 @@ static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub
 
 EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         EFI_LOADED_IMAGE *loaded_image;
-        CHAR8 *sections[] = {
+        const CHAR8 *sections[] = {
                 (CHAR8 *)".cmdline",
                 (CHAR8 *)".linux",
                 (CHAR8 *)".initrd",
@@ -25,7 +25,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 NULL
         };
         UINTN addrs[ELEMENTSOF(sections)-1] = {};
-        UINTN offs[ELEMENTSOF(sections)-1] = {};
         UINTN szs[ELEMENTSOF(sections)-1] = {};
         CHAR8 *cmdline = NULL;
         UINTN cmdline_len;
@@ -36,18 +35,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
 
         err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
                                 image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
-        if (EFI_ERROR(err)) {
-                Print(L"Error getting a LoadedImageProtocol handle: %r ", err);
-                uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
-                return err;
-        }
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
 
-        err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, offs, szs);
-        if (EFI_ERROR(err)) {
-                Print(L"Unable to locate embedded .linux section: %r ", err);
-                uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
-                return err;
-        }
+        err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, szs);
+        if (EFI_ERROR(err))
+                return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
 
         if (szs[0] > 0)
                 cmdline = (CHAR8 *)(loaded_image->ImageBase) + addrs[0];
@@ -72,10 +65,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                 err = tpm_log_event(SD_TPM_PCR,
                                     (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
                                     loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
-                if (EFI_ERROR(err)) {
-                        Print(L"Unable to add image options measurement: %r", err);
-                        uefi_call_wrapper(BS->Stall, 1, 200 * 1000);
-                }
+                if (EFI_ERROR(err))
+                        log_error_stall(L"Unable to add image options measurement: %r", err);
 #endif
         }
 
@@ -126,7 +117,5 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                          (UINTN)loaded_image->ImageBase + addrs[2], szs[2]);
 
         graphics_mode(FALSE);
-        Print(L"Execution of embedded linux image failed: %r\n", err);
-        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
-        return err;
+        return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
 }
index 6f4e5933d30506ffdd873f417ef73be56bddcba2..c0d1d6daddaa570c4816fbfe8ab8ea218b704c35 100644 (file)
@@ -17,6 +17,12 @@ UINT64 ticks_read(VOID) {
         __asm__ volatile ("rdtsc" : "=A" (val));
         return val;
 }
+#elif defined(__aarch64__)
+UINT64 ticks_read(VOID) {
+        UINT64 val;
+        __asm__ volatile ("mrs %0, cntpct_el0" : "=r" (val));
+        return val;
+}
 #else
 UINT64 ticks_read(VOID) {
         UINT64 val = 1;
@@ -24,6 +30,13 @@ UINT64 ticks_read(VOID) {
 }
 #endif
 
+#if defined(__aarch64__)
+UINT64 ticks_freq(VOID) {
+        UINT64 freq;
+        __asm__ volatile ("mrs %0, cntfrq_el0": "=r" (freq));
+        return freq;
+}
+#else
 /* count TSC ticks during a millisecond delay */
 UINT64 ticks_freq(VOID) {
         UINT64 ticks_start, ticks_end;
@@ -34,6 +47,7 @@ UINT64 ticks_freq(VOID) {
 
         return (ticks_end - ticks_start) * 1000UL;
 }
+#endif
 
 UINT64 time_usec(VOID) {
         UINT64 ticks;
@@ -53,13 +67,17 @@ UINT64 time_usec(VOID) {
 }
 
 EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
+        assert(b);
+
         if (!v)
                 return EFI_INVALID_PARAMETER;
 
         if (strcmpa(v, (CHAR8 *)"1") == 0 ||
             strcmpa(v, (CHAR8 *)"yes") == 0 ||
             strcmpa(v, (CHAR8 *)"y") == 0 ||
-            strcmpa(v, (CHAR8 *)"true") == 0) {
+            strcmpa(v, (CHAR8 *)"true") == 0 ||
+            strcmpa(v, (CHAR8 *)"t") == 0 ||
+            strcmpa(v, (CHAR8 *)"on") == 0) {
                 *b = TRUE;
                 return EFI_SUCCESS;
         }
@@ -67,7 +85,9 @@ EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
         if (strcmpa(v, (CHAR8 *)"0") == 0 ||
             strcmpa(v, (CHAR8 *)"no") == 0 ||
             strcmpa(v, (CHAR8 *)"n") == 0 ||
-            strcmpa(v, (CHAR8 *)"false") == 0) {
+            strcmpa(v, (CHAR8 *)"false") == 0 ||
+            strcmpa(v, (CHAR8 *)"f") == 0 ||
+            strcmpa(v, (CHAR8 *)"off") == 0) {
                 *b = FALSE;
                 return EFI_SUCCESS;
         }
@@ -76,24 +96,37 @@ EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
 }
 
 EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, UINT32 flags) {
+        assert(vendor);
+        assert(name);
+        assert(buf || size == 0);
+
         flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
         return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
 }
 
 EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) {
-        return efivar_set_raw(vendor, name, value, value ? (StrLen(value) + 1) * sizeof(CHAR16) : 0, flags);
+        assert(vendor);
+        assert(name);
+
+        return efivar_set_raw(vendor, name, value, value ? StrSize(value) : 0, flags);
 }
 
 EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) {
         CHAR16 str[32];
 
-        SPrint(str, 32, L"%u", i);
+        assert(vendor);
+        assert(name);
+
+        SPrint(str, ELEMENTSOF(str), L"%u", i);
         return efivar_set(vendor, name, str, flags);
 }
 
 EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 value, UINT32 flags) {
         UINT8 buf[4];
 
+        assert(vendor);
+        assert(name);
+
         buf[0] = (UINT8)(value >> 0U & 0xFF);
         buf[1] = (UINT8)(value >> 8U & 0xFF);
         buf[2] = (UINT8)(value >> 16U & 0xFF);
@@ -105,6 +138,9 @@ EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
 EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 value, UINT32 flags) {
         UINT8 buf[8];
 
+        assert(vendor);
+        assert(name);
+
         buf[0] = (UINT8)(value >> 0U & 0xFF);
         buf[1] = (UINT8)(value >> 8U & 0xFF);
         buf[2] = (UINT8)(value >> 16U & 0xFF);
@@ -118,35 +154,38 @@ EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
 }
 
 EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value) {
-        _cleanup_freepool_ CHAR8 *buf = NULL;
+        _cleanup_freepool_ CHAR16 *buf = NULL;
         EFI_STATUS err;
         CHAR16 *val;
         UINTN size;
 
-        err = efivar_get_raw(vendor, name, &buf, &size);
+        assert(vendor);
+        assert(name);
+
+        err = efivar_get_raw(vendor, name, (CHAR8**)&buf, &size);
         if (EFI_ERROR(err))
                 return err;
 
         /* Make sure there are no incomplete characters in the buffer */
-        if ((size % 2) != 0)
+        if ((size % sizeof(CHAR16)) != 0)
                 return EFI_INVALID_PARAMETER;
 
         if (!value)
                 return EFI_SUCCESS;
 
         /* Return buffer directly if it happens to be NUL terminated already */
-        if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) {
-                *value = (CHAR16*) TAKE_PTR(buf);
+        if (size >= sizeof(CHAR16) && buf[size/sizeof(CHAR16)] == 0) {
+                *value = TAKE_PTR(buf);
                 return EFI_SUCCESS;
         }
 
         /* Make sure a terminating NUL is available at the end */
-        val = AllocatePool(size + 2);
+        val = AllocatePool(size + sizeof(CHAR16));
         if (!val)
                 return EFI_OUT_OF_RESOURCES;
 
         CopyMem(val, buf, size);
-        val[size/2] = 0; /* NUL terminate */
+        val[size / sizeof(CHAR16)] = 0; /* NUL terminate */
 
         *value = val;
         return EFI_SUCCESS;
@@ -156,8 +195,12 @@ EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UI
         _cleanup_freepool_ CHAR16 *val = NULL;
         EFI_STATUS err;
 
+        assert(vendor);
+        assert(name);
+        assert(i);
+
         err = efivar_get(vendor, name, &val);
-        if (!EFI_ERROR(err) && i)
+        if (!EFI_ERROR(err))
                 *i = Atoi(val);
 
         return err;
@@ -168,6 +211,9 @@ EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
         UINTN size;
         EFI_STATUS err;
 
+        assert(vendor);
+        assert(name);
+
         err = efivar_get_raw(vendor, name, &buf, &size);
         if (!EFI_ERROR(err) && ret) {
                 if (size != sizeof(UINT32))
@@ -185,6 +231,9 @@ EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
         UINTN size;
         EFI_STATUS err;
 
+        assert(vendor);
+        assert(name);
+
         err = efivar_get_raw(vendor, name, &buf, &size);
         if (!EFI_ERROR(err) && ret) {
                 if (size != sizeof(UINT64))
@@ -203,6 +252,9 @@ EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **bu
         UINTN l;
         EFI_STATUS err;
 
+        assert(vendor);
+        assert(name);
+
         l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
         buf = AllocatePool(l);
         if (!buf)
@@ -226,6 +278,10 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOO
         UINTN size;
         EFI_STATUS err;
 
+        assert(vendor);
+        assert(name);
+        assert(ret);
+
         err = efivar_get_raw(vendor, name, &b, &size);
         if (!EFI_ERROR(err))
                 *ret = *b > 0;
@@ -236,12 +292,15 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOO
 VOID efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) {
         CHAR16 str[32];
 
+        assert(vendor);
+        assert(name);
+
         if (usec == 0)
                 usec = time_usec();
         if (usec == 0)
                 return;
 
-        SPrint(str, 32, L"%ld", usec);
+        SPrint(str, ELEMENTSOF(str), L"%ld", usec);
         efivar_set(vendor, name, str, 0);
 }
 
@@ -249,6 +308,9 @@ static INTN utf8_to_16(const CHAR8 *stra, CHAR16 *c) {
         CHAR16 unichar;
         UINTN len;
 
+        assert(stra);
+        assert(c);
+
         if (!(stra[0] & 0x80))
                 len = 1;
         else if ((stra[0] & 0xe0) == 0xc0)
@@ -302,6 +364,8 @@ CHAR16 *stra_to_str(const CHAR8 *stra) {
         UINTN i;
         CHAR16 *str;
 
+        assert(stra);
+
         len = strlena(stra);
         str = AllocatePool((len + 1) * sizeof(CHAR16));
 
@@ -330,6 +394,8 @@ CHAR16 *stra_to_path(const CHAR8 *stra) {
         UINTN len;
         UINTN i;
 
+        assert(stra);
+
         len = strlena(stra);
         str = AllocatePool((len + 2) * sizeof(CHAR16));
 
@@ -362,6 +428,7 @@ CHAR16 *stra_to_path(const CHAR8 *stra) {
 }
 
 CHAR8 *strchra(const CHAR8 *s, CHAR8 c) {
+        assert(s);
         do {
                 if (*s == c)
                         return (CHAR8*) s;
@@ -374,6 +441,9 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s
         _cleanup_freepool_ CHAR8 *buf = NULL;
         EFI_STATUS err;
 
+        assert(name);
+        assert(ret);
+
         err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
         if (EFI_ERROR(err))
                 return err;
@@ -411,8 +481,44 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s
         return err;
 }
 
+VOID log_error_stall(const CHAR16 *fmt, ...) {
+        va_list args;
+
+        assert(fmt);
+
+        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
+
+        Print(L"\n");
+        va_start(args, fmt);
+        VPrint(fmt, args);
+        va_end(args);
+        Print(L"\n");
+
+        uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+}
+
 EFI_STATUS log_oom(void) {
-        Print(L"Out of memory.");
-        (void) uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
+        log_error_stall(L"Out of memory.");
         return EFI_OUT_OF_RESOURCES;
 }
+
+VOID *memmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len) {
+        assert(haystack || haystack_len == 0);
+        assert(needle || needle_len == 0);
+
+        if (needle_len == 0)
+                return (VOID*)haystack;
+
+        for (const CHAR8 *h = haystack, *n = needle; haystack_len >= needle_len; h++, haystack_len--)
+                if (*h == *n && CompareMem(h + 1, n + 1, needle_len - 1) == 0)
+                        return (VOID*)h;
+
+        return NULL;
+}
+
+VOID print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str) {
+        assert(str);
+        uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, x, y);
+        uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, attr);
+        uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, (CHAR16*)str);
+}
index 1a42b0103344b1ce683c92b7cc2a0b64b200f8ae..9eef51cc7e91da5f23e72343b120b04d6cb79e22 100644 (file)
@@ -74,4 +74,22 @@ static inline void FileHandleClosep(EFI_FILE_HANDLE *handle) {
 #define UINT64_MAX ((UINT64) -1)
 #endif
 
+VOID log_error_stall(const CHAR16 *fmt, ...);
 EFI_STATUS log_oom(void);
+
+/* This works just like log_error_errno() from userspace, but requires you
+ * to provide err a second time if you want to use %r in the message! */
+#define log_error_status_stall(err, fmt, ...) \
+        ({ \
+                log_error_stall(fmt, ##__VA_ARGS__); \
+                err; \
+        })
+
+VOID *memmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len);
+
+static inline VOID *mempmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len) {
+        CHAR8 *p = memmem_safe(haystack, haystack_len, needle, needle_len);
+        return p ? p + needle_len : NULL;
+}
+
+VOID print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str);
index 89b32f4c73fddd1864ef1b89594b27723a3cbc04..4dbb11f15b935d1a895e6cb08b78c9d62e3c8921 100644 (file)
@@ -147,7 +147,7 @@ static int parse_xml_annotation(Context *context, uint64_t *flags) {
                         break;
 
                 default:
-                        assert_not_reached("Bad state");
+                        assert_not_reached();
                 }
         }
 }
index 8aa769e0f9232ca1b0bf0449b9169648d76feeaa..fb91fb41b6e8fbb48d147467df50431ddf135a57 100644 (file)
@@ -121,7 +121,7 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) {
                         break;
 
                 default:
-                        assert_not_reached("Hmm, unknown transport type.");
+                        assert_not_reached();
                 }
         }
         if (r < 0)
@@ -339,9 +339,7 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
                         if (r < 0)
                                 log_debug_errno(r, "Failed to acquire credentials of service %s, ignoring: %m", k);
                         else {
-                                char m[SD_ID128_STRING_MAX];
-
-                                r = table_add_cell(table, NULL, TABLE_STRING, sd_id128_to_string(mid, m));
+                                r = table_add_cell(table, NULL, TABLE_ID128, &mid);
                                 if (r < 0)
                                         return table_log_add_error(r);
 
@@ -718,7 +716,7 @@ static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) {
                         break;
 
                 default:
-                        assert_not_reached("Unknown basic type.");
+                        assert_not_reached();
                 }
 
                 needs_space = true;
@@ -1957,7 +1955,7 @@ static int json_transform_one(sd_bus_message *m, JsonVariant **ret) {
                 break;
 
         default:
-                assert_not_reached("Unexpected element type");
+                assert_not_reached();
         }
 
         *ret = TAKE_PTR(v);
@@ -2525,7 +2523,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index f39aa53da0b9123e3deec4fa064449e10abef223..ab5f153d9df1c51f15093f3875aadb4ef61e018a 100644 (file)
@@ -141,7 +141,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_machine && arg_show_unit != SHOW_UNIT_NONE)
index 50b0f63552c284e2bba69309304090f0876955a4..e5ab904c4fc0d4ad185705e047a59ce7f1b9aa81 100644 (file)
@@ -95,7 +95,7 @@ static Group *group_free(Group *g) {
 
 static const char *maybe_format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
         if (arg_raw) {
-               snprintf(buf, l, USEC_FMT, t);
+               (void) snprintf(buf, l, USEC_FMT, t);
                return buf;
         }
         return format_timespan(buf, l, t, accuracy);
@@ -109,7 +109,7 @@ static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, uint64
         if (!is_valid)
                 return "-";
         if (arg_raw) {
-                snprintf(buf, l, "%" PRIu64, t);
+                (void) snprintf(buf, l, "%" PRIu64, t);
                 return buf;
         }
         return format_bytes(buf, l, t);
@@ -885,7 +885,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind == argc - 1)
index 8a345a44981cba862f8f386c7449068ab5c00191..4daa7f76b0cf1fe9ae13269369ff2ac056db4707 100644 (file)
@@ -9,6 +9,7 @@
 #include "fileio.h"
 #include "nulstr-util.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
@@ -260,7 +261,7 @@ int bpf_devices_apply_policy(
         r = bpf_program_cgroup_attach(prog, BPF_CGROUP_DEVICE, controller_path, BPF_F_ALLOW_MULTI);
         if (r < 0)
                 return log_error_errno(r, "Attaching device control BPF program to cgroup %s failed: %m",
-                                       cgroup_path);
+                                       empty_to_root(cgroup_path));
 
  finish:
         /* Unref the old BPF program (which will implicitly detach it) right before attaching the new program. */
diff --git a/src/core/bpf/restrict_ifaces/meson.build b/src/core/bpf/restrict_ifaces/meson.build
new file mode 100644 (file)
index 0000000..5bf024d
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+if conf.get('BPF_FRAMEWORK') == 1
+        restrict_ifaces_skel_h = custom_target(
+                'restrict-ifaces.skel.h',
+                input : 'restrict-ifaces.bpf.c',
+                output : 'restrict-ifaces.skel.h',
+                command : [build_bpf_skel_py,
+                           '--clang_exec', clang.path(),
+                           '--llvm_strip_exec', llvm_strip.path(),
+                           '--bpftool_exec', bpftool.path(),
+                           '--arch', host_machine.cpu_family(),
+                           '@INPUT@', '@OUTPUT@'])
+endif
diff --git a/src/core/bpf/restrict_ifaces/restrict-ifaces.bpf.c b/src/core/bpf/restrict_ifaces/restrict-ifaces.bpf.c
new file mode 100644 (file)
index 0000000..347a3a8
--- /dev/null
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+/* <linux/bpf.h> must precede <bpf/bpf_helpers.h> due to integer types
+ * in bpf helpers signatures.
+ */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+const volatile __u8 is_allow_list = 0;
+
+/* Map containing the network interfaces indexes.
+ * The interpretation of the map depends on the value of is_allow_list.
+ */
+struct {
+        __uint(type, BPF_MAP_TYPE_HASH);
+        __type(key, __u32);
+        __type(value, __u8);
+} sd_restrictif SEC(".maps");
+
+#define DROP 0
+#define PASS 1
+
+static inline int restrict_network_interfaces_impl(const struct __sk_buff *sk) {
+        __u32 zero = 0, ifindex;
+        __u8 *lookup_result;
+
+        ifindex = sk->ifindex;
+        lookup_result = bpf_map_lookup_elem(&sd_restrictif, &ifindex);
+        if (is_allow_list) {
+                /* allow-list: let the packet pass if iface in the list */
+                if (lookup_result)
+                        return PASS;
+        } else {
+            /* deny-list: let the packet pass if iface *not* in the list */
+                if (!lookup_result)
+                        return PASS;
+        }
+
+        return DROP;
+}
+
+SEC("cgroup_skb/egress")
+int sd_restrictif_e(const struct __sk_buff *sk) {
+        return restrict_network_interfaces_impl(sk);
+}
+
+SEC("cgroup_skb/ingress")
+int sd_restrictif_i(const struct __sk_buff *sk) {
+        return restrict_network_interfaces_impl(sk);
+}
+
+static const char _license[] SEC("license") = "LGPL-2.1-or-later";
index e5fd6672bb3482d6cdf97e9555497e20746f0870..52bdf54b5fb690159326b1b94134dda8c7474227 100644 (file)
@@ -28,6 +28,7 @@
 #include "percent-util.h"
 #include "process-util.h"
 #include "procfs-util.h"
+#include "restrict-ifaces.h"
 #include "special.h"
 #include "stat-util.h"
 #include "stdio-util.h"
@@ -84,7 +85,7 @@ static int set_attribute_and_warn(Unit *u, const char *controller, const char *a
         r = cg_set_attribute(controller, u->cgroup_path, attribute, value);
         if (r < 0)
                 log_unit_full_errno(u, LOG_LEVEL_CGROUP_WRITE(r), r, "Failed to set '%s' attribute on '%s' to '%.*s': %m",
-                                    strna(attribute), isempty(u->cgroup_path) ? "/" : u->cgroup_path, (int) strcspn(value, NEWLINE), value);
+                                    strna(attribute), empty_to_root(u->cgroup_path), (int) strcspn(value, NEWLINE), value);
 
         return r;
 }
@@ -246,6 +247,8 @@ void cgroup_context_done(CGroupContext *c) {
         while (c->bpf_foreign_programs)
                 cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
 
+        c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
+
         cpu_set_reset(&c->cpuset_cpus);
         cpu_set_reset(&c->cpuset_mems);
 }
@@ -372,11 +375,11 @@ static char *format_cgroup_memory_limit_comparison(char *buf, size_t l, Unit *u,
         }
 
         if (r < 0) {
-                snprintf(buf, l, " (error getting kernel value: %s)", strerror_safe(r));
+                (void) snprintf(buf, l, " (error getting kernel value: %s)", strerror_safe(r));
                 return buf;
         }
 
-        snprintf(buf, l, " (different value in kernel: %" PRIu64 ")", kval);
+        (void) snprintf(buf, l, " (different value in kernel: %" PRIu64 ")", kval);
 
         return buf;
 }
@@ -583,6 +586,12 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
                         cgroup_context_dump_socket_bind_item(bi, f);
                 fputc('\n', f);
         }
+
+        if (c->restrict_network_interfaces) {
+                char *iface;
+                SET_FOREACH(iface, c->restrict_network_interfaces)
+                        fprintf(f, "%sRestrictNetworkInterfaces: %s\n", prefix, iface);
+        }
 }
 
 void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE *f) {
@@ -706,30 +715,29 @@ void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path) {
         if (c->moom_preference == MANAGED_OOM_PREFERENCE_OMIT) {
                 r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_omit", "1", 1, 0);
                 if (r < 0)
-                        log_unit_debug_errno(u, r, "Failed to set oomd_omit flag on control group %s, ignoring: %m", cgroup_path);
+                        log_unit_debug_errno(u, r, "Failed to set oomd_omit flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
         }
 
         if (c->moom_preference == MANAGED_OOM_PREFERENCE_AVOID) {
                 r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_avoid", "1", 1, 0);
                 if (r < 0)
-                        log_unit_debug_errno(u, r, "Failed to set oomd_avoid flag on control group %s, ignoring: %m", cgroup_path);
+                        log_unit_debug_errno(u, r, "Failed to set oomd_avoid flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
         }
 
         if (c->moom_preference != MANAGED_OOM_PREFERENCE_AVOID) {
                 r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_avoid");
-                if (r != -ENODATA)
-                        log_unit_debug_errno(u, r, "Failed to remove oomd_avoid flag on control group %s, ignoring: %m", cgroup_path);
+                if (r < 0 && r != -ENODATA)
+                        log_unit_debug_errno(u, r, "Failed to remove oomd_avoid flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
         }
 
         if (c->moom_preference != MANAGED_OOM_PREFERENCE_OMIT) {
                 r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_omit");
-                if (r != -ENODATA)
-                        log_unit_debug_errno(u, r, "Failed to remove oomd_omit flag on control group %s, ignoring: %m", cgroup_path);
+                if (r < 0 && r != -ENODATA)
+                        log_unit_debug_errno(u, r, "Failed to remove oomd_omit flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
         }
 }
 
 static void cgroup_xattr_apply(Unit *u) {
-        char ids[SD_ID128_STRING_MAX];
         int r;
 
         assert(u);
@@ -740,10 +748,10 @@ static void cgroup_xattr_apply(Unit *u) {
         if (!sd_id128_is_null(u->invocation_id)) {
                 r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
                                  "trusted.invocation_id",
-                                 sd_id128_to_string(u->invocation_id, ids), 32,
+                                 SD_ID128_TO_STRING(u->invocation_id), 32,
                                  0);
                 if (r < 0)
-                        log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", u->cgroup_path);
+                        log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
         }
 
         if (unit_cgroup_delegate(u)) {
@@ -752,11 +760,11 @@ static void cgroup_xattr_apply(Unit *u) {
                                  "1", 1,
                                  0);
                 if (r < 0)
-                        log_unit_debug_errno(u, r, "Failed to set delegate flag on control group %s, ignoring: %m", u->cgroup_path);
+                        log_unit_debug_errno(u, r, "Failed to set delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
         } else {
                 r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "trusted.delegate");
-                if (r != -ENODATA)
-                        log_unit_debug_errno(u, r, "Failed to remove delegate flag on control group %s, ignoring: %m", u->cgroup_path);
+                if (r < 0 && r != -ENODATA)
+                        log_unit_debug_errno(u, r, "Failed to remove delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
         }
 
         cgroup_oomd_xattr_apply(u, u->cgroup_path);
@@ -1097,6 +1105,12 @@ static void cgroup_apply_socket_bind(Unit *u) {
         (void) bpf_socket_bind_install(u);
 }
 
+static void cgroup_apply_restrict_network_interfaces(Unit *u) {
+        assert(u);
+
+        (void) restrict_network_interfaces_install(u);
+}
+
 static int cgroup_apply_devices(Unit *u) {
         _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
         const char *path;
@@ -1527,6 +1541,9 @@ static void cgroup_context_apply(
 
         if (apply_mask & CGROUP_MASK_BPF_SOCKET_BIND)
                 cgroup_apply_socket_bind(u);
+
+        if (apply_mask & CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES)
+                cgroup_apply_restrict_network_interfaces(u);
 }
 
 static bool unit_get_needs_bpf_firewall(Unit *u) {
@@ -1580,6 +1597,17 @@ static bool unit_get_needs_socket_bind(Unit *u) {
         return c->socket_bind_allow || c->socket_bind_deny;
 }
 
+static bool unit_get_needs_restrict_network_interfaces(Unit *u) {
+        CGroupContext *c;
+        assert(u);
+
+        c = unit_get_cgroup_context(u);
+        if (!c)
+                return false;
+
+        return !set_isempty(c->restrict_network_interfaces);
+}
+
 static CGroupMask unit_get_cgroup_mask(Unit *u) {
         CGroupMask mask = 0;
         CGroupContext *c;
@@ -1635,6 +1663,9 @@ static CGroupMask unit_get_bpf_mask(Unit *u) {
         if (unit_get_needs_socket_bind(u))
                 mask |= CGROUP_MASK_BPF_SOCKET_BIND;
 
+        if (unit_get_needs_restrict_network_interfaces(u))
+                mask |= CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES;
+
         return mask;
 }
 
@@ -1913,12 +1944,12 @@ int unit_watch_cgroup(Unit *u) {
                                       * is not an error */
                         return 0;
 
-                return log_unit_error_errno(u, errno, "Failed to add control inotify watch descriptor for control group %s: %m", u->cgroup_path);
+                return log_unit_error_errno(u, errno, "Failed to add control inotify watch descriptor for control group %s: %m", empty_to_root(u->cgroup_path));
         }
 
         r = hashmap_put(u->manager->cgroup_control_inotify_wd_unit, INT_TO_PTR(u->cgroup_control_inotify_wd), u);
         if (r < 0)
-                return log_unit_error_errno(u, r, "Failed to add control inotify watch descriptor to hash map: %m");
+                return log_unit_error_errno(u, r, "Failed to add control inotify watch descriptor for control group %s to hash map: %m", empty_to_root(u->cgroup_path));
 
         return 0;
 }
@@ -1976,12 +2007,12 @@ int unit_watch_cgroup_memory(Unit *u) {
                                       * is not an error */
                         return 0;
 
-                return log_unit_error_errno(u, errno, "Failed to add memory inotify watch descriptor for control group %s: %m", u->cgroup_path);
+                return log_unit_error_errno(u, errno, "Failed to add memory inotify watch descriptor for control group %s: %m", empty_to_root(u->cgroup_path));
         }
 
         r = hashmap_put(u->manager->cgroup_memory_inotify_wd_unit, INT_TO_PTR(u->cgroup_memory_inotify_wd), u);
         if (r < 0)
-                return log_unit_error_errno(u, r, "Failed to add memory inotify watch descriptor to hash map: %m");
+                return log_unit_error_errno(u, r, "Failed to add memory inotify watch descriptor for control group %s to hash map: %m", empty_to_root(u->cgroup_path));
 
         return 0;
 }
@@ -2004,9 +2035,9 @@ int unit_pick_cgroup_path(Unit *u) {
 
         r = unit_set_cgroup_path(u, path);
         if (r == -EEXIST)
-                return log_unit_error_errno(u, r, "Control group %s exists already.", path);
+                return log_unit_error_errno(u, r, "Control group %s exists already.", empty_to_root(path));
         if (r < 0)
-                return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", path);
+                return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", empty_to_root(path));
 
         return 0;
 }
@@ -2034,7 +2065,7 @@ static int unit_update_cgroup(
         /* First, create our own group */
         r = cg_create_everywhere(u->manager->cgroup_supported, target_mask, u->cgroup_path);
         if (r < 0)
-                return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", u->cgroup_path);
+                return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", empty_to_root(u->cgroup_path));
         created = r;
 
         /* Start watching it */
@@ -2050,7 +2081,7 @@ static int unit_update_cgroup(
                 /* Enable all controllers we need */
                 r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path, &result_mask);
                 if (r < 0)
-                        log_unit_warning_errno(u, r, "Failed to enable/disable controllers on cgroup %s, ignoring: %m", u->cgroup_path);
+                        log_unit_warning_errno(u, r, "Failed to enable/disable controllers on cgroup %s, ignoring: %m", empty_to_root(u->cgroup_path));
 
                 /* Remember what's actually enabled now */
                 u->cgroup_enabled_mask = result_mask;
@@ -2072,12 +2103,12 @@ static int unit_update_cgroup(
         if (cg_all_unified() == 0) {
                 r = cg_migrate_v1_controllers(u->manager->cgroup_supported, migrate_mask, u->cgroup_path, migrate_callback, u);
                 if (r < 0)
-                        log_unit_warning_errno(u, r, "Failed to migrate controller cgroups from %s, ignoring: %m", u->cgroup_path);
+                        log_unit_warning_errno(u, r, "Failed to migrate controller cgroups from %s, ignoring: %m", empty_to_root(u->cgroup_path));
 
                 is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
                 r = cg_trim_v1_controllers(u->manager->cgroup_supported, ~target_mask, u->cgroup_path, !is_root_slice);
                 if (r < 0)
-                        log_unit_warning_errno(u, r, "Failed to delete controller cgroups %s, ignoring: %m", u->cgroup_path);
+                        log_unit_warning_errno(u, r, "Failed to delete controller cgroups %s, ignoring: %m", empty_to_root(u->cgroup_path));
         }
 
         /* Set attributes */
@@ -2167,7 +2198,7 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) {
 
                         log_unit_full_errno(u, again ? LOG_DEBUG : LOG_INFO,  q,
                                             "Couldn't move process "PID_FMT" to%s requested cgroup '%s': %m",
-                                            pid, again ? " directly" : "", p);
+                                            pid, again ? " directly" : "", empty_to_root(p));
 
                         if (again) {
                                 int z;
@@ -2179,7 +2210,7 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) {
 
                                 z = unit_attach_pid_to_cgroup_via_bus(u, pid, suffix_path);
                                 if (z < 0)
-                                        log_unit_info_errno(u, z, "Couldn't move process "PID_FMT" to requested cgroup '%s' (directly or via the system bus): %m", pid, p);
+                                        log_unit_info_errno(u, z, "Couldn't move process "PID_FMT" to requested cgroup '%s' (directly or via the system bus): %m", pid, empty_to_root(p));
                                 else
                                         continue; /* When the bus thing worked via the bus we are fully done for this PID. */
                         }
@@ -2213,7 +2244,7 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) {
                                         continue; /* Success! */
 
                                 log_unit_debug_errno(u, q, "Failed to attach PID " PID_FMT " to requested cgroup %s in controller %s, falling back to unit's cgroup: %m",
-                                                     pid, p, cgroup_controller_to_string(c));
+                                                     pid, empty_to_root(p), cgroup_controller_to_string(c));
                         }
 
                         /* So this controller is either not delegate or realized, or something else weird happened. In
@@ -2648,7 +2679,7 @@ void unit_prune_cgroup(Unit *u) {
                  * the containing slice is stopped. So even if we failed now, this unit shouldn't assume
                  * that the cgroup is still realized the next time it is started. Do not return early
                  * on error, continue cleanup. */
-                log_unit_full_errno(u, r == -EBUSY ? LOG_DEBUG : LOG_WARNING, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path);
+                log_unit_full_errno(u, r == -EBUSY ? LOG_DEBUG : LOG_WARNING, r, "Failed to destroy cgroup %s, ignoring: %m", empty_to_root(u->cgroup_path));
 
         if (is_root_slice)
                 return;
@@ -2861,7 +2892,7 @@ void unit_add_to_cgroup_empty_queue(Unit *u) {
 
         r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
         if (r < 0) {
-                log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", u->cgroup_path);
+                log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", empty_to_root(u->cgroup_path));
                 return;
         }
         if (r == 0)
@@ -3031,6 +3062,9 @@ static int unit_check_cgroup_events(Unit *u) {
 
         assert(u);
 
+        if (!u->cgroup_path)
+                return 0;
+
         r = cg_get_keyed_attribute_graceful(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events",
                                             STRV_MAKE("populated", "frozen"), values);
         if (r < 0)
@@ -3129,6 +3163,11 @@ static int cg_bpf_mask_supported(CGroupMask *ret) {
         if (r > 0)
                 mask |= CGROUP_MASK_BPF_SOCKET_BIND;
 
+        /* BPF-based cgroup_skb/{egress|ingress} hooks */
+        r = restrict_network_interfaces_supported();
+        if (r > 0)
+                mask |= CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES;
+
         *ret = mask;
         return 0;
 }
@@ -3863,6 +3902,21 @@ void unit_invalidate_cgroup_bpf(Unit *u) {
         }
 }
 
+void unit_cgroup_catchup(Unit *u) {
+        assert(u);
+
+        if (!UNIT_HAS_CGROUP_CONTEXT(u))
+                return;
+
+        /* We dropped the inotify watch during reexec/reload, so we need to
+         * check these as they may have changed.
+         * Note that (currently) the kernel doesn't actually update cgroup
+         * file modification times, so we can't just serialize and then check
+         * the mtime for file(s) we are interested in. */
+        (void) unit_check_cgroup_events(u);
+        unit_add_to_cgroup_oom_queue(u);
+}
+
 bool unit_cgroup_delegate(Unit *u) {
         CGroupContext *c;
 
index ea929368cbd4d409a6dae3136ddc8d5d5d612a6c..99bf7e22d8f18fc3b08ba6bca4da8eea69c425f6 100644 (file)
@@ -160,6 +160,9 @@ struct CGroupContext {
         char **ip_filters_egress;
         LIST_HEAD(CGroupBPFForeignProgram, bpf_foreign_programs);
 
+        Set *restrict_network_interfaces;
+        bool restrict_network_interfaces_is_allow_list;
+
         /* For legacy hierarchies */
         uint64_t cpu_shares;
         uint64_t startup_cpu_shares;
@@ -313,6 +316,8 @@ void manager_invalidate_startup_units(Manager *m);
 const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_;
 CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_;
 
+void unit_cgroup_catchup(Unit *u);
+
 bool unit_cgroup_delegate(Unit *u);
 
 int compare_job_priority(const void *a, const void *b);
index 77fbca8389061c0bbe548e1f3de1f81d2d3ba36f..75efa9fe1f37836140a4d16ebc730b43b33e708c 100644 (file)
@@ -20,6 +20,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "percent-util.h"
+#include "socket-util.h"
 
 BUS_DEFINE_PROPERTY_GET(bus_property_get_tasks_max, "t", TasksMax, tasks_max_resolve);
 
@@ -403,6 +404,47 @@ static int property_get_socket_bind(
         return sd_bus_message_close_container(reply);
 }
 
+static int property_get_restrict_network_interfaces(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        int r;
+        CGroupContext *c = userdata;
+        char *iface;
+
+        assert(bus);
+        assert(reply);
+        assert(c);
+
+        r = sd_bus_message_open_container(reply, 'r', "bas");
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "b", c->restrict_network_interfaces_is_allow_list);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_open_container(reply, 'a', "s");
+        if (r < 0)
+                return r;
+
+        SET_FOREACH(iface, c->restrict_network_interfaces) {
+                r = sd_bus_message_append(reply, "s", iface);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_bus_message_close_container(reply);
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_close_container(reply);
+}
+
 const sd_bus_vtable bus_cgroup_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
@@ -457,6 +499,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
         SD_BUS_PROPERTY("BPFProgram", "a(ss)", property_get_bpf_foreign_program, 0, 0),
         SD_BUS_PROPERTY("SocketBindAllow", "a(iiqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_allow), 0),
         SD_BUS_PROPERTY("SocketBindDeny", "a(iiqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_deny), 0),
+        SD_BUS_PROPERTY("RestrictNetworkInterfaces", "(bas)", property_get_restrict_network_interfaces, 0, 0),
         SD_BUS_VTABLE_END
 };
 
@@ -1963,6 +2006,64 @@ int bus_cgroup_set_property(
 
                 return 1;
         }
+        if (streq(name, "RestrictNetworkInterfaces")) {
+                int is_allow_list;
+                _cleanup_strv_free_ char **l = NULL;
+
+                r = sd_bus_message_enter_container(message, 'r', "bas");
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_read(message, "b", &is_allow_list);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_read_strv(message, &l);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_exit_container(message);
+                if (r < 0)
+                        return r;
+
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                        _cleanup_free_ char *joined = NULL;
+                        char **s;
+
+                        if (strv_isempty(l)) {
+                                c->restrict_network_interfaces_is_allow_list = false;
+                                c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
+
+                                unit_write_settingf(u, flags, name, "%s=", name);
+                                return 1;
+                        }
+
+                        if (set_isempty(c->restrict_network_interfaces))
+                                c->restrict_network_interfaces_is_allow_list = is_allow_list;
+
+                        STRV_FOREACH(s, l) {
+                                if (!ifname_valid(*s)) {
+                                        log_full(LOG_WARNING, "Invalid interface name, ignoring: %s", *s);
+                                        continue;
+                                }
+                                if (c->restrict_network_interfaces_is_allow_list != (bool) is_allow_list)
+                                        free(set_remove(c->restrict_network_interfaces, *s));
+                                else {
+                                        r = set_put_strdup(&c->restrict_network_interfaces, *s);
+                                        if (r < 0)
+                                                return log_oom();
+                                }
+                        }
+
+                        joined = strv_join(l, " ");
+                        if (!joined)
+                                return -ENOMEM;
+
+                        unit_write_settingf(u, flags, name, "%s=%s%s", name, is_allow_list ? "" : "~", joined);
+                }
+
+                return 1;
+        }
 
         if (streq(name, "DisableControllers") || (u->transient && u->load_state == UNIT_STUB))
                 return bus_cgroup_set_transient_property(u, c, name, message, flags, error);
index f7784bb73dce3c678f6b3ab701d87af42c58f8ba..5ea97b919403146c94d5ee06f489cba813cf62f5 100644 (file)
@@ -1,6 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <linux/oom.h>
 #include <sys/mount.h>
 #include <sys/prctl.h>
 
@@ -95,10 +94,6 @@ static int property_get_environment_files(
         return sd_bus_message_close_container(reply);
 }
 
-static bool oom_score_adjust_is_valid(int oa) {
-        return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX;
-}
-
 static int property_get_oom_score_adjust(
                 sd_bus *bus,
                 const char *path,
@@ -2893,7 +2888,7 @@ int bus_exec_context_set_transient_property(
                                 if (!joined)
                                         return -ENOMEM;
 
-                                e = strv_env_merge(2, c->environment, l);
+                                e = strv_env_merge(c->environment, l);
                                 if (!e)
                                         return -ENOMEM;
 
@@ -2927,7 +2922,7 @@ int bus_exec_context_set_transient_property(
                                 if (!joined)
                                         return -ENOMEM;
 
-                                e = strv_env_merge(2, c->unset_environment, l);
+                                e = strv_env_merge(c->unset_environment, l);
                                 if (!e)
                                         return -ENOMEM;
 
index f45a5c16eb4ee6de31a9ac3381909c0e7c4cb1bb..a3b1e0442f4f3c16149923aa883bebffb8b9202c 100644 (file)
@@ -65,7 +65,7 @@ static int property_get_listen(
                                 break;
 
                         default:
-                                assert_not_reached("Unknown socket type");
+                                assert_not_reached();
                 }
 
                 r = sd_bus_message_append(reply, "(ss)", socket_port_type_to_string(p), a);
index aa10939a041eaa4ac7ac3129e7a16dd32ce30a28..124b9e496b26d1975fb4309c89bf4b5452e28cc4 100644 (file)
@@ -905,7 +905,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("OnSuccesJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("OnSuccesJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* deprecated */
+        SD_BUS_PROPERTY("OnSuccessJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
index 5ed5ceb290429f7a614942bda1abf85e3c582356..66841984feab5fd42e0c359805349cefd479a340 100644 (file)
@@ -771,11 +771,11 @@ static Unit *device_following(Unit *u) {
                 return NULL;
 
         /* Make everybody follow the unit that's named after the sysfs path */
-        LIST_FOREACH_AFTER(same_sysfs, other, d)
+        LIST_FOREACH(same_sysfs, other, d->same_sysfs_next)
                 if (startswith(UNIT(other)->id, "sys-"))
                         return UNIT(other);
 
-        LIST_FOREACH_BEFORE(same_sysfs, other, d) {
+        LIST_FOREACH_BACKWARDS(same_sysfs, other, d->same_sysfs_prev) {
                 if (startswith(UNIT(other)->id, "sys-"))
                         return UNIT(other);
 
@@ -802,13 +802,13 @@ static int device_following_set(Unit *u, Set **_set) {
         if (!set)
                 return -ENOMEM;
 
-        LIST_FOREACH_AFTER(same_sysfs, other, d) {
+        LIST_FOREACH(same_sysfs, other, d->same_sysfs_next) {
                 r = set_put(set, other);
                 if (r < 0)
                         return r;
         }
 
-        LIST_FOREACH_BEFORE(same_sysfs, other, d) {
+        LIST_FOREACH_BACKWARDS(same_sysfs, other, d->same_sysfs_prev) {
                 r = set_put(set, other);
                 if (r < 0)
                         return r;
index 7da87fd818bb4ce0c79202e0e29e4c4d5b0da32c..2672496724c72e2012ed7b12b4b7f383839e2e16 100644 (file)
@@ -253,7 +253,7 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
                         break;
 
                 default:
-                        assert_not_reached("unknown phase");
+                        assert_not_reached();
                 }
 
                 /* Make sure whatever we picked here actually is in the right range */
index 9e8c79e67a6b4702bb5430040a25352640576274..0234772b95326f0e9e35e94ff6dc3b3088830ca2 100644 (file)
@@ -146,7 +146,7 @@ void emergency_action(
                 break;
 
         default:
-                assert_not_reached("Unknown emergency action");
+                assert_not_reached();
         }
 }
 
index 2b49e324741be639e560e71faa3d6bd11b01ec37..c1fad61554825ae48f3f4b8212f55f0b1112ced7 100644 (file)
@@ -544,7 +544,7 @@ static int setup_input(
         }
 
         default:
-                assert_not_reached("Unknown input type");
+                assert_not_reached();
         }
 }
 
@@ -730,7 +730,7 @@ static int setup_output(
         }
 
         default:
-                assert_not_reached("Unknown error type");
+                assert_not_reached();
         }
 }
 
@@ -923,7 +923,7 @@ static int ask_for_confirmation(const char *vc, Unit *u, const char *cmdline) {
                         r = CONFIRM_EXECUTE;
                         break;
                 default:
-                        assert_not_reached("Unhandled choice");
+                        assert_not_reached();
                 }
                 break;
         }
@@ -4158,8 +4158,7 @@ static int exec_child(
                 return log_oom();
         }
 
-        accum_env = strv_env_merge(5,
-                                   params->environment,
+        accum_env = strv_env_merge(params->environment,
                                    our_env,
                                    pass_env,
                                    context->environment,
@@ -4351,7 +4350,7 @@ static int exec_child(
 
         _cleanup_free_ char *executable = NULL;
         _cleanup_close_ int executable_fd = -1;
-        r = find_executable_full(command->path, false, &executable, &executable_fd);
+        r = find_executable_full(command->path, /* root= */ NULL, false, &executable, &executable_fd);
         if (r < 0) {
                 if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
                         log_unit_struct_errno(unit, LOG_INFO, r,
@@ -5214,7 +5213,7 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c,
                         else {
                                 char **m;
 
-                                m = strv_env_merge(2, r, p);
+                                m = strv_env_merge(r, p);
                                 strv_free(r);
                                 strv_free(p);
                                 if (!m)
index a526e5e4ee63a0e84aa0ec20f51377553edf825c..dd16a0b2804ed1f5e6f1d425fa10a986fab08379 100644 (file)
@@ -407,7 +407,7 @@ bool job_type_is_redundant(JobType a, UnitActiveState b) {
                 return true;
 
         default:
-                assert_not_reached("Invalid job type");
+                assert_not_reached();
         }
 }
 
@@ -781,7 +781,7 @@ static int job_perform_on_unit(Job **j) {
                         break;
 
                 default:
-                        assert_not_reached("Invalid job type");
+                        assert_not_reached();
         }
 
         /* Log if the job still exists and the start/stop/reload function actually did something. Note that this means
@@ -849,7 +849,7 @@ int job_run_and_invalidate(Job *j) {
                         break;
 
                 default:
-                        assert_not_reached("Unknown job type");
+                        assert_not_reached();
         }
 
         if (j) {
index 96507907f67173e3e4c4f21863e92a821fa61b32..489841af7a73bf1302d25a6c4e3d58d020230e9a 100644 (file)
 {{type}}.BPFProgram,                       config_parse_bpf_foreign_program,            0,                                  offsetof({{type}}, cgroup_context)
 {{type}}.SocketBindAllow,                  config_parse_cgroup_socket_bind,             0,                                  offsetof({{type}}, cgroup_context.socket_bind_allow)
 {{type}}.SocketBindDeny,                   config_parse_cgroup_socket_bind,             0,                                  offsetof({{type}}, cgroup_context.socket_bind_deny)
+{{type}}.RestrictNetworkInterfaces,        config_parse_restrict_network_interfaces,    0,                                  offsetof({{type}}, cgroup_context)
 {%- endmacro -%}
 
 %{
index 6eefaaf27d7700c28de9f2be5f4cf34419ee9077..92815b1dbaeab966226fe747f03596f4f87ab7a1 100644 (file)
@@ -5711,6 +5711,72 @@ int config_parse_cgroup_socket_bind(
         return 0;
 }
 
+int config_parse_restrict_network_interfaces(
+                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) {
+        CGroupContext *c = data;
+        bool is_allow_rule = true;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                /* Empty assignment resets the list */
+                c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
+                return 0;
+        }
+
+        if (rvalue[0] == '~') {
+                is_allow_rule = false;
+                rvalue++;
+        }
+
+        if (set_isempty(c->restrict_network_interfaces))
+                /* Only initialize this when creating the set */
+                c->restrict_network_interfaces_is_allow_list = is_allow_rule;
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *word = NULL;
+
+                r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
+                if (r == 0)
+                        break;
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
+                        break;
+                }
+
+                if (!ifname_valid(word)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", word);
+                        continue;
+                }
+
+                if (c->restrict_network_interfaces_is_allow_list != is_allow_rule)
+                        free(set_remove(c->restrict_network_interfaces, word));
+                else {
+                        r = set_put_strdup(&c->restrict_network_interfaces, word);
+                        if (r < 0)
+                                return log_oom();
+                }
+        }
+
+        return 0;
+}
+
 static int merge_by_names(Unit **u, Set *names, const char *id) {
         char *k;
         int r;
index 45e9c397e4e1f0eca338221643f7cfceadfca5c6..fe98091ee47c4cf50b0c599dd774977ae0445f5e 100644 (file)
@@ -141,6 +141,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_socket_timestamping);
 CONFIG_PARSER_PROTOTYPE(config_parse_extension_images);
 CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program);
 CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_socket_bind);
+CONFIG_PARSER_PROTOTYPE(config_parse_restrict_network_interfaces);
 
 /* gperf prototypes */
 const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
index 64761ddb118127c80a6a5e80e467264fb1499d6e..59ddb9c48758272306590383ed090effa3c333e8 100644 (file)
@@ -85,7 +85,7 @@ int locale_setup(char ***environment) {
         else {
                 char **merged;
 
-                merged = strv_env_merge(2, *environment, add);
+                merged = strv_env_merge(*environment, add);
                 if (!merged)
                         return -ENOMEM;
 
index f3272a27370ae65bfa49d477af16766b6e856984..0914f92a2e0b09d4c5d308a67e831596f5860e16 100644 (file)
@@ -83,6 +83,7 @@
 #include "switch-root.h"
 #include "sysctl-util.h"
 #include "terminal-util.h"
+#include "time-util.h"
 #include "umask-util.h"
 #include "user-util.h"
 #include "util.h"
@@ -257,7 +258,7 @@ _noreturn_ static void crash(int sig) {
                         pid = raw_getpid();
                         (void) kill(pid, sig); /* raise() would kill the parent */
 
-                        assert_not_reached("We shouldn't be here...");
+                        assert_not_reached();
                         _exit(EXIT_EXCEPTION);
                 } else {
                         siginfo_t status;
@@ -1080,7 +1081,7 @@ static int parse_argv(int argc, char *argv[]) {
                                 return 0;
 
                 default:
-                        assert_not_reached("Unhandled option code.");
+                        assert_not_reached();
                 }
 
         if (optind < argc && getpid_cached() != 1)
@@ -1598,11 +1599,18 @@ static void initialize_clock(void) {
                  */
                 (void) clock_reset_timewarp();
 
-        r = clock_apply_epoch();
-        if (r < 0)
-                log_error_errno(r, "Current system time is before build time, but cannot correct: %m");
-        else if (r > 0)
+        ClockChangeDirection change_dir;
+        r = clock_apply_epoch(&change_dir);
+        if (r > 0 && change_dir == CLOCK_CHANGE_FORWARD)
                 log_info("System time before build time, advancing clock.");
+        else if (r > 0 && change_dir == CLOCK_CHANGE_BACKWARD)
+                log_info("System time is further ahead than %s after build time, resetting clock to build time.",
+                         FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY));
+        else if (r < 0 && change_dir == CLOCK_CHANGE_FORWARD)
+                log_error_errno(r, "Current system time is before build time, but cannot correct: %m");
+        else if (r < 0 && change_dir == CLOCK_CHANGE_BACKWARD)
+                log_error_errno(r, "Current system time is further ahead %s after build time, but cannot correct: %m",
+                                FORMAT_TIMESPAN(CLOCK_VALID_RANGE_USEC_MAX, USEC_PER_DAY));
 }
 
 static void apply_clock_update(void) {
@@ -1721,9 +1729,14 @@ static void update_numa_policy(bool skip_setup) {
                 log_warning_errno(r, "Failed to set NUMA memory policy: %m");
 }
 
-static void filter_args(const char* dst[], unsigned *pos, char **src, int argc) {
+static void filter_args(
+                const char* dst[],
+                size_t *dst_index,
+                char **src,
+                int argc) {
+
         assert(dst);
-        assert(pos);
+        assert(dst_index);
 
         /* Copy some filtered arguments into the dst array from src. */
         for (int i = 1; i < argc; i++) {
@@ -1757,8 +1770,7 @@ static void filter_args(const char* dst[], unsigned *pos, char **src, int argc)
                         continue;
 
                 /* Seems we have a good old option. Let's pass it over to the new instance. */
-                dst[*pos] = src[i];
-                (*pos)++;
+                dst[(*dst_index)++] = src[i];
         }
 }
 
@@ -1772,10 +1784,11 @@ static void do_reexecute(
                 const char *switch_root_init,
                 const char **ret_error_message) {
 
-        unsigned i, args_size;
+        size_t i, args_size;
         const char **args;
         int r;
 
+        assert(argc >= 0);
         assert(saved_rlimit_nofile);
         assert(saved_rlimit_memlock);
         assert(ret_error_message);
@@ -2034,7 +2047,7 @@ static int invoke_main_loop(
                 }
 
                 default:
-                        assert_not_reached("Unknown or unexpected manager objective.");
+                        assert_not_reached();
                 }
         }
 }
@@ -2895,7 +2908,7 @@ int main(int argc, char *argv[]) {
 
         before_startup = now(CLOCK_MONOTONIC);
 
-        r = manager_startup(m, arg_serialization, fds);
+        r = manager_startup(m, arg_serialization, fds, /* root= */ NULL);
         if (r < 0) {
                 error_message = "Failed to start up manager";
                 goto finish;
index a3607ea44f6f7b19db99ff398be4adb4b0d1448a..63679268fb0b55c5bb187f850554b0e10937396c 100644 (file)
@@ -578,8 +578,7 @@ static int manager_setup_signals(Manager *m) {
                         SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */
                         SIGRTMIN+23, /* systemd: set log level to LOG_INFO */
                         SIGRTMIN+24, /* systemd: Immediate exit (--user only) */
-
-                        /* .. one free signal here ... */
+                        SIGRTMIN+25, /* systemd: reexecute manager */
 
                         /* Apparently Linux on hppa had fewer RT signals until v3.18,
                          * SIGRTMAX was SIGRTMIN+25, and then SIGRTMIN was lowered,
@@ -1438,6 +1437,10 @@ static void manager_clear_jobs_and_units(Manager *m) {
         assert(!m->cleanup_queue);
         assert(!m->gc_unit_queue);
         assert(!m->gc_job_queue);
+        assert(!m->cgroup_realize_queue);
+        assert(!m->cgroup_empty_queue);
+        assert(!m->cgroup_oom_queue);
+        assert(!m->target_deps_queue);
         assert(!m->stop_when_unneeded_queue);
         assert(!m->start_when_upheld_queue);
         assert(!m->stop_when_bound_queue);
@@ -1538,7 +1541,7 @@ Manager* manager_free(Manager *m) {
 static void manager_enumerate_perpetual(Manager *m) {
         assert(m);
 
-        if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
+        if (FLAGS_SET(m->test_run_flags, MANAGER_TEST_RUN_MINIMAL))
                 return;
 
         /* Let's ask every type to load all units from disk/kernel that it might know */
@@ -1556,7 +1559,7 @@ static void manager_enumerate_perpetual(Manager *m) {
 static void manager_enumerate(Manager *m) {
         assert(m);
 
-        if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
+        if (FLAGS_SET(m->test_run_flags, MANAGER_TEST_RUN_MINIMAL))
                 return;
 
         /* Let's ask every type to load all units from disk/kernel that it might know */
@@ -1728,7 +1731,7 @@ void manager_reloading_stopp(Manager **m) {
         }
 }
 
-int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
+int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *root) {
         int r;
 
         assert(m);
@@ -1737,7 +1740,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
          * but we should not touch the real generator directories. */
         r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope,
                               MANAGER_IS_TEST_RUN(m) ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0,
-                              NULL);
+                              root);
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize path lookup table: %m");
 
@@ -2846,6 +2849,10 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
                         /* This is a nop on init */
                         break;
 
+                case 25:
+                        m->objective = MANAGER_REEXECUTE;
+                        break;
+
                 case 26:
                 case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */
                         manager_restore_original_log_target(m);
@@ -3669,7 +3676,7 @@ int manager_transient_environment_add(Manager *m, char **plus) {
         if (strv_isempty(plus))
                 return 0;
 
-        a = strv_env_merge(2, m->transient_environment, plus);
+        a = strv_env_merge(m->transient_environment, plus);
         if (!a)
                 return log_oom();
 
@@ -3701,7 +3708,7 @@ int manager_client_environment_modify(
         }
 
         if (!strv_isempty(plus)) {
-                b = strv_env_merge(2, l, plus);
+                b = strv_env_merge(l, plus);
                 if (!b) {
                         strv_free(a);
                         return -ENOMEM;
@@ -3728,7 +3735,7 @@ int manager_get_effective_environment(Manager *m, char ***ret) {
         assert(m);
         assert(ret);
 
-        l = strv_env_merge(2, m->transient_environment, m->client_environment);
+        l = strv_env_merge(m->transient_environment, m->client_environment);
         if (!l)
                 return -ENOMEM;
 
index 284ea42a9dd43a5b989ac91131d942784f48a490..1220c9fb1610b24fca71f2169f1788ab7c70f543 100644 (file)
@@ -128,11 +128,12 @@ typedef enum WatchdogType {
 #include "unit-name.h"
 
 typedef enum ManagerTestRunFlags {
-        MANAGER_TEST_NORMAL             = 0,       /* run normally */
-        MANAGER_TEST_RUN_MINIMAL        = 1 << 0,  /* create basic data structures */
-        MANAGER_TEST_RUN_BASIC          = 1 << 1,  /* interact with the environment */
-        MANAGER_TEST_RUN_ENV_GENERATORS = 1 << 2,  /* also run env generators  */
-        MANAGER_TEST_RUN_GENERATORS     = 1 << 3,  /* also run unit generators */
+        MANAGER_TEST_NORMAL                  = 0,       /* run normally */
+        MANAGER_TEST_RUN_MINIMAL             = 1 << 0,  /* create basic data structures */
+        MANAGER_TEST_RUN_BASIC               = 1 << 1,  /* interact with the environment */
+        MANAGER_TEST_RUN_ENV_GENERATORS      = 1 << 2,  /* also run env generators  */
+        MANAGER_TEST_RUN_GENERATORS          = 1 << 3,  /* also run unit generators */
+        MANAGER_TEST_RUN_IGNORE_DEPENDENCIES = 1 << 4,  /* run while ignoring dependencies */
         MANAGER_TEST_FULL = MANAGER_TEST_RUN_BASIC | MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_GENERATORS,
 } ManagerTestRunFlags;
 
@@ -470,7 +471,7 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
 Manager* manager_free(Manager *m);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 
-int manager_startup(Manager *m, FILE *serialization, FDSet *fds);
+int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *root);
 
 Job *manager_get_job(Manager *m, uint32_t id);
 Unit *manager_get_unit(Manager *m, const char *name);
index 728e2f080760cf27fc09aa6b94911ca428611273..87a15ec71aec8bdfd5324c4ccbd4279b68477ba7 100644 (file)
@@ -97,6 +97,8 @@ libcore_sources = '''
         namespace.h
         path.c
         path.h
+        restrict-ifaces.c
+        restrict-ifaces.h
         scope.c
         scope.h
         selinux-access.c
@@ -136,6 +138,11 @@ if conf.get('BPF_FRAMEWORK') == 1
         libcore_sources += [socket_bind_skel_h]
 endif
 
+subdir('bpf/restrict_ifaces')
+if conf.get('BPF_FRAMEWORK') == 1
+        libcore_sources += [restrict_ifaces_skel_h]
+endif
+
 load_fragment_gperf_gperf = custom_target(
         'load-fragment-gperf.gperf',
         input : 'load-fragment-gperf.gperf.in',
@@ -210,9 +217,9 @@ meson.add_install_script('sh', '-c', mkdir_p.format(systemgeneratordir))
 meson.add_install_script('sh', '-c', mkdir_p.format(usergeneratordir))
 
 if install_sysconfdir
-        meson.add_install_script('sh', '-c', mkdir_p.format(join_paths(pkgsysconfdir, 'system')))
-        meson.add_install_script('sh', '-c', mkdir_p.format(join_paths(pkgsysconfdir, 'user')))
-        meson.add_install_script('sh', '-c', mkdir_p.format(join_paths(sysconfdir, 'xdg/systemd')))
+        meson.add_install_script('sh', '-c', mkdir_p.format(pkgsysconfdir / 'system'))
+        meson.add_install_script('sh', '-c', mkdir_p.format(pkgsysconfdir / 'user'))
+        meson.add_install_script('sh', '-c', mkdir_p.format(sysconfdir / 'xdg/systemd'))
 endif
 
 ############################################################
index 608ead5bc43232cbb90f36a018bec989225835a0..fb8f72e2576d1eb5f514dbce8093904b3eecd379 100644 (file)
@@ -1223,7 +1223,7 @@ static int mount_stop(Unit *u) {
                 return 0;
 
         default:
-                assert_not_reached("Unexpected state.");
+                assert_not_reached();
         }
 }
 
@@ -1383,7 +1383,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         else if (code == CLD_DUMPED)
                 f = MOUNT_FAILURE_CORE_DUMP;
         else
-                assert_not_reached("Unknown code");
+                assert_not_reached();
 
         if (IN_SET(m->state, MOUNT_REMOUNTING, MOUNT_REMOUNTING_SIGKILL, MOUNT_REMOUNTING_SIGTERM))
                 mount_set_reload_result(m, f);
@@ -1465,7 +1465,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                 break;
 
         default:
-                assert_not_reached("Uh, control process died at wrong time.");
+                assert_not_reached();
         }
 
         /* Notify clients about changed exit status */
@@ -1541,7 +1541,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
                 break;
 
         default:
-                assert_not_reached("Timeout at wrong time.");
+                assert_not_reached();
         }
 
         return 0;
index 982aeeac1915b002cbc93badaf3020dab267ffb2..a31ea4b1bd56aca6026c298d3b588a36b6d33807 100644 (file)
@@ -570,7 +570,7 @@ static int append_protect_home(MountEntry **p, ProtectHome protect_home, bool ig
                 return append_static_mounts(p, protect_home_yes_table, ELEMENTSOF(protect_home_yes_table), ignore_protect);
 
         default:
-                assert_not_reached("Unexpected ProtectHome= value");
+                assert_not_reached();
         }
 }
 
@@ -592,7 +592,7 @@ static int append_protect_system(MountEntry **p, ProtectSystem protect_system, b
                 return append_static_mounts(p, protect_system_full_table, ELEMENTSOF(protect_system_full_table), ignore_protect);
 
         default:
-                assert_not_reached("Unexpected ProtectSystem= value");
+                assert_not_reached();
         }
 }
 
@@ -1359,7 +1359,7 @@ static int apply_one_mount(
                 return mount_overlay(m);
 
         default:
-                assert_not_reached("Unknown mode");
+                assert_not_reached();
         }
 
         assert(what);
@@ -2497,7 +2497,6 @@ static int make_tmp_prefix(const char *prefix) {
 static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, char **tmp_path) {
         _cleanup_free_ char *x = NULL;
         _cleanup_free_ char *y = NULL;
-        char bid[SD_ID128_STRING_MAX];
         sd_id128_t boot_id;
         bool rw = true;
         int r;
@@ -2513,7 +2512,7 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
         if (r < 0)
                 return r;
 
-        x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX");
+        x = strjoin(prefix, "/systemd-private-", SD_ID128_TO_STRING(boot_id), "-", id, "-XXXXXX");
         if (!x)
                 return -ENOMEM;
 
diff --git a/src/core/restrict-ifaces.c b/src/core/restrict-ifaces.c
new file mode 100644 (file)
index 0000000..709f749
--- /dev/null
@@ -0,0 +1,205 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "restrict-ifaces.h"
+#include "netlink-util.h"
+
+#if BPF_FRAMEWORK
+/* libbpf, clang and llc compile time dependencies are satisfied */
+
+#include "bpf-dlopen.h"
+#include "bpf-link.h"
+
+#include "bpf/restrict_ifaces/restrict-ifaces.skel.h"
+
+static struct restrict_ifaces_bpf *restrict_ifaces_bpf_free(struct restrict_ifaces_bpf *obj) {
+        restrict_ifaces_bpf__destroy(obj);
+        return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct restrict_ifaces_bpf *, restrict_ifaces_bpf_free);
+
+static int prepare_restrict_ifaces_bpf(Unit* u, bool is_allow_list,
+                const Set *restrict_network_interfaces,
+                struct restrict_ifaces_bpf **ret_object) {
+        _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        char *iface;
+        int r, map_fd;
+
+        assert(ret_object);
+
+        obj = restrict_ifaces_bpf__open();
+        if (!obj)
+                return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOMEM), "Failed to open BPF object");
+
+        r = sym_bpf_map__resize(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u));
+        if (r != 0)
+                return log_unit_error_errno(u, r,
+                                "Failed to resize BPF map '%s': %m",
+                                sym_bpf_map__name(obj->maps.sd_restrictif));
+
+        obj->rodata->is_allow_list = is_allow_list;
+
+        r = restrict_ifaces_bpf__load(obj);
+        if (r != 0)
+                return log_unit_error_errno(u, r, "Failed to load BPF object: %m");
+
+        map_fd = sym_bpf_map__fd(obj->maps.sd_restrictif);
+
+        SET_FOREACH(iface, restrict_network_interfaces) {
+                uint8_t dummy = 0;
+                int ifindex;
+                ifindex = rtnl_resolve_interface(&rtnl, iface);
+                if (ifindex < 0) {
+                        log_unit_warning_errno(u, ifindex, "Couldn't find index of network interface: %m. Ignoring '%s'", iface);
+                        continue;
+                }
+
+                if (sym_bpf_map_update_elem(map_fd, &ifindex, &dummy, BPF_ANY))
+                        return log_unit_error_errno(u, errno, "Failed to update BPF map '%s' fd: %m", sym_bpf_map__name(obj->maps.sd_restrictif));
+        }
+
+        *ret_object = TAKE_PTR(obj);
+        return 0;
+}
+
+int restrict_network_interfaces_supported(void) {
+        _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
+        int r;
+        static int supported = -1;
+
+        if (supported >= 0)
+                return supported;
+
+        r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
+        if (r < 0) {
+                log_warning_errno(r, "Can't determine whether the unified hierarchy is used: %m");
+                supported = 0;
+                return supported;
+        }
+        if (r == 0) {
+                log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                "Not running with unified cgroup hierarchy, BPF is not supported");
+                supported = 0;
+                return supported;
+        }
+
+        if (dlopen_bpf() < 0)
+                return false;
+
+        if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*ifindex=*/0)) {
+                log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                "BPF program type cgroup_skb is not supported");
+                supported = 0;
+                return supported;
+        }
+
+        r = prepare_restrict_ifaces_bpf(NULL, true, NULL, &obj);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to load BPF object: %m");
+
+        supported = bpf_can_link_program(obj->progs.sd_restrictif_i);
+        return supported;
+}
+
+static int restrict_network_interfaces_install_impl(Unit *u) {
+        _cleanup_(bpf_link_freep) struct bpf_link *egress_link = NULL, *ingress_link = NULL;
+        _cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
+        _cleanup_free_ char *cgroup_path = NULL;
+        _cleanup_close_ int cgroup_fd = -1;
+        CGroupContext *cc;
+        int r;
+
+        cc = unit_get_cgroup_context(u);
+        if (!cc)
+                return 0;
+
+        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
+        if (r < 0)
+                return log_unit_error_errno(u, r, "Failed to get cgroup path: %m");
+
+        if (!cc->restrict_network_interfaces)
+                return 0;
+
+        r = prepare_restrict_ifaces_bpf(u,
+                cc->restrict_network_interfaces_is_allow_list,
+                cc->restrict_network_interfaces,
+                &obj);
+        if (r < 0)
+                return r;
+
+        cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY, 0);
+        if (cgroup_fd < 0)
+                return -errno;
+
+        ingress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_i, cgroup_fd);
+        r = sym_libbpf_get_error(ingress_link);
+        if (r != 0)
+                return log_unit_error_errno(u, r, "Failed to create ingress cgroup link: %m");
+
+        egress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_e, cgroup_fd);
+        r = sym_libbpf_get_error(egress_link);
+        if (r != 0)
+                return log_unit_error_errno(u, r, "Failed to create egress cgroup link: %m");
+
+        u->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link);
+        u->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link);
+
+        return 0;
+}
+
+int restrict_network_interfaces_install(Unit *u) {
+        int r = restrict_network_interfaces_install_impl(u);
+        fdset_close(u->initial_restric_ifaces_link_fds);
+        return r;
+}
+
+int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) {
+        int r;
+
+        assert(u);
+
+        r = bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_ingress_bpf_link);
+        if (r < 0)
+                return r;
+
+        return bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_egress_bpf_link);
+}
+
+int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) {
+        int r;
+
+        assert(u);
+
+        if (!u->initial_restric_ifaces_link_fds) {
+                u->initial_restric_ifaces_link_fds = fdset_new();
+                if (!u->initial_restric_ifaces_link_fds)
+                        return log_oom();
+        }
+
+        r = fdset_put(u->initial_restric_ifaces_link_fds, fd);
+        if (r < 0)
+                return log_unit_error_errno(u, r, "Failed to put restrict-ifaces-bpf-fd %d to restored fdset: %m", fd);
+
+        return 0;
+}
+
+#else /* ! BPF_FRAMEWORK */
+int restrict_network_interfaces_supported(void) {
+        return 0;
+}
+
+int restrict_network_interfaces_install(Unit *u) {
+        return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
+                        "Failed to install RestrictInterfaces: BPF programs built from source code are not supported: %m");
+}
+
+int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) {
+        return 0;
+}
+
+int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) {
+        return 0;
+}
+#endif
diff --git a/src/core/restrict-ifaces.h b/src/core/restrict-ifaces.h
new file mode 100644 (file)
index 0000000..be9e522
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "fdset.h"
+#include "unit.h"
+
+typedef struct Unit Unit;
+
+int restrict_network_interfaces_supported(void);
+int restrict_network_interfaces_install(Unit *u);
+
+int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds);
+
+/* Add BPF link fd created before daemon-reload or daemon-reexec.
+ * FDs will be closed at the end of restrict_network_interfaces_install. */
+int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd);
index 1b60af22f3a5f6be114d987603e820fbe16f5fa9..560a7d9f3eed2b7ee8714ef06163d7e080ff569b 100644 (file)
@@ -562,7 +562,7 @@ static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *user
                 break;
 
         default:
-                assert_not_reached("Timeout at wrong time.");
+                assert_not_reached();
         }
 
         return 0;
index d077d5dea751c6e936c5f99ce8c5648549825038..984e324d74aa7103d0f9853f11ce157e92930506 100644 (file)
@@ -57,11 +57,11 @@ static int audit_callback(
         if (sd_bus_creds_get_egid(audit->creds, &gid) >= 0)
                 xsprintf(gid_buf, GID_FMT, gid);
 
-        snprintf(msgbuf, msgbufsize,
-                 "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
-                 login_uid_buf, uid_buf, gid_buf,
-                 audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
-                 audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
+        (void) snprintf(msgbuf, msgbufsize,
+                        "auid=%s uid=%s gid=%s%s%s%s%s%s%s",
+                        login_uid_buf, uid_buf, gid_buf,
+                        audit->path ? " path=\"" : "", strempty(audit->path), audit->path ? "\"" : "",
+                        audit->cmdline ? " cmdline=\"" : "", strempty(audit->cmdline), audit->cmdline ? "\"" : "");
 
         return 0;
 }
index f4a79dc2af531dde2600036b8d2d295a39f5efb0..c55304d170efe4b531cd134e64317ba64ad87d4f 100644 (file)
@@ -916,7 +916,6 @@ static int service_is_suitable_main_pid(Service *s, pid_t pid, int prio) {
 }
 
 static int service_load_pid_file(Service *s, bool may_warn) {
-        char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         bool questionable_pid_file = false;
         _cleanup_free_ char *k = NULL;
         _cleanup_close_ int fd = -1;
@@ -945,8 +944,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
 
         /* Let's read the PID file now that we chased it down. But we need to convert the O_PATH fd
          * chase_symlinks() returned us into a proper fd first. */
-        xsprintf(procfs, "/proc/self/fd/%i", fd);
-        r = read_one_line_file(procfs, &k);
+        r = read_one_line_file(FORMAT_PROC_FD_PATH(fd), &k);
         if (r < 0)
                 return log_unit_error_errno(UNIT(s), r,
                                             "Can't convert PID files %s O_PATH file descriptor to proper file descriptor: %m",
@@ -1546,7 +1544,7 @@ static int service_spawn(
         if (r < 0)
                 return r;
 
-        final_env = strv_env_merge(2, exec_params.environment, our_env, NULL);
+        final_env = strv_env_merge(exec_params.environment, our_env);
         if (!final_env)
                 return -ENOMEM;
 
@@ -1683,7 +1681,7 @@ static bool service_shall_restart(Service *s, const char **reason) {
                 return IN_SET(s->result, SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP);
 
         default:
-                assert_not_reached("unknown restart setting");
+                assert_not_reached();
         }
 }
 
@@ -2157,7 +2155,7 @@ static void service_enter_start(Service *s) {
                 service_set_main_pid(s, pid);
                 service_set_state(s, SERVICE_START);
         } else
-                assert_not_reached("Unknown service type");
+                assert_not_reached();
 
         return;
 
@@ -2780,7 +2778,7 @@ int service_deserialize_exec_command(
                                 return -ENOMEM;
                         break;
                 default:
-                        assert_not_reached("Logic error in exec command deserialization");
+                        assert_not_reached();
                 }
         }
 
@@ -3392,7 +3390,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         else if (code == CLD_DUMPED)
                 f = SERVICE_FAILURE_CORE_DUMP;
         else
-                assert_not_reached("Unknown code");
+                assert_not_reached();
 
         if (s->main_pid == pid) {
                 /* Clean up the exec_fd event source. We want to do this here, not later in
@@ -3523,7 +3521,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 break;
 
                         default:
-                                assert_not_reached("Uh, main process died at wrong time.");
+                                assert_not_reached();
                         }
                 }
 
@@ -3701,7 +3699,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 break;
 
                         default:
-                                assert_not_reached("Uh, control process died at wrong time.");
+                                assert_not_reached();
                         }
                 }
         } else /* Neither control nor main PID? If so, don't notify about anything */
@@ -3756,7 +3754,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
                         break;
 
                 default:
-                        assert_not_reached("unknown timeout mode");
+                        assert_not_reached();
                 }
                 break;
 
@@ -3796,7 +3794,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
                         break;
 
                 default:
-                        assert_not_reached("unknown timeout mode");
+                        assert_not_reached();
                 }
                 break;
 
@@ -3857,7 +3855,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
                         break;
 
                 default:
-                        assert_not_reached("unknown timeout mode");
+                        assert_not_reached();
                 }
                 break;
 
@@ -3912,7 +3910,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
                 break;
 
         default:
-                assert_not_reached("Timeout at wrong time.");
+                assert_not_reached();
         }
 
         return 0;
index 8a2a68845092a7243a910dedf6dbeb966d621dd9..675ad3c025038aac9e51a2160991bc07984d6cdb 100644 (file)
@@ -433,7 +433,7 @@ static void peer_address_hash_func(const SocketPeer *s, struct siphash *state) {
         else if (s->peer.sa.sa_family == AF_VSOCK)
                 siphash24_compress(&s->peer.vm.svm_cid, sizeof(s->peer.vm.svm_cid), state);
         else
-                assert_not_reached("Unknown address family.");
+                assert_not_reached();
 }
 
 static int peer_address_compare_func(const SocketPeer *x, const SocketPeer *y) {
@@ -451,7 +451,7 @@ static int peer_address_compare_func(const SocketPeer *x, const SocketPeer *y) {
         case AF_VSOCK:
                 return CMP(x->peer.vm.svm_cid, y->peer.vm.svm_cid);
         }
-        assert_not_reached("Black sheep in the family!");
+        assert_not_reached();
 }
 
 DEFINE_PRIVATE_HASH_OPS(peer_address_hash_ops, SocketPeer, peer_address_hash_func, peer_address_compare_func);
@@ -558,7 +558,7 @@ _const_ static const char* listen_lookup(int family, int type) {
         else if (type == SOCK_SEQPACKET)
                 return "ListenSequentialPacket";
 
-        assert_not_reached("Unknown socket type");
+        assert_not_reached();
         return NULL;
 }
 
@@ -914,7 +914,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
                 break;
 
         default:
-                assert_not_reached("Unhandled socket type.");
+                assert_not_reached();
         }
 
         *instance = r;
@@ -1724,7 +1724,7 @@ static int socket_open_fds(Socket *orig_s) {
                         break;
                 }
                 default:
-                        assert_not_reached("Unknown port type");
+                        assert_not_reached();
                 }
         }
 
@@ -3071,7 +3071,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         else if (code == CLD_DUMPED)
                 f = SOCKET_FAILURE_CORE_DUMP;
         else
-                assert_not_reached("Unknown sigchld code");
+                assert_not_reached();
 
         if (s->control_command) {
                 exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
@@ -3149,7 +3149,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                         break;
 
                 default:
-                        assert_not_reached("Uh, control process died at wrong time.");
+                        assert_not_reached();
                 }
         }
 
@@ -3226,7 +3226,7 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use
                 break;
 
         default:
-                assert_not_reached("Timeout at wrong time.");
+                assert_not_reached();
         }
 
         return 0;
index d7089f1c55c78b07e4fd15353f1eff3d040bad92..48ba5c766492c0455080fbddbad15147b2091e7d 100644 (file)
@@ -979,7 +979,7 @@ static int swap_stop(Unit *u) {
                 return 0;
 
         default:
-                assert_not_reached("Unexpected state.");
+                assert_not_reached();
         }
 }
 
@@ -1073,7 +1073,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         else if (code == CLD_DUMPED)
                 f = SWAP_FAILURE_CORE_DUMP;
         else
-                assert_not_reached("Unknown code");
+                assert_not_reached();
 
         if (s->result == SWAP_SUCCESS)
                 s->result = f;
@@ -1118,7 +1118,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                 break;
 
         default:
-                assert_not_reached("Uh, control process died at wrong time.");
+                assert_not_reached();
         }
 
         /* Notify clients about changed exit status */
@@ -1169,7 +1169,7 @@ static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userd
                 break;
 
         default:
-                assert_not_reached("Timeout at wrong time.");
+                assert_not_reached();
         }
 
         return 0;
@@ -1323,11 +1323,11 @@ static Unit *swap_following(Unit *u) {
         if (streq_ptr(s->what, s->devnode))
                 return NULL;
 
-        LIST_FOREACH_AFTER(same_devnode, other, s)
+        LIST_FOREACH(same_devnode, other, s->same_devnode_next)
                 if (streq_ptr(other->what, other->devnode))
                         return UNIT(other);
 
-        LIST_FOREACH_BEFORE(same_devnode, other, s) {
+        LIST_FOREACH_BACKWARDS(same_devnode, other, s->same_devnode_prev) {
                 if (streq_ptr(other->what, other->devnode))
                         return UNIT(other);
 
index 2c7f304cdd27c081db7c7115fbc0f68efb3bfd86..12515a6a75692a859d265c59214c7bea952aff78 100644 (file)
@@ -473,7 +473,7 @@ static void timer_enter_waiting(Timer *t, bool time_change) {
                                 break;
 
                         default:
-                                assert_not_reached("Unknown timer base");
+                                assert_not_reached();
                         }
 
                         v->next_elapse = usec_add(usec_shift_clock(base, CLOCK_MONOTONIC, TIMER_MONOTONIC_CLOCK(t)), v->value);
@@ -797,7 +797,7 @@ static void timer_trigger_notify(Unit *u, Unit *other) {
                 break;
 
         default:
-                assert_not_reached("Unknown timer state");
+                assert_not_reached();
         }
 }
 
index ab0f03a0ca5309ba7865cc2e808966b71110fe3a..9e1664ff53af5215e5f52930313d1d44e73d5799 100644 (file)
@@ -7,6 +7,7 @@
 #include "fileio.h"
 #include "format-util.h"
 #include "parse-util.h"
+#include "restrict-ifaces.h"
 #include "serialize.h"
 #include "string-table.h"
 #include "unit-serialize.h"
@@ -173,6 +174,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
         (void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-ingress-installed", u->ip_bpf_custom_ingress_installed);
         (void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-egress-installed", u->ip_bpf_custom_egress_installed);
 
+        (void) serialize_restrict_network_interfaces(u, f, fds);
+
         if (uid_is_valid(u->ref_uid))
                 (void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
         if (gid_is_valid(u->ref_gid))
@@ -413,6 +416,21 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                          (void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_egress_installed);
                          continue;
 
+                } else if (streq(l, "restrict-ifaces-bpf-fd")) {
+                        int fd;
+
+                        if (safe_atoi(v, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) {
+                                log_unit_debug(u, "Failed to parse restrict-ifaces-bpf-fd value: %s", v);
+                                continue;
+                        }
+                        if (fdset_remove(fds, fd) < 0) {
+                                log_unit_debug(u, "Failed to remove restrict-ifaces-bpf-fd %d from fdset", fd);
+                                continue;
+                        }
+
+                        (void) restrict_network_interfaces_add_initial_link_fd(u, fd);
+                        continue;
+
                 } else if (streq(l, "ref-uid")) {
                         uid_t uid;
 
index c32317c863d4e4b75585958542a94258c30a9259..7d72dfa864be73b118a4e7385e172ed2ce22ddd2 100644 (file)
@@ -418,7 +418,7 @@ bool unit_may_gc(Unit *u) {
                 break;
 
         default:
-                assert_not_reached("Unknown garbage collection mode");
+                assert_not_reached();
         }
 
         if (u->cgroup_path) {
@@ -427,7 +427,7 @@ bool unit_may_gc(Unit *u) {
 
                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
                 if (r < 0)
-                        log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", u->cgroup_path);
+                        log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", empty_to_root(u->cgroup_path));
                 if (r <= 0)
                         return false;
         }
@@ -733,6 +733,9 @@ Unit* unit_free(Unit *u) {
         if (u->in_dbus_queue)
                 LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u);
 
+        if (u->in_cleanup_queue)
+                LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u);
+
         if (u->in_gc_queue)
                 LIST_REMOVE(gc_queue, u->manager->gc_unit_queue, u);
 
@@ -742,8 +745,8 @@ Unit* unit_free(Unit *u) {
         if (u->in_cgroup_empty_queue)
                 LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u);
 
-        if (u->in_cleanup_queue)
-                LIST_REMOVE(cleanup_queue, u->manager->cleanup_queue, u);
+        if (u->in_cgroup_oom_queue)
+                LIST_REMOVE(cgroup_oom_queue, u->manager->cgroup_oom_queue, u);
 
         if (u->in_target_deps_queue)
                 LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
@@ -763,6 +766,12 @@ Unit* unit_free(Unit *u) {
 
         bpf_program_unref(u->bpf_device_control_installed);
 
+#if BPF_FRAMEWORK
+        bpf_link_free(u->restrict_ifaces_ingress_bpf_link);
+        bpf_link_free(u->restrict_ifaces_egress_bpf_link);
+#endif
+        fdset_free(u->initial_restric_ifaces_link_fds);
+
         condition_free_list(u->conditions);
         condition_free_list(u->asserts);
 
@@ -2591,7 +2600,7 @@ static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags)
                 break;
 
         default:
-                assert_not_reached("Job type unknown");
+                assert_not_reached();
         }
 
         return unexpected;
@@ -2983,7 +2992,7 @@ bool unit_job_is_applicable(Unit *u, JobType j) {
                 return unit_can_reload(u) && unit_can_start(u);
 
         default:
-                assert_not_reached("Invalid job type");
+                assert_not_reached();
         }
 }
 
@@ -3050,6 +3059,9 @@ int unit_add_dependency(
                 return 0;
         }
 
+        if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
+                return 0;
+
         /* Note that ordering a device unit after a unit is permitted since it allows to start its job
          * running timeout at a specific time. */
         if (FLAGS_SET(a, UNIT_ATOM_BEFORE) && other->type == UNIT_DEVICE) {
@@ -3173,6 +3185,9 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, boo
         if (r < 0)
                 return r;
 
+        if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
+                return 0;
+
         r = manager_load_unit(u->manager, name, NULL, NULL, &other);
         if (r < 0)
                 return r;
@@ -3192,6 +3207,9 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency
         if (r < 0)
                 return r;
 
+        if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
+                return 0;
+
         r = manager_load_unit(u->manager, name, NULL, NULL, &other);
         if (r < 0)
                 return r;
@@ -3309,6 +3327,9 @@ int unit_set_default_slice(Unit *u) {
 
         assert(u);
 
+        if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
+                return 0;
+
         if (UNIT_GET_SLICE(u))
                 return 0;
 
@@ -3573,7 +3594,6 @@ int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask m
 int unit_coldplug(Unit *u) {
         int r = 0, q;
         char **i;
-        Job *uj;
 
         assert(u);
 
@@ -3596,9 +3616,13 @@ int unit_coldplug(Unit *u) {
                         r = q;
         }
 
-        uj = u->job ?: u->nop_job;
-        if (uj) {
-                q = job_coldplug(uj);
+        if (u->job) {
+                q = job_coldplug(u->job);
+                if (q < 0 && r >= 0)
+                        r = q;
+        }
+        if (u->nop_job) {
+                q = job_coldplug(u->nop_job);
                 if (q < 0 && r >= 0)
                         r = q;
         }
@@ -3611,6 +3635,8 @@ void unit_catchup(Unit *u) {
 
         if (UNIT_VTABLE(u)->catchup)
                 UNIT_VTABLE(u)->catchup(u);
+
+        unit_cgroup_catchup(u);
 }
 
 static bool fragment_mtime_newer(const char *path, usec_t mtime, bool path_masked) {
@@ -4459,7 +4485,7 @@ static int operation_to_signal(const KillContext *c, KillOperation k, bool *note
                 return c->watchdog_signal;
 
         default:
-                assert_not_reached("KillOperation unknown");
+                assert_not_reached();
         }
 }
 
@@ -4548,7 +4574,7 @@ int unit_kill_context(
                                       log_func, u);
                 if (r < 0) {
                         if (!IN_SET(r, -EAGAIN, -ESRCH, -ENOENT))
-                                log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path);
+                                log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
 
                 } else if (r > 0) {
 
@@ -5006,7 +5032,7 @@ int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret) {
         if (u->cgroup_path) {
                 r = cg_attach_everywhere(u->manager->cgroup_supported, u->cgroup_path, 0, NULL, NULL);
                 if (r < 0) {
-                        log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", u->cgroup_path);
+                        log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", empty_to_root(u->cgroup_path));
                         _exit(EXIT_CGROUP);
                 }
         }
@@ -5497,10 +5523,19 @@ const char *unit_label_path(const Unit *u) {
         /* Returns the file system path to use for MAC access decisions, i.e. the file to read the SELinux label off
          * when validating access checks. */
 
+        if (IN_SET(u->load_state, UNIT_MASKED, UNIT_NOT_FOUND, UNIT_MERGED))
+                return NULL; /* Shortcut things if we know there is no real, relevant unit file around */
+
         p = u->source_path ?: u->fragment_path;
         if (!p)
                 return NULL;
 
+        if (IN_SET(u->load_state, UNIT_LOADED, UNIT_BAD_SETTING, UNIT_ERROR))
+                return p; /* Shortcut things, if we successfully loaded at least some stuff from the unit file */
+
+        /* Not loaded yet, we need to go to disk */
+        assert(u->load_state == UNIT_STUB);
+
         /* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense */
         if (null_or_empty_path(p) > 0)
                 return NULL;
index 48074d8ca5b680b6723d79c043a4c97bc068b459..b3e9c2106fd3498e97eed8bab28cc41b9e9743d0 100644 (file)
@@ -335,6 +335,12 @@ typedef struct Unit {
         struct bpf_link *ipv6_socket_bind_link;
 #endif
 
+        FDSet *initial_restric_ifaces_link_fds;
+#if BPF_FRAMEWORK
+        struct bpf_link *restrict_ifaces_ingress_bpf_link;
+        struct bpf_link *restrict_ifaces_egress_bpf_link;
+#endif
+
         /* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new
          * ones which might have appeared. */
         sd_event_source *rewatch_pids_event_source;
index 8261bff21301942f882914755c77b68562db32d1..d370de290af5a2b68b5dccc0cf9a9ff893439a7a 100644 (file)
@@ -331,7 +331,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY &&
index 0edb1b40a72286800c79f69ca0735f57ada3b0c3..e46b324cdf77fb003f4f290a1978a2776ecb65c5 100644 (file)
@@ -153,6 +153,8 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e
                                             program_header->p_offset,
                                             program_header->p_filesz,
                                             ELF_T_NHDR);
+                if (!data)
+                        continue;
 
                 while (note_offset < data->d_size &&
                        (note_offset = gelf_getnote(data, note_offset, &note_header, &name_offset, &desc_offset)) > 0) {
index c2a2eabbed40758cc18a37905019bd4b9fb1f10a..f880a79a822fe73577dfc7d008c644a4549c2d15 100644 (file)
@@ -232,7 +232,7 @@ static int transcode(
                 return r;
 
         default:
-                assert_not_reached("Unexpected transcoding mode");
+                assert_not_reached();
         }
 }
 
@@ -761,7 +761,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
         }
 
index 0314831174be1ea6cb996b99d9ef7260f52cc78f..1775912d8e52711de35bf9df4d1cd4d909ff7fe0 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "ask-password-api.h"
 #include "cryptenroll-password.h"
+#include "env-util.h"
 #include "escape.h"
 #include "memory-util.h"
 #include "pwquality-util.h"
@@ -27,8 +28,7 @@ int enroll_password(
                 if (!new_password)
                         return log_oom();
 
-                string_erase(e);
-                assert_se(unsetenv("NEWPASSWORD") == 0);
+                assert_se(unsetenv_erase("NEWPASSWORD") >= 0);
 
         } else {
                 _cleanup_free_ char *disk_path = NULL;
index 9c1478c474e34a1476c1112277a5caf0d89b27f0..697b4c2335b36d1b12deb6482bed21bfcec50d97 100644 (file)
@@ -65,6 +65,7 @@ int enroll_tpm2(struct crypt_device *cd,
         _cleanup_(erase_and_freep) char *base64_encoded = NULL;
         size_t secret_size, secret2_size, blob_size, hash_size;
         _cleanup_free_ void *blob = NULL, *hash = NULL;
+        uint16_t pcr_bank;
         const char *node;
         int r, keyslot;
 
@@ -75,7 +76,7 @@ int enroll_tpm2(struct crypt_device *cd,
 
         assert_se(node = crypt_get_device_name(cd));
 
-        r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size);
+        r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank);
         if (r < 0)
                 return r;
 
@@ -92,7 +93,7 @@ int enroll_tpm2(struct crypt_device *cd,
 
         /* Quick verification that everything is in order, we are not in a hurry after all. */
         log_debug("Unsealing for verification...");
-        r = tpm2_unseal(device, pcr_mask, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
+        r = tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
         if (r < 0)
                 return r;
 
@@ -118,7 +119,7 @@ int enroll_tpm2(struct crypt_device *cd,
         if (keyslot < 0)
                 return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
 
-        r = tpm2_make_luks2_json(keyslot, pcr_mask, blob, blob_size, hash, hash_size, &v);
+        r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v);
         if (r < 0)
                 return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
 
index 2255946643b3a9b842895eb79156b303a2ee9529..8042a0fe835883808368d9ab4e42562b11b55337 100644 (file)
@@ -367,7 +367,7 @@ int wipe_slots(struct crypt_device *cd,
 
                 break;
         default:
-                assert_not_reached("Unexpected wipe scope");
+                assert_not_reached();
         }
 
         /* Then add all slots that match a token type */
index d253b2bc0da7b463c3079325fafec048556f1550..cf99aab96db410406a394293bed0dbd97d42bb26 100644 (file)
@@ -12,6 +12,7 @@
 #include "cryptenroll-wipe.h"
 #include "cryptenroll.h"
 #include "cryptsetup-util.h"
+#include "env-util.h"
 #include "escape.h"
 #include "libfido2-util.h"
 #include "main-func.h"
@@ -358,7 +359,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
         }
 
@@ -426,8 +427,7 @@ static int prepare_luks(
                 if (!password)
                         return log_oom();
 
-                string_erase(e);
-                assert_se(unsetenv("PASSWORD") >= 0);
+                assert_se(unsetenv_erase("PASSWORD") >= 0);
 
                 r = crypt_volume_key_get(
                                 cd,
index dfaded3cdb7afc7e59b472274f55c8ab9ceca1ae..74b6bff1aa0484e5f7d0ca92097607defbcfd80d 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "ask-password-api.h"
 #include "cryptsetup-fido2.h"
+#include "env-util.h"
 #include "fileio.h"
 #include "hexdecoct.h"
 #include "json.h"
@@ -70,9 +71,7 @@ int acquire_fido2_key(
                 if (!pins)
                         return log_oom();
 
-                string_erase(e);
-                if (unsetenv("PIN") < 0)
-                        return log_error_errno(errno, "Failed to unset $PIN: %m");
+                assert_se(unsetenv_erase("PIN") >= 0);
         }
 
         for (;;) {
index e743f10151be0f46590c585097d9235f4e0bc695..31960de5970dd2a936ec0a31be89faa7f0a351eb 100644 (file)
 #include "stat-util.h"
 #include "strv.h"
 
-struct pkcs11_callback_data {
-        const char *friendly_name;
-        usec_t until;
-        void *encrypted_key;
-        size_t encrypted_key_size;
-        void *decrypted_key;
-        size_t decrypted_key_size;
-        bool free_encrypted_key;
-        bool headless;
-};
-
-static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
-        erase_and_free(data->decrypted_key);
-
-        if (data->free_encrypted_key)
-                free(data->encrypted_key);
-}
-
-static int pkcs11_callback(
-                CK_FUNCTION_LIST *m,
-                CK_SESSION_HANDLE session,
-                CK_SLOT_ID slot_id,
-                const CK_SLOT_INFO *slot_info,
-                const CK_TOKEN_INFO *token_info,
-                P11KitUri *uri,
-                void *userdata) {
-
-        struct pkcs11_callback_data *data = userdata;
-        CK_OBJECT_HANDLE object;
-        int r;
-
-        assert(m);
-        assert(slot_info);
-        assert(token_info);
-        assert(uri);
-        assert(data);
-
-        /* Called for every token matching our URI */
-
-        r = pkcs11_token_login(
-                        m,
-                        session,
-                        slot_id,
-                        token_info,
-                        data->friendly_name,
-                        "drive-harddisk",
-                        "pkcs11-pin",
-                        "cryptsetup.pkcs11-pin",
-                        data->until,
-                        data->headless,
-                        NULL);
-        if (r < 0)
-                return r;
-
-        /* We are likely called during early boot, where entropy is scarce. Mix some data from the PKCS#11
-         * token, if it supports that. It should be cheap, given that we already are talking to it anyway and
-         * shouldn't hurt. */
-        (void) pkcs11_token_acquire_rng(m, session);
-
-        r = pkcs11_token_find_private_key(m, session, uri, &object);
-        if (r < 0)
-                return r;
-
-        r = pkcs11_token_decrypt_data(
-                        m,
-                        session,
-                        object,
-                        data->encrypted_key,
-                        data->encrypted_key_size,
-                        &data->decrypted_key,
-                        &data->decrypted_key_size);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
 int decrypt_pkcs11_key(
                 const char *volume_name,
                 const char *friendly_name,
@@ -115,7 +38,7 @@ int decrypt_pkcs11_key(
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size) {
 
-        _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
+        _cleanup_(pkcs11_crypt_device_callback_data_release) pkcs11_crypt_device_callback_data data = {
                 .friendly_name = friendly_name,
                 .until = until,
                 .headless = headless,
@@ -155,7 +78,7 @@ int decrypt_pkcs11_key(
                 data.free_encrypted_key = true;
         }
 
-        r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data);
+        r = pkcs11_find_token(pkcs11_uri, pkcs11_crypt_device_callback, &data);
         if (r < 0)
                 return r;
 
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
new file mode 100644 (file)
index 0000000..12bb976
--- /dev/null
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <libcryptsetup.h>
+#include <string.h>
+
+#include "cryptsetup-token.h"
+#include "cryptsetup-token-util.h"
+#include "hexdecoct.h"
+#include "json.h"
+#include "luks2-fido2.h"
+#include "memory-util.h"
+#include "version.h"
+
+#define TOKEN_NAME "systemd-fido2"
+#define TOKEN_VERSION_MAJOR "1"
+#define TOKEN_VERSION_MINOR "0"
+
+/* for libcryptsetup debug purpose */
+_public_ const char *cryptsetup_token_version(void) {
+        return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")";
+}
+
+_public_ int cryptsetup_token_open_pin(
+                struct crypt_device *cd, /* is always LUKS2 context */
+                int token /* is always >= 0 */,
+                const char *pin,
+                size_t pin_size,
+                char **password, /* freed by cryptsetup_token_buffer_free */
+                size_t *password_len,
+                void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+        int r;
+        const char *json;
+        _cleanup_(erase_and_freep) char *pin_string = NULL;
+
+        assert(!pin || pin_size);
+        assert(token >= 0);
+
+        /* This must not fail at this moment (internal error) */
+        r = crypt_token_json_get(cd, token, &json);
+        assert(token == r);
+        assert(json);
+
+        if (pin && memchr(pin, 0, pin_size - 1))
+                return crypt_log_error_errno(cd, ENOANO, "PIN must be characters string.");
+
+        /* pin was passed as pin = pin, pin_size = strlen(pin). We need to add terminating
+         * NULL byte to addressable memory*/
+        if (pin && pin[pin_size-1] != '\0') {
+                pin_string = strndup(pin, pin_size);
+                if (!pin_string)
+                        return crypt_log_oom(cd);
+        }
+
+        return acquire_luks2_key(cd, json, (const char *)usrptr, pin_string ?: pin, password, password_len);
+}
+
+/*
+ * This function is called from within following libcryptsetup calls
+ * provided conditions further below are met:
+ *
+ * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-fido2'):
+ *
+ * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
+ *   (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
+ *    and token is assigned to at least single keyslot).
+ *
+ * - if plugin defines validate funtion (see cryptsetup_token_validate below) it must have
+ *   passed the check (aka return 0)
+ */
+_public_ int cryptsetup_token_open(
+                struct crypt_device *cd, /* is always LUKS2 context */
+                int token /* is always >= 0 */,
+                char **password, /* freed by cryptsetup_token_buffer_free */
+                size_t *password_len,
+                void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+        return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr);
+}
+
+/*
+ * libcryptsetup callback for memory deallocation of 'password' parameter passed in
+ * any crypt_token_open_* plugin function
+ */
+_public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) {
+        erase_and_free(buffer);
+}
+
+/*
+ * prints systemd-fido2 token content in crypt_dump().
+ * 'type' and 'keyslots' fields are printed by libcryptsetup
+ */
+_public_ void cryptsetup_token_dump(
+                struct crypt_device *cd /* is always LUKS2 context */,
+                const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
+
+        int r;
+        Fido2EnrollFlags required;
+        size_t cid_size, salt_size;
+        const char *client_pin_req_str, *up_req_str, *uv_req_str;
+        _cleanup_free_ void *cid = NULL, *salt = NULL;
+        _cleanup_free_ char *rp_id = NULL, *cid_str = NULL, *salt_str = NULL;
+
+        assert(json);
+
+        r = parse_luks2_fido2_data(cd, json, &rp_id, &salt, &salt_size, &cid, &cid_size, &required);
+        if (r < 0)
+                return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
+
+        r = crypt_dump_buffer_to_hex_string(cid, cid_size, &cid_str);
+        if (r < 0)
+                return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
+
+        r = crypt_dump_buffer_to_hex_string(salt, salt_size, &salt_str);
+        if (r < 0)
+                return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
+
+        if (required & FIDO2ENROLL_PIN)
+                client_pin_req_str = "true";
+        else if (required & FIDO2ENROLL_PIN_IF_NEEDED)
+                client_pin_req_str = NULL;
+        else
+                client_pin_req_str = "false";
+
+        if (required & FIDO2ENROLL_UP)
+                up_req_str = "true";
+        else if (required & FIDO2ENROLL_UP_IF_NEEDED)
+                up_req_str = NULL;
+        else
+                up_req_str = "false";
+
+        if (required & FIDO2ENROLL_UV)
+                uv_req_str = "true";
+        else if (required & FIDO2ENROLL_UV_OMIT)
+                uv_req_str = NULL;
+        else
+                uv_req_str = "false";
+
+        crypt_log(cd, "\tfido2-credential:" CRYPT_DUMP_LINE_SEP "%s\n", cid_str);
+        crypt_log(cd, "\tfido2-salt: %s\n", salt_str);
+
+        /* optional fields */
+        if (rp_id)
+                crypt_log(cd, "\tfido2-rp:   %s\n", rp_id);
+        if (client_pin_req_str)
+                crypt_log(cd, "\tfido2-clientPin-required:" CRYPT_DUMP_LINE_SEP "%s\n",
+                          client_pin_req_str);
+        if (up_req_str)
+                crypt_log(cd, "\tfido2-up-required:" CRYPT_DUMP_LINE_SEP "%s\n", up_req_str);
+        if (uv_req_str)
+                crypt_log(cd, "\tfido2-uv-required:" CRYPT_DUMP_LINE_SEP "%s\n", uv_req_str);
+}
+
+/*
+ * Note:
+ *   If plugin is available in library path, it's called in before following libcryptsetup calls:
+ *
+ *   crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour
+ */
+_public_ int cryptsetup_token_validate(
+                struct crypt_device *cd, /* is always LUKS2 context */
+                const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-tpm2' */) {
+
+        int r;
+        JsonVariant *w;
+       _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+        assert(json);
+
+        r = json_parse(json, 0, &v, NULL, NULL);
+        if (r < 0)
+                return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
+
+        w = json_variant_by_key(v, "fido2-credential");
+        if (!w || !json_variant_is_string(w)) {
+                crypt_log_debug(cd, "FIDO2 token data lacks 'fido2-credential' field.");
+                return 1;
+        }
+
+        r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+        if (r < 0)
+                return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'fido2-credential' field: %m");
+
+        w = json_variant_by_key(v, "fido2-salt");
+        if (!w || !json_variant_is_string(w)) {
+                crypt_log_debug(cd, "FIDO2 token data lacks 'fido2-salt' field.");
+                return 1;
+        }
+
+        r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+        if (r < 0)
+                return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded salt: %m.");
+
+        /* The "rp" field is optional. */
+        w = json_variant_by_key(v, "fido2-rp");
+        if (w && !json_variant_is_string(w)) {
+                crypt_log_debug(cd, "FIDO2 token data's 'fido2-rp' field is not a string.");
+                return 1;
+        }
+
+        /* The "fido2-clientPin-required" field is optional. */
+        w = json_variant_by_key(v, "fido2-clientPin-required");
+        if (w && !json_variant_is_boolean(w)) {
+                crypt_log_debug(cd, "FIDO2 token data's 'fido2-clientPin-required' field is not a boolean.");
+                return 1;
+        }
+
+        /* The "fido2-up-required" field is optional. */
+        w = json_variant_by_key(v, "fido2-up-required");
+        if (w && !json_variant_is_boolean(w)) {
+                crypt_log_debug(cd, "FIDO2 token data's 'fido2-up-required' field is not a boolean.");
+                return 1;
+        }
+
+        /* The "fido2-uv-required" field is optional. */
+        w = json_variant_by_key(v, "fido2-uv-required");
+        if (w && !json_variant_is_boolean(w)) {
+                crypt_log_debug(cd, "FIDO2 token data's 'fido2-uv-required' field is not a boolean.");
+                return 1;
+        }
+
+        return 0;
+}
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c
new file mode 100644 (file)
index 0000000..8e26f4f
--- /dev/null
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <libcryptsetup.h>
+
+#include "cryptsetup-token.h"
+#include "cryptsetup-token-util.h"
+#include "hexdecoct.h"
+#include "json.h"
+#include "luks2-pkcs11.h"
+#include "memory-util.h"
+#include "pkcs11-util.h"
+#include "version.h"
+
+#define TOKEN_NAME "systemd-pkcs11"
+#define TOKEN_VERSION_MAJOR "1"
+#define TOKEN_VERSION_MINOR "0"
+
+/* for libcryptsetup debug purpose */
+_public_ const char *cryptsetup_token_version(void) {
+        return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")";
+}
+
+_public_ int cryptsetup_token_open_pin(
+                struct crypt_device *cd, /* is always LUKS2 context */
+                int token /* is always >= 0 */,
+                const char *pin,
+                size_t pin_size,
+                char **password, /* freed by cryptsetup_token_buffer_free */
+                size_t *password_len,
+                void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+        const char *json;
+        int r;
+
+        assert(!pin || pin_size);
+        assert(token >= 0);
+
+        /* This must not fail at this moment (internal error) */
+        r = crypt_token_json_get(cd, token, &json);
+        assert(token == r);
+        assert(json);
+
+        return acquire_luks2_key(cd, json, usrptr, pin, pin_size, password, password_len);
+}
+
+/*
+ * This function is called from within following libcryptsetup calls
+ * provided conditions further below are met:
+ *
+ * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-pkcs11'):
+ *
+ * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
+ *   (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
+ *    and token is assigned to at least single keyslot).
+ *
+ * - if plugin defines validate funtion (see cryptsetup_token_validate below) it must have
+ *   passed the check (aka return 0)
+ */
+_public_ int cryptsetup_token_open(
+                struct crypt_device *cd, /* is always LUKS2 context */
+                int token /* is always >= 0 */,
+                char **password, /* freed by cryptsetup_token_buffer_free */
+                size_t *password_len,
+                void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+        return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr);
+}
+
+/*
+ * libcryptsetup callback for memory deallocation of 'password' parameter passed in
+ * any crypt_token_open_* plugin function
+ */
+_public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) {
+        erase_and_free(buffer);
+}
+
+/*
+ * prints systemd-pkcs11 token content in crypt_dump().
+ * 'type' and 'keyslots' fields are printed by libcryptsetup
+ */
+_public_ void cryptsetup_token_dump(
+                struct crypt_device *cd /* is always LUKS2 context */,
+                const char *json /* validated 'systemd-pkcs11' token if cryptsetup_token_validate is defined */) {
+
+        int r;
+        size_t pkcs11_key_size;
+        _cleanup_free_ char *pkcs11_uri = NULL, *key_str = NULL;
+        _cleanup_free_ void *pkcs11_key = NULL;
+
+        r = parse_luks2_pkcs11_data(cd, json, &pkcs11_uri, &pkcs11_key, &pkcs11_key_size);
+        if (r < 0)
+                return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
+
+        r = crypt_dump_buffer_to_hex_string(pkcs11_key, pkcs11_key_size, &key_str);
+        if (r < 0)
+                return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
+
+        crypt_log(cd, "\tpkcs11-uri: %s\n", pkcs11_uri);
+        crypt_log(cd, "\tpkcs11-key: %s\n", key_str);
+}
+
+/*
+ * Note:
+ *   If plugin is available in library path, it's called in before following libcryptsetup calls:
+ *
+ *   crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour
+ */
+_public_ int cryptsetup_token_validate(
+                struct crypt_device *cd, /* is always LUKS2 context */
+                const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-pkcs11' */) {
+
+        int r;
+        JsonVariant *w;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+        r = json_parse(json, 0, &v, NULL, NULL);
+        if (r < 0)
+                return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
+
+        w = json_variant_by_key(v, "pkcs11-uri");
+        if (!w || !json_variant_is_string(w)) {
+                crypt_log_debug(cd, "PKCS#11 token data lacks 'pkcs11-uri' field.");
+                return 1;
+        }
+
+        if (!pkcs11_uri_valid(json_variant_string(w))) {
+                crypt_log_debug(cd, "PKCS#11 token data contains invalid PKCS#11 URI.");
+                return 1;
+        }
+
+        w = json_variant_by_key(v, "pkcs11-key");
+        if (!w || !json_variant_is_string(w)) {
+                crypt_log_debug(cd, "PKCS#11 token data lacks 'pkcs11-key' field.");
+                return 1;
+        }
+
+        r = unbase64mem(json_variant_string(w), SIZE_MAX, NULL, NULL);
+        if (r < 0)
+                return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m.");
+
+        return 0;
+}
index 152b06b111accc2f0c9fb213065ffa12dd0e42fc..2daa55c457ed2fb0aee7d66707069bc42904d823 100644 (file)
@@ -43,7 +43,7 @@ static int log_debug_open_error(struct crypt_device *cd, int r) {
  *   (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
  *    and token is assigned to at least single keyslot).
  *
- * - if plugin defines validate funtion (see cryptsetup_token_validate below) it must have
+ * - if plugin defines validate function (see cryptsetup_token_validate below) it must have
  *   passed the check (aka return 0)
  */
 _public_ int cryptsetup_token_open(
@@ -57,6 +57,7 @@ _public_ int cryptsetup_token_open(
         const char *json;
         size_t blob_size, policy_hash_size, decrypted_key_size;
         uint32_t pcr_mask;
+        uint16_t pcr_bank;
         systemd_tpm2_plugin_params params = {
                 .search_pcr_mask = UINT32_MAX
         };
@@ -77,7 +78,7 @@ _public_ int cryptsetup_token_open(
         if (usrptr)
                 params = *(systemd_tpm2_plugin_params *)usrptr;
 
-        r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &base64_blob, &hex_policy_hash);
+        r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &base64_blob, &hex_policy_hash);
         if (r < 0)
                 return log_debug_open_error(cd, r);
 
@@ -93,6 +94,7 @@ _public_ int cryptsetup_token_open(
 
         r = acquire_luks2_key(
                         pcr_mask,
+                        pcr_bank,
                         params.device,
                         blob,
                         blob_size,
@@ -108,7 +110,7 @@ _public_ int cryptsetup_token_open(
         if (r < 0)
                 return log_debug_open_error(cd, r);
 
-        /* free'd automaticaly by libcryptsetup */
+        /* free'd automatically by libcryptsetup */
         *password_len = strlen(base64_encoded);
         *password = TAKE_PTR(base64_encoded);
 
@@ -132,7 +134,8 @@ _public_ void cryptsetup_token_dump(
                 const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
 
         int r;
-        uint32_t i, pcr_mask;
+        uint32_t pcr_mask;
+        uint16_t pcr_bank;
         size_t decoded_blob_size;
         _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL,
                             *pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL;
@@ -140,11 +143,11 @@ _public_ void cryptsetup_token_dump(
 
         assert(json);
 
-        r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &base64_blob, &hex_policy_hash);
+        r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &base64_blob, &hex_policy_hash);
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
 
-        for (i = 0; i < TPM2_PCRS_MAX; i++) {
+        for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) {
                 if ((pcr_mask & (UINT32_C(1) << i)) &&
                     ((r = strextendf_with_separator(&pcrs_str, ", ", "%" PRIu32, i)) < 0))
                         return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
@@ -162,7 +165,8 @@ _public_ void cryptsetup_token_dump(
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
 
-        crypt_log(cd, "\ttpm2-pcrs:  %s\n", pcrs_str ?: "");
+        crypt_log(cd, "\ttpm2-pcrs:  %s\n", strna(pcrs_str));
+        crypt_log(cd, "\ttpm2-bank:  %s\n", strna(tpm2_pcr_bank_to_string(pcr_bank)));
         crypt_log(cd, "\ttmp2-blob:  %s\n", blob_str);
         crypt_log(cd, "\ttmp2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
 }
@@ -208,6 +212,22 @@ _public_ int cryptsetup_token_validate(
                 }
         }
 
+        /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded to SHA256 */
+        w = json_variant_by_key(v, "tpm2-pcr-bank");
+        if (w) {
+                /* The PCR bank field is optional */
+
+                if (!json_variant_is_string(w)) {
+                        crypt_log_debug(cd, "TPM2 PCR bank is not a string.");
+                        return 1;
+                }
+
+                if (tpm2_pcr_bank_from_string(json_variant_string(w)) < 0) {
+                        crypt_log_debug(cd, "TPM2 PCR bank invalid or not supported: %s.", json_variant_string(w));
+                        return 1;
+                }
+        }
+
         w = json_variant_by_key(v, "tpm2-blob");
         if (!w || !json_variant_is_string(w)) {
                 crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-blob' field.");
index b8ea4c24221ad41111af9513f2d63c95446155a7..57ffca136f4740aa3528b4332b2d137c9c557b35 100644 (file)
@@ -2,23 +2,34 @@
 
 #pragma once
 
+#include <stdbool.h>
 #include <stddef.h>
+#include <libcryptsetup.h>
 
 /* crypt_dump() internal indentation magic */
 #define CRYPT_DUMP_LINE_SEP "\n\t            "
 
-#define crypt_log_debug(cd, ...) crypt_logf(cd, CRYPT_LOG_DEBUG,  __VA_ARGS__)
-#define crypt_log_error(cd, ...) crypt_logf(cd, CRYPT_LOG_ERROR,  __VA_ARGS__)
-#define crypt_log(cd, ...)       crypt_logf(cd, CRYPT_LOG_NORMAL, __VA_ARGS__)
+#define crypt_log_debug(cd, ...)   crypt_logf(cd, CRYPT_LOG_DEBUG,   __VA_ARGS__)
+#define crypt_log_error(cd, ...)   crypt_logf(cd, CRYPT_LOG_ERROR,   __VA_ARGS__)
+#define crypt_log_verbose(cd, ...) crypt_logf(cd, CRYPT_LOG_VERBOSE, __VA_ARGS__)
+#define crypt_log(cd, ...)         crypt_logf(cd, CRYPT_LOG_NORMAL,  __VA_ARGS__)
 
-#define crypt_log_debug_errno(cd, e, ...) ({ \
+#define crypt_log_full_errno(cd, e, lvl, ...) ({ \
         int _e = abs(e), _s = errno; \
         errno = _e; \
-        crypt_logf(cd, CRYPT_LOG_DEBUG, __VA_ARGS__); \
+        crypt_logf(cd, lvl, __VA_ARGS__); \
         errno = _s; \
         -_e; \
 })
 
+#define crypt_log_debug_errno(cd, e, ...) \
+        crypt_log_full_errno(cd, e, CRYPT_LOG_DEBUG, __VA_ARGS__)
+
+#define crypt_log_error_errno(cd, e, ...) \
+        crypt_log_full_errno(cd, e, CRYPT_LOG_ERROR, __VA_ARGS__)
+
+#define crypt_log_oom(cd) crypt_log_error_errno(cd, ENOMEM, "Not enough memory.")
+
 int crypt_dump_buffer_to_hex_string(
                 const char *buf,
                 size_t buf_size,
index 010e010ff03b5e7d96fee3b774b7b7473b995bb6..2a9d23f3e3eaa33dda618703bc9519220cb80aaf 100644 (file)
@@ -8,6 +8,10 @@ const char *cryptsetup_token_version(void);
 int cryptsetup_token_open(struct crypt_device *cd, int token,
         char **password, size_t *password_len, void *usrptr);
 
+int cryptsetup_token_open_pin(struct crypt_device *cd, int token,
+        const char *pin, size_t pin_size,
+        char **password, size_t *password_len, void *usrptr);
+
 void cryptsetup_token_dump(struct crypt_device *cd, const char *json);
 
 int cryptsetup_token_validate(struct crypt_device *cd, const char *json);
index bddf1b59b225786a06d720f89c6c7cabeceea38f..730e78e0552204f2919412fd7c9e4310416c799b 100644 (file)
@@ -10,6 +10,7 @@
 CRYPTSETUP_TOKEN_1.0 {
 global:
         cryptsetup_token_open;
+        cryptsetup_token_open_pin;
         cryptsetup_token_buffer_free;
         cryptsetup_token_validate;
         cryptsetup_token_dump;
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
new file mode 100644 (file)
index 0000000..442a57b
--- /dev/null
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <libcryptsetup.h>
+
+#include "cryptsetup-token-util.h"
+#include "hexdecoct.h"
+#include "json.h"
+#include "luks2-fido2.h"
+#include "memory-util.h"
+#include "strv.h"
+
+int acquire_luks2_key(
+                struct crypt_device *cd,
+                const char *json,
+                const char *device,
+                const char *pin,
+                char **ret_keyslot_passphrase,
+                size_t *ret_keyslot_passphrase_size) {
+
+        int r;
+        Fido2EnrollFlags required;
+        size_t cid_size, salt_size, decrypted_key_size;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        _cleanup_free_ void *cid = NULL, *salt = NULL;
+        _cleanup_free_ char *rp_id = NULL;
+        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+        _cleanup_strv_free_erase_ char **pins = NULL;
+
+        assert(ret_keyslot_passphrase);
+        assert(ret_keyslot_passphrase_size);
+
+        r = parse_luks2_fido2_data(cd, json, &rp_id, &salt, &salt_size, &cid, &cid_size, &required);
+        if (r < 0)
+                return r;
+
+        if (pin) {
+                pins = strv_new(pin);
+                if (!pins)
+                        return crypt_log_oom(cd);
+        }
+
+        /* configured to use pin but none was provided */
+        if ((required & FIDO2ENROLL_PIN) && strv_isempty(pins))
+                return -ENOANO;
+
+        r = fido2_use_hmac_hash(
+                        device,
+                        rp_id ?: "io.systemd.cryptsetup",
+                        salt, salt_size,
+                        cid, cid_size,
+                        pins,
+                        required,
+                        &decrypted_key,
+                        &decrypted_key_size);
+        if (r == -ENOLCK) /* libcryptsetup returns -ENOANO also on wrong pin */
+                r = -ENOANO;
+        if (r < 0)
+                return r;
+
+        /* Before using this key as passphrase we base64 encode it, for compat with homed */
+        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        if (r < 0)
+                return crypt_log_error_errno(cd, r, "Can not base64 encode key: %m");
+
+        *ret_keyslot_passphrase = TAKE_PTR(base64_encoded);
+        *ret_keyslot_passphrase_size = strlen(*ret_keyslot_passphrase);
+
+        return 0;
+}
+
+/* this function expects valid "systemd-fido2" in json */
+int parse_luks2_fido2_data(
+                struct crypt_device *cd,
+                const char *json,
+                char **ret_rp_id,
+                void **ret_salt,
+                size_t *ret_salt_size,
+                void **ret_cid,
+                size_t *ret_cid_size,
+                Fido2EnrollFlags *ret_required) {
+
+        _cleanup_free_ void *cid = NULL, *salt = NULL;
+        size_t cid_size = 0, salt_size = 0;
+        _cleanup_free_ char *rp = NULL;
+        int r;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        JsonVariant *w;
+        Fido2EnrollFlags required = 0;
+
+        assert(json);
+        assert(ret_rp_id);
+        assert(ret_salt);
+        assert(ret_salt_size);
+        assert(ret_cid);
+        assert(ret_cid_size);
+        assert(ret_required);
+
+        r = json_parse(json, 0, &v, NULL, NULL);
+        if (r < 0)
+                return crypt_log_error_errno(cd, r, "Failed to parse JSON token data: %m");
+
+        w = json_variant_by_key(v, "fido2-credential");
+        if (!w)
+                return -EINVAL;
+
+        r = unbase64mem(json_variant_string(w), SIZE_MAX, &cid, &cid_size);
+        if (r < 0)
+                return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-credentials' field: %m");
+
+        w = json_variant_by_key(v, "fido2-salt");
+        if (!w)
+                return -EINVAL;
+
+        r = unbase64mem(json_variant_string(w), SIZE_MAX, &salt, &salt_size);
+        if (r < 0)
+                return crypt_log_error_errno(cd, r, "Failed to parse 'fido2-salt' field: %m");
+
+        w = json_variant_by_key(v, "fido2-rp");
+        if (w) {
+                /* The "rp" field is optional. */
+                rp = strdup(json_variant_string(w));
+                if (!rp) {
+                        crypt_log_error(cd, "Not enough memory.");
+                        return -ENOMEM;
+                }
+        }
+
+        w = json_variant_by_key(v, "fido2-clientPin-required");
+        if (w)
+                /* The "fido2-clientPin-required" field is optional. */
+                SET_FLAG(required, FIDO2ENROLL_PIN, json_variant_boolean(w));
+        else
+                required |= FIDO2ENROLL_PIN_IF_NEEDED; /* compat with 248, where the field was unset */
+
+        w = json_variant_by_key(v, "fido2-up-required");
+        if (w)
+                /* The "fido2-up-required" field is optional. */
+                SET_FLAG(required, FIDO2ENROLL_UP, json_variant_boolean(w));
+        else
+                required |= FIDO2ENROLL_UP_IF_NEEDED; /* compat with 248 */
+
+        w = json_variant_by_key(v, "fido2-uv-required");
+        if (w)
+                /* The "fido2-uv-required" field is optional. */
+                SET_FLAG(required, FIDO2ENROLL_UV, json_variant_boolean(w));
+        else
+                required |= FIDO2ENROLL_UV_OMIT; /* compat with 248 */
+
+        *ret_rp_id = TAKE_PTR(rp);
+        *ret_cid = TAKE_PTR(cid);
+        *ret_cid_size = cid_size;
+        *ret_salt = TAKE_PTR(salt);
+        *ret_salt_size = salt_size;
+        *ret_required = required;
+
+        return 0;
+}
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-fido2.h b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.h
new file mode 100644 (file)
index 0000000..48416ec
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "libfido2-util.h"
+
+struct crypt_device;
+
+int acquire_luks2_key(
+                struct crypt_device *cd,
+                const char *json,
+                const char *device,
+                const char *pin,
+                char **ret_keyslot_passphrase,
+                size_t *ret_keyslot_passphrase_size);
+
+int parse_luks2_fido2_data(
+                struct crypt_device *cd,
+                const char *json,
+                char **ret_rp_id,
+                void **ret_salt,
+                size_t *ret_salt_size,
+                void **ret_cid,
+                size_t *ret_cid_size,
+                Fido2EnrollFlags *ret_required);
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c b/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
new file mode 100644 (file)
index 0000000..9e88c8c
--- /dev/null
@@ -0,0 +1,271 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <p11-kit/p11-kit.h>
+#include <p11-kit/uri.h>
+
+#include "cryptsetup-token-util.h"
+#include "escape.h"
+#include "hexdecoct.h"
+#include "json.h"
+#include "luks2-pkcs11.h"
+#include "memory-util.h"
+#include "pkcs11-util.h"
+#include "time-util.h"
+
+struct luks2_pkcs11_callback_data {
+        struct crypt_device *cd;
+        const char *pin;
+        size_t pin_size;
+        void *encrypted_key;
+        size_t encrypted_key_size;
+        void *decrypted_key;
+        size_t decrypted_key_size;
+};
+
+static int luks2_pkcs11_callback(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_SLOT_ID slot_id,
+                const CK_SLOT_INFO *slot_info,
+                const CK_TOKEN_INFO *token_info,
+                P11KitUri *uri,
+                void *userdata) {
+
+        CK_OBJECT_HANDLE object;
+        CK_RV rv;
+        CK_TOKEN_INFO updated_token_info;
+        int r;
+        _cleanup_free_ char *token_label = NULL;
+        struct luks2_pkcs11_callback_data *data = userdata;
+
+        assert(m);
+        assert(slot_info);
+        assert(token_info);
+        assert(uri);
+        assert(data);
+
+        token_label = pkcs11_token_label(token_info);
+        if (!token_label)
+                return -ENOMEM;
+
+        /* Called for every token matching our URI */
+        r = pkcs11_token_login_by_pin(m, session, token_info, token_label, data->pin, data->pin_size);
+        if (r == -ENOLCK) {
+                /* Referesh the token info, so that we can prompt knowing the new flags if they changed. */
+                rv = m->C_GetTokenInfo(slot_id, &updated_token_info);
+                if (rv != CKR_OK) {
+                        crypt_log_error(data->cd,
+                                       "Failed to acquire updated security token information for slot %lu: %s",
+                                       slot_id, p11_kit_strerror(rv));
+                        return -EIO;
+                }
+                token_info = &updated_token_info;
+                r = -ENOANO;
+        }
+
+        if (r == -ENOANO) {
+                if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
+                        crypt_log_error(data->cd, "Please enter correct PIN for security token "
+                                        "'%s' in order to unlock it (final try).", token_label);
+                else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW))
+                        crypt_log_error(data->cd, "PIN has been entered incorrectly previously, "
+                                      "please enter correct PIN for security token '%s' in order to unlock it.",
+                                      token_label);
+        }
+
+        if (r == -EPERM) /* pin is locked, but map it to -ENOANO anyway */
+                r = -ENOANO;
+
+        if (r < 0)
+                return r;
+
+        r = pkcs11_token_find_private_key(m, session, uri, &object);
+        if (r < 0)
+                return r;
+
+        r = pkcs11_token_decrypt_data(
+                        m,
+                        session,
+                        object,
+                        data->encrypted_key,
+                        data->encrypted_key_size,
+                        &data->decrypted_key,
+                        &data->decrypted_key_size);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static void luks2_pkcs11_callback_data_release(struct luks2_pkcs11_callback_data *data) {
+        erase_and_free(data->decrypted_key);
+}
+
+static int acquire_luks2_key_by_pin(
+                struct crypt_device *cd,
+                const char *pkcs11_uri,
+                const void *pin,
+                size_t pin_size,
+                void *encrypted_key,
+                size_t encrypted_key_size,
+                void **ret_decrypted_key,
+                size_t *ret_decrypted_key_size) {
+
+        int r;
+        _cleanup_(luks2_pkcs11_callback_data_release) struct luks2_pkcs11_callback_data data = {
+                .cd = cd,
+                .pin = pin,
+                .pin_size = pin_size,
+                .encrypted_key = encrypted_key,
+                .encrypted_key_size = encrypted_key_size,
+        };
+
+        assert(pkcs11_uri);
+        assert(encrypted_key);
+        assert(ret_decrypted_key);
+        assert(ret_decrypted_key_size);
+
+        r = pkcs11_find_token(pkcs11_uri, luks2_pkcs11_callback, &data);
+        if (r < 0)
+                return r;
+
+        *ret_decrypted_key = TAKE_PTR(data.decrypted_key);
+        *ret_decrypted_key_size = data.decrypted_key_size;
+
+        return 0;
+}
+
+/* called from within systemd utilities */
+static int acquire_luks2_key_systemd(
+                const char *pkcs11_uri,
+                systemd_pkcs11_plugin_params *params,
+                void *encrypted_key,
+                size_t encrypted_key_size,
+                void **ret_decrypted_key,
+                size_t *ret_decrypted_key_size) {
+
+        int r;
+        _cleanup_(pkcs11_crypt_device_callback_data_release) pkcs11_crypt_device_callback_data data = {
+                .encrypted_key = encrypted_key,
+                .encrypted_key_size = encrypted_key_size,
+                .free_encrypted_key = false
+        };
+
+        assert(pkcs11_uri);
+        assert(encrypted_key);
+        assert(ret_decrypted_key);
+        assert(ret_decrypted_key_size);
+        assert(params);
+
+        data.friendly_name = params->friendly_name;
+        data.headless = params->headless;
+        data.until = params->until;
+
+        /* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
+        r = pkcs11_find_token(pkcs11_uri, pkcs11_crypt_device_callback, &data);
+        if (r < 0)
+                return r;
+
+        *ret_decrypted_key = TAKE_PTR(data.decrypted_key);
+        *ret_decrypted_key_size = data.decrypted_key_size;
+
+        return 0;
+}
+
+int acquire_luks2_key(
+                struct crypt_device *cd,
+                const char *json,
+                void *userdata,
+                const void *pin,
+                size_t pin_size,
+                char **ret_password,
+                size_t *ret_password_size) {
+
+        int r;
+        size_t decrypted_key_size, encrypted_key_size;
+        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+        _cleanup_free_ char *pkcs11_uri = NULL;
+        _cleanup_free_ void *encrypted_key = NULL;
+        systemd_pkcs11_plugin_params *pkcs11_params = userdata;
+
+        assert(json);
+        assert(ret_password);
+        assert(ret_password_size);
+
+        r = parse_luks2_pkcs11_data(cd, json, &pkcs11_uri, &encrypted_key, &encrypted_key_size);
+        if (r < 0)
+                return r;
+
+        if (pkcs11_params && pin)
+                crypt_log_verbose(cd, "PIN parameter ignored in interactive mode.");
+
+        if (pkcs11_params) /* systemd based activation with interactive pin query callbacks */
+                r = acquire_luks2_key_systemd(
+                        pkcs11_uri,
+                        pkcs11_params,
+                        encrypted_key, encrypted_key_size,
+                        &decrypted_key, &decrypted_key_size);
+        else /* default activation that provides single PIN if needed */
+                r = acquire_luks2_key_by_pin(
+                        cd, pkcs11_uri, pin, pin_size,
+                        encrypted_key, encrypted_key_size,
+                        &decrypted_key, &decrypted_key_size);
+        if (r < 0)
+                return r;
+
+        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        if (r < 0)
+                return crypt_log_error_errno(cd, r, "Can not base64 encode key: %m");
+
+        *ret_password = TAKE_PTR(base64_encoded);
+        *ret_password_size = strlen(*ret_password);
+
+        return 0;
+}
+
+int parse_luks2_pkcs11_data(
+                struct crypt_device *cd,
+                const char *json,
+                char **ret_uri,
+                void **ret_encrypted_key,
+                size_t *ret_encrypted_key_size) {
+
+        int r;
+        size_t key_size;
+        _cleanup_free_ char *uri = NULL;
+        _cleanup_free_ void *key = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        JsonVariant *w;
+
+        assert(json);
+        assert(ret_uri);
+        assert(ret_encrypted_key);
+        assert(ret_encrypted_key_size);
+
+        r = json_parse(json, 0, &v, NULL, NULL);
+        if (r < 0)
+                return r;
+
+        w = json_variant_by_key(v, "pkcs11-uri");
+        if (!w)
+                return -EINVAL;
+
+        uri = strdup(json_variant_string(w));
+        if (!uri)
+                return -ENOMEM;
+
+        w = json_variant_by_key(v, "pkcs11-key");
+        if (!w)
+                return -EINVAL;
+
+        r = unbase64mem(json_variant_string(w), SIZE_MAX, &key, &key_size);
+        if (r < 0)
+                return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded key: %m.");
+
+        *ret_uri = TAKE_PTR(uri);
+        *ret_encrypted_key = TAKE_PTR(key);
+        *ret_encrypted_key_size = key_size;
+
+        return 0;
+}
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.h b/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.h
new file mode 100644 (file)
index 0000000..41ce9f0
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#pragma once
+
+struct crypt_device;
+
+int acquire_luks2_key(
+                struct crypt_device *cd,
+                const char *json,
+                void *userdata,
+                const void *pin,
+                size_t pin_size,
+                char **password,
+                size_t *password_size);
+
+int parse_luks2_pkcs11_data(
+                struct crypt_device *cd,
+                const char *json,
+                char **ret_uri,
+                void **ret_encrypted_key,
+                size_t *ret_encrypted_key_size);
index 00540659266cda50e250426fcb17fce7973195d4..a5571f31f6dba262d3deab64ef69414572c05d17 100644 (file)
@@ -10,6 +10,7 @@
 
 int acquire_luks2_key(
                 uint32_t pcr_mask,
+                uint16_t pcr_bank,
                 const char *device,
                 const void *key_data,
                 size_t key_data_size,
@@ -34,7 +35,12 @@ int acquire_luks2_key(
                 device = auto_device;
         }
 
-        return tpm2_unseal(device, pcr_mask, key_data, key_data_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
+        return tpm2_unseal(
+                        device,
+                        pcr_mask, pcr_bank,
+                        key_data, key_data_size,
+                        policy_hash, policy_hash_size,
+                        ret_decrypted_key, ret_decrypted_key_size);
 }
 
 /* this function expects valid "systemd-tpm2" in json */
@@ -42,19 +48,22 @@ int parse_luks2_tpm2_data(
                 const char *json,
                 uint32_t search_pcr_mask,
                 uint32_t *ret_pcr_mask,
+                uint16_t *ret_pcr_bank,
                 char **ret_base64_blob,
                 char **ret_hex_policy_hash) {
 
         int r;
         JsonVariant *w, *e;
         uint32_t pcr_mask = 0;
+        uint16_t pcr_bank = UINT16_MAX;
         _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
 
         assert(json);
+        assert(ret_pcr_mask);
+        assert(ret_pcr_bank);
         assert(ret_base64_blob);
         assert(ret_hex_policy_hash);
-        assert(ret_pcr_mask);
 
         r = json_parse(json, 0, &v, NULL, NULL);
         if (r < 0)
@@ -81,6 +90,20 @@ int parse_luks2_tpm2_data(
             search_pcr_mask != pcr_mask)
                 return -ENXIO;
 
+        w = json_variant_by_key(v, "tpm2-pcr-bank");
+        if (w) {
+                /* The PCR bank field is optional */
+
+                if (!json_variant_is_string(w))
+                        return -EINVAL;
+
+                r = tpm2_pcr_bank_from_string(json_variant_string(w));
+                if (r < 0)
+                        return r;
+
+                pcr_bank = r;
+        }
+
         w = json_variant_by_key(v, "tpm2-blob");
         if (!w || !json_variant_is_string(w))
                 return -EINVAL;
@@ -98,6 +121,7 @@ int parse_luks2_tpm2_data(
                 return -ENOMEM;
 
         *ret_pcr_mask = pcr_mask;
+        *ret_pcr_bank = pcr_bank;
         *ret_base64_blob = TAKE_PTR(base64_blob);
         *ret_hex_policy_hash = TAKE_PTR(hex_policy_hash);
 
index d36623baf960355295a1b868f34bf1cd8dd606c2..1a20f2cc1fd73bd1480b18ef2efbc0328f6571d9 100644 (file)
@@ -6,6 +6,7 @@ struct crypt_device;
 
 int acquire_luks2_key(
                 uint32_t pcr_mask,
+                uint16_t pcr_bank,
                 const char *device,
                 const void *key_data,
                 size_t key_data_size,
@@ -18,5 +19,6 @@ int parse_luks2_tpm2_data(
                 const char *json,
                 uint32_t search_pcr_mask,
                 uint32_t *ret_pcr_mask,
+                uint16_t *ret_pcr_bank,
                 char **ret_base64_blob,
                 char **ret_hex_policy_hash);
index 61c879c768a8ca2cecb192633c5aa258ee834313..d174fcb714ec7f91845b08502b292da2baeb1a2e 100644 (file)
@@ -5,7 +5,7 @@ if conf.get('HAVE_LIBCRYPTSETUP_PLUGINS') == 1
 cryptsetup_token_c_args = ['-fvisibility=hidden']
 
 cryptsetup_token_sym = files('cryptsetup-token.sym')
-cryptsetup_token_sym_path = join_paths(meson.current_source_dir(), 'cryptsetup-token.sym')
+cryptsetup_token_sym_path = meson.current_source_dir() / 'cryptsetup-token.sym'
 
 if conf.get('HAVE_TPM2') == 1
         cryptsetup_token_systemd_tpm2_sources = files('''
@@ -21,7 +21,43 @@ if conf.get('HAVE_TPM2') == 1
                 'cryptsetup-token-systemd-tpm2_static',
                 cryptsetup_token_systemd_tpm2_sources,
                 include_directories : includes,
-                dependencies : libshared_deps + [libcryptsetup],
+                dependencies : libshared_deps + [libcryptsetup, versiondep],
+                c_args : cryptsetup_token_c_args)
+endif
+
+if conf.get('HAVE_LIBFIDO2') == 1
+        cryptsetup_token_systemd_fido2_sources = files('''
+                cryptsetup-token-systemd-fido2.c
+                cryptsetup-token.h
+                cryptsetup-token-util.h
+                cryptsetup-token-util.c
+                luks2-fido2.c
+                luks2-fido2.h
+        '''.split())
+
+        cryptsetup_token_systemd_fido2_static = static_library(
+                'cryptsetup-token-systemd-fido2_static',
+                cryptsetup_token_systemd_fido2_sources,
+                include_directories : includes,
+                dependencies : libshared_deps + [libcryptsetup, versiondep],
+                c_args : cryptsetup_token_c_args)
+endif
+
+if conf.get('HAVE_P11KIT') == 1
+        cryptsetup_token_systemd_pkcs11_sources = files('''
+                cryptsetup-token-systemd-pkcs11.c
+                cryptsetup-token.h
+                cryptsetup-token-util.h
+                cryptsetup-token-util.c
+                luks2-pkcs11.c
+                luks2-pkcs11.h
+        '''.split())
+
+        cryptsetup_token_systemd_pkcs11_static = static_library(
+                'cryptsetup-token-systemd-pkcs11_static',
+                cryptsetup_token_systemd_pkcs11_sources,
+                include_directories : includes,
+                dependencies : libshared_deps + [libcryptsetup, versiondep],
                 c_args : cryptsetup_token_c_args)
 endif
 
index 4757c5882d0e3becbfe14e36e04738809062dc9a..8da1880a35c4d8f75cf9eee93c9c2b144d76405f 100644 (file)
@@ -13,6 +13,7 @@ int acquire_tpm2_key(
                 const char *volume_name,
                 const char *device,
                 uint32_t pcr_mask,
+                uint16_t pcr_bank,
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
@@ -62,7 +63,7 @@ int acquire_tpm2_key(
                 blob = loaded_blob;
         }
 
-        return tpm2_unseal(device, pcr_mask, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
+        return tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
 }
 
 int find_tpm2_auto_data(
@@ -70,6 +71,7 @@ int find_tpm2_auto_data(
                 uint32_t search_pcr_mask,
                 int start_token,
                 uint32_t *ret_pcr_mask,
+                uint16_t *ret_pcr_bank,
                 void **ret_blob,
                 size_t *ret_blob_size,
                 void **ret_policy_hash,
@@ -81,6 +83,7 @@ int find_tpm2_auto_data(
         size_t blob_size = 0, policy_hash_size = 0;
         int r, keyslot = -1, token = -1;
         uint32_t pcr_mask = 0;
+        uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
 
         assert(cd);
 
@@ -119,6 +122,23 @@ int find_tpm2_auto_data(
                     search_pcr_mask != pcr_mask) /* PCR mask doesn't match what is configured, ignore this entry */
                         continue;
 
+                /* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded to SHA256 */
+                assert(pcr_bank == UINT16_MAX);
+                w = json_variant_by_key(v, "tpm2-pcr-bank");
+                if (w) {
+                        /* The PCR bank field is optional */
+
+                        if (!json_variant_is_string(w))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "TPM2 PCR bank is not a string.");
+
+                        r = tpm2_pcr_bank_from_string(json_variant_string(w));
+                        if (r < 0)
+                                return log_error_errno(r, "TPM2 PCR bank invalid or not supported: %s", json_variant_string(w));
+
+                        pcr_bank = r;
+                }
+
                 assert(!blob);
                 w = json_variant_by_key(v, "tpm2-blob");
                 if (!w || !json_variant_is_string(w))
@@ -163,6 +183,7 @@ int find_tpm2_auto_data(
         *ret_policy_hash_size = policy_hash_size;
         *ret_keyslot = keyslot;
         *ret_token = token;
+        *ret_pcr_bank = pcr_bank;
 
         return 0;
 }
index 8ddf301a6357101802419a7b3530b6e3eeaa9542..a82ecb459419d819a190d85adbf601a7361b777b 100644 (file)
@@ -13,6 +13,7 @@ int acquire_tpm2_key(
                 const char *volume_name,
                 const char *device,
                 uint32_t pcr_mask,
+                uint16_t pcr_bank,
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
@@ -28,6 +29,7 @@ int find_tpm2_auto_data(
                 uint32_t search_pcr_mask,
                 int start_token,
                 uint32_t *ret_pcr_mask,
+                uint16_t *ret_pcr_bank,
                 void **ret_blob,
                 size_t *ret_blob_size,
                 void **ret_policy_hash,
@@ -41,6 +43,7 @@ static inline int acquire_tpm2_key(
                 const char *volume_name,
                 const char *device,
                 uint32_t pcr_mask,
+                uint16_t pcr_bank,
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
@@ -60,6 +63,7 @@ static inline int find_tpm2_auto_data(
                 uint32_t search_pcr_mask,
                 int start_token,
                 uint32_t *ret_pcr_mask,
+                uint16_t *ret_pcr_bank,
                 void **ret_blob,
                 size_t *ret_blob_size,
                 void **ret_policy_hash,
index 48621ef5838e45e91907c315f0d3d64a84e9a9cd..fdd57def86866bd17f15c2b0585bbe9d7356bec0 100644 (file)
@@ -736,6 +736,105 @@ static int make_security_device_monitor(sd_event *event, sd_device_monitor **ret
         return 0;
 }
 
+static bool libcryptsetup_plugins_support(void) {
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+        return crypt_token_external_path() != NULL;
+#else
+        return false;
+#endif
+}
+
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+static int acquire_pins_from_env_variable(char ***ret_pins) {
+        char *e;
+        _cleanup_strv_free_erase_ char **pins = NULL;
+
+        assert(ret_pins);
+
+        e = getenv("PIN");
+        if (e) {
+                pins = strv_new(e);
+                if (!pins)
+                        return log_oom();
+
+                string_erase(e);
+                if (unsetenv("PIN") < 0)
+                        return log_error_errno(errno, "Failed to unset $PIN: %m");
+        }
+
+        *ret_pins = TAKE_PTR(pins);
+
+        return 0;
+}
+#endif
+
+static int attach_luks2_by_fido2(
+                struct crypt_device *cd,
+                const char *name,
+                usec_t until,
+                bool headless,
+                void *usrptr,
+                uint32_t activation_flags) {
+
+        int r = -EOPNOTSUPP;
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+        char **p;
+        _cleanup_strv_free_erase_ char **pins = NULL;
+        AskPasswordFlags flags = ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
+
+        r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags);
+        if (r > 0) /* returns unlocked keyslot id on success */
+                r = 0;
+        if (r != -ENOANO) /* needs pin or pin is wrong */
+                return r;
+
+        r = acquire_pins_from_env_variable(&pins);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(p, pins) {
+                r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
+                if (r > 0) /* returns unlocked keyslot id on success */
+                        r = 0;
+                if (r != -ENOANO) /* needs pin or pin is wrong */
+                        return r;
+        }
+
+        if (headless)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
+
+        pins = strv_free_erase(pins);
+        r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(p, pins) {
+                r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
+                if (r > 0) /* returns unlocked keyslot id on success */
+                        r = 0;
+                if (r != -ENOANO) /* needs pin or pin is wrong */
+                        return r;
+        }
+
+        flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
+        for (;;) {
+                pins = strv_free_erase(pins);
+                r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
+                if (r < 0)
+                        return r;
+
+                STRV_FOREACH(p, pins) {
+                        r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
+                        if (r > 0) /* returns unlocked keyslot id on success */
+                                r = 0;
+                        if (r != -ENOANO) /* needs pin or pin is wrong */
+                                return r;
+                }
+        }
+#endif
+        return r;
+}
+
 static int attach_luks_or_plain_or_bitlk_by_fido2(
                 struct crypt_device *cd,
                 const char *name,
@@ -750,12 +849,13 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
         _cleanup_(erase_and_freep) void *decrypted_key = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         _cleanup_free_ void *discovered_salt = NULL, *discovered_cid = NULL;
-        size_t discovered_salt_size, discovered_cid_size, cid_size, decrypted_key_size;
+        size_t discovered_salt_size, discovered_cid_size, decrypted_key_size, cid_size = 0;
         _cleanup_free_ char *friendly = NULL, *discovered_rp_id = NULL;
         int keyslot = arg_key_slot, r;
-        const char *rp_id;
-        const void *cid;
+        const char *rp_id = NULL;
+        const void *cid = NULL;
         Fido2EnrollFlags required;
+        bool use_libcryptsetup_plugin = libcryptsetup_plugins_support();
 
         assert(cd);
         assert(name);
@@ -775,7 +875,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
                  * use PIN + UP when needed, and do not configure UV at all. Eventually, we should make this
                  * explicitly configurable. */
                 required = FIDO2ENROLL_PIN_IF_NEEDED | FIDO2ENROLL_UP_IF_NEEDED | FIDO2ENROLL_UV_OMIT;
-        } else {
+        } else if (!use_libcryptsetup_plugin) {
                 r = find_fido2_auto_data(
                                 cd,
                                 &discovered_rp_id,
@@ -810,21 +910,30 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
         for (;;) {
                 bool processed = false;
 
-                r = acquire_fido2_key(
-                                name,
-                                friendly,
-                                arg_fido2_device,
-                                rp_id,
-                                cid, cid_size,
-                                key_file, arg_keyfile_size, arg_keyfile_offset,
-                                key_data, key_data_size,
-                                until,
-                                arg_headless,
-                                required,
-                                &decrypted_key, &decrypted_key_size,
-                                arg_ask_password_flags);
-                if (r >= 0)
-                        break;
+                if (use_libcryptsetup_plugin && !arg_fido2_cid) {
+                        r = attach_luks2_by_fido2(cd, name, until, arg_headless, arg_fido2_device, flags);
+                        if (IN_SET(r, -ENOTUNIQ, -ENXIO, -ENOENT))
+                                return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+                                                       "Automatic FIDO2 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
+
+                } else {
+                        r = acquire_fido2_key(
+                                        name,
+                                        friendly,
+                                        arg_fido2_device,
+                                        rp_id,
+                                        cid, cid_size,
+                                        key_file, arg_keyfile_size, arg_keyfile_offset,
+                                        key_data, key_data_size,
+                                        until,
+                                        arg_headless,
+                                        required,
+                                        &decrypted_key, &decrypted_key_size,
+                                        arg_ask_password_flags);
+                        if (r >= 0)
+                                break;
+                }
+
                 if (r != -EAGAIN) /* EAGAIN means: token not found */
                         return r;
 
@@ -887,6 +996,32 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
         return 0;
 }
 
+static int attach_luks2_by_pkcs11(
+                struct crypt_device *cd,
+                const char *name,
+                const char *friendly_name,
+                usec_t until,
+                bool headless,
+                uint32_t flags) {
+
+        int r = -ENOTSUP;
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+        if (!crypt_get_type(cd) || strcmp(crypt_get_type(cd), CRYPT_LUKS2))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Automatic PKCS#11 metadata requires LUKS2 device.");
+
+        systemd_pkcs11_plugin_params params = {
+                .friendly_name = friendly_name,
+                .until = until,
+                .headless = headless
+        };
+
+        r = crypt_activate_by_token_pin(cd, name, "systemd-pkcs11", CRYPT_ANY_TOKEN, NULL, 0, &params, flags);
+        if (r > 0) /* returns unlocked keyslot id on success */
+                r = 0;
+#endif
+        return r;
+}
+
 static int attach_luks_or_plain_or_bitlk_by_pkcs11(
                 struct crypt_device *cd,
                 const char *name,
@@ -904,23 +1039,26 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         _cleanup_free_ void *discovered_key = NULL;
         int keyslot = arg_key_slot, r;
-        const char *uri;
+        const char *uri = NULL;
+        bool use_libcryptsetup_plugin = libcryptsetup_plugins_support();
 
         assert(cd);
         assert(name);
         assert(arg_pkcs11_uri || arg_pkcs11_uri_auto);
 
         if (arg_pkcs11_uri_auto) {
-                r = find_pkcs11_auto_data(cd, &discovered_uri, &discovered_key, &discovered_key_size, &keyslot);
-                if (IN_SET(r, -ENOTUNIQ, -ENXIO))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                               "Automatic PKCS#11 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
-                if (r < 0)
-                        return r;
+                if (!use_libcryptsetup_plugin) {
+                        r = find_pkcs11_auto_data(cd, &discovered_uri, &discovered_key, &discovered_key_size, &keyslot);
+                        if (IN_SET(r, -ENOTUNIQ, -ENXIO))
+                                return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+                                                       "Automatic PKCS#11 metadata discovery was not possible because missing or not unique, falling back to traditional unlocking.");
+                        if (r < 0)
+                                return r;
 
-                uri = discovered_uri;
-                key_data = discovered_key;
-                key_data_size = discovered_key_size;
+                        uri = discovered_uri;
+                        key_data = discovered_key;
+                        key_data_size = discovered_key_size;
+                }
         } else {
                 uri = arg_pkcs11_uri;
 
@@ -935,17 +1073,22 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
         for (;;) {
                 bool processed = false;
 
-                r = decrypt_pkcs11_key(
-                                name,
-                                friendly,
-                                uri,
-                                key_file, arg_keyfile_size, arg_keyfile_offset,
-                                key_data, key_data_size,
-                                until,
-                                arg_headless,
-                                &decrypted_key, &decrypted_key_size);
-                if (r >= 0)
-                        break;
+                if (use_libcryptsetup_plugin && arg_pkcs11_uri_auto)
+                        r = attach_luks2_by_pkcs11(cd, name, friendly, until, arg_headless, flags);
+                else {
+                        r = decrypt_pkcs11_key(
+                                        name,
+                                        friendly,
+                                        uri,
+                                        key_file, arg_keyfile_size, arg_keyfile_offset,
+                                        key_data, key_data_size,
+                                        until,
+                                        arg_headless,
+                                        &decrypted_key, &decrypted_key_size);
+                        if (r >= 0)
+                                break;
+                }
+
                 if (r != -EAGAIN) /* EAGAIN means: token not found */
                         return r;
 
@@ -985,6 +1128,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
 
                 log_debug("Got one or more potentially relevant udev events, rescanning PKCS#11...");
         }
+        assert(decrypted_key);
 
         if (pass_volume_key)
                 r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
@@ -1103,6 +1247,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                         name,
                                         arg_tpm2_device,
                                         arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask,
+                                        UINT16_MAX,
                                         key_file, arg_keyfile_size, arg_keyfile_offset,
                                         key_data, key_data_size,
                                         NULL, 0, /* we don't know the policy hash */
@@ -1139,12 +1284,14 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
 
                         for (;;) {
                                 uint32_t pcr_mask;
+                                uint16_t pcr_bank;
 
                                 r = find_tpm2_auto_data(
                                                 cd,
                                                 arg_tpm2_pcr_mask, /* if != UINT32_MAX we'll only look for tokens with this PCR mask */
                                                 token, /* search for the token with this index, or any later index than this */
                                                 &pcr_mask,
+                                                &pcr_bank,
                                                 &blob, &blob_size,
                                                 &policy_hash, &policy_hash_size,
                                                 &keyslot,
@@ -1166,6 +1313,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                                 name,
                                                 arg_tpm2_device,
                                                 pcr_mask,
+                                                pcr_bank,
                                                 NULL, 0, 0, /* no key file */
                                                 blob, blob_size,
                                                 policy_hash, policy_hash_size,
index f78c6b9c0d3ee8c549e966f84804e14ab0cf6d90..282168a584ccb8de0ddc6ba11038e9fb3d54a2e7 100644 (file)
@@ -625,7 +625,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index bfdede6ce7a5c05fe0697caaa1649a4366016791..d284fcbe82abac98460c8b6211d2e88873836147 100644 (file)
@@ -109,7 +109,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind < argc)
index 88bb3de40f776e14cd8c8f29870a97d0a80f9eee..4693e7433b47c0236d879701d17446fe2ca3b111 100644 (file)
@@ -287,7 +287,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         }
@@ -344,7 +344,7 @@ static int parse_argv(int argc, char *argv[]) {
                 break;
 
         default:
-                assert_not_reached("Unknown action.");
+                assert_not_reached();
         }
 
         return 1;
@@ -679,7 +679,7 @@ static int action_copy(DissectedImage *m, LoopDevice *d) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target);
 
-                (void) copy_xattr(source_fd, target_fd);
+                (void) copy_xattr(source_fd, target_fd, 0);
                 (void) copy_access(source_fd, target_fd);
                 (void) copy_times(source_fd, target_fd, 0);
 
@@ -748,7 +748,7 @@ static int action_copy(DissectedImage *m, LoopDevice *d) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
 
-                (void) copy_xattr(source_fd, target_fd);
+                (void) copy_xattr(source_fd, target_fd, 0);
                 (void) copy_access(source_fd, target_fd);
                 (void) copy_times(source_fd, target_fd, 0);
 
@@ -818,7 +818,7 @@ static int run(int argc, char *argv[]) {
                 break;
 
         default:
-                assert_not_reached("Unknown action.");
+                assert_not_reached();
         }
 
         return r;
index 1575a684107709170c93c7924ca59210c02caeb4..167305cb03322fe151b27b70438b48a6d9dd1cf4 100644 (file)
@@ -119,7 +119,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind >= argc)
index 2cb4f80d5d5d9a00398a2f6a0023908811b3ebfb..93ce800bd7b4876d006f8d4c11b2568358e541ef 100644 (file)
@@ -566,7 +566,6 @@ static int process_hostname(void) {
 
 static int process_machine_id(void) {
         const char *etc_machine_id;
-        char id[SD_ID128_STRING_MAX];
         int r;
 
         etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
@@ -576,7 +575,7 @@ static int process_machine_id(void) {
         if (sd_id128_is_null(arg_machine_id))
                 return 0;
 
-        r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id),
+        r = write_string_file(etc_machine_id, SD_ID128_TO_STRING(arg_machine_id),
                               WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
                               (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
         if (r < 0)
@@ -1299,7 +1298,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         /* We check if the specified locale strings are valid down here, so that we can take --root= into
index bafc7b8f960bb5d2a75dbeea1d8edc1cd6166471..8a4340c5ba9290d0e5571f34855ee72a77efe3f4 100644 (file)
@@ -434,7 +434,8 @@ static int add_mount(
 
         /* Order the mount unit we generate relative to the post unit, so that DefaultDependencies= on the
          * target unit won't affect us. */
-        if (post && !FLAGS_SET(flags, MOUNT_AUTOMOUNT) && !FLAGS_SET(flags, MOUNT_NOAUTO))
+        if (post && !FLAGS_SET(flags, MOUNT_AUTOMOUNT) && !FLAGS_SET(flags, MOUNT_NOAUTO) &&
+            !FLAGS_SET(flags, MOUNT_NOFAIL))
                 fprintf(f, "Before=%s\n", post);
 
         if (passno != 0) {
@@ -451,6 +452,10 @@ static int add_mount(
                 "\n"
                 "[Mount]\n");
 
+        r = write_what(f, what);
+        if (r < 0)
+                return r;
+
         if (original_where)
                 fprintf(f, "# Canonicalized from %s\n", original_where);
 
@@ -459,10 +464,6 @@ static int add_mount(
                 return log_oom();
         fprintf(f, "Where=%s\n", where_escaped);
 
-        r = write_what(f, what);
-        if (r < 0)
-                return r;
-
         if (!isempty(fstype) && !streq(fstype, "auto")) {
                 _cleanup_free_ char *t = NULL;
 
index 967518600d404db52ad232d87789302899f2ff21..20d8dabf296fd5bbfcd96f58fd5a90f97be08de4 100644 (file)
 #define _const_ __attribute__((__const__))
 #define _pure_ __attribute__((__pure__))
 #define _section_(x) __attribute__((__section__(x)))
+#define _packed_ __attribute__((__packed__))
 #define _used_ __attribute__((__used__))
 #define _unused_ __attribute__((__unused__))
 #define _cleanup_(x) __attribute__((__cleanup__(x)))
+#define _likely_(x) (__builtin_expect(!!(x), 1))
+#define _unlikely_(x) (__builtin_expect(!!(x), 0))
+#if __GNUC__ >= 7
+#define _fallthrough_ __attribute__((__fallthrough__))
+#else
+#define _fallthrough_
+#endif
+/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
+ * compiler versions */
+#ifndef _noreturn_
+#if __STDC_VERSION__ >= 201112L
+#define _noreturn_ _Noreturn
+#else
+#define _noreturn_ __attribute__((__noreturn__))
+#endif
+#endif
 
 #define XSTRINGIFY(x) #x
 #define STRINGIFY(x) XSTRINGIFY(x)
 #define CONCATENATE(x, y) XCONCATENATE(x, y)
 
 #ifdef SD_BOOT
-#define assert(expr) do {} while (false)
+        #ifdef NDEBUG
+                #define assert(expr)
+                #define assert_not_reached() __builtin_unreachable()
+        #else
+                void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
+                #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
+                #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+        #endif
 #endif
 
 #if defined(static_assert)
                 UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
         })
 
-/* evaluates to (void) if _A or _B are not constant or of different types */
+#define IS_UNSIGNED_INTEGER_TYPE(type) \
+        (__builtin_types_compatible_p(typeof(type), unsigned char) ||   \
+         __builtin_types_compatible_p(typeof(type), unsigned short) ||  \
+         __builtin_types_compatible_p(typeof(type), unsigned) ||        \
+         __builtin_types_compatible_p(typeof(type), unsigned long) ||   \
+         __builtin_types_compatible_p(typeof(type), unsigned long long))
+
+#define IS_SIGNED_INTEGER_TYPE(type) \
+        (__builtin_types_compatible_p(typeof(type), signed char) ||   \
+         __builtin_types_compatible_p(typeof(type), signed short) ||  \
+         __builtin_types_compatible_p(typeof(type), signed) ||        \
+         __builtin_types_compatible_p(typeof(type), signed long) ||   \
+         __builtin_types_compatible_p(typeof(type), signed long long))
+
+/* Evaluates to (void) if _A or _B are not constant or of different types (being integers of different sizes
+ * is also OK as long as the signedness matches) */
 #define CONST_MAX(_A, _B) \
         (__builtin_choose_expr(                                         \
                 __builtin_constant_p(_A) &&                             \
                 __builtin_constant_p(_B) &&                             \
-                __builtin_types_compatible_p(typeof(_A), typeof(_B)),   \
+                (__builtin_types_compatible_p(typeof(_A), typeof(_B)) || \
+                 (IS_UNSIGNED_INTEGER_TYPE(_A) && IS_UNSIGNED_INTEGER_TYPE(_B)) || \
+                 (IS_SIGNED_INTEGER_TYPE(_A) && IS_SIGNED_INTEGER_TYPE(_B))), \
                 ((_A) > (_B)) ? (_A) : (_B),                            \
                 VOID_0))
 
                 (ptr) = NULL;                   \
                 _ptr_;                          \
         })
+
+/*
+ * STRLEN - return the length of a string literal, minus the trailing NUL byte.
+ *          Contrary to strlen(), this is a constant expression.
+ * @x: a string literal.
+ */
+#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
index 40b9ab8e2cd3d02e904090fb846e1c0289aaac42..3c43081cf61d54f1c0d68b9653cd303b46d0174e 100644 (file)
@@ -14,8 +14,8 @@ sources = '''
 
 # for sd-boot
 fundamental_source_paths = []
-foreach s : sources
-        fundamental_source_paths += join_paths(meson.current_source_dir(), s)
+foreach source : sources
+        fundamental_source_paths += meson.current_source_dir() / source
 endforeach
 
 # for libbasic
index 407cede48d987e6b955f3a5afed7edadfccc4896..7455c05492392c4fd5bf2a838a8b15d43ae0a7a6 100644 (file)
@@ -16,6 +16,7 @@
 #define strncmp(a, b, n) StrnCmp((a), (b), (n))
 #define strcasecmp(a, b) StriCmp((a), (b))
 #define STR_C(str)       (L ## str)
+#define memcmp(a, b, n)  CompareMem(a, b, n)
 #else
 #define STR_C(str)       (str)
 #endif
@@ -65,3 +66,19 @@ static inline const sd_char *yes_no(sd_bool b) {
 }
 
 sd_int strverscmp_improved(const sd_char *a, const sd_char *b);
+
+/* Like startswith(), but operates on arbitrary memory blocks */
+static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) {
+        assert(token);
+
+        sd_size_t n = strlen(token) * sizeof(sd_char);
+        if (sz < n)
+                return NULL;
+
+        assert(p);
+
+        if (memcmp(p, token, n) != 0)
+                return NULL;
+
+        return (uint8_t*) p + n;
+}
index 6273cb6c90693bf09c57cdad0019d01590e08069..227c499c9c7e16b7b7beec663ec676e7de23dbfe 100644 (file)
@@ -215,9 +215,7 @@ static int acquire_existing_password(
                 if (r < 0)
                         return log_error_errno(r, "Failed to store password: %m");
 
-                string_erase(e);
-                assert_se(unsetenv("PASSWORD") == 0);
-
+                assert_se(unsetenv_erase("PASSWORD") >= 0);
                 return 1;
         }
 
@@ -273,9 +271,7 @@ static int acquire_token_pin(
                 if (r < 0)
                         return log_error_errno(r, "Failed to store token PIN: %m");
 
-                string_erase(e);
-                assert_se(unsetenv("PIN") == 0);
-
+                assert_se(unsetenv_erase("PIN") >= 0);
                 return 1;
         }
 
@@ -821,14 +817,13 @@ static int apply_identity_changes(JsonVariant **_v) {
 
         if (arg_identity_extra_this_machine || !strv_isempty(arg_identity_filter)) {
                 _cleanup_(json_variant_unrefp) JsonVariant *per_machine = NULL, *mmid = NULL;
-                char mids[SD_ID128_STRING_MAX];
                 sd_id128_t mid;
 
                 r = sd_id128_get_machine(&mid);
                 if (r < 0)
                         return log_error_errno(r, "Failed to acquire machine ID: %m");
 
-                r = json_variant_new_string(&mmid, sd_id128_to_string(mid, mids));
+                r = json_variant_new_string(&mmid, SD_ID128_TO_STRING(mid));
                 if (r < 0)
                         return log_error_errno(r, "Failed to allocate matchMachineId object: %m");
 
@@ -1097,8 +1092,7 @@ static int acquire_new_password(
                 if (r < 0)
                         return log_error_errno(r, "Failed to store password: %m");
 
-                string_erase(e);
-                assert_se(unsetenv("NEWPASSWORD") == 0);
+                assert_se(unsetenv_erase("NEWPASSWORD") >= 0);
 
                 if (ret)
                         *ret = TAKE_PTR(copy);
@@ -2033,143 +2027,143 @@ static int help(int argc, char *argv[], void *userdata) {
         printf("%1$s [OPTIONS...] COMMAND ...\n\n"
                "%2$sCreate, manipulate or inspect home directories.%3$s\n"
                "\n%4$sCommands:%5$s\n"
-               "  list                        List home areas\n"
-               "  activate USER…              Activate a home area\n"
-               "  deactivate USER…            Deactivate a home area\n"
-               "  inspect USER…               Inspect a home area\n"
-               "  authenticate USER…          Authenticate a home area\n"
-               "  create USER                 Create a home area\n"
-               "  remove USER…                Remove a home area\n"
-               "  update USER                 Update a home area\n"
-               "  passwd USER                 Change password of a home area\n"
-               "  resize USER SIZE            Resize a home area\n"
-               "  lock USER…                  Temporarily lock an active home area\n"
-               "  unlock USER…                Unlock a temporarily locked home area\n"
-               "  lock-all                    Lock all suitable home areas\n"
-               "  deactivate-all              Deactivate all active home areas\n"
-               "  with USER [COMMAND…]        Run shell or command with access to a home area\n"
+               "  list                         List home areas\n"
+               "  activate USER…               Activate a home area\n"
+               "  deactivate USER…             Deactivate a home area\n"
+               "  inspect USER…                Inspect a home area\n"
+               "  authenticate USER…           Authenticate a home area\n"
+               "  create USER                  Create a home area\n"
+               "  remove USER…                 Remove a home area\n"
+               "  update USER                  Update a home area\n"
+               "  passwd USER                  Change password of a home area\n"
+               "  resize USER SIZE             Resize a home area\n"
+               "  lock USER…                   Temporarily lock an active home area\n"
+               "  unlock USER…                 Unlock a temporarily locked home area\n"
+               "  lock-all                     Lock all suitable home areas\n"
+               "  deactivate-all               Deactivate all active home areas\n"
+               "  with USER [COMMAND…]         Run shell or command with access to a home area\n"
                "\n%4$sOptions:%5$s\n"
-               "  -h --help                   Show this help\n"
-               "     --version                Show package version\n"
-               "     --no-pager               Do not pipe output into a pager\n"
-               "     --no-legend              Do not show the headers and footers\n"
-               "     --no-ask-password        Do not ask for system passwords\n"
-               "  -H --host=[USER@]HOST       Operate on remote host\n"
-               "  -M --machine=CONTAINER      Operate on local container\n"
-               "     --identity=PATH          Read JSON identity from file\n"
-               "     --json=FORMAT            Output inspection data in JSON (takes one of\n"
-               "                              pretty, short, off)\n"
-               "  -j                          Equivalent to --json=pretty (on TTY) or\n"
-               "                              --json=short (otherwise)\n"
-               "     --export-format=         Strip JSON inspection data (full, stripped,\n"
-               "                              minimal)\n"
-               "  -E                          When specified once equals -j --export-format=\n"
-               "                              stripped, when specified twice equals\n"
-               "                              -j --export-format=minimal\n"
+               "  -h --help                    Show this help\n"
+               "     --version                 Show package version\n"
+               "     --no-pager                Do not pipe output into a pager\n"
+               "     --no-legend               Do not show the headers and footers\n"
+               "     --no-ask-password         Do not ask for system passwords\n"
+               "  -H --host=[USER@]HOST        Operate on remote host\n"
+               "  -M --machine=CONTAINER       Operate on local container\n"
+               "     --identity=PATH           Read JSON identity from file\n"
+               "     --json=FORMAT             Output inspection data in JSON (takes one of\n"
+               "                               pretty, short, off)\n"
+               "  -j                           Equivalent to --json=pretty (on TTY) or\n"
+               "                               --json=short (otherwise)\n"
+               "     --export-format=          Strip JSON inspection data (full, stripped,\n"
+               "                               minimal)\n"
+               "  -E                           When specified once equals -j --export-format=\n"
+               "                               stripped, when specified twice equals\n"
+               "                               -j --export-format=minimal\n"
                "\n%4$sGeneral User Record Properties:%5$s\n"
-               "  -c --real-name=REALNAME     Real name for user\n"
-               "     --realm=REALM            Realm to create user in\n"
-               "     --email-address=EMAIL    Email address for user\n"
-               "     --location=LOCATION      Set location of user on earth\n"
-               "     --icon-name=NAME         Icon name for user\n"
-               "  -d --home-dir=PATH          Home directory\n"
-               "  -u --uid=UID                Numeric UID for user\n"
-               "  -G --member-of=GROUP        Add user to group\n"
-               "     --skel=PATH              Skeleton directory to use\n"
-               "     --shell=PATH             Shell for account\n"
-               "     --setenv=VARIABLE=VALUE  Set an environment variable at log-in\n"
-               "     --timezone=TIMEZONE      Set a time-zone\n"
-               "     --language=LOCALE        Set preferred language\n"
+               "  -c --real-name=REALNAME      Real name for user\n"
+               "     --realm=REALM             Realm to create user in\n"
+               "     --email-address=EMAIL     Email address for user\n"
+               "     --location=LOCATION       Set location of user on earth\n"
+               "     --icon-name=NAME          Icon name for user\n"
+               "  -d --home-dir=PATH           Home directory\n"
+               "  -u --uid=UID                 Numeric UID for user\n"
+               "  -G --member-of=GROUP         Add user to group\n"
+               "     --skel=PATH               Skeleton directory to use\n"
+               "     --shell=PATH              Shell for account\n"
+               "     --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n"
+               "     --timezone=TIMEZONE       Set a time-zone\n"
+               "     --language=LOCALE         Set preferred language\n"
                "     --ssh-authorized-keys=KEYS\n"
-               "                              Specify SSH public keys\n"
-               "     --pkcs11-token-uri=URI   URI to PKCS#11 security token containing\n"
-               "                              private key and matching X.509 certificate\n"
-               "     --fido2-device=PATH      Path to FIDO2 hidraw device with hmac-secret\n"
-               "                              extension\n"
+               "                               Specify SSH public keys\n"
+               "     --pkcs11-token-uri=URI    URI to PKCS#11 security token containing\n"
+               "                               private key and matching X.509 certificate\n"
+               "     --fido2-device=PATH       Path to FIDO2 hidraw device with hmac-secret\n"
+               "                               extension\n"
                "     --fido2-with-client-pin=BOOL\n"
-               "                              Whether to require entering a PIN to unlock the\n"
-               "                              account\n"
+               "                               Whether to require entering a PIN to unlock the\n"
+               "                               account\n"
                "     --fido2-with-user-presence=BOOL\n"
-               "                              Whether to require user presence to unlock the\n"
-               "                              account\n"
+               "                               Whether to require user presence to unlock the\n"
+               "                               account\n"
                "     --fido2-with-user-verification=BOOL\n"
-               "                              Whether to require user verification to unlock the\n"
-               "                              account\n"
-               "     --recovery-key=BOOL      Add a recovery key\n"
-               "\n%4$sAccount Management User Record Properties:%5$s\n"
-               "     --locked=BOOL            Set locked account state\n"
-               "     --not-before=TIMESTAMP   Do not allow logins before\n"
-               "     --not-after=TIMESTAMP    Do not allow logins after\n"
+               "                               Whether to require user verification to unlock\n"
+               "                               the account\n"
+               "     --recovery-key=BOOL       Add a recovery key\n"
+               "\n%4$sAccount Management User  Record Properties:%5$s\n"
+               "     --locked=BOOL             Set locked account state\n"
+               "     --not-before=TIMESTAMP    Do not allow logins before\n"
+               "     --not-after=TIMESTAMP     Do not allow logins after\n"
                "     --rate-limit-interval=SECS\n"
-               "                              Login rate-limit interval in seconds\n"
+               "                               Login rate-limit interval in seconds\n"
                "     --rate-limit-burst=NUMBER\n"
-               "                              Login rate-limit attempts per interval\n"
+               "                               Login rate-limit attempts per interval\n"
                "\n%4$sPassword Policy User Record Properties:%5$s\n"
-               "     --password-hint=HINT     Set Password hint\n"
+               "     --password-hint=HINT      Set Password hint\n"
                "     --enforce-password-policy=BOOL\n"
-               "                              Control whether to enforce system's password\n"
-               "                              policy for this user\n"
-               "  -P                          Equivalent to --enforce-password-password=no\n"
+               "                               Control whether to enforce system's password\n"
+               "                               policy for this user\n"
+               "  -P                           Same as --enforce-password-password=no\n"
                "     --password-change-now=BOOL\n"
-               "                              Require the password to be changed on next login\n"
+               "                               Require the password to be changed on next login\n"
                "     --password-change-min=TIME\n"
-               "                              Require minimum time between password changes\n"
+               "                               Require minimum time between password changes\n"
                "     --password-change-max=TIME\n"
-               "                              Require maximum time between password changes\n"
+               "                               Require maximum time between password changes\n"
                "     --password-change-warn=TIME\n"
-               "                              How much time to warn before password expiry\n"
+               "                               How much time to warn before password expiry\n"
                "     --password-change-inactive=TIME\n"
-               "                              How much time to block password after expiry\n"
+               "                               How much time to block password after expiry\n"
                "\n%4$sResource Management User Record Properties:%5$s\n"
-               "     --disk-size=BYTES        Size to assign the user on disk\n"
-               "     --access-mode=MODE       User home directory access mode\n"
-               "     --umask=MODE             Umask for user when logging in\n"
-               "     --nice=NICE              Nice level for user\n"
+               "     --disk-size=BYTES         Size to assign the user on disk\n"
+               "     --access-mode=MODE        User home directory access mode\n"
+               "     --umask=MODE              Umask for user when logging in\n"
+               "     --nice=NICE               Nice level for user\n"
                "     --rlimit=LIMIT=VALUE[:VALUE]\n"
-               "                              Set resource limits\n"
-               "     --tasks-max=MAX          Set maximum number of per-user tasks\n"
-               "     --memory-high=BYTES      Set high memory threshold in bytes\n"
-               "     --memory-max=BYTES       Set maximum memory limit\n"
-               "     --cpu-weight=WEIGHT      Set CPU weight\n"
-               "     --io-weight=WEIGHT       Set IO weight\n"
+               "                               Set resource limits\n"
+               "     --tasks-max=MAX           Set maximum number of per-user tasks\n"
+               "     --memory-high=BYTES       Set high memory threshold in bytes\n"
+               "     --memory-max=BYTES        Set maximum memory limit\n"
+               "     --cpu-weight=WEIGHT       Set CPU weight\n"
+               "     --io-weight=WEIGHT        Set IO weight\n"
                "\n%4$sStorage User Record Properties:%5$s\n"
-               "     --storage=STORAGE        Storage type to use (luks, fscrypt, directory,\n"
-               "                              subvolume, cifs)\n"
-               "     --image-path=PATH        Path to image file/directory\n"
+               "     --storage=STORAGE         Storage type to use (luks, fscrypt, directory,\n"
+               "                               subvolume, cifs)\n"
+               "     --image-path=PATH         Path to image file/directory\n"
                "\n%4$sLUKS Storage User Record Properties:%5$s\n"
-               "     --fs-type=TYPE           File system type to use in case of luks\n"
-               "                              storage (btrfs, ext4, xfs)\n"
-               "     --luks-discard=BOOL      Whether to use 'discard' feature of file system\n"
-               "                              when activated (mounted)\n"
+               "     --fs-type=TYPE            File system type to use in case of luks\n"
+               "                               storage (btrfs, ext4, xfs)\n"
+               "     --luks-discard=BOOL       Whether to use 'discard' feature of file system\n"
+               "                               when activated (mounted)\n"
                "     --luks-offline-discard=BOOL\n"
-               "                              Whether to trim file on logout\n"
-               "     --luks-cipher=CIPHER     Cipher to use for LUKS encryption\n"
-               "     --luks-cipher-mode=MODE  Cipher mode to use for LUKS encryption\n"
+               "                               Whether to trim file on logout\n"
+               "     --luks-cipher=CIPHER      Cipher to use for LUKS encryption\n"
+               "     --luks-cipher-mode=MODE   Cipher mode to use for LUKS encryption\n"
                "     --luks-volume-key-size=BITS\n"
-               "                              Volume key size to use for LUKS encryption\n"
-               "     --luks-pbkdf-type=TYPE   Password-based Key Derivation Function to use\n"
+               "                               Volume key size to use for LUKS encryption\n"
+               "     --luks-pbkdf-type=TYPE    Password-based Key Derivation Function to use\n"
                "     --luks-pbkdf-hash-algorithm=ALGORITHM\n"
-               "                              PBKDF hash algorithm to use\n"
+               "                               PBKDF hash algorithm to use\n"
                "     --luks-pbkdf-time-cost=SECS\n"
-               "                              Time cost for PBKDF in seconds\n"
+               "                               Time cost for PBKDF in seconds\n"
                "     --luks-pbkdf-memory-cost=BYTES\n"
-               "                              Memory cost for PBKDF in bytes\n"
+               "                               Memory cost for PBKDF in bytes\n"
                "     --luks-pbkdf-parallel-threads=NUMBER\n"
-               "                              Number of parallel threads for PKBDF\n"
+               "                               Number of parallel threads for PKBDF\n"
                "\n%4$sMounting User Record Properties:%5$s\n"
-               "     --nosuid=BOOL            Control the 'nosuid' flag of the home mount\n"
-               "     --nodev=BOOL             Control the 'nodev' flag of the home mount\n"
-               "     --noexec=BOOL            Control the 'noexec' flag of the home mount\n"
+               "     --nosuid=BOOL             Control the 'nosuid' flag of the home mount\n"
+               "     --nodev=BOOL              Control the 'nodev' flag of the home mount\n"
+               "     --noexec=BOOL             Control the 'noexec' flag of the home mount\n"
                "\n%4$sCIFS User Record Properties:%5$s\n"
-               "     --cifs-domain=DOMAIN     CIFS (Windows) domain\n"
-               "     --cifs-user-name=USER    CIFS (Windows) user name\n"
-               "     --cifs-service=SERVICE   CIFS (Windows) service to mount as home area\n"
+               "     --cifs-domain=DOMAIN      CIFS (Windows) domain\n"
+               "     --cifs-user-name=USER     CIFS (Windows) user name\n"
+               "     --cifs-service=SERVICE    CIFS (Windows) service to mount as home area\n"
                "\n%4$sLogin Behaviour User Record Properties:%5$s\n"
-               "     --stop-delay=SECS        How long to leave user services running after\n"
-               "                              logout\n"
-               "     --kill-processes=BOOL    Whether to kill user processes when sessions\n"
-               "                              terminate\n"
-               "     --auto-login=BOOL        Try to log this user in automatically\n"
+               "     --stop-delay=SECS         How long to leave user services running after\n"
+               "                               logout\n"
+               "     --kill-processes=BOOL     Whether to kill user processes when sessions\n"
+               "                               terminate\n"
+               "     --auto-login=BOOL         Try to log this user in automatically\n"
                "\nSee the %6$s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -2673,10 +2667,6 @@ static int parse_argv(int argc, char *argv[]) {
                                 break;
                         }
 
-                        if (!env_assignment_is_valid(optarg))
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Environment assignment '%s' not valid.", optarg);
-
                         e = json_variant_by_key(arg_identity_extra, "environment");
                         if (e) {
                                 r = json_variant_strv(e, &l);
@@ -2684,9 +2674,9 @@ static int parse_argv(int argc, char *argv[]) {
                                         return log_error_errno(r, "Failed to parse JSON environment field: %m");
                         }
 
-                        r = strv_env_replace_strdup(&l, optarg);
+                        r = strv_env_replace_strdup_passthrough(&l, optarg);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to replace JSON environment field: %m");
+                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
 
                         strv_sort(l);
 
@@ -3463,7 +3453,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
         }
 
index ef11c72b74c213eb0e5a3140c1ccbe051be87036..bbdc6940f3da0f0db4e0fc3a871655233eb98d01 100644 (file)
@@ -957,7 +957,7 @@ static int home_on_worker_process(sd_event_source *s, const siginfo_t *si, void
                 break;
 
         default:
-                assert_not_reached("Unexpected state after worker exited");
+                assert_not_reached();
         }
 
         return 0;
@@ -2163,7 +2163,6 @@ int home_augment_status(
         uint64_t disk_size = UINT64_MAX, disk_usage = UINT64_MAX, disk_free = UINT64_MAX, disk_ceiling = UINT64_MAX, disk_floor = UINT64_MAX;
         _cleanup_(json_variant_unrefp) JsonVariant *j = NULL, *v = NULL, *m = NULL, *status = NULL;
         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
-        char ids[SD_ID128_STRING_MAX];
         HomeState state;
         sd_id128_t id;
         int r;
@@ -2227,7 +2226,7 @@ int home_augment_status(
 
         j = json_variant_ref(h->record->json);
         v = json_variant_ref(json_variant_by_key(j, "status"));
-        m = json_variant_ref(json_variant_by_key(v, sd_id128_to_string(id, ids)));
+        m = json_variant_ref(json_variant_by_key(v, SD_ID128_TO_STRING(id)));
 
         r = json_variant_filter(&m, STRV_MAKE("diskSize", "diskUsage", "diskFree", "diskCeiling", "diskFloor", "signedLocally"));
         if (r < 0)
@@ -2237,7 +2236,7 @@ int home_augment_status(
         if (r < 0)
                 return r;
 
-        r = json_variant_set_field(&v, ids, m);
+        r = json_variant_set_field(&v, SD_ID128_TO_STRING(id), m);
         if (r < 0)
                 return r;
 
index b25542638250ffdc32eeae062808b03db0f9cf88..6549e6bd4ef24b46988729e74a34927b6b3ee38d 100644 (file)
@@ -597,7 +597,7 @@ static int manager_acquire_uid(
                         break;
 
                 default:
-                        assert_not_reached("unknown phase");
+                        assert_not_reached();
                 }
 
                 other = hashmap_get(m->homes_by_uid, UID_TO_PTR(candidate));
index b35d24c85a09e012b8c2a0dbdb08885d77ef6451..45a2fb9db78b72467b26f84b845b7b799033a34d 100644 (file)
@@ -141,7 +141,7 @@ int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {
                 break;
 
         default:
-                assert_not_reached("unexpected storage");
+                assert_not_reached();
         }
 
         temporary = TAKE_PTR(d); /* Needs to be destroyed now */
index 3d4361c4ecfa705e33f28aca4dce694cdd4c7f9b..a2d7d388e09ec98e49fcea7a24c092e10781be69 100644 (file)
@@ -1487,10 +1487,10 @@ static int luks_format(
         _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_(erase_and_freep) void *volume_key = NULL;
         struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
-        char suuid[ID128_UUID_STRING_MAX], **pp;
         _cleanup_free_ char *text = NULL;
         size_t volume_key_size;
         int slot = 0, r;
+        char **pp;
 
         assert(node);
         assert(dm_name);
@@ -1526,19 +1526,20 @@ static int luks_format(
         build_good_pbkdf(&good_pbkdf, hr);
         build_minimal_pbkdf(&minimal_pbkdf, hr);
 
-        r = sym_crypt_format(cd,
-                         CRYPT_LUKS2,
-                         user_record_luks_cipher(hr),
-                         user_record_luks_cipher_mode(hr),
-                         id128_to_uuid_string(uuid, suuid),
-                         volume_key,
-                         volume_key_size,
-                         &(struct crypt_params_luks2) {
-                                 .label = label,
-                                 .subsystem = "systemd-home",
-                                 .sector_size = 512U,
-                                 .pbkdf = &good_pbkdf,
-                         });
+        r = sym_crypt_format(
+                        cd,
+                        CRYPT_LUKS2,
+                        user_record_luks_cipher(hr),
+                        user_record_luks_cipher_mode(hr),
+                        ID128_TO_UUID_STRING(uuid),
+                        volume_key,
+                        volume_key_size,
+                        &(struct crypt_params_luks2) {
+                                .label = label,
+                                .subsystem = "systemd-home",
+                                .sector_size = 512U,
+                                .pbkdf = &good_pbkdf,
+                        });
         if (r < 0)
                 return log_error_errno(r, "Failed to format LUKS image: %m");
 
@@ -1621,7 +1622,6 @@ static int make_partition_table(
         _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
         uint64_t offset, size;
         sd_id128_t disk_uuid;
-        char uuids[ID128_UUID_STRING_MAX];
         int r;
 
         assert(fd >= 0);
@@ -1676,7 +1676,7 @@ static int make_partition_table(
         if (r < 0)
                 return log_error_errno(r, "Failed to set partition name: %m");
 
-        r = fdisk_partition_set_uuid(p, id128_to_uuid_string(uuid, uuids));
+        r = fdisk_partition_set_uuid(p, ID128_TO_UUID_STRING(uuid));
         if (r < 0)
                 return log_error_errno(r, "Failed to set partition UUID: %m");
 
index bdd9ac649e53a7bdc6da5edffa5752b7460b0ef3..ee1d4068bad92779eadd5ee3ade372b66da5eb96 100644 (file)
@@ -791,7 +791,7 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) {
                 break;
 
         default:
-                assert_not_reached("unexpected type");
+                assert_not_reached();
         }
 
         /* Note that the returned object might either be a reference to an updated version of the existing
@@ -1305,7 +1305,7 @@ static int home_remove(UserRecord *h) {
                 break;
 
         default:
-                assert_not_reached("unknown storage type");
+                assert_not_reached();
         }
 
         if (hd) {
@@ -1370,7 +1370,7 @@ static int home_validate_update(UserRecord *h, HomeSetup *setup) {
         }
 
         default:
-                assert_not_reached("unexpected storage type");
+                assert_not_reached();
         }
 
         return has_mount; /* return true if the home record is already active */
index b5ed6b1689500c53c16376bb55cfe6571813dafe..bc0c4171b4f38265a1bf1858e65d19806271d0e1 100644 (file)
@@ -24,7 +24,6 @@ int user_record_synthesize(
                 gid_t gid) {
 
         _cleanup_free_ char *hd = NULL, *un = NULL, *ip = NULL, *rr = NULL, *user_name_and_realm = NULL;
-        char smid[SD_ID128_STRING_MAX];
         sd_id128_t mid;
         int r;
 
@@ -86,7 +85,7 @@ int user_record_synthesize(
                                        JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(realm)),
                                        JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("regular")),
                                        JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
-                                                                       JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+                                                                       JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
                                                                                                        JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path)),
                                                                                                        JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING(hd)),
                                                                                                        JSON_BUILD_PAIR("storage", JSON_BUILD_STRING(user_storage_to_string(storage))),
@@ -109,7 +108,6 @@ int user_record_synthesize(
 
 int group_record_synthesize(GroupRecord *g, UserRecord *h) {
         _cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL, *description = NULL;
-        char smid[SD_ID128_STRING_MAX];
         sd_id128_t mid;
         int r;
 
@@ -147,11 +145,11 @@ int group_record_synthesize(GroupRecord *g, UserRecord *h) {
                                        JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(rr)),
                                        JSON_BUILD_PAIR("description", JSON_BUILD_STRING(description)),
                                        JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
-                                                                       JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+                                                                       JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
                                                                                                        JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h))))))),
                                        JSON_BUILD_PAIR_CONDITION(h->disposition >= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h)))),
                                        JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
-                                                                       JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
+                                                                       JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
                                                                                                        JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
         if (r < 0)
                 return r;
@@ -284,7 +282,6 @@ int user_record_add_binding(
                 gid_t gid) {
 
         _cleanup_(json_variant_unrefp) JsonVariant *new_binding_entry = NULL, *binding = NULL;
-        char smid[SD_ID128_STRING_MAX], partition_uuids[ID128_UUID_STRING_MAX], luks_uuids[ID128_UUID_STRING_MAX], fs_uuids[ID128_UUID_STRING_MAX];
         _cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
         sd_id128_t mid;
         int r;
@@ -297,7 +294,6 @@ int user_record_add_binding(
         r = sd_id128_get_machine(&mid);
         if (r < 0)
                 return r;
-        sd_id128_to_string(mid, smid);
 
         if (image_path) {
                 ip = strdup(image_path);
@@ -336,9 +332,9 @@ int user_record_add_binding(
         r = json_build(&new_binding_entry,
                        JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
-                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(id128_to_uuid_string(partition_uuid, partition_uuids))),
-                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(id128_to_uuid_string(luks_uuid, luks_uuids))),
-                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(id128_to_uuid_string(fs_uuid, fs_uuids))),
+                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(partition_uuid))),
+                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(luks_uuid))),
+                                       JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(fs_uuid))),
                                        JSON_BUILD_PAIR_CONDITION(!!luks_cipher, "luksCipher", JSON_BUILD_STRING(luks_cipher)),
                                        JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode)),
                                        JSON_BUILD_PAIR_CONDITION(luks_volume_key_size != UINT64_MAX, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size)),
@@ -355,7 +351,7 @@ int user_record_add_binding(
                 _cleanup_(json_variant_unrefp) JsonVariant *be = NULL;
 
                 /* Merge the new entry with an old one, if that exists */
-                be = json_variant_ref(json_variant_by_key(binding, smid));
+                be = json_variant_ref(json_variant_by_key(binding, SD_ID128_TO_STRING(mid)));
                 if (be) {
                         r = json_variant_merge(&be, new_binding_entry);
                         if (r < 0)
@@ -366,7 +362,7 @@ int user_record_add_binding(
                 }
         }
 
-        r = json_variant_set_field(&binding, smid, new_binding_entry);
+        r = json_variant_set_field(&binding, SD_ID128_TO_STRING(mid), new_binding_entry);
         if (r < 0)
                 return r;
 
@@ -544,7 +540,7 @@ int user_record_test_image_path(UserRecord *h) {
                 return -ENOTDIR;
 
         default:
-                assert_not_reached("Unexpected record type");
+                assert_not_reached();
         }
 }
 
@@ -633,7 +629,6 @@ int user_record_test_recovery_key(UserRecord *h, UserRecord *secret) {
 int user_record_set_disk_size(UserRecord *h, uint64_t disk_size) {
         _cleanup_(json_variant_unrefp) JsonVariant *new_per_machine = NULL, *midv = NULL, *midav = NULL, *ne = NULL;
         _cleanup_free_ JsonVariant **array = NULL;
-        char smid[SD_ID128_STRING_MAX];
         size_t idx = SIZE_MAX, n;
         JsonVariant *per_machine;
         sd_id128_t mid;
@@ -651,9 +646,7 @@ int user_record_set_disk_size(UserRecord *h, uint64_t disk_size) {
         if (r < 0)
                 return r;
 
-        sd_id128_to_string(mid, smid);
-
-        r = json_variant_new_string(&midv, smid);
+        r = json_variant_new_string(&midv, SD_ID128_TO_STRING(mid));
         if (r < 0)
                 return r;
 
@@ -1208,7 +1201,6 @@ int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
 
 int user_record_good_authentication(UserRecord *h) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
-        char buf[SD_ID128_STRING_MAX];
         uint64_t counter, usec;
         sd_id128_t mid;
         int r;
@@ -1235,7 +1227,7 @@ int user_record_good_authentication(UserRecord *h) {
 
         v = json_variant_ref(h->json);
         w = json_variant_ref(json_variant_by_key(v, "status"));
-        z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+        z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
 
         r = json_variant_set_field_unsigned(&z, "goodAuthenticationCounter", counter);
         if (r < 0)
@@ -1245,7 +1237,7 @@ int user_record_good_authentication(UserRecord *h) {
         if (r < 0)
                 return r;
 
-        r = json_variant_set_field(&w, buf, z);
+        r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
         if (r < 0)
                 return r;
 
@@ -1265,7 +1257,6 @@ int user_record_good_authentication(UserRecord *h) {
 
 int user_record_bad_authentication(UserRecord *h) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
-        char buf[SD_ID128_STRING_MAX];
         uint64_t counter, usec;
         sd_id128_t mid;
         int r;
@@ -1292,7 +1283,7 @@ int user_record_bad_authentication(UserRecord *h) {
 
         v = json_variant_ref(h->json);
         w = json_variant_ref(json_variant_by_key(v, "status"));
-        z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+        z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
 
         r = json_variant_set_field_unsigned(&z, "badAuthenticationCounter", counter);
         if (r < 0)
@@ -1302,7 +1293,7 @@ int user_record_bad_authentication(UserRecord *h) {
         if (r < 0)
                 return r;
 
-        r = json_variant_set_field(&w, buf, z);
+        r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
         if (r < 0)
                 return r;
 
@@ -1323,7 +1314,6 @@ int user_record_bad_authentication(UserRecord *h) {
 int user_record_ratelimit(UserRecord *h) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
         usec_t usec, new_ratelimit_begin_usec, new_ratelimit_count;
-        char buf[SD_ID128_STRING_MAX];
         sd_id128_t mid;
         int r;
 
@@ -1355,7 +1345,7 @@ int user_record_ratelimit(UserRecord *h) {
 
         v = json_variant_ref(h->json);
         w = json_variant_ref(json_variant_by_key(v, "status"));
-        z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
+        z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
 
         r = json_variant_set_field_unsigned(&z, "rateLimitBeginUSec", new_ratelimit_begin_usec);
         if (r < 0)
@@ -1365,7 +1355,7 @@ int user_record_ratelimit(UserRecord *h) {
         if (r < 0)
                 return r;
 
-        r = json_variant_set_field(&w, buf, z);
+        r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
         if (r < 0)
                 return r;
 
index 283038c7cbaf55e5dc3d96122400df543803588c..bc780701314e13f3b4b298c1dd1208013d679aea 100644 (file)
@@ -620,7 +620,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index 36702f2fb0cd22ffbff8443a6ddd3c069c7d34e8..2557d6d8071033def8281df883269b1964581054 100644 (file)
@@ -575,8 +575,7 @@ static int property_get_default_hostname(
 }
 
 static void context_determine_hostname_source(Context *c) {
-        char hostname[HOST_NAME_MAX + 1] = {};
-        _cleanup_free_ char *fallback = NULL;
+        _cleanup_free_ char *hostname = NULL;
         int r;
 
         assert(c);
@@ -584,11 +583,13 @@ static void context_determine_hostname_source(Context *c) {
         if (c->hostname_source >= 0)
                 return;
 
-        (void) get_hostname_filtered(hostname);
+        (void) gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &hostname);
 
         if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME]))
                 c->hostname_source = HOSTNAME_STATIC;
         else {
+                _cleanup_free_ char *fallback = NULL;
+
                 /* If the hostname was not set by us, try to figure out where it came from. If we set it to
                  * the default hostname, the file will tell us. We compare the string because it is possible
                  * that the hostname was set by an older version that had a different fallback, in the
index 26cc83f31bd57249bf5cc3bf7d63f64195e6e270..e2144df62cb9aa504b9c0d5f52b1adbe49c6d1c8 100644 (file)
@@ -98,7 +98,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         return 1;
index 435a56d4f34de47a5bcdf139ff29cec20d984923..89b944f8c037afb6ef605082c44553ce5ae62e34 100644 (file)
@@ -226,7 +226,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index ed2ac0a654a69c3a832ee15bf34a5e775f63b9c0..d6a16b4f57cf8c38962c766ed73bcea672cd1023 100644 (file)
@@ -256,6 +256,9 @@ int curl_glue_make(CURL **ret, const char *url, void *userdata) {
         if (curl_easy_setopt(c, CURLOPT_LOW_SPEED_LIMIT, 30L) != CURLE_OK)
                 return -EIO;
 
+        if (curl_easy_setopt(c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_FILE) != CURLE_OK)
+                return -EIO;
+
         *ret = TAKE_PTR(c);
         return 0;
 }
index d42c56172fdde8d9a12645bc37c08401f36900a3..6617a9c9b673530c02f42869db224f9a6a241527 100644 (file)
@@ -223,7 +223,7 @@ static int raw_export_process(RawExport *e) {
 finish:
         if (r >= 0) {
                 (void) copy_times(e->input_fd, e->output_fd, COPY_CRTIME);
-                (void) copy_xattr(e->input_fd, e->output_fd);
+                (void) copy_xattr(e->input_fd, e->output_fd, 0);
         }
 
         if (e->on_finished)
index a4f3f6c38e073402ab28dfe622d0cbccc347cbd2..26533baeb89a11e5a7e42c4823c06ed3b568112c 100644 (file)
@@ -261,7 +261,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index f77564c41dc53f8fa8ca5cf3270083f42a37639c..247f49a855aec19b4b939d83e1ff0394a85582a4 100644 (file)
@@ -13,6 +13,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "hostname-util.h"
 #include "import-common.h"
 #include "os-util.h"
 #include "process-util.h"
 #include "tmpfile-util.h"
 #include "util.h"
 
-int import_make_read_only_fd(int fd) {
-        struct stat st;
-        int r;
-
-        assert(fd >= 0);
-
-        /* First, let's make this a read-only subvolume if it refers
-         * to a subvolume */
-        r = btrfs_subvol_set_read_only_fd(fd, true);
-        if (r >= 0)
-                return 0;
-
-        if (!ERRNO_IS_NOT_SUPPORTED(r) && !IN_SET(r, -ENOTDIR, -EINVAL))
-                return log_error_errno(r, "Failed to make subvolume read-only: %m");
-
-        /* This doesn't refer to a subvolume, or the file system isn't even btrfs. In that, case fall back to
-         * chmod()ing */
-
-        r = fstat(fd, &st);
-        if (r < 0)
-                return log_error_errno(errno, "Failed to stat image: %m");
-
-        if (S_ISDIR(st.st_mode)) {
-                /* For directories set the immutable flag on the dir itself */
-
-                r = chattr_fd(fd, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to set +i attribute on directory image: %m");
-
-        } else if (S_ISREG(st.st_mode)) {
-                /* For regular files drop "w" flags */
-
-                if ((st.st_mode & 0222) != 0)
-                        if (fchmod(fd, st.st_mode & 07555) < 0)
-                                return log_error_errno(errno, "Failed to chmod() image: %m");
-        } else
-                return log_error_errno(SYNTHETIC_ERRNO(EBADFD), "Image of unexpected type");
-
-        return 0;
-}
-
-int import_make_read_only(const char *path) {
-        _cleanup_close_ int fd = 1;
-
-        fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
-        if (fd < 0)
-                return log_error_errno(errno, "Failed to open %s: %m", path);
-
-        return import_make_read_only_fd(fd);
-}
-
 int import_fork_tar_x(const char *path, pid_t *ret) {
         _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
         bool use_selinux;
@@ -327,3 +277,38 @@ int import_mangle_os_tree(const char *path) {
 
         return 0;
 }
+
+bool import_validate_local(const char *name, ImportFlags flags) {
+
+        /* By default we insist on a valid hostname for naming images. But optionally we relax that, in which
+         * case it can be any path name */
+
+        if (FLAGS_SET(flags, IMPORT_DIRECT))
+                return path_is_valid(name);
+
+        return hostname_is_valid(name, 0);
+}
+
+static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+        log_notice("Transfer aborted.");
+        sd_event_exit(sd_event_source_get_event(s), EINTR);
+        return 0;
+}
+
+int import_allocate_event_with_signals(sd_event **ret) {
+        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+        int r;
+
+        assert(ret);
+
+        r = sd_event_default(&event);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate event loop: %m");
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
+        (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler,  NULL);
+        (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+
+        *ret = TAKE_PTR(event);
+        return 0;
+}
index d7e8fc485f31e87f0776afa50b3698d5c76044d1..97fc16d1b9d32c2bf61e9602a155af0fa93c11f2 100644 (file)
@@ -3,17 +3,26 @@
 
 #include <sys/types.h>
 
+#include "sd-event.h"
+
 typedef enum ImportFlags {
-        IMPORT_FORCE     = 1 << 0, /* replace existing image */
-        IMPORT_READ_ONLY = 1 << 1, /* make generated image read-only */
+        IMPORT_FORCE          = 1 << 0, /* replace existing image */
+        IMPORT_READ_ONLY      = 1 << 1, /* make generated image read-only */
+        IMPORT_BTRFS_SUBVOL   = 1 << 2, /* tar: preferably create images as btrfs subvols */
+        IMPORT_BTRFS_QUOTA    = 1 << 3, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
+        IMPORT_CONVERT_QCOW2  = 1 << 4, /* raw: if we detect a qcow2 image, unpack it */
+        IMPORT_DIRECT         = 1 << 5, /* import without rename games */
+        IMPORT_SYNC           = 1 << 6, /* fsync() right before we are done */
 
-        IMPORT_FLAGS_MASK = IMPORT_FORCE|IMPORT_READ_ONLY,
+        IMPORT_FLAGS_MASK_TAR = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_BTRFS_SUBVOL|IMPORT_BTRFS_QUOTA|IMPORT_DIRECT|IMPORT_SYNC,
+        IMPORT_FLAGS_MASK_RAW = IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_CONVERT_QCOW2|IMPORT_DIRECT|IMPORT_SYNC,
 } ImportFlags;
 
-int import_make_read_only_fd(int fd);
-int import_make_read_only(const char *path);
-
 int import_fork_tar_c(const char *path, pid_t *ret);
 int import_fork_tar_x(const char *path, pid_t *ret);
 
 int import_mangle_os_tree(const char *path);
+
+bool import_validate_local(const char *name, ImportFlags flags);
+
+int import_allocate_event_with_signals(sd_event **ret);
index aa837af5659b61d0b29a47b4eb43454627ccfbf4..03f5efa00ec6ee4e789806de547d03f4572303e0 100644 (file)
@@ -190,7 +190,7 @@ int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCo
 #endif
 
         default:
-                assert_not_reached("Unknown compression");
+                assert_not_reached();
         }
 
         return 1;
index e8c3740879b1f8016a63abf989d9ca19a3919386..cacd48fe96e83be86e1b8738a15a58a86c177d30 100644 (file)
 #include "hostname-util.h"
 #include "import-common.h"
 #include "import-util.h"
+#include "install-file.h"
+#include "main-func.h"
 #include "mkdir.h"
+#include "parse-argument.h"
 #include "ratelimit.h"
 #include "rm-rf.h"
+#include "signal-util.h"
 #include "string-util.h"
+#include "terminal-util.h"
 #include "tmpfile-util.h"
 #include "verbs.h"
 
 static bool arg_force = false;
 static bool arg_read_only = false;
+static bool arg_btrfs_subvol = true;
+static bool arg_btrfs_quota = true;
+static bool arg_sync = true;
+static bool arg_direct = false;
 static const char *arg_image_root = "/var/lib/machines";
 
 typedef struct ProgressInfo {
@@ -31,12 +40,6 @@ typedef struct ProgressInfo {
         bool logged_incomplete;
 } ProgressInfo;
 
-static volatile sig_atomic_t cancelled = false;
-
-static void sigterm_sigint(int sig) {
-        cancelled = true;
-}
-
 static void progress_info_free(ProgressInfo *p) {
         free(p->path);
 }
@@ -56,7 +59,7 @@ static void progress_show(ProgressInfo *p) {
 
         /* Mention the list is incomplete before showing first output. */
         if (!p->logged_incomplete) {
-                log_notice("(Note, file list shown below is incomplete, and is intended as sporadic progress report only.)");
+                log_notice("(Note: file list shown below is incomplete, and is intended as sporadic progress report only.)");
                 p->logged_incomplete = true;
         }
 
@@ -72,9 +75,6 @@ static int progress_path(const char *path, const struct stat *st, void *userdata
 
         assert(p);
 
-        if (cancelled)
-                return -EOWNERDEAD;
-
         r = free_and_strdup(&p->path, path);
         if (r < 0)
                 return r;
@@ -91,9 +91,6 @@ static int progress_bytes(uint64_t nbytes, void *userdata) {
         assert(p);
         assert(p->size != UINT64_MAX);
 
-        if (cancelled)
-                return -EOWNERDEAD;
-
         p->size += nbytes;
 
         progress_show(p);
@@ -103,44 +100,60 @@ static int progress_bytes(uint64_t nbytes, void *userdata) {
 static int import_fs(int argc, char *argv[], void *userdata) {
         _cleanup_(rm_rf_subvolume_and_freep) char *temp_path = NULL;
         _cleanup_(progress_info_free) ProgressInfo progress = {};
-        const char *path = NULL, *local = NULL, *final_path;
+        _cleanup_free_ char *l = NULL, *final_path = NULL;
+        const char *path = NULL, *local = NULL, *dest = NULL;
         _cleanup_close_ int open_fd = -1;
-        struct sigaction old_sigint_sa, old_sigterm_sa;
-        static const struct sigaction sa = {
-                .sa_handler = sigterm_sigint,
-                .sa_flags = SA_RESTART,
-        };
         int r, fd;
 
         if (argc >= 2)
-                path = argv[1];
-        path = empty_or_dash_to_null(path);
+                path = empty_or_dash_to_null(argv[1]);
 
         if (argc >= 3)
-                local = argv[2];
-        else if (path)
-                local = basename(path);
-        local = empty_or_dash_to_null(local);
+                local = empty_or_dash_to_null(argv[2]);
+        else if (path) {
+                r = path_extract_filename(path, &l);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
 
-        if (local) {
-                if (!hostname_is_valid(local, 0))
+                local = l;
+        }
+
+        if (arg_direct) {
+                if (!local)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No local path specified.");
+
+                if (path_is_absolute(local))
+                        final_path = strdup(local);
+                else
+                        final_path = path_join(arg_image_root, local);
+                if (!final_path)
+                        return log_oom();
+
+                if (!path_is_valid(final_path))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Local image name '%s' is not valid.",
-                                               local);
+                                               "Local path name '%s' is not valid.", final_path);
+        } else {
+                if (local) {
+                        if (!hostname_is_valid(local, 0))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Local image name '%s' is not valid.", local);
+                } else
+                        local = "imported";
+
+                final_path = path_join(arg_image_root, local);
+                if (!final_path)
+                        return log_oom();
 
                 if (!arg_force) {
                         r = image_find(IMAGE_MACHINE, local, NULL, NULL);
                         if (r < 0) {
                                 if (r != -ENOENT)
                                         return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
-                        } else {
+                        } else
                                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
-                                                       "Image '%s' already exists.",
-                                                       local);
-                        }
+                                                       "Image '%s' already exists.", local);
                 }
-        } else
-                local = "imported";
+        }
 
         if (path) {
                 open_fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
@@ -159,84 +172,108 @@ static int import_fs(int argc, char *argv[], void *userdata) {
                 log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
         }
 
-        final_path = prefix_roota(arg_image_root, local);
+        if (!arg_sync)
+                log_info("File system synchronization on completion is off.");
 
-        r = tempfn_random(final_path, NULL, &temp_path);
-        if (r < 0)
-                return log_oom();
+        if (arg_direct) {
+                if (arg_force)
+                        (void) rm_rf(final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
 
-        (void) mkdir_parents_label(temp_path, 0700);
-
-        progress.limit = (RateLimit) { 200*USEC_PER_MSEC, 1 };
+                dest = final_path;
+        } else {
+                r = tempfn_random(final_path, NULL, &temp_path);
+                if (r < 0)
+                        return log_oom();
 
-        /* Hook into SIGINT/SIGTERM, so that we can cancel things then */
-        assert_se(sigaction(SIGINT, &sa, &old_sigint_sa) >= 0);
-        assert_se(sigaction(SIGTERM, &sa, &old_sigterm_sa) >= 0);
-
-        r = btrfs_subvol_snapshot_fd_full(
-                        fd,
-                        temp_path,
-                        BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|BTRFS_SNAPSHOT_QUOTA,
-                        progress_path,
-                        progress_bytes,
-                        &progress);
-        if (r == -EOWNERDEAD) { /* SIGINT + SIGTERM cause this, see signal handler above */
-                log_error("Copy cancelled.");
-                goto finish;
-        }
-        if (r < 0) {
-                log_error_errno(r, "Failed to copy directory: %m");
-                goto finish;
+                dest = temp_path;
         }
 
-        r = import_mangle_os_tree(temp_path);
-        if (r < 0)
-                goto finish;
+        (void) mkdir_parents_label(dest, 0700);
 
-        (void) import_assign_pool_quota_and_warn(arg_image_root);
-        (void) import_assign_pool_quota_and_warn(temp_path);
+        progress.limit = (RateLimit) { 200*USEC_PER_MSEC, 1 };
 
-        if (arg_read_only) {
-                r = import_make_read_only(temp_path);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to make directory read-only: %m");
-                        goto finish;
-                }
+        {
+                BLOCK_SIGNALS(SIGINT, SIGTERM);
+
+                if (arg_btrfs_subvol)
+                        r = btrfs_subvol_snapshot_fd_full(
+                                        fd,
+                                        dest,
+                                        BTRFS_SNAPSHOT_FALLBACK_COPY|
+                                        BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
+                                        BTRFS_SNAPSHOT_RECURSIVE|
+                                        BTRFS_SNAPSHOT_SIGINT|
+                                        BTRFS_SNAPSHOT_SIGTERM,
+                                        progress_path,
+                                        progress_bytes,
+                                        &progress);
+                else
+                        r = copy_directory_fd_full(
+                                        fd,
+                                        dest,
+                                        COPY_REFLINK|
+                                        COPY_SAME_MOUNT|
+                                        COPY_HARDLINKS|
+                                        COPY_SIGINT|
+                                        COPY_SIGTERM|
+                                        (arg_direct ? COPY_MERGE_EMPTY : 0),
+                                        progress_path,
+                                        progress_bytes,
+                                        &progress);
+                if (r == -EINTR) /* SIGINT/SIGTERM hit */
+                        return log_error_errno(r, "Copy cancelled.");
+                if (r < 0)
+                        return log_error_errno(r, "Failed to copy directory: %m");
         }
 
-        if (arg_force)
-                (void) rm_rf(final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+        r = import_mangle_os_tree(dest);
+        if (r < 0)
+                return r;
 
-        r = rename_noreplace(AT_FDCWD, temp_path, AT_FDCWD, final_path);
-        if (r < 0) {
-                log_error_errno(r, "Failed to move image into place: %m");
-                goto finish;
+        if (arg_btrfs_quota) {
+                if (!arg_direct)
+                        (void) import_assign_pool_quota_and_warn(arg_image_root);
+                (void) import_assign_pool_quota_and_warn(dest);
         }
 
-        temp_path = mfree(temp_path);
-
-        log_info("Exiting.");
+        r = install_file(AT_FDCWD, dest,
+                         AT_FDCWD, arg_direct ? NULL : final_path, /* pass NULL as target in case of direct
+                                                                    * mode since file is already in place */
+                         (arg_force ? INSTALL_REPLACE : 0) |
+                         (arg_read_only ? INSTALL_READ_ONLY : 0) |
+                         (arg_sync ? INSTALL_SYNCFS : 0));
+        if (r < 0)
+                return log_error_errno(r, "Failed install directory as '%s': %m", final_path);
 
-finish:
-        /* Put old signal handlers into place */
-        assert_se(sigaction(SIGINT, &old_sigint_sa, NULL) >= 0);
-        assert_se(sigaction(SIGTERM, &old_sigterm_sa, NULL) >= 0);
+        temp_path = mfree(temp_path);
 
+        log_info("Directory '%s successfully installed. Exiting.", final_path);
         return 0;
 }
 
 static int help(int argc, char *argv[], void *userdata) {
 
-        printf("%s [OPTIONS...] {COMMAND} ...\n\n"
-               "Import container images from a file system.\n\n"
+        printf("%1$s [OPTIONS...] {COMMAND} ...\n"
+               "\n%4$sImport container images from a file system directories.%5$s\n"
+               "\n%2$sCommands:%3$s\n"
+               "  run DIRECTORY [NAME]        Import a directory\n"
+               "\n%2$sOptions:%3$s\n"
                "  -h --help                   Show this help\n"
                "     --version                Show package version\n"
                "     --force                  Force creation of image\n"
                "     --image-root=PATH        Image root directory\n"
-               "     --read-only              Create a read-only image\n\n"
-               "Commands:\n"
-               "  run DIRECTORY [NAME]             Import a directory\n",
-               program_invocation_short_name);
+               "     --read-only              Create a read-only image\n"
+               "     --direct                 Import directly to specified directory\n"
+               "     --btrfs-subvol=BOOL      Controls whether to create a btrfs subvolume\n"
+               "                              instead of a directory\n"
+               "     --btrfs-quota=BOOL       Controls whether to set up quota for btrfs\n"
+               "                              subvolume\n"
+               "     --sync=BOOL              Controls whether to sync() before completing\n",
+               program_invocation_short_name,
+               ansi_underline(),
+               ansi_normal(),
+               ansi_highlight(),
+               ansi_normal());
 
         return 0;
 }
@@ -248,6 +285,10 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_FORCE,
                 ARG_IMAGE_ROOT,
                 ARG_READ_ONLY,
+                ARG_DIRECT,
+                ARG_BTRFS_SUBVOL,
+                ARG_BTRFS_QUOTA,
+                ARG_SYNC,
         };
 
         static const struct option options[] = {
@@ -256,10 +297,14 @@ static int parse_argv(int argc, char *argv[]) {
                 { "force",           no_argument,       NULL, ARG_FORCE           },
                 { "image-root",      required_argument, NULL, ARG_IMAGE_ROOT      },
                 { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
+                { "direct",          no_argument,       NULL, ARG_DIRECT          },
+                { "btrfs-subvol",    required_argument, NULL, ARG_BTRFS_SUBVOL    },
+                { "btrfs-quota",     required_argument, NULL, ARG_BTRFS_QUOTA     },
+                { "sync",            required_argument, NULL, ARG_SYNC            },
                 {}
         };
 
-        int c;
+        int c, r;
 
         assert(argc >= 0);
         assert(argv);
@@ -286,11 +331,36 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_read_only = true;
                         break;
 
+                case ARG_DIRECT:
+                        arg_direct = true;
+                        break;
+
+                case ARG_BTRFS_SUBVOL:
+                        r = parse_boolean_argument("--btrfs-subvol=", optarg, &arg_btrfs_subvol);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case ARG_BTRFS_QUOTA:
+                        r = parse_boolean_argument("--btrfs-quota=", optarg, &arg_btrfs_quota);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case ARG_SYNC:
+                        r = parse_boolean_argument("--sync=", optarg, &arg_sync);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
@@ -307,7 +377,7 @@ static int import_fs_main(int argc, char *argv[]) {
         return dispatch_verb(argc, argv, verbs, NULL);
 }
 
-int main(int argc, char *argv[]) {
+static int run(int argc, char *argv[]) {
         int r;
 
         setlocale(LC_ALL, "");
@@ -316,10 +386,9 @@ int main(int argc, char *argv[]) {
 
         r = parse_argv(argc, argv);
         if (r <= 0)
-                goto finish;
-
-        r = import_fs_main(argc, argv);
+                return r;
 
-finish:
-        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+        return import_fs_main(argc, argv);
 }
+
+DEFINE_MAIN_FUNCTION(run);
index 0e7757b6f0c114e1cf95d79876086510f1d70cc3..153b74d123fa5596b443bb4305c8e81eaa774a79 100644 (file)
@@ -9,11 +9,13 @@
 #include "btrfs-util.h"
 #include "copy.h"
 #include "fd-util.h"
+#include "format-util.h"
 #include "fs-util.h"
 #include "hostname-util.h"
 #include "import-common.h"
 #include "import-compress.h"
 #include "import-raw.h"
+#include "install-file.h"
 #include "io-util.h"
 #include "machine-pool.h"
 #include "mkdir.h"
@@ -52,23 +54,27 @@ struct RawImport {
         uint64_t written_compressed;
         uint64_t written_uncompressed;
 
-        struct stat st;
+        struct stat input_stat;
+        struct stat output_stat;
 
         unsigned last_percent;
         RateLimit progress_ratelimit;
+
+        uint64_t offset;
+        uint64_t size_max;
 };
 
 RawImport* raw_import_unref(RawImport *i) {
         if (!i)
                 return NULL;
 
-        sd_event_unref(i->event);
+        sd_event_source_unref(i->input_event_source);
 
         unlink_and_free(i->temp_path);
 
         import_compress_free(&i->compress);
 
-        sd_event_source_unref(i->input_event_source);
+        sd_event_unref(i->event);
 
         safe_close(i->output_fd);
 
@@ -107,6 +113,8 @@ int raw_import_new(
                 .last_percent = UINT_MAX,
                 .image_root = TAKE_PTR(root),
                 .progress_ratelimit = { 100 * USEC_PER_MSEC, 1 },
+                .offset = UINT64_MAX,
+                .size_max = UINT64_MAX,
         };
 
         if (event)
@@ -118,7 +126,6 @@ int raw_import_new(
         }
 
         *ret = TAKE_PTR(i);
-
         return 0;
 }
 
@@ -127,13 +134,13 @@ static void raw_import_report_progress(RawImport *i) {
         assert(i);
 
         /* We have no size information, unless the source is a regular file */
-        if (!S_ISREG(i->st.st_mode))
+        if (!S_ISREG(i->input_stat.st_mode))
                 return;
 
-        if (i->written_compressed >= (uint64_t) i->st.st_size)
+        if (i->written_compressed >= (uint64_t) i->input_stat.st_size)
                 percent = 100;
         else
-                percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
+                percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->input_stat.st_size);
 
         if (percent == i->last_percent)
                 return;
@@ -149,11 +156,18 @@ static void raw_import_report_progress(RawImport *i) {
 
 static int raw_import_maybe_convert_qcow2(RawImport *i) {
         _cleanup_close_ int converted_fd = -1;
-        _cleanup_free_ char *t = NULL;
+        _cleanup_(unlink_and_freep) char *t = NULL;
+        _cleanup_free_ char *f = NULL;
         int r;
 
         assert(i);
 
+        /* Do QCOW2 conversion if enabled and not in direct mode */
+        if ((i->flags & (IMPORT_CONVERT_QCOW2|IMPORT_DIRECT)) != IMPORT_CONVERT_QCOW2)
+                return 0;
+
+        assert(i->final_path);
+
         r = qcow2_detect(i->output_fd);
         if (r < 0)
                 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
@@ -161,26 +175,26 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) {
                 return 0;
 
         /* This is a QCOW2 image, let's convert it */
-        r = tempfn_random(i->final_path, NULL, &t);
+        r = tempfn_random(i->final_path, NULL, &f);
         if (r < 0)
                 return log_oom();
 
-        converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+        converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
         if (converted_fd < 0)
-                return log_error_errno(errno, "Failed to create %s: %m", t);
+                return log_error_errno(errno, "Failed to create %s: %m", f);
+
+        t = TAKE_PTR(f);
 
         (void) import_set_nocow_and_log(converted_fd, t);
 
         log_info("Unpacking QCOW2 file.");
 
         r = qcow2_convert(i->output_fd, converted_fd);
-        if (r < 0) {
-                (void) unlink(t);
+        if (r < 0)
                 return log_error_errno(r, "Failed to convert qcow2 image: %m");
-        }
 
-        (void) unlink(i->temp_path);
-        free_and_replace(i->temp_path, t);
+        unlink_and_free(i->temp_path);
+        i->temp_path = TAKE_PTR(t);
         CLOSE_AND_REPLACE(i->output_fd, converted_fd);
 
         return 1;
@@ -191,39 +205,52 @@ static int raw_import_finish(RawImport *i) {
 
         assert(i);
         assert(i->output_fd >= 0);
-        assert(i->temp_path);
-        assert(i->final_path);
 
-        /* In case this was a sparse file, make sure the file system is right */
-        if (i->written_uncompressed > 0) {
-                if (ftruncate(i->output_fd, i->written_uncompressed) < 0)
-                        return log_error_errno(errno, "Failed to truncate file: %m");
-        }
+        /* Nothing of what is below applies to block devices */
+        if (S_ISBLK(i->output_stat.st_mode)) {
 
-        r = raw_import_maybe_convert_qcow2(i);
-        if (r < 0)
-                return r;
+                if (i->flags & IMPORT_SYNC) {
+                        if (fsync(i->output_fd) < 0)
+                                return log_error_errno(errno, "Failed to synchronize block device: %m");
+                }
 
-        if (S_ISREG(i->st.st_mode)) {
-                (void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME);
-                (void) copy_xattr(i->input_fd, i->output_fd);
+                return 0;
         }
 
-        if (i->flags & IMPORT_READ_ONLY) {
-                r = import_make_read_only_fd(i->output_fd);
+        assert(S_ISREG(i->output_stat.st_mode));
+
+        /* If an offset is specified we only are supposed to affect part of an existing output file or block
+         * device, thus don't manipulate file properties in that case */
+
+        if (i->offset == UINT64_MAX) {
+                /* In case this was a sparse file, make sure the file size is right */
+                if (i->written_uncompressed > 0) {
+                        if (ftruncate(i->output_fd, i->written_uncompressed) < 0)
+                                return log_error_errno(errno, "Failed to truncate file: %m");
+                }
+
+                r = raw_import_maybe_convert_qcow2(i);
                 if (r < 0)
                         return r;
-        }
 
-        if (i->flags & IMPORT_FORCE)
-                (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+                if (S_ISREG(i->input_stat.st_mode)) {
+                        (void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME);
+                        (void) copy_xattr(i->input_fd, i->output_fd, 0);
+                }
+        }
 
-        r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+        r = install_file(AT_FDCWD, i->temp_path ?: i->local,
+                         AT_FDCWD, i->final_path,
+                         (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
+                         (i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
+                         (i->flags & IMPORT_SYNC ? INSTALL_FSYNC_FULL : 0));
         if (r < 0)
                 return log_error_errno(r, "Failed to move image into place: %m");
 
         i->temp_path = mfree(i->temp_path);
 
+        log_info("Wrote %s.", FORMAT_BYTES(i->written_uncompressed));
+
         return 0;
 }
 
@@ -231,26 +258,56 @@ static int raw_import_open_disk(RawImport *i) {
         int r;
 
         assert(i);
-
+        assert(i->local);
         assert(!i->final_path);
         assert(!i->temp_path);
         assert(i->output_fd < 0);
 
-        i->final_path = strjoin(i->image_root, "/", i->local, ".raw");
-        if (!i->final_path)
-                return log_oom();
+        if (i->flags & IMPORT_DIRECT) {
+                (void) mkdir_parents_label(i->local, 0700);
 
-        r = tempfn_random(i->final_path, NULL, &i->temp_path);
-        if (r < 0)
-                return log_oom();
+                /* In direct mode we just open/create the local path and truncate it (like shell >
+                 * redirection would do it) — except if an offset was passed, in which case we are supposed
+                 * to operate on a section of the file only, in which case we apparently work on an some
+                 * existing thing (i.e. are not the sole thing stored in the file), in which case we will
+                 * neither truncate nor create. */
+
+                i->output_fd = open(i->local, O_RDWR|O_NOCTTY|O_CLOEXEC|(i->offset == UINT64_MAX ? O_TRUNC|O_CREAT : 0), 0664);
+                if (i->output_fd < 0)
+                        return log_error_errno(errno, "Failed to open destination '%s': %m", i->local);
 
-        (void) mkdir_parents_label(i->temp_path, 0700);
+                if (i->offset == UINT64_MAX)
+                        (void) import_set_nocow_and_log(i->output_fd, i->local);
+        } else {
+                i->final_path = strjoin(i->image_root, "/", i->local, ".raw");
+                if (!i->final_path)
+                        return log_oom();
 
-        i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
-        if (i->output_fd < 0)
-                return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
+                r = tempfn_random(i->final_path, NULL, &i->temp_path);
+                if (r < 0)
+                        return log_oom();
+
+                (void) mkdir_parents_label(i->temp_path, 0700);
+
+                i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+                if (i->output_fd < 0)
+                        return log_error_errno(errno, "Failed to open destination '%s': %m", i->temp_path);
+
+                (void) import_set_nocow_and_log(i->output_fd, i->temp_path);
+        }
+
+        if (fstat(i->output_fd, &i->output_stat) < 0)
+                return log_error_errno(errno, "Failed to stat() output file: %m");
+
+        if (!S_ISREG(i->output_stat.st_mode) && !S_ISBLK(i->output_stat.st_mode))
+                return log_error_errno(SYNTHETIC_ERRNO(EBADFD),
+                                       "Target file is not a regular file or block device");
+
+        if (i->offset != UINT64_MAX) {
+                if (lseek(i->output_fd, i->offset, SEEK_SET) == (off_t) -1)
+                        return log_error_errno(errno, "Failed to seek to offset: %m");
+        }
 
-        (void) import_set_nocow_and_log(i->output_fd, i->temp_path);
         return 0;
 }
 
@@ -265,7 +322,10 @@ static int raw_import_try_reflink(RawImport *i) {
         if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED)
                 return 0;
 
-        if (!S_ISREG(i->st.st_mode))
+        if (i->offset != UINT64_MAX || i->size_max != UINT64_MAX)
+                return 0;
+
+        if (!S_ISREG(i->input_stat.st_mode) || !S_ISREG(i->output_stat.st_mode))
                 return 0;
 
         p = lseek(i->input_fd, 0, SEEK_CUR);
@@ -280,21 +340,57 @@ static int raw_import_try_reflink(RawImport *i) {
         if (r >= 0)
                 return 1;
 
+        log_debug_errno(r, "Couldn't establish reflink, using copy: %m");
         return 0;
 }
 
 static int raw_import_write(const void *p, size_t sz, void *userdata) {
         RawImport *i = userdata;
-        ssize_t n;
+        bool too_much = false;
+        int r;
+
+        assert(i);
+        assert(p);
+        assert(sz > 0);
+
+        if (i->written_uncompressed >= UINT64_MAX - sz)
+                return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow");
 
-        n = sparse_write(i->output_fd, p, sz, 64);
-        if (n < 0)
-                return (int) n;
-        if ((size_t) n < sz)
-                return -EIO;
+        if (i->size_max != UINT64_MAX) {
+                if (i->written_uncompressed >= i->size_max) {
+                        too_much = true;
+                        goto finish;
+                }
+
+                if (i->written_uncompressed + sz > i->size_max) {
+                        too_much = true;
+                        sz = i->size_max - i->written_uncompressed; /* since we have the data in memory
+                                                                     * already, we might as well write it to
+                                                                     * disk to the max */
+                }
+        }
+
+        /* Generate sparse file if we created/truncated the file */
+        if (S_ISREG(i->output_stat.st_mode) && i->offset == UINT64_MAX) {
+                ssize_t n;
+
+                n = sparse_write(i->output_fd, p, sz, 64);
+                if (n < 0)
+                        return log_error_errno((int) n, "Failed to write file: %m");
+                if ((size_t) n < sz)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
+        } else {
+                r = loop_write(i->output_fd, p, sz, false);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to write file: %m");
+        }
 
         i->written_uncompressed += sz;
 
+finish:
+        if (too_much)
+                return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "File too large");
+
         return 0;
 }
 
@@ -382,15 +478,22 @@ static int raw_import_on_defer(sd_event_source *s, void *userdata) {
         return raw_import_process(i);
 }
 
-int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags) {
+int raw_import_start(
+                RawImport *i,
+                int fd,
+                const char *local,
+                uint64_t offset,
+                uint64_t size_max,
+                ImportFlags flags) {
         int r;
 
         assert(i);
         assert(fd >= 0);
         assert(local);
-        assert(!(flags & ~IMPORT_FLAGS_MASK));
+        assert(!(flags & ~IMPORT_FLAGS_MASK_RAW));
+        assert(offset == UINT64_MAX || FLAGS_SET(flags, IMPORT_DIRECT));
 
-        if (!hostname_is_valid(local, 0))
+        if (!import_validate_local(local, flags))
                 return -EINVAL;
 
         if (i->input_fd >= 0)
@@ -405,8 +508,10 @@ int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags)
                 return r;
 
         i->flags = flags;
+        i->offset = offset;
+        i->size_max = size_max;
 
-        if (fstat(fd, &i->st) < 0)
+        if (fstat(fd, &i->input_stat) < 0)
                 return -errno;
 
         r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i);
@@ -422,5 +527,5 @@ int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags)
                 return r;
 
         i->input_fd = fd;
-        return r;
+        return 0;
 }
index e99703c15597c1d0dd27da288e7fbb0e1eaa206f..63384eb171f539903983f5e5cb23ec4c835e7164 100644 (file)
@@ -16,4 +16,4 @@ RawImport* raw_import_unref(RawImport *import);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(RawImport*, raw_import_unref);
 
-int raw_import_start(RawImport *i, int fd, const char *local, ImportFlags flags);
+int raw_import_start(RawImport *i, int fd, const char *local, uint64_t offset, uint64_t size_max, ImportFlags flags);
index 7c4e5127a9fb495e475910dab7464ebedfb3ffdc..bb67862d620039bb6e7bdcb1379d81af127f9815 100644 (file)
@@ -15,6 +15,7 @@
 #include "import-common.h"
 #include "import-compress.h"
 #include "import-tar.h"
+#include "install-file.h"
 #include "io-util.h"
 #include "machine-pool.h"
 #include "mkdir.h"
@@ -54,7 +55,7 @@ struct TarImport {
         uint64_t written_compressed;
         uint64_t written_uncompressed;
 
-        struct stat st;
+        struct stat input_stat;
 
         pid_t tar_pid;
 
@@ -136,13 +137,13 @@ static void tar_import_report_progress(TarImport *i) {
         assert(i);
 
         /* We have no size information, unless the source is a regular file */
-        if (!S_ISREG(i->st.st_mode))
+        if (!S_ISREG(i->input_stat.st_mode))
                 return;
 
-        if (i->written_compressed >= (uint64_t) i->st.st_size)
+        if (i->written_compressed >= (uint64_t) i->input_stat.st_size)
                 percent = 100;
         else
-                percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
+                percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->input_stat.st_size);
 
         if (percent == i->last_percent)
                 return;
@@ -157,12 +158,11 @@ static void tar_import_report_progress(TarImport *i) {
 }
 
 static int tar_import_finish(TarImport *i) {
+        const char *d;
         int r;
 
         assert(i);
         assert(i->tar_fd >= 0);
-        assert(i->temp_path);
-        assert(i->final_path);
 
         i->tar_fd = safe_close(i->tar_fd);
 
@@ -175,22 +175,20 @@ static int tar_import_finish(TarImport *i) {
                         return -EPROTO;
         }
 
-        r = import_mangle_os_tree(i->temp_path);
+        assert_se(d = i->temp_path ?: i->local);
+
+        r = import_mangle_os_tree(d);
         if (r < 0)
                 return r;
 
-        if (i->flags & IMPORT_READ_ONLY) {
-                r = import_make_read_only(i->temp_path);
-                if (r < 0)
-                        return r;
-        }
-
-        if (i->flags & IMPORT_FORCE)
-                (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
-
-        r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+        r = install_file(
+                        AT_FDCWD, d,
+                        AT_FDCWD, i->final_path,
+                        (i->flags & IMPORT_FORCE ? INSTALL_REPLACE : 0) |
+                        (i->flags & IMPORT_READ_ONLY ? INSTALL_READ_ONLY : 0) |
+                        (i->flags & IMPORT_SYNC ? INSTALL_SYNCFS : 0));
         if (r < 0)
-                return log_error_errno(r, "Failed to move image into place: %m");
+                return log_error_errno(r, "Failed to move '%s' into place: %m", i->final_path ?: i->local);
 
         i->temp_path = mfree(i->temp_path);
 
@@ -198,33 +196,54 @@ static int tar_import_finish(TarImport *i) {
 }
 
 static int tar_import_fork_tar(TarImport *i) {
+        const char *d, *root;
         int r;
 
         assert(i);
-
+        assert(i->local);
         assert(!i->final_path);
         assert(!i->temp_path);
         assert(i->tar_fd < 0);
 
-        i->final_path = path_join(i->image_root, i->local);
-        if (!i->final_path)
-                return log_oom();
+        if (i->flags & IMPORT_DIRECT) {
+                d = i->local;
+                root = NULL;
+        } else {
+                i->final_path = path_join(i->image_root, i->local);
+                if (!i->final_path)
+                        return log_oom();
 
-        r = tempfn_random(i->final_path, NULL, &i->temp_path);
-        if (r < 0)
-                return log_oom();
+                r = tempfn_random(i->final_path, NULL, &i->temp_path);
+                if (r < 0)
+                        return log_oom();
+
+                d = i->temp_path;
+                root = i->image_root;
+        }
+
+        assert(d);
 
-        (void) mkdir_parents_label(i->temp_path, 0700);
+        (void) mkdir_parents_label(d, 0700);
 
-        r = btrfs_subvol_make_fallback(i->temp_path, 0755);
+        if (FLAGS_SET(i->flags, IMPORT_DIRECT|IMPORT_FORCE))
+                (void) rm_rf(d, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+
+        if (i->flags & IMPORT_BTRFS_SUBVOL)
+                r = btrfs_subvol_make_fallback(d, 0755);
+        else
+                r = mkdir(d, 0755) < 0 ? -errno : 0;
+        if (r == -EEXIST && (i->flags & IMPORT_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
+                                                         * because in that case our temporary path collided */
+                r = 0;
         if (r < 0)
-                return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path);
-        if (r > 0) { /* actually btrfs subvol */
-                (void) import_assign_pool_quota_and_warn(i->image_root);
-                (void) import_assign_pool_quota_and_warn(i->temp_path);
+                return log_error_errno(r, "Failed to create directory/subvolume %s: %m", d);
+        if (r > 0 && (i->flags & IMPORT_BTRFS_QUOTA)) { /* actually btrfs subvol */
+                if (!(i->flags & IMPORT_DIRECT))
+                        (void) import_assign_pool_quota_and_warn(root);
+                (void) import_assign_pool_quota_and_warn(d);
         }
 
-        i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
+        i->tar_fd = import_fork_tar_x(d, &i->tar_pid);
         if (i->tar_fd < 0)
                 return i->tar_fd;
 
@@ -327,9 +346,9 @@ int tar_import_start(TarImport *i, int fd, const char *local, ImportFlags flags)
         assert(i);
         assert(fd >= 0);
         assert(local);
-        assert(!(flags & ~IMPORT_FLAGS_MASK));
+        assert(!(flags & ~IMPORT_FLAGS_MASK_TAR));
 
-        if (!hostname_is_valid(local, 0))
+        if (!import_validate_local(local, flags))
                 return -EINVAL;
 
         if (i->input_fd >= 0)
@@ -345,7 +364,7 @@ int tar_import_start(TarImport *i, int fd, const char *local, ImportFlags flags)
 
         i->flags = flags;
 
-        if (fstat(fd, &i->st) < 0)
+        if (fstat(fd, &i->input_stat) < 0)
                 return -errno;
 
         r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
@@ -361,5 +380,5 @@ int tar_import_start(TarImport *i, int fd, const char *local, ImportFlags flags)
                 return r;
 
         i->input_fd = fd;
-        return r;
+        return 0;
 }
index fe4c03a4da52f7972e1cb261d30b6f9bc4b9a0be..87ed8767e56d02732ce660046971f0c8df9d4030 100644 (file)
 
 #include "alloc-util.h"
 #include "discover-image.h"
+#include "env-util.h"
 #include "fd-util.h"
 #include "fs-util.h"
 #include "hostname-util.h"
 #include "import-raw.h"
 #include "import-tar.h"
 #include "import-util.h"
+#include "io-util.h"
 #include "main-func.h"
+#include "parse-argument.h"
+#include "parse-util.h"
 #include "signal-util.h"
 #include "string-util.h"
+#include "terminal-util.h"
 #include "verbs.h"
 
 static const char *arg_image_root = "/var/lib/machines";
-static ImportFlags arg_import_flags = 0;
+static ImportFlags arg_import_flags = IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC;
+static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
 
-static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
-        log_notice("Transfer aborted.");
-        sd_event_exit(sd_event_source_get_event(s), EINTR);
+static int normalize_local(const char *local, char **ret) {
+        _cleanup_free_ char *ll = NULL;
+        int r;
+
+        assert(ret);
+
+        if (arg_import_flags & IMPORT_DIRECT) {
+
+                if (!local)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No local path specified.");
+
+                if (!path_is_absolute(local))  {
+                        ll = path_join(arg_image_root, local);
+                        if (!ll)
+                                return log_oom();
+
+                        local = ll;
+                }
+
+                if (!path_is_valid(local))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Local path name '%s' is not valid.", local);
+        } else {
+                if (local) {
+                        if (!hostname_is_valid(local, 0))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                       "Local image name '%s' is not valid.",
+                                                       local);
+                } else
+                        local = "imported";
+
+                if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
+                        r = image_find(IMAGE_MACHINE, local, NULL, NULL);
+                        if (r < 0) {
+                                if (r != -ENOENT)
+                                        return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
+                        } else
+                                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+                                                       "Image '%s' already exists.",
+                                                       local);
+                }
+        }
+
+        if (!ll) {
+                ll = strdup(local);
+                if (!ll)
+                        return log_oom();
+        }
+
+        *ret = TAKE_PTR(ll);
         return 0;
 }
 
+static int open_source(const char *path, const char *local, int *ret_open_fd) {
+        _cleanup_close_ int open_fd = -1;
+        int retval;
+
+        assert(local);
+        assert(ret_open_fd);
+
+        if (path) {
+                open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (open_fd < 0)
+                        return log_error_errno(errno, "Failed to open raw image to import: %m");
+
+                retval = open_fd;
+
+                if (arg_offset != UINT64_MAX)
+                        log_info("Importing '%s', saving at offset %" PRIu64 " in '%s'.", path, arg_offset, local);
+                else
+                        log_info("Importing '%s', saving as '%s'.", path, local);
+        } else {
+                _cleanup_free_ char *pretty = NULL;
+
+                retval = STDIN_FILENO;
+
+                (void) fd_get_path(STDIN_FILENO, &pretty);
+
+                if (arg_offset != UINT64_MAX)
+                        log_info("Importing '%s', saving at offset %" PRIu64 " in '%s'.", strempty(pretty), arg_offset, local);
+                else
+                        log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
+        }
+
+        *ret_open_fd = TAKE_FD(open_fd);
+        return retval;
+}
+
 static void on_tar_finished(TarImport *import, int error, void *userdata) {
         sd_event *event = userdata;
         assert(import);
@@ -40,9 +128,9 @@ static void on_tar_finished(TarImport *import, int error, void *userdata) {
 
 static int import_tar(int argc, char *argv[], void *userdata) {
         _cleanup_(tar_import_unrefp) TarImport *import = NULL;
+        _cleanup_free_ char *ll = NULL, *normalized = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         const char *path = NULL, *local = NULL;
-        _cleanup_free_ char *ll = NULL;
         _cleanup_close_ int open_fd = -1;
         int r, fd;
 
@@ -51,64 +139,44 @@ static int import_tar(int argc, char *argv[], void *userdata) {
 
         if (argc >= 3)
                 local = empty_or_dash_to_null(argv[2]);
-        else if (path)
-                local = basename(path);
+        else if (path) {
+                _cleanup_free_ char *l = NULL;
 
-        if (local) {
-                r = tar_strip_suffixes(local, &ll);
+                r = path_extract_filename(path, &l);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
+
+                r = tar_strip_suffixes(l, &ll);
                 if (r < 0)
                         return log_oom();
 
                 local = ll;
+        }
 
-                if (!hostname_is_valid(local, 0))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Local image name '%s' is not valid.",
-                                               local);
-
-                if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
-                        r = image_find(IMAGE_MACHINE, local, NULL, NULL);
-                        if (r < 0) {
-                                if (r != -ENOENT)
-                                        return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
-                        } else
-                                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
-                                                       "Image '%s' already exists.",
-                                                       local);
-                }
-        } else
-                local = "imported";
-
-        if (path) {
-                open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (open_fd < 0)
-                        return log_error_errno(errno, "Failed to open tar image to import: %m");
-
-                fd = open_fd;
-
-                log_info("Importing '%s', saving as '%s'.", path, local);
-        } else {
-                _cleanup_free_ char *pretty = NULL;
+        r = normalize_local(local, &normalized);
+        if (r < 0)
+                return r;
 
-                fd = STDIN_FILENO;
+        fd = open_source(path, normalized, &open_fd);
+        if (fd < 0)
+                return r;
 
-                (void) fd_get_path(fd, &pretty);
-                log_info("Importing '%s', saving as '%s'.", strna(pretty), local);
-        }
-
-        r = sd_event_default(&event);
+        r = import_allocate_event_with_signals(&event);
         if (r < 0)
-                return log_error_errno(r, "Failed to allocate event loop: %m");
+                return r;
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
-        (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler,  NULL);
-        (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+        if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
+                log_info("File system synchronization on completion is off.");
 
         r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate importer: %m");
 
-        r = tar_import_start(import, fd, local, arg_import_flags);
+        r = tar_import_start(
+                        import,
+                        fd,
+                        normalized,
+                        arg_import_flags & IMPORT_FLAGS_MASK_TAR);
         if (r < 0)
                 return log_error_errno(r, "Failed to import image: %m");
 
@@ -132,9 +200,9 @@ static void on_raw_finished(RawImport *import, int error, void *userdata) {
 
 static int import_raw(int argc, char *argv[], void *userdata) {
         _cleanup_(raw_import_unrefp) RawImport *import = NULL;
+        _cleanup_free_ char *ll = NULL, *normalized = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         const char *path = NULL, *local = NULL;
-        _cleanup_free_ char *ll = NULL;
         _cleanup_close_ int open_fd = -1;
         int r, fd;
 
@@ -143,64 +211,46 @@ static int import_raw(int argc, char *argv[], void *userdata) {
 
         if (argc >= 3)
                 local = empty_or_dash_to_null(argv[2]);
-        else if (path)
-                local = basename(path);
+        else if (path) {
+                _cleanup_free_ char *l = NULL;
+
+                r = path_extract_filename(path, &l);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);
 
-        if (local) {
-                r = raw_strip_suffixes(local, &ll);
+                r = raw_strip_suffixes(l, &ll);
                 if (r < 0)
                         return log_oom();
 
                 local = ll;
+        }
 
-                if (!hostname_is_valid(local, 0))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Local image name '%s' is not valid.",
-                                               local);
-
-                if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
-                        r = image_find(IMAGE_MACHINE, local, NULL, NULL);
-                        if (r < 0) {
-                                if (r != -ENOENT)
-                                        return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
-                        } else
-                                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
-                                                       "Image '%s' already exists.",
-                                                       local);
-                }
-        } else
-                local = "imported";
-
-        if (path) {
-                open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (open_fd < 0)
-                        return log_error_errno(errno, "Failed to open raw image to import: %m");
-
-                fd = open_fd;
-
-                log_info("Importing '%s', saving as '%s'.", path, local);
-        } else {
-                _cleanup_free_ char *pretty = NULL;
-
-                fd = STDIN_FILENO;
+        r = normalize_local(local, &normalized);
+        if (r < 0)
+                return r;
 
-                (void) fd_get_path(fd, &pretty);
-                log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
-        }
+        fd = open_source(path, normalized, &open_fd);
+        if (fd < 0)
+                return fd;
 
-        r = sd_event_default(&event);
+        r = import_allocate_event_with_signals(&event);
         if (r < 0)
-                return log_error_errno(r, "Failed to allocate event loop: %m");
+                return r;
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
-        (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler,  NULL);
-        (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+        if (!FLAGS_SET(arg_import_flags, IMPORT_SYNC))
+                log_info("File system synchronization on completion is off.");
 
         r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate importer: %m");
 
-        r = raw_import_start(import, fd, local, arg_import_flags);
+        r = raw_import_start(
+                        import,
+                        fd,
+                        normalized,
+                        arg_offset,
+                        arg_size_max,
+                        arg_import_flags & IMPORT_FLAGS_MASK_RAW);
         if (r < 0)
                 return log_error_errno(r, "Failed to import image: %m");
 
@@ -214,17 +264,32 @@ static int import_raw(int argc, char *argv[], void *userdata) {
 
 static int help(int argc, char *argv[], void *userdata) {
 
-        printf("%s [OPTIONS...] {COMMAND} ...\n\n"
-               "Import container or virtual machine images.\n\n"
+        printf("%1$s [OPTIONS...] {COMMAND} ...\n"
+               "\n%4$sImport container or virtual machine images.%5$s\n"
+               "\n%2$sCommands:%3$s\n"
+               "  tar FILE [NAME]             Import a TAR image\n"
+               "  raw FILE [NAME]             Import a RAW image\n"
+               "\n%2$sOptions:%3$s\n"
                "  -h --help                   Show this help\n"
                "     --version                Show package version\n"
                "     --force                  Force creation of image\n"
                "     --image-root=PATH        Image root directory\n"
-               "     --read-only              Create a read-only image\n\n"
-               "Commands:\n"
-               "  tar FILE [NAME]             Import a TAR image\n"
-               "  raw FILE [NAME]             Import a RAW image\n",
-               program_invocation_short_name);
+               "     --read-only              Create a read-only image\n"
+               "     --direct                 Import directly to specified file\n"
+               "     --btrfs-subvol=BOOL      Controls whether to create a btrfs subvolume\n"
+               "                              instead of a directory\n"
+               "     --btrfs-quota=BOOL       Controls whether to set up quota for btrfs\n"
+               "                              subvolume\n"
+               "     --convert-qcow2=BOOL     Controls whether to convert QCOW2 images to\n"
+               "                              regular disk images\n"
+               "     --sync=BOOL              Controls whether to sync() before completing\n"
+               "     --offset=BYTES           Offset to seek to in destination\n"
+               "     --size-max=BYTES         Maximum number of bytes to write to destination\n",
+               program_invocation_short_name,
+               ansi_underline(),
+               ansi_normal(),
+               ansi_highlight(),
+               ansi_normal());
 
         return 0;
 }
@@ -236,6 +301,13 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_FORCE,
                 ARG_IMAGE_ROOT,
                 ARG_READ_ONLY,
+                ARG_DIRECT,
+                ARG_BTRFS_SUBVOL,
+                ARG_BTRFS_QUOTA,
+                ARG_CONVERT_QCOW2,
+                ARG_SYNC,
+                ARG_OFFSET,
+                ARG_SIZE_MAX,
         };
 
         static const struct option options[] = {
@@ -244,10 +316,17 @@ static int parse_argv(int argc, char *argv[]) {
                 { "force",           no_argument,       NULL, ARG_FORCE           },
                 { "image-root",      required_argument, NULL, ARG_IMAGE_ROOT      },
                 { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
+                { "direct",          no_argument,       NULL, ARG_DIRECT          },
+                { "btrfs-subvol",    required_argument, NULL, ARG_BTRFS_SUBVOL    },
+                { "btrfs-quota",     required_argument, NULL, ARG_BTRFS_QUOTA     },
+                { "convert-qcow2",   required_argument, NULL, ARG_CONVERT_QCOW2   },
+                { "sync",            required_argument, NULL, ARG_SYNC            },
+                { "offset",          required_argument, NULL, ARG_OFFSET          },
+                { "size-max",        required_argument, NULL, ARG_SIZE_MAX        },
                 {}
         };
 
-        int c;
+        int r, c;
 
         assert(argc >= 0);
         assert(argv);
@@ -274,13 +353,84 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_import_flags |= IMPORT_READ_ONLY;
                         break;
 
+                case ARG_DIRECT:
+                        arg_import_flags |= IMPORT_DIRECT;
+                        break;
+
+                case ARG_BTRFS_SUBVOL:
+                        r = parse_boolean_argument("--btrfs-subvol=", optarg, NULL);
+                        if (r < 0)
+                                return r;
+
+                        SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
+                        break;
+
+                case ARG_BTRFS_QUOTA:
+                        r = parse_boolean_argument("--btrfs-quota=", optarg, NULL);
+                        if (r < 0)
+                                return r;
+
+                        SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
+                        break;
+
+                case ARG_CONVERT_QCOW2:
+                        r = parse_boolean_argument("--convert-qcow2=", optarg, NULL);
+                        if (r < 0)
+                                return r;
+
+                        SET_FLAG(arg_import_flags, IMPORT_CONVERT_QCOW2, r);
+                        break;
+
+                case ARG_SYNC:
+                        r = parse_boolean_argument("--sync=", optarg, NULL);
+                        if (r < 0)
+                                return r;
+
+                        SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
+                        break;
+
+                case ARG_OFFSET: {
+                        uint64_t u;
+
+                        r = safe_atou64(optarg, &u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --offset= argument: %s", optarg);
+                        if (!FILE_SIZE_VALID(u))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --offset= switch too large: %s", optarg);
+
+                        arg_offset = u;
+                        break;
+                }
+
+                case ARG_SIZE_MAX: {
+                        uint64_t u;
+
+                        r = parse_size(optarg, 1024, &u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --size-max= argument: %s", optarg);
+                        if (!FILE_SIZE_VALID(u))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --size-max= switch too large: %s", optarg);
+
+                        arg_size_max = u;
+                        break;
+                }
+
                 case '?':
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
+        /* Make sure offset+size is still in the valid range if both set */
+        if (arg_offset != UINT64_MAX && arg_size_max != UINT64_MAX &&
+            ((arg_size_max > (UINT64_MAX - arg_offset)) ||
+             !FILE_SIZE_VALID(arg_offset + arg_size_max)))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset und maximum size out of range.");
+
+        if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_import_flags, IMPORT_DIRECT))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
+
         return 1;
 }
 
@@ -295,6 +445,31 @@ static int import_main(int argc, char *argv[]) {
         return dispatch_verb(argc, argv, verbs, NULL);
 }
 
+static void parse_env(void) {
+        int r;
+
+        /* Let's make these relatively low-level settings also controllable via env vars. User can then set
+         * them to systemd-import if they like to tweak behaviour */
+
+        r = getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
+        if (r >= 0)
+                SET_FLAG(arg_import_flags, IMPORT_BTRFS_SUBVOL, r);
+        else if (r != -ENXIO)
+                log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
+
+        r = getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
+        if (r >= 0)
+                SET_FLAG(arg_import_flags, IMPORT_BTRFS_QUOTA, r);
+        else if (r != -ENXIO)
+                log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
+
+        r = getenv_bool("SYSTEMD_IMPORT_SYNC");
+        if (r >= 0)
+                SET_FLAG(arg_import_flags, IMPORT_SYNC, r);
+        else if (r != -ENXIO)
+                log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_SYNC: %m");
+}
+
 static int run(int argc, char *argv[]) {
         int r;
 
@@ -302,9 +477,11 @@ static int run(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
 
+        parse_env();
+
         r = parse_argv(argc, argv);
         if (r <= 0)
-                return 0;
+                return r;
 
         (void) ignore_signals(SIGPIPE);
 
index f0f61ca7848e62153b5dad7f5bcb450615147c6e..86181628d9d6b3200e23a41b681c026c53960733 100644 (file)
@@ -431,7 +431,7 @@ static int transfer_start(Transfer *t) {
                         break;
 
                 default:
-                        assert_not_reached("Unexpected transfer type");
+                        assert_not_reached();
                 }
 
                 switch (t->type) {
@@ -928,7 +928,7 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
         if (r < 0)
                 return r;
 
-        if (!http_url_is_valid(remote))
+        if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
                                          "URL %s is invalid", remote);
 
index cb77454e0f6983015e216fd1f94601828548c31d..39067933b5eac6d09883564d8e23e9c4328d0c37 100644 (file)
@@ -9,7 +9,9 @@
 #include "dirent-util.h"
 #include "escape.h"
 #include "fd-util.h"
+#include "hostname-util.h"
 #include "io-util.h"
+#include "memory-util.h"
 #include "path-util.h"
 #include "process-util.h"
 #include "pull-common.h"
@@ -111,34 +113,6 @@ int pull_find_old_etags(
         return 0;
 }
 
-int pull_make_local_copy(const char *final, const char *image_root, const char *local, PullFlags flags) {
-        const char *p;
-        int r;
-
-        assert(final);
-        assert(local);
-
-        if (!image_root)
-                image_root = "/var/lib/machines";
-
-        p = prefix_roota(image_root, local);
-
-        if (FLAGS_SET(flags, PULL_FORCE))
-                (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
-
-        r = btrfs_subvol_snapshot(final, p,
-                                  BTRFS_SNAPSHOT_QUOTA|
-                                  BTRFS_SNAPSHOT_FALLBACK_COPY|
-                                  BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
-                                  BTRFS_SNAPSHOT_RECURSIVE);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create local image: %m");
-
-        log_info("Created new local image '%s'.", local);
-
-        return 0;
-}
-
 static int hash_url(const char *url, char **ret) {
         uint64_t h;
         static const sd_id128_t k = SD_ID128_ARRAY(df,89,16,87,01,cc,42,30,98,ab,4a,19,a6,a5,63,4f);
@@ -205,7 +179,9 @@ int pull_make_auxiliary_job(
                 const char *url,
                 int (*strip_suffixes)(const char *name, char **ret),
                 const char *suffix,
+                ImportVerify verify,
                 CurlGlue *glue,
+                PullJobOpenDisk on_open_disk,
                 PullJobFinished on_finished,
                 void *userdata) {
 
@@ -237,45 +213,82 @@ int pull_make_auxiliary_job(
         if (r < 0)
                 return r;
 
+        job->on_open_disk = on_open_disk;
         job->on_finished = on_finished;
         job->compressed_max = job->uncompressed_max = 1ULL * 1024ULL * 1024ULL;
+        job->calc_checksum = IN_SET(verify, IMPORT_VERIFY_CHECKSUM, IMPORT_VERIFY_SIGNATURE);
 
         *ret = TAKE_PTR(job);
-
         return 0;
 }
 
+static bool is_checksum_file(const char *fn) {
+        /* Returns true if the specified filename refers to a checksum file we grok */
+
+        if (!fn)
+                return false;
+
+        return streq(fn, "SHA256SUMS") || endswith(fn, ".sha256");
+}
+
+static bool is_signature_file(const char *fn) {
+        /* Returns true if the specified filename refers to a signature file we grok (reminder:
+         * suse-style .sha256 files are inline signed) */
+
+        if (!fn)
+                return false;
+
+        return streq(fn, "SHA256SUMS.gpg") || endswith(fn, ".sha256");
+}
+
 int pull_make_verification_jobs(
                 PullJob **ret_checksum_job,
                 PullJob **ret_signature_job,
                 ImportVerify verify,
+                const char *checksum, /* set if literal checksum verification is requested, in which case 'verify' is set to _IMPORT_VERIFY_INVALID */
                 const char *url,
                 CurlGlue *glue,
                 PullJobFinished on_finished,
                 void *userdata) {
 
         _cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL;
+        _cleanup_free_ char *fn = NULL;
         int r;
 
         assert(ret_checksum_job);
         assert(ret_signature_job);
-        assert(verify >= 0);
-        assert(verify < _IMPORT_VERIFY_MAX);
+        assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
+        assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
+        assert((verify < 0) || !checksum);
         assert(url);
         assert(glue);
 
-        if (verify != IMPORT_VERIFY_NO) {
-                _cleanup_free_ char *checksum_url = NULL, *fn = NULL;
-                const char *chksums = NULL;
+        /* If verification is turned off, or if the checksum to validate is already specified we don't need
+         * to download a checksum file or signature, hence shortcut things */
+        if (verify == IMPORT_VERIFY_NO || checksum) {
+                *ret_checksum_job = *ret_signature_job = NULL;
+                return 0;
+        }
+
+        r = import_url_last_component(url, &fn);
+        if (r < 0 && r != -EADDRNOTAVAIL) /* EADDRNOTAVAIL means there was no last component, which is OK for
+                                           * us, we'll just assume it's not a checksum/signature file */
+                return r;
+
+        /* Acquire the checksum file if verification or signature verification is requested and the main file
+         * to acquire isn't a checksum or signature file anyway */
+        if (verify != IMPORT_VERIFY_NO && !is_checksum_file(fn) && !is_signature_file(fn)) {
+                _cleanup_free_ char *checksum_url = NULL;
+                const char *suffixed = NULL;
 
                 /* Queue jobs for the checksum file for the image. */
-                r = import_url_last_component(url, &fn);
-                if (r < 0)
-                        return r;
 
-                chksums = strjoina(fn, ".sha256");
+                if (fn)
+                        suffixed = strjoina(fn, ".sha256"); /* Start with the suse-style checksum (if there's a base filename) */
+                else
+                        suffixed = "SHA256SUMS";
 
-                r = import_url_change_last_component(url, chksums, &checksum_url);
+                r = import_url_change_last_component(url, suffixed, &checksum_url);
                 if (r < 0)
                         return r;
 
@@ -285,9 +298,10 @@ int pull_make_verification_jobs(
 
                 checksum_job->on_finished = on_finished;
                 checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL;
+                checksum_job->on_not_found = pull_job_restart_with_sha256sum; /* if this fails, look for ubuntu-style checksum */
         }
 
-        if (verify == IMPORT_VERIFY_SIGNATURE) {
+        if (verify == IMPORT_VERIFY_SIGNATURE && !is_signature_file(fn)) {
                 _cleanup_free_ char *signature_url = NULL;
 
                 /* Queue job for the SHA256SUMS.gpg file for the image. */
@@ -305,7 +319,6 @@ int pull_make_verification_jobs(
 
         *ret_checksum_job = TAKE_PTR(checksum_job);
         *ret_signature_job = TAKE_PTR(signature_job);
-
         return 0;
 }
 
@@ -334,31 +347,33 @@ static int verify_one(PullJob *checksum_job, PullJob *job) {
 
         r = import_url_last_component(job->url, &fn);
         if (r < 0)
-                return log_oom();
+                return log_error_errno(r, "Failed to extract filename from URL '%s': %m", job->url);
 
         if (!filename_is_valid(fn))
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
                                        "Cannot verify checksum, could not determine server-side file name.");
 
-        line = strjoina(job->checksum, " *", fn, "\n");
-
-        p = memmem(checksum_job->payload,
-                   checksum_job->payload_size,
-                   line,
-                   strlen(line));
-
-        if (!p) {
-                line = strjoina(job->checksum, "  ", fn, "\n");
+        if (is_checksum_file(fn) || is_signature_file(fn)) /* We cannot verify checksum files or signature files with a checksum file */
+                return log_error_errno(SYNTHETIC_ERRNO(ELOOP),
+                                       "Cannot verify checksum/signature files via themselves.");
 
-                p = memmem(checksum_job->payload,
+        line = strjoina(job->checksum, " *", fn, "\n"); /* string for binary mode */
+        p = memmem_safe(checksum_job->payload,
                         checksum_job->payload_size,
                         line,
                         strlen(line));
+        if (!p) {
+                line = strjoina(job->checksum, "  ", fn, "\n"); /* string for text mode */
+                p = memmem_safe(checksum_job->payload,
+                                checksum_job->payload_size,
+                                line,
+                                strlen(line));
         }
 
+        /* Only counts if found at beginning of a line */
         if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n'))
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                       "DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn);
+                                       "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.", fn);
 
         log_info("SHA256 checksum of %s is valid.", job->url);
         return 1;
@@ -471,10 +486,10 @@ static int verify_gpg(
         pid = 0;
         if (r < 0)
                 goto finish;
-        if (r != EXIT_SUCCESS) {
-                log_error("DOWNLOAD INVALID: Signature verification failed.");
-                r = -EBADMSG;
-        else {
+        if (r != EXIT_SUCCESS)
+                r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                    "DOWNLOAD INVALID: Signature verification failed.");
+        else {
                 log_info("Signature verification succeeded.");
                 r = 0;
         }
@@ -490,6 +505,7 @@ finish:
 }
 
 int pull_verify(ImportVerify verify,
+                const char *checksum, /* Verify with literal checksum */
                 PullJob *main_job,
                 PullJob *checksum_job,
                 PullJob *signature_job,
@@ -498,37 +514,79 @@ int pull_verify(ImportVerify verify,
                 PullJob *roothash_signature_job,
                 PullJob *verity_job) {
 
+        _cleanup_free_ char *fn = NULL;
         VerificationStyle style;
-        PullJob *j;
+        PullJob *verify_job;
         int r;
 
+        assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
+        assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
+        assert((verify < 0) || !checksum);
         assert(main_job);
         assert(main_job->state == PULL_JOB_DONE);
 
-        if (verify == IMPORT_VERIFY_NO)
+        if (verify == IMPORT_VERIFY_NO) /* verification turned off */
                 return 0;
 
-        assert(main_job->calc_checksum);
-        assert(main_job->checksum);
-        assert(checksum_job);
-        assert(checksum_job->state == PULL_JOB_DONE);
+        if (checksum) {
+                /* Verification by literal checksum */
+                assert(!checksum_job);
+                assert(!signature_job);
+                assert(!settings_job);
+                assert(!roothash_job);
+                assert(!roothash_signature_job);
+                assert(!verity_job);
 
-        if (!checksum_job->payload || checksum_job->payload_size <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
-                                       "Checksum is empty, cannot verify.");
+                assert(main_job->calc_checksum);
+                assert(main_job->checksum);
 
-        FOREACH_POINTER(j, main_job, settings_job, roothash_job, roothash_signature_job, verity_job) {
-                r = verify_one(checksum_job, j);
-                if (r < 0)
-                        return r;
+                if (!strcaseeq(checksum, main_job->checksum))
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "DOWNLOAD INVALID: Checksum of %s file did not check out, file has been tampered with.",
+                                               main_job->url);
+
+                return 0;
+        }
+
+        r = import_url_last_component(main_job->url, &fn);
+        if (r < 0)
+                return log_error_errno(r, "Failed to extract filename from URL '%s': %m", main_job->url);
+
+        if (is_signature_file(fn))
+                return log_error_errno(SYNTHETIC_ERRNO(ELOOP),
+                                       "Main download is a signature file, can't verify it.");
+
+        if (is_checksum_file(fn)) {
+                log_debug("Main download is a checksum file, can't validate its checksum with itself, skipping.");
+                verify_job = main_job;
+        } else {
+                PullJob *j;
+                assert(main_job->calc_checksum);
+                assert(main_job->checksum);
+                assert(checksum_job);
+                assert(checksum_job->state == PULL_JOB_DONE);
+
+                if (!checksum_job->payload || checksum_job->payload_size <= 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "Checksum is empty, cannot verify.");
+
+                FOREACH_POINTER(j, main_job, settings_job, roothash_job, roothash_signature_job, verity_job) {
+                        r = verify_one(checksum_job, j);
+                        if (r < 0)
+                                return r;
+                }
+
+                verify_job = checksum_job;
         }
 
-        if (verify == IMPORT_VERIFY_CHECKSUM)
+        if (verify != IMPORT_VERIFY_SIGNATURE)
                 return 0;
 
-        r = verification_style_from_url(checksum_job->url, &style);
+        assert(verify_job);
+
+        r = verification_style_from_url(verify_job->url, &style);
         if (r < 0)
-                return log_error_errno(r, "Failed to determine verification style from URL '%s': %m", checksum_job->url);
+                return log_error_errno(r, "Failed to determine verification style from URL '%s': %m", verify_job->url);
 
         if (style == VERIFICATION_PER_DIRECTORY) {
                 assert(signature_job);
@@ -538,9 +596,9 @@ int pull_verify(ImportVerify verify,
                         return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Signature is empty, cannot verify.");
 
-                return verify_gpg(checksum_job->payload, checksum_job->payload_size, signature_job->payload, signature_job->payload_size);
+                return verify_gpg(verify_job->payload, verify_job->payload_size, signature_job->payload, signature_job->payload_size);
         } else
-                return verify_gpg(checksum_job->payload, checksum_job->payload_size, NULL, 0);
+                return verify_gpg(verify_job->payload, verify_job->payload_size, NULL, 0);
 }
 
 int verification_style_from_url(const char *url, VerificationStyle *ret) {
@@ -594,3 +652,28 @@ int pull_job_restart_with_sha256sum(PullJob *j, char **ret) {
 
         return 1;
 }
+
+bool pull_validate_local(const char *name, PullFlags flags) {
+
+        if (FLAGS_SET(flags, PULL_DIRECT))
+                return path_is_valid(name);
+
+        return hostname_is_valid(name, 0);
+}
+
+int pull_url_needs_checksum(const char *url) {
+        _cleanup_free_ char *fn = NULL;
+        int r;
+
+        /* Returns true if we need to validate this resource via a hash value. This returns true for all
+         * files — except for gpg signature files and SHA256SUMS files and the like, which are validated with
+         * a validation tool like gpg. */
+
+        r = import_url_last_component(url, &fn);
+        if (r == -EADDRNOTAVAIL) /* no last component? then let's assume it's not a signature/checksum file */
+                return false;
+        if (r < 0)
+                return r;
+
+        return !is_checksum_file(fn) && !is_signature_file(fn);
+}
index 3902e29f2b35af5b3b7b2597998bd4f56bf19654..2347db3bacf33faf82168bf1c3b3bf956f4a0f40 100644 (file)
@@ -7,27 +7,31 @@
 #include "pull-job.h"
 
 typedef enum PullFlags {
-        PULL_FORCE              = 1 << 0,  /* replace existing image */
-        PULL_SETTINGS           = 1 << 1,  /* .nspawn settings file */
-        PULL_ROOTHASH           = 1 << 2,  /* only for raw: .roothash file for verity */
-        PULL_ROOTHASH_SIGNATURE = 1 << 3,  /* only for raw: .roothash.p7s file for verity */
-        PULL_VERITY             = 1 << 4,  /* only for raw: .verity file for verity */
+        PULL_FORCE              = 1 << 0, /* replace existing image */
+        PULL_READ_ONLY          = 1 << 1, /* make generated image read-only */
+        PULL_SETTINGS           = 1 << 1, /* download .nspawn settings file */
+        PULL_ROOTHASH           = 1 << 2, /* only for raw: download .roothash file for verity */
+        PULL_ROOTHASH_SIGNATURE = 1 << 3, /* only for raw: download .roothash.p7s file for verity */
+        PULL_VERITY             = 1 << 4, /* only for raw: download .verity file for verity */
+        PULL_BTRFS_SUBVOL       = 1 << 2, /* tar: preferably create images as btrfs subvols */
+        PULL_BTRFS_QUOTA        = 1 << 3, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
+        PULL_CONVERT_QCOW2      = 1 << 4, /* raw: if we detect a qcow2 image, unpack it */
+        PULL_DIRECT             = 1 << 5, /* download without rename games */
+        PULL_SYNC               = 1 << 6, /* fsync() right before we are done */
 
         /* The supported flags for the tar and the raw pulling */
-        PULL_FLAGS_MASK_TAR     = PULL_FORCE|PULL_SETTINGS,
-        PULL_FLAGS_MASK_RAW     = PULL_FORCE|PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY,
+        PULL_FLAGS_MASK_TAR     = PULL_FORCE|PULL_READ_ONLY|PULL_SETTINGS|PULL_BTRFS_SUBVOL|PULL_BTRFS_QUOTA|PULL_DIRECT|PULL_SYNC,
+        PULL_FLAGS_MASK_RAW     = PULL_FORCE|PULL_READ_ONLY|PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY|PULL_CONVERT_QCOW2|PULL_DIRECT|PULL_SYNC,
 } PullFlags;
 
-int pull_make_local_copy(const char *final, const char *root, const char *local, PullFlags flags);
-
 int pull_find_old_etags(const char *url, const char *root, int dt, const char *prefix, const char *suffix, char ***etags);
 
 int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret);
 
-int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
-int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
+int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, ImportVerify verify, CurlGlue *glue, PullJobOpenDisk on_open_disk, PullJobFinished on_finished, void *userdata);
+int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *checksum, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata);
 
-int pull_verify(ImportVerify verify, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job);
+int pull_verify(ImportVerify verify, const char *checksum, PullJob *main_job, PullJob *checksum_job, PullJob *signature_job, PullJob *settings_job, PullJob *roothash_job, PullJob *roothash_signature_job, PullJob *verity_job);
 
 typedef enum VerificationStyle {
         VERIFICATION_PER_FILE,      /* SuSE-style ".sha256" files with inline gpg signature */
@@ -39,3 +43,7 @@ typedef enum VerificationStyle {
 int verification_style_from_url(const char *url, VerificationStyle *style);
 
 int pull_job_restart_with_sha256sum(PullJob *job, char **ret);
+
+bool pull_validate_local(const char *name, PullFlags flags);
+
+int pull_url_needs_checksum(const char *url);
index 038fa2be8ba9542fae44873aa5269bbbedd9451e..4e37dce33f5b764e9fab6ba0f6ea024d66109d63 100644 (file)
@@ -7,6 +7,7 @@
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "format-util.h"
+#include "fs-util.h"
 #include "gcrypt-util.h"
 #include "hexdecoct.h"
 #include "import-util.h"
 #include "strv.h"
 #include "xattr-util.h"
 
+void pull_job_close_disk_fd(PullJob *j) {
+        if (!j)
+                return;
+
+        if (j->close_disk_fd)
+                safe_close(j->disk_fd);
+
+        j->disk_fd = -1;
+}
+
 PullJob* pull_job_unref(PullJob *j) {
         if (!j)
                 return NULL;
 
+        pull_job_close_disk_fd(j);
+
         curl_glue_remove_and_free(j->glue, j->curl);
         curl_slist_free_all(j->request_header);
 
-        safe_close(j->disk_fd);
-
         import_compress_free(&j->compress);
 
         if (j->checksum_context)
@@ -106,7 +117,7 @@ static int pull_job_restart(PullJob *j, const char *new_url) {
 void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
         PullJob *j = NULL;
         CURLcode code;
-        long status;
+        long protocol;
         int r;
 
         if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK)
@@ -116,69 +127,76 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
                 return;
 
         if (result != CURLE_OK) {
-                log_error("Transfer failed: %s", curl_easy_strerror(result));
-                r = -EIO;
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Transfer failed: %s", curl_easy_strerror(result));
                 goto finish;
         }
 
-        code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
+        code = curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol);
         if (code != CURLE_OK) {
-                log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
-                r = -EIO;
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code));
                 goto finish;
-        } else if (status == 304) {
-                log_info("Image already downloaded. Skipping download.");
-                j->etag_exists = true;
-                r = 0;
-                goto finish;
-        } else if (status >= 300) {
+        }
 
-                if (status == 404 && j->on_not_found) {
-                        _cleanup_free_ char *new_url = NULL;
+        if (IN_SET(protocol, CURLPROTO_HTTP, CURLPROTO_HTTPS)) {
+                long status;
 
-                        /* This resource wasn't found, but the implementor wants to maybe let us know a new URL, query for it. */
-                        r = j->on_not_found(j, &new_url);
-                        if (r < 0)
-                                goto finish;
+                code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
+                if (code != CURLE_OK) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code));
+                        goto finish;
+                }
+
+                if (status == 304) {
+                        log_info("Image already downloaded. Skipping download.");
+                        j->etag_exists = true;
+                        r = 0;
+                        goto finish;
+                } else if (status >= 300) {
 
-                        if (r > 0) { /* A new url to use */
-                                assert(new_url);
+                        if (status == 404 && j->on_not_found) {
+                                _cleanup_free_ char *new_url = NULL;
 
-                                r = pull_job_restart(j, new_url);
+                                /* This resource wasn't found, but the implementor wants to maybe let us know a new URL, query for it. */
+                                r = j->on_not_found(j, &new_url);
                                 if (r < 0)
                                         goto finish;
 
-                                code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
-                                if (code != CURLE_OK) {
-                                        log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
-                                        r = -EIO;
-                                        goto finish;
-                                }
+                                if (r > 0) { /* A new url to use */
+                                        assert(new_url);
+
+                                        r = pull_job_restart(j, new_url);
+                                        if (r < 0)
+                                                goto finish;
+
+                                        code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
+                                        if (code != CURLE_OK) {
+                                                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code));
+                                                goto finish;
+                                        }
 
-                                if (status == 0)
-                                        return;
+                                        if (status == 0)
+                                                return;
+                                }
                         }
-                }
 
-                log_error("HTTP request to %s failed with code %li.", j->url, status);
-                r = -EIO;
-                goto finish;
-        } else if (status < 200) {
-                log_error("HTTP request to %s finished with unexpected code %li.", j->url, status);
-                r = -EIO;
-                goto finish;
+                        r = log_error_errno(
+                                        status == 404 ? SYNTHETIC_ERRNO(ENOMEDIUM) : SYNTHETIC_ERRNO(EIO), /* Make the most common error recognizable */
+                                        "HTTP request to %s failed with code %li.", j->url, status);
+                        goto finish;
+                } else if (status < 200) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(EIO), "HTTP request to %s finished with unexpected code %li.", j->url, status);
+                        goto finish;
+                }
         }
 
         if (j->state != PULL_JOB_RUNNING) {
-                log_error("Premature connection termination.");
-                r = -EIO;
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Premature connection termination.");
                 goto finish;
         }
 
         if (j->content_length != UINT64_MAX &&
             j->content_length != j->written_compressed) {
-                log_error("Download truncated.");
-                r = -EIO;
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Download truncated.");
                 goto finish;
         }
 
@@ -187,8 +205,7 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
 
                 k = gcry_md_read(j->checksum_context, GCRY_MD_SHA256);
                 if (!k) {
-                        log_error("Failed to get checksum.");
-                        r = -EIO;
+                        r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum.");
                         goto finish;
                 }
 
@@ -201,31 +218,62 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
                 log_debug("SHA256 of %s is %s.", j->url, j->checksum);
         }
 
-        if (j->disk_fd >= 0 && j->allow_sparse) {
-                /* Make sure the file size is right, in case the file was
-                 * sparse and we just seeked for the last part */
+        /* Do a couple of finishing disk operations, but only if we are the sole owner of the file (i.e. no
+         * offset is specified, which indicates we only own the file partially) */
 
-                if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) {
-                        r = log_error_errno(errno, "Failed to truncate file: %m");
-                        goto finish;
-                }
+        if (j->disk_fd >= 0) {
 
-                if (j->etag)
-                        (void) fsetxattr(j->disk_fd, "user.source_etag", j->etag, strlen(j->etag), 0);
-                if (j->url)
-                        (void) fsetxattr(j->disk_fd, "user.source_url", j->url, strlen(j->url), 0);
+                if (S_ISREG(j->disk_stat.st_mode)) {
 
-                if (j->mtime != 0) {
-                        struct timespec ut[2];
+                        if (j->offset == UINT64_MAX) {
+
+                                if (j->written_compressed > 0) {
+                                        /* Make sure the file size is right, in case the file was sparse and we just seeked
+                                         * for the last part */
+                                        if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) {
+                                                r = log_error_errno(errno, "Failed to truncate file: %m");
+                                                goto finish;
+                                        }
+                                }
 
-                        timespec_store(&ut[0], j->mtime);
-                        ut[1] = ut[0];
-                        (void) futimens(j->disk_fd, ut);
+                                if (j->etag)
+                                        (void) fsetxattr(j->disk_fd, "user.source_etag", j->etag, strlen(j->etag), 0);
+                                if (j->url)
+                                        (void) fsetxattr(j->disk_fd, "user.source_url", j->url, strlen(j->url), 0);
 
-                        (void) fd_setcrtime(j->disk_fd, j->mtime);
+                                if (j->mtime != 0) {
+                                        struct timespec ut;
+
+                                        timespec_store(&ut, j->mtime);
+
+                                        if (futimens(j->disk_fd, (struct timespec[]) { ut, ut }) < 0)
+                                                log_debug_errno(errno, "Failed to adjust atime/mtime of created image, ignoring: %m");
+
+                                        r = fd_setcrtime(j->disk_fd, j->mtime);
+                                        if (r < 0)
+                                                log_debug_errno(r, "Failed to adjust crtime of created image, ignoring: %m");
+                                }
+                        }
+
+                        if (j->sync) {
+                                r = fsync_full(j->disk_fd);
+                                if (r < 0) {
+                                        log_error_errno(r, "Failed to synchronize file to disk: %m");
+                                        goto finish;
+                                }
+                        }
+
+                } else if (S_ISBLK(j->disk_stat.st_mode) && j->sync) {
+
+                        if (fsync(j->disk_fd) < 0) {
+                                r = log_error_errno(errno, "Failed to synchronize block device: %m");
+                                goto finish;
+                        }
                 }
         }
 
+        log_info("Acquired %s.", FORMAT_BYTES(j->written_uncompressed));
+
         r = 0;
 
 finish:
@@ -234,37 +282,46 @@ finish:
 
 static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) {
         PullJob *j = userdata;
-        ssize_t n;
+        bool too_much = false;
+        int r;
 
         assert(j);
         assert(p);
+        assert(sz > 0);
 
-        if (sz <= 0)
-                return 0;
+        if (j->written_uncompressed > UINT64_MAX - sz)
+                return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow");
 
-        if (j->written_uncompressed + sz < j->written_uncompressed)
-                return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW),
-                                       "File too large, overflow");
+        if (j->written_uncompressed >= j->uncompressed_max) {
+                too_much = true;
+                goto finish;
+        }
 
-        if (j->written_uncompressed + sz > j->uncompressed_max)
-                return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
-                                       "File overly large, refusing");
+        if (j->written_uncompressed + sz > j->uncompressed_max) {
+                too_much = true;
+                sz = j->uncompressed_max - j->written_uncompressed; /* since we have the data in memory
+                                                                     * already, we might as well write it to
+                                                                     * disk to the max */
+        }
 
         if (j->disk_fd >= 0) {
 
-                if (j->allow_sparse)
+                if (S_ISREG(j->disk_stat.st_mode) && j->offset == UINT64_MAX) {
+                        ssize_t n;
+
                         n = sparse_write(j->disk_fd, p, sz, 64);
-                else {
-                        n = write(j->disk_fd, p, sz);
                         if (n < 0)
-                                n = -errno;
+                                return log_error_errno((int) n, "Failed to write file: %m");
+                        if ((size_t) n < sz)
+                                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
+                } else {
+                        r = loop_write(j->disk_fd, p, sz, false);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to write file: %m");
                 }
-                if (n < 0)
-                        return log_error_errno((int) n, "Failed to write file: %m");
-                if ((size_t) n < sz)
-                        return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
-        } else {
+        }
 
+        if (j->disk_fd < 0 || j->force_memory) {
                 if (!GREEDY_REALLOC(j->payload, j->payload_size + sz))
                         return log_oom();
 
@@ -274,6 +331,10 @@ static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata)
 
         j->written_uncompressed += sz;
 
+finish:
+        if (too_much)
+                return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing.");
+
         return 0;
 }
 
@@ -321,15 +382,12 @@ static int pull_job_open_disk(PullJob *j) {
         }
 
         if (j->disk_fd >= 0) {
-                /* Check if we can do sparse files */
+                if (fstat(j->disk_fd, &j->disk_stat) < 0)
+                        return log_error_errno(errno, "Failed to stat disk file: %m");
 
-                if (lseek(j->disk_fd, SEEK_SET, 0) == 0)
-                        j->allow_sparse = true;
-                else {
-                        if (errno != ESPIPE)
+                if (j->offset != UINT64_MAX) {
+                        if (lseek(j->disk_fd, j->offset, SEEK_SET) == (off_t) -1)
                                 return log_error_errno(errno, "Failed to seek on file descriptor: %m");
-
-                        j->allow_sparse = false;
                 }
         }
 
@@ -421,7 +479,7 @@ static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb,
                 goto fail;
 
         default:
-                assert_not_reached("Impossible state.");
+                assert_not_reached();
         }
 
         return sz;
@@ -461,8 +519,7 @@ static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb
 
         code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
         if (code != CURLE_OK) {
-                log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
-                r = -EIO;
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code));
                 goto fail;
         }
 
@@ -576,7 +633,12 @@ static int pull_job_progress_callback(void *userdata, curl_off_t dltotal, curl_o
         return 0;
 }
 
-int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) {
+int pull_job_new(
+                PullJob **ret,
+                const char *url,
+                CurlGlue *glue,
+                void *userdata) {
+
         _cleanup_(pull_job_unrefp) PullJob *j = NULL;
         _cleanup_free_ char *u = NULL;
 
@@ -595,6 +657,7 @@ int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata)
         *j = (PullJob) {
                 .state = PULL_JOB_INIT,
                 .disk_fd = -1,
+                .close_disk_fd = true,
                 .userdata = userdata,
                 .glue = glue,
                 .content_length = UINT64_MAX,
@@ -602,6 +665,8 @@ int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata)
                 .compressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
                 .uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
                 .url = TAKE_PTR(u),
+                .offset = UINT64_MAX,
+                .sync = true,
         };
 
         *ret = TAKE_PTR(j);
index 023ccc8302fd13c97e62d651affe76a85094697b..bc5258a6934cd83bfdb862761bc7cbfac21f8fb8 100644 (file)
@@ -2,10 +2,12 @@
 #pragma once
 
 #include <gcrypt.h>
+#include <sys/stat.h>
 
 #include "curl-util.h"
 #include "import-compress.h"
 #include "macro.h"
+#include "pull-common.h"
 
 typedef struct PullJob PullJob;
 
@@ -51,6 +53,7 @@ struct PullJob {
         uint64_t content_length;
         uint64_t written_compressed;
         uint64_t written_uncompressed;
+        uint64_t offset;
 
         uint64_t uncompressed_max;
         uint64_t compressed_max;
@@ -59,6 +62,8 @@ struct PullJob {
         size_t payload_size;
 
         int disk_fd;
+        bool close_disk_fd;
+        struct stat disk_stat;
 
         usec_t mtime;
 
@@ -68,12 +73,12 @@ struct PullJob {
         usec_t start_usec;
         usec_t last_status_usec;
 
-        bool allow_sparse;
-
         bool calc_checksum;
         gcry_md_hd_t checksum_context;
 
         char *checksum;
+        bool sync;
+        bool force_memory;
 };
 
 int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata);
@@ -83,4 +88,6 @@ int pull_job_begin(PullJob *j);
 
 void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result);
 
+void pull_job_close_disk_fd(PullJob *j);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(PullJob*, pull_job_unref);
index 9b5d8ef9e30c009e96b40f9273f5ec30a0a0612f..6a0c2c8b1778faae636333ef042e6e2e35dc0d2a 100644 (file)
@@ -15,6 +15,7 @@
 #include "hostname-util.h"
 #include "import-common.h"
 #include "import-util.h"
+#include "install-file.h"
 #include "macro.h"
 #include "mkdir.h"
 #include "path-util.h"
@@ -46,6 +47,8 @@ struct RawPull {
         ImportVerify verify;
         char *image_root;
 
+        uint64_t offset;
+
         PullJob *raw_job;
         PullJob *checksum_job;
         PullJob *signature_job;
@@ -57,7 +60,8 @@ struct RawPull {
         RawPullFinished on_finished;
         void *userdata;
 
-        char *local;
+        char *local; /* In PULL_DIRECT mode the path we are supposed to place things in, otherwise the
+                      * machine name of the final copy we make */
 
         char *final_path;
         char *temp_path;
@@ -73,6 +77,8 @@ struct RawPull {
 
         char *verity_path;
         char *verity_temp_path;
+
+        char *checksum;
 };
 
 RawPull* raw_pull_unref(RawPull *i) {
@@ -103,6 +109,7 @@ RawPull* raw_pull_unref(RawPull *i) {
         free(i->verity_path);
         free(i->image_root);
         free(i->local);
+        free(i->checksum);
 
         return mfree(i);
 }
@@ -148,6 +155,7 @@ int raw_pull_new(
                 .image_root = TAKE_PTR(root),
                 .event = TAKE_PTR(e),
                 .glue = TAKE_PTR(g),
+                .offset = UINT64_MAX,
         };
 
         i->glue->on_finished = pull_job_curl_on_finished;
@@ -222,7 +230,7 @@ static void raw_pull_report_progress(RawPull *i, RawProgress p) {
                 break;
 
         default:
-                assert_not_reached("Unknown progress state");
+                assert_not_reached();
         }
 
         sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
@@ -230,12 +238,20 @@ static void raw_pull_report_progress(RawPull *i, RawProgress p) {
 }
 
 static int raw_pull_maybe_convert_qcow2(RawPull *i) {
+        _cleanup_(unlink_and_freep) char *t = NULL;
         _cleanup_close_ int converted_fd = -1;
-        _cleanup_free_ char *t = NULL;
+        _cleanup_free_ char *f = NULL;
         int r;
 
         assert(i);
         assert(i->raw_job);
+        assert(!FLAGS_SET(i->flags, PULL_DIRECT));
+
+        if (!FLAGS_SET(i->flags, PULL_CONVERT_QCOW2))
+                return 0;
+
+        assert(i->final_path);
+        assert(i->raw_job->close_disk_fd);
 
         r = qcow2_detect(i->raw_job->disk_fd);
         if (r < 0)
@@ -244,32 +260,35 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) {
                 return 0;
 
         /* This is a QCOW2 image, let's convert it */
-        r = tempfn_random(i->final_path, NULL, &t);
+        r = tempfn_random(i->final_path, NULL, &f);
         if (r < 0)
                 return log_oom();
 
-        converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+        converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
         if (converted_fd < 0)
-                return log_error_errno(errno, "Failed to create %s: %m", t);
+                return log_error_errno(errno, "Failed to create %s: %m", f);
+
+        t = TAKE_PTR(f);
 
         (void) import_set_nocow_and_log(converted_fd, t);
 
         log_info("Unpacking QCOW2 file.");
 
         r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
-        if (r < 0) {
-                (void) unlink(t);
+        if (r < 0)
                 return log_error_errno(r, "Failed to convert qcow2 image: %m");
-        }
 
-        (void) unlink(i->temp_path);
-        free_and_replace(i->temp_path, t);
+        unlink_and_free(i->temp_path);
+        i->temp_path = TAKE_PTR(t);
         CLOSE_AND_REPLACE(i->raw_job->disk_fd, converted_fd);
 
         return 1;
 }
 
-static int raw_pull_determine_path(RawPull *i, const char *suffix, char **field) {
+static int raw_pull_determine_path(
+                RawPull *i,
+                const char *suffix,
+                char **field /* input + output (!) */) {
         int r;
 
         assert(i);
@@ -290,7 +309,7 @@ static int raw_pull_determine_path(RawPull *i, const char *suffix, char **field)
 static int raw_pull_copy_auxiliary_file(
                 RawPull *i,
                 const char *suffix,
-                char **path) {
+                char **path /* input + output (!) */) {
 
         const char *local;
         int r;
@@ -305,7 +324,14 @@ static int raw_pull_copy_auxiliary_file(
 
         local = strjoina(i->image_root, "/", i->local, suffix);
 
-        r = copy_file_atomic(*path, local, 0644, 0, 0, COPY_REFLINK | (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0));
+        r = copy_file_atomic(
+                        *path,
+                        local,
+                        0644,
+                        0, 0,
+                        COPY_REFLINK |
+                        (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) |
+                        (FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 0));
         if (r == -EEXIST)
                 log_warning_errno(r, "File %s already exists, not replacing.", local);
         else if (r == -ENOENT)
@@ -319,13 +345,15 @@ static int raw_pull_copy_auxiliary_file(
 }
 
 static int raw_pull_make_local_copy(RawPull *i) {
-        _cleanup_free_ char *tp = NULL;
+        _cleanup_(unlink_and_freep) char *tp = NULL;
+        _cleanup_free_ char *f = NULL;
         _cleanup_close_ int dfd = -1;
         const char *p;
         int r;
 
         assert(i);
         assert(i->raw_job);
+        assert(!FLAGS_SET(i->flags, PULL_DIRECT));
 
         if (!i->local)
                 return 0;
@@ -342,6 +370,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
                 /* We freshly downloaded the image, use it */
 
                 assert(i->raw_job->disk_fd >= 0);
+                assert(i->offset == UINT64_MAX);
 
                 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
                         return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
@@ -349,38 +378,38 @@ static int raw_pull_make_local_copy(RawPull *i) {
 
         p = strjoina(i->image_root, "/", i->local, ".raw");
 
-        if (FLAGS_SET(i->flags, PULL_FORCE))
-                (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
-
-        r = tempfn_random(p, NULL, &tp);
+        r = tempfn_random(p, NULL, &f);
         if (r < 0)
                 return log_oom();
 
-        dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+        dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
         if (dfd < 0)
                 return log_error_errno(errno, "Failed to create writable copy of image: %m");
 
+        tp = TAKE_PTR(f);
+
         /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
          * since it reduces fragmentation caused by not allowing in-place writes. */
         (void) import_set_nocow_and_log(dfd, tp);
 
         r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
-        if (r < 0) {
-                (void) unlink(tp);
+        if (r < 0)
                 return log_error_errno(r, "Failed to make writable copy of image: %m");
-        }
 
         (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
-        (void) copy_xattr(i->raw_job->disk_fd, dfd);
+        (void) copy_xattr(i->raw_job->disk_fd, dfd, 0);
 
         dfd = safe_close(dfd);
 
-        r = rename(tp, p);
-        if (r < 0)  {
-                r = log_error_errno(errno, "Failed to move writable image into place: %m");
-                (void) unlink(tp);
-                return r;
-        }
+        r = install_file(AT_FDCWD, tp,
+                         AT_FDCWD, p,
+                         (i->flags & PULL_FORCE ? INSTALL_REPLACE : 0) |
+                         (i->flags & PULL_READ_ONLY ? INSTALL_READ_ONLY : 0) |
+                         (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
+        if (r < 0)
+                return log_error_errno(errno, "Failed to move local image into place '%s': %m", p);
+
+        tp = mfree(tp);
 
         log_info("Created new local image '%s'.", i->local);
 
@@ -442,9 +471,10 @@ static int raw_pull_rename_auxiliary_file(
         int r;
 
         assert(i);
+        assert(path);
         assert(temp_path);
+        assert(*temp_path);
         assert(suffix);
-        assert(path);
 
         /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should
          * incorporate it in the file name if we can */
@@ -453,61 +483,78 @@ static int raw_pull_rename_auxiliary_file(
         if (r < 0)
                 return r;
 
-        r = import_make_read_only(*temp_path);
+        r = install_file(
+                        AT_FDCWD, *temp_path,
+                        AT_FDCWD, *path,
+                        INSTALL_READ_ONLY|
+                        (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
         if (r < 0)
-                return r;
-
-        r = rename_noreplace(AT_FDCWD, *temp_path, AT_FDCWD, *path);
-        if (r < 0)
-                return log_error_errno(r, "Failed to rename file %s to %s: %m", *temp_path, *path);
+                return log_error_errno(r, "Failed to move '%s' into place: %m", *path);
 
         *temp_path = mfree(*temp_path);
-
         return 1;
 }
 
 static void raw_pull_job_on_finished(PullJob *j) {
         RawPull *i;
+        PullJob *jj;
         int r;
 
         assert(j);
         assert(j->userdata);
 
         i = j->userdata;
-        if (j == i->settings_job) {
-                if (j->error != 0)
+
+        if (j->error != 0) {
+                /* Only the main job and the checksum job are fatal if they fail. The other fails are just
+                 * "decoration", that we'll download if we can. The signature job isn't fatal here because we
+                 * might not actually need it in case Suse style signatures are used, that are inline in the
+                 * checksum file. */
+
+                if (j == i->raw_job) {
+                        if (j->error == ENOMEDIUM) /* HTTP 404 */
+                                r = log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
+                        else
+                                r = log_error_errno(j->error, "Failed to retrieve image file.");
+                        goto finish;
+                } else if (j == i->checksum_job) {
+                        r = log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
+                        goto finish;
+                } else if (j == i->signature_job)
+                        log_debug_errno(j->error, "Signature job for %s failed, proceeding for now.", j->url);
+                else if (j == i->settings_job)
                         log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
-        } else if (j == i->roothash_job) {
-                if (j->error != 0)
+                else if (j == i->roothash_job)
                         log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without.");
-        } else if (j == i->roothash_signature_job) {
-                if (j->error != 0)
+                else if (j == i->roothash_signature_job)
                         log_info_errno(j->error, "Root hash signature file could not be retrieved, proceeding without.");
-        } else if (j == i->verity_job) {
-                if (j->error != 0)
-                        log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without. %s", j->url);
-        } else if (j->error != 0 && j != i->signature_job) {
-                if (j == i->checksum_job)
-                        log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
+                else if (j == i->verity_job)
+                        log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without.");
                 else
-                        log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
-
-                r = j->error;
-                goto finish;
+                        assert_not_reached();
         }
 
         /* This is invoked if either the download completed successfully, or the download was skipped because
          * we already have the etag. In this case ->etag_exists is true.
          *
-         * We only do something when we got all three files */
+         * We only do something when we got all files */
 
         if (!raw_pull_is_done(i))
                 return;
 
         if (i->signature_job && i->signature_job->error != 0) {
                 VerificationStyle style;
+                PullJob *verify_job;
+
+                /* The signature job failed. Let's see if we actually need it */
+
+                verify_job = i->checksum_job ?: i->raw_job; /* if the checksum job doesn't exist this must be
+                                                             * because the main job is the checksum file
+                                                             * itself */
+
+                assert(verify_job);
 
-                r = verification_style_from_url(i->checksum_job->url, &style);
+                r = verification_style_from_url(verify_job->url, &style);
                 if (r < 0) {
                         log_error_errno(r, "Failed to determine verification style from checksum URL: %m");
                         goto finish;
@@ -517,32 +564,21 @@ static void raw_pull_job_on_finished(PullJob *j) {
                                                             * in per-directory verification mode, since only
                                                             * then the signature is detached, and thus a file
                                                             * of its own. */
-                        log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
-                        r = i->signature_job->error;
+                        r = log_error_errno(i->signature_job->error,
+                                            "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
                         goto finish;
                 }
         }
 
-        if (i->settings_job)
-                i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
-        if (i->roothash_job)
-                i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd);
-        if (i->roothash_signature_job)
-                i->roothash_signature_job->disk_fd = safe_close(i->roothash_signature_job->disk_fd);
-        if (i->verity_job)
-                i->verity_job->disk_fd = safe_close(i->verity_job->disk_fd);
-
-        r = raw_pull_determine_path(i, ".raw", &i->final_path);
-        if (r < 0)
-                goto finish;
+        /* Let's close these auxiliary files now, we don't need access to them anymore. */
+        FOREACH_POINTER(jj, i->settings_job, i->roothash_job, i->roothash_signature_job, i->verity_job)
+                pull_job_close_disk_fd(jj);
 
         if (!i->raw_job->etag_exists) {
-                /* This is a new download, verify it, and move it into place */
-                assert(i->raw_job->disk_fd >= 0);
-
                 raw_pull_report_progress(i, RAW_VERIFYING);
 
                 r = pull_verify(i->verify,
+                                i->checksum,
                                 i->raw_job,
                                 i->checksum_job,
                                 i->signature_job,
@@ -552,50 +588,91 @@ static void raw_pull_job_on_finished(PullJob *j) {
                                 i->verity_job);
                 if (r < 0)
                         goto finish;
+        }
 
-                raw_pull_report_progress(i, RAW_UNPACKING);
+        if (i->flags & PULL_DIRECT) {
+                assert(!i->settings_job);
+                assert(!i->roothash_job);
+                assert(!i->roothash_signature_job);
+                assert(!i->verity_job);
+
+                raw_pull_report_progress(i, RAW_FINALIZING);
 
-                r = raw_pull_maybe_convert_qcow2(i);
+                if (i->local) {
+                        r = install_file(AT_FDCWD, i->local,
+                                         AT_FDCWD, NULL,
+                                         ((i->flags & PULL_READ_ONLY) && i->offset == UINT64_MAX ? INSTALL_READ_ONLY : 0) |
+                                         (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to finalize raw file to '%s': %m", i->local);
+                                goto finish;
+                        }
+                }
+        } else {
+                r = raw_pull_determine_path(i, ".raw", &i->final_path);
                 if (r < 0)
                         goto finish;
 
-                raw_pull_report_progress(i, RAW_FINALIZING);
+                if (!i->raw_job->etag_exists) {
+                        /* This is a new download, verify it, and move it into place */
+
+                        assert(i->temp_path);
+                        assert(i->final_path);
 
-                if (i->raw_job->etag) {
-                        /* Only make a read-only copy if ETag header is set. */
-                        r = import_make_read_only_fd(i->raw_job->disk_fd);
+                        raw_pull_report_progress(i, RAW_UNPACKING);
+
+                        r = raw_pull_maybe_convert_qcow2(i);
                         if (r < 0)
                                 goto finish;
 
-                        r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+                        raw_pull_report_progress(i, RAW_FINALIZING);
+
+                        r = install_file(AT_FDCWD, i->temp_path,
+                                         AT_FDCWD, i->final_path,
+                                         INSTALL_READ_ONLY|
+                                         (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
                         if (r < 0) {
-                                log_error_errno(r, "Failed to rename raw file to %s: %m", i->final_path);
+                                log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);
                                 goto finish;
                         }
-                }
 
-                i->temp_path = mfree(i->temp_path);
+                        i->temp_path = mfree(i->temp_path);
 
-                if (i->roothash_job &&
-                    i->roothash_job->error == 0) {
-                        r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path);
-                        if (r < 0)
-                                goto finish;
-                }
+                        if (i->settings_job &&
+                            i->settings_job->error == 0) {
+                                r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path);
+                                if (r < 0)
+                                        goto finish;
+                        }
 
-                if (i->settings_job &&
-                    i->settings_job->error == 0) {
-                        r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path);
-                        if (r < 0)
-                                goto finish;
+                        if (i->roothash_job &&
+                            i->roothash_job->error == 0) {
+                                r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path);
+                                if (r < 0)
+                                        goto finish;
+                        }
+
+                        if (i->roothash_signature_job &&
+                            i->roothash_signature_job->error == 0) {
+                                r = raw_pull_rename_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_temp_path, &i->roothash_signature_path);
+                                if (r < 0)
+                                        goto finish;
+                        }
+
+                        if (i->verity_job &&
+                            i->verity_job->error == 0) {
+                                r = raw_pull_rename_auxiliary_file(i, ".verity", &i->verity_temp_path, &i->verity_path);
+                                if (r < 0)
+                                        goto finish;
+                        }
                 }
-        }
 
-        raw_pull_report_progress(i, RAW_COPYING);
+                raw_pull_report_progress(i, RAW_COPYING);
 
-        r = raw_pull_make_local_copy(i);
-        if (r < 0)
-                goto finish;
+                r = raw_pull_make_local_copy(i);
+                if (r < 0)
+                        goto finish;
+        }
 
         r = 0;
 
@@ -610,7 +687,7 @@ static int raw_pull_job_on_open_disk_generic(
                 RawPull *i,
                 PullJob *j,
                 const char *extra,
-                char **temp_path) {
+                char **temp_path /* input + output */) {
 
         int r;
 
@@ -619,6 +696,8 @@ static int raw_pull_job_on_open_disk_generic(
         assert(extra);
         assert(temp_path);
 
+        assert(!FLAGS_SET(i->flags, PULL_DIRECT));
+
         if (!*temp_path) {
                 r = tempfn_random_child(i->image_root, extra, temp_path);
                 if (r < 0)
@@ -643,12 +722,34 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) {
 
         i = j->userdata;
         assert(i->raw_job == j);
+        assert(j->disk_fd < 0);
 
-        r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
-        if (r < 0)
-                return r;
+        if (i->flags & PULL_DIRECT) {
+
+                if (!i->local) { /* If no local name specified, the pull job will write its data to stdout */
+                        j->disk_fd = STDOUT_FILENO;
+                        j->close_disk_fd = false;
+                        return 0;
+                }
+
+                (void) mkdir_parents_label(i->local, 0700);
+
+                j->disk_fd = open(i->local, O_RDWR|O_NOCTTY|O_CLOEXEC|(i->offset == UINT64_MAX ? O_TRUNC|O_CREAT : 0), 0664);
+                if (j->disk_fd < 0)
+                        return log_error_errno(errno, "Failed to open destination '%s': %m", i->local);
+
+                if (i->offset == UINT64_MAX)
+                        (void) import_set_nocow_and_log(j->disk_fd, i->local);
+
+        } else {
+                r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
+                if (r < 0)
+                        return r;
+
+                assert(i->offset == UINT64_MAX);
+                (void) import_set_nocow_and_log(j->disk_fd, i->temp_path);
+        }
 
-        (void) import_set_nocow_and_log(j->disk_fd, i->temp_path);
         return 0;
 }
 
@@ -715,20 +816,29 @@ int raw_pull_start(
                 RawPull *i,
                 const char *url,
                 const char *local,
+                uint64_t offset,
+                uint64_t size_max,
                 PullFlags flags,
-                ImportVerify verify) {
+                ImportVerify verify,
+                const char *checksum) {
 
+        PullJob *j;
         int r;
 
         assert(i);
-        assert(verify < _IMPORT_VERIFY_MAX);
-        assert(verify >= 0);
+        assert(url);
+        assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
+        assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
+        assert((verify < 0) || !checksum);
         assert(!(flags & ~PULL_FLAGS_MASK_RAW));
+        assert(offset == UINT64_MAX || FLAGS_SET(flags, PULL_DIRECT));
+        assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !(flags & PULL_DIRECT));
+        assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !checksum);
 
-        if (!http_url_is_valid(url))
+        if (!http_url_is_valid(url) && !file_url_is_valid(url))
                 return -EINVAL;
 
-        if (local && !hostname_is_valid(local, 0))
+        if (local && !pull_validate_local(local, flags))
                 return -EINVAL;
 
         if (i->raw_job)
@@ -738,6 +848,10 @@ int raw_pull_start(
         if (r < 0)
                 return r;
 
+        r = free_and_strdup(&i->checksum, checksum);
+        if (r < 0)
+                return r;
+
         i->flags = flags;
         i->verify = verify;
 
@@ -748,98 +862,121 @@ int raw_pull_start(
 
         i->raw_job->on_finished = raw_pull_job_on_finished;
         i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
-        i->raw_job->on_progress = raw_pull_job_on_progress;
-        i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
-
-        r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
-        if (r < 0)
-                return r;
-
-        r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i);
-        if (r < 0)
-                return r;
-
-        if (FLAGS_SET(flags, PULL_SETTINGS)) {
-                r = pull_make_auxiliary_job(&i->settings_job, url, raw_strip_suffixes, ".nspawn", i->glue, raw_pull_job_on_finished, i);
-                if (r < 0)
-                        return r;
 
-                i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings;
-                i->settings_job->on_progress = raw_pull_job_on_progress;
-                i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
-        }
+        if (checksum)
+                i->raw_job->calc_checksum = true;
+        else if (verify != IMPORT_VERIFY_NO) {
+                /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its
+                 * signature, which we let gpg verify instead. */
 
-        if (FLAGS_SET(flags, PULL_ROOTHASH)) {
-                r = pull_make_auxiliary_job(&i->roothash_job, url, raw_strip_suffixes, ".roothash", i->glue, raw_pull_job_on_finished, i);
+                r = pull_url_needs_checksum(url);
                 if (r < 0)
                         return r;
 
-                i->roothash_job->on_open_disk = raw_pull_job_on_open_disk_roothash;
-                i->roothash_job->on_progress = raw_pull_job_on_progress;
-                i->roothash_job->calc_checksum = verify != IMPORT_VERIFY_NO;
+                i->raw_job->calc_checksum = r;
+                i->raw_job->force_memory = true; /* make sure this is both written to disk if that's
+                                                  * requested and into memory, since we need to verify it */
         }
 
-        if (FLAGS_SET(flags, PULL_ROOTHASH_SIGNATURE)) {
-                r = pull_make_auxiliary_job(&i->roothash_signature_job, url, raw_strip_suffixes, ".roothash.p7s", i->glue, raw_pull_job_on_finished, i);
-                if (r < 0)
-                        return r;
-
-                i->roothash_signature_job->on_open_disk = raw_pull_job_on_open_disk_roothash_signature;
-                i->roothash_signature_job->on_progress = raw_pull_job_on_progress;
-                i->roothash_signature_job->calc_checksum = verify != IMPORT_VERIFY_NO;
-        }
+        if (size_max != UINT64_MAX)
+                i->raw_job->uncompressed_max = size_max;
+        if (offset != UINT64_MAX)
+                i->raw_job->offset = i->offset = offset;
 
-        if (FLAGS_SET(flags, PULL_VERITY)) {
-                r = pull_make_auxiliary_job(&i->verity_job, url, raw_strip_suffixes, ".verity", i->glue, raw_pull_job_on_finished, i);
+        if (!FLAGS_SET(flags, PULL_DIRECT)) {
+                r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
                 if (r < 0)
                         return r;
-
-                i->verity_job->on_open_disk = raw_pull_job_on_open_disk_verity;
-                i->verity_job->on_progress = raw_pull_job_on_progress;
-                i->verity_job->calc_checksum = verify != IMPORT_VERIFY_NO;
         }
 
-        r = pull_job_begin(i->raw_job);
+        r = pull_make_verification_jobs(
+                        &i->checksum_job,
+                        &i->signature_job,
+                        verify,
+                        i->checksum,
+                        url,
+                        i->glue,
+                        raw_pull_job_on_finished,
+                        i);
         if (r < 0)
                 return r;
 
-        if (i->checksum_job) {
-                i->checksum_job->on_progress = raw_pull_job_on_progress;
-                i->checksum_job->on_not_found = pull_job_restart_with_sha256sum;
-
-                r = pull_job_begin(i->checksum_job);
+        if (FLAGS_SET(flags, PULL_SETTINGS)) {
+                r = pull_make_auxiliary_job(
+                                &i->settings_job,
+                                url,
+                                raw_strip_suffixes,
+                                ".nspawn",
+                                verify,
+                                i->glue,
+                                raw_pull_job_on_open_disk_settings,
+                                raw_pull_job_on_finished,
+                                i);
                 if (r < 0)
                         return r;
         }
 
-        if (i->signature_job) {
-                i->signature_job->on_progress = raw_pull_job_on_progress;
-
-                r = pull_job_begin(i->signature_job);
+        if (FLAGS_SET(flags, PULL_ROOTHASH)) {
+                r = pull_make_auxiliary_job(
+                                &i->roothash_job,
+                                url,
+                                raw_strip_suffixes,
+                                ".roothash",
+                                verify,
+                                i->glue,
+                                raw_pull_job_on_open_disk_roothash,
+                                raw_pull_job_on_finished,
+                                i);
                 if (r < 0)
                         return r;
         }
 
-        if (i->settings_job) {
-                r = pull_job_begin(i->settings_job);
+        if (FLAGS_SET(flags, PULL_ROOTHASH_SIGNATURE)) {
+                r = pull_make_auxiliary_job(
+                                &i->roothash_signature_job,
+                                url,
+                                raw_strip_suffixes,
+                                ".roothash.p7s",
+                                verify,
+                                i->glue,
+                                raw_pull_job_on_open_disk_roothash_signature,
+                                raw_pull_job_on_finished,
+                                i);
                 if (r < 0)
                         return r;
         }
 
-        if (i->roothash_job) {
-                r = pull_job_begin(i->roothash_job);
+        if (FLAGS_SET(flags, PULL_VERITY)) {
+                r = pull_make_auxiliary_job(
+                                &i->verity_job,
+                                url,
+                                raw_strip_suffixes,
+                                ".verity",
+                                verify,
+                                i->glue,
+                                raw_pull_job_on_open_disk_verity,
+                                raw_pull_job_on_finished,
+                                i);
                 if (r < 0)
                         return r;
         }
 
-        if (i->roothash_signature_job) {
-                r = pull_job_begin(i->roothash_signature_job);
-                if (r < 0)
-                        return r;
-        }
+        FOREACH_POINTER(j,
+                        i->raw_job,
+                        i->checksum_job,
+                        i->signature_job,
+                        i->settings_job,
+                        i->roothash_job,
+                        i->roothash_signature_job,
+                        i->verity_job) {
+
+                if (!j)
+                        continue;
+
+                j->on_progress = raw_pull_job_on_progress;
+                j->sync = FLAGS_SET(flags, PULL_SYNC);
 
-        if (i->verity_job) {
-                r = pull_job_begin(i->verity_job);
+                r = pull_job_begin(j);
                 if (r < 0)
                         return r;
         }
index 985bda4762ce8e58d4a5f112a3789f063e061b9d..b39e4e257cd4a9008277a2a3ad83a20f556069fd 100644 (file)
@@ -16,4 +16,4 @@ RawPull* raw_pull_unref(RawPull *pull);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref);
 
-int raw_pull_start(RawPull *pull, const char *url, const char *local, PullFlags flags, ImportVerify verify);
+int raw_pull_start(RawPull *pull, const char *url, const char *local, uint64_t offset, uint64_t size_max, PullFlags flags, ImportVerify verify, const char *checksum);
index a2ba56df2f39262a67fe6f1b9c757bc8d54c98ce..06d336bca91ca717f82fbc94204b8e1c84d36f3e 100644 (file)
@@ -14,6 +14,7 @@
 #include "hostname-util.h"
 #include "import-common.h"
 #include "import-util.h"
+#include "install-file.h"
 #include "macro.h"
 #include "mkdir.h"
 #include "path-util.h"
@@ -25,6 +26,7 @@
 #include "string-util.h"
 #include "strv.h"
 #include "tmpfile-util.h"
+#include "user-util.h"
 #include "utf8.h"
 #include "util.h"
 #include "web-util.h"
@@ -61,6 +63,8 @@ struct TarPull {
 
         char *settings_path;
         char *settings_temp_path;
+
+        char *checksum;
 };
 
 TarPull* tar_pull_unref(TarPull *i) {
@@ -87,6 +91,7 @@ TarPull* tar_pull_unref(TarPull *i) {
         free(i->settings_path);
         free(i->image_root);
         free(i->local);
+        free(i->checksum);
 
         return mfree(i);
 }
@@ -187,14 +192,17 @@ static void tar_pull_report_progress(TarPull *i, TarProgress p) {
                 break;
 
         default:
-                assert_not_reached("Unknown progress state");
+                assert_not_reached();
         }
 
         sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
         log_debug("Combined progress %u%%", percent);
 }
 
-static int tar_pull_determine_path(TarPull *i, const char *suffix, char **field) {
+static int tar_pull_determine_path(
+                TarPull *i,
+                const char *suffix,
+                char **field /* input + output (!) */) {
         int r;
 
         assert(i);
@@ -213,6 +221,8 @@ static int tar_pull_determine_path(TarPull *i, const char *suffix, char **field)
 }
 
 static int tar_pull_make_local_copy(TarPull *i) {
+        _cleanup_(rm_rf_subvolume_and_freep) char *t = NULL;
+        const char *p;
         int r;
 
         assert(i);
@@ -221,9 +231,38 @@ static int tar_pull_make_local_copy(TarPull *i) {
         if (!i->local)
                 return 0;
 
-        r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->flags);
+        assert(i->final_path);
+
+        p = prefix_roota(i->image_root, i->local);
+
+        r = tempfn_random(p, NULL, &t);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to generate temporary filename for %s: %m", p);
+
+        if (i->flags & PULL_BTRFS_SUBVOL)
+                r = btrfs_subvol_snapshot(
+                                i->final_path,
+                                t,
+                                (i->flags & PULL_BTRFS_QUOTA ? BTRFS_SNAPSHOT_QUOTA : 0)|
+                                BTRFS_SNAPSHOT_FALLBACK_COPY|
+                                BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|
+                                BTRFS_SNAPSHOT_RECURSIVE);
+        else
+                r = copy_tree(i->final_path, t, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_HARDLINKS);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create local image: %m");
+
+        r = install_file(AT_FDCWD, t,
+                         AT_FDCWD, p,
+                         (i->flags & PULL_FORCE ? INSTALL_REPLACE : 0) |
+                         (i->flags & PULL_READ_ONLY ? INSTALL_READ_ONLY : 0) |
+                         (i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0));
+        if (r < 0)
+                return log_error_errno(r, "Failed to install local image '%s': %m", p);
+
+        t = mfree(t);
+
+        log_info("Created new local image '%s'.", i->local);
 
         if (FLAGS_SET(i->flags, PULL_SETTINGS)) {
                 const char *local_settings;
@@ -235,7 +274,14 @@ static int tar_pull_make_local_copy(TarPull *i) {
 
                 local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
 
-                r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, 0, COPY_REFLINK | (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0));
+                r = copy_file_atomic(
+                                i->settings_path,
+                                local_settings,
+                                0664,
+                                0, 0,
+                                COPY_REFLINK |
+                                (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) |
+                                (FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 0));
                 if (r == -EEXIST)
                         log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
                 else if (r == -ENOENT)
@@ -274,17 +320,22 @@ static void tar_pull_job_on_finished(PullJob *j) {
 
         i = j->userdata;
 
-        if (j == i->settings_job) {
-                if (j->error != 0)
+        if (j->error != 0) {
+                if (j == i->tar_job) {
+                        if (j->error == ENOMEDIUM) /* HTTP 404 */
+                                r = log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
+                        else
+                                r = log_error_errno(j->error, "Failed to retrieve image file.");
+                        goto finish;
+                } else if (j == i->checksum_job) {
+                        r = log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
+                        goto finish;
+                } else if (j == i->signature_job)
+                        log_debug_errno(j->error, "Signature job for %s failed, proceeding for now.", j->url);
+                else if (j == i->settings_job)
                         log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
-        } else if (j->error != 0 && j != i->signature_job) {
-                if (j == i->checksum_job)
-                        log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
                 else
-                        log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
-
-                r = j->error;
-                goto finish;
+                        assert("unexpected job");
         }
 
         /* This is invoked if either the download completed successfully, or the download was skipped because
@@ -296,6 +347,8 @@ static void tar_pull_job_on_finished(PullJob *j) {
         if (i->signature_job && i->signature_job->error != 0) {
                 VerificationStyle style;
 
+                assert(i->checksum_job);
+
                 r = verification_style_from_url(i->checksum_job->url, &style);
                 if (r < 0) {
                         log_error_errno(r, "Failed to determine verification style from checksum URL: %m");
@@ -306,19 +359,14 @@ static void tar_pull_job_on_finished(PullJob *j) {
                                                             * in per-directory verification mode, since only
                                                             * then the signature is detached, and thus a file
                                                             * of its own. */
-                        log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
-                        r = i->signature_job->error;
+                        r = log_error_errno(i->signature_job->error,
+                                            "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
                         goto finish;
                 }
         }
 
-        i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd);
-        if (i->settings_job)
-                i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
-
-        r = tar_pull_determine_path(i, NULL, &i->final_path);
-        if (r < 0)
-                goto finish;
+        pull_job_close_disk_fd(i->tar_job);
+        pull_job_close_disk_fd(i->settings_job);
 
         if (i->tar_pid > 0) {
                 r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG);
@@ -337,6 +385,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
                 tar_pull_report_progress(i, TAR_VERIFYING);
 
                 r = pull_verify(i->verify,
+                                i->checksum,
                                 i->tar_job,
                                 i->checksum_job,
                                 i->signature_job,
@@ -346,59 +395,92 @@ static void tar_pull_job_on_finished(PullJob *j) {
                                 /* verity_job = */ NULL);
                 if (r < 0)
                         goto finish;
+        }
 
-                tar_pull_report_progress(i, TAR_FINALIZING);
+        if (i->flags & PULL_DIRECT) {
+                assert(!i->settings_job);
+                assert(i->local);
+                assert(!i->temp_path);
 
-                r = import_mangle_os_tree(i->temp_path);
-                if (r < 0)
-                        goto finish;
+                tar_pull_report_progress(i, TAR_FINALIZING);
 
-                r = import_make_read_only(i->temp_path);
+                r = import_mangle_os_tree(i->local);
                 if (r < 0)
                         goto finish;
 
-                r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+                r = install_file(
+                                AT_FDCWD, i->local,
+                                AT_FDCWD, NULL,
+                                (i->flags & PULL_READ_ONLY) ? INSTALL_READ_ONLY : 0 |
+                                (i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0));
                 if (r < 0) {
-                        log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path);
+                        log_error_errno(r, "Failed to finalize '%s': %m", i->local);
                         goto finish;
                 }
+        } else {
+                r = tar_pull_determine_path(i, NULL, &i->final_path);
+                if (r < 0)
+                        goto finish;
 
-                i->temp_path = mfree(i->temp_path);
-
-                if (i->settings_job &&
-                    i->settings_job->error == 0) {
+                if (!i->tar_job->etag_exists) {
+                        /* This is a new download, verify it, and move it into place */
 
-                        /* Also move the settings file into place, if it exists. Note that we do so only if we also
-                         * moved the tar file in place, to keep things strictly in sync. */
-                        assert(i->settings_temp_path);
+                        assert(i->temp_path);
+                        assert(i->final_path);
 
-                        /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and
-                         * we should incorporate it in the file name if we can */
-                        i->settings_path = mfree(i->settings_path);
+                        tar_pull_report_progress(i, TAR_FINALIZING);
 
-                        r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
+                        r = import_mangle_os_tree(i->temp_path);
                         if (r < 0)
                                 goto finish;
 
-                        r = import_make_read_only(i->settings_temp_path);
-                        if (r < 0)
-                                goto finish;
-
-                        r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path);
+                        r = install_file(
+                                        AT_FDCWD, i->temp_path,
+                                        AT_FDCWD, i->final_path,
+                                        INSTALL_READ_ONLY|
+                                        (i->flags & PULL_SYNC ? INSTALL_SYNCFS : 0));
                         if (r < 0) {
-                                log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path);
+                                log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path);
                                 goto finish;
                         }
 
-                        i->settings_temp_path = mfree(i->settings_temp_path);
+                        i->temp_path = mfree(i->temp_path);
+
+                        if (i->settings_job &&
+                            i->settings_job->error == 0) {
+
+                                /* Also move the settings file into place, if it exists. Note that we do so only if we also
+                                 * moved the tar file in place, to keep things strictly in sync. */
+                                assert(i->settings_temp_path);
+
+                                /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and
+                                 * we should incorporate it in the file name if we can */
+                                i->settings_path = mfree(i->settings_path);
+
+                                r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
+                                if (r < 0)
+                                        goto finish;
+
+                                r = install_file(
+                                                AT_FDCWD, i->settings_temp_path,
+                                                AT_FDCWD, i->settings_path,
+                                                INSTALL_READ_ONLY|
+                                                (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
+                                if (r < 0) {
+                                        log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path);
+                                        goto finish;
+                                }
+
+                                i->settings_temp_path = mfree(i->settings_temp_path);
+                        }
                 }
-        }
 
-        tar_pull_report_progress(i, TAR_COPYING);
+                tar_pull_report_progress(i, TAR_COPYING);
 
-        r = tar_pull_make_local_copy(i);
-        if (r < 0)
-                goto finish;
+                r = tar_pull_make_local_copy(i);
+                if (r < 0)
+                        goto finish;
+        }
 
         r = 0;
 
@@ -410,6 +492,7 @@ finish:
 }
 
 static int tar_pull_job_on_open_disk_tar(PullJob *j) {
+        const char *where;
         TarPull *i;
         int r;
 
@@ -420,23 +503,39 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
         assert(i->tar_job == j);
         assert(i->tar_pid <= 0);
 
-        if (!i->temp_path) {
-                r = tempfn_random_child(i->image_root, "tar", &i->temp_path);
-                if (r < 0)
-                        return log_oom();
+        if (i->flags & PULL_DIRECT)
+                where = i->local;
+        else {
+                if (!i->temp_path) {
+                        r = tempfn_random_child(i->image_root, "tar", &i->temp_path);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+                where = i->temp_path;
         }
 
-        mkdir_parents_label(i->temp_path, 0700);
+        (void) mkdir_parents_label(where, 0700);
+
+        if (FLAGS_SET(i->flags, PULL_DIRECT|PULL_FORCE))
+                (void) rm_rf(where, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
 
-        r = btrfs_subvol_make_fallback(i->temp_path, 0755);
+        if (i->flags & PULL_BTRFS_SUBVOL)
+                r = btrfs_subvol_make_fallback(where, 0755);
+        else
+                r = mkdir(where, 0755) < 0 ? -errno : 0;
+        if (r == -EEXIST && (i->flags & PULL_DIRECT)) /* EEXIST is OK if in direct mode, but not otherwise,
+                                                       * because in that case our temporary path collided */
+                r = 0;
         if (r < 0)
-                return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path);
-        if (r > 0) { /* actually btrfs subvol */
-                (void) import_assign_pool_quota_and_warn(i->image_root);
-                (void) import_assign_pool_quota_and_warn(i->temp_path);
+                return log_error_errno(r, "Failed to create directory/subvolume %s: %m", where);
+        if (r > 0 && (i->flags & PULL_BTRFS_QUOTA)) { /* actually btrfs subvol */
+                if (!(i->flags & PULL_DIRECT))
+                        (void) import_assign_pool_quota_and_warn(i->image_root);
+                (void) import_assign_pool_quota_and_warn(where);
         }
 
-        j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
+        j->disk_fd = import_fork_tar_x(where, &i->tar_pid);
         if (j->disk_fd < 0)
                 return j->disk_fd;
 
@@ -459,7 +558,7 @@ static int tar_pull_job_on_open_disk_settings(PullJob *j) {
                         return log_oom();
         }
 
-        mkdir_parents_label(i->settings_temp_path, 0700);
+        (void) mkdir_parents_label(i->settings_temp_path, 0700);
 
         j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
         if (j->disk_fd < 0)
@@ -484,19 +583,24 @@ int tar_pull_start(
                 const char *url,
                 const char *local,
                 PullFlags flags,
-                ImportVerify verify) {
+                ImportVerify verify,
+                const char *checksum) {
 
+        PullJob *j;
         int r;
 
         assert(i);
-        assert(verify < _IMPORT_VERIFY_MAX);
-        assert(verify >= 0);
+        assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
+        assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
+        assert((verify < 0) || !checksum);
         assert(!(flags & ~PULL_FLAGS_MASK_TAR));
+        assert(!(flags & PULL_SETTINGS) || !(flags & PULL_DIRECT));
+        assert(!(flags & PULL_SETTINGS) || !checksum);
 
-        if (!http_url_is_valid(url))
+        if (!http_url_is_valid(url) && !file_url_is_valid(url))
                 return -EINVAL;
 
-        if (local && !hostname_is_valid(local, 0))
+        if (local && !pull_validate_local(local, flags))
                 return -EINVAL;
 
         if (i->tar_job)
@@ -506,6 +610,10 @@ int tar_pull_start(
         if (r < 0)
                 return r;
 
+        r = free_and_strdup(&i->checksum, checksum);
+        if (r < 0)
+                return r;
+
         i->flags = flags;
         i->verify = verify;
 
@@ -516,52 +624,56 @@ int tar_pull_start(
 
         i->tar_job->on_finished = tar_pull_job_on_finished;
         i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar;
-        i->tar_job->on_progress = tar_pull_job_on_progress;
-        i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
+        i->tar_job->calc_checksum = checksum || IN_SET(verify, IMPORT_VERIFY_CHECKSUM, IMPORT_VERIFY_SIGNATURE);
 
-        r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
-        if (r < 0)
-                return r;
+        if (!FLAGS_SET(flags, PULL_DIRECT)) {
+                r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
+                if (r < 0)
+                        return r;
+        }
 
         /* Set up download of checksum/signature files */
-        r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
+        r = pull_make_verification_jobs(
+                        &i->checksum_job,
+                        &i->signature_job,
+                        verify,
+                        checksum,
+                        url,
+                        i->glue,
+                        tar_pull_job_on_finished,
+                        i);
         if (r < 0)
                 return r;
 
         /* Set up download job for the settings file (.nspawn) */
         if (FLAGS_SET(flags, PULL_SETTINGS)) {
-                r = pull_make_auxiliary_job(&i->settings_job, url, tar_strip_suffixes, ".nspawn", i->glue, tar_pull_job_on_finished, i);
+                r = pull_make_auxiliary_job(
+                                &i->settings_job,
+                                url,
+                                tar_strip_suffixes,
+                                ".nspawn",
+                                verify,
+                                i->glue,
+                                tar_pull_job_on_open_disk_settings,
+                                tar_pull_job_on_finished,
+                                i);
                 if (r < 0)
                         return r;
-
-                i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings;
-                i->settings_job->on_progress = tar_pull_job_on_progress;
-                i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
         }
 
-        r = pull_job_begin(i->tar_job);
-        if (r < 0)
-                return r;
-
-        if (i->checksum_job) {
-                i->checksum_job->on_progress = tar_pull_job_on_progress;
-                i->checksum_job->on_not_found = pull_job_restart_with_sha256sum;
-
-                r = pull_job_begin(i->checksum_job);
-                if (r < 0)
-                        return r;
-        }
+        FOREACH_POINTER(j,
+                        i->tar_job,
+                        i->checksum_job,
+                        i->signature_job,
+                        i->settings_job) {
 
-        if (i->signature_job) {
-                i->signature_job->on_progress = tar_pull_job_on_progress;
+                if (!j)
+                        continue;
 
-                r = pull_job_begin(i->signature_job);
-                if (r < 0)
-                        return r;
-        }
+                j->on_progress = tar_pull_job_on_progress;
+                j->sync = FLAGS_SET(flags, PULL_SYNC);
 
-        if (i->settings_job) {
-                r = pull_job_begin(i->settings_job);
+                r = pull_job_begin(j);
                 if (r < 0)
                         return r;
         }
index 414077549fdd41c77f3c771097b8f73b41a6a9f4..e54c01c302a4293c10e8dc1bdbc8dfbaa613e7b5 100644 (file)
@@ -16,4 +16,4 @@ TarPull* tar_pull_unref(TarPull *pull);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref);
 
-int tar_pull_start(TarPull *pull, const char *url, const char *local, PullFlags flags, ImportVerify verify);
+int tar_pull_start(TarPull *pull, const char *url, const char *local, PullFlags flags, ImportVerify verify, const char *checksum);
index d24c71b00ecf530d4173306ab91edd1c6797b34c..2cf0cca14ff8857ba21d4c7d1a9222ff84587ff7 100644 (file)
@@ -8,24 +8,87 @@
 
 #include "alloc-util.h"
 #include "discover-image.h"
+#include "env-util.h"
+#include "hexdecoct.h"
 #include "hostname-util.h"
+#include "import-common.h"
 #include "import-util.h"
+#include "io-util.h"
 #include "main-func.h"
+#include "parse-argument.h"
 #include "parse-util.h"
 #include "pull-raw.h"
 #include "pull-tar.h"
 #include "signal-util.h"
 #include "string-util.h"
+#include "terminal-util.h"
 #include "verbs.h"
 #include "web-util.h"
 
 static const char *arg_image_root = "/var/lib/machines";
 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
-static PullFlags arg_pull_flags = PULL_SETTINGS | PULL_ROOTHASH | PULL_ROOTHASH_SIGNATURE | PULL_VERITY;
+static PullFlags arg_pull_flags = PULL_SETTINGS | PULL_ROOTHASH | PULL_ROOTHASH_SIGNATURE | PULL_VERITY | PULL_BTRFS_SUBVOL | PULL_BTRFS_QUOTA | PULL_CONVERT_QCOW2 | PULL_SYNC;
+static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
+static char *arg_checksum = NULL;
 
-static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
-        log_notice("Transfer aborted.");
-        sd_event_exit(sd_event_source_get_event(s), EINTR);
+STATIC_DESTRUCTOR_REGISTER(arg_checksum, freep);
+
+static int normalize_local(const char *local, const char *url, char **ret) {
+        _cleanup_free_ char *ll = NULL;
+        int r;
+
+        if (arg_pull_flags & PULL_DIRECT) {
+
+                if (!local)
+                        log_debug("Writing downloaded data to STDOUT.");
+                else {
+                        if (!path_is_absolute(local)) {
+                                ll = path_join(arg_image_root, local);
+                                if (!ll)
+                                        return log_oom();
+
+                                local = ll;
+                        }
+
+                        if (!path_is_valid(local))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Local path name '%s' is not valid.", local);
+                }
+
+        } else if (local) {
+
+                if (!hostname_is_valid(local, 0))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Local image name '%s' is not valid.",
+                                               local);
+
+                if (!FLAGS_SET(arg_pull_flags, PULL_FORCE)) {
+                        r = image_find(IMAGE_MACHINE, local, NULL, NULL);
+                        if (r < 0) {
+                                if (r != -ENOENT)
+                                        return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
+                        } else
+                                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+                                                       "Image '%s' already exists.",
+                                                       local);
+                }
+        }
+
+        if (!ll && local) {
+                ll = strdup(local);
+                if (!ll)
+                        return log_oom();
+        }
+
+        if (ll) {
+                if (arg_offset != UINT64_MAX)
+                        log_info("Pulling '%s', saving at offset %" PRIu64 " in '%s'.", url, arg_offset, ll);
+                else
+                        log_info("Pulling '%s', saving as '%s'.", url, ll);
+        } else
+                log_info("Pulling '%s'.", url);
+
+        *ret = TAKE_PTR(ll);
         return 0;
 }
 
@@ -40,70 +103,57 @@ static void on_tar_finished(TarPull *pull, int error, void *userdata) {
 }
 
 static int pull_tar(int argc, char *argv[], void *userdata) {
-        _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
+        _cleanup_free_ char *ll = NULL, *normalized = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+        _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
         const char *url, *local;
-        _cleanup_free_ char *l = NULL, *ll = NULL;
         int r;
 
         url = argv[1];
-        if (!http_url_is_valid(url))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "URL '%s' is not valid.", url);
+        if (!http_url_is_valid(url) && !file_url_is_valid(url))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "URL '%s' is not valid.", url);
 
         if (argc >= 3)
-                local = argv[2];
+                local = empty_or_dash_to_null(argv[2]);
         else {
+                _cleanup_free_ char *l = NULL;
+
                 r = import_url_last_component(url, &l);
                 if (r < 0)
-                        return log_error_errno(r, "Failed get final component of URL: %m");
-
-                local = l;
-        }
+                        return log_error_errno(r, "Failed to get final component of URL: %m");
 
-        local = empty_or_dash_to_null(local);
-
-        if (local) {
-                r = tar_strip_suffixes(local, &ll);
+                r = tar_strip_suffixes(l, &ll);
                 if (r < 0)
                         return log_oom();
 
                 local = ll;
+        }
 
-                if (!hostname_is_valid(local, 0))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Local image name '%s' is not valid.",
-                                               local);
-
-                if (!FLAGS_SET(arg_pull_flags, PULL_FORCE)) {
-                        r = image_find(IMAGE_MACHINE, local, NULL, NULL);
-                        if (r < 0) {
-                                if (r != -ENOENT)
-                                        return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
-                        } else {
-                                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
-                                                       "Image '%s' already exists.",
-                                                       local);
-                        }
-                }
+        if (!local && FLAGS_SET(arg_pull_flags, PULL_DIRECT))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Pulling tar images to STDOUT is not supported.");
 
-                log_info("Pulling '%s', saving as '%s'.", url, local);
-        } else
-                log_info("Pulling '%s'.", url);
+        r = normalize_local(local, url, &normalized);
+        if (r < 0)
+                return r;
 
-        r = sd_event_default(&event);
+        r = import_allocate_event_with_signals(&event);
         if (r < 0)
-                return log_error_errno(r, "Failed to allocate event loop: %m");
+                return r;
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
-        (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler,  NULL);
-        (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+        if (!FLAGS_SET(arg_pull_flags, PULL_SYNC))
+                log_info("File system synchronization on completion is off.");
 
         r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate puller: %m");
 
-        r = tar_pull_start(pull, url, local, arg_pull_flags & PULL_FLAGS_MASK_TAR, arg_verify);
+        r = tar_pull_start(
+                        pull,
+                        url,
+                        normalized,
+                        arg_pull_flags & PULL_FLAGS_MASK_TAR,
+                        arg_verify,
+                        arg_checksum);
         if (r < 0)
                 return log_error_errno(r, "Failed to pull image: %m");
 
@@ -126,70 +176,55 @@ static void on_raw_finished(RawPull *pull, int error, void *userdata) {
 }
 
 static int pull_raw(int argc, char *argv[], void *userdata) {
-        _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
+        _cleanup_free_ char *ll = NULL, *normalized = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+        _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
         const char *url, *local;
-        _cleanup_free_ char *l = NULL, *ll = NULL;
         int r;
 
         url = argv[1];
-        if (!http_url_is_valid(url))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "URL '%s' is not valid.", url);
+        if (!http_url_is_valid(url) && !file_url_is_valid(url))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "URL '%s' is not valid.", url);
 
         if (argc >= 3)
-                local = argv[2];
+                local = empty_or_dash_to_null(argv[2]);
         else {
+                _cleanup_free_ char *l = NULL;
+
                 r = import_url_last_component(url, &l);
                 if (r < 0)
-                        return log_error_errno(r, "Failed get final component of URL: %m");
-
-                local = l;
-        }
+                        return log_error_errno(r, "Failed to get final component of URL: %m");
 
-        local = empty_or_dash_to_null(local);
-
-        if (local) {
-                r = raw_strip_suffixes(local, &ll);
+                r = raw_strip_suffixes(l, &ll);
                 if (r < 0)
                         return log_oom();
 
                 local = ll;
+        }
 
-                if (!hostname_is_valid(local, 0))
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Local image name '%s' is not valid.",
-                                               local);
-
-                if (!FLAGS_SET(arg_pull_flags, PULL_FORCE)) {
-                        r = image_find(IMAGE_MACHINE, local, NULL, NULL);
-                        if (r < 0) {
-                                if (r != -ENOENT)
-                                        return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
-                        } else {
-                                return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
-                                                       "Image '%s' already exists.",
-                                                       local);
-                        }
-                }
-
-                log_info("Pulling '%s', saving as '%s'.", url, local);
-        } else
-                log_info("Pulling '%s'.", url);
-
-        r = sd_event_default(&event);
+        r = normalize_local(local, url, &normalized);
         if (r < 0)
-                return log_error_errno(r, "Failed to allocate event loop: %m");
+                return r;
 
-        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
-        (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler,  NULL);
-        (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
+        r = import_allocate_event_with_signals(&event);
+        if (r < 0)
+                return r;
 
-        r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
+        if (!FLAGS_SET(arg_pull_flags, PULL_SYNC))
+                log_info("File system synchronization on completion is off.");
+         r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate puller: %m");
 
-        r = raw_pull_start(pull, url, local, arg_pull_flags & PULL_FLAGS_MASK_RAW, arg_verify);
+        r = raw_pull_start(
+                        pull,
+                        url,
+                        normalized,
+                        arg_offset,
+                        arg_size_max,
+                        arg_pull_flags & PULL_FLAGS_MASK_RAW,
+                        arg_verify,
+                        arg_checksum);
         if (r < 0)
                 return log_error_errno(r, "Failed to pull image: %m");
 
@@ -203,23 +238,39 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
 
 static int help(int argc, char *argv[], void *userdata) {
 
-        printf("%s [OPTIONS...] {COMMAND} ...\n\n"
-               "Download container or virtual machine images.\n\n"
+        printf("%1$s [OPTIONS...] {COMMAND} ...\n"
+               "\n%4$sDownload container or virtual machine images.%5$s\n"
+               "\n%2$sCommands:%3$s\n"
+               "  tar URL [NAME]              Download a TAR image\n"
+               "  raw URL [NAME]              Download a RAW image\n"
+               "\n%2$sOptions:%3$s\n"
                "  -h --help                   Show this help\n"
                "     --version                Show package version\n"
                "     --force                  Force creation of image\n"
                "     --verify=MODE            Verify downloaded image, one of: 'no',\n"
-               "                              'checksum', 'signature'\n"
+               "                              'checksum', 'signature' or literal SHA256 hash\n"
                "     --settings=BOOL          Download settings file with image\n"
                "     --roothash=BOOL          Download root hash file with image\n"
                "     --roothash-signature=BOOL\n"
                "                              Download root hash signature file with image\n"
                "     --verity=BOOL            Download verity file with image\n"
                "     --image-root=PATH        Image root directory\n\n"
-               "Commands:\n"
-               "  tar URL [NAME]              Download a TAR image\n"
-               "  raw URL [NAME]              Download a RAW image\n",
-               program_invocation_short_name);
+               "     --read-only              Create a read-only image\n"
+               "     --direct                 Download directly to specified file\n"
+               "     --btrfs-subvol=BOOL      Controls whether to create a btrfs subvolume\n"
+               "                              instead of a directory\n"
+               "     --btrfs-quota=BOOL       Controls whether to set up quota for btrfs\n"
+               "                              subvolume\n"
+               "     --convert-qcow2=BOOL     Controls whether to convert QCOW2 images to\n"
+               "                              regular disk images\n"
+               "     --sync=BOOL              Controls whether to sync() before completing\n"
+               "     --offset=BYTES           Offset to seek to in destination\n"
+               "     --size-max=BYTES         Maximum number of bytes to write to destination\n",
+               program_invocation_short_name,
+               ansi_underline(),
+               ansi_normal(),
+               ansi_highlight(),
+               ansi_normal());
 
         return 0;
 }
@@ -235,6 +286,14 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_ROOTHASH,
                 ARG_ROOTHASH_SIGNATURE,
                 ARG_VERITY,
+                ARG_READ_ONLY,
+                ARG_DIRECT,
+                ARG_BTRFS_SUBVOL,
+                ARG_BTRFS_QUOTA,
+                ARG_CONVERT_QCOW2,
+                ARG_SYNC,
+                ARG_OFFSET,
+                ARG_SIZE_MAX,
         };
 
         static const struct option options[] = {
@@ -247,6 +306,14 @@ static int parse_argv(int argc, char *argv[]) {
                 { "roothash",           required_argument, NULL, ARG_ROOTHASH           },
                 { "roothash-signature", required_argument, NULL, ARG_ROOTHASH_SIGNATURE },
                 { "verity",             required_argument, NULL, ARG_VERITY             },
+                { "read-only",          no_argument,       NULL, ARG_READ_ONLY          },
+                { "direct",             no_argument,       NULL, ARG_DIRECT             },
+                { "btrfs-subvol",       required_argument, NULL, ARG_BTRFS_SUBVOL       },
+                { "btrfs-quota",        required_argument, NULL, ARG_BTRFS_QUOTA        },
+                { "convert-qcow2",      required_argument, NULL, ARG_CONVERT_QCOW2      },
+                { "sync",               required_argument, NULL, ARG_SYNC               },
+                { "offset",             required_argument, NULL, ARG_OFFSET             },
+                { "size-max",           required_argument, NULL, ARG_SIZE_MAX           },
                 {}
         };
 
@@ -273,26 +340,51 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_image_root = optarg;
                         break;
 
-                case ARG_VERIFY:
-                        arg_verify = import_verify_from_string(optarg);
-                        if (arg_verify < 0)
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Invalid verification setting '%s'", optarg);
+                case ARG_VERIFY: {
+                        ImportVerify v;
+
+                        v = import_verify_from_string(optarg);
+                        if (v < 0) {
+                                _cleanup_free_ void *h = NULL;
+                                char *hh;
+                                size_t n;
+
+                                /* If this is not a valid verification mode, maybe it's a literally specified
+                                 * SHA256 hash? We can handle that too... */
+
+                                r = unhexmem(optarg, (size_t) -1, &h, &n);
+                                if (r < 0 || n == 0)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                               "Invalid verification setting: %s", optarg);
+                                if (n != 32)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                               "64 hex character SHA256 hash required when specifying explicit checksum, %zu specified", n * 2);
+
+                                hh = hexmem(h, n); /* bring into canonical (lowercase) form */
+                                if (!hh)
+                                        return log_oom();
+
+                                free_and_replace(arg_checksum, hh);
+                                arg_pull_flags &= ~(PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY);
+                                arg_verify = _IMPORT_VERIFY_INVALID;
+                        } else
+                                arg_verify = v;
 
                         break;
+                }
 
                 case ARG_SETTINGS:
-                        r = parse_boolean(optarg);
+                        r = parse_boolean_argument("--settings=", optarg, NULL);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse --settings= parameter '%s': %m", optarg);
+                                return r;
 
                         SET_FLAG(arg_pull_flags, PULL_SETTINGS, r);
                         break;
 
                 case ARG_ROOTHASH:
-                        r = parse_boolean(optarg);
+                        r = parse_boolean_argument("--roothash=", optarg, NULL);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse --roothash= parameter '%s': %m", optarg);
+                                return r;
 
                         SET_FLAG(arg_pull_flags, PULL_ROOTHASH, r);
 
@@ -302,31 +394,135 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_ROOTHASH_SIGNATURE:
-                        r = parse_boolean(optarg);
+                        r = parse_boolean_argument("--roothash-signature=", optarg, NULL);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse --roothash-signature= parameter '%s': %m", optarg);
+                                return r;
 
                         SET_FLAG(arg_pull_flags, PULL_ROOTHASH_SIGNATURE, r);
                         break;
 
                 case ARG_VERITY:
-                        r = parse_boolean(optarg);
+                        r = parse_boolean_argument("--verity=", optarg, NULL);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to parse --verity= parameter '%s': %m", optarg);
+                                return r;
 
                         SET_FLAG(arg_pull_flags, PULL_VERITY, r);
                         break;
 
+                case ARG_READ_ONLY:
+                        arg_pull_flags |= PULL_READ_ONLY;
+                        break;
+
+                case ARG_DIRECT:
+                        arg_pull_flags |= PULL_DIRECT;
+                        arg_pull_flags &= ~(PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY);
+                        break;
+
+                case ARG_BTRFS_SUBVOL:
+                        r = parse_boolean_argument("--btrfs-subvol=", optarg, NULL);
+                        if (r < 0)
+                                return r;
+
+                        SET_FLAG(arg_pull_flags, PULL_BTRFS_SUBVOL, r);
+                        break;
+
+                case ARG_BTRFS_QUOTA:
+                        r = parse_boolean_argument("--btrfs-quota=", optarg, NULL);
+                        if (r < 0)
+                                return r;
+
+                        SET_FLAG(arg_pull_flags, PULL_BTRFS_QUOTA, r);
+                        break;
+
+                case ARG_CONVERT_QCOW2:
+                        r = parse_boolean_argument("--convert-qcow2=", optarg, NULL);
+                        if (r < 0)
+                                return r;
+
+                        SET_FLAG(arg_pull_flags, PULL_CONVERT_QCOW2, r);
+                        break;
+
+                case ARG_SYNC:
+                        r = parse_boolean_argument("--sync=", optarg, NULL);
+                        if (r < 0)
+                                return r;
+
+                        SET_FLAG(arg_pull_flags, PULL_SYNC, r);
+                        break;
+
+                case ARG_OFFSET: {
+                        uint64_t u;
+
+                        r = safe_atou64(optarg, &u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --offset= argument: %s", optarg);
+                        if (!FILE_SIZE_VALID(u))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --offset= switch too large: %s", optarg);
+
+                        arg_offset = u;
+                        break;
+                }
+
+                case ARG_SIZE_MAX: {
+                        uint64_t u;
+
+                        r = parse_size(optarg, 1024, &u);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --size-max= argument: %s", optarg);
+                        if (!FILE_SIZE_VALID(u))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Argument to --size-max= switch too large: %s", optarg);
+
+                        arg_size_max = u;
+                        break;
+                }
+
                 case '?':
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
+        /* Make sure offset+size is still in the valid range if both set */
+        if (arg_offset != UINT64_MAX && arg_size_max != UINT64_MAX &&
+            ((arg_size_max > (UINT64_MAX - arg_offset)) ||
+             !FILE_SIZE_VALID(arg_offset + arg_size_max)))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset und maximum size out of range.");
+
+        if (arg_offset != UINT64_MAX && !FLAGS_SET(arg_pull_flags, PULL_DIRECT))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File offset only supported in --direct mode.");
+
+        if (arg_checksum && (arg_pull_flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) != 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Literal checksum verification only supported if no associated files are downloaded.");
+
         return 1;
 }
 
+static void parse_env(void) {
+        int r;
+
+        /* Let's make these relatively low-level settings also controllable via env vars. User can then set
+         * them for systemd-importd.service if they like to tweak behaviour */
+
+        r = getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
+        if (r >= 0)
+                SET_FLAG(arg_pull_flags, PULL_BTRFS_SUBVOL, r);
+        else if (r != -ENXIO)
+                log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
+
+        r = getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
+        if (r >= 0)
+                SET_FLAG(arg_pull_flags, PULL_BTRFS_QUOTA, r);
+        else if (r != -ENXIO)
+                log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
+
+        r = getenv_bool("SYSTEMD_IMPORT_SYNC");
+        if (r >= 0)
+                SET_FLAG(arg_pull_flags, PULL_SYNC, r);
+        else if (r != -ENXIO)
+                log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_SYNC: %m");
+}
+
 static int pull_main(int argc, char *argv[]) {
         static const Verb verbs[] = {
                 { "help", VERB_ANY, VERB_ANY, 0, help     },
@@ -345,6 +541,8 @@ static int run(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
 
+        parse_env();
+
         r = parse_argv(argc, argv);
         if (r <= 0)
                 return r;
index ab1cf7b109673349d170f6bb7ca45ba2a7c2e275..38972fe1a96ff164ad418212dd098585bb79aee4 100644 (file)
@@ -992,7 +992,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind < argc)
index ae1d43756a143ede25b95ad2b2d000681e6f034a..140ecac2073deabd34e364bb90feca5ad272382f 100644 (file)
@@ -1003,7 +1003,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unknown option code.");
+                        assert_not_reached();
                 }
 
         if (optind < argc)
index 6e5aebdc485704f110b64058055f0779dfa7f496..9c1ee0188dd334f68cd5b22395ea4627922a5e1a 100644 (file)
@@ -58,7 +58,7 @@ static int open_output(RemoteServer *s, Writer *w, const char* host) {
         }
 
         default:
-                assert_not_reached("what?");
+                assert_not_reached();
         }
 
         r = journal_file_open_reliably(filename,
@@ -110,7 +110,7 @@ int journal_remote_get_writer(RemoteServer *s, const char *host, Writer **writer
                 break;
 
         default:
-                assert_not_reached("what split mode?");
+                assert_not_reached();
         }
 
         w = hashmap_get(s->writers, key);
@@ -323,7 +323,7 @@ int journal_remote_server_init(
         else if (split_mode == JOURNAL_WRITE_SPLIT_HOST)
                 s->output = REMOTE_JOURNAL_PATH;
         else
-                assert_not_reached("bad split mode");
+                assert_not_reached();
 
         r = sd_event_default(&s->events);
         if (r < 0)
index f23d5cf8b9ca6d9a2edc2fe85393e1d0f569b220..164ed6590cda683e62d49def1bdade18d81f5885 100644 (file)
@@ -103,14 +103,13 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) {
                         _fallthrough_;
                 case ENTRY_BOOT_ID: {
                         sd_id128_t boot_id;
-                        char sid[SD_ID128_STRING_MAX];
 
                         r = sd_journal_get_monotonic_usec(u->journal, NULL, &boot_id);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to get monotonic timestamp: %m");
 
                         r = snprintf(buf + pos, size - pos,
-                                     "_BOOT_ID=%s\n", sd_id128_to_string(boot_id, sid));
+                                     "_BOOT_ID=%s\n", SD_ID128_TO_STRING(boot_id));
                         assert(r >= 0);
                         if ((size_t) r > size - pos)
                                 /* not enough space */
@@ -229,10 +228,10 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) {
                         return pos;
 
                 default:
-                        assert_not_reached("WTF?");
+                        assert_not_reached();
                 }
         }
-        assert_not_reached("WTF?");
+        assert_not_reached();
 }
 
 static void check_update_watchdog(Uploader *u) {
index 2a38d206ea16aa3c01ebb4e9a86befa71c1a05c2..3c28bbab7a20c5424b7f75f63c4f70d17d35959f 100644 (file)
@@ -779,7 +779,7 @@ static int parse_argv(int argc, char *argv[]) {
                                                argv[optind - 1]);
 
                 default:
-                        assert_not_reached("Unhandled option code.");
+                        assert_not_reached();
                 }
 
         if (!arg_url)
index d516fbabf7b8fdc536aa3fdcc8bd26707c8ec754..5670d55ec5502e88711ea7d9295a5d305cac2e38 100644 (file)
@@ -62,7 +62,7 @@ endforeach
 
 if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_MICROHTTPD') == 1
         install_data('browse.html',
-                     install_dir : join_paths(pkgdatadir, 'gatewayd'))
+                     install_dir : pkgdatadir / 'gatewayd')
 
         if get_option('create-log-dirs')
                 meson.add_install_script('sh', '-c',
index 4ccc5e0a330aab3922939206aaa6f20cc680f9e1..0c2c0d6152d113de3c5d53d1e63f5396e47bf481 100644 (file)
@@ -115,7 +115,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index fe98aecf51bbe28624db8239352c97e14a2b1c2f..b1786c7954409b9959d1a1da4d51b089768d2a80 100644 (file)
@@ -1046,7 +1046,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_follow && !arg_no_tail && !arg_since && arg_lines == ARG_LINES_DEFAULT)
@@ -1227,7 +1227,7 @@ static int discover_next_boot(sd_journal *j,
                 BootId **ret) {
 
         _cleanup_free_ BootId *next_boot = NULL;
-        char match[9+32+1] = "_BOOT_ID=";
+        char match[STRLEN("_BOOT_ID=") + SD_ID128_STRING_MAX] = "_BOOT_ID=";
         sd_id128_t boot_id;
         int r;
 
@@ -1336,7 +1336,7 @@ static int get_boots(
          * If no reference is given, the journal head/tail will do,
          * they're "virtual" boots after all. */
         if (boot_id && !sd_id128_is_null(*boot_id)) {
-                char match[9+32+1] = "_BOOT_ID=";
+                char match[STRLEN("_BOOT_ID=") + SD_ID128_STRING_MAX] = "_BOOT_ID=";
 
                 sd_journal_flush_matches(j);
 
@@ -1467,7 +1467,7 @@ static int list_boots(sd_journal *j) {
 }
 
 static int add_boot(sd_journal *j) {
-        char match[9+32+1] = "_BOOT_ID=";
+        char match[STRLEN("_BOOT_ID=") + SD_ID128_STRING_MAX] = "_BOOT_ID=";
         sd_id128_t boot_id;
         int r;
 
@@ -2229,7 +2229,7 @@ int main(int argc, char *argv[]) {
                 break;
 
         default:
-                assert_not_reached("Unknown action");
+                assert_not_reached();
         }
 
         if (arg_directory)
@@ -2314,7 +2314,7 @@ int main(int argc, char *argv[]) {
         case ACTION_FLUSH:
         case ACTION_SYNC:
         case ACTION_ROTATE:
-                assert_not_reached("Unexpected action.");
+                assert_not_reached();
 
         case ACTION_PRINT_HEADER:
                 journal_print_header(j);
@@ -2380,7 +2380,7 @@ int main(int argc, char *argv[]) {
                 break;
 
         default:
-                assert_not_reached("Unknown action");
+                assert_not_reached();
         }
 
         if (arg_boot_offset != 0 &&
index d410d7cf19ce5ddf9412f30fae073f124f8e5617..f2189964f0fafaee538b7c73cc8c985df430f783 100644 (file)
@@ -408,6 +408,13 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
         if (s->runtime_journal)
                 return s->runtime_journal;
 
+        /* If we are not in persistent mode, then we need return NULL immediately rather than opening a
+         * persistent journal of any sort.
+         *
+         * Fixes https://github.com/systemd/systemd/issues/20390 */
+        if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT))
+                return NULL;
+
         if (uid_for_system_journal(uid))
                 return s->system_journal;
 
index c6720b6b13950813d1a46a7011ded4a2d5e4ffba..0a90091a86335c0fce27b6b55eea1093b21e7eba 100644 (file)
@@ -449,7 +449,7 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
                 return stdout_stream_log(s, orig, line_break);
         }
 
-        assert_not_reached("Unknown stream state");
+        assert_not_reached();
 }
 
 static int stdout_stream_found(
index 4117188f147463913050a1a465e815b9316d076a..893832461e46d6d6176ee099a672e5040cb44010 100644 (file)
@@ -15,7 +15,7 @@ if want_kernel_install
 
         if install_sysconfdir
                 meson.add_install_script('sh', '-c',
-                                         mkdir_p.format(join_paths(sysconfdir, 'kernel/install.d')))
+                                         mkdir_p.format(sysconfdir / 'kernel/install.d'))
         endif
 
 endif
index 34928e0286901b2e3da573c4b72951e944eaab03..2bb7f3cce3c2984a6f737eb0a4b9413183b9954d 100644 (file)
@@ -30,6 +30,8 @@ typedef struct DHCPClientId {
 } DHCPClientId;
 
 typedef struct DHCPLease {
+        sd_dhcp_server *server;
+
         DHCPClientId client_id;
 
         be32_t address;
@@ -67,10 +69,10 @@ struct sd_dhcp_server {
 
         bool emit_router;
 
-        Hashmap *leases_by_client_id;
+        Hashmap *bound_leases_by_client_id;
+        Hashmap *bound_leases_by_address;
         Hashmap *static_leases_by_client_id;
-        DHCPLease **bound_leases;
-        DHCPLease invalid_lease;
+        Hashmap *static_leases_by_address;
 
         uint32_t max_lease_time, default_lease_time;
 
@@ -96,6 +98,8 @@ typedef struct DHCPRequest {
         const uint8_t *agent_info_option;
 } DHCPRequest;
 
+extern const struct hash_ops dhcp_lease_hash_ops;
+
 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                                size_t length);
 int dhcp_server_send_packet(sd_dhcp_server *server,
index c854d921c00eb09ceabf809170cf76ad18662bd2..e90284f6f2e37c80bcf8e5b655a7c8fef95ad145 100644 (file)
@@ -23,7 +23,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
         uint8_t *client_id;
         DHCPLease *lease;
-        int pool_offset;
 
         if (size < sizeof(DHCPMessage))
                 return 0;
@@ -46,9 +45,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1));
         lease->expiration = UINT64_MAX;
         memcpy(lease->chaddr, chaddr, 16);
-        pool_offset = get_pool_offset(server, lease->address);
-        server->bound_leases[pool_offset] = lease;
-        assert_se(hashmap_put(server->leases_by_client_id, &lease->client_id, lease) >= 0);
+        assert_se(hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease) >= 0);
+        assert_se(hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease) >= 0);
+        lease->server = server;
 
         (void) dhcp_server_handle_message(server, (DHCPMessage*)data, size);
 
index 67c6b55d84164fa60caf7fb8b35b2c6d34343622..0b8c3e4cc3d71cf6e1bfcb8001b709d2a60d4732 100644 (file)
@@ -186,7 +186,6 @@ int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *ret_dst,
 
         /* namelen == 0 only happens when running the test-suite over a socketpair */
 
-        assert(!(msg.msg_flags & MSG_CTRUNC));
         assert(!(msg.msg_flags & MSG_TRUNC));
 
         CMSG_FOREACH(cmsg, &msg) {
index 7fe660466feb60b6987be6106cf419f35b0a8f21..fb89f7c7f1944386b5e3e0bf76064731a3600861 100644 (file)
@@ -1308,7 +1308,7 @@ static int client_timeout_resend(
                 goto error;
 
         default:
-                assert_not_reached("Unhandled choice");
+                assert_not_reached();
         }
 
         r = event_reset_time(client->event, &client->timeout_resend,
@@ -1896,7 +1896,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
                 r = -EINVAL;
                 goto error;
         default:
-                assert_not_reached("invalid state");
+                assert_not_reached();
         }
 
 error:
index e2ea79f58414b31dae29084d3583b25cfb0b540c..23a1a5254401c5b4aa038e20e6120190a2f6c5d9 100644 (file)
@@ -28,6 +28,22 @@ static DHCPLease *dhcp_lease_free(DHCPLease *lease) {
         if (!lease)
                 return NULL;
 
+        if (lease->server) {
+                DHCPLease *e;
+
+                e = hashmap_get(lease->server->bound_leases_by_client_id, &lease->client_id);
+                if (e == lease) {
+                        hashmap_remove(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address));
+                        hashmap_remove(lease->server->bound_leases_by_client_id, &lease->client_id);
+                }
+
+                e = hashmap_get(lease->server->static_leases_by_client_id, &lease->client_id);
+                if (e == lease) {
+                        hashmap_remove(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address));
+                        hashmap_remove(lease->server->static_leases_by_client_id, &lease->client_id);
+                }
+        }
+
         free(lease->client_id.data);
         return mfree(lease);
 }
@@ -85,11 +101,6 @@ int sd_dhcp_server_configure_pool(
 
         if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
 
-                free(server->bound_leases);
-                server->bound_leases = new0(DHCPLease*, size);
-                if (!server->bound_leases)
-                        return -ENOMEM;
-
                 server->pool_offset = offset;
                 server->pool_size = size;
 
@@ -97,11 +108,9 @@ int sd_dhcp_server_configure_pool(
                 server->netmask = netmask;
                 server->subnet = address->s_addr & netmask;
 
-                if (server_off >= offset && server_off - offset < size)
-                        server->bound_leases[server_off - offset] = &server->invalid_lease;
-
                 /* Drop any leases associated with the old address range */
-                hashmap_clear(server->leases_by_client_id);
+                hashmap_clear(server->bound_leases_by_address);
+                hashmap_clear(server->bound_leases_by_client_id);
 
                 if (server->callback)
                         server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
@@ -144,8 +153,13 @@ int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
         return memcmp(a->data, b->data, a->length);
 }
 
-DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(dhcp_lease_hash_ops, DHCPClientId, client_id_hash_func, client_id_compare_func,
-                                              DHCPLease, dhcp_lease_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        dhcp_lease_hash_ops,
+        DHCPClientId,
+        client_id_hash_func,
+        client_id_compare_func,
+        DHCPLease,
+        dhcp_lease_free);
 
 static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
         assert(server);
@@ -161,8 +175,10 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
         for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
                 free(server->servers[i].addr);
 
-        hashmap_free(server->leases_by_client_id);
-        hashmap_free(server->static_leases_by_client_id);
+        server->bound_leases_by_address = hashmap_free(server->bound_leases_by_address);
+        server->bound_leases_by_client_id = hashmap_free(server->bound_leases_by_client_id);
+        server->static_leases_by_address = hashmap_free(server->static_leases_by_address);
+        server->static_leases_by_client_id = hashmap_free(server->static_leases_by_client_id);
 
         ordered_set_free(server->extra_options);
         ordered_set_free(server->vendor_options);
@@ -170,8 +186,6 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
         free(server->agent_circuit_id);
         free(server->agent_remote_id);
 
-        free(server->bound_leases);
-
         free(server->ifname);
         return mfree(server);
 }
@@ -201,13 +215,6 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
                 .max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC),
         };
 
-        server->leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
-        if (!server->leases_by_client_id)
-                return -ENOMEM;
-        server->static_leases_by_client_id = hashmap_new(&dhcp_lease_hash_ops);
-        if (!server->static_leases_by_client_id)
-                return -ENOMEM;
-
         *ret = TAKE_PTR(server);
 
         return 0;
@@ -839,18 +846,6 @@ static int prepare_new_lease(
         return 0;
 }
 
-static bool static_leases_have_address(sd_dhcp_server *server, be32_t address) {
-        DHCPLease *s;
-
-        assert(server);
-
-        HASHMAP_FOREACH(s, server->static_leases_by_client_id)
-                if (s->address == address)
-                        return true;
-
-        return false;
-}
-
 #define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
 
 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) {
@@ -880,14 +875,13 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                 /* this only fails on critical errors */
                 return r;
 
-        existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id);
+        existing_lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
         static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
 
         switch(type) {
 
         case DHCP_DISCOVER: {
                 be32_t address = INADDR_ANY;
-                unsigned i;
 
                 log_dhcp_server(server, "DISCOVER (0x%x)", be32toh(req->message->xid));
 
@@ -903,7 +897,6 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                 else {
                         struct siphash state;
                         uint64_t hash;
-                        uint32_t next_offer;
 
                         /* even with no persistence of leases, we try to offer the same client
                            the same IP address. we do this by using the hash of the client id
@@ -912,18 +905,16 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                         siphash24_init(&state, HASH_KEY.bytes);
                         client_id_hash_func(&req->client_id, &state);
                         hash = htole64(siphash24_finalize(&state));
-                        next_offer = hash % server->pool_size;
-
-                        for (i = 0; i < server->pool_size; i++) {
-                                if (!server->bound_leases[next_offer]) {
-                                        be32_t tmp = server->subnet | htobe32(server->pool_offset + next_offer);
-                                        if (!static_leases_have_address(server, tmp)) {
-                                                address = tmp;
-                                                break;
-                                        }
-                                }
 
-                                next_offer = (next_offer + 1) % server->pool_size;
+                        for (unsigned i = 0; i < server->pool_size; i++) {
+                                be32_t tmp_address;
+
+                                tmp_address = server->subnet | htobe32(server->pool_offset + (hash + i) % server->pool_size);
+                                if (!hashmap_contains(server->bound_leases_by_address, &tmp_address) &&
+                                    !hashmap_contains(server->static_leases_by_address, &tmp_address)) {
+                                        address = tmp_address;
+                                        break;
+                                }
                         }
                 }
 
@@ -947,6 +938,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                 return 1;
 
         case DHCP_REQUEST: {
+                DHCPLease *existing_lease_by_address;
                 be32_t address;
                 bool init_reboot = false;
                 int pool_offset;
@@ -997,11 +989,12 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                 }
 
                 pool_offset = get_pool_offset(server, address);
+                existing_lease_by_address = hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(address));
 
                 /* verify that the requested address is from the pool, and either
                    owned by the current client or free */
-                if (pool_offset >= 0 && static_lease) {
-                        _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old_lease = NULL;
+                if (static_lease && static_lease->address == address) {
+                        _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
                         usec_t time_now, expiration;
 
                         r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
@@ -1022,12 +1015,16 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
 
                         log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
 
-                        server->bound_leases[pool_offset] = lease;
+                        dhcp_lease_free(hashmap_remove(server->bound_leases_by_client_id, &lease->client_id));
+                        r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
+                        if (r < 0)
+                                return log_dhcp_server_errno(server, r, "Could not save lease: %m");
 
-                        old_lease = hashmap_remove(server->leases_by_client_id, &lease->client_id);
-                        r = hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
+                        r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
                         if (r < 0)
                                 return log_dhcp_server_errno(server, r, "Could not save lease: %m");
+
+                        lease->server = server;
                         TAKE_PTR(lease);
 
                         if (server->callback)
@@ -1035,11 +1032,13 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
 
                         return DHCP_ACK;
 
-                } else if (pool_offset >= 0 && server->bound_leases[pool_offset] == existing_lease) {
+                } else if (pool_offset >= 0 && existing_lease_by_address == existing_lease) {
                         _cleanup_(dhcp_lease_freep) DHCPLease *new_lease = NULL;
                         usec_t time_now, expiration;
                         DHCPLease *lease;
 
+                        /* Note that in the above condition we accept the case that both leases are NULL. */
+
                         r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
                         if (r < 0)
                                 return r;
@@ -1065,10 +1064,14 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
 
                         log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
 
-                        server->bound_leases[pool_offset] = lease;
-                        r = hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
+                        r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
                         if (r < 0)
                                 return log_dhcp_server_errno(server, r, "Could not save lease: %m");
+                        r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+                        if (r < 0)
+                                return log_dhcp_server_errno(server, r, "Could not save lease: %m");
+
+                        lease->server = server;
                         TAKE_PTR(new_lease);
 
                         if (server->callback)
@@ -1090,8 +1093,6 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
         }
 
         case DHCP_RELEASE: {
-                int pool_offset;
-
                 log_dhcp_server(server, "RELEASE (0x%x)",
                                 be32toh(req->message->xid));
 
@@ -1101,18 +1102,10 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                 if (existing_lease->address != req->message->ciaddr)
                         return 0;
 
-                pool_offset = get_pool_offset(server, req->message->ciaddr);
-                if (pool_offset < 0)
-                        return 0;
+                dhcp_lease_free(existing_lease);
 
-                if (server->bound_leases[pool_offset] == existing_lease) {
-                        server->bound_leases[pool_offset] = NULL;
-                        hashmap_remove(server->leases_by_client_id, existing_lease);
-                        dhcp_lease_free(existing_lease);
-
-                        if (server->callback)
-                                server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
-                }
+                if (server->callback)
+                        server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
 
                 return 0;
         }}
@@ -1266,24 +1259,17 @@ on_error:
 }
 
 int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
-        int r = 0;
+        DHCPLease *lease;
+        int k, r = 0;
 
         assert_return(server, -EINVAL);
-        assert(server->bound_leases);
-
-        for (uint32_t i = 0; i < server->pool_size; i++) {
-                DHCPLease *lease = server->bound_leases[i];
 
-                if (!lease || lease == &server->invalid_lease)
-                        continue;
+        log_dhcp_server(server, "FORCERENEW");
 
-                r = server_send_forcerenew(server, lease->address,
-                                           lease->gateway,
-                                           lease->chaddr);
-                if (r < 0)
-                        return r;
-
-                log_dhcp_server(server, "FORCERENEW");
+        HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) {
+                k = server_send_forcerenew(server, lease->address, lease->gateway, lease->chaddr);
+                if (k < 0)
+                        r = k;
         }
 
         return r;
@@ -1479,8 +1465,7 @@ int sd_dhcp_server_set_static_lease(
                 uint8_t *client_id,
                 size_t client_id_size) {
 
-        _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old = NULL;
-        DHCPClientId c;
+        _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
         int r;
 
         assert_return(server, -EINVAL);
@@ -1492,6 +1477,7 @@ int sd_dhcp_server_set_static_lease(
         * the server removes any static lease with the specified mac address. */
         if (!address || address->s_addr == 0) {
                 _cleanup_free_ void *data = NULL;
+                DHCPClientId c;
 
                 data = memdup(client_id, client_id_size);
                 if (!data)
@@ -1502,11 +1488,11 @@ int sd_dhcp_server_set_static_lease(
                         .data = data,
                 };
 
-                old = hashmap_remove(server->static_leases_by_client_id, &c);
+                dhcp_lease_free(hashmap_remove(server->static_leases_by_client_id, &c));
                 return 0;
         }
 
-        if (static_leases_have_address(server, address->s_addr))
+        if (hashmap_contains(server->static_leases_by_address, UINT32_TO_PTR(address->s_addr)))
                 return -EEXIST;
 
         lease = new(DHCPLease, 1);
@@ -1524,9 +1510,13 @@ int sd_dhcp_server_set_static_lease(
                 return -ENOMEM;
 
         r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
+        if (r < 0)
+                return r;
+        r = hashmap_ensure_put(&server->static_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
         if (r < 0)
                 return r;
 
+        lease->server = server;
         TAKE_PTR(lease);
         return 0;
 }
index 079f154acd3715d861d3dc0a56914dd387c1ca42..d4ab9df992b5dadaf558839c8bc599d56c54e61d 100644 (file)
@@ -1134,30 +1134,24 @@ static int client_parse_message(
 
                 switch (optcode) {
                 case SD_DHCP6_OPTION_CLIENTID:
-                        if (clientid) {
-                                log_dhcp6_client(client, "%s contains multiple clientids",
-                                                 dhcp6_message_type_to_string(message->type));
-                                return -EINVAL;
-                        }
+                        if (clientid)
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple clientids",
+                                                              dhcp6_message_type_to_string(message->type));
 
                         if (optlen != client->duid_len ||
-                            memcmp(&client->duid, optval, optlen) != 0) {
-                                log_dhcp6_client(client, "%s DUID does not match",
-                                                 dhcp6_message_type_to_string(message->type));
+                            memcmp(&client->duid, optval, optlen) != 0)
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s DUID does not match",
+                                                              dhcp6_message_type_to_string(message->type));
 
-                                return -EINVAL;
-                        }
                         clientid = true;
 
                         break;
 
                 case SD_DHCP6_OPTION_SERVERID:
                         r = dhcp6_lease_get_serverid(lease, NULL, NULL);
-                        if (r >= 0) {
-                                log_dhcp6_client(client, "%s contains multiple serverids",
-                                                 dhcp6_message_type_to_string(message->type));
-                                return -EINVAL;
-                        }
+                        if (r >= 0)
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple serverids",
+                                                              dhcp6_message_type_to_string(message->type));
 
                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
                         if (r < 0)
@@ -1180,20 +1174,16 @@ static int client_parse_message(
                         if (status < 0)
                                 return status;
 
-                        if (status > 0) {
-                                log_dhcp6_client(client, "%s Status %s",
-                                                 dhcp6_message_type_to_string(message->type),
-                                                 dhcp6_message_status_to_string(status));
-
-                                return -EINVAL;
-                        }
+                        if (status > 0)
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s Status %s",
+                                                              dhcp6_message_type_to_string(message->type),
+                                                              dhcp6_message_status_to_string(status));
 
                         break;
 
                 case SD_DHCP6_OPTION_IA_NA:
                         if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
-                                log_dhcp6_client(client, "Information request ignoring IA NA option");
-
+                                log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
                                 break;
                         }
 
@@ -1210,23 +1200,20 @@ static int client_parse_message(
                         if (r < 0)
                                 return r;
 
-                        if (client->ia_na.ia_na.id != iaid_lease) {
-                                log_dhcp6_client(client, "%s has wrong IAID for IA NA",
-                                                 dhcp6_message_type_to_string(message->type));
-                                return -EINVAL;
-                        }
+                        if (client->ia_na.ia_na.id != iaid_lease)
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has wrong IAID for IA NA",
+                                                              dhcp6_message_type_to_string(message->type));
 
                         if (lease->ia.addresses) {
                                 lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
-                                lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
+                                lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2));
                         }
 
                         break;
 
                 case SD_DHCP6_OPTION_IA_PD:
                         if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
-                                log_dhcp6_client(client, "Information request ignoring IA PD option");
-
+                                log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
                                 break;
                         }
 
@@ -1243,11 +1230,9 @@ static int client_parse_message(
                         if (r < 0)
                                 return r;
 
-                        if (client->ia_pd.ia_pd.id != iaid_lease) {
-                                log_dhcp6_client(client, "%s has wrong IAID for IA PD",
-                                                 dhcp6_message_type_to_string(message->type));
-                                return -EINVAL;
-                        }
+                        if (client->ia_pd.ia_pd.id != iaid_lease)
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has wrong IAID for IA PD",
+                                                              dhcp6_message_type_to_string(message->type));
 
                         if (lease->pd.addresses) {
                                 lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
@@ -1309,35 +1294,28 @@ static int client_parse_message(
                 pos += offsetof(DHCP6Option, data) + optlen;
         }
 
-        if (ia_na_status > 0 && ia_pd_status > 0) {
-                log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
-                return -EINVAL;
-        }
+        if (ia_na_status > 0 && ia_pd_status > 0)
+                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
 
-        if (!clientid) {
-                log_dhcp6_client(client, "%s has incomplete options",
-                                 dhcp6_message_type_to_string(message->type));
-                return -EINVAL;
-        }
+        if (!clientid)
+                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options",
+                                              dhcp6_message_type_to_string(message->type));
 
         if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
                 r = dhcp6_lease_get_serverid(lease, NULL, NULL);
-                if (r < 0) {
-                        log_dhcp6_client(client, "%s has no server id",
-                                         dhcp6_message_type_to_string(message->type));
-                        return -EINVAL;
-                }
+                if (r < 0)
+                        return log_dhcp6_client_errno(client, r, "%s has no server id",
+                                                      dhcp6_message_type_to_string(message->type));
+        }
 
-        } else {
-                if (lease->ia.addresses) {
-                        lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
-                        lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
-                }
+        if (lease->ia.addresses) {
+                lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
+                lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
+        }
 
-                if (lease->pd.addresses) {
-                        lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
-                        lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
-                }
+        if (lease->pd.addresses) {
+                lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
+                lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
         }
 
         client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
@@ -1479,15 +1457,16 @@ static int client_receive_message(
                 return 0;
         }
 
-        if (client->transaction_id != (message->transaction_id &
-                                       htobe32(0x00ffffff)))
+        if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff)))
                 return 0;
 
         switch (client->state) {
         case DHCP6_STATE_INFORMATION_REQUEST:
                 r = client_receive_reply(client, message, len);
-                if (r < 0)
+                if (r < 0) {
+                        log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
                         return 0;
+                }
 
                 client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
 
@@ -1497,10 +1476,13 @@ static int client_receive_message(
 
         case DHCP6_STATE_SOLICITATION:
                 r = client_receive_advertise(client, message, len);
+                if (r < 0) {
+                        log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m");
+                        return 0;
+                }
 
                 if (r == DHCP6_STATE_REQUEST) {
                         client_start(client, r);
-
                         break;
                 }
 
@@ -1510,11 +1492,12 @@ static int client_receive_message(
         case DHCP6_STATE_REBIND:
 
                 r = client_receive_reply(client, message, len);
-                if (r < 0)
+                if (r < 0) {
+                        log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
                         return 0;
+                }
 
                 if (r == DHCP6_STATE_BOUND) {
-
                         r = client_start(client, DHCP6_STATE_BOUND);
                         if (r < 0) {
                                 client_stop(client, r);
index c6aa9aa11a7d6f3f831bda2ff445f0aea74d148c..6a39f44e6c8634b239740e612328028ca629c0d9 100644 (file)
@@ -293,7 +293,7 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
                 break;
 
         default:
-                assert_not_reached("Invalid state.");
+                assert_not_reached();
         }
 
         return 0;
@@ -409,7 +409,7 @@ static int ipv4acd_on_packet(
                 break;
 
         default:
-                assert_not_reached("Invalid state.");
+                assert_not_reached();
         }
 
         return 0;
index 7b9d63ca3c5b74b98d5969ae27ac4d3ee67b7709..f2cc22db266b1329bbfc6f29503f3fe9980da021 100644 (file)
@@ -360,7 +360,7 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
                 break;
 
         default:
-                assert_not_reached("Invalid IPv4ACD event.");
+                assert_not_reached();
         }
 
         return;
index f4aab85ed40b47d87044a92801a5ed7be1b90bd0..e369f5a5921c89c206e5b25e935e6667cc360901 100644 (file)
@@ -30,7 +30,7 @@ static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) {
                 log_error("the client was stopped");
                 break;
         default:
-                assert_not_reached("invalid ACD event");
+                assert_not_reached();
         }
 }
 
index 6e0fdcd769ae5e483e9b0bfe78ce3e4df3c0144f..10db7f6c4bcde2d24c0b2c57553ee6b1145020c0 100644 (file)
@@ -34,7 +34,7 @@ static be32_t xid;
 static sd_event_source *test_hangcheck;
 
 static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
-        assert_not_reached("Test case should have completed in 2 seconds");
+        assert_not_reached();
 
         return 0;
 }
index a72c13684dea6512061fe81efbb5863502f9644f..5511350489deed5a5115ac8d90d0e7f4be010086 100644 (file)
@@ -560,10 +560,8 @@ static int test_advertise_option(sd_event *e) {
         return 0;
 }
 
-static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
-        assert_not_reached("Test case should have completed in 2 seconds");
-
-        return 0;
+static int test_check_completed_in_2_seconds(sd_event_source *s, uint64_t usec, void *userdata) {
+        assert_not_reached();
 }
 
 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
@@ -861,14 +859,8 @@ static int test_client_verify_information_request(DHCP6Message *information_requ
                         break;
 
                 case SD_DHCP6_OPTION_IA_NA:
-                        assert_not_reached("IA TA option must not be present");
-
-                        break;
-
                 case SD_DHCP6_OPTION_SERVERID:
-                        assert_not_reached("Server ID option must not be present");
-
-                        break;
+                        assert_not_reached();
 
                 case SD_DHCP6_OPTION_ELAPSED_TIME:
                         assert_se(!found_elapsed_time);
@@ -965,7 +957,7 @@ static int test_client_solicit(sd_event *e) {
 
         assert_se(sd_event_add_time_relative(e, &hangcheck, clock_boottime_or_monotonic(),
                                              2 * USEC_PER_SEC, 0,
-                                             test_hangcheck, NULL) >= 0);
+                                             test_check_completed_in_2_seconds, NULL) >= 0);
 
         assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
 
index a253acbd8355409b762e62404de2371669a9bb43..3fea894f302f26edd68e8f474991528ffa6acddc 100644 (file)
@@ -38,7 +38,7 @@ static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
                 log_error("the client was stopped with address %s", strna(address));
                 break;
         default:
-                assert_not_reached("invalid LL event");
+                assert_not_reached();
         }
 }
 
index 154d9acd2a295f3043819980ffd8d68cd25ce3dc..479b81b352045e4ad3cac9d48317bd634f499ae7 100644 (file)
@@ -171,7 +171,7 @@ libsystemd_static = static_library(
         c_args : libsystemd_c_args)
 
 libsystemd_sym = files('libsystemd.sym')
-libsystemd_sym_path = join_paths(meson.current_source_dir(), 'libsystemd.sym')
+libsystemd_sym_path = meson.current_source_dir() / 'libsystemd.sym'
 
 static_libsystemd = get_option('static-libsystemd')
 static_libsystemd_pic = static_libsystemd == 'true' or static_libsystemd == 'pic'
index 1159af46cdf25ebb98764e7634d6b4bd7cfdecfd..b3c0279c0bd8a49a614d81d91a8f24257117e152 100644 (file)
@@ -75,8 +75,7 @@ int bus_container_connect_socket(sd_bus *b) {
         r = wait_for_terminate_and_check("(sd-buscntrns)", child, 0);
         if (r < 0)
                 return r;
-        if (r != EXIT_SUCCESS)
-                return -EPROTO;
+        bool nonzero_exit_status = r != EXIT_SUCCESS;
 
         n = read(pair[0], &error_buf, sizeof(error_buf));
         if (n < 0)
@@ -95,8 +94,11 @@ int bus_container_connect_socket(sd_bus *b) {
                         return 1;
 
                 if (error_buf > 0)
-                        return log_debug_errno(error_buf, "Got error from (sd-buscntr): %m");
+                        return log_debug_errno(error_buf, "(sd-buscntr) failed to connect to D-Bus socket: %m");
         }
 
+        if (nonzero_exit_status)
+                return -EPROTO;
+
         return bus_socket_start_auth(b);
 }
index 65b188e88e971556a314b5211dc12104e1caa687..d50cd1eedd1c2ee48a0ca72035ffe0da0c30c0db 100644 (file)
@@ -259,7 +259,7 @@ _public_ int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags) {
                         break;
 
                 default:
-                        assert_not_reached("Unknown basic type.");
+                        assert_not_reached();
                 }
         }
 
index 850540a7852923f598a5d77e02859ae89971187b..07decfd1f3d7463739eaa1123589f7bf6783988c 100644 (file)
@@ -87,7 +87,7 @@ int bus_gvariant_get_size(const char *signature) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unknown signature type");
+                        assert_not_reached();
                 }
 
                 p += n;
@@ -164,7 +164,7 @@ int bus_gvariant_get_alignment(const char *signature) {
                 }
 
                 default:
-                        assert_not_reached("Unknown signature type");
+                        assert_not_reached();
                 }
 
                 if (a < 0)
@@ -229,7 +229,7 @@ int bus_gvariant_is_fixed_size(const char *signature) {
                 }
 
                 default:
-                        assert_not_reached("Unknown signature type");
+                        assert_not_reached();
                 }
 
                 p += n;
@@ -270,7 +270,7 @@ size_t bus_gvariant_read_word_le(void *p, size_t sz) {
         else if (sz == 8)
                 return le64toh(x.u64);
 
-        assert_not_reached("unknown word width");
+        assert_not_reached();
 }
 
 void bus_gvariant_write_word_le(void *p, size_t sz, size_t value) {
@@ -293,7 +293,7 @@ void bus_gvariant_write_word_le(void *p, size_t sz, size_t value) {
         else if (sz == 8)
                 x.u64 = htole64((uint64_t) value);
         else
-                assert_not_reached("unknown word width");
+                assert_not_reached();
 
         memcpy(p, &x, sz);
 }
index 648f1ca62fe3b2d1ebae75743f4432d74a4cfea5..12a50845db108ba7bb1caf5aaced5074b4c927b7 100644 (file)
@@ -199,7 +199,7 @@ static bool value_node_test(
                 return false;
 
         default:
-                assert_not_reached("Invalid node type");
+                assert_not_reached();
         }
 }
 
@@ -234,7 +234,7 @@ static bool value_node_same(
                 return streq(node->value.str, value_str);
 
         default:
-                assert_not_reached("Invalid node type");
+                assert_not_reached();
         }
 }
 
@@ -375,7 +375,7 @@ int bus_match_run(
                 break;
 
         default:
-                assert_not_reached("Unknown match type.");
+                assert_not_reached();
         }
 
         if (BUS_MATCH_CAN_HASH(node->type)) {
@@ -997,20 +997,16 @@ const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[]
                 return "path_namespace";
 
         case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
-                snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
-                return buf;
+                return snprintf_ok(buf, l, "arg%i", t - BUS_MATCH_ARG);
 
         case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
-                snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
-                return buf;
+                return snprintf_ok(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
 
         case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
-                snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
-                return buf;
+                return snprintf_ok(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
 
         case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
-                snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
-                return buf;
+                return snprintf_ok(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
 
         default:
                 return NULL;
index 20f7396c74ef1ebf80734e2014c823cdf52036a7..954b159bf2e40a0eaacbcf9b348fb1933bc92d4b 100644 (file)
@@ -2275,7 +2275,7 @@ _public_ int sd_bus_message_close_container(sd_bus_message *m) {
         else if (IN_SET(c->enclosing, SD_BUS_TYPE_STRUCT, SD_BUS_TYPE_DICT_ENTRY))
                 r = bus_message_close_struct(m, c, true);
         else
-                assert_not_reached("Unknown container type");
+                assert_not_reached();
 
         free(c->signature);
         free(c->offsets);
@@ -3247,7 +3247,7 @@ static int container_next_item(sd_bus_message *m, struct bus_container *c, size_
         } else if (c->enclosing == SD_BUS_TYPE_VARIANT)
                 goto end;
         else
-                assert_not_reached("Unknown container type");
+                assert_not_reached();
 
         return 0;
 
@@ -3460,7 +3460,7 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
                         }
 
                         default:
-                                assert_not_reached("unexpected type");
+                                assert_not_reached();
                         }
                 }
 
@@ -3575,7 +3575,7 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
                         }
 
                         default:
-                                assert_not_reached("Unknown basic type...");
+                                assert_not_reached();
                         }
                 }
         }
index a0009a7a87d7ccdbabfded3f96da9054549007a1..9f289575ef25809ca39e65462ff2c15f75c6f8de 100644 (file)
@@ -161,7 +161,7 @@ void bus_slot_disconnect(sd_bus_slot *slot, bool unref) {
                 break;
 
         default:
-                assert_not_reached("Wut? Unknown slot type?");
+                assert_not_reached();
         }
 
         bus = slot->bus;
index b408d657a5e7f031755e3cfe6f85ffb31edb37e2..4b8d73c3e083c054542941e49c72c7032fa018e6 100644 (file)
@@ -173,12 +173,12 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
         if (!d)
                 return 0;
 
-        e = memmem(d + 2, b->rbuffer_size - (d - (char*) b->rbuffer) - 2, "\r\n", 2);
+        e = memmem_safe(d + 2, b->rbuffer_size - (d - (char*) b->rbuffer) - 2, "\r\n", 2);
         if (!e)
                 return 0;
 
         if (b->accept_fd) {
-                f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
+                f = memmem_safe(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
                 if (!f)
                         return 0;
 
@@ -399,7 +399,7 @@ static int bus_socket_auth_verify_server(sd_bus *b) {
         for (;;) {
                 /* Check if line is complete */
                 line = (char*) b->rbuffer + b->auth_rbegin;
-                e = memmem(line, b->rbuffer_size - b->auth_rbegin, "\r\n", 2);
+                e = memmem_safe(line, b->rbuffer_size - b->auth_rbegin, "\r\n", 2);
                 if (!e)
                         return processed;
 
index 6960161c3658e3df85b24e301adbd75287080213..80f2bdd87f370e750975c04781ee01309c213f61 100644 (file)
@@ -2545,7 +2545,7 @@ _public_ int sd_bus_get_events(sd_bus *bus) {
                 break;
 
         default:
-                assert_not_reached("Unknown state");
+                assert_not_reached();
         }
 
         return flags;
@@ -2604,7 +2604,7 @@ _public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) {
                 return 0;
 
         default:
-                assert_not_reached("Unknown or unexpected stat");
+                assert_not_reached();
         }
 }
 
@@ -2873,7 +2873,6 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) {
                 r = sd_bus_message_new_method_return(m, &reply);
         else if (streq_ptr(m->member, "GetMachineId")) {
                 sd_id128_t id;
-                char sid[SD_ID128_STRING_MAX];
 
                 r = sd_id128_get_machine(&id);
                 if (r < 0)
@@ -2883,7 +2882,7 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) {
                 if (r < 0)
                         return r;
 
-                r = sd_bus_message_append(reply, "s", sd_id128_to_string(id, sid));
+                r = sd_bus_message_append(reply, "s", SD_ID128_TO_STRING(id));
         } else {
                 r = sd_bus_message_new_method_errorf(
                                 m, &reply,
@@ -3063,7 +3062,7 @@ static int bus_exit_now(sd_bus *bus) {
         else
                 exit(EXIT_FAILURE);
 
-        assert_not_reached("exit() didn't exit?");
+        assert_not_reached();
 }
 
 static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c) {
@@ -3234,7 +3233,7 @@ static int bus_process_internal(sd_bus *bus, sd_bus_message **ret) {
                 return process_closing(bus, ret);
 
         default:
-                assert_not_reached("Unknown state");
+                assert_not_reached();
         }
 
         if (ERRNO_IS_DISCONNECT(r)) {
index 13c08fe2955c21f3666c07051155497d6135e25d..317653bedc27cd5b78d0564dd94d5aba8151c44a 100644 (file)
@@ -57,7 +57,7 @@ static void server(sd_bus *b, size_t *result) {
                         return;
 
                 } else if (!sd_bus_message_is_signal(m, NULL, NULL))
-                        assert_not_reached("Unknown method");
+                        assert_not_reached();
         }
 }
 
index ea7efe68e49a03cab33dd2f131f1dc653a748614..ba94804869ce71a22951806ec17b1dd17a8516bf 100644 (file)
@@ -121,7 +121,7 @@ int main(int argc, char *argv[]) {
 
         OBJECT_PATH_FOREACH_PREFIX(prefix, "/") {
                 log_info("<%s>", prefix);
-                assert_not_reached("???");
+                assert_not_reached();
         }
 
         r = 0;
index 388128bf330857332dfe81b720ee84cd449c6beb..c7e1bd8bb81e0d29675d170147ad78869ee12298 100644 (file)
@@ -639,7 +639,7 @@ int device_read_uevent_file(sd_device *device) {
 
                         break;
                 default:
-                        assert_not_reached("Invalid state when parsing uevent file");
+                        assert_not_reached();
                 }
 
         if (major) {
@@ -2159,7 +2159,6 @@ _public_ int sd_device_trigger_with_uuid(
                 sd_device_action_t action,
                 sd_id128_t *ret_uuid) {
 
-        char buf[ID128_UUID_STRING_MAX];
         const char *s, *j;
         sd_id128_t u;
         int r;
@@ -2178,8 +2177,7 @@ _public_ int sd_device_trigger_with_uuid(
         if (r < 0)
                 return r;
 
-        id128_to_uuid_string(u, buf);
-        j = strjoina(s, " ", buf);
+        j = strjoina(s, " ", ID128_TO_UUID_STRING(u));
 
         r = sd_device_set_sysattr_value(device, "uevent", j);
         if (r < 0)
index e9199deb418c8b26ddd1ee790c5c0700def65e2d..cb8f54b1d522b03c83fdb2f3e9875f94ba0d0e65 100644 (file)
@@ -915,7 +915,7 @@ static void source_disconnect(sd_event_source *s) {
         }
 
         default:
-                assert_not_reached("Wut? I shouldn't exist.");
+                assert_not_reached();
         }
 
         if (s->pending)
@@ -2407,7 +2407,7 @@ static int event_source_offline(
                 break;
 
         default:
-                assert_not_reached("Wut? I shouldn't exist.");
+                assert_not_reached();
         }
 
         /* Always reshuffle time prioq, as the ratelimited flag may be changed. */
@@ -2495,7 +2495,7 @@ static int event_source_online(
                 break;
 
         default:
-                assert_not_reached("Wut? I shouldn't exist.");
+                assert_not_reached();
         }
 
         s->enabled = enabled;
@@ -3569,7 +3569,7 @@ static int source_dispatch(sd_event_source *s) {
         case SOURCE_WATCHDOG:
         case _SOURCE_EVENT_SOURCE_TYPE_MAX:
         case _SOURCE_EVENT_SOURCE_TYPE_INVALID:
-                assert_not_reached("Wut? I shouldn't exist.");
+                assert_not_reached();
         }
 
         s->dispatching = false;
@@ -3929,7 +3929,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
                                         break;
 
                                 default:
-                                        assert_not_reached("Unexpected event source type");
+                                        assert_not_reached();
                                 }
 
                                 break;
@@ -3953,7 +3953,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
                                 break;
 
                         default:
-                                assert_not_reached("Invalid wake-up pointer");
+                                assert_not_reached();
                         }
                 }
                 if (r < 0)
index 9b92dac650d2a38a90a13f40592f1348e9a29fa1..e28885b5e06c6931ef899b73f1e2dd9da380fc9a 100644 (file)
@@ -55,7 +55,7 @@ static int io_handler(sd_event_source *s, int fd, uint32_t revents, void *userda
                 else
                         assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
         } else
-                assert_not_reached("Yuck!");
+                assert_not_reached();
 
         return 1;
 }
@@ -170,7 +170,7 @@ static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
                         got_c = true;
                 }
         } else
-                assert_not_reached("Huh?");
+                assert_not_reached();
 
         return 2;
 }
@@ -452,7 +452,7 @@ static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, v
                 log_info("inotify-handler <%s>: delete of %s", description, ev->name);
                 assert_se(streq(ev->name, "sub"));
         } else
-                assert_not_reached("unexpected inotify event");
+                assert_not_reached();
 
         maybe_exit(s, c);
         return 1;
@@ -470,7 +470,7 @@ static int delete_self_handler(sd_event_source *s, const struct inotify_event *e
         } else if (ev->mask & IN_IGNORED) {
                 log_info("delete-self-handler: ignore");
         } else
-                assert_not_reached("unexpected inotify event (delete-self)");
+                assert_not_reached();
 
         maybe_exit(s, c);
         return 1;
index 053ef0a6a83f091a9aeddfa32b601d0e53dde0dd..b7327a1f0700794e7c33046ec62916ff5b20d687 100644 (file)
@@ -12,6 +12,8 @@
 
 char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]);
 
+#define ID128_TO_UUID_STRING(id) id128_to_uuid_string((id), (char[ID128_UUID_STRING_MAX]) {})
+
 bool id128_is_valid(const char *s) _pure_;
 
 typedef enum Id128Format {
index abbe35c33b0b94375bede6013006fbef918f2ec4..9fecc84495b2eadf006e23762a3c1038bbd33948 100644 (file)
@@ -3273,7 +3273,6 @@ fail:
 #define FORMAT_TIMESTAMP_SAFE(t) (FORMAT_TIMESTAMP(t) ?: " --- ")
 
 void journal_file_print_header(JournalFile *f) {
-        char a[SD_ID128_STRING_MAX], b[SD_ID128_STRING_MAX], c[SD_ID128_STRING_MAX], d[SD_ID128_STRING_MAX];
         struct stat st;
 
         assert(f);
@@ -3300,10 +3299,10 @@ void journal_file_print_header(JournalFile *f) {
                "Objects: %"PRIu64"\n"
                "Entry objects: %"PRIu64"\n",
                f->path,
-               sd_id128_to_string(f->header->file_id, a),
-               sd_id128_to_string(f->header->machine_id, b),
-               sd_id128_to_string(f->header->boot_id, c),
-               sd_id128_to_string(f->header->seqnum_id, d),
+               SD_ID128_TO_STRING(f->header->file_id),
+               SD_ID128_TO_STRING(f->header->machine_id),
+               SD_ID128_TO_STRING(f->header->boot_id),
+               SD_ID128_TO_STRING(f->header->seqnum_id),
                f->header->state == STATE_OFFLINE ? "OFFLINE" :
                f->header->state == STATE_ONLINE ? "ONLINE" :
                f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
index 5728c537bc603059fe65a7aa5bfe7e902ec38b00..efa67de3345f0443e528387e3293f84b1d333a5a 100644 (file)
@@ -190,7 +190,7 @@ static bool same_field(const void *_a, size_t s, const void *_b, size_t t) {
                         return true;
         }
 
-        assert_not_reached("\"=\" not found");
+        assert_not_reached();
 }
 
 static Match *match_new(Match *p, MatchType t) {
@@ -920,7 +920,6 @@ _public_ int sd_journal_previous_skip(sd_journal *j, uint64_t skip) {
 _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
         Object *o;
         int r;
-        char bid[SD_ID128_STRING_MAX], sid[SD_ID128_STRING_MAX];
 
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
@@ -933,13 +932,10 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
         if (r < 0)
                 return r;
 
-        sd_id128_to_string(j->current_file->header->seqnum_id, sid);
-        sd_id128_to_string(o->entry.boot_id, bid);
-
         if (asprintf(cursor,
                      "s=%s;i=%"PRIx64";b=%s;m=%"PRIx64";t=%"PRIx64";x=%"PRIx64,
-                     sid, le64toh(o->entry.seqnum),
-                     bid, le64toh(o->entry.monotonic),
+                     SD_ID128_TO_STRING(j->current_file->header->seqnum_id), le64toh(o->entry.seqnum),
+                     SD_ID128_TO_STRING(o->entry.boot_id), le64toh(o->entry.monotonic),
                      le64toh(o->entry.realtime),
                      le64toh(o->entry.xor_hash)) < 0)
                 return -ENOMEM;
index eb3459e27eac8d958f483c40d363edb5be2a1fcc..f222bf30212f6278ddb8f4758945688a7ae645c2 100644 (file)
@@ -68,7 +68,7 @@ static char* make_buf(size_t count, const char *type) {
                 random_bytes(buf + 8*step, step);
                 memzero(buf + 9*step, step);
         } else
-                assert_not_reached("here");
+                assert_not_reached();
 
         return buf;
 }
index 3adf1cef5cf579e9c2701c877f4cf9aea7f3db90..d127443c4c0f9b8518743c91c1965b1cae1c131b 100644 (file)
@@ -999,7 +999,7 @@ _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
 
 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
         if (m)
-                close_nointr(MONITOR_TO_FD(m));
+                (void) close_nointr(MONITOR_TO_FD(m));
 
         return NULL;
 }
index 473d74b01a157939c86192816db52eb846c68c58..c4dc4e67dd5f4680f51887b83e09a47d743d974d 100644 (file)
@@ -1163,7 +1163,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
                         break;
                 }
                 default:
-                        assert_not_reached("sd-netlink: invalid type system union type");
+                        assert_not_reached();
                 }
         } else
                 return -EINVAL;
index b6de545fe26fc6506dd5ab6dda02f6976256581b..76b4ccaa960ae28f4cd50f2e4591a6c026997a32 100644 (file)
@@ -92,7 +92,7 @@ void netlink_slot_disconnect(sd_netlink_slot *slot, bool unref) {
 
                 break;
         default:
-                assert_not_reached("Wut? Unknown slot type?");
+                assert_not_reached();
         }
 
         slot->type = _NETLINK_SLOT_INVALID;
index e771b95e0848bf5064d3592a4a94306d21766b7a..9127167bb2224546135086698d429c84cd4b61ff 100644 (file)
@@ -288,7 +288,7 @@ int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret,
                 assert_return(IN_SET(nh_family, AF_UNSPEC, AF_INET, AF_INET6), -EINVAL);
                 break;
         default:
-                assert_not_reached("Invalid message type.");
+                assert_not_reached();
         }
         assert_return(ret, -EINVAL);
 
@@ -443,8 +443,12 @@ int sd_rtnl_message_new_neigh(sd_netlink *rtnl, sd_netlink_message **ret, uint16
         if (r < 0)
                 return r;
 
-        if (nlmsg_type == RTM_NEWNEIGH)
-                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+        if (nlmsg_type == RTM_NEWNEIGH) {
+                if (ndm_family == AF_BRIDGE)
+                        (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
+                else
+                        (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
+        }
 
         ndm = NLMSG_DATA((*ret)->hdr);
 
index 7273a77156a1dd3128a996bce6f3e1db73971cb1..ee93dae9e3a79965bc1331b1542285286cc9ad42 100644 (file)
@@ -444,7 +444,7 @@ _public_ int sd_network_monitor_new(sd_network_monitor **m, const char *category
 
 _public_ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
         if (m)
-                close_nointr(MONITOR_TO_FD(m));
+                (void) close_nointr(MONITOR_TO_FD(m));
 
         return NULL;
 }
index ee973c0692278444cfb45b868819ed659da879f4..c8af16a91811205fe06bc98222935852fb434d6b 100644 (file)
@@ -397,7 +397,7 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) {
                  return -ECONNRESET;
 
         default:
-                assert_not_reached("Unknown request");
+                assert_not_reached();
         }
 
         return 0;
@@ -661,7 +661,7 @@ static int complete_query(sd_resolve *resolve, sd_resolve_query *q) {
                 break;
 
         default:
-                assert_not_reached("Cannot complete unknown query type");
+                assert_not_reached();
         }
 
         resolve->current = NULL;
index b973dfd90d24b9e2d9b0d45a2e56ac1cb31a9b45..647bd1e381d6f6dc577a77ca2e53af82ad5a4783 100644 (file)
@@ -99,7 +99,7 @@ int main(int argc, char *argv[]) {
                 }
                 if (r < 0) {
                         log_error_errno(r, "sd_resolve_wait(): %m");
-                        assert_not_reached("sd_resolve_wait() failed");
+                        assert_not_reached();
                 }
         }
 
index 61e7cc20a6abbc46718a2058262241d5d753349f..130374d14fb0f743f8abc9c4be9fab60fbbcb40a 100644 (file)
@@ -19,10 +19,10 @@ libudev_sources = files(
 libudev_includes = [includes, include_directories('.')]
 
 libudev_sym = files('libudev.sym')
-libudev_sym_path = join_paths(meson.current_source_dir(), 'libudev.sym')
+libudev_sym_path = meson.current_source_dir() / 'libudev.sym'
 
 install_headers('libudev.h')
-libudev_h_path = join_paths(meson.current_source_dir(), 'libudev.h')
+libudev_h_path = meson.current_source_dir() / 'libudev.h'
 
 libudev_basic = static_library(
         'udev-basic',
index a7510567954145197b634246824c32f31a862738..354ded3a2b495e818f15e23a37b91d450ebb5778 100644 (file)
@@ -445,7 +445,7 @@ static int parse_args(int argc, char *argv[], const char **syspath, const char *
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option code.");
+                        assert_not_reached();
                 }
 
         return 1;
index d2f0566dbc5214cc709c0b01dd7688d449c17b9a..32af23d6924187d19add02b32402767237595421 100644 (file)
@@ -691,7 +691,7 @@ int find_language_fallback(const char *lang, char **language) {
                 }
         }
 
-        assert_not_reached("should not be here");
+        assert_not_reached();
 }
 
 int x11_convert_to_vconsole(Context *c) {
@@ -845,7 +845,7 @@ int locale_gen_enable_locale(const char *locale) {
                 r = copy_access(fileno(fr), fileno(fw));
                 if (r < 0)
                         return r;
-                r = copy_xattr(fileno(fr), fileno(fw));
+                r = copy_xattr(fileno(fr), fileno(fw), COPY_ALL_XATTRS);
                 if (r < 0)
                         return r;
         }
index 548ac8eb2c357ce330516b4f5de1fac30d2b86c3..0de3532f9786258ab7468348afc4f8d4a8ad4feb 100644 (file)
@@ -293,7 +293,7 @@ static int list_x11_keymaps(int argc, char **argv, void *userdata) {
         else if (streq(argv[0], "list-x11-keymap-options"))
                 look_for = OPTIONS;
         else
-                assert_not_reached("Wrong parameter");
+                assert_not_reached();
 
         for (;;) {
                 _cleanup_free_ char *line = NULL;
@@ -474,7 +474,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index 757d4457e93ec2152e93bc57d84e6bfc3e7df06f..a2ff2a98736ff48331a0dc80bf1933ec3c3400b2 100644 (file)
@@ -20,8 +20,8 @@ endif
 # If you know a way that allows the same variables to be used
 # in sources list and concatenated to a string for test_env,
 # let me know.
-kbd_model_map = join_paths(meson.current_source_dir(), 'kbd-model-map')
-language_fallback_map = join_paths(meson.current_source_dir(), 'language-fallback-map')
+kbd_model_map = meson.current_source_dir() / 'kbd-model-map'
+language_fallback_map = meson.current_source_dir() / 'language-fallback-map'
 
 if conf.get('ENABLE_LOCALED') == 1
         install_data('kbd-model-map',
index e871628b9a5d8ade9004580a03b9e6502de146cc..ff5a5d06f516cf76f0177bd0029e737e4e4bd2b7 100644 (file)
@@ -256,7 +256,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_action == ACTION_INHIBIT && optind == argc)
index 4b5598ecf66401c9a5b8fd5f23b5ef96975dc8ac..846a31012eba036d47af7b6151becbbfaf568e67 100644 (file)
@@ -1412,7 +1412,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index 8ed066c25e53a15d9fb2fb781a56fb5785704805..e1729109482b031d9f0a896ac4b369d8b325b59d 100644 (file)
@@ -27,6 +27,7 @@ const char* manager_target_for_action(HandleAction handle) {
                 [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
                 [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
                 [HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
+                [HANDLE_FACTORY_RESET] = SPECIAL_FACTORY_RESET_TARGET,
         };
 
         assert(handle >= 0);
@@ -51,6 +52,7 @@ int manager_handle_action(
                 [HANDLE_HIBERNATE] = "Hibernating...",
                 [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...",
                 [HANDLE_SUSPEND_THEN_HIBERNATE] = "Suspending, then hibernating...",
+                [HANDLE_FACTORY_RESET] = "Performing factory reset...",
         };
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -179,6 +181,7 @@ static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
         [HANDLE_HIBERNATE] = "hibernate",
         [HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
         [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate",
+        [HANDLE_FACTORY_RESET] = "factory-reset",
         [HANDLE_LOCK] = "lock",
 };
 
index 0baad37a84c92cea1ecd6a82dd73b35babd1e57b..ec2fece2b7b056ebbae95abff228106a199a6b4b 100644 (file)
@@ -14,6 +14,7 @@ typedef enum HandleAction {
         HANDLE_HYBRID_SLEEP,
         HANDLE_SUSPEND_THEN_HIBERNATE,
         HANDLE_LOCK,
+        HANDLE_FACTORY_RESET,
         _HANDLE_ACTION_MAX,
         _HANDLE_ACTION_INVALID = -EINVAL,
 } HandleAction;
index a2eb4c5269fb3f47844315f48d1ef7cf2aa4ab7c..7fb811463938a6b1b5e5e7a2556e462c3149ab13 100644 (file)
@@ -279,12 +279,28 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
                 }
 
         } else if (ev.type == EV_KEY && ev.value == 0) {
-                if (ev.code == KEY_RESTART) {
-                        if (b->manager->reboot_key_long_press_event_source) {
+
+                switch (ev.code) {
+
+                case KEY_POWER:
+                case KEY_POWER2:
+                        if (b->manager->power_key_long_press_event_source) {
                                 /* Long press event timer is still pending and key release
                                    event happened.  This means that key press duration was
                                    insufficient to trigger a long press event
                                 */
+                                log_struct(LOG_INFO,
+                                           LOG_MESSAGE("Power key pressed short."),
+                                           "MESSAGE_ID=" SD_MESSAGE_POWER_KEY_STR);
+
+                                b->manager->power_key_long_press_event_source = sd_event_source_unref(b->manager->power_key_long_press_event_source);
+
+                                manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
+                        }
+                        break;
+
+                case KEY_RESTART:
+                        if (b->manager->reboot_key_long_press_event_source) {
                                 log_struct(LOG_INFO,
                                            LOG_MESSAGE("Reboot key pressed short."),
                                            "MESSAGE_ID=" SD_MESSAGE_REBOOT_KEY_STR);
@@ -293,6 +309,30 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
 
                                 manager_handle_action(b->manager, INHIBIT_HANDLE_REBOOT_KEY, b->manager->handle_reboot_key, b->manager->reboot_key_ignore_inhibited, true);
                         }
+                        break;
+
+                case KEY_SLEEP:
+                        if (b->manager->suspend_key_long_press_event_source) {
+                                log_struct(LOG_INFO,
+                                           LOG_MESSAGE("Suspend key pressed short."),
+                                           "MESSAGE_ID=" SD_MESSAGE_SUSPEND_KEY_STR);
+
+                                b->manager->suspend_key_long_press_event_source = sd_event_source_unref(b->manager->suspend_key_long_press_event_source);
+
+                                manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
+                        }
+                        break;
+                case KEY_SUSPEND:
+                        if (b->manager->hibernate_key_long_press_event_source) {
+                                log_struct(LOG_INFO,
+                                           LOG_MESSAGE("Hibernate key pressed short."),
+                                           "MESSAGE_ID=" SD_MESSAGE_HIBERNATE_KEY_STR);
+
+                                b->manager->hibernate_key_long_press_event_source = sd_event_source_unref(b->manager->hibernate_key_long_press_event_source);
+
+                                manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
+                        }
+                        break;
                 }
 
         } else if (ev.type == EV_SW && ev.value > 0) {
index 999a6095199509eb402e39d31badfe276f0cfa6f..6e9dde1c15576ab6ac3106767d979e42b3faa80d 100644 (file)
@@ -1491,41 +1491,59 @@ static int have_multiple_sessions(
         return false;
 }
 
-static int bus_manager_log_shutdown(
-                Manager *m,
-                const char *unit_name) {
-
-        const char *p, *q;
-
+_printf_(2, 0)
+static int log_with_wall_message(Manager *m, const char *d, const char *p, const char *q) {
         assert(m);
-        assert(unit_name);
-
-        if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) {
-                p = "MESSAGE=System is powering down";
-                q = "SHUTDOWN=power-off";
-        } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) {
-                p = "MESSAGE=System is rebooting";
-                q = "SHUTDOWN=reboot";
-        } else if (streq(unit_name, SPECIAL_HALT_TARGET)) {
-                p = "MESSAGE=System is halting";
-                q = "SHUTDOWN=halt";
-        } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) {
-                p = "MESSAGE=System is rebooting with kexec";
-                q = "SHUTDOWN=kexec";
-        } else {
-                p = "MESSAGE=System is shutting down";
-                q = NULL;
-        }
 
         if (isempty(m->wall_message))
                 p = strjoina(p, ".");
         else
                 p = strjoina(p, " (", m->wall_message, ").");
 
-        return log_struct(LOG_NOTICE,
-                          "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
-                          p,
-                          q);
+        return log_struct(LOG_NOTICE, d, p, q);
+}
+
+static int bus_manager_log_shutdown(
+                Manager *m,
+                const char *unit_name) {
+
+        assert(m);
+        assert(unit_name);
+
+        if (streq(unit_name, SPECIAL_POWEROFF_TARGET))
+                return log_with_wall_message(m,
+                                             "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
+                                             "MESSAGE=System is powering down",
+                                             "SHUTDOWN=power-off");
+
+        if (streq(unit_name, SPECIAL_REBOOT_TARGET))
+                return log_with_wall_message(m,
+                                             "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
+                                             "MESSAGE=System is rebooting",
+                                             "SHUTDOWN=reboot");
+
+        if (streq(unit_name, SPECIAL_HALT_TARGET))
+                return log_with_wall_message(m,
+                                             "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
+                                             "MESSAGE=System is halting",
+                                             "SHUTDOWN=halt");
+
+        if (streq(unit_name, SPECIAL_KEXEC_TARGET))
+                return log_with_wall_message(m,
+                                             "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
+                                             "MESSAGE=System is rebooting with kexec",
+                                             "SHUTDOWN=kexec");
+
+        if (streq(unit_name, SPECIAL_FACTORY_RESET_TARGET))
+                return log_with_wall_message(m,
+                                             "MESSAGE_ID=" SD_MESSAGE_FACTORY_RESET_STR,
+                                             "MESSAGE=System is performing factory reset",
+                                             NULL);
+
+        return log_with_wall_message(m,
+                                     "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
+                                     "MESSAGE=System is shutting down",
+                                     NULL);
 }
 
 static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) {
@@ -2141,7 +2159,7 @@ static int manager_scheduled_shutdown_handler(
         else if (streq(m->scheduled_shutdown_type, "halt"))
                 target = SPECIAL_HALT_TARGET;
         else
-                assert_not_reached("unexpected shutdown type");
+                assert_not_reached();
 
         /* Don't allow multiple jobs being executed at the same time */
         if (m->action_what > 0) {
index f2adb969d4e4119fd9a9e9d12c948f81b4b26932..1c4d543889edba318605562954d0fa2837d500e8 100644 (file)
@@ -141,7 +141,7 @@ static int session_device_open(SessionDevice *sd, bool active) {
                          * that so fail at all times and let caller retry in inactive state. */
                         r = sd_drmsetmaster(fd);
                         if (r < 0) {
-                                close_nointr(fd);
+                                (void) close_nointr(fd);
                                 return r;
                         }
                 } else
index f8bd17eefecaafe87fd6d587f769003677e93151..1b643d52ca95a8e95f52818ff6cf2252e7ff3db8 100644 (file)
@@ -705,7 +705,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
          * "systemd-user" we simply set XDG_RUNTIME_DIR and
          * leave. */
 
-        (void) pam_get_item(handle, PAM_SERVICE, (const void**) &service);
+        r = pam_get_item(handle, PAM_SERVICE, (const void**) &service);
+        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM service: %s", pam_strerror(handle, r));
+                return r;
+        }
         if (streq_ptr(service, "systemd-user")) {
                 char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
 
@@ -719,10 +723,26 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         /* Otherwise, we ask logind to create a session for us */
 
-        (void) pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
-        (void) pam_get_item(handle, PAM_TTY, (const void**) &tty);
-        (void) pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
-        (void) pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
+        r = pam_get_item(handle, PAM_XDISPLAY, (const void**) &display);
+        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM XDISPLAY: %s", pam_strerror(handle, r));
+                return r;
+        }
+        r = pam_get_item(handle, PAM_TTY, (const void**) &tty);
+        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM TTY: %s", pam_strerror(handle, r));
+                return r;
+        }
+        r = pam_get_item(handle, PAM_RUSER, (const void**) &remote_user);
+        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM RUSER: %s", pam_strerror(handle, r));
+                return r;
+        }
+        r = pam_get_item(handle, PAM_RHOST, (const void**) &remote_host);
+        if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM RHOST: %s", pam_strerror(handle, r));
+                return r;
+        }
 
         seat = getenv_harder(handle, "XDG_SEAT", NULL);
         cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
@@ -789,11 +809,31 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         remote = !isempty(remote_host) && !is_localhost(remote_host);
 
-        (void) pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max);
-        (void) pam_get_data(handle, "systemd.tasks_max",  (const void **)&tasks_max);
-        (void) pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight);
-        (void) pam_get_data(handle, "systemd.io_weight",  (const void **)&io_weight);
-        (void) pam_get_data(handle, "systemd.runtime_max_sec", (const void **)&runtime_max_sec);
+        r = pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.memory_max data: %s", pam_strerror(handle, r));
+                return r;
+        }
+        r = pam_get_data(handle, "systemd.tasks_max",  (const void **)&tasks_max);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.tasks_max data: %s", pam_strerror(handle, r));
+                return r;
+        }
+        r = pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.cpu_weight data: %s", pam_strerror(handle, r));
+                return r;
+        }
+        r = pam_get_data(handle, "systemd.io_weight",  (const void **)&io_weight);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.io_weight data: %s", pam_strerror(handle, r));
+                return r;
+        }
+        r = pam_get_data(handle, "systemd.runtime_max_sec", (const void **)&runtime_max_sec);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.runtime_max_sec data: %s", pam_strerror(handle, r));
+                return r;
+        }
 
         /* Talk to logind over the message bus */
 
@@ -996,7 +1036,11 @@ _public_ PAM_EXTERN int pam_sm_close_session(
 
         /* Only release session if it wasn't pre-existing when we
          * tried to create it */
-        (void) pam_get_data(handle, "systemd.existing", &existing);
+        r = pam_get_data(handle, "systemd.existing", &existing);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM systemd.existing data: %s", pam_strerror(handle, r));
+                return r;
+        }
 
         id = pam_getenv(handle, "XDG_SESSION_ID");
         if (id && !existing) {
index 07d72d214def188909aa17395d4e9edc5a9e03e6..a1087ed31c9965ae0dbe0b4a2cc4691ef7c09f8f 100644 (file)
@@ -207,7 +207,7 @@ static int run(int argc, char *argv[]) {
                 return do_mount(argv[2]);
         if (streq(argv[1], "stop"))
                 return do_umount(argv[2]);
-        assert_not_reached("Unknown verb!");
+        assert_not_reached();
 }
 
 DEFINE_MAIN_FUNCTION(run);
index 5af6bfeafe4af3b40668313505b956670a75b0ad..44fcdaae8194a7448f41c58596007cdf45deae44 100644 (file)
@@ -110,7 +110,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind < argc)
@@ -127,7 +127,6 @@ static int run(int argc, char *argv[]) {
         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
         _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
         _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
-        char buf[SD_ID128_STRING_MAX];
         sd_id128_t id;
         int r;
 
@@ -177,7 +176,7 @@ static int run(int argc, char *argv[]) {
         }
 
         if (arg_print)
-                puts(sd_id128_to_string(id, buf));
+                puts(SD_ID128_TO_STRING(id));
 
         return 0;
 }
index 299e6d8207e4052e92a6837a3749a3420ce3c8fd..c3a2384f1500d9eb4d09525e0b42ae0bf54c1e63 100644 (file)
@@ -2125,7 +2125,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
         assert(bus);
 
         remote = argv[1];
-        if (!http_url_is_valid(remote))
+        if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "URL '%s' is not valid.", remote);
 
@@ -2181,7 +2181,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
         assert(bus);
 
         remote = argv[1];
-        if (!http_url_is_valid(remote))
+        if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "URL '%s' is not valid.", remote);
 
@@ -2510,7 +2510,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --kill-who=WHO           Who to send signal to\n"
                "  -s --signal=SIGNAL          Which signal to send\n"
                "     --uid=USER               Specify user ID to invoke shell as\n"
-               "  -E --setenv=VAR=VALUE       Add an environment variable for shell\n"
+               "  -E --setenv=VAR[=VALUE]     Add an environment variable for shell\n"
                "     --read-only              Create read-only bind mount\n"
                "     --mkdir                  Create directory before bind mounting, if missing\n"
                "  -n --lines=INTEGER          Number of journal entries to show\n"
@@ -2765,13 +2765,9 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'E':
-                        if (!env_assignment_is_valid(optarg))
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Environment assignment invalid: %s", optarg);
-
-                        r = strv_extend(&arg_setenv, optarg);
+                        r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
                         if (r < 0)
-                                return log_oom();
+                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
                         break;
 
                 case ARG_MAX_ADDRESSES:
@@ -2789,7 +2785,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
         }
 
index 009d283acc6450c844b99c807e00e7e3dc6edaab..fc0b0f11ad9494d9ffefbced069d1ab3e7ad9080 100644 (file)
@@ -297,7 +297,7 @@ static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid, char
                 desc = mfree(desc);
 
         *ret_gid = converted_gid;
-        *ret_description = desc;
+        *ret_description = TAKE_PTR(desc);
         return 0;
 }
 
index b2a41a7e755025f67b7604b3acd1992ecfd1824f..eda847cb4a0292e93b15d4e8f7f357d7777ca8c8 100644 (file)
@@ -152,7 +152,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index c213c905a1aa627368c94570960ad2556b7264aa..b0de83b8d0365d63cf00d267eaf8eaf13d3532f8 100644 (file)
@@ -329,7 +329,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL)
@@ -1530,7 +1530,7 @@ static int run(int argc, char* argv[]) {
                 break;
 
         default:
-                assert_not_reached("Unexpected action.");
+                assert_not_reached();
         }
 
         return r;
index 1ac8bf01c5e37ec5606b909bdf075965f6235a5c..624b0a0b5dfda77e0d0b031336031ee939ccb34c 100644 (file)
@@ -157,7 +157,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index 22c0e49d94ced74cd57a88d6f38ed28c1d1dfc87..e210625ac590b7509e0661c57cfcbb45a66ac196 100644 (file)
@@ -29,7 +29,7 @@ static int bare_udp_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *
                 log_netdev_info(netdev, "BareUDP netdev exists, using existing without changing its parameters.");
         else if (r < 0) {
                 log_netdev_warning_errno(netdev, r, "BareUDP netdev could not be created: %m");
-                netdev_drop(netdev);
+                netdev_enter_failed(netdev);
 
                 return 1;
         }
index 6863257a58948099a218f778b50c9df73bceab34..e01de0f1510d66110f85acc044041dec76b8cc58 100644 (file)
@@ -58,7 +58,7 @@ static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **r
                 encap_type = FOU_ENCAP_GUE;
                 break;
         default:
-                assert_not_reached("invalid encap type");
+                assert_not_reached();
         }
 
         r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, encap_type);
@@ -108,7 +108,7 @@ static int fou_tunnel_create_handler(sd_netlink *rtnl, sd_netlink_message *m, Ne
                 log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
         else if (r < 0) {
                 log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
-                netdev_drop(netdev);
+                netdev_enter_failed(netdev);
 
                 return 1;
         }
@@ -243,7 +243,7 @@ static int netdev_fou_tunnel_verify(NetDev *netdev, const char *filename) {
                                                       filename);
                 break;
         default:
-                assert_not_reached("Invalid fou encap type");
+                assert_not_reached();
         }
 
         if (t->peer_family == AF_UNSPEC && t->peer_port > 0)
index fd0b5119e7f3ac79acb139b9895de72ea0ae96e7..1bffbc958654325d5231088008aa092c799668ab 100644 (file)
@@ -37,7 +37,7 @@ static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m,
                 log_netdev_info(netdev, "Geneve netdev exists, using existing without changing its parameters");
         else if (r < 0) {
                 log_netdev_warning_errno(netdev, r, "Geneve netdev could not be created: %m");
-                netdev_drop(netdev);
+                netdev_enter_failed(netdev);
 
                 return 1;
         }
@@ -164,16 +164,18 @@ static int netdev_geneve_create(NetDev *netdev) {
         return r;
 }
 
-int config_parse_geneve_vni(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) {
+int config_parse_geneve_vni(
+                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) {
+
         Geneve *v = userdata;
         uint32_t f;
         int r;
@@ -199,16 +201,18 @@ int config_parse_geneve_vni(const char *unit,
         return 0;
 }
 
-int config_parse_geneve_address(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) {
+int config_parse_geneve_address(
+                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) {
+
         Geneve *v = userdata;
         union in_addr_union *addr = data, buffer;
         int r, f;
@@ -236,16 +240,18 @@ int config_parse_geneve_address(const char *unit,
         return 0;
 }
 
-int config_parse_geneve_flow_label(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) {
+int config_parse_geneve_flow_label(
+                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) {
+
         Geneve *v = userdata;
         uint32_t f;
         int r;
@@ -272,16 +278,18 @@ int config_parse_geneve_flow_label(const char *unit,
         return 0;
 }
 
-int config_parse_geneve_ttl(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) {
+int config_parse_geneve_ttl(
+                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) {
+
         Geneve *v = userdata;
         unsigned f;
         int r;
index 32ec0245581b8b1a3743c96e981372757373a896..f651b2f7bf2c70561aab2e0fe4a6922a3f6d8f93 100644 (file)
@@ -355,7 +355,7 @@ static int l2tp_create_tunnel_handler(sd_netlink *rtnl, sd_netlink_message *m, N
                 log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
         else if (r < 0) {
                 log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
-                netdev_drop(netdev);
+                netdev_enter_failed(netdev);
 
                 return 1;
         }
index 77c5f8c4e7987d02a95694888aa660768e21f53c..74e9fdd10bf68866a1c0ee1430ec58e1b7ba0bd5 100644 (file)
@@ -317,7 +317,7 @@ static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_messa
         else if (r < 0) {
                 log_netdev_warning_errno(netdev, r,
                                          "Failed to add receive secure association: %m");
-                netdev_drop(netdev);
+                netdev_enter_failed(netdev);
 
                 return 1;
         }
@@ -375,7 +375,7 @@ static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *
         else if (r < 0) {
                 log_netdev_warning_errno(netdev, r,
                                          "Failed to add receive secure channel: %m");
-                netdev_drop(netdev);
+                netdev_enter_failed(netdev);
 
                 return 1;
         }
@@ -387,7 +387,7 @@ static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *
                 if (r < 0) {
                         log_netdev_warning_errno(netdev, r,
                                                  "Failed to configure receive security association: %m");
-                        netdev_drop(netdev);
+                        netdev_enter_failed(netdev);
                         return 1;
                 }
         }
@@ -441,7 +441,7 @@ static int macsec_transmit_association_handler(sd_netlink *rtnl, sd_netlink_mess
         else if (r < 0) {
                 log_netdev_warning_errno(netdev, r,
                                          "Failed to add transmit secure association: %m");
-                netdev_drop(netdev);
+                netdev_enter_failed(netdev);
 
                 return 1;
         }
index 46b08261482831ae2bfda6d65cdef2d613d8f20e..9d037c2f3683a5ab6dc45e9491c6d32853bc8f8d 100644 (file)
@@ -5,6 +5,7 @@
 #include "conf-parser.h"
 #include "macvlan.h"
 #include "macvlan-util.h"
+#include "networkd-network.h"
 #include "parse-util.h"
 
 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
@@ -16,6 +17,7 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net
         assert(netdev);
         assert(link);
         assert(netdev->ifname);
+        assert(link->network);
 
         if (netdev->kind == NETDEV_KIND_MACVLAN)
                 m = MACVLAN(netdev);
@@ -52,6 +54,13 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MODE attribute: %m");
         }
 
+        /* set the nopromisc flag if Promiscuous= of the link is explicitly set to false */
+        if (m->mode == NETDEV_MACVLAN_MODE_PASSTHRU && link->network->promiscuous == 0) {
+                r = sd_netlink_message_append_u16(req, IFLA_MACVLAN_FLAGS, MACVLAN_FLAG_NOPROMISC);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_FLAGS attribute: %m");
+        }
+
         if (m->bc_queue_length != UINT32_MAX) {
                 r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_BC_QUEUE_LEN, m->bc_queue_length);
                 if (r < 0)
index 53534d4873410876ca0009a38f960f6d42970376..426be8c7fc7aae67da374666752a2cbad01f327f 100644 (file)
@@ -167,6 +167,54 @@ bool netdev_is_managed(NetDev *netdev) {
         return hashmap_get(netdev->manager->netdevs, netdev->ifname) == netdev;
 }
 
+static bool netdev_is_stacked_and_independent(NetDev *netdev) {
+        assert(netdev);
+
+        if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
+                return false;
+
+        switch (netdev->kind) {
+        case NETDEV_KIND_ERSPAN:
+                return ERSPAN(netdev)->independent;
+        case NETDEV_KIND_GRE:
+                return GRE(netdev)->independent;
+        case NETDEV_KIND_GRETAP:
+                return GRETAP(netdev)->independent;
+        case NETDEV_KIND_IP6GRE:
+                return IP6GRE(netdev)->independent;
+        case NETDEV_KIND_IP6GRETAP:
+                return IP6GRETAP(netdev)->independent;
+        case NETDEV_KIND_IP6TNL:
+                return IP6TNL(netdev)->independent;
+        case NETDEV_KIND_IPIP:
+                return IPIP(netdev)->independent;
+        case NETDEV_KIND_SIT:
+                return SIT(netdev)->independent;
+        case NETDEV_KIND_VTI:
+                return VTI(netdev)->independent;
+        case NETDEV_KIND_VTI6:
+                return VTI6(netdev)->independent;
+        case NETDEV_KIND_VXLAN:
+                return VXLAN(netdev)->independent;
+        case NETDEV_KIND_XFRM:
+                return XFRM(netdev)->independent;
+        default:
+                return false;
+        }
+}
+
+static bool netdev_is_stacked(NetDev *netdev) {
+        assert(netdev);
+
+        if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
+                return false;
+
+        if (netdev_is_stacked_and_independent(netdev))
+                return false;
+
+        return true;
+}
+
 static void netdev_detach_from_manager(NetDev *netdev) {
         if (netdev->ifname && netdev->manager)
                 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
@@ -202,8 +250,18 @@ static NetDev *netdev_free(NetDev *netdev) {
 DEFINE_TRIVIAL_REF_UNREF_FUNC(NetDev, netdev, netdev_free);
 
 void netdev_drop(NetDev *netdev) {
-        if (!netdev || netdev->state == NETDEV_STATE_LINGER)
+        if (!netdev)
+                return;
+
+        if (netdev_is_stacked(netdev)) {
+                /* The netdev may be removed due to the underlying device removal, and the device may
+                 * be re-added later. */
+                netdev->state = NETDEV_STATE_LOADING;
+                netdev->ifindex = 0;
+
+                log_netdev_debug(netdev, "netdev removed");
                 return;
+        }
 
         netdev->state = NETDEV_STATE_LINGER;
 
@@ -232,9 +290,8 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret) {
         return 0;
 }
 
-static int netdev_enter_failed(NetDev *netdev) {
+void netdev_enter_failed(NetDev *netdev) {
         netdev->state = NETDEV_STATE_FAILED;
-        return 0;
 }
 
 static int netdev_enter_ready(NetDev *netdev) {
@@ -266,7 +323,7 @@ static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev
                 log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
         else if (r < 0) {
                 log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
-                netdev_drop(netdev);
+                netdev_enter_failed(netdev);
 
                 return 1;
         }
@@ -528,7 +585,7 @@ int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callb
                         return r;
                 break;
         default:
-                assert_not_reached("Cannot join independent netdev");
+                assert_not_reached();
         }
 
         return 0;
@@ -560,12 +617,12 @@ static bool netdev_is_ready_to_create(NetDev *netdev, Link *link) {
         return true;
 }
 
-int request_process_create_stacked_netdev(Request *req) {
+int request_process_stacked_netdev(Request *req) {
         int r;
 
         assert(req);
         assert(req->link);
-        assert(req->type == REQUEST_TYPE_CREATE_STACKED_NETDEV);
+        assert(req->type == REQUEST_TYPE_STACKED_NETDEV);
         assert(req->netdev);
         assert(req->netlink_handler);
 
@@ -632,46 +689,42 @@ static int link_create_stacked_netdev_after_configured_handler(sd_netlink *rtnl,
         return 0;
 }
 
-int link_request_to_crate_stacked_netdev(Link *link, NetDev *netdev) {
-        NetDevCreateType create_type;
+int link_request_stacked_netdev(Link *link, NetDev *netdev) {
         int r;
 
         assert(link);
         assert(netdev);
 
-        create_type = netdev_get_create_type(netdev);
-        if (!IN_SET(create_type, NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
+        if (!netdev_is_stacked(netdev))
                 return -EINVAL;
 
-        if (netdev->state != NETDEV_STATE_LOADING || netdev->ifindex > 0)
-                /* Already created (or removed?) */
-                return 0;
+        if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0)
+                return 0; /* Already created. */
 
-        if (create_type == NETDEV_CREATE_STACKED) {
+        if (netdev_get_create_type(netdev) == NETDEV_CREATE_STACKED) {
                 link->stacked_netdevs_created = false;
-                r = link_queue_request(link, REQUEST_TYPE_CREATE_STACKED_NETDEV, netdev, false,
+                r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
                                        &link->create_stacked_netdev_messages,
                                        link_create_stacked_netdev_handler,
                                        NULL);
         } else {
                 link->stacked_netdevs_after_configured_created = false;
-                r = link_queue_request(link, REQUEST_TYPE_CREATE_STACKED_NETDEV, netdev, false,
+                r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
                                        &link->create_stacked_netdev_after_configured_messages,
                                        link_create_stacked_netdev_after_configured_handler,
                                        NULL);
         }
         if (r < 0)
-                return log_link_error_errno(link, r, "Failed to request to create stacked netdev '%s': %m",
+                return log_link_error_errno(link, r, "Failed to request stacked netdev '%s': %m",
                                             netdev->ifname);
 
-        log_link_debug(link, "Requested to create stacked netdev '%s'", netdev->ifname);
+        log_link_debug(link, "Requested stacked netdev '%s'", netdev->ifname);
         return 0;
 }
 
 int netdev_load_one(Manager *manager, const char *filename) {
         _cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL;
         const char *dropin_dirname;
-        bool independent = false;
         int r;
 
         assert(manager);
@@ -792,48 +845,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
                         return r;
         }
 
-        switch (netdev->kind) {
-        case NETDEV_KIND_IPIP:
-                independent = IPIP(netdev)->independent;
-                break;
-        case NETDEV_KIND_GRE:
-                independent = GRE(netdev)->independent;
-                break;
-        case NETDEV_KIND_GRETAP:
-                independent = GRETAP(netdev)->independent;
-                break;
-        case NETDEV_KIND_IP6GRE:
-                independent = IP6GRE(netdev)->independent;
-                break;
-        case NETDEV_KIND_IP6GRETAP:
-                independent = IP6GRETAP(netdev)->independent;
-                break;
-        case NETDEV_KIND_SIT:
-                independent = SIT(netdev)->independent;
-                break;
-        case NETDEV_KIND_VTI:
-                independent = VTI(netdev)->independent;
-                break;
-        case NETDEV_KIND_VTI6:
-                independent = VTI6(netdev)->independent;
-                break;
-        case NETDEV_KIND_IP6TNL:
-                independent = IP6TNL(netdev)->independent;
-                break;
-        case NETDEV_KIND_ERSPAN:
-                independent = ERSPAN(netdev)->independent;
-                break;
-        case NETDEV_KIND_XFRM:
-                independent = XFRM(netdev)->independent;
-                break;
-        case NETDEV_KIND_VXLAN:
-                independent = VXLAN(netdev)->independent;
-                break;
-        default:
-                break;
-        }
-
-        if (independent) {
+        if (netdev_is_stacked_and_independent(netdev)) {
                 r = netdev_create(netdev, NULL, NULL);
                 if (r < 0)
                         return r;
index 493ae32b2293450ed47b2287949bc3ff7fc95033..4614f6566e9b84332a0337dc1e15f0547649c4cb 100644 (file)
@@ -184,6 +184,7 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
 int netdev_load(Manager *manager, bool reload);
 int netdev_load_one(Manager *manager, const char *filename);
 void netdev_drop(NetDev *netdev);
+void netdev_enter_failed(NetDev *netdev);
 
 NetDev *netdev_unref(NetDev *netdev);
 NetDev *netdev_ref(NetDev *netdev);
@@ -196,8 +197,8 @@ int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink);
 int netdev_get_mac(const char *ifname, struct ether_addr **ret);
 int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb);
 
-int request_process_create_stacked_netdev(Request *req);
-int link_request_to_crate_stacked_netdev(Link *link, NetDev *netdev);
+int request_process_stacked_netdev(Request *req);
+int link_request_stacked_netdev(Link *link, NetDev *netdev);
 
 const char *netdev_kind_to_string(NetDevKind d) _const_;
 NetDevKind netdev_kind_from_string(const char *d) _pure_;
index 23718081b90bc5c513fae83c6afd44061ec3243a..cae2ef1a909b381d07c65bf8c289de0ebc656891 100644 (file)
@@ -128,7 +128,7 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_
                 t = GRETAP(netdev);
                 break;
         default:
-                assert_not_reached("invalid netdev kind");
+                assert_not_reached();
         }
 
         assert(t);
@@ -457,7 +457,7 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
                 t = ERSPAN(netdev);
                 break;
         default:
-                assert_not_reached("Invalid tunnel kind");
+                assert_not_reached();
         }
 
         assert(t);
@@ -501,16 +501,18 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
         return 0;
 }
 
-int config_parse_tunnel_address(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) {
+int config_parse_tunnel_address(
+                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) {
+
         Tunnel *t = userdata;
         union in_addr_union *addr = data, buffer;
         int r, f;
@@ -555,16 +557,18 @@ int config_parse_tunnel_address(const char *unit,
         return 0;
 }
 
-int config_parse_tunnel_key(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) {
+int config_parse_tunnel_key(
+                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) {
+
         union in_addr_union buffer;
         Tunnel *t = userdata;
         uint32_t k;
@@ -596,16 +600,18 @@ int config_parse_tunnel_key(const char *unit,
         return 0;
 }
 
-int config_parse_ipv6_flowlabel(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) {
+int config_parse_ipv6_flowlabel(
+                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) {
+
         IPv6FlowLabel *ipv6_flowlabel = data;
         Tunnel *t = userdata;
         int k = 0;
@@ -635,16 +641,18 @@ int config_parse_ipv6_flowlabel(const char* unit,
         return 0;
 }
 
-int config_parse_encap_limit(const char* unit,
-                             const char *filename,
-                             unsigned line,
-                             const char *section,
-                             unsigned section_line,
-                             const char *lvalue,
-                             int ltype,
-                             const char *rvalue,
-                             void *data,
-                             void *userdata) {
+int config_parse_encap_limit(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
         Tunnel *t = userdata;
         int k = 0;
         int r;
@@ -673,26 +681,27 @@ int config_parse_encap_limit(const char* unit,
         return 0;
 }
 
-int config_parse_6rd_prefix(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) {
+int config_parse_6rd_prefix(
+                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) {
+
         Tunnel *t = userdata;
+        union in_addr_union p;
+        uint8_t l;
+        int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
 
-        union in_addr_union p;
-        uint8_t l;
-        int r;
-
         r = in_addr_prefix_from_string(rvalue, AF_INET6, &p, &l);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 6rd prefix \"%s\", ignoring: %m", rvalue);
@@ -722,7 +731,7 @@ static void ipip_sit_init(NetDev *n) {
                 t = SIT(n);
                 break;
         default:
-                assert_not_reached("invalid netdev kind");
+                assert_not_reached();
         }
 
         assert(t);
@@ -763,7 +772,7 @@ static void gre_erspan_init(NetDev *n) {
                 t = GRETAP(n);
                 break;
         default:
-                assert_not_reached("invalid netdev kind");
+                assert_not_reached();
         }
 
         assert(t);
index 52d8b3736ccd43afdcc3e06df6d2fa456e5aff0e..6829017f808e37e875eefcc0c8415ec4e2c264f1 100644 (file)
@@ -174,16 +174,18 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
         return r;
 }
 
-int config_parse_vxlan_address(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) {
+int config_parse_vxlan_address(
+                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) {
+
         VxLan *v = userdata;
         union in_addr_union *addr = data, buffer;
         int r, f;
@@ -225,16 +227,18 @@ int config_parse_vxlan_address(const char *unit,
         return 0;
 }
 
-int config_parse_port_range(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) {
+int config_parse_port_range(
+                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) {
+
         VxLan *v = userdata;
         uint16_t low, high;
         int r;
@@ -257,16 +261,18 @@ int config_parse_port_range(const char *unit,
         return 0;
 }
 
-int config_parse_flow_label(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) {
+int config_parse_flow_label(
+                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) {
+
         VxLan *v = userdata;
         unsigned f;
         int r;
@@ -293,16 +299,18 @@ int config_parse_flow_label(const char *unit,
         return 0;
 }
 
-int config_parse_vxlan_ttl(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) {
+int config_parse_vxlan_ttl(
+                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) {
+
         VxLan *v = userdata;
         unsigned f;
         int r;
index 0ebb11cefac50c7151fa01c9e06c3cd12b9d61ef..09a19192bcf698c3519a1de222b01130fd3bba6d 100644 (file)
@@ -2970,7 +2970,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
         }
 
index f4257b3b91b11161a3a0112ed541dd6ef01e9c0d..c0d4e9ae673701a58f114182ebea8d488a36e14d 100644 (file)
@@ -166,7 +166,7 @@ Address *address_free(Address *address) {
                 set_remove(address->link->dhcp6_pd_addresses, address);
                 set_remove(address->link->dhcp6_pd_addresses_old, address);
                 SET_FOREACH(n, address->link->ndisc_addresses)
-                        if (n->address == address)
+                        if (address_equal(n->address, address))
                                 free(set_remove(address->link->ndisc_addresses, n));
 
                 if (address->family == AF_INET6 &&
@@ -1428,7 +1428,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                 break;
 
         default:
-                assert_not_reached("Received unsupported address family");
+                assert_not_reached();
         }
 
         r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &tmp->cinfo);
@@ -1469,7 +1469,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                 break;
 
         default:
-                assert_not_reached("Received invalid RTNL message type");
+                assert_not_reached();
         }
 
         return 1;
index 0f808fb515dbcdbab009ce709e4578d935e6ec65..6eca4708265c980850f427594d155c2d60d8919b 100644 (file)
@@ -168,7 +168,7 @@ static int bridge_mdb_configure(BridgeMDB *mdb, Link *link, link_netlink_message
                 break;
 
         default:
-                assert_not_reached("Invalid address family");
+                assert_not_reached();
         }
 
         /* create new RTM message */
index 950b4134dd6b0129384f78cd40715bb7cbf996a5..7a0a6e552261349b84b518be3e7610926f1ba48b 100644 (file)
 #include "parse-util.h"
 #include "string-util.h"
 
-#define CAN_TERMINATION_OHM_VALUE 120
-
-int config_parse_can_bitrate(
-                const char* unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        uint32_t *br = data;
-        uint64_t sz;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = parse_size(rvalue, 1000, &sz);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse can bitrate '%s', ignoring: %m", rvalue);
-                return 0;
-        }
-
-        /* Linux uses __u32 for bitrates, so the value should not exceed that. */
-        if (sz <= 0 || sz > UINT32_MAX) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Bit rate out of permitted range 1...4294967295");
-                return 0;
-        }
-
-        *br = (uint32_t) sz;
-
-        return 0;
-}
+#define CAN_TERMINATION_DEFAULT_OHM_VALUE 120
 
 int can_set_netlink_message(Link *link, sd_netlink_message *m) {
-        struct can_ctrlmode cm = {};
         int r;
 
         assert(link);
@@ -72,10 +31,11 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) {
         if (r < 0)
                 return log_link_debug_errno(link, r, "Could not open IFLA_INFO_DATA container: %m");
 
-        if (link->network->can_bitrate > 0 || link->network->can_sample_point > 0) {
+        if (link->network->can_bitrate > 0) {
                 struct can_bittiming bt = {
                         .bitrate = link->network->can_bitrate,
                         .sample_point = link->network->can_sample_point,
+                        .sjw = link->network->can_sync_jump_width,
                 };
 
                 log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate);
@@ -87,12 +47,26 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) {
                 r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
                 if (r < 0)
                         return log_link_debug_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m");
+        } else if (link->network->can_time_quanta_ns > 0) {
+                struct can_bittiming bt = {
+                        .tq = link->network->can_time_quanta_ns,
+                        .prop_seg = link->network->can_propagation_segment,
+                        .phase_seg1 = link->network->can_phase_buffer_segment_1,
+                        .phase_seg2 = link->network->can_phase_buffer_segment_2,
+                        .sjw = link->network->can_sync_jump_width,
+                };
+
+                log_link_debug(link, "Setting time quanta = %"PRIu32" nsec", bt.tq);
+                r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt));
+                if (r < 0)
+                        return log_link_debug_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m");
         }
 
-        if (link->network->can_data_bitrate > 0 || link->network->can_data_sample_point > 0) {
+        if (link->network->can_data_bitrate > 0) {
                 struct can_bittiming bt = {
                         .bitrate = link->network->can_data_bitrate,
                         .sample_point = link->network->can_data_sample_point,
+                        .sjw = link->network->can_data_sync_jump_width,
                 };
 
                 log_link_debug(link, "Setting data bitrate = %d bit/s", bt.bitrate);
@@ -104,6 +78,19 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) {
                 r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt));
                 if (r < 0)
                         return log_link_debug_errno(link, r, "Could not append IFLA_CAN_DATA_BITTIMING attribute: %m");
+        } else if (link->network->can_data_time_quanta_ns > 0) {
+                struct can_bittiming bt = {
+                        .tq = link->network->can_data_time_quanta_ns,
+                        .prop_seg = link->network->can_data_propagation_segment,
+                        .phase_seg1 = link->network->can_data_phase_buffer_segment_1,
+                        .phase_seg2 = link->network->can_data_phase_buffer_segment_2,
+                        .sjw = link->network->can_data_sync_jump_width,
+                };
+
+                log_link_debug(link, "Setting data time quanta = %"PRIu32" nsec", bt.tq);
+                r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt));
+                if (r < 0)
+                        return log_link_debug_errno(link, r, "Could not append IFLA_CAN_DATA_BITTIMING attribute: %m");
         }
 
         if (link->network->can_restart_us > 0) {
@@ -114,58 +101,27 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) {
                 else
                         restart_ms = DIV_ROUND_UP(link->network->can_restart_us, USEC_PER_MSEC);
 
-                if (restart_ms > UINT32_MAX)
-                        return log_link_debug_errno(link, SYNTHETIC_ERRNO(ERANGE), "restart timeout (%s) too big.",
-                                                    FORMAT_TIMESPAN(restart_ms * 1000, MSEC_PER_SEC));
-
                 log_link_debug(link, "Setting restart = %s", FORMAT_TIMESPAN(restart_ms * 1000, MSEC_PER_SEC));
-
                 r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms);
                 if (r < 0)
                         return log_link_debug_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m");
         }
 
-        if (link->network->can_fd_mode >= 0) {
-                cm.mask |= CAN_CTRLMODE_FD;
-                SET_FLAG(cm.flags, CAN_CTRLMODE_FD, link->network->can_fd_mode);
-                log_link_debug(link, "Setting FD mode to '%s'.", yes_no(link->network->can_fd_mode));
-        }
-
-        if (link->network->can_non_iso >= 0) {
-                cm.mask |= CAN_CTRLMODE_FD_NON_ISO;
-                SET_FLAG(cm.flags, CAN_CTRLMODE_FD_NON_ISO, link->network->can_non_iso);
-                log_link_debug(link, "Setting FD non-ISO mode to '%s'.", yes_no(link->network->can_non_iso));
-        }
-
-        if (link->network->can_triple_sampling >= 0) {
-                cm.mask |= CAN_CTRLMODE_3_SAMPLES;
-                SET_FLAG(cm.flags, CAN_CTRLMODE_3_SAMPLES, link->network->can_triple_sampling);
-                log_link_debug(link, "Setting triple-sampling to '%s'.", yes_no(link->network->can_triple_sampling));
-        }
-
-        if (link->network->can_berr_reporting >= 0) {
-                cm.mask |= CAN_CTRLMODE_BERR_REPORTING;
-                SET_FLAG(cm.flags, CAN_CTRLMODE_BERR_REPORTING, link->network->can_berr_reporting);
-                log_link_debug(link, "Setting bus error reporting to '%s'.", yes_no(link->network->can_berr_reporting));
-        }
-
-        if (link->network->can_listen_only >= 0) {
-                cm.mask |= CAN_CTRLMODE_LISTENONLY;
-                SET_FLAG(cm.flags, CAN_CTRLMODE_LISTENONLY, link->network->can_listen_only);
-                log_link_debug(link, "Setting listen-only mode to '%s'.", yes_no(link->network->can_listen_only));
-        }
+        if (link->network->can_control_mode_mask != 0) {
+                struct can_ctrlmode cm = {
+                        .mask = link->network->can_control_mode_mask,
+                        .flags = link->network->can_control_mode_flags,
+                };
 
-        if (cm.mask != 0) {
                 r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
                 if (r < 0)
                         return log_link_debug_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m");
         }
 
-        if (link->network->can_termination >= 0) {
-                log_link_debug(link, "Setting can-termination to '%s'.", yes_no(link->network->can_termination));
+        if (link->network->can_termination_set) {
+                log_link_debug(link, "Setting can-termination to '%u'.", link->network->can_termination);
 
-                r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION,
-                                link->network->can_termination ? CAN_TERMINATION_OHM_VALUE : 0);
+                r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION, link->network->can_termination);
                 if (r < 0)
                         return log_link_debug_errno(link, r, "Could not append IFLA_CAN_TERMINATION attribute: %m");
         }
@@ -180,3 +136,205 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) {
 
         return 0;
 }
+
+int config_parse_can_bitrate(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        uint32_t *br = data;
+        uint64_t sz;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = parse_size(rvalue, 1000, &sz);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse can bitrate '%s', ignoring: %m", rvalue);
+                return 0;
+        }
+
+        /* Linux uses __u32 for bitrates, so the value should not exceed that. */
+        if (sz <= 0 || sz > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Bit rate out of permitted range 1...4294967295");
+                return 0;
+        }
+
+        *br = (uint32_t) sz;
+
+        return 0;
+}
+
+int config_parse_can_time_quanta(
+                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) {
+
+        nsec_t val, *tq = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = parse_nsec(rvalue, &val);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse can time quanta '%s', ignoring: %m", rvalue);
+                return 0;
+        }
+
+        /* Linux uses __u32 for bitrates, so the value should not exceed that. */
+        if (val <= 0 || val > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Time quanta out of permitted range 1...4294967295");
+                return 0;
+        }
+
+        *tq = val;
+        return 0;
+}
+
+int config_parse_can_restart_usec(
+                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) {
+
+        usec_t usec, *restart_usec = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = parse_sec(rvalue, &usec);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse CAN restart sec '%s', ignoring: %m", rvalue);
+                return 0;
+        }
+
+        if (usec != USEC_INFINITY &&
+            DIV_ROUND_UP(usec, USEC_PER_MSEC) > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "CAN RestartSec= must be in the range 0...%"PRIu32"ms, ignoring: %s", UINT32_MAX, rvalue);
+                return 0;
+        }
+
+        *restart_usec = usec;
+        return 0;
+}
+
+int config_parse_can_control_mode(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = userdata;
+        uint32_t mask = ltype;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(userdata);
+        assert(mask != 0);
+
+        if (isempty(rvalue)) {
+                network->can_control_mode_mask &= ~mask;
+                network->can_control_mode_flags &= ~mask;
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse CAN control mode '%s', ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        network->can_control_mode_mask |= mask;
+        SET_FLAG(network->can_control_mode_flags, mask, r);
+        return 0;
+}
+
+int config_parse_can_termination(
+                const char* unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = userdata;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                network->can_termination_set = false;
+                return 0;
+        }
+
+        /* Note that 0 termination ohm value means no termination resistor, and there is no conflict
+         * between parse_boolean() and safe_atou16() when Termination=0. However, Termination=1 must be
+         * treated as 1 ohm, instead of true (and then the default ohm value). So, we need to parse the
+         * string with safe_atou16() at first. */
+
+        r = safe_atou16(rvalue, &network->can_termination);
+        if (r < 0) {
+                r = parse_boolean(rvalue);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse CAN termination value, ignoring: %s", rvalue);
+                        return 0;
+                }
+
+                network->can_termination = r ? CAN_TERMINATION_DEFAULT_OHM_VALUE : 0;
+        }
+
+        network->can_termination_set = true;
+        return 0;
+}
index 781494ed3c9a925db147e90e89a6c5a81578d490..3945082498f30bf218ff770d749db20fb1ae7265 100644 (file)
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <linux/can/netlink.h>
+
 #include "sd-netlink.h"
 
 #include "conf-parser.h"
@@ -10,3 +12,7 @@ typedef struct Link Link;
 int can_set_netlink_message(Link *link, sd_netlink_message *m);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_can_bitrate);
+CONFIG_PARSER_PROTOTYPE(config_parse_can_time_quanta);
+CONFIG_PARSER_PROTOTYPE(config_parse_can_restart_usec);
+CONFIG_PARSER_PROTOTYPE(config_parse_can_control_mode);
+CONFIG_PARSER_PROTOTYPE(config_parse_can_termination);
index 249d780887eeca0b972bfa1ab29049dc10e26f43..19c888287c5ecdf0bdfe0dc5ad784a5a059d8b8c 100644 (file)
@@ -96,113 +96,64 @@ const DUID *link_get_duid(Link *link, int family) {
         return duid;
 }
 
-static int link_configure_and_start_dhcp_delayed(Link *link) {
-        int r;
-
-        assert(link);
-
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
-                return 0;
-
-        if (!link->dhcp_client) {
-                r = dhcp4_configure(link);
-                if (r < 0)
-                        return r;
-        }
-
-        if (!link->dhcp6_client) {
-                r = dhcp6_configure(link);
-                if (r < 0)
-                        return r;
-        }
-
-        if (link->set_flags_messages > 0)
-                return 0;
-
-        if (!link_has_carrier(link))
-                return 0;
-
-        r = dhcp4_start(link);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to start DHCPv4 client: %m");
-
-        r = ndisc_start(link);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
-
-        r = dhcp6_start(link);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
-
-        return 0;
-}
-
 static int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
         Manager *manager = userdata;
         const sd_bus_error *e;
         const void *a;
         size_t sz;
-        Link *link;
         int r;
 
         assert(m);
         assert(manager);
 
+        /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
+         * even if the method fails. */
+        manager->has_product_uuid = true;
+
         e = sd_bus_message_get_error(m);
         if (e) {
                 r = sd_bus_error_get_errno(e);
                 log_warning_errno(r, "Could not get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %s",
                                   bus_error_message(e, r));
-                goto configure;
+                return 0;
         }
 
         r = sd_bus_message_read_array(m, 'y', &a, &sz);
         if (r < 0) {
                 log_warning_errno(r, "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
-                goto configure;
+                return 0;
         }
 
         if (sz != sizeof(sd_id128_t)) {
                 log_warning("Invalid product UUID. Falling back to use machine-app-specific ID as DUID-UUID.");
-                goto configure;
+                return 0;
         }
 
+        log_debug("Successfully obtained product UUID");
+
         memcpy(&manager->duid_product_uuid.raw_data, a, sz);
         manager->duid_product_uuid.raw_data_len = sz;
 
-configure:
-        /* To avoid calling GetProductUUID() bus method so frequently, set the flag below
-         * even if the method fails. */
-        manager->has_product_uuid = true;
-
-        while ((link = set_steal_first(manager->links_requesting_uuid))) {
-                r = link_configure_and_start_dhcp_delayed(link);
-                if (r < 0)
-                        link_enter_failed(link);
-
-                link_unref(link);
-        }
-
-        manager->links_requesting_uuid = set_free(manager->links_requesting_uuid);
-
-        return 1;
+        return 0;
 }
 
 int manager_request_product_uuid(Manager *m) {
+        static bool bus_method_is_called = false;
         int r;
 
         assert(m);
 
-        if (m->product_uuid_requested)
+        if (bus_method_is_called)
                 return 0;
 
-        log_debug("Requesting product UUID");
-
-        if (sd_bus_is_ready(m->bus) <= 0) {
+        if (sd_bus_is_ready(m->bus) <= 0 && !m->product_uuid_requested) {
                 log_debug("Not connected to system bus, requesting product UUID later.");
+                m->product_uuid_requested = true;
                 return 0;
         }
 
+        m->product_uuid_requested = false;
+
         r = sd_bus_call_method_async(
                         m->bus,
                         NULL,
@@ -217,7 +168,9 @@ int manager_request_product_uuid(Manager *m) {
         if (r < 0)
                 return log_warning_errno(r, "Failed to get product UUID: %m");
 
-        m->product_uuid_requested = true;
+        log_debug("Requesting product UUID.");
+
+        bus_method_is_called = true;
 
         return 0;
 }
@@ -242,15 +195,11 @@ int dhcp_configure_duid(Link *link, const DUID *duid) {
         if (r < 0) {
                 log_link_warning_errno(link, r,
                                        "Failed to get product UUID. Falling back to use machine-app-specific ID as DUID-UUID: %m");
+
+                m->has_product_uuid = true; /* Do not request UUID again on failure. */
                 return 1;
         }
 
-        r = set_ensure_put(&m->links_requesting_uuid, NULL, link);
-        if (r < 0)
-                return log_oom();
-        if (r > 0)
-                link_ref(link);
-
         return 0;
 }
 
@@ -511,16 +460,17 @@ int config_parse_section_route_table(
         return 0;
 }
 
-int config_parse_iaid(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) {
+int config_parse_iaid(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         Network *network = userdata;
         uint32_t iaid;
@@ -543,8 +493,8 @@ int config_parse_iaid(const char *unit,
                 network->dhcp_iaid = iaid;
                 network->dhcp_iaid_set = true;
                 if (!network->dhcp6_iaid_set_explicitly) {
-                        /* Backward compatibility. Previously, IAID is shared by DHCP4 and DHCP6.
-                         * If DHCP6 IAID is not specified explicitly, then use DHCP4 IAID for DHCP6. */
+                        /* Backward compatibility. Previously, IAID is shared by DHCPv4 and DHCPv6.
+                         * If DHCPv6 IAID is not specified explicitly, then use DHCPv4 IAID for DHCPv6. */
                         network->dhcp6_iaid = iaid;
                         network->dhcp6_iaid_set = true;
                 }
@@ -1036,7 +986,7 @@ int config_parse_manager_duid_type(
 
         assert(manager);
 
-        /* For backward compatibility. Setting both DHCP4 and DHCP6 DUID if they are not specified explicitly. */
+        /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
 
         r = config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
         if (r < 0)
@@ -1066,7 +1016,7 @@ int config_parse_network_duid_type(
         if (r < 0)
                 return r;
 
-        /* For backward compatibility, also set DHCP6 DUID if not specified explicitly. */
+        /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
         return config_parse_duid_type(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
 }
 
@@ -1161,7 +1111,7 @@ int config_parse_manager_duid_rawdata(
 
         assert(manager);
 
-        /* For backward compatibility. Setting both DHCP4 and DHCP6 DUID if they are not specified explicitly. */
+        /* For backward compatibility. Setting both DHCPv4 and DHCPv6 DUID if they are not specified explicitly. */
 
         r = config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &manager->dhcp_duid, manager);
         if (r < 0)
@@ -1191,6 +1141,6 @@ int config_parse_network_duid_rawdata(
         if (r < 0)
                 return r;
 
-        /* For backward compatibility, also set DHCP6 DUID if not specified explicitly. */
+        /* For backward compatibility, also set DHCPv6 DUID if not specified explicitly. */
         return config_parse_duid_rawdata(unit, filename, line, section, section_line, lvalue, false, rvalue, &network->dhcp6_duid, network);
 }
index a38cb99c2cd116590c20966f45a4b650e427525d..81f4a6706bb9cc2486f05321212ef8e8452e4903 100644 (file)
@@ -38,7 +38,7 @@ static int property_get_leases(
         if (r < 0)
                 return r;
 
-        HASHMAP_FOREACH(lease, s->leases_by_client_id) {
+        HASHMAP_FOREACH(lease, s->bound_leases_by_client_id) {
                 r = sd_bus_message_open_container(reply, 'r', "uayayayayt");
                 if (r < 0)
                         return r;
index 9e2faa56753acd19fc52967847a21b85c1ce4731..f190bcf428b029c0802476c000b90b0a49e9e360 100644 (file)
@@ -106,7 +106,7 @@ static int dhcp_server_find_uplink(Link *link, Link **ret) {
         if (link->network->dhcp_server_uplink_index > 0)
                 return link_get_by_index(link->manager, link->network->dhcp_server_uplink_index, ret);
 
-        if (link->network->dhcp_server_uplink_index == 0) {
+        if (link->network->dhcp_server_uplink_index == UPLINK_INDEX_AUTO) {
                 /* It is not necessary to propagate error in automatic selection. */
                 if (manager_find_uplink(link->manager, AF_INET, link, ret) < 0)
                         *ret = NULL;
@@ -202,7 +202,7 @@ static int link_push_uplink_to_dhcp_server(
                 break;
 
         default:
-                assert_not_reached("Unexpected server type");
+                assert_not_reached();
         }
 
         if (use_dhcp_lease_data && link->dhcp_lease) {
@@ -663,55 +663,3 @@ int config_parse_dhcp_server_address(
         network->dhcp_server_address_prefixlen = prefixlen;
         return 0;
 }
-
-int config_parse_dhcp_server_uplink(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        Network *network = userdata;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        if (isempty(rvalue) || streq(rvalue, ":auto")) {
-                network->dhcp_server_uplink_index = 0; /* uplink will be selected automatically */
-                network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name);
-                return 0;
-        }
-
-        if (streq(rvalue, ":none")) {
-                network->dhcp_server_uplink_index = -1; /* uplink will not be selected automatically */
-                network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name);
-                return 0;
-        }
-
-        r = parse_ifindex(rvalue);
-        if (r > 0) {
-                network->dhcp_server_uplink_index = r;
-                network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name);
-                return 0;
-        }
-
-        if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue);
-                return 0;
-        }
-
-        r = free_and_strdup_warn(&network->dhcp_server_uplink_name, rvalue);
-        if (r < 0)
-                return r;
-
-        network->dhcp_server_uplink_index = 0;
-        return 0;
-}
index 3f23f97bb8dbb789a268eba197711e41be5a830c..a02cd995ec049c3247a9417add3d072a7709cdc6 100644 (file)
@@ -15,4 +15,3 @@ int request_process_dhcp_server(Request *req);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_address);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_uplink);
index 8936e68a33f7c4322d1dabb73995e663e5e66240..6b4f6aaabd46d4ba42822a68bd0b7d441f996e17 100644 (file)
@@ -992,6 +992,12 @@ static int dhcp4_request_address(Link *link, bool announce) {
         addr->route_metric = link->network->dhcp_route_metric;
         addr->duplicate_address_detection = link->network->dhcp_send_decline ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_NO;
 
+        if (link->network->dhcp_label) {
+                addr->label = strdup(link->network->dhcp_label);
+                if (!addr->label)
+                        return log_oom();
+        }
+
         if (address_get(link, addr, NULL) < 0)
                 link->dhcp4_configured = false;
 
@@ -1311,7 +1317,7 @@ static int dhcp4_set_hostname(Link *link) {
         else {
                 r = gethostname_strict(&hostname);
                 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
-                        return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to get hostname: %m");
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to get hostname: %m");
 
                 hn = hostname;
         }
@@ -1319,9 +1325,9 @@ static int dhcp4_set_hostname(Link *link) {
         r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
         if (r == -EINVAL && hostname)
                 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
-                log_link_debug_errno(link, r, "DHCP4 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
+                log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
         else if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set hostname: %m");
+                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set hostname: %m");
 
         return 0;
 }
@@ -1351,7 +1357,7 @@ static int dhcp4_set_client_identifier(Link *link) {
                                                          duid->raw_data_len > 0 ? duid->raw_data : NULL,
                                                          duid->raw_data_len);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set IAID+DUID: %m");
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set IAID+DUID: %m");
                 break;
         }
         case DHCP_CLIENT_ID_DUID_ONLY: {
@@ -1367,7 +1373,7 @@ static int dhcp4_set_client_identifier(Link *link) {
                                                     duid->raw_data_len > 0 ? duid->raw_data : NULL,
                                                     duid->raw_data_len);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set DUID: %m");
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set DUID: %m");
                 break;
         }
         case DHCP_CLIENT_ID_MAC: {
@@ -1385,25 +1391,16 @@ static int dhcp4_set_client_identifier(Link *link) {
                                                  hw_addr,
                                                  hw_addr_len);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set client ID: %m");
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set client ID: %m");
                 break;
         }
         default:
-                assert_not_reached("Unknown client identifier type.");
+                assert_not_reached();
         }
 
         return 0;
 }
 
-static int dhcp4_configure_duid(Link *link) {
-        assert(link);
-
-        if (!IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
-                return 1;
-
-        return dhcp_configure_duid(link, link_get_dhcp4_duid(link));
-}
-
 static int dhcp4_set_request_address(Link *link) {
         Address *a;
 
@@ -1424,7 +1421,7 @@ static int dhcp4_set_request_address(Link *link) {
         if (!a)
                 return 0;
 
-        log_link_debug(link, "DHCP4 CLIENT: requesting " IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
+        log_link_debug(link, "DHCPv4 CLIENT: requesting " IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
 
         return sd_dhcp_client_set_request_address(link->dhcp_client, &a->in_addr.in);
 }
@@ -1445,15 +1442,15 @@ static bool link_needs_dhcp_broadcast(Link *link) {
         if (r < 0 && link->sd_device && sd_device_get_property_value(link->sd_device, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
                 r = parse_boolean(val);
                 if (r < 0)
-                        log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
+                        log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
                 else
-                        log_link_debug(link, "DHCP4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
+                        log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
 
         }
         return r == true;
 }
 
-int dhcp4_configure(Link *link) {
+static int dhcp4_configure(Link *link) {
         sd_dhcp_option *send_option;
         void *request_options;
         int r;
@@ -1461,88 +1458,81 @@ int dhcp4_configure(Link *link) {
         assert(link);
         assert(link->network);
 
-        if (!link_dhcp4_enabled(link))
-                return 0;
-
         if (link->dhcp_client)
-                return -EBUSY; /* Already configured. */
-
-        r = dhcp4_configure_duid(link);
-        if (r <= 0)
-                return r;
+                return log_link_debug_errno(link, SYNTHETIC_ERRNO(EBUSY), "DHCPv4 client is already configured.");
 
         r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
         if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to allocate DHCP4 client: %m");
+                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to allocate DHCPv4 client: %m");
 
         r = sd_dhcp_client_attach_event(link->dhcp_client, link->manager->event, 0);
         if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to attach event to DHCP4 client: %m");
+                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to attach event to DHCPv4 client: %m");
 
         r = sd_dhcp_client_set_mac(link->dhcp_client,
                                    link->hw_addr.bytes,
                                    link->bcast_addr.length > 0 ? link->bcast_addr.bytes : NULL,
                                    link->hw_addr.length, link->iftype);
         if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set MAC address: %m");
+                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set MAC address: %m");
 
         r = sd_dhcp_client_set_ifindex(link->dhcp_client, link->ifindex);
         if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set ifindex: %m");
+                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set ifindex: %m");
 
         r = sd_dhcp_client_set_callback(link->dhcp_client, dhcp4_handler, link);
         if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set callback: %m");
+                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set callback: %m");
 
         r = sd_dhcp_client_set_request_broadcast(link->dhcp_client, link_needs_dhcp_broadcast(link));
         if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for broadcast: %m");
+                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for broadcast: %m");
 
         if (link->mtu > 0) {
                 r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set MTU: %m");
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set MTU: %m");
         }
 
         if (!link->network->dhcp_anonymize) {
                 if (link->network->dhcp_use_mtu) {
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_INTERFACE_MTU);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for MTU: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for MTU: %m");
                 }
 
                 if (link->network->dhcp_use_routes) {
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_STATIC_ROUTE);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for static route: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for static route: %m");
 
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for classless static route: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for classless static route: %m");
                 }
 
                 if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for domain search list: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for domain search list: %m");
                 }
 
                 if (link->network->dhcp_use_ntp) {
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for NTP server: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for NTP server: %m");
                 }
 
                 if (link->network->dhcp_use_sip) {
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_SIP_SERVER);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for SIP server: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for SIP server: %m");
                 }
 
                 if (link->network->dhcp_use_timezone) {
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for timezone: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for timezone: %m");
                 }
 
                 SET_FOREACH(request_options, link->network->dhcp_request_options) {
@@ -1550,7 +1540,7 @@ int dhcp4_configure(Link *link) {
 
                         r = sd_dhcp_client_set_request_option(link->dhcp_client, option);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for '%u': %m", option);
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for '%u': %m", option);
                 }
 
                 ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_options) {
@@ -1558,7 +1548,7 @@ int dhcp4_configure(Link *link) {
                         if (r == -EEXIST)
                                 continue;
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set send option: %m");
                 }
 
                 ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_vendor_options) {
@@ -1566,7 +1556,7 @@ int dhcp4_configure(Link *link) {
                         if (r == -EEXIST)
                                 continue;
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set send option: %m");
                 }
 
                 r = dhcp4_set_hostname(link);
@@ -1577,49 +1567,49 @@ int dhcp4_configure(Link *link) {
                         r = sd_dhcp_client_set_vendor_class_identifier(link->dhcp_client,
                                                                        link->network->dhcp_vendor_class_identifier);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set vendor class identifier: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set vendor class identifier: %m");
                 }
 
                 if (link->network->dhcp_mudurl) {
                         r = sd_dhcp_client_set_mud_url(link->dhcp_client, link->network->dhcp_mudurl);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set MUD URL: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set MUD URL: %m");
                 }
 
                 if (link->network->dhcp_user_class) {
                         r = sd_dhcp_client_set_user_class(link->dhcp_client, link->network->dhcp_user_class);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set user class: %m");
+                                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set user class: %m");
                 }
         }
 
         if (link->network->dhcp_client_port > 0) {
                 r = sd_dhcp_client_set_client_port(link->dhcp_client, link->network->dhcp_client_port);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set listen port: %m");
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set listen port: %m");
         }
 
         if (link->network->dhcp_max_attempts > 0) {
                 r = sd_dhcp_client_set_max_attempts(link->dhcp_client, link->network->dhcp_max_attempts);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set max attempts: %m");
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set max attempts: %m");
         }
 
         if (link->network->dhcp_ip_service_type > 0) {
                 r = sd_dhcp_client_set_service_type(link->dhcp_client, link->network->dhcp_ip_service_type);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set IP service type: %m");
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set IP service type: %m");
         }
 
         if (link->network->dhcp_fallback_lease_lifetime > 0) {
                 r = sd_dhcp_client_set_fallback_lease_lifetime(link->dhcp_client, link->network->dhcp_fallback_lease_lifetime);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed set to lease lifetime: %m");
+                        return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed set to lease lifetime: %m");
         }
 
         r = dhcp4_set_request_address(link);
         if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set initial DHCPv4 address: %m");
+                return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set initial DHCPv4 address: %m");
 
         return dhcp4_set_client_identifier(link);
 }
@@ -1642,17 +1632,83 @@ int dhcp4_update_mac(Link *link) {
 }
 
 int dhcp4_start(Link *link) {
+        int r;
+
         assert(link);
 
         if (!link->dhcp_client)
                 return 0;
 
+        if (!link_has_carrier(link))
+                return 0;
+
         if (sd_dhcp_client_is_running(link->dhcp_client) > 0)
                 return 0;
 
-        log_link_debug(link, "Acquiring DHCPv4 lease");
+        r = sd_dhcp_client_start(link->dhcp_client);
+        if (r < 0)
+                return r;
 
-        return sd_dhcp_client_start(link->dhcp_client);
+        return 1;
+}
+
+static int dhcp4_configure_duid(Link *link) {
+        assert(link);
+
+        if (!IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY))
+                return 1;
+
+        return dhcp_configure_duid(link, link_get_dhcp4_duid(link));
+}
+
+int request_process_dhcp4_client(Request *req) {
+        Link *link;
+        int r;
+
+        assert(req);
+        assert(req->link);
+        assert(req->type == REQUEST_TYPE_DHCP4_CLIENT);
+
+        link = req->link;
+
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                return 0;
+
+        r = dhcp4_configure_duid(link);
+        if (r <= 0)
+                return r;
+
+        r = dhcp4_configure(req->link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to configure DHCPv4 client: %m");
+
+        r = dhcp4_start(link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to start DHCPv4 client: %m");
+
+        log_link_debug(link, "DHCPv4 client is configured%s.",
+                       r > 0 ? ", acquiring DHCPv4 lease" : "");
+
+        return 1;
+}
+
+int link_request_dhcp4_client(Link *link) {
+        int r;
+
+        assert(link);
+
+        if (!link_dhcp4_enabled(link))
+                return 0;
+
+        if (link->dhcp_client)
+                return 0;
+
+        r = link_queue_request(link, REQUEST_TYPE_DHCP4_CLIENT, NULL, false, NULL, NULL, NULL);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to request configuring of the DHCPv4 client: %m");
+
+        log_link_debug(link, "Requested configuring of the DHCPv4 client.");
+        return 0;
 }
 
 int config_parse_dhcp_max_attempts(
@@ -1788,7 +1844,8 @@ int config_parse_dhcp_ip_service_type(
         return 0;
 }
 
-int config_parse_dhcp_fallback_lease_lifetime(const char *unit,
+int config_parse_dhcp_fallback_lease_lifetime(
+                const char *unit,
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1798,6 +1855,7 @@ int config_parse_dhcp_fallback_lease_lifetime(const char *unit,
                 const char *rvalue,
                 void *data,
                 void *userdata) {
+
         Network *network = userdata;
         uint32_t k;
 
@@ -1826,6 +1884,39 @@ int config_parse_dhcp_fallback_lease_lifetime(const char *unit,
         return 0;
 }
 
+int config_parse_dhcp_label(
+                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 **label = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *label = mfree(*label);
+                return 0;
+        }
+
+        if (!address_label_valid(rvalue)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Address label is too long or invalid, ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        return free_and_strdup_warn(label, rvalue);
+}
+
 static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
         [DHCP_CLIENT_ID_MAC] = "mac",
         [DHCP_CLIENT_ID_DUID] = "duid",
index e41e39fc6e941ade3189914d3d1071124c300809..541fd62aa2325e96e8601ada00d8caaee3bc89fd 100644 (file)
@@ -4,6 +4,8 @@
 #include "conf-parser.h"
 
 typedef struct Link Link;
+typedef struct Network Network;
+typedef struct Request Request;
 
 typedef enum DHCPClientIdentifier {
         DHCP_CLIENT_ID_MAC,
@@ -18,14 +20,17 @@ typedef enum DHCPClientIdentifier {
 } DHCPClientIdentifier;
 
 void network_adjust_dhcp4(Network *network);
-int dhcp4_configure(Link *link);
 int dhcp4_update_mac(Link *link);
 int dhcp4_start(Link *link);
 int dhcp4_lease_lost(Link *link);
 
+int request_process_dhcp4_client(Request *req);
+int link_request_dhcp4_client(Link *link);
+
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_acl_ip_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_ip_service_type);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_mud_url);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_fallback_lease_lifetime);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_label);
index f03bf0651b182ac7e1f6b6be3f2a9e49b95dea50..2f755bc4ebb18ef8ae8caa2392e908f7c10efb4c 100644 (file)
@@ -920,7 +920,7 @@ static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_
         log_link_full(link,
                       set_contains(link->dhcp6_pd_prefixes, p) ? LOG_DEBUG :
                       prefixlen > 64 || prefixlen < 48 ? LOG_WARNING : LOG_INFO,
-                      "DHCP6: received PD Prefix %s%s",
+                      "DHCPv6: received PD Prefix %s%s",
                       strna(buf),
                       prefixlen > 64 ? " with prefix length > 64, ignoring." :
                       prefixlen < 48 ? " with prefix length < 48, looks unusual.": "");
@@ -928,7 +928,7 @@ static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_
         /* Store PD prefix even if prefixlen > 64, not to make logged at warning level so frequently. */
         r = set_ensure_put(&link->dhcp6_pd_prefixes, &in_addr_prefix_hash_ops_free, p);
         if (r < 0)
-                return log_link_error_errno(link, r, "Failed to store DHCP6 PD prefix %s: %m", strna(buf));
+                return log_link_error_errno(link, r, "Failed to store DHCPv6 PD prefix %s: %m", strna(buf));
         if (r > 0)
                 TAKE_PTR(p);
 
@@ -1393,6 +1393,8 @@ int dhcp6_request_information(Link *link, int ir) {
 }
 
 int dhcp6_start(Link *link) {
+        int r;
+
         assert(link);
 
         if (!link->dhcp6_client)
@@ -1401,6 +1403,9 @@ int dhcp6_start(Link *link) {
         if (!link_dhcp6_enabled(link))
                 return 0;
 
+        if (!link_has_carrier(link))
+                return 0;
+
         if (link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_NO)
                 return 0;
 
@@ -1412,9 +1417,11 @@ int dhcp6_start(Link *link) {
         if (sd_dhcp6_client_is_running(link->dhcp6_client) > 0)
                 return 0;
 
-        log_link_debug(link, "Acquiring DHCPv6 lease");
+        r = dhcp6_request_information(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
+        if (r < 0)
+                return r;
 
-        return dhcp6_request_information(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
+        return 1;
 }
 
 int dhcp6_request_prefix_delegation(Link *link) {
@@ -1505,7 +1512,7 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
         else {
                 r = gethostname_strict(&hostname);
                 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
-                        return r;
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to get hostname: %m");
 
                 hn = hostname;
         }
@@ -1513,9 +1520,9 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
         r = sd_dhcp6_client_set_fqdn(client, hn);
         if (r == -EINVAL && hostname)
                 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
-                log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
+                log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
         else if (r < 0)
-                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
+                return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set hostname: %m");
 
         return 0;
 }
@@ -1571,7 +1578,7 @@ static int dhcp6_set_identifier(Link *link, sd_dhcp6_client *client) {
         return 0;
 }
 
-int dhcp6_configure(Link *link) {
+static int dhcp6_configure(Link *link) {
         _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
         sd_dhcp6_option *vendor_option;
         sd_dhcp6_option *send_option;
@@ -1581,36 +1588,29 @@ int dhcp6_configure(Link *link) {
         assert(link);
         assert(link->network);
 
-        if (!link_dhcp6_enabled(link) && !link_ipv6_accept_ra_enabled(link))
-                return 0;
-
         if (link->dhcp6_client)
-                return -EBUSY;
-
-        r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
-        if (r <= 0)
-                return r;
+                return log_link_debug_errno(link, SYNTHETIC_ERRNO(EBUSY), "DHCPv6 client is already configured.");
 
         r = sd_dhcp6_client_new(&client);
         if (r == -ENOMEM)
-                return log_oom();
+                return log_oom_debug();
         if (r < 0)
-                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
+                return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to create DHCPv6 client: %m");
 
         r = sd_dhcp6_client_attach_event(client, link->manager->event, 0);
         if (r < 0)
-                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
+                return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to attach event: %m");
 
         r = dhcp6_set_identifier(link, client);
         if (r < 0)
-                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set identifier: %m");
+                return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set identifier: %m");
 
         ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options) {
                 r = sd_dhcp6_client_add_option(client, send_option);
                 if (r == -EEXIST)
                         continue;
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set option: %m");
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set option: %m");
         }
 
         r = dhcp6_set_hostname(client, link);
@@ -1619,18 +1619,18 @@ int dhcp6_configure(Link *link) {
 
         r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
         if (r < 0)
-                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
+                return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set ifindex: %m");
 
         if (link->network->dhcp6_rapid_commit) {
                 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set request flag for rapid commit: %m");
         }
 
         if (link->network->dhcp6_mudurl) {
                 r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MUD URL: %m");
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set MUD URL: %m");
         }
 
         SET_FOREACH(request_options, link->network->dhcp6_request_options) {
@@ -1638,23 +1638,23 @@ int dhcp6_configure(Link *link) {
 
                 r = sd_dhcp6_client_set_request_option(client, option);
                 if (r == -EEXIST) {
-                        log_link_debug(link, "DHCP6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
+                        log_link_debug(link, "DHCPv6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
                         continue;
                 }
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for '%u': %m", option);
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set request flag for '%u': %m", option);
         }
 
         if (link->network->dhcp6_user_class) {
                 r = sd_dhcp6_client_set_request_user_class(client, link->network->dhcp6_user_class);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set user class: %m");
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set user class: %m");
         }
 
         if (link->network->dhcp6_vendor_class) {
                 r = sd_dhcp6_client_set_request_vendor_class(client, link->network->dhcp6_vendor_class);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor class: %m");
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set vendor class: %m");
         }
 
         ORDERED_HASHMAP_FOREACH(vendor_option, link->network->dhcp6_client_send_vendor_options) {
@@ -1662,23 +1662,23 @@ int dhcp6_configure(Link *link) {
                 if (r == -EEXIST)
                         continue;
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor option: %m");
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set vendor option: %m");
         }
 
         r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
         if (r < 0)
-                return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
+                return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set callback: %m");
 
         if (dhcp6_enable_prefix_delegation(link)) {
                 r = sd_dhcp6_client_set_prefix_delegation(client, true);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix delegation: %m");
         }
 
         if (link->network->dhcp6_pd_length > 0) {
                 r = sd_dhcp6_client_set_prefix_delegation_hint(client, link->network->dhcp6_pd_length, &link->network->dhcp6_pd_address);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix hint: %m");
+                        return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix hint: %m");
         }
 
         link->dhcp6_client = TAKE_PTR(client);
@@ -1716,6 +1716,60 @@ int dhcp6_update_mac(Link *link) {
         return 0;
 }
 
+int request_process_dhcp6_client(Request *req) {
+        Link *link;
+        int r;
+
+        assert(req);
+        assert(req->link);
+        assert(req->type == REQUEST_TYPE_DHCP6_CLIENT);
+
+        link = req->link;
+
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                return 0;
+
+        r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
+        if (r <= 0)
+                return r;
+
+        r = dhcp6_configure(link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to configure DHCPv6 client: %m");
+
+        r = ndisc_start(link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
+
+        r = dhcp6_start(link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
+
+        log_link_debug(link, "DHCPv6 client is configured%s.",
+                       r > 0 ? ", acquiring DHCPv6 lease" : "");
+
+        return 1;
+}
+
+int link_request_dhcp6_client(Link *link) {
+        int r;
+
+        assert(link);
+
+        if (!link_dhcp6_enabled(link) && !link_ipv6_accept_ra_enabled(link))
+                return 0;
+
+        if (link->dhcp6_client)
+                return 0;
+
+        r = link_queue_request(link, REQUEST_TYPE_DHCP6_CLIENT, NULL, false, NULL, NULL, NULL);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to request configuring of the DHCPv6 client: %m");
+
+        log_link_debug(link, "Requested configuring of the DHCPv6 client.");
+        return 0;
+}
+
 int link_serialize_dhcp6_client(Link *link, FILE *f) {
         _cleanup_free_ char *duid = NULL;
         uint32_t iaid;
index 82becb03217e18ad32f1210e79306764aee5735c..23612454d94e32315a8f14a1d93905a416835a98 100644 (file)
@@ -1,9 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include "sd-dhcp6-client.h"
-
 #include "conf-parser.h"
+#include "in-addr-util.h"
 #include "macro.h"
 
 typedef enum DHCP6ClientStartMode {
@@ -15,7 +14,7 @@ typedef enum DHCP6ClientStartMode {
 } DHCP6ClientStartMode;
 
 typedef struct Link Link;
-typedef struct Manager Manager;
+typedef struct Request Request;
 
 typedef struct DHCP6DelegatedPrefix {
         struct in6_addr prefix;     /* Prefix assigned to the link */
@@ -29,12 +28,14 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free);
 bool link_dhcp6_with_address_enabled(Link *link);
 bool link_dhcp6_pd_is_enabled(Link *link);
 int dhcp6_pd_remove(Link *link);
-int dhcp6_configure(Link *link);
 int dhcp6_update_mac(Link *link);
 int dhcp6_start(Link *link);
 int dhcp6_request_information(Link *link, int ir);
 int dhcp6_request_prefix_delegation(Link *link);
 
+int request_process_dhcp6_client(Request *req);
+int link_request_dhcp6_client(Link *link);
+
 int link_serialize_dhcp6_client(Link *link, FILE *f);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
index c2cadc0a3028c79b54e733b0a51ddf3cff843b10..5c57032f2a851e4d7214504d45fb977e6e11195d 100644 (file)
@@ -117,7 +117,7 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
                 break;
 
         default:
-                assert_not_reached("Invalid IPv4ACD event.");
+                assert_not_reached();
         }
 }
 
index a6f8174b80fd4d87020961ccfba323e99498eb9b..59313bcdfd6c8949f1da752d2fab8d82ba313338 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/if.h>
 #include <linux/if_arp.h>
 #include <linux/if_link.h>
+#include <linux/netdevice.h>
 #include <sys/socket.h>
 #include <unistd.h>
 
@@ -506,7 +507,7 @@ void link_check_ready(Link *link) {
             !link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address &&
             !link->ipv4ll_address_configured)
                 /* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */
-                return (void) log_link_debug(link, "%s(): DHCP4, DHCP6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__);
+                return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__);
 
         /* Ignore NDisc when ConfigureWithoutCarrier= is enabled, as IPv6AcceptRA= is enabled by default. */
         if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) ||
@@ -521,8 +522,8 @@ void link_check_ready(Link *link) {
                         /* When DHCP[46], NDisc, or IPv4LL is enabled, at least one protocol must be finished. */
                         return (void) log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__);
 
-                log_link_debug(link, "%s(): dhcp4:%s ipv4ll:%s dhcp6_addresses:%s dhcp6_routes:%s "
-                               "dhcp6_pd_addresses:%s dhcp6_pd_routes:%s ndisc_addresses:%s ndisc_routes:%s",
+                log_link_debug(link, "%s(): DHCPv4:%s IPv4LL:%s DHCPv6_addresses:%s DHCPv6_routes:%s "
+                               "DHCPv6PD_addresses:%s DHCPv6PD_routes:%s NDisc_addresses:%s NDisc_routes:%s",
                                __func__,
                                yes_no(link->dhcp4_configured),
                                yes_no(link->ipv4ll_address_configured),
@@ -593,7 +594,7 @@ static int link_request_stacked_netdevs(Link *link) {
         link->stacked_netdevs_after_configured_created = false;
 
         HASHMAP_FOREACH(netdev, link->network->stacked_netdevs) {
-                r = link_request_to_crate_stacked_netdev(link, netdev);
+                r = link_request_stacked_netdev(link, netdev);
                 if (r < 0)
                         return r;
         }
@@ -617,12 +618,8 @@ static int link_acquire_dynamic_ipv6_conf(Link *link) {
 
                 log_link_debug(link, "Starting IPv6 Router Advertisements");
 
-                r = radv_emit_dns(link);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to configure DNS or Domains in IPv6 Router Advertisement: %m");
-
                 r = sd_radv_start(link->radv);
-                if (r < 0 && r != -EBUSY)
+                if (r < 0)
                         return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
         }
 
@@ -653,12 +650,14 @@ static int link_acquire_dynamic_ipv4_conf(Link *link) {
                 if (r < 0)
                         return log_link_warning_errno(link, r, "Failed to start DHCPv4 client: %m");
 
-        } else if (link->ipv4ll) {
-                log_link_debug(link, "Acquiring IPv4 link-local address");
+                log_link_debug(link, "Acquiring DHCPv4 lease.");
 
+        } else if (link->ipv4ll) {
                 r = sd_ipv4ll_start(link->ipv4ll);
                 if (r < 0)
                         return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
+
+                log_link_debug(link, "Acquiring IPv4 link-local address.");
         }
 
         if (link->dhcp_server) {
@@ -693,6 +692,12 @@ static int link_acquire_dynamic_conf(Link *link) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m");
 
+        if (link->lldp) {
+                r = sd_lldp_start(link->lldp);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Failed to start LLDP client: %m");
+        }
+
         return 0;
 }
 
@@ -978,8 +983,6 @@ static Link *link_drop(Link *link) {
 
         link_drop_from_master(link);
 
-        link_unref(set_remove(link->manager->links_requesting_uuid, link));
-
         (void) unlink(link->state_file);
         link_clean(link);
 
@@ -1002,6 +1005,16 @@ static int link_drop_foreign_config(Link *link) {
         assert(link);
         assert(link->manager);
 
+        /* Drop foreign config, but ignore unmanaged, loopback, or critical interfaces. We do not want
+         * to remove loopback address or addresses used for root NFS. */
+
+        if (IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED))
+                return 0;
+        if (FLAGS_SET(link->flags, IFF_LOOPBACK))
+                return 0;
+        if (link->network->keep_configuration == KEEP_CONFIGURATION_YES)
+                return 0;
+
         r = link_drop_foreign_routes(link);
 
         k = link_drop_foreign_nexthops(link);
@@ -1130,11 +1143,11 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        r = dhcp4_configure(link);
+        r = link_request_dhcp4_client(link);
         if (r < 0)
                 return r;
 
-        r = dhcp6_configure(link);
+        r = link_request_dhcp6_client(link);
         if (r < 0)
                 return r;
 
@@ -1146,7 +1159,7 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        r = radv_configure(link);
+        r = link_request_radv(link);
         if (r < 0)
                 return r;
 
@@ -1154,14 +1167,9 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        /* Drop foreign config, but ignore loopback or critical devices.
-         * We do not want to remove loopback address or addresses used for root NFS. */
-        if (!(link->flags & IFF_LOOPBACK) &&
-            link->network->keep_configuration != KEEP_CONFIGURATION_YES) {
-                r = link_drop_foreign_config(link);
-                if (r < 0)
-                        return r;
-        }
+        r = link_drop_foreign_config(link);
+        if (r < 0)
+                return r;
 
         r = link_request_static_configs(link);
         if (r < 0)
@@ -1227,23 +1235,22 @@ static int link_get_network(Link *link, Network **ret) {
 }
 
 static int link_reconfigure_impl(Link *link, bool force) {
-        Network *network;
+        Network *network = NULL;
         int r;
 
         assert(link);
 
         r = link_get_network(link, &network);
-        if (r == -ENOENT) {
-                link_set_state(link, LINK_STATE_UNMANAGED);
-                return 0;
-        }
-        if (r < 0)
+        if (r < 0 && r != -ENOENT)
                 return r;
 
         if (link->network == network && !force)
                 return 0;
 
-        log_link_info(link, "Re-configuring with %s", network->filename);
+        if (network)
+                log_link_info(link, "Reconfiguring with %s.", network->filename);
+        else
+                log_link_info(link, "Unmanaging interface.");
 
         /* Dropping old .network file */
         r = link_stop_engines(link, false);
@@ -1256,17 +1263,14 @@ static int link_reconfigure_impl(Link *link, bool force) {
         if (r < 0)
                 return r;
 
-        if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) {
-                log_link_debug(link, "State is %s, dropping foreign config", link_state_to_string(link->state));
-                r = link_drop_foreign_config(link);
-                if (r < 0)
-                        return r;
-        }
-
         link_free_carrier_maps(link);
         link_free_engines(link);
         link->network = network_unref(link->network);
-        link_unref(set_remove(link->manager->links_requesting_uuid, link));
+
+        if (!network) {
+                link_set_state(link, LINK_STATE_UNMANAGED);
+                return 0;
+        }
 
         /* Then, apply new .network file */
         link->network = network_ref(network);
@@ -1287,44 +1291,107 @@ static int link_reconfigure_impl(Link *link, bool force) {
         return 1;
 }
 
-static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force) {
+static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force, bool update_wifi) {
+        bool link_was_lower_up;
         int r;
 
+        assert(link);
+
+        link_was_lower_up = link->flags & IFF_LOWER_UP;
+
         r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state");
         if (r <= 0)
                 return r;
 
+        if (update_wifi && link_was_lower_up && link->flags & IFF_LOWER_UP) {
+                /* If the interface's L1 was not up, then wifi_get_info() is already called in
+                 * link_update_flags(). So, it is not necessary to re-call here. */
+                r = wifi_get_info(link);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return 0;
+                }
+        }
+
         r = link_reconfigure_impl(link, force);
-        if (r < 0)
+        if (r < 0) {
                 link_enter_failed(link);
+                return 0;
+        }
 
-        return 0;
+        return r;
 }
 
 static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return link_reconfigure_handler_internal(rtnl, m, link, false);
+        return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ false);
 }
 
 static int link_force_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return link_reconfigure_handler_internal(rtnl, m, link, true);
+        return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true, /* update_wifi = */ false);
 }
 
-int link_reconfigure(Link *link, bool force) {
+static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+
+        r = link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ true);
+        if (r != 0)
+                return r;
+
+        /* r == 0 means an error occurs, the link is unmanaged, or the matching network file is unchanged. */
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                return 0;
+
+        /* re-request static configs, and restart engines. */
+        r = link_stop_engines(link, false);
+        if (r < 0) {
+                link_enter_failed(link);
+                return 0;
+        }
+
+        r = link_acquire_dynamic_conf(link);
+        if (r < 0) {
+                link_enter_failed(link);
+                return 0;
+        }
+
+        r = link_request_static_configs(link);
+        if (r < 0) {
+                link_enter_failed(link);
+                return 0;
+        }
+
+        return 0;
+}
+
+static int link_reconfigure_internal(Link *link, link_netlink_message_handler_t callback) {
         int r;
 
+        assert(link);
+        assert(callback);
+
         /* When link in pending or initialized state, then link_configure() will be called. To prevent
          * the function from being called multiple times simultaneously, refuse to reconfigure the
          * interface in these cases. */
         if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_LINGER))
                 return 0; /* 0 means no-op. */
 
-        r = link_call_getlink(link, force ? link_force_reconfigure_handler : link_reconfigure_handler);
+        r = link_call_getlink(link, callback);
         if (r < 0)
                 return r;
 
         return 1; /* 1 means the interface will be reconfigured. */
 }
 
+int link_reconfigure(Link *link, bool force) {
+        return link_reconfigure_internal(link, force ? link_force_reconfigure_handler : link_reconfigure_handler);
+}
+
+int link_reconfigure_after_sleep(Link *link) {
+        return link_reconfigure_internal(link, link_reconfigure_after_sleep_handler);
+}
+
 static int link_initialized_and_synced(Link *link) {
         Network *network;
         int r;
@@ -1563,34 +1630,7 @@ static int link_carrier_lost(Link *link) {
         if (r < 0)
                 return r;
 
-        if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) {
-                log_link_debug(link, "State is %s, dropping foreign config", link_state_to_string(link->state));
-                r = link_drop_foreign_config(link);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-int link_carrier_reset(Link *link) {
-        int r;
-
-        assert(link);
-
-        if (!link_has_carrier(link))
-                return 0;
-
-        r = link_carrier_lost(link);
-        if (r < 0)
-                return r;
-
-        r = link_carrier_gained(link);
-        if (r < 0)
-                return r;
-
-        log_link_info(link, "Reset carrier");
-        return 0;
+        return link_drop_foreign_config(link);
 }
 
 static int link_admin_state_up(Link *link) {
@@ -1946,10 +1986,6 @@ static int link_update_flags(Link *link, sd_netlink_message *message) {
                         return r;
         }
 
-        r = link_update_lldp(link);
-        if (r < 0)
-                return r;
-
         if (!had_carrier && link_has_carrier(link)) {
                 log_link_info(link, "Gained carrier");
 
@@ -2480,7 +2516,7 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man
                 break;
 
         default:
-                assert_not_reached("Received link message with invalid RTNL message type.");
+                assert_not_reached();
         }
 
         return 1;
index 4077ccaf09c5b99ba32b702834052873d826a128..79400dee2e9c1b22d30435bca75536ee2c82fc5a 100644 (file)
@@ -232,7 +232,6 @@ void link_check_ready(Link *link);
 
 void link_update_operstate(Link *link, bool also_update_bond_master);
 
-int link_carrier_reset(Link *link);
 bool link_has_carrier(Link *link);
 
 bool link_ipv6_enabled(Link *link);
@@ -247,6 +246,7 @@ const char* link_state_to_string(LinkState s) _const_;
 LinkState link_state_from_string(const char *s) _pure_;
 
 int link_reconfigure(Link *link, bool force);
+int link_reconfigure_after_sleep(Link *link);
 
 int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata);
 int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
index c30cc36b51c0b48df6b629ca33206bebbd504e8c..57c8446adef0111f4bfde4f9ea3fbbf3c4c504cc 100644 (file)
@@ -103,38 +103,9 @@ int link_lldp_rx_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        r = link_update_lldp(link);
-        if (r < 0)
-                return r;
-
         return 0;
 }
 
-int link_update_lldp(Link *link) {
-        int r;
-
-        assert(link);
-
-        if (!link->lldp)
-                return 0;
-
-        if (link->flags & IFF_UP) {
-                r = sd_lldp_start(link->lldp);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to start LLDP: %m");
-                if (r > 0)
-                        log_link_debug(link, "Started LLDP.");
-        } else {
-                r = sd_lldp_stop(link->lldp);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to stop LLDP: %m");
-                if (r > 0)
-                        log_link_debug(link, "Stopped LLDP.");
-        }
-
-        return r;
-}
-
 int link_lldp_save(Link *link) {
         _cleanup_(unlink_and_freep) char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
index 49306eafd0ae04d6e24a9efdacf1fccb362612cf..22f6602bd0ffd8c5f853c6680e3e6a957ca0d228 100644 (file)
@@ -14,7 +14,6 @@ typedef enum LLDPMode {
 } LLDPMode;
 
 int link_lldp_rx_configure(Link *link);
-int link_update_lldp(Link *link);
 int link_lldp_save(Link *link);
 
 const char* lldp_mode_to_string(LLDPMode m) _const_;
index 061059558ec5bfec53b44c5311640b77043d535e..66b3216bf4364a34a7eadc4cbc8898453471aeeb 100644 (file)
@@ -281,7 +281,6 @@ static int lldp_send_packet(
 }
 
 static int link_send_lldp(Link *link) {
-        char machine_id_string[SD_ID128_STRING_MAX];
         _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL;
         _cleanup_free_ void *packet = NULL;
         size_t packet_size = 0;
@@ -313,7 +312,7 @@ static int link_send_lldp(Link *link) {
 
         r = lldp_make_packet(link->network->lldp_emit,
                              &link->hw_addr.ether,
-                             sd_id128_to_string(machine_id, machine_id_string),
+                             SD_ID128_TO_STRING(machine_id),
                              link->ifname,
                              (uint16_t) ttl,
                              link->network ? link->network->description : NULL,
index 374d27bef34cd2654ce025a5a087eca1658cdf41..e7aa804f096581e43308d574c740dc939c1b4c43 100644 (file)
@@ -59,9 +59,9 @@ static int manager_reset_all(Manager *m) {
         assert(m);
 
         HASHMAP_FOREACH(link, m->links_by_index) {
-                r = link_carrier_reset(link);
+                r = link_reconfigure_after_sleep(link);
                 if (r < 0) {
-                        log_link_warning_errno(link, r, "Could not reset carrier: %m");
+                        log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
                         link_enter_failed(link);
                 }
         }
@@ -103,7 +103,7 @@ static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *r
                 (void) manager_set_hostname(m, m->dynamic_hostname);
         if (m->dynamic_timezone)
                 (void) manager_set_timezone(m, m->dynamic_timezone);
-        if (!set_isempty(m->links_requesting_uuid))
+        if (m->product_uuid_requested)
                 (void) manager_request_product_uuid(m);
 
         return 0;
@@ -460,7 +460,6 @@ Manager* manager_free(Manager *m) {
         m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free);
 
         m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
-        m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref);
         m->links_by_name = hashmap_free(m->links_by_name);
         m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr);
         m->links_by_index = hashmap_free_with_destructor(m->links_by_index, link_unref);
index 4ee48f3468bf92b25ad582e97a062b4401876e98..4e789045380d5fb1e6dcfc7a28b4db628438c2de 100644 (file)
@@ -60,7 +60,6 @@ struct Manager {
         DUID duid_product_uuid;
         bool has_product_uuid;
         bool product_uuid_requested;
-        Set *links_requesting_uuid;
 
         char* dynamic_hostname;
         char* dynamic_timezone;
index efc47078550847c7695012c261d30fd6b44a6521..fe1f1e0333cf9fc7ee9dd259eff15aff311bf9dc 100644 (file)
 
 #define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e)
 
+typedef enum IPv6TokenAddressGeneration {
+        IPV6_TOKEN_ADDRESS_GENERATION_NONE,
+        IPV6_TOKEN_ADDRESS_GENERATION_STATIC,
+        IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE,
+        _IPV6_TOKEN_ADDRESS_GENERATION_MAX,
+        _IPV6_TOKEN_ADDRESS_GENERATION_INVALID = -EINVAL,
+} IPv6TokenAddressGeneration;
+
+typedef struct IPv6Token {
+        IPv6TokenAddressGeneration address_generation_type;
+
+        uint8_t dad_counter;
+        struct in6_addr prefix;
+} IPv6Token;
+
 bool link_ipv6_accept_ra_enabled(Link *link) {
         assert(link);
 
@@ -521,9 +536,9 @@ static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
 static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         _cleanup_(route_freep) Route *route = NULL;
         struct in6_addr gateway;
-        uint16_t lifetime;
+        uint32_t table, mtu = 0;
         unsigned preference;
-        uint32_t table, mtu;
+        uint16_t lifetime;
         usec_t time_now;
         int r;
 
@@ -560,11 +575,11 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
-        r = sd_ndisc_router_get_mtu(rt, &mtu);
-        if (r == -ENODATA)
-                mtu = 0;
-        else if (r < 0)
-                return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m");
+        if (link->network->ipv6_accept_ra_use_mtu) {
+                r = sd_ndisc_router_get_mtu(rt, &mtu);
+                if (r < 0 && r != -ENODATA)
+                        return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m");
+        }
 
         table = link_get_ipv6_accept_ra_route_table(link);
 
@@ -619,7 +634,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
-static bool stableprivate_address_is_valid(const struct in6_addr *addr) {
+static bool stable_private_address_is_valid(const struct in6_addr *addr) {
         assert(addr);
 
         /* According to rfc4291, generated address should not be in the following ranges. */
@@ -636,7 +651,7 @@ static bool stableprivate_address_is_valid(const struct in6_addr *addr) {
         return true;
 }
 
-static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr **ret) {
+static int make_stable_private_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr **ret) {
         _cleanup_free_ struct in6_addr *addr = NULL;
         sd_id128_t secret_key;
         struct siphash state;
@@ -649,7 +664,7 @@ static int make_stableprivate_address(Link *link, const struct in6_addr *prefix,
 
         r = sd_id128_get_machine_app_specific(NDISC_APP_ID, &secret_key);
         if (r < 0)
-                return log_error_errno(r, "Failed to generate key: %m");
+                return log_link_warning_errno(link, r, "Failed to generate key for IPv6 stable private address: %m");
 
         siphash24_init(&state, secret_key.bytes);
 
@@ -672,7 +687,7 @@ static int make_stableprivate_address(Link *link, const struct in6_addr *prefix,
         memcpy(addr->s6_addr, prefix->s6_addr, l);
         memcpy(addr->s6_addr + l, &rid, 16 - l);
 
-        if (!stableprivate_address_is_valid(addr)) {
+        if (!stable_private_address_is_valid(addr)) {
                 *ret = NULL;
                 return 0;
         }
@@ -704,7 +719,7 @@ static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address,
                          * only when the address generation algorithm produces an invalid address, and the loop
                          * may exit with an address which ends up being unusable due to duplication on the link. */
                         for (; j->dad_counter < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; j->dad_counter++) {
-                                r = make_stableprivate_address(link, address, prefixlen, j->dad_counter, &new_address);
+                                r = make_stable_private_address(link, address, prefixlen, j->dad_counter, &new_address);
                                 if (r < 0)
                                         return r;
                                 if (r > 0)
@@ -765,7 +780,9 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
         assert(link);
         assert(rt);
 
-        r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+        /* Do not use clock_boottime_or_monotonic() here, as the kernel internally manages cstamp and
+         * tstamp with jiffies, and it is not increased while the system is suspended. */
+        r = sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &time_now);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
@@ -1028,7 +1045,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
                 if (rdnss) {
                         rdnss->marked = false;
                         rdnss->router = router;
-                        rdnss->valid_until = time_now + lifetime * USEC_PER_SEC;
+                        rdnss->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC);
                         continue;
                 }
 
@@ -1039,7 +1056,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
                 *x = (NDiscRDNSS) {
                         .address = a[j],
                         .router = router,
-                        .valid_until = time_now + lifetime * USEC_PER_SEC,
+                        .valid_until = usec_add(time_now, lifetime * USEC_PER_SEC),
                 };
 
                 r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x));
@@ -1126,12 +1143,12 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
                 if (dnssl) {
                         dnssl->marked = false;
                         dnssl->router = router;
-                        dnssl->valid_until = time_now + lifetime * USEC_PER_SEC;
+                        dnssl->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC);
                         continue;
                 }
 
                 s->router = router;
-                s->valid_until = time_now + lifetime * USEC_PER_SEC;
+                s->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC);
 
                 r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s));
                 if (r < 0)
@@ -1350,7 +1367,7 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router
                 }
                 break;
         default:
-                assert_not_reached("Unknown NDisc event");
+                assert_not_reached();
         }
 }
 
@@ -1394,6 +1411,9 @@ int ndisc_start(Link *link) {
         if (!link->ndisc || !link->dhcp6_client)
                 return 0;
 
+        if (!link_has_carrier(link))
+                return 0;
+
         log_link_debug(link, "Discovering IPv6 routers");
 
         return sd_ndisc_start(link->ndisc);
@@ -1428,7 +1448,7 @@ void ndisc_flush(Link *link) {
         link->ndisc_dnssl = set_free(link->ndisc_dnssl);
 }
 
-int ipv6token_new(IPv6Token **ret) {
+static int ipv6token_new(IPv6Token **ret) {
         IPv6Token *p;
 
         p = new(IPv6Token, 1);
index 2ff9a8969d6bc8a0eb512825bb5897c1b131191c..2660794b5cac761073e829930533eecc97a57043 100644 (file)
@@ -7,16 +7,6 @@
 #include "networkd-route.h"
 #include "time-util.h"
 
-typedef struct IPv6Token IPv6Token;
-
-typedef enum IPv6TokenAddressGeneration {
-        IPV6_TOKEN_ADDRESS_GENERATION_NONE,
-        IPV6_TOKEN_ADDRESS_GENERATION_STATIC,
-        IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE,
-        _IPV6_TOKEN_ADDRESS_GENERATION_MAX,
-        _IPV6_TOKEN_ADDRESS_GENERATION_INVALID = -EINVAL,
-} IPv6TokenAddressGeneration;
-
 typedef enum IPv6AcceptRAStartDHCP6Client {
         IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO,
         IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS,
@@ -55,15 +45,6 @@ typedef struct NDiscDNSSL {
         /* The domain name follows immediately. */
 } NDiscDNSSL;
 
-struct IPv6Token {
-        IPv6TokenAddressGeneration address_generation_type;
-
-        uint8_t dad_counter;
-        struct in6_addr prefix;
-};
-
-int ipv6token_new(IPv6Token **ret);
-
 static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
         return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
 }
index b23f081bfb221ec1b06ddf44aeae08ad6c12114e..d5a8b6e97ee759a2e1e1c50ac6b8c8ad1a7c57d0 100644 (file)
@@ -595,7 +595,7 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
                 break;
 
         default:
-                assert_not_reached("Received invalid RTNL message type");
+                assert_not_reached();
         }
 
         return 1;
index de4120c47ec55db8518b0c5825475b153520861c..846e54aed7596178b8dd3edc56f347da93f22d41 100644 (file)
@@ -212,6 +212,7 @@ DHCPv4.RequestOptions,                       config_parse_dhcp_request_options,
 DHCPv4.Anonymize,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_anonymize)
 DHCPv4.SendHostname,                         config_parse_bool,                                        0,                             offsetof(Network, dhcp_send_hostname)
 DHCPv4.Hostname,                             config_parse_hostname,                                    0,                             offsetof(Network, dhcp_hostname)
+DHCPv4.Label,                                config_parse_dhcp_label,                                  0,                             offsetof(Network, dhcp_label)
 DHCPv4.RequestBroadcast,                     config_parse_tristate,                                    0,                             offsetof(Network, dhcp_broadcast)
 DHCPv4.VendorClassIdentifier,                config_parse_string,                                      0,                             offsetof(Network, dhcp_vendor_class_identifier)
 DHCPv4.MUDURL,                               config_parse_mud_url,                                     0,                             offsetof(Network, dhcp_mudurl)
@@ -255,6 +256,7 @@ IPv6AcceptRA.UseAutonomousPrefix,            config_parse_bool,
 IPv6AcceptRA.UseOnLinkPrefix,                config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
 IPv6AcceptRA.UseDNS,                         config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_dns)
 IPv6AcceptRA.UseDomains,                     config_parse_ipv6_accept_ra_use_domains,                  0,                             offsetof(Network, ipv6_accept_ra_use_domains)
+IPv6AcceptRA.UseMTU,                         config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_mtu)
 IPv6AcceptRA.DHCPv6Client,                   config_parse_ipv6_accept_ra_start_dhcp6_client,           0,                             offsetof(Network, ipv6_accept_ra_start_dhcp6_client)
 IPv6AcceptRA.RouteTable,                     config_parse_section_route_table,                         0,                             0
 IPv6AcceptRA.RouteMetric,                    config_parse_dhcp_route_metric,                           0,                             0
@@ -265,7 +267,7 @@ IPv6AcceptRA.PrefixDenyList,                 config_parse_ndisc_address_filter,
 IPv6AcceptRA.RouteAllowList,                 config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_allow_listed_route_prefix)
 IPv6AcceptRA.RouteDenyList,                  config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_deny_listed_route_prefix)
 DHCPServer.ServerAddress,                    config_parse_dhcp_server_address,                         0,                             0
-DHCPServer.UplinkInterface,                  config_parse_dhcp_server_uplink,                          0,                             0
+DHCPServer.UplinkInterface,                  config_parse_uplink,                                      0,                             0
 DHCPServer.RelayTarget,                      config_parse_in_addr_non_null,                            AF_INET,                       offsetof(Network, dhcp_server_relay_target)
 DHCPServer.RelayAgentCircuitId,              config_parse_dhcp_server_relay_agent_suboption,           0,                             offsetof(Network, dhcp_server_relay_agent_circuit_id)
 DHCPServer.RelayAgentRemoteId,               config_parse_dhcp_server_relay_agent_suboption,           0,                             offsetof(Network, dhcp_server_relay_agent_remote_id)
@@ -333,6 +335,7 @@ IPv6SendRA.DNS,                              config_parse_radv_dns,
 IPv6SendRA.EmitDomains,                      config_parse_bool,                                        0,                             offsetof(Network, router_emit_domains)
 IPv6SendRA.Domains,                          config_parse_radv_search_domains,                         0,                             0
 IPv6SendRA.DNSLifetimeSec,                   config_parse_sec,                                         0,                             offsetof(Network, router_dns_lifetime_usec)
+IPv6SendRA.UplinkInterface,                  config_parse_uplink,                                      0,                             0
 IPv6Prefix.Prefix,                           config_parse_prefix,                                      0,                             0
 IPv6Prefix.OnLink,                           config_parse_prefix_flags,                                0,                             0
 IPv6Prefix.AddressAutoconfiguration,         config_parse_prefix_flags,                                0,                             0
@@ -345,15 +348,29 @@ IPv6RoutePrefix.LifetimeSec,                 config_parse_route_prefix_lifetime,
 LLDP.MUDURL,                                 config_parse_mud_url,                                     0,                             offsetof(Network, lldp_mud)
 CAN.BitRate,                                 config_parse_can_bitrate,                                 0,                             offsetof(Network, can_bitrate)
 CAN.SamplePoint,                             config_parse_permille,                                    0,                             offsetof(Network, can_sample_point)
+CAN.TimeQuantaNSec,                          config_parse_can_time_quanta,                             0,                             offsetof(Network, can_time_quanta_ns)
+CAN.PropagationSegment,                      config_parse_uint32,                                      0,                             offsetof(Network, can_propagation_segment)
+CAN.PhaseBufferSegment1,                     config_parse_uint32,                                      0,                             offsetof(Network, can_phase_buffer_segment_1)
+CAN.PhaseBufferSegment2,                     config_parse_uint32,                                      0,                             offsetof(Network, can_phase_buffer_segment_2)
+CAN.SyncJumpWidth,                           config_parse_uint32,                                      0,                             offsetof(Network, can_sync_jump_width)
 CAN.DataBitRate,                             config_parse_can_bitrate,                                 0,                             offsetof(Network, can_data_bitrate)
 CAN.DataSamplePoint,                         config_parse_permille,                                    0,                             offsetof(Network, can_data_sample_point)
-CAN.FDMode,                                  config_parse_tristate,                                    0,                             offsetof(Network, can_fd_mode)
-CAN.FDNonISO,                                config_parse_tristate,                                    0,                             offsetof(Network, can_non_iso)
-CAN.RestartSec,                              config_parse_sec,                                         0,                             offsetof(Network, can_restart_us)
-CAN.TripleSampling,                          config_parse_tristate,                                    0,                             offsetof(Network, can_triple_sampling)
-CAN.BusErrorReporting,                       config_parse_tristate,                                    0,                             offsetof(Network, can_berr_reporting)
-CAN.Termination,                             config_parse_tristate,                                    0,                             offsetof(Network, can_termination)
-CAN.ListenOnly,                              config_parse_tristate,                                    0,                             offsetof(Network, can_listen_only)
+CAN.DataTimeQuantaNSec,                      config_parse_can_time_quanta,                             0,                             offsetof(Network, can_data_time_quanta_ns)
+CAN.DataPropagationSegment,                  config_parse_uint32,                                      0,                             offsetof(Network, can_data_propagation_segment)
+CAN.DataPhaseBufferSegment1,                 config_parse_uint32,                                      0,                             offsetof(Network, can_data_phase_buffer_segment_1)
+CAN.DataPhaseBufferSegment2,                 config_parse_uint32,                                      0,                             offsetof(Network, can_data_phase_buffer_segment_2)
+CAN.DataSyncJumpWidth,                       config_parse_uint32,                                      0,                             offsetof(Network, can_data_sync_jump_width)
+CAN.RestartSec,                              config_parse_can_restart_usec,                            0,                             offsetof(Network, can_restart_us)
+CAN.Loopback,                                config_parse_can_control_mode,                            CAN_CTRLMODE_LOOPBACK,         0
+CAN.ListenOnly,                              config_parse_can_control_mode,                            CAN_CTRLMODE_LISTENONLY,       0
+CAN.TripleSampling,                          config_parse_can_control_mode,                            CAN_CTRLMODE_3_SAMPLES,        0
+CAN.OneShot,                                 config_parse_can_control_mode,                            CAN_CTRLMODE_ONE_SHOT,         0
+CAN.BusErrorReporting,                       config_parse_can_control_mode,                            CAN_CTRLMODE_BERR_REPORTING,   0
+CAN.FDMode,                                  config_parse_can_control_mode,                            CAN_CTRLMODE_FD,               0
+CAN.PresumeACK,                              config_parse_can_control_mode,                            CAN_CTRLMODE_PRESUME_ACK,      0
+CAN.FDNonISO,                                config_parse_can_control_mode,                            CAN_CTRLMODE_FD_NON_ISO,       0
+CAN.ClassicDataLengthCode,                   config_parse_can_control_mode,                            CAN_CTRLMODE_CC_LEN8_DLC,      0
+CAN.Termination,                             config_parse_can_termination,                             0,                             0
 QDisc.Parent,                                config_parse_qdisc_parent,                                _QDISC_KIND_INVALID,           0
 QDisc.Handle,                                config_parse_qdisc_handle,                                _QDISC_KIND_INVALID,           0
 BFIFO.Parent,                                config_parse_qdisc_parent,                                QDISC_KIND_BFIFO,              0
index 850b4f449e10a825fd1c35485420e589b6df1361..1928db537e0c56c170001727b0a9dd11e39055ad 100644 (file)
@@ -400,25 +400,21 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .ipv4_accept_local = -1,
                 .ipv4_route_localnet = -1,
                 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
-                .ipv6_accept_ra = -1,
                 .ipv6_dad_transmits = -1,
                 .ipv6_hop_limit = -1,
                 .ipv6_proxy_ndp = -1,
                 .proxy_arp = -1,
 
+                .ipv6_accept_ra = -1,
                 .ipv6_accept_ra_use_dns = true,
                 .ipv6_accept_ra_use_autonomous_prefix = true,
                 .ipv6_accept_ra_use_onlink_prefix = true,
+                .ipv6_accept_ra_use_mtu = true,
                 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
                 .ipv6_accept_ra_route_metric = DHCP_ROUTE_METRIC,
                 .ipv6_accept_ra_start_dhcp6_client = IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES,
 
-                .can_triple_sampling = -1,
-                .can_berr_reporting = -1,
                 .can_termination = -1,
-                .can_listen_only = -1,
-                .can_fd_mode = -1,
-                .can_non_iso = -1,
         };
 
         r = config_parse_many(
@@ -585,6 +581,7 @@ static Network *network_free(Network *network) {
         free(network->dhcp_mudurl);
         strv_free(network->dhcp_user_class);
         free(network->dhcp_hostname);
+        free(network->dhcp_label);
         set_free(network->dhcp_deny_listed_ip);
         set_free(network->dhcp_allow_listed_ip);
         set_free(network->dhcp_request_options);
@@ -640,6 +637,7 @@ static Network *network_free(Network *network) {
 
         free(network->dhcp_server_timezone);
         free(network->dhcp_server_uplink_name);
+        free(network->router_uplink_name);
 
         for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
                 free(network->dhcp_server_emit[t].addresses);
@@ -718,7 +716,8 @@ bool network_has_static_ipv6_configurations(Network *network) {
         return false;
 }
 
-int config_parse_stacked_netdev(const char *unit,
+int config_parse_stacked_netdev(
+                const char *unit,
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -728,6 +727,7 @@ int config_parse_stacked_netdev(const char *unit,
                 const char *rvalue,
                 void *data,
                 void *userdata) {
+
         _cleanup_free_ char *name = NULL;
         NetDevKind kind = ltype;
         Hashmap **h = data;
@@ -858,26 +858,26 @@ int config_parse_hostname(
                 void *data,
                 void *userdata) {
 
-        _cleanup_free_ char *hn = NULL;
         char **hostname = data;
         int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
-        assert(hostname);
+        assert(data);
 
-        r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata);
-        if (r < 0)
-                return r;
+        if (isempty(rvalue)) {
+                *hostname = mfree(*hostname);
+                return 0;
+        }
 
-        if (!hostname_is_valid(hn, 0)) {
+        if (!hostname_is_valid(rvalue, 0)) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Hostname is not valid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
-        r = dns_name_is_valid(hn);
+        r = dns_name_is_valid(rvalue);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
@@ -889,7 +889,7 @@ int config_parse_hostname(
                 return 0;
         }
 
-        return free_and_replace(*hostname, hn);
+        return free_and_strdup_warn(hostname, rvalue);
 }
 
 int config_parse_timezone(
@@ -904,26 +904,27 @@ int config_parse_timezone(
                 void *data,
                 void *userdata) {
 
-        _cleanup_free_ char *tz = NULL;
-        char **datap = data;
+        char **tz = data;
         int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
-        assert(datap);
+        assert(data);
 
-        r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata);
-        if (r < 0)
-                return r;
+        if (isempty(rvalue)) {
+                *tz = mfree(*tz);
+                return 0;
+        }
 
-        if (!timezone_is_valid(tz, LOG_WARNING)) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
+        r = verify_timezone(rvalue, LOG_WARNING);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Timezone is not valid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
-        return free_and_replace(*datap, tz);
+        return free_and_strdup_warn(tz, rvalue);
 }
 
 int config_parse_dns(
@@ -1185,6 +1186,73 @@ int config_parse_link_group(
         return 0;
 }
 
+
+int config_parse_uplink(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Network *network = userdata;
+        int *index, r;
+        char **name;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (streq(section, "DHCPServer")) {
+                index = &network->dhcp_server_uplink_index;
+                name = &network->dhcp_server_uplink_name;
+        } else if (streq(section, "IPv6SendRA")) {
+                index = &network->router_uplink_index;
+                name = &network->router_uplink_name;
+        } else
+                assert_not_reached();
+
+        if (isempty(rvalue) || streq(rvalue, ":auto")) {
+                *index = UPLINK_INDEX_AUTO;
+                *name = mfree(*name);
+                return 0;
+        }
+
+        if (streq(rvalue, ":none")) {
+                *index = UPLINK_INDEX_NONE;
+                *name = mfree(*name);
+                return 0;
+        }
+
+        r = parse_ifindex(rvalue);
+        if (r > 0) {
+                *index = r;
+                *name = mfree(*name);
+                return 0;
+        }
+
+        if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        /* The interface name will be resolved later. */
+        r = free_and_strdup_warn(name, rvalue);
+        if (r < 0)
+                return r;
+
+        /* Note, if uplink_name is set, then uplink_index will be ignored. So, the below does not mean
+         * an uplink interface will be selected automatically. */
+        *index = UPLINK_INDEX_AUTO;
+        return 0;
+}
+
 DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily,
                          "Failed to parse RequiredFamilyForOnline= setting");
 
index b39063fe8ad918590070fd2c26c739c7870ac1fc..95c86e72304066423dd4593be59f11655458615c 100644 (file)
 #include "resolve-util.h"
 #include "socket-netlink.h"
 
+/* Special values for *_uplink_index. */
+#define UPLINK_INDEX_AUTO  0 /* uplink will be selected automatically */
+#define UPLINK_INDEX_NONE -1 /* uplink will not be selected automatically */
+
 typedef enum KeepConfiguration {
         KEEP_CONFIGURATION_NO            = 0,
         KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0,
@@ -125,6 +129,7 @@ struct Network {
         char *dhcp_mudurl;
         char **dhcp_user_class;
         char *dhcp_hostname;
+        char *dhcp_label;
         uint64_t dhcp_max_attempts;
         uint32_t dhcp_route_metric;
         bool dhcp_route_metric_set;
@@ -224,6 +229,8 @@ struct Network {
         struct in6_addr *router_dns;
         unsigned n_router_dns;
         OrderedSet *router_search_domains;
+        int router_uplink_index;
+        char *router_uplink_name;
 
         /* DHCPv6 Prefix Delegation support */
         int dhcp6_pd;
@@ -259,15 +266,23 @@ struct Network {
         /* CAN support */
         uint32_t can_bitrate;
         unsigned can_sample_point;
+        nsec_t can_time_quanta_ns;
+        uint32_t can_propagation_segment;
+        uint32_t can_phase_buffer_segment_1;
+        uint32_t can_phase_buffer_segment_2;
+        uint32_t can_sync_jump_width;
         uint32_t can_data_bitrate;
         unsigned can_data_sample_point;
+        nsec_t can_data_time_quanta_ns;
+        uint32_t can_data_propagation_segment;
+        uint32_t can_data_phase_buffer_segment_1;
+        uint32_t can_data_phase_buffer_segment_2;
+        uint32_t can_data_sync_jump_width;
         usec_t can_restart_us;
-        int can_triple_sampling;
-        int can_berr_reporting;
-        int can_termination;
-        int can_listen_only;
-        int can_fd_mode;
-        int can_non_iso;
+        uint32_t can_control_mode_mask;
+        uint32_t can_control_mode_flags;
+        uint16_t can_termination;
+        bool can_termination_set;
 
         /* sysctl settings */
         AddressFamily ip_forward;
@@ -286,6 +301,7 @@ struct Network {
         bool ipv6_accept_ra_use_dns;
         bool ipv6_accept_ra_use_autonomous_prefix;
         bool ipv6_accept_ra_use_onlink_prefix;
+        bool ipv6_accept_ra_use_mtu;
         bool active_slave;
         bool primary_slave;
         DHCPUseDomains ipv6_accept_ra_use_domains;
@@ -364,6 +380,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);
 CONFIG_PARSER_PROTOTYPE(config_parse_link_group);
+CONFIG_PARSER_PROTOTYPE(config_parse_uplink);
 
 const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
 
index 87f4ee4b107883658001e6c8343fae74592f9257..92cbb1c843d2f2192aef2e9e0bcd52f4704bb145 100644 (file)
@@ -1012,7 +1012,7 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
                 break;
 
         default:
-                assert_not_reached("Received invalid RTNL message type");
+                assert_not_reached();
         }
 
         return 1;
@@ -1227,7 +1227,7 @@ int config_parse_nexthop_family(
                 n->family = AF_INET6;
                 break;
         default:
-                assert_not_reached("Invalid family.");
+                assert_not_reached();
         }
 
         TAKE_PTR(n);
index 5ef4acf5aca92a17940afad3aef4bcb324ecf470..dcf5f4f0527cccbe91ecfaf5b70b48cb66e83cd0 100644 (file)
@@ -5,6 +5,8 @@
 #include "networkd-bridge-fdb.h"
 #include "networkd-bridge-mdb.h"
 #include "networkd-dhcp-server.h"
+#include "networkd-dhcp4.h"
+#include "networkd-dhcp6.h"
 #include "networkd-ipv6-proxy-ndp.h"
 #include "networkd-manager.h"
 #include "networkd-neighbor.h"
@@ -30,9 +32,9 @@ static void request_free_object(RequestType type, void *object) {
         case REQUEST_TYPE_BRIDGE_MDB:
                 bridge_mdb_free(object);
                 break;
-        case REQUEST_TYPE_CREATE_STACKED_NETDEV:
-                break;
         case REQUEST_TYPE_DHCP_SERVER:
+        case REQUEST_TYPE_DHCP4_CLIENT:
+        case REQUEST_TYPE_DHCP6_CLIENT:
                 break;
         case REQUEST_TYPE_IPV6_PROXY_NDP:
                 free(object);
@@ -43,6 +45,8 @@ static void request_free_object(RequestType type, void *object) {
         case REQUEST_TYPE_NEXTHOP:
                 nexthop_free(object);
                 break;
+        case REQUEST_TYPE_RADV:
+                break;
         case REQUEST_TYPE_ROUTE:
                 route_free(object);
                 break;
@@ -50,10 +54,11 @@ static void request_free_object(RequestType type, void *object) {
                 routing_policy_rule_free(object);
                 break;
         case REQUEST_TYPE_SET_LINK:
+        case REQUEST_TYPE_STACKED_NETDEV:
         case REQUEST_TYPE_UP_DOWN:
                 break;
         default:
-                assert_not_reached("invalid request type.");
+                assert_not_reached();
         }
 }
 
@@ -104,13 +109,15 @@ static void request_hash_func(const Request *req, struct siphash *state) {
         case REQUEST_TYPE_ADDRESS_LABEL:
         case REQUEST_TYPE_BRIDGE_FDB:
         case REQUEST_TYPE_BRIDGE_MDB:
-        case REQUEST_TYPE_CREATE_STACKED_NETDEV:
+        case REQUEST_TYPE_STACKED_NETDEV:
                 /* TODO: Currently, these types do not have any specific hash and compare functions.
                  * Fortunately, all these objects are 'static', thus we can use the trivial functions. */
                 trivial_hash_func(req->object, state);
                 break;
         case REQUEST_TYPE_DHCP_SERVER:
-                /* This type does not have object. */
+        case REQUEST_TYPE_DHCP4_CLIENT:
+        case REQUEST_TYPE_DHCP6_CLIENT:
+                /* These types do not have an object. */
                 break;
         case REQUEST_TYPE_IPV6_PROXY_NDP:
                 in6_addr_hash_func(req->ipv6_proxy_ndp, state);
@@ -121,6 +128,9 @@ static void request_hash_func(const Request *req, struct siphash *state) {
         case REQUEST_TYPE_NEXTHOP:
                 nexthop_hash_func(req->nexthop, state);
                 break;
+        case REQUEST_TYPE_RADV:
+                /* This type does not have an object. */
+                break;
         case REQUEST_TYPE_ROUTE:
                 route_hash_func(req->route, state);
                 break;
@@ -134,7 +144,7 @@ static void request_hash_func(const Request *req, struct siphash *state) {
         case REQUEST_TYPE_UP_DOWN:
                 break;
         default:
-                assert_not_reached("invalid request type.");
+                assert_not_reached();
         }
 }
 
@@ -162,9 +172,11 @@ static int request_compare_func(const struct Request *a, const struct Request *b
         case REQUEST_TYPE_ADDRESS_LABEL:
         case REQUEST_TYPE_BRIDGE_FDB:
         case REQUEST_TYPE_BRIDGE_MDB:
-        case REQUEST_TYPE_CREATE_STACKED_NETDEV:
+        case REQUEST_TYPE_STACKED_NETDEV:
                 return trivial_compare_func(a->object, b->object);
         case REQUEST_TYPE_DHCP_SERVER:
+        case REQUEST_TYPE_DHCP4_CLIENT:
+        case REQUEST_TYPE_DHCP6_CLIENT:
                 return 0;
         case REQUEST_TYPE_IPV6_PROXY_NDP:
                 return in6_addr_compare_func(a->ipv6_proxy_ndp, b->ipv6_proxy_ndp);
@@ -174,6 +186,8 @@ static int request_compare_func(const struct Request *a, const struct Request *b
                 return nexthop_compare_func(a->nexthop, b->nexthop);
         case REQUEST_TYPE_ROUTE:
                 return route_compare_func(a->route, b->route);
+        case REQUEST_TYPE_RADV:
+                return 0;
         case REQUEST_TYPE_ROUTING_POLICY_RULE:
                 return routing_policy_rule_compare_func(a->rule, b->rule);
         case REQUEST_TYPE_SET_LINK:
@@ -181,7 +195,7 @@ static int request_compare_func(const struct Request *a, const struct Request *b
         case REQUEST_TYPE_UP_DOWN:
                 return 0;
         default:
-                assert_not_reached("invalid request type.");
+                assert_not_reached();
         }
 }
 
@@ -211,10 +225,18 @@ int link_queue_request(
         assert(IN_SET(type,
                       REQUEST_TYPE_ACTIVATE_LINK,
                       REQUEST_TYPE_DHCP_SERVER,
+                      REQUEST_TYPE_DHCP4_CLIENT,
+                      REQUEST_TYPE_DHCP6_CLIENT,
+                      REQUEST_TYPE_RADV,
                       REQUEST_TYPE_SET_LINK,
                       REQUEST_TYPE_UP_DOWN) ||
                object);
-        assert(type == REQUEST_TYPE_DHCP_SERVER || netlink_handler);
+        assert(IN_SET(type,
+                      REQUEST_TYPE_DHCP_SERVER,
+                      REQUEST_TYPE_DHCP4_CLIENT,
+                      REQUEST_TYPE_DHCP6_CLIENT,
+                      REQUEST_TYPE_RADV) ||
+               netlink_handler);
 
         req = new(Request, 1);
         if (!req) {
@@ -283,12 +305,15 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
                         case REQUEST_TYPE_BRIDGE_MDB:
                                 r = request_process_bridge_mdb(req);
                                 break;
-                        case REQUEST_TYPE_CREATE_STACKED_NETDEV:
-                                r = request_process_create_stacked_netdev(req);
-                                break;
                         case REQUEST_TYPE_DHCP_SERVER:
                                 r = request_process_dhcp_server(req);
                                 break;
+                        case REQUEST_TYPE_DHCP4_CLIENT:
+                                r = request_process_dhcp4_client(req);
+                                break;
+                        case REQUEST_TYPE_DHCP6_CLIENT:
+                                r = request_process_dhcp6_client(req);
+                                break;
                         case REQUEST_TYPE_IPV6_PROXY_NDP:
                                 r = request_process_ipv6_proxy_ndp_address(req);
                                 break;
@@ -298,6 +323,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
                         case REQUEST_TYPE_NEXTHOP:
                                 r = request_process_nexthop(req);
                                 break;
+                        case REQUEST_TYPE_RADV:
+                                r = request_process_radv(req);
+                                break;
                         case REQUEST_TYPE_ROUTE:
                                 r = request_process_route(req);
                                 break;
@@ -307,6 +335,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
                         case REQUEST_TYPE_SET_LINK:
                                 r = request_process_set_link(req);
                                 break;
+                        case REQUEST_TYPE_STACKED_NETDEV:
+                                r = request_process_stacked_netdev(req);
+                                break;
                         case REQUEST_TYPE_UP_DOWN:
                                 r = request_process_link_up_or_down(req);
                                 break;
index 1f4b15f087dd2ae09b528203fbf6dc2b9134b92a..b1c0a9f0d4e8682a82aa12e09d6cef082983c003 100644 (file)
@@ -26,14 +26,17 @@ typedef enum RequestType {
         REQUEST_TYPE_ADDRESS_LABEL,
         REQUEST_TYPE_BRIDGE_FDB,
         REQUEST_TYPE_BRIDGE_MDB,
-        REQUEST_TYPE_CREATE_STACKED_NETDEV,
         REQUEST_TYPE_DHCP_SERVER,
+        REQUEST_TYPE_DHCP4_CLIENT,
+        REQUEST_TYPE_DHCP6_CLIENT,
         REQUEST_TYPE_IPV6_PROXY_NDP,
         REQUEST_TYPE_NEIGHBOR,
         REQUEST_TYPE_NEXTHOP,
+        REQUEST_TYPE_RADV,
         REQUEST_TYPE_ROUTE,
         REQUEST_TYPE_ROUTING_POLICY_RULE,
         REQUEST_TYPE_SET_LINK,
+        REQUEST_TYPE_STACKED_NETDEV,
         REQUEST_TYPE_UP_DOWN,
         _REQUEST_TYPE_MAX,
         _REQUEST_TYPE_INVALID = -EINVAL,
index 805d3fff6ccd5224dd722e47fec0635c6f45cc7f..afb3795f2fa8db5f48f71df50aeab474b55b5e59 100644 (file)
@@ -10,6 +10,7 @@
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
+#include "networkd-queue.h"
 #include "networkd-radv.h"
 #include "parse-util.h"
 #include "string-util.h"
@@ -583,10 +584,7 @@ static int radv_set_dns(Link *link, Link *uplink) {
                 goto set_dns;
 
         if (uplink) {
-                if (!uplink->network) {
-                        log_link_debug(uplink, "Cannot fetch DNS servers as uplink interface is not managed by us");
-                        return 0;
-                }
+                assert(uplink->network);
 
                 r = network_get_ipv6_dns(uplink->network, &dns, &n_dns);
                 if (r > 0)
@@ -595,7 +593,7 @@ static int radv_set_dns(Link *link, Link *uplink) {
 
         return 0;
 
- set_dns:
+set_dns:
         return sd_radv_set_rdnss(link->radv,
                                  DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC),
                                  dns, n_dns);
@@ -622,10 +620,7 @@ static int radv_set_domains(Link *link, Link *uplink) {
                 goto set_domains;
 
         if (uplink) {
-                if (!uplink->network) {
-                        log_link_debug(uplink, "Cannot fetch DNS search domains as uplink interface is not managed by us");
-                        return 0;
-                }
+                assert(uplink->network);
 
                 search_domains = uplink->network->search_domains;
                 if (search_domains)
@@ -634,7 +629,7 @@ static int radv_set_domains(Link *link, Link *uplink) {
 
         return 0;
 
- set_domains:
+set_domains:
         s = ordered_set_get_strv(search_domains);
         if (!s)
                 return log_oom();
@@ -645,20 +640,23 @@ static int radv_set_domains(Link *link, Link *uplink) {
 
 }
 
-int radv_emit_dns(Link *link) {
-        Link *uplink = NULL;
-        int r;
+static int radv_find_uplink(Link *link, Link **ret) {
+        assert(link);
 
-        (void) manager_find_uplink(link->manager, AF_INET6, link, &uplink);
+        if (link->network->router_uplink_name)
+                return link_get_by_name(link->manager, link->network->router_uplink_name, ret);
 
-        r = radv_set_dns(link, uplink);
-        if (r < 0)
-                log_link_warning_errno(link, r, "Could not set RA DNS: %m");
+        if (link->network->router_uplink_index > 0)
+                return link_get_by_index(link->manager, link->network->router_uplink_index, ret);
 
-        r = radv_set_domains(link, uplink);
-        if (r < 0)
-                log_link_warning_errno(link, r, "Could not set RA Domains: %m");
+        if (link->network->router_uplink_index == UPLINK_INDEX_AUTO) {
+                /* It is not necessary to propagate error in automatic selection. */
+                if (manager_find_uplink(link->manager, AF_INET6, link, ret) < 0)
+                        *ret = NULL;
+                return 0;
+        }
 
+        *ret = NULL;
         return 0;
 }
 
@@ -671,8 +669,9 @@ static bool link_radv_enabled(Link *link) {
         return link->network->router_prefix_delegation;
 }
 
-int radv_configure(Link *link) {
+static int radv_configure(Link *link) {
         uint16_t router_lifetime;
+        Link *uplink = NULL;
         RoutePrefix *q;
         Prefix *p;
         int r;
@@ -680,9 +679,6 @@ int radv_configure(Link *link) {
         assert(link);
         assert(link->network);
 
-        if (!link_radv_enabled(link))
-                return 0;
-
         if (link->radv)
                 return -EBUSY;
 
@@ -748,6 +744,16 @@ int radv_configure(Link *link) {
                         return r;
         }
 
+        (void) radv_find_uplink(link, &uplink);
+
+        r = radv_set_dns(link, uplink);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Could not set RA DNS: %m");
+
+        r = radv_set_domains(link, uplink);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
+
         return 0;
 }
 
@@ -779,6 +785,96 @@ int radv_update_mac(Link *link) {
         return 0;
 }
 
+static int radv_is_ready_to_configure(Link *link) {
+        bool needs_uplink = false;
+        int r;
+
+        assert(link);
+        assert(link->network);
+
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                return false;
+
+        if (in6_addr_is_null(&link->ipv6ll_address))
+                return false;
+
+        if (link->network->router_emit_dns && !link->network->router_dns) {
+                _cleanup_free_ struct in6_addr *dns = NULL;
+                size_t n_dns;
+
+                r = network_get_ipv6_dns(link->network, &dns, &n_dns);
+                if (r < 0)
+                        return r;
+
+                needs_uplink = r == 0;
+        }
+
+        if (link->network->router_emit_domains &&
+            !link->network->router_search_domains &&
+            !link->network->search_domains)
+                needs_uplink = true;
+
+        if (needs_uplink) {
+                Link *uplink = NULL;
+
+                if (radv_find_uplink(link, &uplink) < 0)
+                        return false;
+
+                if (uplink && !uplink->network)
+                        return false;
+        }
+
+        return true;
+}
+
+int request_process_radv(Request *req) {
+        Link *link;
+        int r;
+
+        assert(req);
+        assert(req->link);
+        assert(req->type == REQUEST_TYPE_RADV);
+
+        link = req->link;
+
+        r = radv_is_ready_to_configure(link);
+        if (r <= 0)
+                return r;
+
+        r = radv_configure(link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
+
+        if (link_has_carrier(link)) {
+                r = sd_radv_start(link->radv);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
+        }
+
+        log_link_debug(link, "IPv6 Router Advertisement engine is configured%s.",
+                       link_has_carrier(link) ? " and started." : "");
+        return 1;
+}
+
+int link_request_radv(Link *link) {
+        int r;
+
+        assert(link);
+
+        if (!link_radv_enabled(link))
+                return 0;
+
+        if (link->radv)
+                return 0;
+
+        r = link_queue_request(link, REQUEST_TYPE_RADV, NULL, false, NULL, NULL, NULL);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m");
+
+        log_link_debug(link, "Requested configuring of the IPv6 Router Advertisement engine.");
+        return 0;
+}
+
 int radv_add_prefix(
                 Link *link,
                 const struct in6_addr *prefix,
index f6efd326979b3d440c2759e8bde2df6b936fa655..a8adb9bce1c538f091fd462947581bb070896a00 100644 (file)
@@ -14,8 +14,9 @@
 #include "conf-parser.h"
 #include "networkd-util.h"
 
-typedef struct Network Network;
 typedef struct Link Link;
+typedef struct Network Network;
+typedef struct Request Request;
 
 typedef enum RADVPrefixDelegation {
         RADV_PREFIX_DELEGATION_NONE   = 0,
@@ -50,12 +51,13 @@ void network_drop_invalid_prefixes(Network *network);
 void network_drop_invalid_route_prefixes(Network *network);
 void network_adjust_radv(Network *network);
 
-int radv_emit_dns(Link *link);
-int radv_configure(Link *link);
 int radv_update_mac(Link *link);
 int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
                     uint32_t lifetime_preferred, uint32_t lifetime_valid);
 
+int request_process_radv(Request *req);
+int link_request_radv(Link *link);
+
 const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_;
 RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
 
index b6c19223b67823b223043297fb05fc2468c14cfb..46fbf4f324c7cb25809231d11f3465b214c37d6f 100644 (file)
@@ -276,7 +276,7 @@ Route *route_free(Route *route) {
                 set_remove(route->link->dhcp6_pd_routes, route);
                 set_remove(route->link->dhcp6_pd_routes_old, route);
                 SET_FOREACH(n, route->link->ndisc_routes)
-                        if (n->route == route)
+                        if (route_equal(n->route, route))
                                 free(set_remove(route->link->ndisc_routes, n));
         }
 
@@ -432,7 +432,7 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 route_compare_func,
                 route_free);
 
-static bool route_equal(const Route *r1, const Route *r2) {
+bool route_equal(const Route *r1, const Route *r2) {
         if (r1 == r2)
                 return true;
 
@@ -1881,7 +1881,7 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, const
                 break;
 
         default:
-                assert_not_reached("Received route message with invalid RTNL message type");
+                assert_not_reached();
         }
 
         return 1;
@@ -2335,7 +2335,7 @@ int config_parse_destination(
                 buffer = &n->src;
                 prefixlen = &n->src_prefixlen;
         } else
-                assert_not_reached(lvalue);
+                assert_not_reached();
 
         if (n->family == AF_UNSPEC)
                 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
@@ -2580,7 +2580,7 @@ int config_parse_route_boolean(
         else if (streq(lvalue, "TTLPropagate"))
                 n->ttl_propagate = r;
         else
-                assert_not_reached("Invalid lvalue");
+                assert_not_reached();
 
         TAKE_PTR(n);
         return 0;
@@ -2814,7 +2814,7 @@ int config_parse_tcp_window(
         else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
                 n->initrwnd = k;
         else
-                assert_not_reached("Invalid TCP window type.");
+                assert_not_reached();
 
         TAKE_PTR(n);
         return 0;
index fa0b3ba0fc9213fc7e30f660ef853d77b6db522b..2d262819add0f3877d10aece97b1afbbd4dd369d 100644 (file)
@@ -66,6 +66,7 @@ typedef struct Route {
 
 void route_hash_func(const Route *route, struct siphash *state);
 int route_compare_func(const Route *a, const Route *b);
+bool route_equal(const Route *r1, const Route *r2);
 extern const struct hash_ops route_hash_ops;
 
 int route_new(Route **ret);
index af7e8a973cf47087be9e5b65f1af1a5acff40bb4..51eb3059de40767142d76a680da6775bf59cc41d 100644 (file)
@@ -163,7 +163,9 @@ void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash
                 siphash24_compress(&rule->type, sizeof(rule->type), state);
                 siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state);
                 siphash24_compress(&rule->fwmask, sizeof(rule->fwmask), state);
-                siphash24_compress(&rule->priority, sizeof(rule->priority), state);
+                siphash24_compress_boolean(rule->priority_set, state);
+                if (rule->priority_set)
+                        siphash24_compress(&rule->priority, sizeof(rule->priority), state);
                 siphash24_compress(&rule->table, sizeof(rule->table), state);
                 siphash24_compress(&rule->suppress_prefixlen, sizeof(rule->suppress_prefixlen), state);
 
@@ -229,10 +231,16 @@ int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPo
                 if (r != 0)
                         return r;
 
-                r = CMP(a->priority, b->priority);
+                r = CMP(a->priority_set, b->priority_set);
                 if (r != 0)
                         return r;
 
+                if (a->priority_set) {
+                        r = CMP(a->priority, b->priority);
+                        if (r != 0)
+                                return r;
+                }
+
                 r = CMP(a->table, b->table);
                 if (r != 0)
                         return r;
@@ -293,8 +301,9 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 routing_policy_rule_compare_func,
                 routing_policy_rule_free);
 
-static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
+static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *rule, bool require_priority, RoutingPolicyRule **ret) {
         RoutingPolicyRule *existing;
+        int r;
 
         assert(m);
 
@@ -312,6 +321,23 @@ static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *rule, Ro
                 return 0;
         }
 
+        if (!require_priority && rule->priority_set) {
+                _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
+
+                r = routing_policy_rule_dup(rule, &tmp);
+                if (r < 0)
+                        return r;
+
+                tmp->priority_set = false;
+
+                existing = set_get(m->rules, tmp);
+                if (existing) {
+                        if (ret)
+                                *ret = existing;
+                        return 1;
+                }
+        }
+
         return -ENOENT;
 }
 
@@ -328,7 +354,7 @@ static int routing_policy_rule_add(Manager *m, const RoutingPolicyRule *in, Rout
         if (r < 0)
                 return r;
 
-        r = routing_policy_rule_get(m, rule, &existing);
+        r = routing_policy_rule_get(m, rule, true, &existing);
         if (r == -ENOENT) {
                 /* Rule does not exist, use a new one. */
                 r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, rule);
@@ -371,6 +397,32 @@ static int routing_policy_rule_consume_foreign(Manager *m, RoutingPolicyRule *ru
         return 1;
 }
 
+static int routing_policy_rule_update_priority(RoutingPolicyRule *rule, uint32_t priority) {
+        int r;
+
+        assert(rule);
+        assert(rule->manager);
+
+        if (rule->priority_set)
+                return 0;
+
+        if (!set_remove(rule->manager->rules, rule))
+                return -ENOENT;
+
+        rule->priority = priority;
+        rule->priority_set = true;
+
+        r = set_put(rule->manager->rules, rule);
+        if (r <= 0) {
+                /* Undo */
+                rule->priority_set = false;
+                assert_se(set_put(rule->manager->rules, rule) > 0);
+                return r == 0 ? -EEXIST : r;
+        }
+
+        return 1;
+}
+
 static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, const char *str, const Link *link, const Manager *m) {
         _cleanup_free_ char *from = NULL, *to = NULL, *table = NULL;
 
@@ -422,9 +474,11 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule
                         return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
         }
 
-        r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not append FRA_PRIORITY attribute: %m");
+        if (rule->priority_set) {
+                r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append FRA_PRIORITY attribute: %m");
+        }
 
         if (rule->tos > 0) {
                 r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos);
@@ -662,6 +716,28 @@ int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const L
                         continue;
                 }
 
+                if (!foreign) {
+                        _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
+
+                        /* The rule may be configured without priority. Try to find without priority. */
+
+                        k = routing_policy_rule_dup(rule, &tmp);
+                        if (k < 0) {
+                                if (r >= 0)
+                                        r = k;
+                                continue;
+                        }
+
+                        tmp->priority_set = false;
+
+                        k = links_have_routing_policy_rule(m, tmp, except);
+                        if (k != 0) {
+                                if (k < 0 && r >= 0)
+                                        r = k;
+                                continue;
+                        }
+                }
+
                 k = routing_policy_rule_remove(rule, m);
                 if (k < 0 && r >= 0)
                         r = k;
@@ -821,11 +897,11 @@ int request_process_routing_policy_rule(Request *req) {
 }
 
 static const RoutingPolicyRule kernel_rules[] = {
-        { .family = AF_INET,  .priority = 0,     .table = RT_TABLE_LOCAL,   .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
-        { .family = AF_INET,  .priority = 32766, .table = RT_TABLE_MAIN,    .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
-        { .family = AF_INET,  .priority = 32767, .table = RT_TABLE_DEFAULT, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
-        { .family = AF_INET6, .priority = 0,     .table = RT_TABLE_LOCAL,   .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
-        { .family = AF_INET6, .priority = 32766, .table = RT_TABLE_MAIN,    .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+        { .family = AF_INET,  .priority_set = true, .priority = 0,     .table = RT_TABLE_LOCAL,   .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+        { .family = AF_INET,  .priority_set = true, .priority = 32766, .table = RT_TABLE_MAIN,    .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+        { .family = AF_INET,  .priority_set = true, .priority = 32767, .table = RT_TABLE_DEFAULT, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+        { .family = AF_INET6, .priority_set = true, .priority = 0,     .table = RT_TABLE_LOCAL,   .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
+        { .family = AF_INET6, .priority_set = true, .priority = 32766, .table = RT_TABLE_MAIN,    .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
 };
 
 static bool routing_policy_rule_is_created_by_kernel(const RoutingPolicyRule *rule) {
@@ -936,6 +1012,9 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
                 log_warning_errno(r, "rtnl: could not get FRA_PRIORITY attribute, ignoring: %m");
                 return 0;
         }
+        /* The kernel does not send priority if priority is zero. So, the flag below must be always set
+         * even if the message does not contain FRA_PRIORITY. */
+        tmp->priority_set = true;
 
         r = sd_netlink_message_read_u32(message, FRA_TABLE, &tmp->table);
         if (r < 0 && r != -ENODATA) {
@@ -1027,13 +1106,16 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
                  * protocol of the received rule is RTPROT_KERNEL or RTPROT_STATIC. */
                 tmp->protocol = routing_policy_rule_is_created_by_kernel(tmp) ? RTPROT_KERNEL : RTPROT_STATIC;
 
-        (void) routing_policy_rule_get(m, tmp, &rule);
+        (void) routing_policy_rule_get(m, tmp, false, &rule);
 
         switch (type) {
         case RTM_NEWRULE:
-                if (rule)
+                if (rule) {
                         log_routing_policy_rule_debug(tmp, "Received remembered", NULL, m);
-                else if (!m->manage_foreign_routes)
+                        r = routing_policy_rule_update_priority(rule, tmp->priority);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to update priority of remembered routing policy rule, ignoring: %m");
+                } else if (!m->manage_foreign_routes)
                         log_routing_policy_rule_debug(tmp, "Ignoring received foreign", NULL, m);
                 else {
                         log_routing_policy_rule_debug(tmp, "Remembering foreign", NULL, m);
@@ -1051,7 +1133,7 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
                 break;
 
         default:
-                assert_not_reached("Received invalid RTNL message type");
+                assert_not_reached();
         }
 
         return 1;
@@ -1155,11 +1237,19 @@ int config_parse_routing_policy_rule_priority(
         if (r < 0)
                 return log_oom();
 
+        if (isempty(rvalue)) {
+                n->priority = 0;
+                n->priority_set = false;
+                TAKE_PTR(n);
+                return 0;
+        }
+
         r = safe_atou32(rvalue, &n->priority);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue);
                 return 0;
         }
+        n->priority_set = true;
 
         TAKE_PTR(n);
         return 0;
@@ -1636,7 +1726,7 @@ static int routing_policy_rule_section_verify(RoutingPolicyRule *rule) {
          * update routing_policy_rule_is_created_by_kernel() when a new setting which sets the flag is
          * added in the future. */
         if (rule->l3mdev > 0)
-                assert_not_reached("FRA_L3MDEV flag should not be configured.");
+                assert_not_reached();
 
         return 0;
 }
index aed37b00d21d7689c92f7b1efd257beb0df4048c..557048c3f48e3c0570d3f960dfb74191fa4ac677 100644 (file)
@@ -20,6 +20,7 @@ typedef struct RoutingPolicyRule {
         NetworkConfigSection *section;
 
         bool invert_rule;
+        bool priority_set;
 
         uint8_t tos;
         uint8_t type;
index a316a6c59b5aafe749d1d87d3a2b16168121c81f..f09c3185170ba6b7a8349d51d16c7a1891661a62 100644 (file)
@@ -95,9 +95,16 @@ static int set_link_handler_internal(
         return 1;
 
 on_error:
-        if (op == SET_LINK_FLAGS) {
+        switch (op) {
+        case SET_LINK_FLAGS:
                 assert(link->set_flags_messages > 0);
                 link->set_flags_messages--;
+                break;
+        case SET_LINK_MASTER:
+                link->master_set = true;
+                break;
+        default:
+                break;
         }
 
         return 0;
@@ -106,7 +113,7 @@ on_error:
 static int link_set_addrgen_mode_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
-        r = set_link_handler_internal(rtnl, m, link, SET_LINK_ADDRESS_GENERATION_MODE, true, NULL);
+        r = set_link_handler_internal(rtnl, m, link, SET_LINK_ADDRESS_GENERATION_MODE, /* ignore = */ true, NULL);
         if (r <= 0)
                 return r;
 
@@ -120,31 +127,31 @@ static int link_set_addrgen_mode_handler(sd_netlink *rtnl, sd_netlink_message *m
 }
 
 static int link_set_bond_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return set_link_handler_internal(rtnl, m, link, SET_LINK_BOND, false, NULL);
+        return set_link_handler_internal(rtnl, m, link, SET_LINK_BOND, /* ignore = */ false, NULL);
 }
 
 static int link_set_bridge_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE, false, NULL);
+        return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE, /* ignore = */ true, NULL);
 }
 
 static int link_set_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE_VLAN, false, NULL);
+        return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE_VLAN, /* ignore = */ false, NULL);
 }
 
 static int link_set_can_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return set_link_handler_internal(rtnl, m, link, SET_LINK_CAN, false, NULL);
+        return set_link_handler_internal(rtnl, m, link, SET_LINK_CAN, /* ignore = */ false, NULL);
 }
 
 static int link_set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return set_link_handler_internal(rtnl, m, link, SET_LINK_FLAGS, false, get_link_update_flag_handler);
+        return set_link_handler_internal(rtnl, m, link, SET_LINK_FLAGS, /* ignore = */ false, get_link_update_flag_handler);
 }
 
 static int link_set_group_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return set_link_handler_internal(rtnl, m, link, SET_LINK_GROUP, false, NULL);
+        return set_link_handler_internal(rtnl, m, link, SET_LINK_GROUP, /* ignore = */ false, NULL);
 }
 
 static int link_set_mac_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return set_link_handler_internal(rtnl, m, link, SET_LINK_MAC, true, get_link_default_handler);
+        return set_link_handler_internal(rtnl, m, link, SET_LINK_MAC, /* ignore = */ true, get_link_default_handler);
 }
 
 static int link_set_mac_allow_retry_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@@ -180,13 +187,18 @@ static int link_set_mac_allow_retry_handler(sd_netlink *rtnl, sd_netlink_message
 }
 
 static int link_set_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
-        return set_link_handler_internal(rtnl, m, link, SET_LINK_MASTER, false, get_link_master_handler);
+        return set_link_handler_internal(rtnl, m, link, SET_LINK_MASTER, /* ignore = */ false, get_link_master_handler);
+}
+
+static int link_unset_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        /* Some devices do not support setting master ifindex. Let's ignore error on unsetting master ifindex. */
+        return set_link_handler_internal(rtnl, m, link, SET_LINK_MASTER, /* ignore = */ true, get_link_master_handler);
 }
 
 static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
-        r = set_link_handler_internal(rtnl, m, link, SET_LINK_MTU, true, get_link_default_handler);
+        r = set_link_handler_internal(rtnl, m, link, SET_LINK_MTU, /* ignore = */ true, get_link_default_handler);
         if (r <= 0)
                 return r;
 
@@ -217,10 +229,14 @@ static int link_configure(
 
         log_link_debug(link, "Setting %s", set_link_operation_to_string(op));
 
-        if (IN_SET(op, SET_LINK_BOND, SET_LINK_CAN)) {
+        if (op == SET_LINK_BOND) {
                 r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_NEWLINK, link->master_ifindex);
                 if (r < 0)
                         return log_link_debug_errno(link, r, "Could not allocate RTM_NEWLINK message: %m");
+        } else if (op == SET_LINK_CAN) {
+                r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_NEWLINK, link->ifindex);
+                if (r < 0)
+                        return log_link_debug_errno(link, r, "Could not allocate RTM_NEWLINK message: %m");
         } else {
                 r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
                 if (r < 0)
@@ -460,7 +476,7 @@ static int link_configure(
                         return log_link_debug_errno(link, r, "Could not append IFLA_MTU attribute: %m");
                 break;
         default:
-                assert_not_reached("Invalid set link operation");
+                assert_not_reached();
         }
 
         r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
@@ -745,10 +761,14 @@ int link_request_to_set_mac(Link *link, bool allow_retry) {
 
 int link_request_to_set_master(Link *link) {
         assert(link);
+        assert(link->network);
 
         link->master_set = false;
 
-        return link_request_set_link(link, SET_LINK_MASTER, link_set_master_handler, NULL);
+        if (link->network->batadv || link->network->bond || link->network->bridge || link->network->vrf)
+                return link_request_set_link(link, SET_LINK_MASTER, link_set_master_handler, NULL);
+        else
+                return link_request_set_link(link, SET_LINK_MASTER, link_unset_master_handler, NULL);
 }
 
 int link_request_to_set_mtu(Link *link, uint32_t mtu) {
@@ -998,7 +1018,7 @@ int link_request_to_activate(Link *link) {
                 up = false;
                 break;
         default:
-                assert_not_reached("invalid activation policy");
+                assert_not_reached();
         }
 
         link->activated = false;
index 7a76b61c4aefec0384bb1b92500564b0717eb4df..106560974e994e7ace4096a771ac3c9f246bc729 100644 (file)
@@ -309,7 +309,7 @@ int config_parse_sr_iov_uint32(
                 else if (streq(lvalue, "QualityOfService"))
                         sr_iov->qos = 0;
                 else
-                        assert_not_reached("Invalid lvalue");
+                        assert_not_reached();
 
                 TAKE_PTR(sr_iov);
                 return 0;
@@ -337,7 +337,7 @@ int config_parse_sr_iov_uint32(
         } else if (streq(lvalue, "QualityOfService"))
                 sr_iov->qos = k;
         else
-                assert_not_reached("Invalid lvalue");
+                assert_not_reached();
 
         TAKE_PTR(sr_iov);
         return 0;
@@ -467,7 +467,7 @@ int config_parse_sr_iov_boolean(
                 else if (streq(lvalue, "Trust"))
                         sr_iov->trust = -1;
                 else
-                        assert_not_reached("Invalid lvalue");
+                        assert_not_reached();
 
                 TAKE_PTR(sr_iov);
                 return 0;
@@ -486,7 +486,7 @@ int config_parse_sr_iov_boolean(
         else if (streq(lvalue, "Trust"))
                 sr_iov->trust = r;
         else
-                assert_not_reached("Invalid lvalue");
+                assert_not_reached();
 
         TAKE_PTR(sr_iov);
         return 0;
index 807c247b5747e38359dd0efb048a15e1d1303903..33f40d87d443da0350a0d3c944ac7385da310334 100644 (file)
@@ -168,7 +168,7 @@ int config_parse_controlled_delay_usec(
         else if (streq(lvalue, "CEThresholdSec"))
                 p = &cd->ce_threshold_usec;
         else
-                assert_not_reached("Invalid lvalue");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 if (streq(lvalue, "CEThresholdSec"))
index 8214a575bf7b96d12644c9da1fd9d54bf62fd172..f8dd1adabc8e7079603e902b59a66580c31f22f5 100644 (file)
@@ -113,7 +113,7 @@ int config_parse_ets_u8(
         else if (streq(lvalue, "StrictBands"))
                 p = &ets->n_strict;
         else
-                assert_not_reached("Invalid lvalue.");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 *p = 0;
index 3aa957373148eeda68cbf52ca5cf5d8c10e75ee1..3dd7f0b5f405e9e6edaee64c641727c505cc6576 100644 (file)
@@ -30,7 +30,7 @@ static int fifo_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req)
                 fifo = PFIFO_HEAD_DROP(qdisc);
                 break;
         default:
-                assert_not_reached("Invalid QDisc kind.");
+                assert_not_reached();
         }
 
         opt.limit = fifo->limit;
@@ -81,7 +81,7 @@ int config_parse_pfifo_size(
                 fifo = PFIFO_HEAD_DROP(qdisc);
                 break;
         default:
-                assert_not_reached("Invalid QDisc kind.");
+                assert_not_reached();
         }
 
         if (isempty(rvalue)) {
index bcc734df90fc910a9e3ce2a3ebbe9264e7e1f732..7c61cf2ac65e4e896fce40da3680b757fa805efb 100644 (file)
@@ -133,7 +133,7 @@ int config_parse_fair_queueing_controlled_delay_u32(
         else if (streq(lvalue, "Flows"))
                 p = &fqcd->flows;
         else
-                assert_not_reached("Invalid lvalue.");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 *p = 0;
@@ -196,7 +196,7 @@ int config_parse_fair_queueing_controlled_delay_usec(
         else if (streq(lvalue, "CEThresholdSec"))
                 p = &fqcd->ce_threshold_usec;
         else
-                assert_not_reached("Invalid lvalue.");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 if (streq(lvalue, "CEThresholdSec"))
@@ -315,7 +315,7 @@ int config_parse_fair_queueing_controlled_delay_size(
         else if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum"))
                 p = &fqcd->quantum;
         else
-                assert_not_reached("Invalid lvalue.");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit"))
index d48aea86fbca05885f54cb242b6bb74c58f24bc4..d10a863495cc88153a51e3657c9d93a0eeb7d4c9 100644 (file)
@@ -145,7 +145,7 @@ int config_parse_fair_queueing_u32(
         else if (streq(lvalue, "OrphanMask"))
                 p = &fq->orphan_mask;
         else
-                assert_not_reached("Invalid lvalue");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 *p = 0;
@@ -207,7 +207,7 @@ int config_parse_fair_queueing_size(
         else if (STR_IN_SET(lvalue, "InitialQuantumBytes", "InitialQuantum"))
                 p = &fq->initial_quantum;
         else
-                assert_not_reached("Invalid lvalue");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 *p = 0;
index 04fcd59e48bcfa262e3008d55d3cfaef2a8518d3..ce26e62450fae3ceec56deddbfea1bd6af052d96 100644 (file)
@@ -106,7 +106,7 @@ int config_parse_generic_random_early_detection_u32(
         else if (streq(lvalue, "DefaultVirtualQueue"))
                 p = &gred->default_virtual_queue;
         else
-                assert_not_reached("Invalid lvalue.");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 *p = 0;
index 17455248a7a66af31c23c5e6237ee43a7392c1f7..14fb1f92e34fac50658a76a36514040a28353d39 100644 (file)
@@ -338,7 +338,7 @@ int config_parse_hierarchy_token_bucket_class_size(
                 else if (streq(lvalue, "CeilBufferBytes"))
                         htb->ceil_buffer = 0;
                 else
-                        assert_not_reached("Invalid lvalue");
+                        assert_not_reached();
 
                 tclass = NULL;
                 return 0;
@@ -369,7 +369,7 @@ int config_parse_hierarchy_token_bucket_class_size(
         else if (streq(lvalue, "CeilBufferBytes"))
                 htb->ceil_buffer = v;
         else
-                assert_not_reached("Invalid lvalue");
+                assert_not_reached();
 
         tclass = NULL;
 
@@ -414,7 +414,7 @@ int config_parse_hierarchy_token_bucket_class_rate(
         else if (streq(lvalue, "CeilRate"))
                 v = &htb->ceil_rate;
         else
-                assert_not_reached("Invalid lvalue");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 *v = 0;
index 50d14a535646d2e3f3fb19f9fd735717f7d7ce43..1d1bc6f0e90a74efdc4162191ed8c6f88c36226a 100644 (file)
@@ -154,7 +154,7 @@ int config_parse_token_bucket_filter_size(
                 else if (streq(lvalue, "MPUBytes"))
                         tbf->mpu = 0;
                 else
-                        assert_not_reached("unknown lvalue");
+                        assert_not_reached();
 
                 TAKE_PTR(qdisc);
                 return 0;
@@ -177,7 +177,7 @@ int config_parse_token_bucket_filter_size(
         else if (streq(lvalue, "MTUBytes"))
                 tbf->mtu = k;
         else
-                assert_not_reached("unknown lvalue");
+                assert_not_reached();
 
         TAKE_PTR(qdisc);
 
@@ -222,7 +222,7 @@ int config_parse_token_bucket_filter_rate(
         else if (streq(lvalue, "PeakRate"))
                 p = &tbf->peak_rate;
         else
-                assert_not_reached("unknown lvalue");
+                assert_not_reached();
 
         if (isempty(rvalue)) {
                 *p = 0;
index 0cd46cf6339fffa01334d49ce230c221679409e7..a3cfed53fa22857ab57f5004a43297b8bad26aab 100644 (file)
@@ -17,7 +17,7 @@ void traffic_control_free(TrafficControl *tc) {
                 tclass_free(TC_TO_TCLASS(tc));
                 break;
         default:
-                assert_not_reached("Invalid traffic control type");
+                assert_not_reached();
         }
 }
 
@@ -31,7 +31,7 @@ static int traffic_control_configure(Link *link, TrafficControl *tc) {
         case TC_KIND_TCLASS:
                 return tclass_configure(link, TC_TO_TCLASS(tc));
         default:
-                assert_not_reached("Invalid traffic control type");
+                assert_not_reached();
         }
 }
 
@@ -72,7 +72,7 @@ static int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_ro
         case TC_KIND_TCLASS:
                 return tclass_section_verify(TC_TO_TCLASS(tc));
         default:
-                assert_not_reached("Invalid traffic control type");
+                assert_not_reached();
         }
 }
 
index 1b24b6f1a695dad0e78cf48ea4b1c9e3a9d534e8..ad6f18bbd4c34bffca378539279c164ef23f6af8 100644 (file)
@@ -187,7 +187,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index 99f54ceaaab609d8c3b1bd0f24c88c4d91af2e96..b468a5bc448cc64ab52bed28866551d87872e96a 100644 (file)
@@ -170,7 +170,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
         }
 
@@ -232,7 +232,7 @@ static int run(int argc, char* argv[]) {
 
         our_env[i++] = NULL;
 
-        final_env = strv_env_merge(2, our_env, argv + optind);
+        final_env = strv_env_merge(our_env, argv + optind);
         if (!final_env)
                 return log_oom();
 
index 60f956da0f612a8b88d80b8716e981f42fb313fe..c59151685c97545ecf3db7bee71f4e677dec5ef0 100644 (file)
@@ -946,7 +946,7 @@ int mount_custom(
                         break;
 
                 default:
-                        assert_not_reached("Unknown custom mount type");
+                        assert_not_reached();
                 }
 
                 if (r < 0)
index 785332e09103633d42bc8c2aff5aab5ed6b6d892..f47f82736104706b584a6becc4243d842259d1ff 100644 (file)
@@ -22,7 +22,6 @@
 #if HAVE_ACL
 
 static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
         acl_t acl;
 
         assert(fd >= 0);
@@ -35,14 +34,11 @@ static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
                 if (child_fd < 0)
                         return -errno;
 
-                xsprintf(procfs_path, "/proc/self/fd/%i", child_fd);
-                acl = acl_get_file(procfs_path, type);
+                acl = acl_get_file(FORMAT_PROC_FD_PATH(child_fd), type);
         } else if (type == ACL_TYPE_ACCESS)
                 acl = acl_get_fd(fd);
-        else {
-                xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-                acl = acl_get_file(procfs_path, type);
-        }
+        else
+                acl = acl_get_file(FORMAT_PROC_FD_PATH(fd), type);
         if (!acl)
                 return -errno;
 
@@ -51,7 +47,6 @@ static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) {
 }
 
 static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
         int r;
 
         assert(fd >= 0);
@@ -64,14 +59,11 @@ static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) {
                 if (child_fd < 0)
                         return -errno;
 
-                xsprintf(procfs_path, "/proc/self/fd/%i", child_fd);
-                r = acl_set_file(procfs_path, type, acl);
+                r = acl_set_file(FORMAT_PROC_FD_PATH(child_fd), type, acl);
         } else if (type == ACL_TYPE_ACCESS)
                 r = acl_set_fd(fd, acl);
-        else {
-                xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-                r = acl_set_file(procfs_path, type, acl);
-        }
+        else
+                r = acl_set_file(FORMAT_PROC_FD_PATH(fd), type, acl);
         if (r < 0)
                 return -errno;
 
index 3cbe4ef5a6c1a3717ed21836dde57f241a51f777..6dbd6ba4c9e45983346ee5fdec46e3a2336eb62d 100644 (file)
@@ -180,7 +180,7 @@ int stub_pid1(sd_id128_t uuid) {
 
                         state = STATE_REBOOT;
                 else
-                        assert_not_reached("Got unexpected signal");
+                        assert_not_reached();
 
                 r = kill_and_sigcont(pid, SIGTERM);
 
index 99a325621976e75409dc933a1a91c6b0929c3d11..0ccab72170969585e5ad47f46622c1bc8ad5103d 100644 (file)
@@ -338,7 +338,7 @@ static int help(void) {
                "  -a --as-pid2              Maintain a stub init as PID1, invoke binary as PID2\n"
                "  -b --boot                 Boot up full system (i.e. invoke init)\n"
                "     --chdir=PATH           Set working directory in the container\n"
-               "  -E --setenv=NAME=VALUE    Pass an environment variable to PID 1\n"
+               "  -E --setenv=NAME[=VALUE]  Pass an environment variable to PID 1\n"
                "  -u --user=USER            Run the command under specified user or UID\n"
                "     --kill-signal=SIGNAL   Select signal to use for shutting down PID 1\n"
                "     --notify-ready=BOOLEAN Receive notifications from the child init process\n\n"
@@ -1121,17 +1121,13 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_settings_mask |= SETTING_CUSTOM_MOUNTS;
                         break;
 
-                case 'E': {
-                        if (!env_assignment_is_valid(optarg))
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Environment variable assignment '%s' is not valid.", optarg);
-                        r = strv_env_replace_strdup(&arg_setenv, optarg);
+                case 'E':
+                        r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
                         if (r < 0)
-                                return r;
+                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
 
                         arg_settings_mask |= SETTING_ENVIRONMENT;
                         break;
-                }
 
                 case 'q':
                         arg_quiet = true;
@@ -1675,7 +1671,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (argc > optind) {
@@ -2013,7 +2009,7 @@ static int setup_timezone(const char *dest) {
                 break;
 
         default:
-                assert_not_reached("unexpected mode");
+                assert_not_reached();
         }
 
         /* Fix permissions of the symlink or file copy we just created */
@@ -2554,7 +2550,6 @@ static int setup_hostname(void) {
 
 static int setup_journal(const char *directory) {
         _cleanup_free_ char *d = NULL;
-        char id[SD_ID128_STRING_MAX];
         const char *dirname, *p, *q;
         sd_id128_t this_id;
         bool try;
@@ -2575,7 +2570,7 @@ static int setup_journal(const char *directory) {
 
         if (sd_id128_equal(arg_uuid, this_id)) {
                 log_full(try ? LOG_WARNING : LOG_ERR,
-                         "Host and machine ids are equal (%s): refusing to link journals", sd_id128_to_string(arg_uuid, id));
+                         "Host and machine ids are equal (%s): refusing to link journals", SD_ID128_TO_STRING(arg_uuid));
                 if (try)
                         return 0;
                 return -EEXIST;
@@ -2591,9 +2586,7 @@ static int setup_journal(const char *directory) {
                 }
         }
 
-        (void) sd_id128_to_string(arg_uuid, id);
-
-        p = strjoina("/var/log/journal/", id);
+        p = strjoina("/var/log/journal/", SD_ID128_TO_STRING(arg_uuid));
         q = prefix_roota(directory, p);
 
         if (path_is_mount_point(p, NULL, 0) > 0) {
@@ -3187,10 +3180,9 @@ static int inner_child(
                 char **os_release_pairs) {
 
         _cleanup_free_ char *home = NULL;
-        char as_uuid[ID128_UUID_STRING_MAX];
         size_t n_env = 1;
-        const char *envp[] = {
-                "PATH=" DEFAULT_PATH_COMPAT,
+        char *envp[] = {
+                (char*) "PATH=" DEFAULT_PATH_COMPAT,
                 NULL, /* container */
                 NULL, /* TERM */
                 NULL, /* HOME */
@@ -3426,17 +3418,17 @@ static int inner_child(
                 n_env++;
 
         if (home || !uid_is_valid(arg_uid) || arg_uid == 0)
-                if (asprintf((char**)(envp + n_env++), "HOME=%s", home ?: "/root") < 0)
+                if (asprintf(envp + n_env++, "HOME=%s", home ?: "/root") < 0)
                         return log_oom();
 
         if (arg_user || !uid_is_valid(arg_uid) || arg_uid == 0)
-                if (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ?: "root") < 0 ||
-                    asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)
+                if (asprintf(envp + n_env++, "USER=%s", arg_user ?: "root") < 0 ||
+                    asprintf(envp + n_env++, "LOGNAME=%s", arg_user ? arg_user : "root") < 0)
                         return log_oom();
 
         assert(!sd_id128_is_null(arg_uuid));
 
-        if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0)
+        if (asprintf(envp + n_env++, "container_uuid=%s", ID128_TO_UUID_STRING(arg_uuid)) < 0)
                 return log_oom();
 
         if (fdset_size(fds) > 0) {
@@ -3444,11 +3436,11 @@ static int inner_child(
                 if (r < 0)
                         return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
 
-                if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", fdset_size(fds)) < 0) ||
-                    (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0))
+                if ((asprintf(envp + n_env++, "LISTEN_FDS=%u", fdset_size(fds)) < 0) ||
+                    (asprintf(envp + n_env++, "LISTEN_PID=1") < 0))
                         return log_oom();
         }
-        if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
+        if (asprintf(envp + n_env++, "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
                 return log_oom();
 
         if (arg_n_credentials > 0) {
@@ -3458,7 +3450,7 @@ static int inner_child(
                 n_env++;
         }
 
-        env_use = strv_env_merge(3, envp, os_release_pairs, arg_setenv);
+        env_use = strv_env_merge(envp, os_release_pairs, arg_setenv);
         if (!env_use)
                 return log_oom();
 
index 0d215cf6e977c083670e8fc2fb32da9145368bc9..1836dc1014fef990b687d50a4d45ea566c498fc2 100644 (file)
@@ -110,7 +110,7 @@ static int parse_argv(int argc, char *argv[]) {
                                 return -EINVAL;
 
                         default:
-                                assert_not_reached("Invalid option passed.");
+                                assert_not_reached();
                 }
 
         return 1;
index deb7b094d504b49858f78f57a1ff9bf5361ac859..8d4014ee0633d73a69ce3e394870b2b79fbd8ade 100644 (file)
@@ -105,7 +105,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unknown option code.");
+                        assert_not_reached();
                 }
 
         if (optind < argc)
index 0497c6cfc602ba5ba7d00e7d030123acfb7044ce..af6a9ef600dad988c2a1834f01e2f287374f2381 100644 (file)
@@ -180,7 +180,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind + 1 != argc)
index 6d535a3bab05334bb655d53f34efee7314deaac0..d056745c090907eb8e98c4b5dcbb9a7281bb149c 100644 (file)
@@ -1581,12 +1581,8 @@ static int context_load_partition_table(
          * /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
         if (*backing_fd < 0)
                 r = fdisk_assign_device(c, node, arg_dry_run);
-        else {
-                char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-                xsprintf(procfs_path, "/proc/self/fd/%i", *backing_fd);
-
-                r = fdisk_assign_device(c, procfs_path, arg_dry_run);
-        }
+        else
+                r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(*backing_fd), arg_dry_run);
         if (r == -EINVAL && arg_size_auto) {
                 struct stat st;
 
@@ -2119,7 +2115,6 @@ static void context_bar_char_process_partition(
 
 static int partition_hint(const Partition *p, const char *node, char **ret) {
         _cleanup_free_ char *buf = NULL;
-        char ids[ID128_UUID_STRING_MAX];
         const char *label;
         sd_id128_t id;
 
@@ -2148,7 +2143,7 @@ static int partition_hint(const Partition *p, const char *node, char **ret) {
         else
                 id = p->type_uuid;
 
-        buf = strdup(id128_to_uuid_string(id, ids));
+        buf = strdup(ID128_TO_UUID_STRING(id));
 
 done:
         if (!buf)
@@ -2548,7 +2543,6 @@ static int partition_encrypt(
         _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_(erase_and_freep) void *volume_key = NULL;
         _cleanup_free_ char *dm_name = NULL, *vol = NULL;
-        char suuid[ID128_UUID_STRING_MAX];
         size_t volume_key_size = 256 / 8;
         sd_id128_t uuid;
         int r;
@@ -2595,7 +2589,7 @@ static int partition_encrypt(
                          CRYPT_LUKS2,
                          "aes",
                          "xts-plain64",
-                         id128_to_uuid_string(uuid, suuid),
+                         ID128_TO_UUID_STRING(uuid),
                          volume_key,
                          volume_key_size,
                          &(struct crypt_params_luks2) {
@@ -2624,9 +2618,10 @@ static int partition_encrypt(
                 _cleanup_(erase_and_freep) void *secret = NULL;
                 _cleanup_free_ void *blob = NULL, *hash = NULL;
                 size_t secret_size, blob_size, hash_size;
+                uint16_t pcr_bank;
                 int keyslot;
 
-                r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size);
+                r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank);
                 if (r < 0)
                         return log_error_errno(r, "Failed to seal to TPM2: %m");
 
@@ -2648,7 +2643,7 @@ static int partition_encrypt(
                 if (keyslot < 0)
                         return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
 
-                r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, blob, blob_size, hash, hash_size, &v);
+                r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v);
                 if (r < 0)
                         return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
 
@@ -2848,13 +2843,13 @@ static int do_copy_files(Partition *p, const char *fs) {
                                                 sfd, ".",
                                                 pfd, fn,
                                                 UID_INVALID, GID_INVALID,
-                                                COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
+                                                COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS);
                         } else
                                 r = copy_tree_at(
                                                 sfd, ".",
                                                 tfd, ".",
                                                 UID_INVALID, GID_INVALID,
-                                                COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
+                                                COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
                 } else {
@@ -2889,7 +2884,7 @@ static int do_copy_files(Partition *p, const char *fs) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
 
-                        (void) copy_xattr(sfd, tfd);
+                        (void) copy_xattr(sfd, tfd, COPY_ALL_XATTRS);
                         (void) copy_access(sfd, tfd);
                         (void) copy_times(sfd, tfd, 0);
                 }
@@ -3343,11 +3338,9 @@ static int context_mangle_partitions(Context *context) {
                         }
 
                         if (!sd_id128_equal(p->new_uuid, p->current_uuid)) {
-                                char buf[ID128_UUID_STRING_MAX];
-
                                 assert(!sd_id128_is_null(p->new_uuid));
 
-                                r = fdisk_partition_set_uuid(p->current_partition, id128_to_uuid_string(p->new_uuid, buf));
+                                r = fdisk_partition_set_uuid(p->current_partition, ID128_TO_UUID_STRING(p->new_uuid));
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to set partition UUID: %m");
 
@@ -3374,7 +3367,6 @@ static int context_mangle_partitions(Context *context) {
                 } else {
                         _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *q = NULL;
                         _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
-                        char ids[ID128_UUID_STRING_MAX];
 
                         assert(!p->new_partition);
                         assert(p->offset % 512 == 0);
@@ -3386,7 +3378,7 @@ static int context_mangle_partitions(Context *context) {
                         if (!t)
                                 return log_oom();
 
-                        r = fdisk_parttype_set_typestr(t, id128_to_uuid_string(p->type_uuid, ids));
+                        r = fdisk_parttype_set_typestr(t, ID128_TO_UUID_STRING(p->type_uuid));
                         if (r < 0)
                                 return log_error_errno(r, "Failed to initialize partition type: %m");
 
@@ -3414,7 +3406,7 @@ static int context_mangle_partitions(Context *context) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to set partition number: %m");
 
-                        r = fdisk_partition_set_uuid(q, id128_to_uuid_string(p->new_uuid, ids));
+                        r = fdisk_partition_set_uuid(q, ID128_TO_UUID_STRING(p->new_uuid));
                         if (r < 0)
                                 return log_error_errno(r, "Failed to set partition UUID: %m");
 
@@ -4346,7 +4338,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (argc - optind > 1)
@@ -4592,7 +4584,6 @@ static int find_root(char **ret, int *ret_fd) {
 }
 
 static int resize_pt(int fd) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         int r;
 
@@ -4604,14 +4595,13 @@ static int resize_pt(int fd) {
         if (!c)
                 return log_oom();
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        r = fdisk_assign_device(c, procfs_path, 0);
+        r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(fd), 0);
         if (r < 0)
-                return log_error_errno(r, "Failed to open device '%s': %m", procfs_path);
+                return log_error_errno(r, "Failed to open device '%s': %m", FORMAT_PROC_FD_PATH(fd));
 
         r = fdisk_has_label(c);
         if (r < 0)
-                return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", procfs_path);
+                return log_error_errno(r, "Failed to determine whether disk '%s' has a disk label: %m", FORMAT_PROC_FD_PATH(fd));
         if (r == 0) {
                 log_debug("Not resizing partition table, as there currently is none.");
                 return 0;
index aec75f1fb7b8df5e23d851a62c015c6812704246..0024a606112961b5db5bc49ef57755ffc28c67bf 100644 (file)
@@ -185,7 +185,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index 8f866f57e47a3d7c540ef22c7233b04570a25bba..de82f5bff7f5c8ab33e4944a41459ef516ab4955 100644 (file)
@@ -22,8 +22,8 @@ if conf.get('ENABLE_PORTABLED') == 1
         install_data('org.freedesktop.portable1.policy',
                      install_dir : polkitpolicydir)
 
-        install_data('profile/default/service.conf', install_dir : join_paths(profiledir, 'default'))
-        install_data('profile/nonetwork/service.conf', install_dir : join_paths(profiledir, 'nonetwork'))
-        install_data('profile/strict/service.conf', install_dir : join_paths(profiledir, 'strict'))
-        install_data('profile/trusted/service.conf', install_dir : join_paths(profiledir, 'trusted'))
+        install_data('profile/default/service.conf', install_dir : profiledir / 'default')
+        install_data('profile/nonetwork/service.conf', install_dir : profiledir / 'nonetwork')
+        install_data('profile/strict/service.conf', install_dir : profiledir / 'strict')
+        install_data('profile/trusted/service.conf', install_dir : profiledir / 'trusted')
 endif
index 5afb008bb8f2737d5dcad8902a213a9a7739606a..ba7fd58fc12bf4b8bbd50b9692f799ba77168c83 100644 (file)
@@ -481,7 +481,7 @@ static int portable_extract_by_path(
                                 assert(!os_release);
                                 os_release = TAKE_PTR(add);
                         } else
-                                assert_not_reached("Unexpected metadata item from child.");
+                                assert_not_reached();
                 }
 
                 r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
index 2d8079ad97992ef795d688351aa0938bb8a45db9..c2d79ec3a23d90040060b8ce7368046c69b51cbd 100644 (file)
@@ -1288,7 +1288,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
         }
 
index 93ded6d56437b3d51f34f3b19c3af22abef65a3c..4fdf445c98c996bd07c789fd72aa57b2afe860ce 100644 (file)
@@ -200,7 +200,7 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_mode == _MODE_INVALID)
index e9fb8b32fc55adc53f8bcb17aed2a42aaeb47048..196dccdba08e92306b409ba5331b3c12294b3017 100644 (file)
@@ -3009,7 +3009,7 @@ static int compat_parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_type == 0 && arg_class != 0)
@@ -3261,7 +3261,7 @@ static int native_parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_type == 0 && arg_class != 0)
@@ -3411,7 +3411,7 @@ static int compat_main(int argc, char *argv[], sd_bus *bus) {
                 return translate("revert", arg_ifname, 0, NULL, bus);
 
         case _MODE_INVALID:
-                assert_not_reached("invalid mode");
+                assert_not_reached();
         }
 
         return 0;
index 7226200f00664500bf02d776131f0ca11bacbc5c..8ad50ba29b4e6a7487a60d023589c27a8b45ebe7 100644 (file)
@@ -135,7 +135,7 @@ static int reply_query_state(DnsQuery *q) {
         case DNS_TRANSACTION_VALIDATING:
         case DNS_TRANSACTION_SUCCESS:
         default:
-                assert_not_reached("Impossible state");
+                assert_not_reached();
         }
 }
 
index 283c06345c59015256b1678494813b85a87c02dd..3afaaad9372cdf9a9d7d643fb25ab2683ae71663 100644 (file)
@@ -395,7 +395,7 @@ int config_parse_dnssd_txt(
                         break;
 
                 default:
-                        assert_not_reached("Unknown type of Txt config");
+                        assert_not_reached();
                 }
 
                 LIST_INSERT_AFTER(items, txt_data->txt, last, i);
index 91da5b65156e85f805582c3611728057e11f084b..fd7679f17dd8e378297e9f5734f45b81db8289ef 100644 (file)
@@ -1028,7 +1028,7 @@ int dnssec_verify_rrset_search(
                                 continue;
 
                         default:
-                                assert_not_reached("Unexpected DNSSEC validation result");
+                                assert_not_reached();
                         }
                 }
         }
index 6e7175c754769d0d327bac4964d5f9831817a09d..0b797ecb1a12549695f8b840167dde544ee05a7a 100644 (file)
@@ -332,7 +332,7 @@ static inline size_t udp_header_size(int af) {
         case AF_INET6:
                 return UDP6_PACKET_HEADER_SIZE;
         default:
-                assert_not_reached("Unexpected address family");
+                assert_not_reached();
         }
 }
 
index 4a0327a19dcbf7eec16847ac762027be501e97b8..720b4c58d762eae88e79288aa832482d46aecee9 100644 (file)
@@ -47,8 +47,8 @@ DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const D
         if (cname->key->type == DNS_TYPE_CNAME)
                 return dns_resource_key_new(key->class, key->type, cname->cname.name);
         else {
+                _cleanup_free_ char *destination = NULL;
                 DnsResourceKey *k;
-                char *destination = NULL;
 
                 r = dns_name_change_suffix(dns_resource_key_name(key), dns_resource_key_name(cname->key), cname->dname.name, &destination);
                 if (r < 0)
@@ -58,8 +58,9 @@ DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const D
 
                 k = dns_resource_key_new_consume(key->class, key->type, destination);
                 if (!k)
-                        return mfree(destination);
+                        return NULL;
 
+                TAKE_PTR(destination);
                 return k;
         }
 }
@@ -322,10 +323,10 @@ char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t bu
         c = dns_class_to_string(key->class);
         t = dns_type_to_string(key->type);
 
-        snprintf(buf, buf_size, "%s %s%s%.0u %s%s%.0u",
-                 dns_resource_key_name(key),
-                 strempty(c), c ? "" : "CLASS", c ? 0 : key->class,
-                 strempty(t), t ? "" : "TYPE", t ? 0 : key->type);
+        (void) snprintf(buf, buf_size, "%s %s%s%.0u %s%s%.0u",
+                        dns_resource_key_name(key),
+                        strempty(c), c ? "" : "CLASS", c ? 0 : key->class,
+                        strempty(t), t ? "" : "TYPE", t ? 0 : key->type);
 
         return ans;
 }
index 178482727c3ecb95611bfa4de08fdc20bda851e7..26a3006a6c98e72dbbb78f547d72d291787f9f6f 100644 (file)
@@ -757,7 +757,7 @@ DnsScopeMatch dns_scope_good_domain(
         }
 
         default:
-                assert_not_reached("Unknown scope protocol");
+                assert_not_reached();
         }
 }
 
index 94a46570f2984ec9cbaa19d22a56925c79fb88ea..c9f148a2b99dc54182b5659c54b7d095d132ef4b 100644 (file)
@@ -58,7 +58,7 @@ int dns_search_domain_new(
                 break;
 
         default:
-                assert_not_reached("Unknown search domain type");
+                assert_not_reached();
         }
 
         d->linked = true;
@@ -135,7 +135,7 @@ void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) {
                 break;
 
         default:
-                assert_not_reached("Unknown search domain type");
+                assert_not_reached();
         }
 }
 
index e7a4bce71a4a196f960a775e0bdac983ed9ae5eb..9bb716e2c130600bedaa48b236cd7904ab835c8c 100644 (file)
@@ -90,7 +90,7 @@ int dns_server_new(
                 break;
 
         default:
-                assert_not_reached("Unknown server type");
+                assert_not_reached();
         }
 
         s->linked = true;
@@ -158,7 +158,7 @@ void dns_server_unlink(DnsServer *s) {
                 s->manager->n_dns_servers--;
                 break;
         default:
-                assert_not_reached("Unknown server type");
+                assert_not_reached();
         }
 
         s->linked = false;
@@ -213,7 +213,7 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
                 break;
 
         default:
-                assert_not_reached("Unknown server type");
+                assert_not_reached();
         }
 }
 
@@ -362,9 +362,8 @@ void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level
         if (s->possible_feature_level > level) {
                 s->possible_feature_level = level;
                 dns_server_reset_counters(s);
+                log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
         }
-
-        log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
 }
 
 void dns_server_packet_invalid(DnsServer *s, DnsServerFeatureLevel level) {
index 5b9d32f0013085758eb228541f089c8bc7232e85..aaabc92486ad65ac0488413c03415965423f33c4 100644 (file)
@@ -585,7 +585,7 @@ static int dns_stub_send_reply(
                         DNS_PACKET_RD(q->request_packet),
                         !!q->request_packet->opt,
                         edns0_do,
-                        DNS_PACKET_AD(q->request_packet) && dns_query_fully_authenticated(q),
+                        (DNS_PACKET_AD(q->request_packet) || DNS_PACKET_DO(q->request_packet)) && dns_query_fully_authenticated(q),
                         DNS_PACKET_CD(q->request_packet),
                         q->stub_listener_extra ? ADVERTISE_EXTRA_DATAGRAM_SIZE_MAX : ADVERTISE_DATAGRAM_SIZE_MAX,
                         dns_packet_has_nsid_request(q->request_packet) > 0 && !q->stub_listener_extra);
@@ -627,7 +627,7 @@ static int dns_stub_send_failure(
                         DNS_PACKET_RD(p),
                         !!p->opt,
                         DNS_PACKET_DO(p),
-                        DNS_PACKET_AD(p) && authenticated,
+                        (DNS_PACKET_AD(p) || DNS_PACKET_DO(p)) && authenticated,
                         DNS_PACKET_CD(p),
                         l ? ADVERTISE_EXTRA_DATAGRAM_SIZE_MAX : ADVERTISE_DATAGRAM_SIZE_MAX,
                         dns_packet_has_nsid_request(p) > 0 && !l);
@@ -813,7 +813,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
         case DNS_TRANSACTION_PENDING:
         case DNS_TRANSACTION_VALIDATING:
         default:
-                assert_not_reached("Impossible state");
+                assert_not_reached();
         }
 
         dns_query_free(q);
index 9535a7ba4c69021046dcfbd2f7f4025f677570a5..594ce5e27b9e877576076dbd56cef24667c2ceb4 100644 (file)
@@ -1091,7 +1091,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                 break;
 
         default:
-                assert_not_reached("Invalid DNS protocol.");
+                assert_not_reached();
         }
 
         if (t->received != p) {
@@ -1142,22 +1142,35 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                                 break;
                         }
 
-                        /* Reduce this feature level by one and try again. */
-                        switch (t->current_feature_level) {
-                        case DNS_SERVER_FEATURE_LEVEL_TLS_DO:
-                                t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN;
-                                break;
-                        case DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN + 1:
-                                /* Skip plain TLS when TLS is not supported */
-                                t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN - 1;
-                                break;
-                        default:
-                                t->clamp_feature_level_servfail = t->current_feature_level - 1;
-                        }
+                        /* SERVFAIL can happen for many reasons and may be transient.
+                         * To avoid unnecessary downgrades retry once with the initial level.
+                         * Check for clamp_feature_level_servfail having an invalid value as a sign that this is the
+                         * first attempt to downgrade. If so, clamp to the current value so that the transaction
+                         * is retried without actually downgrading. If the next try also fails we will downgrade by
+                         * hitting the else branch below. */
+                        if (DNS_PACKET_RCODE(p) == DNS_RCODE_SERVFAIL &&
+                            t->clamp_feature_level_servfail < 0) {
+                                t->clamp_feature_level_servfail = t->current_feature_level;
+                                log_debug("Server returned error %s, retrying transaction.",
+                                          dns_rcode_to_string(DNS_PACKET_RCODE(p)));
+                        } else {
+                                /* Reduce this feature level by one and try again. */
+                                switch (t->current_feature_level) {
+                                case DNS_SERVER_FEATURE_LEVEL_TLS_DO:
+                                        t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN;
+                                        break;
+                                case DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN + 1:
+                                        /* Skip plain TLS when TLS is not supported */
+                                        t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN - 1;
+                                        break;
+                                default:
+                                        t->clamp_feature_level_servfail = t->current_feature_level - 1;
+                                }
 
-                        log_debug("Server returned error %s, retrying transaction with reduced feature level %s.",
-                                  dns_rcode_to_string(DNS_PACKET_RCODE(p)),
-                                  dns_server_feature_level_to_string(t->clamp_feature_level_servfail));
+                                log_debug("Server returned error %s, retrying transaction with reduced feature level %s.",
+                                          dns_rcode_to_string(DNS_PACKET_RCODE(p)),
+                                          dns_server_feature_level_to_string(t->clamp_feature_level_servfail));
+                        }
 
                         dns_transaction_retry(t, false /* use the same server */);
                         return;
@@ -1184,7 +1197,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
                 break;
 
         default:
-                assert_not_reached("Invalid DNS protocol.");
+                assert_not_reached();
         }
 
         if (DNS_PACKET_TC(p)) {
@@ -1528,7 +1541,7 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
                         break;
 
                 default:
-                        assert_not_reached("Invalid DNS protocol.");
+                        assert_not_reached();
                 }
 
                 log_debug("Timeout reached on transaction %" PRIu16 ".", t->id);
@@ -1567,7 +1580,7 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
                 return t->scope->resend_timeout;
 
         default:
-                assert_not_reached("Invalid DNS protocol.");
+                assert_not_reached();
         }
 }
 
@@ -1959,7 +1972,7 @@ int dns_transaction_go(DnsTransaction *t) {
                         accuracy = MDNS_JITTER_RANGE_USEC;
                         break;
                 default:
-                        assert_not_reached("bad protocol");
+                        assert_not_reached();
                 }
 
                 assert(!t->timeout_event_source);
@@ -3530,7 +3543,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
                         break;
 
                 default:
-                        assert_not_reached("Unexpected NSEC result.");
+                        assert_not_reached();
                 }
         }
 
index 27d8c8967ec4072dd2569a67643905318a97d2c1..4a26f6b9febd2e369d053fc93b51c14af6868f0b 100644 (file)
@@ -78,7 +78,7 @@ static int reply_query_state(DnsQuery *q) {
         case DNS_TRANSACTION_VALIDATING:
         case DNS_TRANSACTION_SUCCESS:
         default:
-                assert_not_reached("Impossible state");
+                assert_not_reached();
         }
 }
 
index 344c8f66412f2098a6a794bd370bddfbbdf82f29..fb3857122819200645e75fd81f5d4842b7aa0695 100644 (file)
@@ -111,7 +111,7 @@ static int help(void) {
                "     --nice=NICE                  Nice level\n"
                "     --working-directory=PATH     Set working directory\n"
                "  -d --same-dir                   Inherit working directory from caller\n"
-               "  -E --setenv=NAME=VALUE          Set environment\n"
+               "  -E --setenv=NAME[=VALUE]        Set environment variable\n"
                "  -t --pty                        Run service on pseudo TTY as STDIN/STDOUT/\n"
                "                                  STDERR\n"
                "  -P --pipe                       Pass STDIN/STDOUT/STDERR directly to service\n"
@@ -322,8 +322,9 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'E':
-                        if (strv_extend(&arg_environment, optarg) < 0)
-                                return log_oom();
+                        r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
 
                         break;
 
@@ -503,7 +504,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         with_trigger = !!arg_path_property || !!arg_socket_property || arg_with_timer;
@@ -1155,7 +1156,7 @@ static int start_transient_service(
                         if (!pty_path)
                                 return log_oom();
                 } else
-                        assert_not_reached("Can't allocate tty via ssh");
+                        assert_not_reached();
         }
 
         /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin
@@ -1526,7 +1527,7 @@ static int start_transient_scope(sd_bus *bus) {
                         return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
         }
 
-        env = strv_env_merge(3, environ, user_env, arg_environment);
+        env = strv_env_merge(environ, user_env, arg_environment);
         if (!env)
                 return log_oom();
 
@@ -1628,7 +1629,7 @@ static int start_transient_trigger(
         else if (streq(suffix, ".timer"))
                 r = transient_timer_set_properties(m);
         else
-                assert_not_reached("Invalid suffix");
+                assert_not_reached();
         if (r < 0)
                 return r;
 
index 10e1857649403a0380b33b8a97d2b83958c6a7f8..3f286a888b5cd44adf65c938bacd1659c5bae7a4 100644 (file)
@@ -320,7 +320,7 @@ static int acl_entry_equal(acl_entry_t a, acl_entry_t b) {
                 return *gid_a == *gid_b;
         }
         default:
-                assert_not_reached("Unknown acl tag type");
+                assert_not_reached();
         }
 }
 
index f4e291385bebb5bd826abce8f5524f5faf9996e1..b01c72aa9ba5282ceffd58d18afda4da76482097 100644 (file)
@@ -1613,7 +1613,7 @@ int btrfs_subvol_snapshot_fd_full(
                         return -EISDIR;
 
                 r = btrfs_subvol_make(new_path);
-                if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
+                if (ERRNO_IS_NOT_SUPPORTED(r) && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
                         /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
                         if (mkdir(new_path, 0755) < 0)
                                 return -errno;
@@ -1624,8 +1624,16 @@ int btrfs_subvol_snapshot_fd_full(
 
                 r = copy_directory_fd_full(
                                 old_fd, new_path,
-                                COPY_MERGE|COPY_REFLINK|COPY_SAME_MOUNT|COPY_HARDLINKS|(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0),
-                                progress_path, progress_bytes, userdata);
+                                COPY_MERGE_EMPTY|
+                                COPY_REFLINK|
+                                COPY_SAME_MOUNT|
+                                COPY_HARDLINKS|
+                                COPY_ALL_XATTRS|
+                                (FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0)|
+                                (FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGTERM) ? COPY_SIGTERM : 0),
+                                progress_path,
+                                progress_bytes,
+                                userdata);
                 if (r < 0)
                         goto fallback_fail;
 
index 7b18f57719aa5cd660fed09a5d07c64febd44653..b67a4c10fe83424f44389fe1774267a778b3bcd6 100644 (file)
@@ -35,6 +35,7 @@ typedef enum BtrfsSnapshotFlags {
         BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 1 << 4, /* If the destination doesn't support subvolumes, reflink/copy instead */
         BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 1 << 5, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */
         BTRFS_SNAPSHOT_SIGINT             = 1 << 6, /* Check for SIGINT regularly, and return EINTR if seen */
+        BTRFS_SNAPSHOT_SIGTERM            = 1 << 7, /* Ditto, but for SIGTERM */
 } BtrfsSnapshotFlags;
 
 typedef enum BtrfsRemoveFlags {
index 1ff1e7600d76ed7beed344a6c9c3ad4bef531241..669d0df0589b319444cdbbf5835c765fa1d6fbce 100644 (file)
@@ -1384,7 +1384,8 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
 
         if (STR_IN_SET(field, "RestrictAddressFamilies",
                               "SystemCallFilter",
-                              "SystemCallLog")) {
+                              "SystemCallLog",
+                              "RestrictNetworkInterfaces")) {
                 int allow_list = 1;
                 const char *p = eq;
 
index 64ca67993c00e714305b63fb3e32fba3bccea02e..942a59be88cac2d17a446287b81ff40a10ff8e77 100644 (file)
@@ -291,7 +291,7 @@ int bus_connect_transport(
                 break;
 
         default:
-                assert_not_reached("Hmm, unknown transport type.");
+                assert_not_reached();
         }
         if (r < 0)
                 return r;
@@ -337,7 +337,7 @@ int bus_connect_transport_systemd(BusTransport transport, const char *host, bool
                 break;
 
         default:
-                assert_not_reached("Hmm, unknown transport type.");
+                assert_not_reached();
         }
 
         return r;
index 4563729b7c9c03023ff6eb3cf8d6d8e42b7c5293..50848715e34621b8282b13dbfd26fb1162e64e8e 100644 (file)
@@ -21,7 +21,6 @@ static int chown_one(
                 gid_t gid,
                 mode_t mask) {
 
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
         const char *n;
         int r;
 
@@ -30,11 +29,10 @@ static int chown_one(
 
         /* We change ACLs through the /proc/self/fd/%i path, so that we have a stable reference that works
          * with O_PATH. */
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
 
         /* Drop any ACL if there is one */
         FOREACH_STRING(n, "system.posix_acl_access", "system.posix_acl_default")
-                if (removexattr(procfs_path, n) < 0)
+                if (removexattr(FORMAT_PROC_FD_PATH(fd), n) < 0)
                         if (!IN_SET(errno, ENODATA, EOPNOTSUPP, ENOSYS, ENOTTY))
                                 return -errno;
 
index b446daf5819731c02af0432fce2ac58b22a13e17..7c1c48d0690030db1b154cbb31540ace8da39ac1 100644 (file)
@@ -139,10 +139,15 @@ int clock_reset_timewarp(void) {
 
 #define EPOCH_FILE "/usr/lib/clock-epoch"
 
-int clock_apply_epoch(void) {
+int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) {
         struct stat st;
         struct timespec ts;
-        usec_t epoch_usec;
+        usec_t epoch_usec, now_usec;
+
+        /* NB: we update *ret_attempted_change in *all* cases, both
+         * on success and failure, to indicate what we intended to do! */
+
+        assert(ret_attempted_change);
 
         if (stat(EPOCH_FILE, &st) < 0) {
                 if (errno != ENOENT)
@@ -152,8 +157,15 @@ int clock_apply_epoch(void) {
         } else
                 epoch_usec = timespec_load(&st.st_mtim);
 
-        if (now(CLOCK_REALTIME) >= epoch_usec)
+        now_usec = now(CLOCK_REALTIME);
+        if (now_usec < epoch_usec)
+                *ret_attempted_change = CLOCK_CHANGE_FORWARD;
+        else if (now_usec > usec_add(epoch_usec, CLOCK_VALID_RANGE_USEC_MAX))
+                *ret_attempted_change = CLOCK_CHANGE_BACKWARD;
+        else {
+                *ret_attempted_change = CLOCK_CHANGE_NOOP;
                 return 0;
+        }
 
         if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, epoch_usec)) < 0)
                 return -errno;
index 9e96d50d963c0b2e55ecb588c42762fd0767ab4e..c8f6d1b1f1e6089fb076dde37fa102a6dce30b50 100644 (file)
@@ -1,11 +1,20 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <errno.h>
 #include <time.h>
 
+typedef enum ClockChangeDirection {
+        CLOCK_CHANGE_NOOP,
+        CLOCK_CHANGE_FORWARD,
+        CLOCK_CHANGE_BACKWARD,
+        _CLOCK_CHANGE_MAX,
+        _CLOCK_CHANGE_INVALID = -EINVAL,
+} ClockChangeDirection;
+
 int clock_is_localtime(const char* adjtime_path);
 int clock_set_timezone(int *ret_minutesdelta);
 int clock_reset_timewarp(void);
 int clock_get_hwclock(struct tm *tm);
 int clock_set_hwclock(const struct tm *tm);
-int clock_apply_epoch(void);
+int clock_apply_epoch(ClockChangeDirection *ret_attempted_change);
index ec9d57b292a8da92273dc0d62a626bf7ce8ec302..1d28f7ba5795d4b819857e7cdeea8b87b1e34a8f 100644 (file)
@@ -202,7 +202,7 @@ static bool test_order(int k, OrderOperator p) {
                 return k > 0;
 
         default:
-                assert_not_reached("unknown order");
+                assert_not_reached();
 
         }
 }
index 65d75a15d54a514355d6220b65e4e49c71bd6aff..f27ce252ee4e5ef0bda547f1772b32e081c3df68 100644 (file)
@@ -88,6 +88,23 @@ static int fd_is_nonblock_pipe(int fd) {
         return FLAGS_SET(flags, O_NONBLOCK) ? FD_IS_NONBLOCKING_PIPE : FD_IS_BLOCKING_PIPE;
 }
 
+static int look_for_signals(CopyFlags copy_flags) {
+        int r;
+
+        if ((copy_flags & (COPY_SIGINT|COPY_SIGTERM)) == 0)
+                return 0;
+
+        r = pop_pending_signal(copy_flags & COPY_SIGINT ? SIGINT : 0,
+                               copy_flags & COPY_SIGTERM ? SIGTERM : 0);
+        if (r < 0)
+                return r;
+        if (r != 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINTR),
+                                       "Got %s, cancelling copy operation.", signal_to_string(r));
+
+        return 0;
+}
+
 int copy_bytes_full(
                 int fdf, int fdt,
                 uint64_t max_bytes,
@@ -176,13 +193,9 @@ int copy_bytes_full(
                 if (max_bytes <= 0)
                         return 1; /* return > 0 if we hit the max_bytes limit */
 
-                if (FLAGS_SET(copy_flags, COPY_SIGINT)) {
-                        r = pop_pending_signal(SIGINT);
-                        if (r < 0)
-                                return r;
-                        if (r > 0)
-                                return -EINTR;
-                }
+                r = look_for_signals(copy_flags);
+                if (r < 0)
+                        return r;
 
                 if (max_bytes != UINT64_MAX && m > max_bytes)
                         m = max_bytes;
@@ -627,10 +640,8 @@ static int fd_copy_regular(
                 return -errno;
 
         r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress, userdata);
-        if (r < 0) {
-                (void) unlinkat(dt, to, 0);
-                return r;
-        }
+        if (r < 0)
+                goto fail;
 
         if (fchown(fdt,
                    uid_is_valid(override_uid) ? override_uid : st->st_uid,
@@ -641,18 +652,27 @@ static int fd_copy_regular(
                 r = -errno;
 
         (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
-        (void) copy_xattr(fdf, fdt);
+        (void) copy_xattr(fdf, fdt, copy_flags);
 
-        q = close(fdt);
-        fdt = -1;
+        if (copy_flags & COPY_FSYNC) {
+                if (fsync(fdt) < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+        }
 
+        q = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
         if (q < 0) {
-                r = -errno;
-                (void) unlinkat(dt, to, 0);
+                r = q;
+                goto fail;
         }
 
         (void) memorize_hardlink(hardlink_context, st, dt, to);
         return r;
+
+fail:
+        (void) unlinkat(dt, to, 0);
+        return r;
 }
 
 static int fd_copy_fifo(
@@ -845,13 +865,9 @@ static int fd_copy_directory(
                 if (dot_or_dot_dot(de->d_name))
                         continue;
 
-                if (FLAGS_SET(copy_flags, COPY_SIGINT)) {
-                        r = pop_pending_signal(SIGINT);
-                        if (r < 0)
-                                return r;
-                        if (r > 0)
-                                return -EINTR;
-                }
+                r = look_for_signals(copy_flags);
+                if (r < 0)
+                        return r;
 
                 if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
                         r = -errno;
@@ -912,7 +928,7 @@ static int fd_copy_directory(
                 else
                         q = -EOPNOTSUPP;
 
-                if (q == -EINTR) /* Propagate SIGINT up instantly */
+                if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */
                         return q;
                 if (q == -EEXIST && (copy_flags & COPY_MERGE))
                         q = 0;
@@ -929,10 +945,15 @@ static int fd_copy_directory(
                 if (fchmod(fdt, st->st_mode & 07777) < 0)
                         r = -errno;
 
-                (void) copy_xattr(dirfd(d), fdt);
+                (void) copy_xattr(dirfd(d), fdt, copy_flags);
                 (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
         }
 
+        if (copy_flags & COPY_FSYNC_FULL) {
+                if (fsync(fdt) < 0)
+                        return -errno;
+        }
+
         return r;
 }
 
@@ -949,6 +970,7 @@ int copy_tree_at_full(
                 void *userdata) {
 
         struct stat st;
+        int r;
 
         assert(from);
         assert(to);
@@ -957,17 +979,47 @@ int copy_tree_at_full(
                 return -errno;
 
         if (S_ISREG(st.st_mode))
-                return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL, progress_bytes, userdata);
+                r = fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL, progress_bytes, userdata);
         else if (S_ISDIR(st.st_mode))
-                return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+                r = fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
         else if (S_ISLNK(st.st_mode))
-                return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
+                r = fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
         else if (S_ISFIFO(st.st_mode))
-                return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
+                r = fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
         else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
-                return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
+                r = fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
         else
                 return -EOPNOTSUPP;
+        if (r < 0)
+                return r;
+
+        if (S_ISDIR(st.st_mode) && (copy_flags & COPY_SYNCFS)) {
+                /* If the top-level inode is a directory run syncfs() now. */
+                r = syncfs_path(fdt, to);
+                if (r < 0)
+                        return r;
+        } else if ((copy_flags & (COPY_FSYNC_FULL|COPY_SYNCFS)) != 0) {
+                /* fsync() the parent dir of what we just copied if COPY_FSYNC_FULL is set. Also do this in
+                 * case COPY_SYNCFS is set but the top-level inode wasn't actually a directory. We do this so that
+                 * COPY_SYNCFS provides reasonable synchronization semantics on any kind of inode: when the
+                 * copy operation is done the whole inode — regardless of its type — and all its children
+                 * will be synchronized to disk. */
+                r = fsync_parent_at(fdt, to);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int sync_dir_by_flags(const char *path, CopyFlags copy_flags) {
+
+        if (copy_flags & COPY_SYNCFS)
+                return syncfs_path(AT_FDCWD, path);
+        if (copy_flags & COPY_FSYNC_FULL)
+                return fsync_parent_at(AT_FDCWD, path);
+
+        return 0;
 }
 
 int copy_directory_fd_full(
@@ -991,7 +1043,26 @@ int copy_directory_fd_full(
         if (r < 0)
                 return r;
 
-        return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+        r = fd_copy_directory(
+                        dirfd, NULL,
+                        &st,
+                        AT_FDCWD, to,
+                        st.st_dev,
+                        COPY_DEPTH_MAX,
+                        UID_INVALID, GID_INVALID,
+                        copy_flags,
+                        NULL, NULL,
+                        progress_path,
+                        progress_bytes,
+                        userdata);
+        if (r < 0)
+                return r;
+
+        r = sync_dir_by_flags(to, copy_flags);
+        if (r < 0)
+                return r;
+
+        return 0;
 }
 
 int copy_directory_full(
@@ -1015,7 +1086,26 @@ int copy_directory_full(
         if (r < 0)
                 return r;
 
-        return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, COPY_DEPTH_MAX, UID_INVALID, GID_INVALID, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
+        r = fd_copy_directory(
+                        AT_FDCWD, from,
+                        &st,
+                        AT_FDCWD, to,
+                        st.st_dev,
+                        COPY_DEPTH_MAX,
+                        UID_INVALID, GID_INVALID,
+                        copy_flags,
+                        NULL, NULL,
+                        progress_path,
+                        progress_bytes,
+                        userdata);
+        if (r < 0)
+                return r;
+
+        r = sync_dir_by_flags(to, copy_flags);
+        if (r < 0)
+                return r;
+
+        return 0;
 }
 
 int copy_file_fd_full(
@@ -1026,6 +1116,7 @@ int copy_file_fd_full(
                 void *userdata) {
 
         _cleanup_close_ int fdf = -1;
+        struct stat st;
         int r;
 
         assert(from);
@@ -1035,12 +1126,32 @@ int copy_file_fd_full(
         if (fdf < 0)
                 return -errno;
 
+        r = fd_verify_regular(fdf);
+        if (r < 0)
+                return r;
+
+        if (fstat(fdt, &st) < 0)
+                return -errno;
+
         r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata);
+        if (r < 0)
+                return r;
 
-        (void) copy_times(fdf, fdt, copy_flags);
-        (void) copy_xattr(fdf, fdt);
+        if (S_ISREG(fdt)) {
+                (void) copy_times(fdf, fdt, copy_flags);
+                (void) copy_xattr(fdf, fdt, copy_flags);
+        }
 
-        return r;
+        if (copy_flags & COPY_FSYNC_FULL) {
+                r = fsync_full(fdt);
+                if (r < 0)
+                        return r;
+        } else if (copy_flags & COPY_FSYNC) {
+                if (fsync(fdt) < 0)
+                        return -errno;
+        }
+
+        return 0;
 }
 
 int copy_file_full(
@@ -1054,9 +1165,9 @@ int copy_file_full(
                 copy_progress_bytes_t progress_bytes,
                 void *userdata) {
 
-        _cleanup_close_ int fdf = -1;
+        _cleanup_close_ int fdf = -1, fdt = -1;
         struct stat st;
-        int r, fdt = -1;  /* avoid false maybe-uninitialized warning */
+        int r;
 
         assert(from);
         assert(to);
@@ -1065,9 +1176,12 @@ int copy_file_full(
         if (fdf < 0)
                 return -errno;
 
-        if (mode == MODE_INVALID)
-                if (fstat(fdf, &st) < 0)
-                        return -errno;
+        if (fstat(fdf, &st) < 0)
+                return -errno;
+
+        r = stat_verify_regular(&st);
+        if (r < 0)
+                return r;
 
         RUN_WITH_UMASK(0000) {
                 if (copy_flags & COPY_MAC_CREATE) {
@@ -1083,28 +1197,50 @@ int copy_file_full(
                         return -errno;
         }
 
+        if (!FLAGS_SET(flags, O_EXCL)) { /* if O_EXCL was used we created the thing as regular file, no need to check again */
+                r = fd_verify_regular(fdt);
+                if (r < 0)
+                        goto fail;
+        }
+
         if (chattr_mask != 0)
                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL);
 
         r = copy_bytes_full(fdf, fdt, UINT64_MAX, copy_flags, NULL, NULL, progress_bytes, userdata);
-        if (r < 0) {
-                close(fdt);
-                (void) unlink(to);
-                return r;
-        }
+        if (r < 0)
+                goto fail;
 
         (void) copy_times(fdf, fdt, copy_flags);
-        (void) copy_xattr(fdf, fdt);
+        (void) copy_xattr(fdf, fdt, copy_flags);
 
         if (chattr_mask != 0)
                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
 
-        if (close(fdt) < 0) {
-                unlink_noerrno(to);
-                return -errno;
+        if (copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL)) {
+                if (fsync(fdt) < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+        }
+
+        r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
+        if (r < 0)
+                goto fail;
+
+        if (copy_flags & COPY_FSYNC_FULL) {
+                r = fsync_parent_at(AT_FDCWD, to);
+                if (r < 0)
+                        goto fail;
         }
 
         return 0;
+
+fail:
+        /* Only unlink if we definitely are the ones who created the file */
+        if (FLAGS_SET(flags, O_EXCL))
+                (void) unlink(to);
+
+        return r;
 }
 
 int copy_file_atomic_full(
@@ -1172,6 +1308,12 @@ int copy_file_atomic_full(
         if (fchmod(fdt, mode) < 0)
                 return -errno;
 
+        if ((copy_flags & (COPY_FSYNC|COPY_FSYNC_FULL))) {
+                /* Sync the file */
+                if (fsync(fdt) < 0)
+                        return -errno;
+        }
+
         if (copy_flags & COPY_REPLACE) {
                 if (renameat(AT_FDCWD, t, AT_FDCWD, to) < 0)
                         return -errno;
@@ -1181,11 +1323,27 @@ int copy_file_atomic_full(
                         return r;
         }
 
+        t = mfree(t);
+
         if (chattr_mask != 0)
                 (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
 
-        t = mfree(t);
+        r = close_nointr(TAKE_FD(fdt)); /* even if this fails, the fd is now invalidated */
+        if (r < 0)
+                goto fail;
+
+        if (copy_flags & COPY_FSYNC_FULL) {
+                /* Sync the parent directory */
+                r = fsync_parent_at(AT_FDCWD, to);
+                if (r < 0)
+                        goto fail;
+        }
+
         return 0;
+
+fail:
+        (void) unlink(to);
+        return r;
 }
 
 int copy_times(int fdf, int fdt, CopyFlags flags) {
@@ -1241,7 +1399,7 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht) {
         return fchmod_and_chown_with_fallback(fdt, patht, st.st_mode & 07777, st.st_uid, st.st_gid);
 }
 
-int copy_xattr(int fdf, int fdt) {
+int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) {
         _cleanup_free_ char *names = NULL;
         int ret = 0, r;
         const char *p;
@@ -1253,7 +1411,7 @@ int copy_xattr(int fdf, int fdt) {
         NULSTR_FOREACH(p, names) {
                 _cleanup_free_ char *value = NULL;
 
-                if (!startswith(p, "user."))
+                if (!(copy_flags & COPY_ALL_XATTRS) && !startswith(p, "user."))
                         continue;
 
                 r = fgetxattr_malloc(fdf, p, &value);
index b36ddfcb0157bfd8ff7482be8834afe987deecac..a7b45b4fbf4369135670f125fe7daa7a554945d2 100644 (file)
 #include <sys/types.h>
 
 typedef enum CopyFlags {
-        COPY_REFLINK     = 1 << 0, /* Try to reflink */
-        COPY_MERGE       = 1 << 1, /* Merge existing trees with our new one to copy */
-        COPY_REPLACE     = 1 << 2, /* Replace an existing file if there's one */
-        COPY_SAME_MOUNT  = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */
-        COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
-        COPY_CRTIME      = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
-        COPY_SIGINT      = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
-        COPY_MAC_CREATE  = 1 << 7, /* Create files with the correct MAC label (currently SELinux only) */
-        COPY_HARDLINKS   = 1 << 8, /* Try to reproduce hard links */
+        COPY_REFLINK     = 1 << 0,  /* Try to reflink */
+        COPY_MERGE       = 1 << 1,  /* Merge existing trees with our new one to copy */
+        COPY_REPLACE     = 1 << 2,  /* Replace an existing file if there's one */
+        COPY_SAME_MOUNT  = 1 << 3,  /* Don't descend recursively into other file systems, across mount point boundaries */
+        COPY_MERGE_EMPTY = 1 << 4,  /* Merge an existing, empty directory with our new tree to copy */
+        COPY_CRTIME      = 1 << 5,  /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
+        COPY_SIGINT      = 1 << 6,  /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
+        COPY_SIGTERM     = 1 << 7,  /* ditto, but for SIGTERM */
+        COPY_MAC_CREATE  = 1 << 8,  /* Create files with the correct MAC label (currently SELinux only) */
+        COPY_HARDLINKS   = 1 << 9,  /* Try to reproduce hard links */
+        COPY_FSYNC       = 1 << 10, /* fsync() after we are done */
+        COPY_FSYNC_FULL  = 1 << 11, /* fsync_full() after we are done */
+        COPY_SYNCFS      = 1 << 12, /* syncfs() the *top-level* dir after we are done */
+        COPY_ALL_XATTRS  = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
 } CopyFlags;
 
 typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
@@ -68,4 +73,4 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht);
 static inline int copy_rights(int fdf, int fdt) {
         return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */
 }
-int copy_xattr(int fdf, int fdt);
+int copy_xattr(int fdf, int fdt, CopyFlags copy_flags);
index 6c2d9dbc76ca59300208f534c1ebd9b49b744d34..9980cd93a92b47b83abab0b4e559b80e93d2c92c 100644 (file)
@@ -299,6 +299,8 @@ int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *
                         if (ret) {
                                 void *copy;
 
+                                assert(sz <= sizeof(f->data)); /* Ensure we don't read past f->data bounds */
+
                                 copy = memdup(f->data, sz);
                                 if (!copy)
                                         return -ENOMEM;
@@ -369,7 +371,10 @@ struct _packed_ encrypted_credential_header {
 };
 
 struct _packed_ tpm2_credential_header {
-        le64_t pcr_mask;
+        le64_t pcr_mask; /* Note that the spec for PC Clients only mandates 24 PCRs, and that's what systems
+                          * generally have. But keep the door open for more. */
+        le16_t pcr_bank; /* For now, either TPM2_ALG_SHA256 or TPM2_ALG_SHA1 */
+        le16_t _zero;    /* Filler to maintain 32bit alignment */
         le32_t blob_size;
         le32_t policy_hash_size;
         uint8_t policy_hash_and_blob[];
@@ -439,6 +444,7 @@ int encrypt_credential_and_warn(
         struct encrypted_credential_header *h;
         int ksz, bsz, ivsz, tsz, added, r;
         uint8_t md[SHA256_DIGEST_LENGTH];
+        uint16_t tpm2_pcr_bank = 0;
         const EVP_CIPHER *cc;
 #if HAVE_TPM2
         bool try_tpm2 = false;
@@ -505,7 +511,8 @@ int encrypt_credential_and_warn(
                               &tpm2_blob,
                               &tpm2_blob_size,
                               &tpm2_policy_hash,
-                              &tpm2_policy_hash_size);
+                              &tpm2_policy_hash_size,
+                              &tpm2_pcr_bank);
                 if (r < 0) {
                         if (!sd_id128_is_null(with_key))
                                 return r;
@@ -598,6 +605,7 @@ int encrypt_credential_and_warn(
 
                 t = (struct tpm2_credential_header*) ((uint8_t*) output + p);
                 t->pcr_mask = htole64(tpm2_pcr_mask);
+                t->pcr_bank = htole16(tpm2_pcr_bank);
                 t->blob_size = htole32(tpm2_blob_size);
                 t->policy_hash_size = htole32(tpm2_policy_hash_size);
                 memcpy(t->policy_hash_and_blob, tpm2_blob, tpm2_blob_size);
@@ -659,11 +667,11 @@ int encrypt_credential_and_warn(
         p += tsz;
         assert(p <= output_size);
 
-        if (DEBUG_LOGGING) {
+        if (DEBUG_LOGGING && input_size > 0) {
                 size_t base64_size;
 
                 base64_size = DIV_ROUND_UP(p * 4, 3); /* Include base64 size increase in debug output */
-
+                assert(base64_size >= input_size);
                 log_debug("Input of %zu bytes grew to output of %zu bytes (+%2zu%%).", input_size, base64_size, base64_size * 100 / input_size - 100);
         }
 
@@ -739,6 +747,10 @@ int decrypt_credential_and_warn(
 
                 if (le64toh(t->pcr_mask) >= (UINT64_C(1) << TPM2_PCRS_MAX))
                         return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR mask out of range.");
+                if (!tpm2_pcr_bank_supported(le16toh(t->pcr_bank)))
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR bank invalid or not supported");
+                if (le16toh(t->_zero) != 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 padding space not zero.");
                 if (le32toh(t->blob_size) > CREDENTIAL_FIELD_SIZE_MAX)
                         return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected TPM2 blob size.");
                 if (le32toh(t->policy_hash_size) > CREDENTIAL_FIELD_SIZE_MAX)
@@ -755,6 +767,7 @@ int decrypt_credential_and_warn(
 
                 r = tpm2_unseal(tpm2_device,
                                 le64toh(t->pcr_mask),
+                                le16toh(t->pcr_bank),
                                 t->policy_hash_and_blob,
                                 le32toh(t->blob_size),
                                 t->policy_hash_and_blob + le32toh(t->blob_size),
index 521264ec29b3b194a8f636fe76157004746b6c5e..5f8bf4377640dfd4af396347bb9265bbba132159 100644 (file)
@@ -305,7 +305,7 @@ static int image_make(
                 }
 
                 /* Get directory creation time (not available everywhere, but that's OK */
-                (void) fd_getcrtime(dfd, &crtime);
+                (void) fd_getcrtime(fd, &crtime);
 
                 /* If the IMMUTABLE bit is set, we consider the directory read-only. Since the ioctl is not
                  * supported everywhere we ignore failures. */
index 27b9ac9569e7674e094b2cbcffc8fc85e15219b3..32b21eb71b0bbf9a65e3c7eecac3d05bf43cf28c 100644 (file)
@@ -2538,13 +2538,13 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
                 _META_MAX,
         };
 
-        static const char *paths[_META_MAX] = {
+        static const char *const paths[_META_MAX] = {
                 [META_HOSTNAME]          = "/etc/hostname\0",
                 [META_MACHINE_ID]        = "/etc/machine-id\0",
                 [META_MACHINE_INFO]      = "/etc/machine-info\0",
-                [META_OS_RELEASE]        = "/etc/os-release\0"
-                                           "/usr/lib/os-release\0",
-                [META_EXTENSION_RELEASE] = NULL,
+                [META_OS_RELEASE]        = ("/etc/os-release\0"
+                                           "/usr/lib/os-release\0"),
+                [META_EXTENSION_RELEASE] = "extension-release\0", /* Used only for logging. */
         };
 
         _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **extension_release = NULL;
@@ -2561,17 +2561,6 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
 
         assert(m);
 
-        /* As per the os-release spec, if the image is an extension it will have a file
-         * named after the image name in extension-release.d/ */
-        if (m->image_name) {
-                char *ext;
-
-                ext = strjoina("/usr/lib/extension-release.d/extension-release.", m->image_name, "0");
-                ext[strlen(ext) - 1] = '\0'; /* Extra \0 for NULSTR_FOREACH using placeholder from above */
-                paths[META_EXTENSION_RELEASE] = ext;
-        } else
-                log_debug("No image name available, will skip extension-release metadata");
-
         for (; n_meta_initialized < _META_MAX; n_meta_initialized ++) {
                 if (!paths[n_meta_initialized]) {
                         fds[2*n_meta_initialized] = fds[2*n_meta_initialized+1] = -1;
@@ -2625,11 +2614,25 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
 
                         fds[2*k] = safe_close(fds[2*k]);
 
-                        NULSTR_FOREACH(p, paths[k]) {
-                                fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
-                                if (fd >= 0)
-                                        break;
-                        }
+                        if (k == META_EXTENSION_RELEASE) {
+                                /* As per the os-release spec, if the image is an extension it will have a file
+                                 * named after the image name in extension-release.d/ - we use the image name
+                                 * and try to resolve it with the extension-release helpers, as sometimes
+                                 * the image names are mangled on deployment and do not match anymore.
+                                 * Unlike other paths this is not fixed, and the image name
+                                 * can be mangled on deployment, so by calling into the helper
+                                 * we allow a fallback that matches on the first extension-release
+                                 * file found in the directory, if one named after the image cannot
+                                 * be found first. */
+                                r = open_extension_release(t, m->image_name, NULL, &fd);
+                                if (r < 0)
+                                        fd = r; /* Propagate the error. */
+                        } else
+                                NULSTR_FOREACH(p, paths[k]) {
+                                        fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
+                                        if (fd >= 0)
+                                                break;
+                                }
                         if (fd < 0) {
                                 log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
                                 fds[2*k+1] = safe_close(fds[2*k+1]);
index f77f6943ca4faf643d3cfcfff4e3feb4fda86298..c47d819f059683c75dc7bdf32bb6c3663cf90133 100644 (file)
@@ -14,6 +14,7 @@
 #include "memory-util.h"
 #include "socket-util.h"
 #include "string-table.h"
+#include "strv.h"
 #include "strxcpyx.h"
 
 static const char* const duplex_table[_DUP_MAX] = {
@@ -69,13 +70,14 @@ DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
 
 static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
-        [NET_DEV_FEAT_RX]   = "rx-checksum",
-        [NET_DEV_FEAT_TX]   = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
-        [NET_DEV_FEAT_GSO]  = "tx-generic-segmentation",
-        [NET_DEV_FEAT_GRO]  = "rx-gro",
-        [NET_DEV_FEAT_LRO]  = "rx-lro",
-        [NET_DEV_FEAT_TSO]  = "tx-tcp-segmentation",
-        [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation",
+        [NET_DEV_FEAT_RX]     = "rx-checksum",
+        [NET_DEV_FEAT_TX]     = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
+        [NET_DEV_FEAT_GSO]    = "tx-generic-segmentation",
+        [NET_DEV_FEAT_GRO]    = "rx-gro",
+        [NET_DEV_FEAT_GRO_HW] = "rx-gro-hw",
+        [NET_DEV_FEAT_LRO]    = "rx-lro",
+        [NET_DEV_FEAT_TSO]    = "tx-tcp-segmentation",
+        [NET_DEV_FEAT_TSO6]   = "tx-tcp6-segmentation",
 };
 
 static const char* const ethtool_link_mode_bit_table[] = {
@@ -329,6 +331,17 @@ int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct et
                 dest = _v;                             \
         } while(false)
 
+#define UPDATE_WITH_MAX(dest, max, val, updated)       \
+        do {                                           \
+                typeof(dest) _v = (val);               \
+                typeof(dest) _max = (max);             \
+                if (_v == 0 || _v > _max)              \
+                        _v = _max;                     \
+                if (dest != _v)                        \
+                        updated = true;                \
+                dest = _v;                             \
+        } while(false)
+
 int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
         struct ethtool_wolinfo ecmd = {
                 .cmd = ETHTOOL_GWOL,
@@ -382,10 +395,10 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
         assert(ifname);
         assert(ring);
 
-        if (!ring->rx_pending_set &&
-            !ring->rx_mini_pending_set &&
-            !ring->rx_jumbo_pending_set &&
-            !ring->tx_pending_set)
+        if (!ring->rx.set &&
+            !ring->rx_mini.set &&
+            !ring->rx_jumbo.set &&
+            !ring->tx.set)
                 return 0;
 
         r = ethtool_connect(ethtool_fd);
@@ -398,17 +411,17 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netde
         if (r < 0)
                 return -errno;
 
-        if (ring->rx_pending_set)
-                UPDATE(ecmd.rx_pending, ring->rx_pending, need_update);
+        if (ring->rx.set)
+                UPDATE_WITH_MAX(ecmd.rx_pending, ecmd.rx_max_pending, ring->rx.value, need_update);
 
-        if (ring->rx_mini_pending_set)
-                UPDATE(ecmd.rx_mini_pending, ring->rx_mini_pending, need_update);
+        if (ring->rx_mini.set)
+                UPDATE_WITH_MAX(ecmd.rx_mini_pending, ecmd.rx_mini_max_pending, ring->rx_mini.value, need_update);
 
-        if (ring->rx_jumbo_pending_set)
-                UPDATE(ecmd.rx_jumbo_pending, ring->rx_jumbo_pending, need_update);
+        if (ring->rx_jumbo.set)
+                UPDATE_WITH_MAX(ecmd.rx_jumbo_pending, ecmd.rx_jumbo_max_pending, ring->rx_jumbo.value, need_update);
 
-        if (ring->tx_pending_set)
-                UPDATE(ecmd.tx_pending, ring->tx_pending, need_update);
+        if (ring->tx.set)
+                UPDATE_WITH_MAX(ecmd.tx_pending, ecmd.tx_max_pending, ring->tx.value, need_update);
 
         if (!need_update)
                 return 0;
@@ -501,16 +514,26 @@ static int set_features_bit(
         return found ? 0 : -ENODATA;
 }
 
-int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features) {
+int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) {
         _cleanup_free_ struct ethtool_gstrings *strings = NULL;
         struct ethtool_sfeatures *sfeatures;
         struct ifreq ifr = {};
-        int i, r;
+        bool have = false;
+        int r;
 
         assert(ethtool_fd);
         assert(ifname);
         assert(features);
 
+        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
+                if (features[i] >= 0) {
+                        have = true;
+                        break;
+                }
+
+        if (!have)
+                return 0;
+
         r = ethtool_connect(ethtool_fd);
         if (r < 0)
                 return r;
@@ -525,8 +548,8 @@ int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *feature
         sfeatures->cmd = ETHTOOL_SFEATURES;
         sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
 
-        for (i = 0; i < _NET_DEV_FEAT_MAX; i++)
-                if (features[i] != -1) {
+        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
+                if (features[i] >= 0) {
                         r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
                         if (r < 0) {
                                 log_debug_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
@@ -824,10 +847,10 @@ int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *cha
         assert(ifname);
         assert(channels);
 
-        if (!channels->rx_count_set &&
-            !channels->tx_count_set &&
-            !channels->other_count_set &&
-            !channels->combined_count_set)
+        if (!channels->rx.set &&
+            !channels->tx.set &&
+            !channels->other.set &&
+            !channels->combined.set)
                 return 0;
 
         r = ethtool_connect(fd);
@@ -840,17 +863,17 @@ int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *cha
         if (r < 0)
                 return -errno;
 
-        if (channels->rx_count_set)
-                UPDATE(ecmd.rx_count, channels->rx_count, need_update);
+        if (channels->rx.set)
+                UPDATE_WITH_MAX(ecmd.rx_count, ecmd.max_rx, channels->rx.value, need_update);
 
-        if (channels->tx_count_set)
-                UPDATE(ecmd.tx_count, channels->tx_count, need_update);
+        if (channels->tx.set)
+                UPDATE_WITH_MAX(ecmd.tx_count, ecmd.max_tx, channels->tx.value, need_update);
 
-        if (channels->other_count_set)
-                UPDATE(ecmd.other_count, channels->other_count, need_update);
+        if (channels->other.set)
+                UPDATE_WITH_MAX(ecmd.other_count, ecmd.max_other, channels->other.value, need_update);
 
-        if (channels->combined_count_set)
-                UPDATE(ecmd.combined_count, channels->combined_count, need_update);
+        if (channels->combined.set)
+                UPDATE_WITH_MAX(ecmd.combined_count, ecmd.max_combined, channels->combined.value, need_update);
 
         if (!need_update)
                 return 0;
@@ -909,57 +932,6 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
         return 0;
 }
 
-int config_parse_channel(
-                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) {
-
-        netdev_channels *channels = data;
-        uint32_t k;
-        int r;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = safe_atou32(rvalue, &k);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse channel value for %s=, ignoring: %s", lvalue, rvalue);
-                return 0;
-        }
-        if (k < 1) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Invalid %s= value, ignoring: %s", lvalue, rvalue);
-                return 0;
-        }
-
-        if (streq(lvalue, "RxChannels")) {
-                channels->rx_count = k;
-                channels->rx_count_set = true;
-        } else if (streq(lvalue, "TxChannels")) {
-                channels->tx_count = k;
-                channels->tx_count_set = true;
-        } else if (streq(lvalue, "OtherChannels")) {
-                channels->other_count = k;
-                channels->other_count_set = true;
-        } else if (streq(lvalue, "CombinedChannels")) {
-                channels->combined_count = k;
-                channels->combined_count_set = true;
-        }
-
-        return 0;
-}
-
 int config_parse_advertise(
                 const char *unit,
                 const char *filename,
@@ -1015,7 +987,7 @@ int config_parse_advertise(
         }
 }
 
-int config_parse_nic_buffer_size(
+int config_parse_ring_buffer_or_channel(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -1027,7 +999,7 @@ int config_parse_nic_buffer_size(
                 void *data,
                 void *userdata) {
 
-        netdev_ring_param *ring = data;
+        u32_opt *dst = data;
         uint32_t k;
         int r;
 
@@ -1037,10 +1009,22 @@ int config_parse_nic_buffer_size(
         assert(rvalue);
         assert(data);
 
+        if (isempty(rvalue)) {
+                dst->value = 0;
+                dst->set = false;
+                return 0;
+        }
+
+        if (streq(rvalue, "max")) {
+                dst->value = 0;
+                dst->set = true;
+                return 0;
+        }
+
         r = safe_atou32(rvalue, &k);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse interface buffer value, ignoring: %s", rvalue);
+                           "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
                 return 0;
         }
         if (k < 1) {
@@ -1049,20 +1033,8 @@ int config_parse_nic_buffer_size(
                 return 0;
         }
 
-        if (streq(lvalue, "RxBufferSize")) {
-                ring->rx_pending = k;
-                ring->rx_pending_set = true;
-        } else if (streq(lvalue, "RxMiniBufferSize")) {
-                ring->rx_mini_pending = k;
-                ring->rx_mini_pending_set = true;
-        } else if (streq(lvalue, "RxJumboBufferSize")) {
-                ring->rx_jumbo_pending = k;
-                ring->rx_jumbo_pending_set = true;
-        } else if (streq(lvalue, "TxBufferSize")) {
-                ring->tx_pending = k;
-                ring->tx_pending_set = true;
-        }
-
+        dst->value = k;
+        dst->set = true;
         return 0;
 }
 
@@ -1131,3 +1103,207 @@ int config_parse_wol(
 
         return 0;
 }
+
+int config_parse_coalesce_u32(
+                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) {
+        u32_opt *dst = data;
+        uint32_t k;
+        int r;
+
+        if (isempty(rvalue)) {
+                dst->value = 0;
+                dst->set = false;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        dst->value = k;
+        dst->set = true;
+        return 0;
+}
+
+int config_parse_coalesce_sec(
+                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) {
+        u32_opt *dst = data;
+        usec_t usec;
+        int r;
+
+        if (isempty(rvalue)) {
+                dst->value = 0;
+                dst->set = false;
+                return 0;
+        }
+
+        r = parse_sec(rvalue, &usec);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse coalesce setting value, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (usec > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Too large %s= value, ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid %s= value, ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        dst->value = (uint32_t) usec;
+        dst->set = true;
+
+        return 0;
+}
+
+int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce) {
+        struct ethtool_coalesce ecmd = {
+                .cmd = ETHTOOL_GCOALESCE,
+        };
+        struct ifreq ifr = {
+                .ifr_data = (void*) &ecmd,
+        };
+        bool need_update = false;
+        int r;
+
+        assert(ethtool_fd);
+        assert(ifname);
+        assert(coalesce);
+
+        if (coalesce->use_adaptive_rx_coalesce < 0 &&
+            coalesce->use_adaptive_tx_coalesce < 0 &&
+            !coalesce->rx_coalesce_usecs.set &&
+            !coalesce->rx_max_coalesced_frames.set &&
+            !coalesce->rx_coalesce_usecs_irq.set &&
+            !coalesce->rx_max_coalesced_frames_irq.set &&
+            !coalesce->tx_coalesce_usecs.set &&
+            !coalesce->tx_max_coalesced_frames.set &&
+            !coalesce->tx_coalesce_usecs_irq.set &&
+            !coalesce->tx_max_coalesced_frames_irq.set &&
+            !coalesce->stats_block_coalesce_usecs.set &&
+            !coalesce->pkt_rate_low.set &&
+            !coalesce->rx_coalesce_usecs_low.set &&
+            !coalesce->rx_max_coalesced_frames_low.set &&
+            !coalesce->tx_coalesce_usecs_low.set &&
+            !coalesce->tx_max_coalesced_frames_low.set &&
+            !coalesce->pkt_rate_high.set &&
+            !coalesce->rx_coalesce_usecs_high.set &&
+            !coalesce->rx_max_coalesced_frames_high.set &&
+            !coalesce->tx_coalesce_usecs_high.set &&
+            !coalesce->tx_max_coalesced_frames_high.set &&
+            !coalesce->rate_sample_interval.set)
+                return 0;
+
+        r = ethtool_connect(ethtool_fd);
+        if (r < 0)
+                return r;
+
+        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+
+        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
+        if (r < 0)
+                return -errno;
+
+        if (coalesce->use_adaptive_rx_coalesce >= 0)
+                UPDATE(ecmd.use_adaptive_rx_coalesce, (uint32_t) coalesce->use_adaptive_rx_coalesce, need_update);
+
+        if (coalesce->use_adaptive_tx_coalesce >= 0)
+                UPDATE(ecmd.use_adaptive_tx_coalesce, (uint32_t) coalesce->use_adaptive_tx_coalesce, need_update);
+
+        if (coalesce->rx_coalesce_usecs.set)
+                UPDATE(ecmd.rx_coalesce_usecs, coalesce->rx_coalesce_usecs.value, need_update);
+
+        if (coalesce->rx_max_coalesced_frames.set)
+                UPDATE(ecmd.rx_max_coalesced_frames, coalesce->rx_max_coalesced_frames.value, need_update);
+
+        if (coalesce->rx_coalesce_usecs_irq.set)
+                UPDATE(ecmd.rx_coalesce_usecs_irq, coalesce->rx_coalesce_usecs_irq.value, need_update);
+
+        if (coalesce->rx_max_coalesced_frames_irq.set)
+                UPDATE(ecmd.rx_max_coalesced_frames_irq, coalesce->rx_max_coalesced_frames_irq.value, need_update);
+
+        if (coalesce->tx_coalesce_usecs.set)
+                UPDATE(ecmd.tx_coalesce_usecs, coalesce->tx_coalesce_usecs.value, need_update);
+
+        if (coalesce->tx_max_coalesced_frames.set)
+                UPDATE(ecmd.tx_max_coalesced_frames, coalesce->tx_max_coalesced_frames.value, need_update);
+
+        if (coalesce->tx_coalesce_usecs_irq.set)
+                UPDATE(ecmd.tx_coalesce_usecs_irq, coalesce->tx_coalesce_usecs_irq.value, need_update);
+
+        if (coalesce->tx_max_coalesced_frames_irq.set)
+                UPDATE(ecmd.tx_max_coalesced_frames_irq, coalesce->tx_max_coalesced_frames_irq.value, need_update);
+
+        if (coalesce->stats_block_coalesce_usecs.set)
+                UPDATE(ecmd.stats_block_coalesce_usecs, coalesce->stats_block_coalesce_usecs.value, need_update);
+
+        if (coalesce->pkt_rate_low.set)
+                UPDATE(ecmd.pkt_rate_low, coalesce->pkt_rate_low.value, need_update);
+
+        if (coalesce->rx_coalesce_usecs_low.set)
+                UPDATE(ecmd.rx_coalesce_usecs_low, coalesce->rx_coalesce_usecs_low.value, need_update);
+
+        if (coalesce->rx_max_coalesced_frames_low.set)
+                UPDATE(ecmd.rx_max_coalesced_frames_low, coalesce->rx_max_coalesced_frames_low.value, need_update);
+
+        if (coalesce->tx_coalesce_usecs_low.set)
+                UPDATE(ecmd.tx_coalesce_usecs_low, coalesce->tx_coalesce_usecs_low.value, need_update);
+
+        if (coalesce->tx_max_coalesced_frames_low.set)
+                UPDATE(ecmd.tx_max_coalesced_frames_low, coalesce->tx_max_coalesced_frames_low.value, need_update);
+
+        if (coalesce->pkt_rate_high.set)
+                UPDATE(ecmd.pkt_rate_high, coalesce->pkt_rate_high.value, need_update);
+
+        if (coalesce->rx_coalesce_usecs_high.set)
+                UPDATE(ecmd.rx_coalesce_usecs_high, coalesce->rx_coalesce_usecs_high.value, need_update);
+
+        if (coalesce->rx_max_coalesced_frames_high.set)
+                UPDATE(ecmd.rx_max_coalesced_frames_high, coalesce->rx_max_coalesced_frames_high.value, need_update);
+
+        if (coalesce->tx_coalesce_usecs_high.set)
+                UPDATE(ecmd.tx_coalesce_usecs_high, coalesce->tx_coalesce_usecs_high.value, need_update);
+
+        if (coalesce->tx_max_coalesced_frames_high.set)
+                UPDATE(ecmd.tx_max_coalesced_frames_high, coalesce->tx_max_coalesced_frames_high.value, need_update);
+
+        if (coalesce->rate_sample_interval.set)
+                UPDATE(ecmd.rate_sample_interval, DIV_ROUND_UP(coalesce->rate_sample_interval.value, USEC_PER_SEC), need_update);
+
+        if (!need_update)
+                return 0;
+
+        ecmd.cmd = ETHTOOL_SCOALESCE;
+        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
+        if (r < 0)
+                return -errno;
+
+        return 0;
+}
index 7d287666249ae55fc8ceccf400dfd5a6bf46ffcc..6e180995055b7df6abe6faa32b89c51c38b07d1b 100644 (file)
@@ -23,6 +23,7 @@ typedef enum NetDevFeature {
         NET_DEV_FEAT_TX,
         NET_DEV_FEAT_GSO,
         NET_DEV_FEAT_GRO,
+        NET_DEV_FEAT_GRO_HW,
         NET_DEV_FEAT_LRO,
         NET_DEV_FEAT_TSO,
         NET_DEV_FEAT_TSO6,
@@ -57,30 +58,50 @@ struct ethtool_link_usettings {
         } link_modes;
 };
 
+typedef struct u32_opt {
+        uint32_t value; /* a value of 0 indicates the hardware advertised maximum should be used.*/
+        bool set;
+} u32_opt;
+
 typedef struct netdev_channels {
-        uint32_t rx_count;
-        uint32_t tx_count;
-        uint32_t other_count;
-        uint32_t combined_count;
-
-        bool rx_count_set;
-        bool tx_count_set;
-        bool other_count_set;
-        bool combined_count_set;
+        u32_opt rx;
+        u32_opt tx;
+        u32_opt other;
+        u32_opt combined;
 } netdev_channels;
 
 typedef struct netdev_ring_param {
-        uint32_t rx_pending;
-        uint32_t rx_mini_pending;
-        uint32_t rx_jumbo_pending;
-        uint32_t tx_pending;
-
-        bool rx_pending_set;
-        bool rx_mini_pending_set;
-        bool rx_jumbo_pending_set;
-        bool tx_pending_set;
+        u32_opt rx;
+        u32_opt rx_mini;
+        u32_opt rx_jumbo;
+        u32_opt tx;
 } netdev_ring_param;
 
+typedef struct netdev_coalesce_param {
+        u32_opt rx_coalesce_usecs;
+        u32_opt rx_max_coalesced_frames;
+        u32_opt rx_coalesce_usecs_irq;
+        u32_opt rx_max_coalesced_frames_irq;
+        u32_opt tx_coalesce_usecs;
+        u32_opt tx_max_coalesced_frames;
+        u32_opt tx_coalesce_usecs_irq;
+        u32_opt tx_max_coalesced_frames_irq;
+        u32_opt stats_block_coalesce_usecs;
+        int use_adaptive_rx_coalesce;
+        int use_adaptive_tx_coalesce;
+        u32_opt pkt_rate_low;
+        u32_opt rx_coalesce_usecs_low;
+        u32_opt rx_max_coalesced_frames_low;
+        u32_opt tx_coalesce_usecs_low;
+        u32_opt tx_max_coalesced_frames_low;
+        u32_opt pkt_rate_high;
+        u32_opt rx_coalesce_usecs_high;
+        u32_opt rx_max_coalesced_frames_high;
+        u32_opt tx_coalesce_usecs_high;
+        u32_opt tx_max_coalesced_frames_high;
+        u32_opt rate_sample_interval;
+} netdev_coalesce_param;
+
 int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret);
 int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
                           int *ret_autonegotiation, uint64_t *ret_speed,
@@ -88,12 +109,13 @@ int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
 int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret);
 int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts);
 int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring);
-int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features);
+int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]);
 int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
                               int autonegotiation, const uint32_t advertise[static N_ADVERTISE],
                               uint64_t speed, Duplex duplex, NetDevPort port);
 int ethtool_set_channels(int *ethtool_fd, const char *ifname, const netdev_channels *channels);
 int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg);
+int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce);
 
 const char *duplex_to_string(Duplex d) _const_;
 Duplex duplex_from_string(const char *d) _pure_;
@@ -109,6 +131,8 @@ enum ethtool_link_mode_bit_indices ethtool_link_mode_bit_from_string(const char
 CONFIG_PARSER_PROTOTYPE(config_parse_duplex);
 CONFIG_PARSER_PROTOTYPE(config_parse_wol);
 CONFIG_PARSER_PROTOTYPE(config_parse_port);
-CONFIG_PARSER_PROTOTYPE(config_parse_channel);
 CONFIG_PARSER_PROTOTYPE(config_parse_advertise);
-CONFIG_PARSER_PROTOTYPE(config_parse_nic_buffer_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_ring_buffer_or_channel);
+CONFIG_PARSER_PROTOTYPE(config_parse_coalesce_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_coalesce_sec);
+CONFIG_PARSER_PROTOTYPE(config_parse_nic_coalesce_setting);
index 6dca6661e1da43779772ee58d7e2191b1a05c444..9651fd8c0010cafd994ab993aa619c15e66fd612 100644 (file)
@@ -316,7 +316,7 @@ static size_t table_data_size(TableDataType type, const void *data) {
                 return sizeof(mode_t);
 
         default:
-                assert_not_reached("Uh? Unexpected cell type");
+                assert_not_reached();
         }
 }
 
@@ -1048,7 +1048,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         return 0;
 
                 default:
-                        assert_not_reached("Uh? Unexpected data type.");
+                        assert_not_reached();
                 }
 
                 r = table_add_cell(t, &last_cell, type, data);
@@ -1778,7 +1778,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
         }
 
         default:
-                assert_not_reached("Unexpected type?");
+                assert_not_reached();
         }
 
         return d->formatted;
@@ -2551,15 +2551,11 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
         case TABLE_IN6_ADDR:
                 return json_variant_new_array_bytes(ret, &d->address, FAMILY_ADDRESS_SIZE(AF_INET6));
 
-        case TABLE_ID128: {
-                char buf[SD_ID128_STRING_MAX];
-                return json_variant_new_string(ret, sd_id128_to_string(d->id128, buf));
-        }
+        case TABLE_ID128:
+                return json_variant_new_string(ret, SD_ID128_TO_STRING(d->id128));
 
-        case TABLE_UUID: {
-                char buf[ID128_UUID_STRING_MAX];
-                return json_variant_new_string(ret, id128_to_uuid_string(d->id128, buf));
-        }
+        case TABLE_UUID:
+                return json_variant_new_string(ret, ID128_TO_UUID_STRING(d->id128));
 
         case TABLE_UID:
                 if (!uid_is_valid(d->uid))
index da3ed0a8503b6d57ffd875ca6a664b19f5dbd793..a13c06fd8800e129774137c4d26ff963754dbc7d 100644 (file)
@@ -59,7 +59,6 @@ static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatch
                 {},
         };
 
-        char smid[SD_ID128_STRING_MAX];
         JsonVariant *m;
         sd_id128_t mid;
         int r;
@@ -74,7 +73,7 @@ static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatch
         if (r < 0)
                 return json_log(variant, flags, r, "Failed to determine machine ID: %m");
 
-        m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+        m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
         if (!m)
                 return 0;
 
@@ -146,7 +145,6 @@ static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchF
                 {},
         };
 
-        char smid[SD_ID128_STRING_MAX];
         JsonVariant *m;
         sd_id128_t mid;
         int r;
@@ -161,7 +159,7 @@ static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchF
         if (r < 0)
                 return json_log(variant, flags, r, "Failed to determine machine ID: %m");
 
-        m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+        m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
         if (!m)
                 return 0;
 
index 511aa7d0313cb40e37cc6c23f5a8a21972fd71f6..1329b0d1894b91105a5c67f29f7047a11ba5011f 100644 (file)
 #include "util.h"
 
 static int sethostname_idempotent_full(const char *s, bool really) {
-        char buf[HOST_NAME_MAX + 1] = {};
+        _cleanup_free_ char *buf = NULL;
+        int r;
 
         assert(s);
 
-        if (gethostname(buf, sizeof(buf) - 1) < 0)
-                return -errno;
+        r = gethostname_full(GET_HOSTNAME_ALLOW_NONE | GET_HOSTNAME_ALLOW_LOCALHOST, &buf);
+        if (r < 0)
+                return r;
 
         if (streq(buf, s))
                 return 0;
@@ -41,26 +43,6 @@ int sethostname_idempotent(const char *s) {
         return sethostname_idempotent_full(s, true);
 }
 
-bool get_hostname_filtered(char ret[static HOST_NAME_MAX + 1]) {
-        char buf[HOST_NAME_MAX + 1] = {};
-
-        /* Returns true if we got a good hostname, false otherwise. */
-
-        if (gethostname(buf, sizeof(buf) - 1) < 0)
-                return false;  /* This can realistically only fail with ENAMETOOLONG.
-                                * Let's treat that case the same as an invalid hostname. */
-
-        if (isempty(buf))
-                return false;
-
-        /* This is the built-in kernel default hostname */
-        if (streq(buf, "(none)"))
-                return false;
-
-        memcpy(ret, buf, sizeof buf);
-        return true;
-}
-
 int shorten_overlong(const char *s, char **ret) {
         char *h, *p;
 
@@ -195,10 +177,14 @@ int hostname_setup(bool really) {
         }
 
         if (!hn) {
+                _cleanup_free_ char *buf = NULL;
+
                 /* Don't override the hostname if it is already set and not explicitly configured */
 
-                char buf[HOST_NAME_MAX + 1] = {};
-                if (get_hostname_filtered(buf)) {
+                r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &buf);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r >= 0) {
                         log_debug("No hostname configured, leaving existing hostname <%s> in place.", buf);
                         return 0;
                 }
index 5ac7241a59fae20f297eef41a8bb26bfe74b94cd..6def36c350e1b90dbb4edae236650d3c21046ef2 100644 (file)
@@ -21,6 +21,5 @@ int shorten_overlong(const char *s, char **ret);
 int read_etc_hostname_stream(FILE *f, char **ret);
 int read_etc_hostname(const char *path, char **ret);
 
-bool get_hostname_filtered(char ret[static HOST_NAME_MAX + 1]);
 void hostname_update_source_hint(const char *hostname, HostnameSource source);
 int hostname_setup(bool really);
index 2a30e40686f4d58b1d2dfc1bf9aaaa20cda5b671..bb1e5136a736289d34ea3cc06d2b9a3504e763ed 100644 (file)
 #include "string-table.h"
 #include "string-util.h"
 
-int import_url_last_component(const char *url, char **ret) {
-        const char *e, *p;
-        char *s;
+static const char *skip_protocol_and_hostname(const char *url) {
+        const char *d;
+        size_t n;
+
+        /* A very very lenient implementation of RFC3986 Section 3.2 */
+
+        /* Find colon separating protocol and hostname */
+        d = strchr(url, ':');
+        if (!d || url == d)
+                return NULL;
+        d++;
+
+        /* Skip slashes after colon */
+        d += strspn(d, "/");
+
+        /* Skip everything till next slash or end */
+        n = strcspn(d, "/?#");
+        if (n == 0)
+                return NULL;
+
+        return d + n;
+}
 
-        e = strchrnul(url, '?');
+int import_url_last_component(
+                const char *url,
+                char **ret) {
 
-        while (e > url && e[-1] == '/')
+        const char *e, *p, *h;
+
+        /* This extracts the last path component of the specified URI, i.e. the last non-empty substrings
+         * between two "/" characters. This ignores "Query" and "Fragment" suffixes (as per RFC3986). */
+
+        h = skip_protocol_and_hostname(url);
+        if (!h)
+                return -EINVAL;
+
+        e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
+
+        while (e > h && e[-1] == '/') /* Eat trailing slashes */
                 e--;
 
         p = e;
-        while (p > url && p[-1] != '/')
+        while (p > h && p[-1] != '/') /* Find component before that */
                 p--;
 
-        if (e <= p)
-                return -EINVAL;
+        if (e <= p) /* Empty component? */
+                return -EADDRNOTAVAIL;
 
-        s = strndup(p, e - p);
-        if (!s)
-                return -ENOMEM;
+        if (ret) {
+                char *s;
+
+                s = strndup(p, e - p);
+                if (!s)
+                        return -ENOMEM;
+
+                *ret = s;
+        }
 
-        *ret = s;
         return 0;
 }
 
-int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
-        const char *e;
+int import_url_change_suffix(
+                const char *url,
+                size_t n_drop_components,
+                const char *suffix,
+                char **ret) {
+
+        const char *e, *h;
         char *s;
 
         assert(url);
         assert(ret);
 
-        e = strchrnul(url, '?');
+        /* This drops the specified number of path components of the specified URI, i.e. the specified number
+         * of non-empty substring between two "/" characters from the end of the string, and then append the
+         * specified suffix instead. Before doing all this it chops off the "Query" and "Fragment" suffixes
+         * (they are *not* readded to the final URL). Note that n_drop_components may be 0 (in which case the
+         * component are simply added to the end). The suffix may be specified as NULL or empty string in
+         * which case nothing is appended, only the specified number of components chopped off. Note that the
+         * function may be called with n_drop_components == 0 and suffix == NULL, in which case the "Query"
+         * and "Fragment" is chopped off, and ensured the URL ends in a single "/", and that's it. */
+
+        h = skip_protocol_and_hostname(url);
+        if (!h)
+                return -EINVAL;
 
-        while (e > url && e[-1] == '/')
-                e--;
+        e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
 
-        while (e > url && e[-1] != '/')
+        while (e > h && e[-1] == '/') /* Eat trailing slashes */
                 e--;
 
-        if (e <= url)
-                return -EINVAL;
+        /* Drop the specified number of components from the end. Note that this is pretty lenient: if there
+         * are less component we silently drop those and then append the suffix to the top. */
+        while (n_drop_components > 0) {
+                while (e > h && e[-1] != '/') /* Eat last word (we don't mind if empty) */
+                        e--;
+
+                while (e > h && e[-1] == '/') /* Eat slashes before the last word */
+                        e--;
+
+                n_drop_components--;
+        }
 
-        s = new(char, (e - url) + strlen(suffix) + 1);
+        s = new(char, (e - url) + 1 + strlen_ptr(suffix) + 1);
         if (!s)
                 return -ENOMEM;
 
-        strcpy(mempcpy(s, url, e - url), suffix);
+        strcpy(stpcpy(mempcpy(s, url, e - url), "/"), strempty(suffix));
         *ret = s;
         return 0;
 }
index c7ec3b4eabda71281acf80d0ef76cc4fff7ec904..3b2425b916537cdb499375844c0aff39fab61e7a 100644 (file)
@@ -14,7 +14,16 @@ typedef enum ImportVerify {
 } ImportVerify;
 
 int import_url_last_component(const char *url, char **ret);
-int import_url_change_last_component(const char *url, const char *suffix, char **ret);
+
+int import_url_change_suffix(const char *url, size_t n_drop_components, const char *suffix, char **ret);
+
+static inline int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
+        return import_url_change_suffix(url, 1, suffix, ret);
+}
+
+static inline int import_url_append_component(const char *url, const char *suffix, char **ret) {
+        return import_url_change_suffix(url, 0, suffix, ret);
+}
 
 const char* import_verify_to_string(ImportVerify v) _const_;
 ImportVerify import_verify_from_string(const char *s) _pure_;
diff --git a/src/shared/install-file.c b/src/shared/install-file.c
new file mode 100644 (file)
index 0000000..664f028
--- /dev/null
@@ -0,0 +1,269 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/ioctl.h>
+
+#include "btrfs-util.h"
+#include "chattr-util.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "install-file.h"
+#include "missing_syscall.h"
+#include "rm-rf.h"
+
+int fs_make_very_read_only(int fd) {
+        struct stat st;
+        int r;
+
+        assert(fd >= 0);
+
+        /* Tries to make the specified fd "comprehensively" read-only. Primary usecase for this is OS images,
+         * i.e. either loopback files or larger directory hierarchies. Depending on the inode type and
+         * backing file system this means something different:
+         *
+         * 1. If the fd refers to a btrfs subvolume we'll mark it read-only as a whole
+         * 2. If the fd refers to any other directory we'll set the FS_IMMUTABLE_FL flag on it
+         * 3. If the fd refers to a regular file we'll drop the w bits.
+         * 4. If the fd refers to a block device, use BLKROSET to set read-only state
+         *
+         * You might wonder why not drop the x bits for directories. That's because we want to guarantee that
+         * everything "inside" the image remains largely the way it is, in case you mount it. And since the
+         * mode of the root dir of the image is pretty visible we don't want to modify it. btrfs subvol flags
+         * and the FS_IMMUTABLE_FL otoh are much less visible. Changing the mode of regular files should be
+         * OK though, since after all this is supposed to be used for disk images, i.e. the fs in the disk
+         * image doesn't make the mode of the loopback file it is stored in visible. */
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        switch (st.st_mode & S_IFMT) {
+
+        case S_IFDIR:
+                if (btrfs_might_be_subvol(&st)) {
+                        r = btrfs_subvol_set_read_only_fd(fd, true);
+                        if (r >= 0)
+                                return 0;
+
+                        if (!ERRNO_IS_NOT_SUPPORTED(r) && r != -EINVAL)
+                                return r;
+                }
+
+                r = chattr_fd(fd, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case S_IFREG:
+                if ((st.st_mode & 0222) != 0)
+                        if (fchmod(fd, st.st_mode & 07555) < 0)
+                                return -errno;
+
+                break;
+
+        case S_IFBLK: {
+                int ro = 1;
+
+                if (ioctl(fd, BLKROSET, &ro) < 0)
+                        return -errno;
+
+                break;
+        }
+
+        default:
+                return -EBADFD;
+        }
+
+        return 0;
+}
+
+static int unlinkat_maybe_dir(int dirfd, const char *pathname) {
+
+        /* Invokes unlinkat() for regular files first, and if this fails with EISDIR tries again with
+         * AT_REMOVEDIR */
+
+        if (unlinkat(dirfd, pathname, 0) < 0) {
+                if (errno != EISDIR)
+                        return -errno;
+
+                if (unlinkat(dirfd, pathname, AT_REMOVEDIR) < 0)
+                        return -errno;
+        }
+
+        return 0;
+}
+
+int install_file(int source_atfd, const char *source_name,
+                 int target_atfd, const char *target_name,
+                 InstallFileFlags flags) {
+
+        _cleanup_close_ int rofd = -1;
+        int r;
+
+        /* Moves a file or directory tree into place, with some bells and whistles:
+         *
+         * 1. Optionally syncs before/after to ensure file installation can be used as barrier
+         * 2. Optionally marks the file/directory read-only using fs_make_very_read_only()
+         * 3. Optionally operates in replacing or in non-replacing mode.
+         * 4. If it replaces will remove the old tree if needed.
+         */
+
+        assert(source_atfd >= 0 || source_atfd == AT_FDCWD);
+        assert(source_name);
+        assert(target_atfd >= 0 || target_atfd == AT_FDCWD);
+
+        /* If target_name is specified as NULL no renaming takes place. Instead it is assumed the file is
+         * already in place, and only the syncing/read-only marking shall be applied. Note that with
+         * target_name=NULL and flags=0 this call is a NOP */
+
+        if ((flags & (INSTALL_FSYNC|INSTALL_FSYNC_FULL|INSTALL_SYNCFS|INSTALL_READ_ONLY)) != 0) {
+                _cleanup_close_ int pfd = -1;
+                struct stat st;
+
+                /* Open an O_PATH fd for the source if we need to sync things or mark things read only. */
+
+                pfd = openat(source_atfd, source_name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+                if (pfd < 0)
+                        return -errno;
+
+                if (fstat(pfd, &st) < 0)
+                        return -errno;
+
+                switch (st.st_mode & S_IFMT) {
+
+                case S_IFREG: {
+                        _cleanup_close_ int regfd = -1;
+
+                        regfd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
+                        if (regfd < 0)
+                                return regfd;
+
+                        if ((flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0) {
+                                /* If this is just a regular file (as oppose to a fully populated directory)
+                                 * let's downgrade INSTALL_SYNCFS to INSTALL_FSYNC_FULL, after all this is
+                                 * going to be a single inode we install */
+                                r = fsync_full(regfd);
+                                if (r < 0)
+                                        return r;
+                        } else if (flags & INSTALL_FSYNC) {
+                                if (fsync(regfd) < 0)
+                                        return -errno;
+                        }
+
+                        if (flags & INSTALL_READ_ONLY)
+                                rofd = TAKE_FD(regfd);
+
+                        break;
+                }
+
+                case S_IFDIR: {
+                        _cleanup_close_ int dfd = -1;
+
+                        dfd = fd_reopen(pfd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+                        if (dfd < 0)
+                                return dfd;
+
+                        if (flags & INSTALL_SYNCFS) {
+                                if (syncfs(dfd) < 0)
+                                        return -errno;
+                        } else if (flags & INSTALL_FSYNC_FULL) {
+                                r = fsync_full(dfd);
+                                if (r < 0)
+                                        return r;
+                        } else if (flags & INSTALL_FSYNC) {
+                                if (fsync(dfd) < 0)
+                                        return -errno;
+                        }
+
+                        if (flags & INSTALL_READ_ONLY)
+                                rofd = TAKE_FD(dfd);
+
+                        break;
+                }
+
+                default:
+                        /* Other inodes: char/block device inodes, fifos, symlinks, sockets don't need
+                         * syncing themselves, as they only exist in the directory, and have no contents on
+                         * disk */
+
+                        if (target_name && (flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0) {
+                                r = fsync_directory_of_file(pfd);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        break;
+                }
+        }
+
+        if (target_name) {
+                /* Rename the file */
+
+                if (flags & INSTALL_REPLACE) {
+                        /* First, try a simple renamat(), maybe that's enough */
+                        if (renameat(source_atfd, source_name, target_atfd, target_name) < 0) {
+                                _cleanup_close_ int dfd = -1;
+
+                                if (!IN_SET(errno, EEXIST, ENOTDIR, ENOTEMPTY, EISDIR, EBUSY))
+                                        return -errno;
+
+                                /* Hmm, the target apparently existed already. Let's try to use
+                                 * RENAME_EXCHANGE. But let's first open the inode if it's a directory, so
+                                 * that we can later remove its contents if it's a directory. Why do this
+                                 * before the rename()? Mostly because if we have trouble opening the thing
+                                 * we want to know before we start actually modifying the file system. */
+
+                                dfd = openat(target_atfd, target_name, O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
+                                if (dfd < 0 && errno != ENOTDIR)
+                                        return -errno;
+
+                                if (renameat2(source_atfd, source_name, target_atfd, target_name, RENAME_EXCHANGE) < 0) {
+
+                                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL)
+                                                return -errno;
+
+                                        /* The exchange didn't work, let's remove the target first, and try again */
+
+                                        if (dfd >= 0)
+                                                (void) rm_rf_children(TAKE_FD(dfd), REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD, NULL);
+
+                                        r = unlinkat_maybe_dir(target_atfd, target_name);
+                                        if (r < 0)
+                                                return log_debug_errno(r, "Failed to remove target directory: %m");
+
+                                        if (renameat(source_atfd, source_name, target_atfd, target_name) < 0)
+                                                return -errno;
+                                } else {
+                                        /* The exchange worked, hence let's remove the source (i.e. the old target) */
+                                        if (dfd >= 0)
+                                                (void) rm_rf_children(TAKE_FD(dfd), REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD, NULL);
+
+                                        r = unlinkat_maybe_dir(source_atfd, source_name);
+                                        if (r < 0)
+                                                return log_debug_errno(r, "Failed to remove replaced target directory: %m");
+                                }
+                        }
+                } else {
+                        r = rename_noreplace(source_atfd, source_name, target_atfd, target_name);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        if (rofd >= 0) {
+                r = fs_make_very_read_only(rofd);
+                if (r < 0)
+                        return r;
+        }
+
+        if ((flags & (INSTALL_FSYNC_FULL|INSTALL_SYNCFS)) != 0) {
+                if (target_name)
+                        r = fsync_parent_at(target_atfd, target_name);
+                else
+                        r = fsync_parent_at(source_atfd, source_name);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
diff --git a/src/shared/install-file.h b/src/shared/install-file.h
new file mode 100644 (file)
index 0000000..c37254f
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int fs_make_very_read_only(int fd);
+
+typedef enum InstallFileFlags {
+        INSTALL_REPLACE    = 1 << 0, /* Replace an existing inode */
+        INSTALL_READ_ONLY  = 1 << 1, /* Call fs_make_very_read_only() to make the inode comprehensively read-only */
+        INSTALL_FSYNC      = 1 << 2, /* fsync() file contents before moving file in */
+        INSTALL_FSYNC_FULL = 1 << 3, /* like INSTALL_FSYNC, but also fsync() parent dir before+after moving file in */
+        INSTALL_SYNCFS     = 1 << 4, /* syncfs() before moving file in, fsync() parent dir after moving file in */
+} InstallFileFlags;
+
+int install_file(int source_atfd, const char *source_name, int target_atfd, const char *target_name, InstallFileFlags flags);
index 4bf868f8e9e193706649233edac6d37368d049d1..a348f0c5728c1c4c2a5f7d3a8f363d9919ecc354 100644 (file)
@@ -2897,7 +2897,7 @@ int unit_file_lookup_state(
                 break;
 
         default:
-                assert_not_reached("Unexpected unit file type.");
+                assert_not_reached();
         }
 
         *ret = state;
@@ -2989,7 +2989,7 @@ static int presets_find_config(UnitFileScope scope, const char *root_dir, char *
         else if (IN_SET(scope, UNIT_FILE_GLOBAL, UNIT_FILE_USER))
                 dirs = user_dirs;
         else
-                assert_not_reached("Invalid unit file scope");
+                assert_not_reached();
 
         return conf_files_list_strv(files, ".preset", root_dir, 0, dirs);
 }
@@ -3173,7 +3173,7 @@ static int query_presets(const char *name, const UnitFilePresets *presets, char
                 log_debug("Preset files say disable %s.", name);
                 return 0;
         default:
-                assert_not_reached("invalid preset action");
+                assert_not_reached();
         }
 }
 
index 9e11dc09c1cc190db729d16313f6007515a96ddd..bbb152481ec42f9eca0a63d38d13d82f734d94e4 100644 (file)
@@ -417,7 +417,7 @@ int journal_importer_process_data(JournalImporter *imp) {
 
                 return 0; /* continue */
         default:
-                assert_not_reached("wtf?");
+                assert_not_reached();
         }
 }
 
index c52460a3ecea9c7c143524ef773f22be53b5e755..a1608d6aa4e3df8d6ee1503d636f8b07c9d27664 100644 (file)
@@ -447,9 +447,7 @@ int json_variant_new_hex(JsonVariant **ret, const void *p, size_t n) {
 }
 
 int json_variant_new_id128(JsonVariant **ret, sd_id128_t id) {
-        char s[SD_ID128_STRING_MAX];
-
-        return json_variant_new_string(ret, sd_id128_to_string(id, s));
+        return json_variant_new_string(ret, SD_ID128_TO_STRING(id));
 }
 
 static void json_variant_set(JsonVariant *a, JsonVariant *b) {
@@ -505,7 +503,7 @@ static void json_variant_set(JsonVariant *a, JsonVariant *b) {
                 break;
 
         default:
-                assert_not_reached("Unexpected variant type");
+                assert_not_reached();
         }
 }
 
@@ -763,7 +761,7 @@ static size_t json_variant_size(JsonVariant* v) {
                 return offsetof(JsonVariant, value);
 
         default:
-                assert_not_reached("unexpected type");
+                assert_not_reached();
         }
 }
 
@@ -1397,7 +1395,7 @@ bool json_variant_equal(JsonVariant *a, JsonVariant *b) {
         }
 
         default:
-                assert_not_reached("Unknown variant type.");
+                assert_not_reached();
         }
 }
 
@@ -1749,7 +1747,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
         }
 
         default:
-                assert_not_reached("Unexpected variant type.");
+                assert_not_reached();
         }
 
         return 0;
@@ -2787,7 +2785,7 @@ int json_tokenize(
                 return -EINVAL;
 
         default:
-                assert_not_reached("Unexpected tokenizer state");
+                assert_not_reached();
         }
 
 null_return:
@@ -3156,7 +3154,7 @@ static int json_parse_internal(
                         break;
 
                 default:
-                        assert_not_reached("Unexpected token");
+                        assert_not_reached();
                 }
 
                 if (add) {
index bd69e946f55f9816078b4fa5bb7e9dd1d06adccb..d1f6b5df0396385ee0ec87a1404d0e768a0d6b24 100644 (file)
@@ -422,7 +422,7 @@ int local_outbounds(
                         break;
 
                 default:
-                        assert_not_reached("Unexpected protocol");
+                        assert_not_reached();
                 }
 
                 /* So ideally we'd just use IP_UNICAST_IF here to pass the ifindex info to the kernel before
@@ -493,7 +493,7 @@ int local_outbounds(
                         break;
 
                 default:
-                        assert_not_reached("Unexpected protocol");
+                        assert_not_reached();
                 }
         }
 
index 3165cf29da13a4eed73c46e35be64a2e420a6223..82fd13db7580d2dae1f4386f5a0e4f39a8f2389e 100644 (file)
@@ -416,7 +416,7 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou
                         break;
 
                 default:
-                        assert_not_reached("Unknown time format");
+                        assert_not_reached();
                 }
         }
 
@@ -752,13 +752,12 @@ static int output_export(
                 const Set *output_fields,
                 const size_t highlight[2]) {
 
-        sd_id128_t boot_id;
-        char sid[SD_ID128_STRING_MAX];
-        int r;
-        usec_t realtime, monotonic;
         _cleanup_free_ char *cursor = NULL;
+        usec_t realtime, monotonic;
+        sd_id128_t boot_id;
         const void *data;
         size_t length;
+        int r;
 
         assert(j);
 
@@ -784,7 +783,7 @@ static int output_export(
                 cursor,
                 realtime,
                 monotonic,
-                sd_id128_to_string(boot_id, sid));
+                SD_ID128_TO_STRING(boot_id));
 
         JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
                 size_t fieldlen;
index 1f741f7417e4978e3a948872aa4fbe9cc1222063..7eb71b287c9f782bd5ac90bec12be5d2e860fc79 100644 (file)
@@ -152,6 +152,8 @@ shared_sources = files('''
         import-util.c
         import-util.h
         initreq.h
+        install-file.c
+        install-file.h
         install-printf.c
         install-printf.h
         install.c
index ce10e60bb6b9f24d106ce42451adaf5c0bff8467..88a3edec558ca0f02244dec4657c9f58919697c0 100644 (file)
@@ -65,31 +65,27 @@ int make_filesystem(
         if (r < 0)
                 return r;
         if (r == 0) {
-                char suuid[ID128_UUID_STRING_MAX];
-
                 /* Child */
-                id128_to_uuid_string(uuid, suuid);
-
                 if (streq(fstype, "ext4"))
                         (void) execlp(mkfs, mkfs,
-                               "-L", label,
-                               "-U", suuid,
-                               "-I", "256",
-                               "-O", "has_journal",
-                               "-m", "0",
-                               "-E", discard ? "lazy_itable_init=1,discard" : "lazy_itable_init=1,nodiscard",
-                               node, NULL);
+                                      "-L", label,
+                                      "-U", ID128_TO_UUID_STRING(uuid),
+                                      "-I", "256",
+                                      "-O", "has_journal",
+                                      "-m", "0",
+                                      "-E", discard ? "lazy_itable_init=1,discard" : "lazy_itable_init=1,nodiscard",
+                                      node, NULL);
 
                 else if (streq(fstype, "btrfs")) {
                         if (discard)
-                                (void) execlp(mkfs, mkfs, "-L", label, "-U", suuid, node, NULL);
+                                (void) execlp(mkfs, mkfs, "-L", label, "-U", ID128_TO_UUID_STRING(uuid), node, NULL);
                         else
-                                (void) execlp(mkfs, mkfs, "-L", label, "-U", suuid, "--nodiscard", node, NULL);
+                                (void) execlp(mkfs, mkfs, "-L", label, "-U", ID128_TO_UUID_STRING(uuid), "--nodiscard", node, NULL);
 
                 } else if (streq(fstype, "xfs")) {
                         const char *j;
 
-                        j = strjoina("uuid=", suuid);
+                        j = strjoina("uuid=", ID128_TO_UUID_STRING(uuid));
                         if (discard)
                                 (void) execlp(mkfs, mkfs, "-L", label, "-m", j, "-m", "reflink=1", node, NULL);
                         else
@@ -118,9 +114,9 @@ int make_filesystem(
                 } else if (streq(fstype, "swap")) {
 
                         (void) execlp(mkfs, mkfs,
-                               "-L", label,
-                               "-U", suuid,
-                               node, NULL);
+                                      "-L", label,
+                                      "-U", ID128_TO_UUID_STRING(uuid),
+                                      node, NULL);
 
                 } else
                         /* Generic fallback for all other file systems */
index cf8ca8d9d3f66a5cf9c6fd8a02bbaeae7a7bb51f..9a3f33915e1c0e8b8a010708740e9242bd359332 100644 (file)
@@ -42,10 +42,7 @@ int mount_fd(const char *source,
              unsigned long mountflags,
              const void *data) {
 
-        char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
-        xsprintf(path, "/proc/self/fd/%i", target_fd);
-        if (mount(source, path, filesystemtype, mountflags, data) < 0) {
+        if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
                 if (errno != ENOENT)
                         return -errno;
 
@@ -733,8 +730,7 @@ static int mount_in_namespace(
 
         _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
         _cleanup_close_ int self_mntns_fd = -1, mntns_fd = -1, root_fd = -1, pidns_fd = -1, chased_src_fd = -1;
-        char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p,
-                chased_src[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+        char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
         bool mount_slave_created = false, mount_slave_mounted = false,
                 mount_tmp_created = false, mount_tmp_mounted = false,
                 mount_outside_created = false, mount_outside_mounted = false;
@@ -767,9 +763,8 @@ static int mount_in_namespace(
         if (st.st_ino == self_mntns_st.st_ino && st.st_dev == self_mntns_st.st_dev)
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to activate bind mount in target, not running in a mount namespace");
 
-        /* One day, when bind mounting /proc/self/fd/n works across
-         * namespace boundaries we should rework this logic to make
-         * use of it... */
+        /* One day, when bind mounting /proc/self/fd/n works across namespace boundaries we should rework
+         * this logic to make use of it... */
 
         p = strjoina(propagate_path, "/");
         r = laccess(p, F_OK);
@@ -779,7 +774,6 @@ static int mount_in_namespace(
         r = chase_symlinks(src, NULL, CHASE_TRAIL_SLASH, NULL, &chased_src_fd);
         if (r < 0)
                 return log_debug_errno(r, "Failed to resolve source path of %s: %m", src);
-        xsprintf(chased_src, "/proc/self/fd/%i", chased_src_fd);
 
         if (fstat(chased_src_fd, &st) < 0)
                 return log_debug_errno(errno, "Failed to stat() resolved source path %s: %m", src);
@@ -824,9 +818,9 @@ static int mount_in_namespace(
         mount_tmp_created = true;
 
         if (is_image)
-                r = verity_dissect_and_mount(chased_src, mount_tmp, options, NULL, NULL, NULL);
+                r = verity_dissect_and_mount(FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, options, NULL, NULL, NULL);
         else
-                r = mount_follow_verbose(LOG_DEBUG, chased_src, mount_tmp, NULL, MS_BIND, NULL);
+                r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, NULL, MS_BIND, NULL);
         if (r < 0)
                 goto finish;
 
index dfc47c423441a1aea646cac6c7c9fcd2f12141da..19e16d93455c88662e040e97d19f10ef014e335d 100644 (file)
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <sys/poll.h>
+#include <poll.h>
 
 #include "fd-util.h"
 #include "io-util.h"
index 9fc577ca3cf1c7c4dd1f2fc2f262e6d71cc91181..27823ab219175a9bb7877c0b5d6efbe68ad11079 100644 (file)
@@ -3,6 +3,7 @@
 #include <fcntl.h>
 
 #include "ask-password-api.h"
+#include "env-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "format-table.h"
@@ -174,6 +175,55 @@ char *pkcs11_token_model(const CK_TOKEN_INFO *token_info) {
         return t;
 }
 
+int pkcs11_token_login_by_pin(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                const CK_TOKEN_INFO *token_info,
+                const char *token_label,
+                const void *pin,
+                size_t pin_size) {
+
+        CK_RV rv;
+
+        assert(m);
+        assert(token_info);
+
+        if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
+                rv = m->C_Login(session, CKU_USER, NULL, 0);
+                if (rv != CKR_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+
+                log_info("Successfully logged into security token '%s' via protected authentication path.", token_label);
+                return 0;
+        }
+
+        if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
+                log_info("No login into security token '%s' required.", token_label);
+                return 0;
+        }
+
+        if (!pin)
+                return -ENOANO;
+
+        rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) pin, pin_size);
+        if (rv == CKR_OK)  {
+                log_info("Successfully logged into security token '%s'.", token_label);
+                return 0;
+        }
+
+        if (rv == CKR_PIN_LOCKED)
+                return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+                                       "PIN has been locked, please reset PIN of security token '%s'.", token_label);
+        if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+
+        log_notice("PIN for token '%s' is incorrect, please try again.", token_label);
+
+        return -ENOLCK;
+}
+
 int pkcs11_token_login(
                 CK_FUNCTION_LIST *m,
                 CK_SESSION_HANDLE session,
@@ -208,24 +258,12 @@ int pkcs11_token_login(
         if (uri_result != P11_KIT_URI_OK)
                 return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
 
-        if (FLAGS_SET(token_info->flags, CKF_PROTECTED_AUTHENTICATION_PATH)) {
-                rv = m->C_Login(session, CKU_USER, NULL, 0);
-                if (rv != CKR_OK)
-                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                               "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+        r = pkcs11_token_login_by_pin(m, session, token_info, token_label, /* pin= */ NULL, 0);
+        if (r == 0 && ret_used_pin)
+                *ret_used_pin = NULL;
 
-                log_info("Successfully logged into security token '%s' via protected authentication path.", token_label);
-                if (ret_used_pin)
-                        *ret_used_pin = NULL;
-                return 0;
-        }
-
-        if (!FLAGS_SET(token_info->flags, CKF_LOGIN_REQUIRED)) {
-                log_info("No login into security token '%s' required.", token_label);
-                if (ret_used_pin)
-                        *ret_used_pin = NULL;
-                return 0;
-        }
+        if (r != -ENOANO) /* pin required */
+                return r;
 
         token_uri_escaped = cescape(token_uri_string);
         if (!token_uri_escaped)
@@ -245,9 +283,7 @@ int pkcs11_token_login(
                         if (!passwords)
                                 return log_oom();
 
-                        string_erase(e);
-                        if (unsetenv("PIN") < 0)
-                                return log_error_errno(errno, "Failed to unset $PIN: %m");
+                        assert_se(unsetenv_erase("PIN") >= 0);
                 } else if (headless)
                         return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the 'PIN' environment variable.");
                 else {
@@ -279,28 +315,19 @@ int pkcs11_token_login(
                 }
 
                 STRV_FOREACH(i, passwords) {
-                        rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i));
-                        if (rv == CKR_OK)  {
-
-                                if (ret_used_pin) {
-                                        char *c;
-
-                                        c = strdup(*i);
-                                        if (!c)
-                                                return log_oom();
+                        r = pkcs11_token_login_by_pin(m, session, token_info, token_label, *i, strlen(*i));
+                        if (r == 0 && ret_used_pin) {
+                                char *c;
 
-                                        *ret_used_pin = c;
-                                }
+                                c = strdup(*i);
+                                if (!c)
+                                        return log_oom();
 
-                                log_info("Successfully logged into security token '%s'.", token_label);
-                                return 0;
+                                *ret_used_pin = c;
                         }
-                        if (rv == CKR_PIN_LOCKED)
-                                return log_error_errno(SYNTHETIC_ERRNO(EPERM),
-                                                       "PIN has been locked, please reset PIN of security token '%s'.", token_label);
-                        if (!IN_SET(rv, CKR_PIN_INCORRECT, CKR_PIN_LEN_RANGE))
-                                return log_error_errno(SYNTHETIC_ERRNO(EIO),
-                                                       "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
+
+                        if (r != -ENOLCK)
+                                return r;
 
                         /* Referesh the token info, so that we can prompt knowing the new flags if they changed. */
                         rv = m->C_GetTokenInfo(slotid, &updated_token_info);
@@ -310,7 +337,6 @@ int pkcs11_token_login(
                                                        slotid, p11_kit_strerror(rv));
 
                         token_info = &updated_token_info;
-                        log_notice("PIN for token '%s' is incorrect, please try again.", token_label);
                 }
         }
 
@@ -1155,3 +1181,71 @@ int pkcs11_find_token_auto(char **ret) {
                                "PKCS#11 tokens not supported on this build.");
 #endif
 }
+
+#if HAVE_P11KIT
+void pkcs11_crypt_device_callback_data_release(pkcs11_crypt_device_callback_data *data) {
+        erase_and_free(data->decrypted_key);
+
+        if (data->free_encrypted_key)
+                free(data->encrypted_key);
+}
+
+int pkcs11_crypt_device_callback(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_SLOT_ID slot_id,
+                const CK_SLOT_INFO *slot_info,
+                const CK_TOKEN_INFO *token_info,
+                P11KitUri *uri,
+                void *userdata) {
+
+        pkcs11_crypt_device_callback_data *data = userdata;
+        CK_OBJECT_HANDLE object;
+        int r;
+
+        assert(m);
+        assert(slot_info);
+        assert(token_info);
+        assert(uri);
+        assert(data);
+
+        /* Called for every token matching our URI */
+
+        r = pkcs11_token_login(
+                        m,
+                        session,
+                        slot_id,
+                        token_info,
+                        data->friendly_name,
+                        "drive-harddisk",
+                        "pkcs11-pin",
+                        "cryptsetup.pkcs11-pin",
+                        data->until,
+                        data->headless,
+                        NULL);
+        if (r < 0)
+                return r;
+
+        /* We are likely called during early boot, where entropy is scarce. Mix some data from the PKCS#11
+         * token, if it supports that. It should be cheap, given that we already are talking to it anyway and
+         * shouldn't hurt. */
+        (void) pkcs11_token_acquire_rng(m, session);
+
+        r = pkcs11_token_find_private_key(m, session, uri, &object);
+        if (r < 0)
+                return r;
+
+        r = pkcs11_token_decrypt_data(
+                        m,
+                        session,
+                        object,
+                        data->encrypted_key,
+                        data->encrypted_key_size,
+                        &data->decrypted_key,
+                        &data->decrypted_key_size);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+#endif
index f7f32d34d71bb56b7207f75da64b700554e6d7a8..f8195d7a369a0175e294d12b9c734520d54c09d2 100644 (file)
@@ -30,6 +30,7 @@ char *pkcs11_token_label(const CK_TOKEN_INFO *token_info);
 char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info);
 char *pkcs11_token_model(const CK_TOKEN_INFO *token_info);
 
+int pkcs11_token_login_by_pin(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, const CK_TOKEN_INFO *token_info, const char *token_label, const void *pin, size_t pin_size);
 int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, bool headless, char **ret_used_pin);
 
 int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
@@ -49,7 +50,35 @@ int pkcs11_find_token(const char *pkcs11_uri, pkcs11_find_token_callback_t callb
 int pkcs11_acquire_certificate(const char *uri, const char *askpw_friendly_name, const char *askpw_icon_name, X509 **ret_cert, char **ret_pin_used);
 #endif
 
+typedef struct {
+        const char *friendly_name;
+        usec_t until;
+        void *encrypted_key;
+        size_t encrypted_key_size;
+        void *decrypted_key;
+        size_t decrypted_key_size;
+        bool free_encrypted_key;
+        bool headless;
+} pkcs11_crypt_device_callback_data;
+
+void pkcs11_crypt_device_callback_data_release(pkcs11_crypt_device_callback_data *data);
+
+int pkcs11_crypt_device_callback(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_SLOT_ID slot_id,
+                const CK_SLOT_INFO *slot_info,
+                const CK_TOKEN_INFO *token_info,
+                P11KitUri *uri,
+                void *userdata);
+
 #endif
 
+typedef struct {
+        const char *friendly_name;
+        usec_t until;
+        bool headless;
+} systemd_pkcs11_plugin_params;
+
 int pkcs11_list_tokens(void);
 int pkcs11_find_token_auto(char **ret);
index 900a7fb5fff2cb2bf0e1a856d762be3440d86846..d9cd8fb2b2d01445bf872cd613221b0c42719d70 100644 (file)
@@ -19,6 +19,9 @@
 #include "stat-util.h"
 #include "string-util.h"
 
+/* We treat tmpfs/ramfs + cgroupfs as non-physical file systems. cgroupfs is similar to tmpfs in a way
+ * after all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it
+ * to remove those again. */
 static bool is_physical_fs(const struct statfs *sfs) {
         return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
 }
@@ -113,133 +116,145 @@ int fstatat_harder(int dfd,
         return 0;
 }
 
-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
-        _cleanup_closedir_ DIR *d = NULL;
-        struct dirent *de;
-        int ret = 0, r;
-        struct statfs sfs;
+static int rm_rf_children_inner(
+                int fd,
+                const char *fname,
+                int is_dir,
+                RemoveFlags flags,
+                const struct stat *root_dev) {
 
-        assert(fd >= 0);
+        struct stat st;
+        int r;
 
-        /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
-         * fd, in all cases, including on failure.. */
+        assert(fd >= 0);
+        assert(fname);
 
-        if (!(flags & REMOVE_PHYSICAL)) {
+        if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
 
-                r = fstatfs(fd, &sfs);
-                if (r < 0) {
-                        safe_close(fd);
-                        return -errno;
-                }
+                r = fstatat_harder(fd, fname, &st, AT_SYMLINK_NOFOLLOW, flags);
+                if (r < 0)
+                        return r;
 
-                if (is_physical_fs(&sfs)) {
-                        /* We refuse to clean physical file systems with this call,
-                         * unless explicitly requested. This is extra paranoia just
-                         * to be sure we never ever remove non-state data. */
-                        _cleanup_free_ char *path = NULL;
+                is_dir = S_ISDIR(st.st_mode);
+        }
 
-                        (void) fd_get_path(fd, &path);
-                        log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.",
-                                  strna(path));
+        if (is_dir) {
+                _cleanup_close_ int subdir_fd = -1;
+                int q;
 
-                        safe_close(fd);
-                        return -EPERM;
-                }
-        }
+                /* if root_dev is set, remove subdirectories only if device is same */
+                if (root_dev && st.st_dev != root_dev->st_dev)
+                        return 0;
 
-        d = fdopendir(fd);
-        if (!d) {
-                safe_close(fd);
-                return errno == ENOENT ? 0 : -errno;
-        }
+                /* Stop at mount points */
+                r = fd_is_mount_point(fd, fname, 0);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 0;
 
-        FOREACH_DIRENT_ALL(de, d, return -errno) {
-                bool is_dir;
-                struct stat st;
+                if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
 
-                if (dot_or_dot_dot(de->d_name))
-                        continue;
+                        /* This could be a subvolume, try to remove it */
 
-                if (de->d_type == DT_UNKNOWN ||
-                    (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
-                        r = fstatat_harder(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW, flags);
+                        r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
                         if (r < 0) {
-                                if (ret == 0 && r != -ENOENT)
-                                        ret = r;
-                                continue;
-                        }
+                                if (!IN_SET(r, -ENOTTY, -EINVAL))
+                                        return r;
 
-                        is_dir = S_ISDIR(st.st_mode);
-                } else
-                        is_dir = de->d_type == DT_DIR;
+                                /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */
+                        } else
+                                /* It was a subvolume, done. */
+                                return 1;
+                }
 
-                if (is_dir) {
-                        _cleanup_close_ int subdir_fd = -1;
+                subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+                if (subdir_fd < 0)
+                        return -errno;
 
-                        /* if root_dev is set, remove subdirectories only if device is same */
-                        if (root_dev && st.st_dev != root_dev->st_dev)
-                                continue;
+                /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type
+                 * again for each directory */
+                q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
 
-                        subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
-                        if (subdir_fd < 0) {
-                                if (ret == 0 && errno != ENOENT)
-                                        ret = -errno;
-                                continue;
-                        }
+                r = unlinkat_harder(fd, fname, AT_REMOVEDIR, flags);
+                if (r < 0)
+                        return r;
+                if (q < 0)
+                        return q;
 
-                        /* Stop at mount points */
-                        r = fd_is_mount_point(fd, de->d_name, 0);
-                        if (r < 0) {
-                                if (ret == 0 && r != -ENOENT)
-                                        ret = r;
+                return 1;
 
-                                continue;
-                        }
-                        if (r > 0)
-                                continue;
+        } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
+                r = unlinkat_harder(fd, fname, 0, flags);
+                if (r < 0)
+                        return r;
 
-                        if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
+                return 1;
+        }
 
-                                /* This could be a subvolume, try to remove it */
+        return 0;
+}
 
-                                r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
-                                if (r < 0) {
-                                        if (!IN_SET(r, -ENOTTY, -EINVAL)) {
-                                                if (ret == 0)
-                                                        ret = r;
+int rm_rf_children(
+                int fd,
+                RemoveFlags flags,
+                const struct stat *root_dev) {
 
-                                                continue;
-                                        }
+        _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
+        int ret = 0, r;
 
-                                        /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */
-                                } else
-                                        /* It was a subvolume, continue. */
-                                        continue;
-                        }
+        assert(fd >= 0);
+
+        /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
+         * fd, in all cases, including on failure. */
+
+        d = fdopendir(fd);
+        if (!d) {
+                safe_close(fd);
+                return -errno;
+        }
 
-                        /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file
-                         * system type again for each directory */
-                        r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
-                        if (r < 0 && ret == 0)
-                                ret = r;
+        if (!(flags & REMOVE_PHYSICAL)) {
+                struct statfs sfs;
 
-                        r = unlinkat_harder(fd, de->d_name, AT_REMOVEDIR, flags);
-                        if (r < 0 && r != -ENOENT && ret == 0)
-                                ret = r;
+                if (fstatfs(dirfd(d), &sfs) < 0)
+                        return -errno;
+
+                if (is_physical_fs(&sfs)) {
+                        /* We refuse to clean physical file systems with this call, unless explicitly
+                         * requested. This is extra paranoia just to be sure we never ever remove non-state
+                         * data. */
 
-                } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
+                        _cleanup_free_ char *path = NULL;
 
-                        r = unlinkat_harder(fd, de->d_name, 0, flags);
-                        if (r < 0 && r != -ENOENT && ret == 0)
-                                ret = r;
+                        (void) fd_get_path(fd, &path);
+                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+                                               "Attempted to remove disk file system under \"%s\", and we can't allow that.",
+                                               strna(path));
                 }
         }
+
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
+                int is_dir;
+
+                if (dot_or_dot_dot(de->d_name))
+                        continue;
+
+                is_dir =
+                        de->d_type == DT_UNKNOWN ? -1 :
+                        de->d_type == DT_DIR;
+
+                r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev);
+                if (r < 0 && r != -ENOENT && ret == 0)
+                        ret = r;
+        }
+
         return ret;
 }
 
 int rm_rf(const char *path, RemoveFlags flags) {
         int fd, r;
-        struct statfs s;
 
         assert(path);
 
@@ -284,9 +299,10 @@ int rm_rf(const char *path, RemoveFlags flags) {
                 if (FLAGS_SET(flags, REMOVE_ROOT)) {
 
                         if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
+                                struct statfs s;
+
                                 if (statfs(path, &s) < 0)
                                         return -errno;
-
                                 if (is_physical_fs(&s))
                                         return log_error_errno(SYNTHETIC_ERRNO(EPERM),
                                                                "Attempted to remove files from a disk file system under \"%s\", refusing.",
@@ -314,3 +330,22 @@ int rm_rf(const char *path, RemoveFlags flags) {
 
         return r;
 }
+
+int rm_rf_child(int fd, const char *name, RemoveFlags flags) {
+
+        /* Removes one specific child of the specified directory */
+
+        if (fd < 0)
+                return -EBADF;
+
+        if (!filename_is_valid(name))
+                return -EINVAL;
+
+        if ((flags & (REMOVE_ROOT|REMOVE_MISSING_OK)) != 0) /* Doesn't really make sense here, we are not supposed to remove 'fd' anyway */
+                return -EINVAL;
+
+        if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
+                return -EINVAL;
+
+        return rm_rf_children_inner(fd, name, -1, flags, NULL);
+}
index 40f0894c96dfa639dd4299ba52108c025ae03ed8..577a2795e0f34f7834128a21f64e7ca5af82aa99 100644 (file)
@@ -23,7 +23,8 @@ int fstatat_harder(int dfd,
                 int fstatat_flags,
                 RemoveFlags remove_flags);
 
-int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
+int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev);
+int rm_rf_child(int fd, const char *name, RemoveFlags flags);
 int rm_rf(const char *path, RemoveFlags flags);
 
 /* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
index cad0af89f263f549d295006c85ba639d44a3d968..631ca5dd34eb4c1168a89ade5942410bda21227e 100644 (file)
@@ -331,6 +331,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
                 "restart_syscall\0"
                 "rseq\0"
                 "rt_sigreturn\0"
+                "sched_getaffinity\0"
                 "sched_yield\0"
                 "set_robust_list\0"
                 "set_thread_area\0"
@@ -859,7 +860,6 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
                 "get_mempolicy\0"
                 "getcpu\0"
                 "getpriority\0"
-                "getrandom\0"
                 "ioctl\0"
                 "ioprio_get\0"
                 "kcmp\0"
@@ -875,7 +875,6 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = {
                 "remap_file_pages\0"
                 "sched_get_priority_max\0"
                 "sched_get_priority_min\0"
-                "sched_getaffinity\0"
                 "sched_getattr\0"
                 "sched_getparam\0"
                 "sched_getscheduler\0"
index e4890e8170dd1997d147c6aba5a2d5789caa96ad..34e78e6792a9b786bd624aabce75a40c359d09f2 100644 (file)
@@ -266,7 +266,6 @@ int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_pa
         assert(inside_path);
 
 #if HAVE_SELINUX
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         _cleanup_freecon_ char* fcon = NULL;
         struct stat st;
         int r;
@@ -292,8 +291,7 @@ int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_pa
                 goto fail;
         }
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        if (setfilecon_raw(procfs_path, fcon) < 0) {
+        if (setfilecon_raw(FORMAT_PROC_FD_PATH(fd), fcon) < 0) {
                 _cleanup_freecon_ char *oldcon = NULL;
 
                 /* If the FS doesn't support labels, then exit without warning */
@@ -307,7 +305,7 @@ int mac_selinux_fix_container_fd(int fd, const char *path, const char *inside_pa
                 r = -errno;
 
                 /* If the old label is identical to the new one, suppress any kind of error */
-                if (getfilecon_raw(procfs_path, &oldcon) >= 0 && streq(fcon, oldcon))
+                if (getfilecon_raw(FORMAT_PROC_FD_PATH(fd), &oldcon) >= 0 && streq(fcon, oldcon))
                         return 0;
 
                 goto fail;
index 092be6ee7f620ba062b41ae99a66a280b4992c2c..99b1108b7721221f5b6297ff06711618b79ceaed 100644 (file)
@@ -76,7 +76,7 @@ int service_parse_argv(
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unknown option code.");
+                        assert_not_reached();
                 }
 
         if (optind < argc)
index 3362ee3924b04950fb4d1d366bc0ff1237352b57..2ae11e7f9a7dcffac34aa3bdf4d9aa3ad26a0fc0 100644 (file)
@@ -121,8 +121,7 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
         return r;
 }
 
-static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+static int smack_fix_fd(int fd, const char *abspath, LabelFixFlags flags) {
         const char *label;
         struct stat st;
         int r;
@@ -153,8 +152,7 @@ static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) {
         else
                 return 0;
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        if (setxattr(procfs_path, "security.SMACK64", label, strlen(label), 0) < 0) {
+        if (setxattr(FORMAT_PROC_FD_PATH(fd), "security.SMACK64", label, strlen(label), 0) < 0) {
                 _cleanup_free_ char *old_label = NULL;
 
                 r = -errno;
@@ -168,7 +166,7 @@ static int smack_fix_fd(int fd , const char *abspath, LabelFixFlags flags) {
                         return 0;
 
                 /* If the old label is identical to the new one, suppress any kind of error */
-                if (getxattr_malloc(procfs_path, "security.SMACK64", &old_label, false) >= 0 &&
+                if (getxattr_malloc(FORMAT_PROC_FD_PATH(fd), "security.SMACK64", &old_label, false) >= 0 &&
                     streq(old_label, label))
                         return 0;
 
index 0c044378265f06c45dcfd5a584d0a2984766f731..494047a5d159d21f8218fd1c7dc54d920bc4cb91 100644 (file)
@@ -129,7 +129,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                                         .size = sizeof(struct sockaddr_in6),
                                 };
                         else
-                                assert_not_reached("Family quarrel");
+                                assert_not_reached();
                 }
         }
 
index ab7d799029654a66f43984c45aa78954845d2a45..175b6d5499c83ed4a6b454ac9e79808436dcb268 100644 (file)
@@ -160,7 +160,7 @@ bool have_namespaces(void) {
         if (si.si_status == EXIT_FAILURE)
                 return false;
 
-        assert_not_reached("unexpected exit code");
+        assert_not_reached();
 }
 
 bool can_memlock(void) {
index 79e879b482477b2051430ee11b3b70b4c6aa7f05..793a54a449d256f779f1d6b750429abbf4350505 100644 (file)
@@ -25,6 +25,7 @@ TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR primaryHand
 void (*sym_Esys_Finalize)(ESYS_CONTEXT **context) = NULL;
 TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle) = NULL;
 void (*sym_Esys_Free)(void *ptr) = NULL;
+TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData);
 TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes) = NULL;
 TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context,  TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL;
 TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle) = NULL;
@@ -51,6 +52,7 @@ int dlopen_tpm2(void) {
                         DLSYM_ARG(Esys_Finalize),
                         DLSYM_ARG(Esys_FlushContext),
                         DLSYM_ARG(Esys_Free),
+                        DLSYM_ARG(Esys_GetCapability),
                         DLSYM_ARG(Esys_GetRandom),
                         DLSYM_ARG(Esys_Initialize),
                         DLSYM_ARG(Esys_Load),
@@ -310,11 +312,93 @@ static int tpm2_make_primary(
         return 0;
 }
 
+static int tpm2_get_best_pcr_bank(
+                ESYS_CONTEXT *c,
+                TPMI_ALG_HASH *ret) {
+
+        _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
+        TPMI_ALG_HASH hash = TPM2_ALG_SHA1;
+        bool found = false;
+        TPMI_YES_NO more;
+        TSS2_RC rc;
+
+        rc = sym_Esys_GetCapability(
+                        c,
+                        ESYS_TR_NONE,
+                        ESYS_TR_NONE,
+                        ESYS_TR_NONE,
+                        TPM2_CAP_PCRS,
+                        0,
+                        1,
+                        &more,
+                        &pcap);
+        if (rc != TSS2_RC_SUCCESS)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+                                       "Failed to determine TPM2 PCR bank capabilities: %s", sym_Tss2_RC_Decode(rc));
+
+        assert(pcap->capability == TPM2_CAP_PCRS);
+
+        for (size_t i = 0; i < pcap->data.assignedPCR.count; i++) {
+                bool valid = true;
+
+                /* As per
+                 * https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a
+                 * TPM2 on a Client PC must have at least 24 PCRs. If this TPM has less, just skip over
+                 * it. */
+                if (pcap->data.assignedPCR.pcrSelections[i].sizeofSelect < TPM2_PCRS_MAX/8) {
+                        log_debug("Skipping TPM2 PCR bank %s with fewer than 24 PCRs.",
+                                  strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
+                        continue;
+                }
+
+                assert_cc(TPM2_PCRS_MAX % 8 == 0);
+
+                /* It's not enough to check how many PCRs there are, we also need to check that the 24 are
+                 * enabled for this bank. Otherwise this TPM doesn't qualify. */
+                for (size_t j = 0; j < TPM2_PCRS_MAX/8; j++)
+                        if (pcap->data.assignedPCR.pcrSelections[i].pcrSelect[j] != 0xFF) {
+                                valid = false;
+                                break;
+                        }
+
+                if (!valid) {
+                        log_debug("TPM2 PCR bank %s has fewer than 24 PCR bits enabled, ignoring.",
+                                  strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
+                        continue;
+                }
+
+                if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA256) {
+                        hash = TPM2_ALG_SHA256;
+                        found = true;
+                        break;
+                }
+
+                if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA1)
+                        found = true;
+        }
+
+        if (!found)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "TPM2 module supports neither SHA1 nor SHA256 PCR banks, cannot operate.");
+
+        if (hash == TPM2_ALG_SHA256)
+                log_debug("TPM2 device supports SHA256 PCR banks, yay!");
+        else {
+                assert(hash == TPM2_ALG_SHA1);
+                log_debug("TPM2 device lacks support for SHA256 PCR banks, falling back to SHA1 banks.");
+        }
+
+        *ret = hash;
+        return 0;
+}
+
 static int tpm2_make_pcr_session(
                 ESYS_CONTEXT *c,
                 uint32_t pcr_mask,
+                uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
                 ESYS_TR *ret_session,
-                TPM2B_DIGEST **ret_policy_digest) {
+                TPM2B_DIGEST **ret_policy_digest,
+                TPMI_ALG_HASH *ret_pcr_bank) {
 
         static const TPMT_SYM_DEF symmetric = {
                 .algorithm = TPM2_ALG_AES,
@@ -327,7 +411,7 @@ static int tpm2_make_pcr_session(
         };
         TPML_PCR_SELECTION pcr_selection = {
                 .count = 1,
-                .pcrSelections[0].hash = TPM2_ALG_SHA256,
+                .pcrSelections[0].hash = TPM2_ALG_SHA256, /* overridden below, depending on TPM2 capabilities */
                 .pcrSelections[0].sizeofSelect = 3,
                 .pcrSelections[0].pcrSelect[0] = pcr_mask & 0xFF,
                 .pcrSelections[0].pcrSelect[1] = (pcr_mask >> 8) & 0xFF,
@@ -342,6 +426,16 @@ static int tpm2_make_pcr_session(
 
         log_debug("Starting authentication session.");
 
+        if (pcr_bank != UINT16_MAX)
+                pcr_selection.pcrSelections[0].hash = pcr_bank;
+        else {
+                /* No bank configured, pick automatically. Some TPM2 devices only can do SHA1. If we detect
+                 * that use that, but preferably use SHA256 */
+                r = tpm2_get_best_pcr_bank(c, &pcr_selection.pcrSelections[0].hash);
+                if (r < 0)
+                        return r;
+        }
+
         rc = sym_Esys_StartAuthSession(
                         c,
                         ESYS_TR_NONE,
@@ -412,6 +506,9 @@ static int tpm2_make_pcr_session(
         if (ret_policy_digest)
                 *ret_policy_digest = TAKE_PTR(policy_digest);
 
+        if (ret_pcr_bank)
+                *ret_pcr_bank = pcr_selection.pcrSelections[0].hash;
+
         r = 0;
 
 finish:
@@ -427,7 +524,8 @@ int tpm2_seal(
                 void **ret_blob,
                 size_t *ret_blob_size,
                 void **ret_pcr_hash,
-                size_t *ret_pcr_hash_size) {
+                size_t *ret_pcr_hash_size,
+                uint16_t *ret_pcr_bank) {
 
         _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
         _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
@@ -439,6 +537,7 @@ int tpm2_seal(
         TPM2B_SENSITIVE_CREATE hmac_sensitive;
         ESYS_TR primary = ESYS_TR_NONE;
         TPM2B_PUBLIC hmac_template;
+        TPMI_ALG_HASH pcr_bank;
         size_t k, blob_size;
         usec_t start;
         TSS2_RC rc;
@@ -450,6 +549,7 @@ int tpm2_seal(
         assert(ret_blob_size);
         assert(ret_pcr_hash);
         assert(ret_pcr_hash_size);
+        assert(ret_pcr_bank);
 
         assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
 
@@ -478,7 +578,7 @@ int tpm2_seal(
         if (r < 0)
                 return r;
 
-        r = tpm2_make_pcr_session(c.esys_context, pcr_mask, NULL, &policy_digest);
+        r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, NULL, &policy_digest, &pcr_bank);
         if (r < 0)
                 goto finish;
 
@@ -600,6 +700,7 @@ int tpm2_seal(
         *ret_blob_size = blob_size;
         *ret_pcr_hash = TAKE_PTR(hash);
         *ret_pcr_hash_size = policy_digest->size;
+        *ret_pcr_bank = pcr_bank;
 
         r = 0;
 
@@ -611,6 +712,7 @@ finish:
 int tpm2_unseal(
                 const char *device,
                 uint32_t pcr_mask,
+                uint16_t pcr_bank,
                 const void *blob,
                 size_t blob_size,
                 const void *known_policy_hash,
@@ -670,7 +772,7 @@ int tpm2_unseal(
         if (r < 0)
                 return r;
 
-        r = tpm2_make_pcr_session(c.esys_context, pcr_mask, &session, &policy_digest);
+        r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, &session, &policy_digest, NULL);
         if (r < 0)
                 goto finish;
 
@@ -909,6 +1011,7 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
 int tpm2_make_luks2_json(
                 int keyslot,
                 uint32_t pcr_mask,
+                uint16_t pcr_bank,
                 const void *blob,
                 size_t blob_size,
                 const void *policy_hash,
@@ -951,6 +1054,7 @@ int tpm2_make_luks2_json(
                                        JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
                                        JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)),
                                        JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(a)),
+                                       JSON_BUILD_PAIR_CONDITION(!!tpm2_pcr_bank_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_pcr_bank_to_string(pcr_bank))),
                                        JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size))));
         if (r < 0)
                 return r;
@@ -960,3 +1064,36 @@ int tpm2_make_luks2_json(
 
         return keyslot;
 }
+
+/* We want the helpers below to work also if TPM2 libs are not available, hence define these two defines if
+ * they are missing. */
+#ifndef TPM2_ALG_SHA256
+#define TPM2_ALG_SHA256 0xB
+#endif
+
+#ifndef TPM2_ALG_SHA1
+#define TPM2_ALG_SHA1 0x4
+#endif
+
+int tpm2_pcr_bank_supported(uint16_t bank) {
+        /* For now, let's officially only support these two. We can extend this later on, should the need
+         * arise. */
+        return IN_SET(bank, TPM2_ALG_SHA256, TPM2_ALG_SHA1);
+}
+
+const char *tpm2_pcr_bank_to_string(uint16_t bank) {
+        /* Similar here, only support the two for now, we can always extend this later.  */
+        if (bank == TPM2_ALG_SHA256)
+                return "sha256";
+        if (bank == TPM2_ALG_SHA1)
+                return "sha1";
+        return NULL;
+}
+
+int tpm2_pcr_bank_from_string(const char *bank) {
+        if (streq_ptr(bank, "sha256"))
+                return TPM2_ALG_SHA256;
+        if (streq_ptr(bank, "sha1"))
+                return TPM2_ALG_SHA1;
+        return -EINVAL;
+}
index 9f60fef083df96819f2b909aeae5d4c030326b8b..8032afe7b000ea4d64e480f47d1a59bd12ecc68e 100644 (file)
@@ -15,6 +15,7 @@ extern TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR prim
 extern void (*sym_Esys_Finalize)(ESYS_CONTEXT **context);
 extern TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle);
 extern void (*sym_Esys_Free)(void *ptr);
+extern TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData);
 extern TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes);
 extern TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context,  TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion);
 extern TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle);
@@ -33,8 +34,8 @@ extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], siz
 
 int dlopen_tpm2(void);
 
-int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size);
-int tpm2_unseal(const char *device, uint32_t pcr_mask, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size);
+int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank);
+int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size);
 
 #endif
 
@@ -43,13 +44,17 @@ int tpm2_find_device_auto(int log_level, char **ret);
 
 int tpm2_parse_pcrs(const char *s, uint32_t *ret);
 
-int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret);
+int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret);
 
 #define TPM2_PCRS_MAX 24
 
 /* Default to PCR 7 only */
 #define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7)
 
+int tpm2_pcr_bank_supported(uint16_t bank);
+const char *tpm2_pcr_bank_to_string(uint16_t bank);
+int tpm2_pcr_bank_from_string(const char *bank);
+
 typedef struct {
         uint32_t search_pcr_mask;
         const char *device;
index 17460ceaf6746a034229bd0956300633652e0bcb..9bd6a038875c9cefbe5016b95afcae99634cd832 100644 (file)
@@ -1127,7 +1127,6 @@ static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatch
                 {},
         };
 
-        char smid[SD_ID128_STRING_MAX];
         JsonVariant *m;
         sd_id128_t mid;
         int r;
@@ -1142,7 +1141,7 @@ static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatch
         if (r < 0)
                 return json_log(variant, flags, r, "Failed to determine machine ID: %m");
 
-        m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+        m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
         if (!m)
                 return 0;
 
@@ -1368,7 +1367,6 @@ static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchF
                 {},
         };
 
-        char smid[SD_ID128_STRING_MAX];
         JsonVariant *m;
         sd_id128_t mid;
         int r;
@@ -1383,7 +1381,7 @@ static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchF
         if (r < 0)
                 return json_log(variant, flags, r, "Failed to determine machine ID: %m");
 
-        m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
+        m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
         if (!m)
                 return 0;
 
index 91ac7c38320accc8efab467d44d51ca182730350..555c71c82da80d7baab559d4f68fe1eed19e2173 100644 (file)
@@ -90,7 +90,7 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
                 break;
 
         default:
-                assert_not_reached("Unexpected state?");
+                assert_not_reached();
         }
 
         sd_event_unref(iterator->event);
@@ -333,7 +333,7 @@ static int userdb_on_query_reply(
         }
 
         default:
-                assert_not_reached("unexpected lookup");
+                assert_not_reached();
         }
 
 finish:
index b31cdd679ba56ab2c7e4be1c9a8e8f753f11a683..fee5bd1c95fbdedcc8ae04ce4b04209415a90e00 100644 (file)
@@ -215,13 +215,14 @@ int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line
 }
 
 int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
+        _cleanup_(utxent_cleanup) bool utmpx = false;
         struct utmpx lookup = {
                 .ut_type = INIT_PROCESS /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
         }, store, store_wtmp, *found;
 
         assert(id);
 
-        setutxent();
+        utmpx = utxent_start();
 
         /* Copy the whole string if it fits, or just the suffix without the terminating NUL. */
         copy_suffix(store.ut_id, sizeof(store.ut_id), id);
@@ -339,6 +340,7 @@ int utmp_wall(
         bool (*match_tty)(const char *tty, void *userdata),
         void *userdata) {
 
+        _cleanup_(utxent_cleanup) bool utmpx = false;
         _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL;
         struct utmpx *u;
         int r;
@@ -367,7 +369,7 @@ int utmp_wall(
                      message) < 0)
                 return -ENOMEM;
 
-        setutxent();
+        utmpx = utxent_start();
 
         r = 0;
 
index 3a53c16a724249338bfb6f25932a2ec820ffc30c..07a1b96f6013189536b3598ef12505204f07154b 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <malloc.h>
-#include <sys/poll.h>
+#include <poll.h>
 
 #include "alloc-util.h"
 #include "errno-util.h"
@@ -905,7 +905,7 @@ static int varlink_dispatch_method(Varlink *v) {
                 break;
 
         default:
-                assert_not_reached("Unexpected state");
+                assert_not_reached();
 
         }
 
@@ -1521,7 +1521,7 @@ int varlink_call(
                 return varlink_log_errno(v, SYNTHETIC_ERRNO(ETIME), "Connection timed out.");
 
         default:
-                assert_not_reached("Unexpected state after method call.");
+                assert_not_reached();
         }
 }
 
index 82cd5fbd6bb245686c33ce0827d2f77843da5b32..39a300f5c96b1cf73ad76daf6b2638311c0fef7a 100644 (file)
@@ -36,16 +36,29 @@ bool http_url_is_valid(const char *url) {
         return ascii_is_valid(p);
 }
 
+bool file_url_is_valid(const char *url) {
+        const char *p;
+
+        if (isempty(url))
+                return false;
+
+        p = startswith(url, "file:/");
+        if (isempty(p))
+                return false;
+
+        return ascii_is_valid(p);
+}
+
 bool documentation_url_is_valid(const char *url) {
         const char *p;
 
         if (isempty(url))
                 return false;
 
-        if (http_url_is_valid(url))
+        if (http_url_is_valid(url) || file_url_is_valid(url))
                 return true;
 
-        p = STARTSWITH_SET(url, "file:/", "info:", "man:");
+        p = STARTSWITH_SET(url, "info:", "man:");
         if (isempty(p))
                 return false;
 
index ec54669f50131e4be05e6d79dd62b45ac638da71..88b4897be0599afb7100a1966e59d1aae6cb7c9f 100644 (file)
@@ -6,6 +6,7 @@
 #include "macro.h"
 
 bool http_url_is_valid(const char *url) _pure_;
+bool file_url_is_valid(const char *url) _pure_;
 
 bool documentation_url_is_valid(const char *url) _pure_;
 
index 8ff3feafe40790a436b1f9fa7fc69b44f43cbefb..df381d85b8b9f7eaef6b496d197cf3fea7385424 100644 (file)
@@ -233,5 +233,5 @@ int xml_tokenize(const char **p, char **name, void **state, unsigned *line) {
 
         }
 
-        assert_not_reached("Bad state");
+        assert_not_reached();
 }
index a98cfc4d8ae22c73de34e20f3cf2426711ae9425..2ba1a21dae943ad833ca93844e43acac08157c3c 100644 (file)
@@ -149,7 +149,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option code.");
+                        assert_not_reached();
                 }
 
         if (!arg_verb)
@@ -610,7 +610,7 @@ int main(int argc, char *argv[]) {
                 break;
 
         default:
-                assert_not_reached("Unknown magic");
+                assert_not_reached();
         }
 
         (void) reboot(cmd);
index 19c99db1b41fd60a313dee3fda7c915ad7f33130..7064f3a90594bce963a8eab0721369a57d855744 100644 (file)
@@ -365,7 +365,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (argc - optind != 1)
index 6e3ee0d76b00153d51476dfde6016b95af7d2fcc..be0be91f3e46f05b9a0b48b77d777c7541fe5c24 100644 (file)
@@ -657,7 +657,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind >= argc)
index 458a91be29c8e21c901c068dd4e0ff0a08c30544..8c452854b12de5c06cdb45a6bb4466c1270b594b 100644 (file)
@@ -369,7 +369,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_cat_config && argc > optind)
index 572e4007fe27ff0072157520c70c823fbf337790..1e9279d612dd648028e1861190c0a5e62640d3c7 100644 (file)
@@ -566,7 +566,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
                         break;
                 }
                 default:
-                        assert_not_reached("Unsupported image type");
+                        assert_not_reached();
                 }
 
                 r = validate_version(
@@ -957,7 +957,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index 66b48eee857d33f6b656f91cb0e1c52470db46af..eed8c6714484b08473856853fcb52fab7e1f9f5e 100644 (file)
@@ -50,12 +50,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         else
                 log_info(r == 0 ? "Done!" : "Action!");
 
-        if (orig_stdout_fd >= 0) {
-                char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
-
-                xsprintf(path, "/proc/self/fd/%d", orig_stdout_fd);
-                assert_se(freopen(path, "w", stdout));
-        }
+        if (orig_stdout_fd >= 0)
+                assert_se(freopen(FORMAT_PROC_FD_PATH(orig_stdout_fd), "w", stdout));
 
         release_busses(); /* We open the bus for communication with logind.
                            * It needs to be closed to avoid apparent leaks. */
index 2a0290de7f459d90aa75748dbaec5dfba036b460..ba385ea2a2737f831d003cba5fa5fdd8092e2384 100644 (file)
@@ -34,7 +34,7 @@ int add_dependency(int argc, char *argv[], void *userdata) {
         else if (streq(verb, "add-requires"))
                 dep = UNIT_REQUIRES;
         else
-                assert_not_reached("Unknown verb");
+                assert_not_reached();
 
         if (install_client_side()) {
                 r = unit_file_add_dependency(arg_scope, unit_file_flags_from_args(), arg_root, names, target, dep, &changes, &n_changes);
index eca3a6d35490e2d1a8b43b02a74a4d164a1abf90..fb4d6435171bb75aa60e58160111e37e1927cc8c 100644 (file)
@@ -44,7 +44,7 @@ int clean_or_freeze_unit(int argc, char *argv[], void *userdata) {
         else if (streq(argv[0], "thaw"))
                 method = "ThawUnit";
         else
-                assert_not_reached("Unhandled method");
+                assert_not_reached();
 
         STRV_FOREACH(name, names) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
index 51a89ae4a737db7e8c6d9333408dc422a8c87b66..760758322f5671861f9da803a87e6fb16542c242 100644 (file)
@@ -127,7 +127,7 @@ int halt_parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
index ad6325bf35f364e3b1bd989b8409a6038e426351..04b6b7630b95311815a58e77d261fd530a552b95 100644 (file)
@@ -55,7 +55,7 @@ int runlevel_parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind < argc)
index 11154f5f8b261858a409bb62e533432619f0c90e..5e613e2aa20ba79792d00e0aa1d7d21b8b0b4ec1 100644 (file)
@@ -112,7 +112,7 @@ int shutdown_parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) {
index f0e9ca8d7948c922d53b869ffca424cb4cca98bb..c81e9bc3ce33a0cd6b424fd5addbb2cd7ed8588b 100644 (file)
@@ -93,7 +93,7 @@ int telinit_parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind >= argc)
index 03ba90838329f73c7309a365ccc61adef89cff6a..1c2331533795c8308a4938685abf20f2894bd76f 100644 (file)
@@ -35,7 +35,7 @@ int daemon_reload(int argc, char *argv[], void *userdata) {
                 break;
 
         default:
-                assert_not_reached("Unexpected action");
+                assert_not_reached();
         }
 
         r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
index 8f053ca649c71eb08c422b6df7a62fce0a602e9d..dcbe2c730218d6b709fc55847207cb780cb3a896 100644 (file)
@@ -124,7 +124,7 @@ int enable_unit(int argc, char *argv[], void *userdata) {
                 else if (streq(verb, "revert"))
                         r = unit_file_revert(arg_scope, arg_root, names, &changes, &n_changes);
                 else
-                        assert_not_reached("Unknown verb");
+                        assert_not_reached();
 
                 unit_file_dump_changes(r, verb, changes, n_changes, arg_quiet);
                 if (r < 0)
@@ -191,7 +191,7 @@ int enable_unit(int argc, char *argv[], void *userdata) {
                         method = "RevertUnitFiles";
                         send_runtime = send_force = false;
                 } else
-                        assert_not_reached("Unknown verb");
+                        assert_not_reached();
 
                 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
                 if (r < 0)
index 183a7b6a8ad650182dad5ce7f8b0246385149c58..5739bac070a2f98f9e07ce710db18a0087a1ed70 100644 (file)
@@ -6,33 +6,20 @@
 #include "systemctl-util.h"
 #include "systemctl.h"
 
-int set_property(int argc, char *argv[], void *userdata) {
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+static int set_property_one(sd_bus *bus, const char *name, char **properties) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_free_ char *n = NULL;
-        UnitType t;
-        sd_bus *bus;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         int r;
 
-        r = acquire_bus(BUS_MANAGER, &bus);
-        if (r < 0)
-                return r;
-
-        polkit_agent_open_maybe();
-
         r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "SetUnitProperties");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = unit_name_mangle(argv[1], arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, &n);
-        if (r < 0)
-                return log_error_errno(r, "Failed to mangle unit name: %m");
-
-        t = unit_name_to_type(n);
+        UnitType t = unit_name_to_type(name);
         if (t < 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid unit type: %s", n);
+                return log_error_errno(t, "Invalid unit type: %s", name);
 
-        r = sd_bus_message_append(m, "sb", n, arg_runtime);
+        r = sd_bus_message_append(m, "sb", name, arg_runtime);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -40,7 +27,7 @@ int set_property(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = bus_append_unit_property_assignment_many(m, t, strv_skip(argv, 2));
+        r = bus_append_unit_property_assignment_many(m, t, properties);
         if (r < 0)
                 return r;
 
@@ -50,7 +37,33 @@ int set_property(int argc, char *argv[], void *userdata) {
 
         r = sd_bus_call(bus, m, 0, &error, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to set unit properties on %s: %s", n, bus_error_message(&error, r));
+                return log_error_errno(r, "Failed to set unit properties on %s: %s",
+                                       name, bus_error_message(&error, r));
 
         return 0;
 }
+
+int set_property(int argc, char *argv[], void *userdata) {
+        sd_bus *bus;
+        _cleanup_strv_free_ char **names = NULL;
+        char **name;
+        int r, k;
+
+        r = acquire_bus(BUS_MANAGER, &bus);
+        if (r < 0)
+                return r;
+
+        polkit_agent_open_maybe();
+
+        r = expand_unit_names(bus, STRV_MAKE(argv[1]), NULL, &names, NULL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to expand '%s' into names: %m", argv[1]);
+
+        r = 0;
+        STRV_FOREACH(name, names) {
+                k = set_property_one(bus, *name, strv_skip(argv, 2));
+                if (k < 0 && r >= 0)
+                        r = k;
+        }
+        return r;
+}
index b325d44c5ea85080c0121bca183828d4bb5157dc..d008c4172abaac1f5fb0617b71b9db030c238203 100644 (file)
@@ -1023,7 +1023,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
 
                         return 1;
 
-                } else if (STR_IN_SET(name, "SystemCallFilter", "SystemCallLog", "RestrictAddressFamilies")) {
+                } else if (STR_IN_SET(name, "SystemCallFilter", "SystemCallLog", "RestrictAddressFamilies", "RestrictNetworkInterfaces")) {
                         _cleanup_strv_free_ char **l = NULL;
                         int allow_list;
 
index 99f1394bcfa631382ad73fe0e2c7640e8bc71bca..9f3174d106d21bac0bc67a0d761c24fc6547ec10 100644 (file)
@@ -930,6 +930,6 @@ int halt_now(enum action a) {
                                              (arg_dry_run ? REBOOT_DRY_RUN : 0));
 
         default:
-                assert_not_reached("Unknown action.");
+                assert_not_reached();
         }
 }
index 4cc723aab5e49394af9855be36044a21542e8d7e..f5ecc1f60f37e97f372b1f162b4a67205e871710 100644 (file)
@@ -922,7 +922,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_transport == BUS_TRANSPORT_REMOTE && arg_scope != UNIT_FILE_SYSTEM)
@@ -1184,7 +1184,7 @@ static int run(int argc, char *argv[]) {
 
         case _ACTION_INVALID:
         default:
-                assert_not_reached("Unknown action");
+                assert_not_reached();
         }
 
 finish:
index e01281f4547b09215752ebe89f66f22e68ec1d01..6cae6a5767c3ee794ceb70d3f0226a30b3522abf 100644 (file)
@@ -77,7 +77,7 @@ foreach header : _systemd_headers + _not_installed_headers + [libudev_h_path]
                              check_compilation_sh,
                              args : cc.cmd_array() + ['-c', '-x'] + opt +
                                     ['-Werror', '-include',
-                                     join_paths(meson.current_source_dir(), header)])
+                                     meson.current_source_dir() / header])
                 endif
         endforeach
 endforeach
index 35c942b16cc6c3c77f8f0f36f40ab9fef6d51720..8b316fac5ac61e07c13f822024eb5c5cc4b76f15 100644 (file)
@@ -76,7 +76,10 @@ struct sd_bus_vtable {
                         const unsigned *vtable_format_reference;
                 } start;
                 struct {
-                        size_t reserved;
+                        /* This field exists only to make sure we have something to initialize in
+                         * SD_BUS_VTABLE_END in a way that is both compatible with pedantic versions of C and
+                         * C++. It's unused otherwise. */
+                        size_t _reserved;
                 } end;
                 struct {
                         const char *member;
@@ -106,11 +109,11 @@ struct sd_bus_vtable {
                 .type = _SD_BUS_VTABLE_START,                           \
                 .flags = _flags,                                        \
                 .x = {                                                  \
-                    .start = {                                          \
-                        .element_size = sizeof(sd_bus_vtable),          \
-                        .features = _SD_BUS_VTABLE_PARAM_NAMES,         \
-                        .vtable_format_reference = &sd_bus_object_vtable_format, \
-                    },                                                  \
+                        .start = {                                      \
+                                .element_size = sizeof(sd_bus_vtable),  \
+                                .features = _SD_BUS_VTABLE_PARAM_NAMES, \
+                                .vtable_format_reference = &sd_bus_object_vtable_format, \
+                        },                                              \
                 },                                                      \
         }
 
@@ -122,14 +125,14 @@ struct sd_bus_vtable {
                 .type = _SD_BUS_VTABLE_METHOD,                          \
                 .flags = _flags,                                        \
                 .x = {                                                  \
-                    .method = {                                         \
-                        .member = _member,                              \
-                        .signature = _signature,                        \
-                        .result = _result,                              \
-                        .handler = _handler,                            \
-                        .offset = _offset,                              \
-                        .names = _in_names _out_names,                  \
-                    },                                                  \
+                        .method = {                                     \
+                                .member = _member,                      \
+                                .signature = _signature,                \
+                                .result = _result,                      \
+                                .handler = _handler,                    \
+                                .offset = _offset,                      \
+                                .names = _in_names _out_names,          \
+                        },                                              \
                 },                                                      \
         }
 #define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags)   \
@@ -144,14 +147,14 @@ struct sd_bus_vtable {
                 .type = _SD_BUS_VTABLE_SIGNAL,                          \
                 .flags = _flags,                                        \
                 .x = {                                                  \
-                    .signal = {                                         \
-                        .member = _member,                              \
-                        .signature = _signature,                        \
-                        .names = _out_names,                            \
-                    },                                                  \
+                        .signal = {                                     \
+                                .member = _member,                      \
+                                .signature = _signature,                \
+                                .names = _out_names,                    \
+                        },                                              \
                 },                                                      \
-        }
-#define SD_BUS_SIGNAL(_member, _signature, _flags)   \
+                        }
+#define SD_BUS_SIGNAL(_member, _signature, _flags)                      \
         SD_BUS_SIGNAL_WITH_NAMES(_member, _signature, "", _flags)
 
 #define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags)     \
@@ -159,13 +162,13 @@ struct sd_bus_vtable {
                 .type = _SD_BUS_VTABLE_PROPERTY,                        \
                 .flags = _flags,                                        \
                 .x = {                                                  \
-                    .property = {                                       \
-                        .member = _member,                              \
-                        .signature = _signature,                        \
-                        .get = _get,                                    \
-                        .set = NULL,                                    \
-                        .offset = _offset,                              \
-                    },                                                  \
+                        .property = {                                   \
+                                .member = _member,                      \
+                                .signature = _signature,                \
+                                .get = _get,                            \
+                                .set = NULL,                            \
+                                .offset = _offset,                      \
+                        },                                              \
                 },                                                      \
         }
 
@@ -174,13 +177,13 @@ struct sd_bus_vtable {
                 .type = _SD_BUS_VTABLE_WRITABLE_PROPERTY,               \
                 .flags = _flags,                                        \
                 .x = {                                                  \
-                    .property = {                                       \
-                        .member = _member,                              \
-                        .signature = _signature,                        \
-                        .get = _get,                                    \
-                        .set = _set,                                    \
-                        .offset = _offset,                              \
-                    },                                                  \
+                        .property = {                                   \
+                                .member = _member,                      \
+                                .signature = _signature,                \
+                                .get = _get,                            \
+                                .set = _set,                            \
+                                .offset = _offset,                      \
+                        },                                              \
                 },                                                      \
         }
 
@@ -189,9 +192,9 @@ struct sd_bus_vtable {
                 .type = _SD_BUS_VTABLE_END,                             \
                 .flags = 0,                                             \
                 .x = {                                                  \
-                    .end = {                                            \
-                        .reserved = 0,                                  \
-                    },                                                  \
+                        .end = {                                        \
+                                ._reserved = 0,                         \
+                        },                                              \
                 },                                                      \
         }
 
index f2dd7357f863dcfa9085e4b057d208f99f6238f9..e64f3200a3b56dab29311d01ee8e7208be1e84ab 100644 (file)
@@ -34,11 +34,13 @@ union sd_id128 {
         uint64_t qwords[2];
 };
 
-#define SD_ID128_STRING_MAX 33
+#define SD_ID128_STRING_MAX 33U
 
 char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
 int sd_id128_from_string(const char *s, sd_id128_t *ret);
 
+#define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {})
+
 int sd_id128_randomize(sd_id128_t *ret);
 
 int sd_id128_get_machine(sd_id128_t *ret);
index eb285efe94dfeed01926faf7b489db123a80559d..b9445cf0a9dfbf01922e61f4c9fc36e2f6551336 100644 (file)
@@ -81,6 +81,9 @@ _SD_BEGIN_DECLARATIONS;
 #define SD_MESSAGE_SHUTDOWN               SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
 #define SD_MESSAGE_SHUTDOWN_STR           SD_ID128_MAKE_STR(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
 
+#define SD_MESSAGE_FACTORY_RESET          SD_ID128_MAKE(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c)
+#define SD_MESSAGE_FACTORY_RESET_STR      SD_ID128_MAKE_STR(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c)
+
 /* The messages below are actually about jobs, not really about units, the macros are misleadingly named. Moreover
  * SD_MESSAGE_UNIT_FAILED is not actually about a failing unit but about a failed start job. A job either finishes with
  * SD_MESSAGE_UNIT_STARTED or with SD_MESSAGE_UNIT_FAILED hence. */
index a18634d2549d60e457de1ceed0b648389ab3947b..7daebdbe554feaa74280729797d39ccfdee9790a 100644 (file)
@@ -1345,7 +1345,7 @@ static int process_item(Item *i) {
                 return add_group(i);
 
         default:
-                assert_not_reached("Unknown item type");
+                assert_not_reached();
         }
 }
 
@@ -1917,7 +1917,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_replace && arg_cat_config)
index 14725248cefb11698a5cc6833cff3fd6781b2a71..46b497672fb8e746324557f824bd2269dfa9ad73 100644 (file)
@@ -15,7 +15,7 @@ path = run_command(sh, '-c', 'echo "$PATH"').stdout().strip()
 test_env = environment()
 test_env.set('SYSTEMD_KBD_MODEL_MAP', kbd_model_map)
 test_env.set('SYSTEMD_LANGUAGE_FALLBACK_MAP', language_fallback_map)
-test_env.set('PATH', '@0@:@1@'.format(meson.build_root(), path))
+test_env.set('PATH', project_build_root + ':' + path)
 
 ############################################################
 
@@ -188,6 +188,8 @@ tests += [
 
         [['src/test/test-util.c']],
 
+        [['src/test/test-macro.c']],
+
         [['src/test/test-json.c']],
 
         [['src/test/test-modhex.c']],
@@ -215,6 +217,8 @@ tests += [
 
         [['src/test/test-fs-util.c']],
 
+        [['src/test/test-install-file.c']],
+
         [['src/test/test-umask-util.c']],
 
         [['src/test/test-proc-cmdline.c']],
@@ -250,6 +254,8 @@ tests += [
 
         [['src/test/test-sysctl-util.c']],
 
+        [['src/test/test-import-util.c']],
+
         [['src/test/test-user-record.c']],
 
         [['src/test/test-user-util.c']],
index fc1d724a2f26ee685679db46b0fc9267a5b873c8..20643f8aa5a2cabaafb94169d3e9261aa5fba057 100644 (file)
@@ -20,7 +20,7 @@ const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_l
         case NSS_STATUS_RETURN:
                 return "NSS_STATUS_RETURN";
         default:
-                snprintf(buf, buf_len, "%i", status);
+                (void) snprintf(buf, buf_len, "%i", status);
                 return buf;
         }
 };
index 42922a35007da5d1e3a77c1623d5ecf531816e96..655b823c7fbf646d0b29aeaf3c063186b2023fd8 100644 (file)
@@ -46,7 +46,7 @@ int main(int argc, const char *argv[]) {
         bitmap_unset(b, 32);
 
         BITMAP_FOREACH(n, NULL)
-                assert_not_reached("NULL bitmap");
+                assert_not_reached();
 
         assert_se(bitmap_set(b, 0) == 0);
         assert_se(bitmap_set(b, 1) == 0);
index b29c0d78440f0572c459870e4b9a2acb3ea639d6..8b7d46bee3f8d4b9aead3c77c5d8a551cde55439 100644 (file)
@@ -97,7 +97,7 @@ int main(int argc, char *argv[]) {
         /* The simple tests succeeded. Now let's try full unit-based use-case. */
 
         assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         assert_se(u = unit_new(m, sizeof(Service)));
         assert_se(unit_add_name(u, "foo.service") == 0);
index a6f8eb6f4ac612f36193135b9ef9c9d221a05e47..bbf3916872228000c0180f72927c87d82704656f 100644 (file)
@@ -304,7 +304,7 @@ int main(int argc, char *argv[]) {
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
         assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         assert_se(test_bpf_cgroup_programs(m,
                                 "single_prog.service", single_prog, ELEMENTSOF(single_prog)) >= 0);
index 19e159b9ff1c892105576a33732a962f46df7804..415ddf3e984468c2ec13180d667a9d27123f7a62 100644 (file)
@@ -60,7 +60,7 @@ static int test_cgroup_mask(void) {
                 m->default_tasks_accounting = false;
         m->default_tasks_max = TASKS_MAX_UNSET;
 
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        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);
@@ -140,7 +140,7 @@ static void test_cg_mask_to_string_one(CGroupMask mask, const char *t) {
 
 static void test_cg_mask_to_string(void) {
         test_cg_mask_to_string_one(0, NULL);
-        test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign bpf-socket-bind");
+        test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign bpf-socket-bind bpf-restrict-network-interfaces");
         test_cg_mask_to_string_one(CGROUP_MASK_CPU, "cpu");
         test_cg_mask_to_string_one(CGROUP_MASK_CPUACCT, "cpuacct");
         test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset");
index 225d138e41563e281400a0c0a079fb4e0c7d2b43..0fae2f64cb2abc5843676e75fd434cf3a2e101e9 100644 (file)
@@ -33,7 +33,7 @@ static int test_default_memory_low(void) {
         }
 
         assert_se(r >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         /* dml.slice has DefaultMemoryLow=50. Beyond that, individual subhierarchies look like this:
          *
index 892f8a073ff5328d25d81fa223f78457afbc016e..635c44675b2217721e03c490585057c62006f0fb 100644 (file)
@@ -72,7 +72,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind == argc)
index d1dee22bd62969d75665a173981efb2ac46bdde1..62776086c287b187bf60294c35d75d1ef00da699 100644 (file)
@@ -237,16 +237,12 @@ static void test_condition_test_ac_power(void) {
 
 static void test_condition_test_host(void) {
         _cleanup_free_ char *hostname = NULL;
-        char sid[SD_ID128_STRING_MAX];
         Condition *condition;
         sd_id128_t id;
-        int r;
 
-        r = sd_id128_get_machine(&id);
-        assert_se(r >= 0);
-        assert_se(sd_id128_to_string(id, sid));
+        assert_se(sd_id128_get_machine(&id) >= 0);
 
-        condition = condition_new(CONDITION_HOST, sid, false, false);
+        condition = condition_new(CONDITION_HOST, SD_ID128_TO_STRING(id), false, false);
         assert_se(condition);
         assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
@@ -256,7 +252,7 @@ static void test_condition_test_host(void) {
         assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
-        condition = condition_new(CONDITION_HOST, sid, false, true);
+        condition = condition_new(CONDITION_HOST, SD_ID128_TO_STRING(id), false, true);
         assert_se(condition);
         assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
index 6dc16193d3768fb79ab0cb5b9721f9bf94c54dcb..880af36fb5237cfa578d8cccb570be61630f8c58 100644 (file)
@@ -95,7 +95,7 @@ int main(int argc, char *argv[]) {
         if (manager_errno_skip_test(r))
                 return log_tests_skipped_errno(r, "manager_new");
         assert_se(r >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         printf("Load1:\n");
         assert_se(manager_load_startable_unit_or_warn(m, "a.service", NULL, &a) >= 0);
index ed4580e4aff9ca8ee982de8bdcf7f9bf6f14a226..0e4b832ba8727523f2bd1165e5a1ff1b75efe66c 100644 (file)
@@ -73,31 +73,28 @@ static void test_strv_env_unset(void) {
 static void test_strv_env_merge(void) {
         log_info("/* %s */", __func__);
 
-        _cleanup_strv_free_ char **a = NULL, **b = NULL, **r = NULL;
+        char **a = STRV_MAKE("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", "EQ===");
+        char **b = STRV_MAKE("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES");
 
-        a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF");
-        assert_se(a);
-
-        b = strv_new("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES");
-        assert_se(b);
-
-        r = strv_env_merge(2, a, b);
+        _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], "PIEP="));
-        assert_se(streq(r[5], "NANANANA=YES"));
-        assert_se(strv_length(r) == 6);
+        assert_se(streq(r[4], "EQ==="));
+        assert_se(streq(r[5], "PIEP="));
+        assert_se(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], "PIEP="));
-        assert_se(streq(r[4], "NANANANA=YES"));
-        assert_se(strv_length(r) == 5);
+        assert_se(streq(r[3], "EQ==="));
+        assert_se(streq(r[4], "PIEP="));
+        assert_se(streq(r[5], "NANANANA=YES"));
+        assert_se(strv_length(r) == 6);
 }
 
 static void test_strv_env_replace_strdup(void) {
@@ -108,6 +105,7 @@ static void test_strv_env_replace_strdup(void) {
         assert_se(strv_env_replace_strdup(&a, "a=a") == 1);
         assert_se(strv_env_replace_strdup(&a, "b=b") == 1);
         assert_se(strv_env_replace_strdup(&a, "a=A") == 0);
+        assert_se(strv_env_replace_strdup(&a, "c") == -EINVAL);
 
         assert_se(strv_length(a) == 2);
         strv_sort(a);
@@ -115,6 +113,27 @@ static void test_strv_env_replace_strdup(void) {
         assert_se(streq(a[1], "b=b"));
 }
 
+static void test_strv_env_replace_strdup_passthrough(void) {
+        log_info("/* %s */", __func__);
+
+        _cleanup_strv_free_ char **a = NULL;
+
+        assert_se(putenv((char*) "a=a") == 0);
+        assert_se(putenv((char*) "b=") == 0);
+        assert_se(unsetenv("c") == 0);
+
+        assert_se(strv_env_replace_strdup_passthrough(&a, "a") == 1);
+        assert_se(strv_env_replace_strdup_passthrough(&a, "b") == 1);
+        assert_se(strv_env_replace_strdup_passthrough(&a, "c") == 1);
+        assert_se(strv_env_replace_strdup_passthrough(&a, "a") == 0);
+        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="));
+}
+
 static void test_strv_env_assign(void) {
         log_info("/* %s */", __func__);
 
@@ -409,6 +428,52 @@ static void test_setenv_systemd_exec_pid(void) {
         assert_se(set_unset_env("SYSTEMD_EXEC_PID", saved, 1) >= 0);
 }
 
+static void test_unsetenv_erase(void) {
+        int r;
+
+        log_info("/* %s */", __func__);
+
+        r = safe_fork("(sd-unsetenverase)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+        if (r == 0) {
+                _cleanup_strv_free_ char **l = NULL;
+                char **e;
+
+                /* child */
+
+                assert_se(unsetenv_erase("thisenvvardefinitelywontexist") == 0);
+
+                l = strv_new("FOO=BAR", "QUUX=PIFF", "ONE=TWO", "A=B");
+                assert_se(strv_length(l) == 4);
+
+                environ = l;
+
+                STRV_FOREACH(e, environ) {
+                        _cleanup_free_ char *n = NULL;
+                        char *eq;
+
+                        eq = strchr(*e, '=');
+                        if (!eq)
+                                continue;
+
+                        n = strndup(*e, eq - *e);
+                        assert_se(n);
+
+                        assert_se(streq_ptr(getenv(n), eq + 1));
+                        assert_se(getenv(n) == eq + 1);
+                        assert_se(unsetenv_erase(n) > 0);
+                        assert_se(isempty(eq + 1));
+                        assert_se(!getenv(n));
+                }
+
+                environ = NULL;
+                l = strv_free(l);
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        assert_se(r > 0);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
@@ -418,6 +483,7 @@ int main(int argc, char *argv[]) {
         test_strv_env_unset();
         test_strv_env_merge();
         test_strv_env_replace_strdup();
+        test_strv_env_replace_strdup_passthrough();
         test_strv_env_assign();
         test_env_strv_get_n();
         test_replace_env(false);
@@ -431,6 +497,7 @@ int main(int argc, char *argv[]) {
         test_env_assignment_is_valid();
         test_putenv_dup();
         test_setenv_systemd_exec_pid();
+        test_unsetenv_erase();
 
         return 0;
 }
index 125e0bbf4f75c781ae5aaaef7dfe3826558879f7..a0481f119440fc0a53c38a907629aa0d62f9961a 100644 (file)
@@ -844,7 +844,7 @@ static int run_tests(UnitFileScope scope, const test_entry tests[], char **patte
         if (manager_errno_skip_test(r))
                 return log_tests_skipped_errno(r, "manager_new");
         assert_se(r >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         for (const test_entry *test = tests; test->f; test++)
                 if (strv_fnmatch_or_empty(patterns, test->name, FNM_NOESCAPE))
index 1cd3bdfbbec640f4f91d0853f8e63f240ec498dd..39a50013b96d650b11a03765bd662dff8afb0d58 100644 (file)
@@ -278,6 +278,14 @@ static void test_close_all_fds(void) {
         log_open();
 }
 
+static void test_format_proc_fd_path(void) {
+        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"));
+}
+
 int main(int argc, char *argv[]) {
 
         test_setup_logging(LOG_DEBUG);
@@ -290,6 +298,7 @@ int main(int argc, char *argv[]) {
         test_rearrange_stdio();
         test_read_nr_open();
         test_close_all_fds();
+        test_format_proc_fd_path();
 
         return 0;
 }
index 4e6c90adfae0ae5a783dc2d367b04b94a9363fd2..306e0788d081735904bd2ace5d7eee440ca739ca 100644 (file)
@@ -7,6 +7,8 @@
 /* Do some basic checks on STRLEN() and DECIMAL_STR_MAX() */
 assert_cc(STRLEN("xxx") == 3);
 assert_cc(STRLEN("") == 0);
+assert_cc(STRLEN(L"xxx") == 3 * sizeof(wchar_t));
+assert_cc(STRLEN(L"") == 0);
 assert_cc(DECIMAL_STR_MAX(uint8_t) == 5);
 assert_cc(DECIMAL_STR_MAX(int8_t) == 5);
 assert_cc(DECIMAL_STR_MAX(uint64_t) == 22);
diff --git a/src/test/test-import-util.c b/src/test/test-import-util.c
new file mode 100644 (file)
index 0000000..92ff0f5
--- /dev/null
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "import-util.h"
+#include "log.h"
+#include "string-util.h"
+#include "tests.h"
+
+static void test_import_url_last_component_one(const char *input, const char *output, int ret) {
+        _cleanup_free_ char *s = NULL;
+
+        assert_se(import_url_last_component(input, &s) == ret);
+        assert_se(streq_ptr(output, s));
+}
+
+static void test_import_url_last_component(void) {
+        test_import_url_last_component_one("https://foobar/waldo/quux", "quux", 0);
+        test_import_url_last_component_one("https://foobar/waldo/quux/", "quux", 0);
+        test_import_url_last_component_one("https://foobar/waldo/", "waldo", 0);
+        test_import_url_last_component_one("https://foobar/", NULL, -EADDRNOTAVAIL);
+        test_import_url_last_component_one("https://foobar", NULL, -EADDRNOTAVAIL);
+        test_import_url_last_component_one("https://foobar/waldo/quux?foo=bar", "quux", 0);
+        test_import_url_last_component_one("https://foobar/waldo/quux/?foo=bar", "quux", 0);
+        test_import_url_last_component_one("https://foobar/waldo/quux/?foo=bar#piep", "quux", 0);
+        test_import_url_last_component_one("https://foobar/waldo/quux/#piep", "quux", 0);
+        test_import_url_last_component_one("https://foobar/waldo/quux#piep", "quux", 0);
+        test_import_url_last_component_one("https://", NULL, -EINVAL);
+        test_import_url_last_component_one("", NULL, -EINVAL);
+        test_import_url_last_component_one(":", NULL, -EINVAL);
+        test_import_url_last_component_one(":/", NULL, -EINVAL);
+        test_import_url_last_component_one("x:/", NULL, -EINVAL);
+        test_import_url_last_component_one("x:y", NULL, -EADDRNOTAVAIL);
+        test_import_url_last_component_one("x:y/z", "z", 0);
+}
+
+static void test_import_url_change_suffix_one(const char *input, size_t n, const char *suffix, const char *output, int ret) {
+        _cleanup_free_ char *s = NULL;
+
+        assert_se(import_url_change_suffix(input, n, suffix, &s) == ret);
+        assert_se(streq_ptr(output, s));
+}
+
+static void test_import_url_change_suffix(void) {
+        test_import_url_change_suffix_one("https://foobar/waldo/quux", 1, "wuff", "https://foobar/waldo/wuff", 0);
+        test_import_url_change_suffix_one("https://foobar/waldo/quux/", 1, "wuff", "https://foobar/waldo/wuff", 0);
+        test_import_url_change_suffix_one("https://foobar/waldo/quux///?mief", 1, "wuff", "https://foobar/waldo/wuff", 0);
+        test_import_url_change_suffix_one("https://foobar/waldo/quux///?mief#opopo", 1, "wuff", "https://foobar/waldo/wuff", 0);
+        test_import_url_change_suffix_one("https://foobar/waldo/quux/quff", 2, "wuff", "https://foobar/waldo/wuff", 0);
+        test_import_url_change_suffix_one("https://foobar/waldo/quux/quff/", 2, "wuff", "https://foobar/waldo/wuff", 0);
+        test_import_url_change_suffix_one("https://foobar/waldo/quux/quff", 0, "wuff", "https://foobar/waldo/quux/quff/wuff", 0);
+        test_import_url_change_suffix_one("https://foobar/waldo/quux/quff?aa?bb##4", 0, "wuff", "https://foobar/waldo/quux/quff/wuff", 0);
+        test_import_url_change_suffix_one("https://", 0, "wuff", NULL, -EINVAL);
+        test_import_url_change_suffix_one("", 0, "wuff", NULL, -EINVAL);
+        test_import_url_change_suffix_one(":", 0, "wuff", NULL, -EINVAL);
+        test_import_url_change_suffix_one(":/", 0, "wuff", NULL, -EINVAL);
+        test_import_url_change_suffix_one("x:/", 0, "wuff", NULL, -EINVAL);
+        test_import_url_change_suffix_one("x:y", 0, "wuff", "x:y/wuff", 0);
+        test_import_url_change_suffix_one("x:y/z", 0, "wuff", "x:y/z/wuff", 0);
+        test_import_url_change_suffix_one("x:y/z/", 0, "wuff", "x:y/z/wuff", 0);
+        test_import_url_change_suffix_one("x:y/z/", 1, "wuff", "x:y/wuff", 0);
+        test_import_url_change_suffix_one("x:y/z/", 2, "wuff", "x:y/wuff", 0);
+}
+
+int main(int argc, char *argv[]) {
+
+        test_setup_logging(LOG_INFO);
+
+        test_import_url_last_component();
+        test_import_url_change_suffix();
+
+        return 0;
+}
diff --git a/src/test/test-install-file.c b/src/test/test-install-file.c
new file mode 100644 (file)
index 0000000..55d77ac
--- /dev/null
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fileio.h"
+#include "install-file.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+#include "umask-util.h"
+
+static void test_install_file(void) {
+        _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
+        _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL;
+        struct stat stat1, stat2;
+
+        log_info("/* %s */", __func__);
+
+        assert_se(mkdtemp_malloc(NULL, &p) >= 0);
+        assert_se(a = path_join(p, "foo"));
+        assert_se(b = path_join(p, "bar"));
+
+        RUN_WITH_UMASK(0077)
+                assert_se(write_string_file(a, "wups", WRITE_STRING_FILE_CREATE) >= 0);
+
+        assert_se(lstat(a, &stat1) >= 0);
+        assert_se(S_ISREG(stat1.st_mode));
+
+        assert_se(install_file(AT_FDCWD, a, AT_FDCWD, b, 0) >= 0);
+        assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC) >= 0);
+
+        assert_se(write_string_file(b, "ttss", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(install_file(AT_FDCWD, a, AT_FDCWD, b, INSTALL_FSYNC_FULL) == -EEXIST);
+        assert_se(install_file(AT_FDCWD, a, AT_FDCWD, b, INSTALL_FSYNC_FULL|INSTALL_REPLACE) >= 0);
+
+        assert_se(stat(b, &stat2) >= 0);
+        assert_se(stat1.st_dev == stat2.st_dev);
+        assert_se(stat1.st_ino == stat2.st_ino);
+        assert_se((stat2.st_mode & 0222) != 0); /* writable */
+
+        assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL|INSTALL_REPLACE|INSTALL_READ_ONLY) >= 0);
+
+        assert_se(stat(a, &stat2) >= 0);
+        assert_se(stat1.st_dev == stat2.st_dev);
+        assert_se(stat1.st_ino == stat2.st_ino);
+        assert_se((stat2.st_mode & 0222) == 0); /* read-only */
+
+        assert_se(mkdir(b, 0755) >= 0);
+        assert_se(c = path_join(b, "dir"));
+        assert_se(mkdir(c, 0755) >= 0);
+        free(c);
+        assert_se(c = path_join(b, "reg"));
+        assert_se(mknod(c, S_IFREG|0755, 0) >= 0);
+        free(c);
+        assert_se(c = path_join(b, "fifo"));
+        assert_se(mknod(c, S_IFIFO|0755, 0) >= 0);
+
+        assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL) == -EEXIST);
+        assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL|INSTALL_REPLACE) == 0);
+
+        assert_se(write_string_file(b, "ttss", WRITE_STRING_FILE_CREATE) >= 0);
+
+        assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL) == -EEXIST);
+        assert_se(install_file(AT_FDCWD, b, AT_FDCWD, a, INSTALL_FSYNC_FULL|INSTALL_REPLACE) == 0);
+}
+
+int main(int argc, char *argv[]) {
+        test_setup_logging(LOG_INFO);
+
+        test_install_file();
+
+        return 0;
+}
index 50ed23a4ee3eaa9ab4a8b71e8c6d87ad47e65b92..30494f7fa1fe443396a7a8fab234e2a9d226992b 100644 (file)
@@ -284,7 +284,7 @@ static void test_linked_units(const char *root) {
                 else if (q && streq(changes[i].path, q))
                         q = NULL;
                 else
-                        assert_not_reached("wut?");
+                        assert_not_reached();
         }
         assert(!p && !q);
         unit_file_changes_free(changes, n_changes);
@@ -305,7 +305,7 @@ static void test_linked_units(const char *root) {
                 else if (q && streq(changes[i].path, q))
                         q = NULL;
                 else
-                        assert_not_reached("wut?");
+                        assert_not_reached();
         }
         assert(!p && !q);
         unit_file_changes_free(changes, n_changes);
@@ -326,7 +326,7 @@ static void test_linked_units(const char *root) {
                 else if (q && streq(changes[i].path, q))
                         q = NULL;
                 else
-                        assert_not_reached("wut?");
+                        assert_not_reached();
         }
         assert(!p && !q);
         unit_file_changes_free(changes, n_changes);
index b41a8abf7bc552741a938ea3a484512af0315e6a..e0ba99199c9dca5b78e9df93067de97baeb23d72 100644 (file)
@@ -104,7 +104,7 @@ static void test_config_parse_exec(void) {
         }
 
         assert_se(r >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         assert_se(u = unit_new(m, sizeof(Service)));
 
@@ -448,7 +448,7 @@ static void test_config_parse_log_extra_fields(void) {
         }
 
         assert_se(r >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         assert_se(u = unit_new(m, sizeof(Service)));
 
index 972423e2fa2856ffd825f22c702156c2f9f36398..e22481b1b6e5147a0115a94f8780235892546f65 100644 (file)
@@ -89,7 +89,7 @@ static void test_keymaps(void) {
 
 #define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
 static void dump_special_glyphs(void) {
-        assert_cc(SPECIAL_GLYPH_TOUCH + 1 == _SPECIAL_GLYPH_MAX);
+        assert_cc(SPECIAL_GLYPH_SPARKLES + 1 == _SPECIAL_GLYPH_MAX);
 
         log_info("/* %s */", __func__);
 
@@ -120,6 +120,9 @@ static void dump_special_glyphs(void) {
         dump_glyph(SPECIAL_GLYPH_DEPRESSED_SMILEY);
         dump_glyph(SPECIAL_GLYPH_LOCK_AND_KEY);
         dump_glyph(SPECIAL_GLYPH_TOUCH);
+        dump_glyph(SPECIAL_GLYPH_RECYCLING);
+        dump_glyph(SPECIAL_GLYPH_DOWNLOAD);
+        dump_glyph(SPECIAL_GLYPH_SPARKLES);
 }
 
 int main(int argc, char *argv[]) {
index 861309eb3aa869035576b1cf965c05c79f5f6710..de8399dea8a1714d47f6ce66b969f4b5c58f91ae 100644 (file)
@@ -66,11 +66,11 @@ static void test_log_syntax(void) {
 }
 
 int main(int argc, char* argv[]) {
-        int target;
-
         test_file();
 
-        for (target = 0; target < _LOG_TARGET_MAX; target++) {
+        assert_se(log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), "foo") == -EUCLEAN);
+
+        for (int target = 0; target < _LOG_TARGET_MAX; target++) {
                 log_set_target(target);
                 log_open();
 
@@ -79,7 +79,5 @@ int main(int argc, char* argv[]) {
                 test_log_syntax();
         }
 
-        assert_se(log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), "foo") == -EUCLEAN);
-
         return 0;
 }
diff --git a/src/test/test-macro.c b/src/test/test-macro.c
new file mode 100644 (file)
index 0000000..428f3b9
--- /dev/null
@@ -0,0 +1,343 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stddef.h>
+
+#include "log.h"
+#include "macro.h"
+#include "tests.h"
+
+static void test_align_power2(void) {
+        unsigned long i, p2;
+
+        log_info("/* %s */", __func__);
+
+        assert_se(ALIGN_POWER2(0) == 0);
+        assert_se(ALIGN_POWER2(1) == 1);
+        assert_se(ALIGN_POWER2(2) == 2);
+        assert_se(ALIGN_POWER2(3) == 4);
+        assert_se(ALIGN_POWER2(4) == 4);
+        assert_se(ALIGN_POWER2(5) == 8);
+        assert_se(ALIGN_POWER2(6) == 8);
+        assert_se(ALIGN_POWER2(7) == 8);
+        assert_se(ALIGN_POWER2(9) == 16);
+        assert_se(ALIGN_POWER2(10) == 16);
+        assert_se(ALIGN_POWER2(11) == 16);
+        assert_se(ALIGN_POWER2(12) == 16);
+        assert_se(ALIGN_POWER2(13) == 16);
+        assert_se(ALIGN_POWER2(14) == 16);
+        assert_se(ALIGN_POWER2(15) == 16);
+        assert_se(ALIGN_POWER2(16) == 16);
+        assert_se(ALIGN_POWER2(17) == 32);
+
+        assert_se(ALIGN_POWER2(ULONG_MAX) == 0);
+        assert_se(ALIGN_POWER2(ULONG_MAX - 1) == 0);
+        assert_se(ALIGN_POWER2(ULONG_MAX - 1024) == 0);
+        assert_se(ALIGN_POWER2(ULONG_MAX / 2) == ULONG_MAX / 2 + 1);
+        assert_se(ALIGN_POWER2(ULONG_MAX + 1) == 0);
+
+        for (i = 1; i < 131071; ++i) {
+                for (p2 = 1; p2 < i; p2 <<= 1)
+                        /* empty */ ;
+
+                assert_se(ALIGN_POWER2(i) == p2);
+        }
+
+        for (i = ULONG_MAX - 1024; i < ULONG_MAX; ++i) {
+                for (p2 = 1; p2 && p2 < i; p2 <<= 1)
+                        /* empty */ ;
+
+                assert_se(ALIGN_POWER2(i) == p2);
+        }
+}
+
+static void test_max(void) {
+        static const struct {
+                int a;
+                int b[CONST_MAX(10, 100)];
+        } val1 = {
+                .a = CONST_MAX(10, 100),
+        };
+        int d = 0;
+        unsigned long x = 12345;
+        unsigned long y = 54321;
+        const char str[] = "a_string_constant";
+        const unsigned long long arr[] = {9999ULL, 10ULL, 0ULL, 3000ULL, 2000ULL, 1000ULL, 100ULL, 9999999ULL};
+        void *p = (void *)str;
+        void *q = (void *)&str[16];
+
+        log_info("/* %s */", __func__);
+
+        assert_cc(sizeof(val1.b) == sizeof(int) * 100);
+
+        /* CONST_MAX returns (void) instead of a value if the passed arguments
+         * are not of the same type or not constant expressions. */
+        assert_cc(__builtin_types_compatible_p(typeof(CONST_MAX(1, 10)), int));
+        assert_cc(__builtin_types_compatible_p(typeof(CONST_MAX(1, 1U)), void));
+
+        assert_se(val1.a == 100);
+        assert_se(MAX(++d, 0) == 1);
+        assert_se(d == 1);
+
+        assert_cc(MAXSIZE(char[3], uint16_t) == 3);
+        assert_cc(MAXSIZE(char[3], uint32_t) == 4);
+        assert_cc(MAXSIZE(char, long) == sizeof(long));
+
+        assert_se(MAX(-5, 5) == 5);
+        assert_se(MAX(5, 5) == 5);
+        assert_se(MAX(MAX(1, MAX(2, MAX(3, 4))), 5) == 5);
+        assert_se(MAX(MAX(1, MAX(2, MAX(3, 2))), 1) == 3);
+        assert_se(MAX(MIN(1, MIN(2, MIN(3, 4))), 5) == 5);
+        assert_se(MAX(MAX(1, MIN(2, MIN(3, 2))), 1) == 2);
+        assert_se(LESS_BY(8, 4) == 4);
+        assert_se(LESS_BY(8, 8) == 0);
+        assert_se(LESS_BY(4, 8) == 0);
+        assert_se(LESS_BY(16, LESS_BY(8, 4)) == 12);
+        assert_se(LESS_BY(4, LESS_BY(8, 4)) == 0);
+        assert_se(CMP(3, 5) == -1);
+        assert_se(CMP(5, 3) == 1);
+        assert_se(CMP(5, 5) == 0);
+        assert_se(CMP(x, y) == -1);
+        assert_se(CMP(y, x) == 1);
+        assert_se(CMP(x, x) == 0);
+        assert_se(CMP(y, y) == 0);
+        assert_se(CMP(UINT64_MAX, (uint64_t) 0) == 1);
+        assert_se(CMP((uint64_t) 0, UINT64_MAX) == -1);
+        assert_se(CMP(UINT64_MAX, UINT64_MAX) == 0);
+        assert_se(CMP(INT64_MIN, INT64_MAX) == -1);
+        assert_se(CMP(INT64_MAX, INT64_MIN) == 1);
+        assert_se(CMP(INT64_MAX, INT64_MAX) == 0);
+        assert_se(CMP(INT64_MIN, INT64_MIN) == 0);
+        assert_se(CMP(INT64_MAX, (int64_t) 0) == 1);
+        assert_se(CMP((int64_t) 0, INT64_MIN) == 1);
+        assert_se(CMP(INT64_MIN, (int64_t) 0) == -1);
+        assert_se(CMP((int64_t) 0, INT64_MAX) == -1);
+        assert_se(CMP(&str[2], &str[7]) == -1);
+        assert_se(CMP(&str[2], &str[2]) == 0);
+        assert_se(CMP(&str[7], (const char *)str) == 1);
+        assert_se(CMP(str[2], str[7]) == 1);
+        assert_se(CMP(str[7], *str) == 1);
+        assert_se(CMP((const unsigned long long *)arr, &arr[3]) == -1);
+        assert_se(CMP(*arr, arr[3]) == 1);
+        assert_se(CMP(p, q) == -1);
+        assert_se(CMP(q, p) == 1);
+        assert_se(CMP(p, p) == 0);
+        assert_se(CMP(q, q) == 0);
+        assert_se(CLAMP(-5, 0, 1) == 0);
+        assert_se(CLAMP(5, 0, 1) == 1);
+        assert_se(CLAMP(5, -10, 1) == 1);
+        assert_se(CLAMP(5, -10, 10) == 5);
+        assert_se(CLAMP(CLAMP(0, -10, 10), CLAMP(-5, 10, 20), CLAMP(100, -5, 20)) == 10);
+}
+
+#pragma GCC diagnostic push
+#ifdef __clang__
+#  pragma GCC diagnostic ignored "-Waddress-of-packed-member"
+#endif
+
+static void test_container_of(void) {
+        struct mytype {
+                uint8_t pad1[3];
+                uint64_t v1;
+                uint8_t pad2[2];
+                uint32_t v2;
+        } myval = { };
+
+        log_info("/* %s */", __func__);
+
+        assert_cc(sizeof(myval) >= 17);
+        assert_se(container_of(&myval.v1, struct mytype, v1) == &myval);
+        assert_se(container_of(&myval.v2, struct mytype, v2) == &myval);
+        assert_se(container_of(&container_of(&myval.v2,
+                                             struct mytype,
+                                             v2)->v1,
+                               struct mytype,
+                               v1) == &myval);
+}
+
+#pragma GCC diagnostic pop
+
+static void test_div_round_up(void) {
+        int div;
+
+        log_info("/* %s */", __func__);
+
+        /* basic tests */
+        assert_se(DIV_ROUND_UP(0, 8) == 0);
+        assert_se(DIV_ROUND_UP(1, 8) == 1);
+        assert_se(DIV_ROUND_UP(8, 8) == 1);
+        assert_se(DIV_ROUND_UP(12, 8) == 2);
+        assert_se(DIV_ROUND_UP(16, 8) == 2);
+
+        /* test multiple evaluation */
+        div = 0;
+        assert_se(DIV_ROUND_UP(div++, 8) == 0 && div == 1);
+        assert_se(DIV_ROUND_UP(++div, 8) == 1 && div == 2);
+        assert_se(DIV_ROUND_UP(8, div++) == 4 && div == 3);
+        assert_se(DIV_ROUND_UP(8, ++div) == 2 && div == 4);
+
+        /* overflow test with exact division */
+        assert_se(sizeof(0U) == 4);
+        assert_se(0xfffffffaU % 10U == 0U);
+        assert_se(0xfffffffaU / 10U == 429496729U);
+        assert_se(DIV_ROUND_UP(0xfffffffaU, 10U) == 429496729U);
+        assert_se((0xfffffffaU + 10U - 1U) / 10U == 0U);
+        assert_se(0xfffffffaU / 10U + !!(0xfffffffaU % 10U) == 429496729U);
+
+        /* overflow test with rounded division */
+        assert_se(0xfffffffdU % 10U == 3U);
+        assert_se(0xfffffffdU / 10U == 429496729U);
+        assert_se(DIV_ROUND_UP(0xfffffffdU, 10U) == 429496730U);
+        assert_se((0xfffffffdU + 10U - 1U) / 10U == 0U);
+        assert_se(0xfffffffdU / 10U + !!(0xfffffffdU % 10U) == 429496730U);
+}
+
+static void test_ptr_to_int(void) {
+        log_info("/* %s */", __func__);
+
+        /* Primary reason to have this test is to validate that pointers are large enough to hold entire int range */
+        assert_se(PTR_TO_INT(INT_TO_PTR(0)) == 0);
+        assert_se(PTR_TO_INT(INT_TO_PTR(1)) == 1);
+        assert_se(PTR_TO_INT(INT_TO_PTR(-1)) == -1);
+        assert_se(PTR_TO_INT(INT_TO_PTR(INT_MAX)) == INT_MAX);
+        assert_se(PTR_TO_INT(INT_TO_PTR(INT_MIN)) == INT_MIN);
+}
+
+static void test_in_set(void) {
+        log_info("/* %s */", __func__);
+
+        assert_se(IN_SET(1, 1));
+        assert_se(IN_SET(1, 1, 2, 3, 4));
+        assert_se(IN_SET(2, 1, 2, 3, 4));
+        assert_se(IN_SET(3, 1, 2, 3, 4));
+        assert_se(IN_SET(4, 1, 2, 3, 4));
+        assert_se(!IN_SET(0, 1));
+        assert_se(!IN_SET(0, 1, 2, 3, 4));
+}
+
+static void test_foreach_pointer(void) {
+        int a, b, c, *i;
+        size_t k = 0;
+
+        log_info("/* %s */", __func__);
+
+        FOREACH_POINTER(i, &a, &b, &c) {
+                switch (k) {
+
+                case 0:
+                        assert_se(i == &a);
+                        break;
+
+                case 1:
+                        assert_se(i == &b);
+                        break;
+
+                case 2:
+                        assert_se(i == &c);
+                        break;
+
+                default:
+                        assert_not_reached();
+                        break;
+                }
+
+                k++;
+        }
+
+        assert(k == 3);
+
+        FOREACH_POINTER(i, &b) {
+                assert(k == 3);
+                assert(i == &b);
+                k = 4;
+        }
+
+        assert(k == 4);
+
+        FOREACH_POINTER(i, NULL, &c, NULL, &b, NULL, &a, NULL) {
+                switch (k) {
+
+                case 4:
+                        assert_se(i == NULL);
+                        break;
+
+                case 5:
+                        assert_se(i == &c);
+                        break;
+
+                case 6:
+                        assert_se(i == NULL);
+                        break;
+
+                case 7:
+                        assert_se(i == &b);
+                        break;
+
+                case 8:
+                        assert_se(i == NULL);
+                        break;
+
+                case 9:
+                        assert_se(i == &a);
+                        break;
+
+                case 10:
+                        assert_se(i == NULL);
+                        break;
+
+                default:
+                        assert_not_reached();
+                        break;
+                }
+
+                k++;
+        }
+
+        assert(k == 11);
+}
+
+static void test_align_to(void) {
+        log_info("/* %s */", __func__);
+
+        assert_se(ALIGN_TO(0, 1) == 0);
+        assert_se(ALIGN_TO(1, 1) == 1);
+        assert_se(ALIGN_TO(2, 1) == 2);
+        assert_se(ALIGN_TO(3, 1) == 3);
+        assert_se(ALIGN_TO(4, 1) == 4);
+        assert_se(ALIGN_TO(SIZE_MAX-1, 1) == SIZE_MAX-1);
+        assert_se(ALIGN_TO(SIZE_MAX, 1) == SIZE_MAX);
+
+        assert_se(ALIGN_TO(0, 2) == 0);
+        assert_se(ALIGN_TO(1, 2) == 2);
+        assert_se(ALIGN_TO(2, 2) == 2);
+        assert_se(ALIGN_TO(3, 2) == 4);
+        assert_se(ALIGN_TO(4, 2) == 4);
+        assert_se(ALIGN_TO(SIZE_MAX-3, 2) == SIZE_MAX-3);
+        assert_se(ALIGN_TO(SIZE_MAX-2, 2) == SIZE_MAX-1);
+        assert_se(ALIGN_TO(SIZE_MAX-1, 2) == SIZE_MAX-1);
+        assert_se(ALIGN_TO(SIZE_MAX, 2) == SIZE_MAX); /* overflow */
+
+        assert_se(ALIGN_TO(0, 4) == 0);
+        assert_se(ALIGN_TO(1, 4) == 4);
+        assert_se(ALIGN_TO(2, 4) == 4);
+        assert_se(ALIGN_TO(3, 4) == 4);
+        assert_se(ALIGN_TO(4, 4) == 4);
+        assert_se(ALIGN_TO(SIZE_MAX-3, 4) == SIZE_MAX-3);
+        assert_se(ALIGN_TO(SIZE_MAX-2, 4) == SIZE_MAX); /* overflow */
+        assert_se(ALIGN_TO(SIZE_MAX-1, 4) == SIZE_MAX); /* overflow */
+        assert_se(ALIGN_TO(SIZE_MAX, 4) == SIZE_MAX);   /* overflow */
+}
+
+int main(int argc, char *argv[]) {
+        test_setup_logging(LOG_INFO);
+
+        test_align_power2();
+        test_max();
+        test_container_of();
+        test_div_round_up();
+        test_in_set();
+        test_foreach_pointer();
+        test_ptr_to_int();
+        test_align_to();
+
+        return 0;
+}
index cfa46b00b5beff48a4072370740436e6563b49bf..796382f3cf0713f2ebe3b7363aa552132aa47174 100644 (file)
@@ -200,9 +200,8 @@ static void test_protect_kernel_logs(void) {
 }
 
 int main(int argc, char *argv[]) {
-        sd_id128_t bid;
-        char boot_id[SD_ID128_STRING_MAX];
         _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *zz = NULL;
+        sd_id128_t bid;
 
         test_setup_logging(LOG_INFO);
 
@@ -214,16 +213,15 @@ int main(int argc, char *argv[]) {
         }
 
         assert_se(sd_id128_get_boot(&bid) >= 0);
-        sd_id128_to_string(bid, boot_id);
 
-        x = strjoin("/tmp/systemd-private-", boot_id, "-abcd.service-");
-        y = strjoin("/var/tmp/systemd-private-", boot_id, "-abcd.service-");
+        x = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-");
+        y = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-abcd.service-");
         assert_se(x && y);
 
         test_tmpdir("abcd.service", x, y);
 
-        z = strjoin("/tmp/systemd-private-", boot_id, "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
-        zz = strjoin("/var/tmp/systemd-private-", boot_id, "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
+        z = strjoin("/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
+        zz = strjoin("/var/tmp/systemd-private-", SD_ID128_TO_STRING(bid), "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
 
         assert_se(z && zz);
 
index eddb04795133bc5682fbc653fa20a2e2c274941a..f9c0bd6ff9e8e21dfb054bfaeacf124dc22606ea 100644 (file)
@@ -36,7 +36,7 @@ static const char* af_to_string(int family, char *buf, size_t buf_len) {
         if (name)
                 return name;
 
-        snprintf(buf, buf_len, "%i", family);
+        (void) snprintf(buf, buf_len, "%i", family);
         return buf;
 }
 
index 4c041cd57f9d31cfd83219c3c23ffdb637974f66..1a0fda5d1af90bae9ba32ef4d3d739c69d3c59b9 100644 (file)
@@ -204,12 +204,12 @@ static void test_find_executable_full(void) {
 
         log_info("/* %s */", __func__);
 
-        assert_se(find_executable_full("sh", true, &p, NULL) == 0);
+        assert_se(find_executable_full("sh", NULL, true, &p, NULL) == 0);
         puts(p);
         assert_se(streq(basename(p), "sh"));
         free(p);
 
-        assert_se(find_executable_full("sh", false, &p, NULL) == 0);
+        assert_se(find_executable_full("sh", NULL, false, &p, NULL) == 0);
         puts(p);
         assert_se(streq(basename(p), "sh"));
         free(p);
@@ -221,12 +221,12 @@ static void test_find_executable_full(void) {
 
         assert_se(unsetenv("PATH") == 0);
 
-        assert_se(find_executable_full("sh", true, &p, NULL) == 0);
+        assert_se(find_executable_full("sh", NULL, true, &p, NULL) == 0);
         puts(p);
         assert_se(streq(basename(p), "sh"));
         free(p);
 
-        assert_se(find_executable_full("sh", false, &p, NULL) == 0);
+        assert_se(find_executable_full("sh", NULL, false, &p, NULL) == 0);
         puts(p);
         assert_se(streq(basename(p), "sh"));
         free(p);
@@ -277,7 +277,7 @@ static void test_find_executable_exec_one(const char *path) {
         pid_t pid;
         int r;
 
-        r = find_executable_full(path, false, &t, &fd);
+        r = find_executable_full(path, NULL, false, &t, &fd);
 
         log_info_errno(r, "%s: %s → %s: %d/%m", __func__, path, t ?: "-", fd);
 
@@ -348,7 +348,7 @@ static void test_prefixes(void) {
         assert_se(values[i] == NULL);
 
         PATH_FOREACH_PREFIX(s, "////")
-                assert_not_reached("Wut?");
+                assert_not_reached();
 
         b = false;
         PATH_FOREACH_PREFIX_MORE(s, "////") {
@@ -359,7 +359,7 @@ static void test_prefixes(void) {
         assert_se(b);
 
         PATH_FOREACH_PREFIX(s, "")
-                assert_not_reached("wut?");
+                assert_not_reached();
 
         b = false;
         PATH_FOREACH_PREFIX_MORE(s, "") {
index 490fb136a7604b6185371f36587297a87d9390e0..04cb4fa37c710363615023ea33057ec982c0cc73 100644 (file)
@@ -38,7 +38,7 @@ static int setup_test(Manager **m) {
         if (manager_errno_skip_test(r))
                 return log_tests_skipped_errno(r, "manager_new");
         assert_se(r >= 0);
-        assert_se(manager_startup(tmp, NULL, NULL) >= 0);
+        assert_se(manager_startup(tmp, NULL, NULL, NULL) >= 0);
 
         STRV_FOREACH(test_path, tests_path) {
                 _cleanup_free_ char *p = NULL;
index 1f5ee7d768d994f4c55a519d3cdc4e094f052eb0..a72523bd028f4b107757519e3b436c4bb3cb730e 100644 (file)
@@ -82,7 +82,7 @@ static int parse_item_given(const char *key, const char *value, void *data) {
         else if (in_initrd() && !*strip && proc_cmdline_key_streq(key, "rd.zumm"))
                 assert_se(!value);
         else
-                assert_not_reached("Bad key!");
+                assert_not_reached();
 
         return 0;
 }
index 1f125b1d1e23035b0d90921a187923961e25cfa9..35f7be491ab973001b5393af2a1fd06daa988285 100644 (file)
@@ -34,7 +34,7 @@ int main(int argc, char *argv[]) {
         if (manager_errno_skip_test(r))
                 return log_tests_skipped_errno(r, "manager_new");
         assert_se(r >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         /* load idle ok */
         assert_se(manager_load_startable_unit_or_warn(m, "sched_idle_ok.service", NULL, &idle_ok) >= 0);
index f89c968f21a461761d5e7756a80e1a11c8510c48..fd12021cfa7e4fa8aa17eabe58bb08009c269ab5 100644 (file)
@@ -117,6 +117,38 @@ static void test_set_ensure_allocated(void) {
         assert_se(set_size(m) == 0);
 }
 
+static void test_set_copy(void) {
+        Set *s, *copy;
+        char *key1, *key2, *key3, *key4;
+
+        log_info("/* %s */", __func__);
+
+        key1 = strdup("key1");
+        assert_se(key1);
+        key2 = strdup("key2");
+        assert_se(key2);
+        key3 = strdup("key3");
+        assert_se(key3);
+        key4 = strdup("key4");
+        assert_se(key4);
+
+        s = set_new(&string_hash_ops);
+        assert_se(s);
+
+        assert_se(set_put(s, key1) >= 0);
+        assert_se(set_put(s, key2) >= 0);
+        assert_se(set_put(s, key3) >= 0);
+        assert_se(set_put(s, key4) >= 0);
+
+        copy = set_copy(s);
+        assert_se(copy);
+
+        assert(set_equal(s, copy));
+
+        set_free(s);
+        set_free_free(copy);
+}
+
 static void test_set_ensure_put(void) {
         _cleanup_set_free_ Set *m = NULL;
 
@@ -311,6 +343,7 @@ int main(int argc, const char *argv[]) {
         test_set_ensure_consume();
         test_set_strjoin();
         test_set_equal();
+        test_set_copy();
 
         return 0;
 }
index 3c9dc180fa455281486d57d555b7cd8eb3fc3a32..e36bee4e8f97dbe5294956dce887d3e16cb99ca2 100644 (file)
@@ -89,5 +89,7 @@ int main(void) {
         printf("big_enum2_pos → %zu\n", sizeof(big_enum2_pos));
         printf("big_enum2_neg → %zu\n", sizeof(big_enum2_neg));
 
+        printf("timeval: %zu\n", sizeof(struct timeval));
+        printf("timespec: %zu\n", sizeof(struct timespec));
         return 0;
 }
index 989172eee3b9569686ff5dc9c5df9b206e2dc557..ecad86baebff0738463de64e45b7c6af69251b1b 100644 (file)
@@ -138,7 +138,7 @@ int main(int argc, char *argv[]) {
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
         assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "2000", STRV_MAKE("2000"), STRV_MAKE("any")) >= 0);
         assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "2000", STRV_MAKE("ipv6:2001-2002"), STRV_MAKE("any")) >= 0);
index 134525288f1a4b971e2bf49cb5d63a8a4d118564..88ff35fc5a198e58d50111d8992471deb5fe5e15 100644 (file)
@@ -640,10 +640,10 @@ static void test_strv_foreach_backwards(void) {
                 assert_se(streq_ptr(*check, input_table_multiple[i--]));
 
         STRV_FOREACH_BACKWARDS(check, (char**) NULL)
-                assert_not_reached("Let's see that we check empty strv right, too.");
+                assert_not_reached();
 
         STRV_FOREACH_BACKWARDS(check, (char**) { NULL })
-                assert_not_reached("Let's see that we check empty strv right, too.");
+                assert_not_reached();
 }
 
 static void test_strv_foreach_pair(void) {
index fa36f54fcb9dc536582d4dc535269416259b4e1e..8a9ac9058a61f6bd9c1c4f053a3c7ab02e4cb1fe 100644 (file)
 #include "tests.h"
 #include "util.h"
 
-static void test_align_power2(void) {
-        unsigned long i, p2;
-
-        log_info("/* %s */", __func__);
-
-        assert_se(ALIGN_POWER2(0) == 0);
-        assert_se(ALIGN_POWER2(1) == 1);
-        assert_se(ALIGN_POWER2(2) == 2);
-        assert_se(ALIGN_POWER2(3) == 4);
-        assert_se(ALIGN_POWER2(4) == 4);
-        assert_se(ALIGN_POWER2(5) == 8);
-        assert_se(ALIGN_POWER2(6) == 8);
-        assert_se(ALIGN_POWER2(7) == 8);
-        assert_se(ALIGN_POWER2(9) == 16);
-        assert_se(ALIGN_POWER2(10) == 16);
-        assert_se(ALIGN_POWER2(11) == 16);
-        assert_se(ALIGN_POWER2(12) == 16);
-        assert_se(ALIGN_POWER2(13) == 16);
-        assert_se(ALIGN_POWER2(14) == 16);
-        assert_se(ALIGN_POWER2(15) == 16);
-        assert_se(ALIGN_POWER2(16) == 16);
-        assert_se(ALIGN_POWER2(17) == 32);
-
-        assert_se(ALIGN_POWER2(ULONG_MAX) == 0);
-        assert_se(ALIGN_POWER2(ULONG_MAX - 1) == 0);
-        assert_se(ALIGN_POWER2(ULONG_MAX - 1024) == 0);
-        assert_se(ALIGN_POWER2(ULONG_MAX / 2) == ULONG_MAX / 2 + 1);
-        assert_se(ALIGN_POWER2(ULONG_MAX + 1) == 0);
-
-        for (i = 1; i < 131071; ++i) {
-                for (p2 = 1; p2 < i; p2 <<= 1)
-                        /* empty */ ;
-
-                assert_se(ALIGN_POWER2(i) == p2);
-        }
-
-        for (i = ULONG_MAX - 1024; i < ULONG_MAX; ++i) {
-                for (p2 = 1; p2 && p2 < i; p2 <<= 1)
-                        /* empty */ ;
-
-                assert_se(ALIGN_POWER2(i) == p2);
-        }
-}
-
-static void test_max(void) {
-        static const struct {
-                int a;
-                int b[CONST_MAX(10, 100)];
-        } val1 = {
-                .a = CONST_MAX(10, 100),
-        };
-        int d = 0;
-        unsigned long x = 12345;
-        unsigned long y = 54321;
-        const char str[] = "a_string_constant";
-        const unsigned long long arr[] = {9999ULL, 10ULL, 0ULL, 3000ULL, 2000ULL, 1000ULL, 100ULL, 9999999ULL};
-        void *p = (void *)str;
-        void *q = (void *)&str[16];
-
-        log_info("/* %s */", __func__);
-
-        assert_cc(sizeof(val1.b) == sizeof(int) * 100);
-
-        /* CONST_MAX returns (void) instead of a value if the passed arguments
-         * are not of the same type or not constant expressions. */
-        assert_cc(__builtin_types_compatible_p(typeof(CONST_MAX(1, 10)), int));
-        assert_cc(__builtin_types_compatible_p(typeof(CONST_MAX(1, 1U)), void));
-
-        assert_se(val1.a == 100);
-        assert_se(MAX(++d, 0) == 1);
-        assert_se(d == 1);
-
-        assert_cc(MAXSIZE(char[3], uint16_t) == 3);
-        assert_cc(MAXSIZE(char[3], uint32_t) == 4);
-        assert_cc(MAXSIZE(char, long) == sizeof(long));
-
-        assert_se(MAX(-5, 5) == 5);
-        assert_se(MAX(5, 5) == 5);
-        assert_se(MAX(MAX(1, MAX(2, MAX(3, 4))), 5) == 5);
-        assert_se(MAX(MAX(1, MAX(2, MAX(3, 2))), 1) == 3);
-        assert_se(MAX(MIN(1, MIN(2, MIN(3, 4))), 5) == 5);
-        assert_se(MAX(MAX(1, MIN(2, MIN(3, 2))), 1) == 2);
-        assert_se(LESS_BY(8, 4) == 4);
-        assert_se(LESS_BY(8, 8) == 0);
-        assert_se(LESS_BY(4, 8) == 0);
-        assert_se(LESS_BY(16, LESS_BY(8, 4)) == 12);
-        assert_se(LESS_BY(4, LESS_BY(8, 4)) == 0);
-        assert_se(CMP(3, 5) == -1);
-        assert_se(CMP(5, 3) == 1);
-        assert_se(CMP(5, 5) == 0);
-        assert_se(CMP(x, y) == -1);
-        assert_se(CMP(y, x) == 1);
-        assert_se(CMP(x, x) == 0);
-        assert_se(CMP(y, y) == 0);
-        assert_se(CMP(UINT64_MAX, (uint64_t) 0) == 1);
-        assert_se(CMP((uint64_t) 0, UINT64_MAX) == -1);
-        assert_se(CMP(UINT64_MAX, UINT64_MAX) == 0);
-        assert_se(CMP(INT64_MIN, INT64_MAX) == -1);
-        assert_se(CMP(INT64_MAX, INT64_MIN) == 1);
-        assert_se(CMP(INT64_MAX, INT64_MAX) == 0);
-        assert_se(CMP(INT64_MIN, INT64_MIN) == 0);
-        assert_se(CMP(INT64_MAX, (int64_t) 0) == 1);
-        assert_se(CMP((int64_t) 0, INT64_MIN) == 1);
-        assert_se(CMP(INT64_MIN, (int64_t) 0) == -1);
-        assert_se(CMP((int64_t) 0, INT64_MAX) == -1);
-        assert_se(CMP(&str[2], &str[7]) == -1);
-        assert_se(CMP(&str[2], &str[2]) == 0);
-        assert_se(CMP(&str[7], (const char *)str) == 1);
-        assert_se(CMP(str[2], str[7]) == 1);
-        assert_se(CMP(str[7], *str) == 1);
-        assert_se(CMP((const unsigned long long *)arr, &arr[3]) == -1);
-        assert_se(CMP(*arr, arr[3]) == 1);
-        assert_se(CMP(p, q) == -1);
-        assert_se(CMP(q, p) == 1);
-        assert_se(CMP(p, p) == 0);
-        assert_se(CMP(q, q) == 0);
-        assert_se(CLAMP(-5, 0, 1) == 0);
-        assert_se(CLAMP(5, 0, 1) == 1);
-        assert_se(CLAMP(5, -10, 1) == 1);
-        assert_se(CLAMP(5, -10, 10) == 5);
-        assert_se(CLAMP(CLAMP(0, -10, 10), CLAMP(-5, 10, 20), CLAMP(100, -5, 20)) == 10);
-}
-
-#pragma GCC diagnostic push
-#ifdef __clang__
-#  pragma GCC diagnostic ignored "-Waddress-of-packed-member"
-#endif
-
-static void test_container_of(void) {
-        struct mytype {
-                uint8_t pad1[3];
-                uint64_t v1;
-                uint8_t pad2[2];
-                uint32_t v2;
-        } myval = { };
-
-        log_info("/* %s */", __func__);
-
-        assert_cc(sizeof(myval) >= 17);
-        assert_se(container_of(&myval.v1, struct mytype, v1) == &myval);
-        assert_se(container_of(&myval.v2, struct mytype, v2) == &myval);
-        assert_se(container_of(&container_of(&myval.v2,
-                                             struct mytype,
-                                             v2)->v1,
-                               struct mytype,
-                               v1) == &myval);
-}
-
-#pragma GCC diagnostic pop
-
-static void test_div_round_up(void) {
-        int div;
-
-        log_info("/* %s */", __func__);
-
-        /* basic tests */
-        assert_se(DIV_ROUND_UP(0, 8) == 0);
-        assert_se(DIV_ROUND_UP(1, 8) == 1);
-        assert_se(DIV_ROUND_UP(8, 8) == 1);
-        assert_se(DIV_ROUND_UP(12, 8) == 2);
-        assert_se(DIV_ROUND_UP(16, 8) == 2);
-
-        /* test multiple evaluation */
-        div = 0;
-        assert_se(DIV_ROUND_UP(div++, 8) == 0 && div == 1);
-        assert_se(DIV_ROUND_UP(++div, 8) == 1 && div == 2);
-        assert_se(DIV_ROUND_UP(8, div++) == 4 && div == 3);
-        assert_se(DIV_ROUND_UP(8, ++div) == 2 && div == 4);
-
-        /* overflow test with exact division */
-        assert_se(sizeof(0U) == 4);
-        assert_se(0xfffffffaU % 10U == 0U);
-        assert_se(0xfffffffaU / 10U == 429496729U);
-        assert_se(DIV_ROUND_UP(0xfffffffaU, 10U) == 429496729U);
-        assert_se((0xfffffffaU + 10U - 1U) / 10U == 0U);
-        assert_se(0xfffffffaU / 10U + !!(0xfffffffaU % 10U) == 429496729U);
-
-        /* overflow test with rounded division */
-        assert_se(0xfffffffdU % 10U == 3U);
-        assert_se(0xfffffffdU / 10U == 429496729U);
-        assert_se(DIV_ROUND_UP(0xfffffffdU, 10U) == 429496730U);
-        assert_se((0xfffffffdU + 10U - 1U) / 10U == 0U);
-        assert_se(0xfffffffdU / 10U + !!(0xfffffffdU % 10U) == 429496730U);
-}
-
 static void test_u64log2(void) {
         log_info("/* %s */", __func__);
 
@@ -249,18 +64,6 @@ static void test_unprotect_errno(void) {
         assert_se(errno == 4711);
 }
 
-static void test_in_set(void) {
-        log_info("/* %s */", __func__);
-
-        assert_se(IN_SET(1, 1));
-        assert_se(IN_SET(1, 1, 2, 3, 4));
-        assert_se(IN_SET(2, 1, 2, 3, 4));
-        assert_se(IN_SET(3, 1, 2, 3, 4));
-        assert_se(IN_SET(4, 1, 2, 3, 4));
-        assert_se(!IN_SET(0, 1));
-        assert_se(!IN_SET(0, 1, 2, 3, 4));
-}
-
 static void test_log2i(void) {
         log_info("/* %s */", __func__);
 
@@ -409,109 +212,12 @@ static void test_system_tasks_max_scale(void) {
         assert_se(system_tasks_max_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX);
 }
 
-static void test_foreach_pointer(void) {
-        int a, b, c, *i;
-        size_t k = 0;
-
-        log_info("/* %s */", __func__);
-
-        FOREACH_POINTER(i, &a, &b, &c) {
-                switch (k) {
-
-                case 0:
-                        assert_se(i == &a);
-                        break;
-
-                case 1:
-                        assert_se(i == &b);
-                        break;
-
-                case 2:
-                        assert_se(i == &c);
-                        break;
-
-                default:
-                        assert_not_reached("unexpected index");
-                        break;
-                }
-
-                k++;
-        }
-
-        assert(k == 3);
-
-        FOREACH_POINTER(i, &b) {
-                assert(k == 3);
-                assert(i == &b);
-                k = 4;
-        }
-
-        assert(k == 4);
-
-        FOREACH_POINTER(i, NULL, &c, NULL, &b, NULL, &a, NULL) {
-                switch (k) {
-
-                case 4:
-                        assert_se(i == NULL);
-                        break;
-
-                case 5:
-                        assert_se(i == &c);
-                        break;
-
-                case 6:
-                        assert_se(i == NULL);
-                        break;
-
-                case 7:
-                        assert_se(i == &b);
-                        break;
-
-                case 8:
-                        assert_se(i == NULL);
-                        break;
-
-                case 9:
-                        assert_se(i == &a);
-                        break;
-
-                case 10:
-                        assert_se(i == NULL);
-                        break;
-
-                default:
-                        assert_not_reached("unexpected index");
-                        break;
-                }
-
-                k++;
-        }
-
-        assert(k == 11);
-}
-
-static void test_ptr_to_int(void) {
-        log_info("/* %s */", __func__);
-
-        /* Primary reason to have this test is to validate that pointers are large enough to hold entire int range */
-        assert_se(PTR_TO_INT(INT_TO_PTR(0)) == 0);
-        assert_se(PTR_TO_INT(INT_TO_PTR(1)) == 1);
-        assert_se(PTR_TO_INT(INT_TO_PTR(-1)) == -1);
-        assert_se(PTR_TO_INT(INT_TO_PTR(INT_MAX)) == INT_MAX);
-        assert_se(PTR_TO_INT(INT_TO_PTR(INT_MIN)) == INT_MIN);
-}
-
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_INFO);
 
-        test_align_power2();
-        test_max();
-        test_container_of();
-        test_div_round_up();
         test_u64log2();
         test_protect_errno();
         test_unprotect_errno();
-        test_in_set();
         test_log2i();
         test_eqzero();
         test_raw_clone();
@@ -519,8 +225,6 @@ int main(int argc, char *argv[]) {
         test_physical_memory_scale();
         test_system_tasks_max();
         test_system_tasks_max_scale();
-        test_foreach_pointer();
-        test_ptr_to_int();
 
         return 0;
 }
index 4afc46f10f04a58e55a7d0800b5454662f83753d..885ed802d40d65b72939805fd99734896a45e7f9 100644 (file)
@@ -27,7 +27,7 @@ int main(int argc, char *argv[]) {
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
         assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
-        assert_se(manager_startup(m, NULL, NULL) >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
 
         assert_se(a = unit_new(m, sizeof(Service)));
         assert_se(unit_add_name(a, "a.service") >= 0);
index 7017b02ac9b8d481c2e48529cac557520a24f96a..3fbae5738fe2ff334df406d8cb30ad845abc913d 100644 (file)
 
 static void test_fgetxattrat_fake(void) {
         char t[] = "/var/tmp/xattrtestXXXXXX";
+        _cleanup_free_ char *value = NULL;
         _cleanup_close_ int fd = -1;
         const char *x;
         char v[3];
         int r;
         size_t size;
 
+        log_info("/* %s */", __func__);
+
         assert_se(mkdtemp(t));
         x = strjoina(t, "/test");
         assert_se(touch(x) >= 0);
@@ -45,6 +48,13 @@ static void test_fgetxattrat_fake(void) {
         r = fgetxattrat_fake(fd, "usr", "user.idontexist", v, 3, 0, &size);
         assert_se(r == -ENODATA || ERRNO_IS_NOT_SUPPORTED(r));
 
+        safe_close(fd);
+        fd = open(x, O_PATH|O_CLOEXEC);
+        assert_se(fd >= 0);
+        r = fgetxattrat_fake_malloc(fd, NULL, "user.foo", AT_EMPTY_PATH, &value);
+        assert_se(r == 3);
+        assert_se(streq(value, "bar"));
+
 cleanup:
         assert_se(unlink(x) >= 0);
         assert_se(rmdir(t) >= 0);
@@ -56,6 +66,8 @@ static void test_getcrtime(void) {
         usec_t usec, k;
         int r;
 
+        log_info("/* %s */", __func__);
+
         assert_se(tmp_dir(&vt) >= 0);
 
         fd = open_tmpfile_unlinkable(vt, O_RDWR);
index c0575c0323da4bed548aa897eaa56fb0a66adbee..6a4bbdbb480c60aab2bf499e65a8449540c6264d 100644 (file)
@@ -999,7 +999,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1;
index 556a2e9ba866040f5c67b84e164099c83e2abe9d..731dea12e3a54512338365f8013afab993b625d1 100644 (file)
@@ -25,3 +25,4 @@ Time.RootDistanceMaxSec,             config_parse_sec,     0,               offs
 Time.PollIntervalMinSec,             config_parse_sec,     0,               offsetof(Manager, poll_interval_min_usec)
 Time.PollIntervalMaxSec,             config_parse_sec,     0,               offsetof(Manager, poll_interval_max_usec)
 Time.ConnectionRetrySec,             config_parse_sec,     0,               offsetof(Manager, connection_retry_usec)
+Time.SaveIntervalSec,                config_parse_sec,     0,               offsetof(Manager, save_time_interval_usec)
index cb5d42b1d3ff1c7e1fdc34fcbe021e403c31bf3c..3a89d9b1fac175e01cb7e7a278cf272152b0971b 100644 (file)
@@ -59,6 +59,7 @@ static int manager_arm_timer(Manager *m, usec_t next);
 static int manager_clock_watch_setup(Manager *m);
 static int manager_listen_setup(Manager *m);
 static void manager_listen_stop(Manager *m);
+static int manager_save_time_and_rearm(Manager *m);
 
 static double ntp_ts_short_to_d(const struct ntp_ts_short *ts) {
         return be16toh(ts->sec) + (be16toh(ts->frac) / 65536.0);
@@ -303,8 +304,11 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) {
         if (r < 0)
                 return -errno;
 
+        r = manager_save_time_and_rearm(m);
+        if (r < 0)
+                return r;
+
         /* If touch fails, there isn't much we can do. Maybe it'll work next time. */
-        (void) touch("/var/lib/systemd/timesync/clock");
         (void) touch("/run/systemd/timesync/synchronized");
 
         m->drift_freq = tmx.freq;
@@ -412,7 +416,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                 .iov_base = &ntpmsg,
                 .iov_len = sizeof(ntpmsg),
         };
-        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct timeval))) control;
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct timespec))) control;
         union sockaddr_union server_addr;
         struct msghdr msghdr = {
                 .msg_iov = &iov,
@@ -591,7 +595,6 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                   m->poll_interval_usec / USEC_PER_SEC);
 
         if (!spike) {
-                m->sync = true;
                 r = manager_adjust_clock(m, offset, leap_sec);
                 if (r < 0)
                         log_error_errno(r, "Failed to call clock_adjtime(): %m");
@@ -942,6 +945,8 @@ Manager* manager_free(Manager *m) {
         sd_event_source_unref(m->network_event_source);
         sd_network_monitor_unref(m->network_monitor);
 
+        sd_event_source_unref(m->event_save_time);
+
         sd_resolve_unref(m->resolve);
         sd_event_unref(m->event);
 
@@ -1104,6 +1109,8 @@ int manager_new(Manager **ret) {
 
         m->ratelimit = (RateLimit) { RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST };
 
+        m->save_time_interval_usec = DEFAULT_SAVE_TIME_INTERVAL_USEC;
+
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
@@ -1131,3 +1138,60 @@ int manager_new(Manager **ret) {
 
         return 0;
 }
+
+static int manager_save_time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+        Manager *m = userdata;
+
+        assert(m);
+
+        (void) manager_save_time_and_rearm(m);
+        return 0;
+}
+
+int manager_setup_save_time_event(Manager *m) {
+        int r;
+
+        assert(m);
+        assert(!m->event_save_time);
+
+        if (m->save_time_interval_usec == USEC_INFINITY)
+                return 0;
+
+        /* NB: we'll accumulate scheduling latencies here, but this doesn't matter */
+        r = sd_event_add_time_relative(
+                        m->event, &m->event_save_time,
+                        clock_boottime_or_monotonic(),
+                        m->save_time_interval_usec,
+                        10 * USEC_PER_SEC,
+                        manager_save_time_handler, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add save time event: %m");
+
+        (void) sd_event_source_set_description(m->event_save_time, "save-time");
+
+        return 0;
+}
+
+static int manager_save_time_and_rearm(Manager *m) {
+        int r;
+
+        assert(m);
+
+        r = touch(CLOCK_FILE);
+        if (r < 0)
+                log_debug_errno(r, "Failed to update " CLOCK_FILE ", ignoring: %m");
+
+        m->save_on_exit = true;
+
+        if (m->save_time_interval_usec != USEC_INFINITY) {
+                r = sd_event_source_set_time_relative(m->event_save_time, m->save_time_interval_usec);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to rearm save time event: %m");
+
+                r = sd_event_source_set_enabled(m->event_save_time, SD_EVENT_ONESHOT);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to enable save time event: %m");
+        }
+
+        return 0;
+}
index 4aa7575a8029f877d39d424be12148609dfc3107..aceb0098cef37cc26cb5e7218d0af538de44075f 100644 (file)
@@ -27,7 +27,12 @@ typedef struct Manager Manager;
 #define NTP_RETRY_INTERVAL_MIN_USEC     (15 * USEC_PER_SEC)
 #define NTP_RETRY_INTERVAL_MAX_USEC     (6 * 60 * USEC_PER_SEC) /* 6 minutes */
 
-#define DEFAULT_CONNECTION_RETRY_USEC (30*USEC_PER_SEC)
+#define DEFAULT_CONNECTION_RETRY_USEC   (30 * USEC_PER_SEC)
+
+#define DEFAULT_SAVE_TIME_INTERVAL_USEC (60 * USEC_PER_SEC)
+
+#define STATE_DIR   "/var/lib/systemd/timesync"
+#define CLOCK_FILE  STATE_DIR "/clock"
 
 struct Manager {
         sd_bus *bus;
@@ -83,7 +88,6 @@ struct Manager {
 
         /* last change */
         bool jumped;
-        bool sync;
         int64_t drift_freq;
 
         /* watch for time changes */
@@ -100,6 +104,11 @@ struct Manager {
         struct ntp_msg ntpmsg;
         struct timespec origin_time, dest_time;
         bool spike;
+
+        /* save time event */
+        sd_event_source *event_save_time;
+        usec_t save_time_interval_usec;
+        bool save_on_exit;
 };
 
 int manager_new(Manager **ret);
@@ -113,3 +122,5 @@ void manager_flush_server_names(Manager *m, ServerType t);
 
 int manager_connect(Manager *m);
 void manager_disconnect(Manager *m);
+
+int manager_setup_save_time_event(Manager *m);
index f7ec317f413df91ed293038d0f532454fcbcb141..79dfd47266965193d04795bca9f52634b1ac5f00 100644 (file)
@@ -79,7 +79,7 @@ int server_name_new(
                 LIST_FIND_TAIL(names, m->fallback_servers, tail);
                 LIST_INSERT_AFTER(names, m->fallback_servers, tail, n);
         } else
-                assert_not_reached("Unknown server type");
+                assert_not_reached();
 
         n->manager = m;
 
@@ -110,7 +110,7 @@ ServerName *server_name_free(ServerName *n) {
                 else if (n->type == SERVER_FALLBACK)
                         LIST_REMOVE(names, n->manager->fallback_servers, n);
                 else
-                        assert_not_reached("Unknown server type");
+                        assert_not_reached();
 
                 if (n->manager->current_server_name == n)
                         manager_set_server_name(n->manager, NULL);
index e6a2b06687346460d526c77be00a324f8613a246..179562696d2dd4206566799a7de91c1fe13488c9 100644 (file)
@@ -21,9 +21,6 @@
 #include "timesyncd-manager.h"
 #include "user-util.h"
 
-#define STATE_DIR   "/var/lib/systemd/timesync"
-#define CLOCK_FILE  STATE_DIR "/clock"
-
 static int load_clock_timestamp(uid_t uid, gid_t gid) {
         _cleanup_close_ int fd = -1;
         usec_t min = TIME_EPOCH * USEC_PER_SEC;
@@ -155,6 +152,10 @@ static int run(int argc, char *argv[]) {
                                       "STATUS=Daemon is running",
                                       NOTIFY_STOPPING);
 
+        r = manager_setup_save_time_event(m);
+        if (r < 0)
+                return r;
+
         if (network_is_online()) {
                 r = manager_connect(m);
                 if (r < 0)
@@ -166,10 +167,10 @@ static int run(int argc, char *argv[]) {
                 return log_error_errno(r, "Failed to run event loop: %m");
 
         /* if we got an authoritative time, store it in the file system */
-        if (m->sync) {
+        if (m->save_on_exit) {
                 r = touch(CLOCK_FILE);
                 if (r < 0)
-                        log_debug_errno(r, "Failed to touch %s, ignoring: %m", CLOCK_FILE);
+                        log_debug_errno(r, "Failed to touch " CLOCK_FILE ", ignoring: %m");
         }
 
         return 0;
index d5f29e1598cb4ecba393b20d5645c7907d95ff57..df02cb5bd60bcddd4774685b68a39aafa10ff7ca 100644 (file)
@@ -18,3 +18,4 @@
 #RootDistanceMaxSec=5
 #PollIntervalMinSec=32
 #PollIntervalMaxSec=2048
+#SaveIntervalSec=60
index 1a961d125bbdde3e250224b343d660ca306cf79a..e509c63961ea13ec70fccfd014440ba24b3c9b30 100644 (file)
@@ -60,7 +60,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 0;
index fb90f6bfbb430211e81ab7967ac2a20f631a04ad..38e9844d3575baab7c8e20b5da4f5a23a0cd8448 100644 (file)
@@ -1058,18 +1058,15 @@ static int parse_xattrs_from_arg(Item *i) {
 }
 
 static int fd_set_xattrs(Item *i, int fd, const char *path, const struct stat *st) {
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         char **name, **value;
 
         assert(i);
         assert(fd >= 0);
         assert(path);
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-
         STRV_FOREACH_PAIR(name, value, i->xattrs) {
                 log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path);
-                if (setxattr(procfs_path, *name, *value, strlen(*value), 0) < 0)
+                if (setxattr(FORMAT_PROC_FD_PATH(fd), *name, *value, strlen(*value), 0) < 0)
                         return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m",
                                                *name, *value, path);
         }
@@ -1161,7 +1158,6 @@ static int path_set_acl(const char *path, const char *pretty, acl_type_t type, a
 static int fd_set_acls(Item *item, int fd, const char *path, const struct stat *st) {
         int r = 0;
 #if HAVE_ACL
-        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         struct stat stbuf;
 
         assert(item);
@@ -1184,14 +1180,12 @@ static int fd_set_acls(Item *item, int fd, const char *path, const struct stat *
                 return 0;
         }
 
-        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-
         if (item->acl_access)
-                r = path_set_acl(procfs_path, path, ACL_TYPE_ACCESS, item->acl_access, item->append_or_force);
+                r = path_set_acl(FORMAT_PROC_FD_PATH(fd), path, ACL_TYPE_ACCESS, item->acl_access, item->append_or_force);
 
         /* set only default acls to folders */
         if (r == 0 && item->acl_default && S_ISDIR(st->st_mode))
-                r = path_set_acl(procfs_path, path, ACL_TYPE_DEFAULT, item->acl_default, item->append_or_force);
+                r = path_set_acl(FORMAT_PROC_FD_PATH(fd), path, ACL_TYPE_DEFAULT, item->acl_default, item->append_or_force);
 
         if (ERRNO_IS_NOT_SUPPORTED(r)) {
                 log_debug_errno(r, "ACLs not supported by file system at %s", path);
@@ -1938,17 +1932,14 @@ static int item_do(Item *i, int fd, const char *path, fdaction_t action) {
         r = action(i, fd, path, &st);
 
         if (S_ISDIR(st.st_mode)) {
-                char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
                 _cleanup_closedir_ DIR *d = NULL;
                 struct dirent *de;
 
-                /* The passed 'fd' was opened with O_PATH. We need to convert
-                 * it into a 'regular' fd before reading the directory content. */
-                xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-
-                d = opendir(procfs_path);
+                /* The passed 'fd' was opened with O_PATH. We need to convert it into a 'regular' fd before
+                 * reading the directory content. */
+                d = opendir(FORMAT_PROC_FD_PATH(fd));
                 if (!d) {
-                        log_error_errno(errno, "Failed to opendir() '%s': %m", procfs_path);
+                        log_error_errno(errno, "Failed to opendir() '%s': %m", FORMAT_PROC_FD_PATH(fd));
                         if (r == 0)
                                 r = -errno;
                         goto finish;
@@ -2447,7 +2438,7 @@ static int remove_item_instance(Item *i, const char *instance) {
                 break;
 
         default:
-                assert_not_reached("wut?");
+                assert_not_reached();
         }
 
         return 0;
@@ -3505,7 +3496,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (arg_operation == 0 && !arg_cat_config)
@@ -3853,7 +3844,7 @@ static int run(int argc, char *argv[]) {
                 else if (phase == PHASE_CREATE)
                         op = arg_operation & OPERATION_CREATE;
                 else
-                        assert_not_reached("unexpected phase");
+                        assert_not_reached();
 
                 if (op == 0) /* Nothing requested in this phase */
                         continue;
index 59b144972b82c0a0d757536901c31d7eee7c55ee..7699bad9e048218a09b6be1360e872f8d60c2739 100644 (file)
@@ -508,7 +508,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         if (optind != argc)
index df0ee6a3bb2653adc20dfcbd750a861ca7fd5685..a2185350d393af19552cf9b5ffe147a79dd9bc03 100644 (file)
@@ -943,7 +943,7 @@ static int parse_argv(int argc, char *argv[]) {
                 case 'h':
                         return help();
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         arg_node = argv[optind];
index 64eba0d314e34d203920aa8dff5f81672fe6f20c..4c0ec2eccb97c51974d3bb4d73d1e69c472f4d19 100644 (file)
@@ -665,7 +665,7 @@ static int parse_argv(int argc, char * const *argv) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         return 1;
index 4e80f9bfd715dc78c5d26615f8103e83e497d32e..87bb341a56259cd69471ca4169023335b4ce1e89 100644 (file)
@@ -115,47 +115,45 @@ libudevd_core = static_library(
         link_with : udev_link_with,
         dependencies : [libblkid, libkmod])
 
-udev_id_progs = [['ata_id/ata_id.c'],
-                 ['cdrom_id/cdrom_id.c'],
-                 ['fido_id/fido_id.c',
-                  'fido_id/fido_id_desc.c',
-                  'fido_id/fido_id_desc.h'],
-                 ['scsi_id/scsi_id.c',
-                  'scsi_id/scsi_id.h',
-                  'scsi_id/scsi_serial.c',
-                  'scsi_id/scsi.h'],
-                 ['v4l_id/v4l_id.c'],
-                 ['mtd_probe/mtd_probe.c',
-                  'mtd_probe/mtd_probe.h',
-                  'mtd_probe/probe_smartmedia.c']]
+udev_progs = [['ata_id/ata_id.c'],
+              ['cdrom_id/cdrom_id.c'],
+              ['fido_id/fido_id.c',
+               'fido_id/fido_id_desc.c',
+               'fido_id/fido_id_desc.h'],
+              ['scsi_id/scsi_id.c',
+               'scsi_id/scsi_id.h',
+               'scsi_id/scsi_serial.c',
+               'scsi_id/scsi.h'],
+              ['v4l_id/v4l_id.c'],
+              ['mtd_probe/mtd_probe.c',
+               'mtd_probe/mtd_probe.h',
+               'mtd_probe/probe_smartmedia.c']]
 
 dmi_arches = ['x86', 'x86_64', 'aarch64', 'arm', 'ia64', 'mips']
 if dmi_arches.contains(host_machine.cpu_family())
-        udev_id_progs += [['dmi_memory_id/dmi_memory_id.c']]
+        udev_progs += [['dmi_memory_id/dmi_memory_id.c']]
 endif
 
-foreach prog : udev_id_progs
+udev_prog_paths = {}
+foreach prog : udev_progs
         name = prog[0].split('/')[0]
 
         exe = executable(
                 name,
                 prog,
                 include_directories : includes,
-                dependencies : [versiondep],
+                dependencies : versiondep,
                 link_with : udev_link_with,
                 install_rpath : udev_rpath,
                 install : true,
                 install_dir : udevlibexecdir)
 
-        # TODO: let's use a dictionary instead as soon as we can depend on meson >= 0.47.
-        if name == 'dmi_memory_id'
-                dmi_memory_id_path = exe.full_path()
-        endif
+        udev_prog_paths += {name : exe.full_path()}
 endforeach
 
 if install_sysconfdir_samples
         install_data('udev.conf',
-                     install_dir : join_paths(sysconfdir, 'udev'))
+                     install_dir : sysconfdir / 'udev')
 endif
 
 custom_target(
@@ -169,7 +167,7 @@ custom_target(
 
 if install_sysconfdir
         meson.add_install_script('sh', '-c',
-                                 mkdir_p.format(join_paths(sysconfdir, 'udev/rules.d')))
+                                 mkdir_p.format(sysconfdir / 'udev/rules.d'))
 endif
 
 fuzzers += [
index e2f07d758be677202928cda65026f351795b7432..44b46cb17c0bd3e2d472d858c0ada9d971cd24c6 100644 (file)
@@ -21,54 +21,77 @@ struct ConfigPerfItem;
 %struct-type
 %includes
 %%
-Match.MACAddress,                      config_parse_hwaddrs,                  0,                             offsetof(LinkConfig, match.mac)
-Match.PermanentMACAddress,             config_parse_hwaddrs,                  0,                             offsetof(LinkConfig, match.permanent_mac)
-Match.OriginalName,                    config_parse_match_ifnames,            0,                             offsetof(LinkConfig, match.ifname)
-Match.Path,                            config_parse_match_strv,               0,                             offsetof(LinkConfig, match.path)
-Match.Driver,                          config_parse_match_strv,               0,                             offsetof(LinkConfig, match.driver)
-Match.Type,                            config_parse_match_strv,               0,                             offsetof(LinkConfig, match.iftype)
-Match.Property,                        config_parse_match_property,           0,                             offsetof(LinkConfig, match.property)
-Match.Host,                            config_parse_net_condition,            CONDITION_HOST,                offsetof(LinkConfig, conditions)
-Match.Virtualization,                  config_parse_net_condition,            CONDITION_VIRTUALIZATION,      offsetof(LinkConfig, conditions)
-Match.KernelCommandLine,               config_parse_net_condition,            CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions)
-Match.KernelVersion,                   config_parse_net_condition,            CONDITION_KERNEL_VERSION,      offsetof(LinkConfig, conditions)
-Match.Architecture,                    config_parse_net_condition,            CONDITION_ARCHITECTURE,        offsetof(LinkConfig, conditions)
-Link.Description,                      config_parse_string,                   0,                             offsetof(LinkConfig, description)
-Link.MACAddressPolicy,                 config_parse_mac_address_policy,       0,                             offsetof(LinkConfig, mac_address_policy)
-Link.MACAddress,                       config_parse_hwaddr,                   0,                             offsetof(LinkConfig, mac)
-Link.NamePolicy,                       config_parse_name_policy,              0,                             offsetof(LinkConfig, name_policy)
-Link.Name,                             config_parse_ifname,                   0,                             offsetof(LinkConfig, name)
-Link.AlternativeName,                  config_parse_ifnames,                  IFNAME_VALID_ALTERNATIVE,      offsetof(LinkConfig, alternative_names)
-Link.AlternativeNamesPolicy,           config_parse_alternative_names_policy, 0,                             offsetof(LinkConfig, alternative_names_policy)
-Link.Alias,                            config_parse_ifalias,                  0,                             offsetof(LinkConfig, alias)
-Link.TransmitQueues,                   config_parse_rx_tx_queues,             0,                             offsetof(LinkConfig, txqueues)
-Link.ReceiveQueues,                    config_parse_rx_tx_queues,             0,                             offsetof(LinkConfig, rxqueues)
-Link.TransmitQueueLength,              config_parse_txqueuelen,               0,                             offsetof(LinkConfig, txqueuelen)
-Link.MTUBytes,                         config_parse_mtu,                      AF_UNSPEC,                     offsetof(LinkConfig, mtu)
-Link.BitsPerSecond,                    config_parse_si_uint64,                0,                             offsetof(LinkConfig, speed)
-Link.Duplex,                           config_parse_duplex,                   0,                             offsetof(LinkConfig, duplex)
-Link.AutoNegotiation,                  config_parse_tristate,                 0,                             offsetof(LinkConfig, autonegotiation)
-Link.WakeOnLan,                        config_parse_wol,                      0,                             offsetof(LinkConfig, wol)
-Link.Port,                             config_parse_port,                     0,                             offsetof(LinkConfig, port)
-Link.ReceiveChecksumOffload,           config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_RX])
-Link.TransmitChecksumOffload,          config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TX])
-Link.GenericSegmentationOffload,       config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_GSO])
-Link.TCPSegmentationOffload,           config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TSO])
-Link.TCP6SegmentationOffload,          config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TSO6])
-Link.UDPSegmentationOffload,           config_parse_warn_compat,              DISABLED_LEGACY,               0
-Link.GenericReceiveOffload,            config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_GRO])
-Link.LargeReceiveOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_LRO])
-Link.RxChannels,                       config_parse_channel,                  0,                             offsetof(LinkConfig, channels)
-Link.TxChannels,                       config_parse_channel,                  0,                             offsetof(LinkConfig, channels)
-Link.OtherChannels,                    config_parse_channel,                  0,                             offsetof(LinkConfig, channels)
-Link.CombinedChannels,                 config_parse_channel,                  0,                             offsetof(LinkConfig, channels)
-Link.Advertise,                        config_parse_advertise,                0,                             offsetof(LinkConfig, advertise)
-Link.RxBufferSize,                     config_parse_nic_buffer_size,          0,                             offsetof(LinkConfig, ring)
-Link.RxMiniBufferSize,                 config_parse_nic_buffer_size,          0,                             offsetof(LinkConfig, ring)
-Link.RxJumboBufferSize,                config_parse_nic_buffer_size,          0,                             offsetof(LinkConfig, ring)
-Link.TxBufferSize,                     config_parse_nic_buffer_size,          0,                             offsetof(LinkConfig, ring)
-Link.RxFlowControl,                    config_parse_tristate,                 0,                             offsetof(LinkConfig, rx_flow_control)
-Link.TxFlowControl,                    config_parse_tristate,                 0,                             offsetof(LinkConfig, tx_flow_control)
-Link.AutoNegotiationFlowControl,       config_parse_tristate,                 0,                             offsetof(LinkConfig, autoneg_flow_control)
-Link.GenericSegmentOffloadMaxBytes,    config_parse_iec_size,                 0,                             offsetof(LinkConfig, gso_max_size)
-Link.GenericSegmentOffloadMaxSegments, config_parse_uint32,                   0,                             offsetof(LinkConfig, gso_max_segments)
+Match.MACAddress,                         config_parse_hwaddrs,                  0,                             offsetof(LinkConfig, match.mac)
+Match.PermanentMACAddress,                config_parse_hwaddrs,                  0,                             offsetof(LinkConfig, match.permanent_mac)
+Match.OriginalName,                       config_parse_match_ifnames,            0,                             offsetof(LinkConfig, match.ifname)
+Match.Path,                               config_parse_match_strv,               0,                             offsetof(LinkConfig, match.path)
+Match.Driver,                             config_parse_match_strv,               0,                             offsetof(LinkConfig, match.driver)
+Match.Type,                               config_parse_match_strv,               0,                             offsetof(LinkConfig, match.iftype)
+Match.Property,                           config_parse_match_property,           0,                             offsetof(LinkConfig, match.property)
+Match.Host,                               config_parse_net_condition,            CONDITION_HOST,                offsetof(LinkConfig, conditions)
+Match.Virtualization,                     config_parse_net_condition,            CONDITION_VIRTUALIZATION,      offsetof(LinkConfig, conditions)
+Match.KernelCommandLine,                  config_parse_net_condition,            CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions)
+Match.KernelVersion,                      config_parse_net_condition,            CONDITION_KERNEL_VERSION,      offsetof(LinkConfig, conditions)
+Match.Architecture,                       config_parse_net_condition,            CONDITION_ARCHITECTURE,        offsetof(LinkConfig, conditions)
+Link.Description,                         config_parse_string,                   0,                             offsetof(LinkConfig, description)
+Link.MACAddressPolicy,                    config_parse_mac_address_policy,       0,                             offsetof(LinkConfig, mac_address_policy)
+Link.MACAddress,                          config_parse_hwaddr,                   0,                             offsetof(LinkConfig, mac)
+Link.NamePolicy,                          config_parse_name_policy,              0,                             offsetof(LinkConfig, name_policy)
+Link.Name,                                config_parse_ifname,                   0,                             offsetof(LinkConfig, name)
+Link.AlternativeName,                     config_parse_ifnames,                  IFNAME_VALID_ALTERNATIVE,      offsetof(LinkConfig, alternative_names)
+Link.AlternativeNamesPolicy,              config_parse_alternative_names_policy, 0,                             offsetof(LinkConfig, alternative_names_policy)
+Link.Alias,                               config_parse_ifalias,                  0,                             offsetof(LinkConfig, alias)
+Link.TransmitQueues,                      config_parse_rx_tx_queues,             0,                             offsetof(LinkConfig, txqueues)
+Link.ReceiveQueues,                       config_parse_rx_tx_queues,             0,                             offsetof(LinkConfig, rxqueues)
+Link.TransmitQueueLength,                 config_parse_txqueuelen,               0,                             offsetof(LinkConfig, txqueuelen)
+Link.MTUBytes,                            config_parse_mtu,                      AF_UNSPEC,                     offsetof(LinkConfig, mtu)
+Link.BitsPerSecond,                       config_parse_si_uint64,                0,                             offsetof(LinkConfig, speed)
+Link.Duplex,                              config_parse_duplex,                   0,                             offsetof(LinkConfig, duplex)
+Link.AutoNegotiation,                     config_parse_tristate,                 0,                             offsetof(LinkConfig, autonegotiation)
+Link.WakeOnLan,                           config_parse_wol,                      0,                             offsetof(LinkConfig, wol)
+Link.Port,                                config_parse_port,                     0,                             offsetof(LinkConfig, port)
+Link.ReceiveChecksumOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_RX])
+Link.TransmitChecksumOffload,             config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TX])
+Link.GenericSegmentationOffload,          config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_GSO])
+Link.TCPSegmentationOffload,              config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TSO])
+Link.TCP6SegmentationOffload,             config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_TSO6])
+Link.UDPSegmentationOffload,              config_parse_warn_compat,              DISABLED_LEGACY,               0
+Link.GenericReceiveOffload,               config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_GRO])
+Link.GenericReceiveOffloadHardware,       config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_GRO_HW])
+Link.LargeReceiveOffload,                 config_parse_tristate,                 0,                             offsetof(LinkConfig, features[NET_DEV_FEAT_LRO])
+Link.RxChannels,                          config_parse_ring_buffer_or_channel,   0,                             offsetof(LinkConfig, channels.rx)
+Link.TxChannels,                          config_parse_ring_buffer_or_channel,   0,                             offsetof(LinkConfig, channels.tx)
+Link.OtherChannels,                       config_parse_ring_buffer_or_channel,   0,                             offsetof(LinkConfig, channels.other)
+Link.CombinedChannels,                    config_parse_ring_buffer_or_channel,   0,                             offsetof(LinkConfig, channels.combined)
+Link.Advertise,                           config_parse_advertise,                0,                             offsetof(LinkConfig, advertise)
+Link.RxBufferSize,                        config_parse_ring_buffer_or_channel,   0,                             offsetof(LinkConfig, ring.rx)
+Link.RxMiniBufferSize,                    config_parse_ring_buffer_or_channel,   0,                             offsetof(LinkConfig, ring.rx_mini)
+Link.RxJumboBufferSize,                   config_parse_ring_buffer_or_channel,   0,                             offsetof(LinkConfig, ring.rx_jumbo)
+Link.TxBufferSize,                        config_parse_ring_buffer_or_channel,   0,                             offsetof(LinkConfig, ring.tx)
+Link.RxFlowControl,                       config_parse_tristate,                 0,                             offsetof(LinkConfig, rx_flow_control)
+Link.TxFlowControl,                       config_parse_tristate,                 0,                             offsetof(LinkConfig, tx_flow_control)
+Link.AutoNegotiationFlowControl,          config_parse_tristate,                 0,                             offsetof(LinkConfig, autoneg_flow_control)
+Link.GenericSegmentOffloadMaxBytes,       config_parse_iec_size,                 0,                             offsetof(LinkConfig, gso_max_size)
+Link.GenericSegmentOffloadMaxSegments,    config_parse_uint32,                   0,                             offsetof(LinkConfig, gso_max_segments)
+Link.RxCoalesceSec,                       config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.rx_coalesce_usecs)
+Link.RxMaxCoalescedFrames,                config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.rx_max_coalesced_frames)
+Link.RxCoalesceIrqSec,                    config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.rx_coalesce_usecs_irq)
+Link.RxMaxCoalescedIrqFrames,             config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.rx_max_coalesced_frames_irq)
+Link.TxCoalesceSec,                       config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.tx_coalesce_usecs)
+Link.TxMaxCoalescedFrames,                config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.tx_max_coalesced_frames)
+Link.TxCoalesceIrqSec,                    config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.tx_coalesce_usecs_irq)
+Link.TxMaxCoalescedIrqFrames,             config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_irq)
+Link.StatisticsBlockCoalesceSec,          config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.stats_block_coalesce_usecs)
+Link.UseAdaptiveRxCoalesce,               config_parse_tristate,                 0,                             offsetof(LinkConfig, coalesce.use_adaptive_rx_coalesce)
+Link.UseAdaptiveTxCoalesce,               config_parse_tristate,                 0,                             offsetof(LinkConfig, coalesce.use_adaptive_tx_coalesce)
+Link.CoalescePacketRateLow,               config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.pkt_rate_low)
+Link.RxCoalesceLowSec,                    config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.rx_coalesce_usecs_low)
+Link.RxMaxCoalescedLowFrames,             config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.rx_max_coalesced_frames_low)
+Link.TxCoalesceLowSec,                    config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.tx_coalesce_usecs_low)
+Link.TxMaxCoalescedLowFrames,             config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_low)
+Link.CoalescePacketRateHigh,              config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.pkt_rate_high)
+Link.RxCoalesceHighSec,                   config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.rx_coalesce_usecs_high)
+Link.RxMaxCoalescedHighFrames,            config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.rx_max_coalesced_frames_high)
+Link.TxCoalesceHighSec,                   config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.tx_coalesce_usecs_high)
+Link.TxMaxCoalescedHighFrames,            config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_high)
+Link.CoalescePacketRateSampleIntervalSec, config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.rate_sample_interval)
index 8dfe23691bc42110212c62dd2deebae4a6bdd222..69f65143503484fa5e816a333c01245ff174042a 100644 (file)
@@ -353,6 +353,10 @@ static int link_config_apply_ethtool_settings(int *ethtool_fd, const LinkConfig
         if (r < 0)
                 log_device_warning_errno(device, r, "Could not set flow control, ignoring: %m");
 
+        r = ethtool_set_nic_coalesce_settings(ethtool_fd, name, &config->coalesce);
+        if (r < 0)
+                log_device_warning_errno(device, r, "Could not set coalesce settings, ignoring: %m");
+
         return 0;
 }
 
@@ -501,7 +505,7 @@ static int link_config_generate_new_name(const LinkConfigContext *ctx, const Lin
                                 (void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &new_name);
                                 break;
                         default:
-                                assert_not_reached("invalid policy");
+                                assert_not_reached();
                         }
                         if (ifname_valid(new_name)) {
                                 log_device_debug(device, "Policy *%s* yields \"%s\".", name_policy_to_string(policy), new_name);
@@ -569,7 +573,7 @@ static int link_config_apply_alternative_names(sd_netlink **rtnl, const LinkConf
                                 (void) sd_device_get_property_value(device, "ID_NET_NAME_MAC", &n);
                                 break;
                         default:
-                                assert_not_reached("invalid policy");
+                                assert_not_reached();
                         }
                         if (!isempty(n)) {
                                 r = strv_extend(&altnames, n);
index b505c94f958330db0e3fbadc6046a0caa63ca9cc..8a29a928228419444dfff4549741cb5347f54040 100644 (file)
@@ -64,6 +64,7 @@ struct LinkConfig {
         int rx_flow_control;
         int tx_flow_control;
         int autoneg_flow_control;
+        netdev_coalesce_param coalesce;
 
         LIST_FIELDS(LinkConfig, links);
 };
index b2d8154d86cbf0e8ed2fecba3d4670b638e602e3..8ce73d03da8e70cb3c1536a6aa981169da337857 100644 (file)
@@ -306,7 +306,7 @@ static int set_options(int argc, char **argv,
                         return -1;
 
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         if (optind < argc && !dev_specified) {
index fd1752c65fd54dad7780249a9ed08e7197f4bf72..b1a631dea2f17f37b4a589328771ddc5816cc140 100644 (file)
@@ -83,7 +83,7 @@ int main(int argc, char *argv[]) {
                 else if (streq(argv[1], "test2"))
                         test2();
                 else
-                        assert_not_reached("unknown command.");
+                        assert_not_reached();
 
                 return 0;
         }
index b1de363083ca8d65dfa883dfe96b02db623d54bd..cdb1df7c04d20f65421d97e5f1305c41c8ce776c 100644 (file)
@@ -116,8 +116,7 @@ static int find_gpt_root(sd_device *dev, blkid_probe pr, bool test) {
 
         _cleanup_free_ char *root_id = NULL, *root_label = NULL;
         bool found_esp = false;
-        blkid_partlist pl;
-        int i, nvals, r;
+        int r;
 
         assert(pr);
 
@@ -126,12 +125,12 @@ static int find_gpt_root(sd_device *dev, blkid_probe pr, bool test) {
          * disk, and add a property indicating its partition UUID. */
 
         errno = 0;
-        pl = blkid_probe_get_partitions(pr);
+        blkid_partlist pl = blkid_probe_get_partitions(pr);
         if (!pl)
                 return errno_or_else(ENOMEM);
 
-        nvals = blkid_partlist_numof_partitions(pl);
-        for (i = 0; i < nvals; i++) {
+        int nvals = blkid_partlist_numof_partitions(pl);
+        for (int i = 0; i < nvals; i++) {
                 blkid_partition pp;
                 const char *stype, *sid, *label;
                 sd_id128_t type;
@@ -240,7 +239,7 @@ static int builtin_blkid(sd_device *dev, int argc, char *argv[], bool test) {
         bool noraid = false, is_gpt = false;
         _cleanup_close_ int fd = -1;
         int64_t offset = 0;
-        int nvals, i, r;
+        int r;
 
         static const struct option options[] = {
                 { "offset", required_argument, NULL, 'o' },
@@ -325,11 +324,11 @@ static int builtin_blkid(sd_device *dev, int argc, char *argv[], bool test) {
         (void) sd_device_get_property_value(dev, "ID_PART_GPT_AUTO_ROOT_UUID", &root_partition);
 
         errno = 0;
-        nvals = blkid_probe_numof_values(pr);
+        int nvals = blkid_probe_numof_values(pr);
         if (nvals < 0)
                 return log_device_debug_errno(dev, errno_or_else(ENOMEM), "Failed to get number of probed values: %m");
 
-        for (i = 0; i < nvals; i++) {
+        for (int i = 0; i < nvals; i++) {
                 if (blkid_probe_get_value(pr, i, &name, &data, NULL) < 0)
                         continue;
 
index 78835185b07345d12c4509a30f4a25b2438c1490..87535dbd005469c15acd2ee9f2efab9373a6f381 100644 (file)
@@ -60,7 +60,7 @@ static const char *modalias_usb(sd_device *dev, char *s, size_t size) {
                 return NULL;
         (void) sd_device_get_sysattr_value(dev, "product", &n);
 
-        snprintf(s, size, "usb:v%04Xp%04X:%s", vn, pn, strempty(n));
+        (void) snprintf(s, size, "usb:v%04Xp%04X:%s", vn, pn, strempty(n));
         return s;
 }
 
index dda53b6da0f25dee7d73e8626bd66e0b30d9dc51..08a9e9c65d549a692fda09c100b64634367e2439 100644 (file)
@@ -92,7 +92,7 @@ static void get_cap_mask(sd_device *pdev, const char* attr,
 
         memzero(bitmask, bitmask_size);
         i = 0;
-        while ((word = strrchr(text, ' ')) != NULL) {
+        while ((word = strrchr(text, ' '))) {
                 r = safe_atolu_full(word+1, 16, &val);
                 if (r < 0)
                         log_device_debug_errno(pdev, r, "Ignoring %s block which failed to parse: %m", attr);
@@ -101,7 +101,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);
                 *word = '\0';
-                ++i;
+                i++;
         }
         r = safe_atolu_full(text, 16, &val);
         if (r < 0)
@@ -120,9 +120,9 @@ static void get_cap_mask(sd_device *pdev, const char* attr,
                 /* skip over leading zeros */
                 while (bitmask[val-1] == 0 && val > 0)
                         --val;
-                for (i = 0; i < val; ++i) {
+                for (unsigned long j = 0; j < val; j++) {
                         DISABLE_WARNING_FORMAT_NONLITERAL;
-                        log_device_debug(pdev, text, i * BITS_PER_LONG, bitmask[i]);
+                        log_device_debug(pdev, text, j * BITS_PER_LONG, bitmask[j]);
                         REENABLE_WARNING;
                 }
         }
@@ -153,7 +153,6 @@ static bool test_pointers(sd_device *dev,
                           const unsigned long* bitmask_rel,
                           const unsigned long* bitmask_props,
                           bool test) {
-        int button, axis;
         bool has_abs_coordinates = false;
         bool has_rel_coordinates = false;
         bool has_mt_coordinates = false;
@@ -193,7 +192,7 @@ static bool test_pointers(sd_device *dev,
         has_stylus = test_bit(BTN_STYLUS, bitmask_key);
         has_pen = test_bit(BTN_TOOL_PEN, bitmask_key);
         finger_but_no_pen = test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key);
-        for (button = BTN_MOUSE; button < BTN_JOYSTICK && !has_mouse_button; button++)
+        for (int button = BTN_MOUSE; button < BTN_JOYSTICK && !has_mouse_button; button++)
                 has_mouse_button = test_bit(button, bitmask_key);
         has_rel_coordinates = test_bit(EV_REL, bitmask_ev) && test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel);
         has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs);
@@ -214,14 +213,14 @@ static bool test_pointers(sd_device *dev,
          * Catz Mad Catz M.M.O.TE). Skip those.
          */
         if (!test_bit(BTN_JOYSTICK - 1, bitmask_key)) {
-                for (button = BTN_JOYSTICK; button < BTN_DIGI && !has_joystick_axes_or_buttons; button++)
+                for (int button = BTN_JOYSTICK; button < BTN_DIGI && !has_joystick_axes_or_buttons; button++)
                         has_joystick_axes_or_buttons = test_bit(button, bitmask_key);
-                for (button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !has_joystick_axes_or_buttons; button++)
+                for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !has_joystick_axes_or_buttons; button++)
                         has_joystick_axes_or_buttons = test_bit(button, bitmask_key);
-                for (button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !has_joystick_axes_or_buttons; button++)
+                for (int button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !has_joystick_axes_or_buttons; button++)
                         has_joystick_axes_or_buttons = test_bit(button, bitmask_key);
         }
-        for (axis = ABS_RX; axis < ABS_PRESSURE && !has_joystick_axes_or_buttons; axis++)
+        for (int axis = ABS_RX; axis < ABS_PRESSURE && !has_joystick_axes_or_buttons; axis++)
                 has_joystick_axes_or_buttons = test_bit(axis, bitmask_abs);
 
         if (has_abs_coordinates) {
@@ -285,10 +284,8 @@ static bool test_key(sd_device *dev,
                      const unsigned long* bitmask_ev,
                      const unsigned long* bitmask_key,
                      bool test) {
-        unsigned i;
-        unsigned long found;
-        unsigned long mask;
-        bool ret = false;
+
+        bool found = false;
 
         /* do we have any KEY_* capability? */
         if (!test_bit(EV_KEY, bitmask_ev)) {
@@ -297,39 +294,32 @@ static bool test_key(sd_device *dev,
         }
 
         /* only consider KEY_* here, not BTN_* */
-        found = 0;
-        for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) {
-                found |= bitmask_key[i];
-                log_device_debug(dev, "test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0);
+        for (size_t i = 0; i < BTN_MISC/BITS_PER_LONG && !found; i++) {
+                if (bitmask_key[i])
+                        found = true;
+
+                log_device_debug(dev, "test_key: checking bit block %zu for any keys; found=%s",
+                                 i * BITS_PER_LONG, yes_no(found));
         }
         /* If there are no keys in the lower block, check the higher blocks */
-        if (!found) {
-                unsigned block;
-                for (block = 0; block < (sizeof(high_key_blocks) / sizeof(struct range)); ++block) {
-                        for (i = high_key_blocks[block].start; i < high_key_blocks[block].end; ++i) {
-                                if (test_bit(i, bitmask_key)) {
-                                        log_device_debug(dev, "test_key: Found key %x in high block", i);
-                                        found = 1;
-                                        break;
-                                }
+        for (size_t block = 0; block < sizeof(high_key_blocks) / sizeof(struct range) && !found; block++)
+                for (unsigned i = high_key_blocks[block].start; i < high_key_blocks[block].end && !found; i++)
+                        if (test_bit(i, bitmask_key)) {
+                                log_device_debug(dev, "test_key: Found key %x in high block", i);
+                                found = true;
                         }
-                }
-        }
 
-        if (found > 0) {
+        if (found)
                 udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1");
-                ret = true;
-        }
 
         /* 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 */
-        mask = 0xFFFFFFFE;
-        if (FLAGS_SET(bitmask_key[0], mask)) {
+        if (FLAGS_SET(bitmask_key[0], 0xFFFFFFFE)) {
                 udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1");
-                ret = true;
+                return true;
         }
 
-        return ret;
+        return found;
 }
 
 static int builtin_input_id(sd_device *dev, int argc, char *argv[], bool test) {
index 3be8bd56f4648b19e0716c17cd81af193536bd74..0e31002b42e69c6a8ac910ad6d9acc8b61030be0 100644 (file)
@@ -21,16 +21,14 @@ _printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *fi
 }
 
 static int builtin_kmod(sd_device *dev, int argc, char *argv[], bool test) {
-        int i;
-
         if (!ctx)
                 return 0;
 
         if (argc < 3 || !streq(argv[1], "load"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "%s: expected: load <module>", argv[0]);
+                                       "%s: expected: load <module>", argv[0]);
 
-        for (i = 2; argv[i]; i++)
+        for (int i = 2; argv[i]; i++)
                 (void) module_load_and_warn(ctx, argv[i], false);
 
         return 0;
index 92917852ba6b95053a95d42a0dbb28963b7e80d7..0aede28f7d6e1fbdcdcb564d0a4a162acbc57cb2 100644 (file)
@@ -78,25 +78,23 @@ struct virtfn_info {
 
 /* skip intermediate virtio devices */
 static sd_device *skip_virtio(sd_device *dev) {
-        sd_device *parent;
-
         /* there can only ever be one virtio bus per parent device, so we can
          * safely ignore any virtio buses. see
          * http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html */
-        for (parent = dev; parent; ) {
+        while (dev) {
                 const char *subsystem;
 
-                if (sd_device_get_subsystem(parent, &subsystem) < 0)
+                if (sd_device_get_subsystem(dev, &subsystem) < 0)
                         break;
 
                 if (!streq(subsystem, "virtio"))
                         break;
 
-                if (sd_device_get_parent(parent, &parent) < 0)
+                if (sd_device_get_parent(dev, &dev) < 0)
                         return NULL;
         }
 
-        return parent;
+        return dev;
 }
 
 static int get_virtfn_info(sd_device *dev, struct netnames *names, struct virtfn_info *ret) {
index 65c40de4c8cb4721d946bbee948110ece25ffdc5..083ce678036845653fb1cab07d2c7a39e6f8b0b0 100644 (file)
@@ -80,22 +80,19 @@ static int format_lun_number(sd_device *dev, char **path) {
 }
 
 static sd_device *skip_subsystem(sd_device *dev, const char *subsys) {
-        sd_device *parent;
-
         assert(dev);
         assert(subsys);
 
-        for (parent = dev; ; ) {
+        for (;;) {
                 const char *subsystem;
 
-                if (sd_device_get_subsystem(parent, &subsystem) < 0)
+                if (sd_device_get_subsystem(dev, &subsystem) < 0)
                         break;
 
                 if (!streq(subsystem, subsys))
                         break;
 
-                dev = parent;
-                if (sd_device_get_parent(dev, &parent) < 0)
+                if (sd_device_get_parent(dev, &dev) < 0)
                         break;
         }
 
@@ -378,7 +375,6 @@ static sd_device *handle_scsi_hyperv(sd_device *parent, char **path, size_t guid
         const char *guid_str;
         _cleanup_free_ char *lun = NULL;
         char guid[39];
-        size_t i, k;
 
         assert(parent);
         assert(path);
@@ -396,7 +392,8 @@ static sd_device *handle_scsi_hyperv(sd_device *parent, char **path, size_t guid
         if (strlen(guid_str) < guid_str_len || guid_str[0] != '{' || guid_str[guid_str_len-1] != '}')
                 return NULL;
 
-        for (i = 1, k = 0; i < guid_str_len-1; i++) {
+        size_t k = 0;
+        for (size_t i = 1; i < guid_str_len-1; i++) {
                 if (guid_str[i] == '-')
                         continue;
                 guid[k++] = guid_str[i];
@@ -681,11 +678,10 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
 
         {
                 char tag[UDEV_NAME_SIZE];
-                size_t i;
-                const char *p;
+                size_t i = 0;
 
                 /* compose valid udev tag name */
-                for (p = path, i = 0; *p; p++) {
+                for (const char *p = path; *p; p++) {
                         if ((*p >= '0' && *p <= '9') ||
                             (*p >= 'A' && *p <= 'Z') ||
                             (*p >= 'a' && *p <= 'z') ||
index bdaecb812b02b4032b9d25c55eb44fbbc559703f..746fb40e645188ed5775d4cec46bae28a36fae0e 100644 (file)
@@ -405,10 +405,8 @@ fallback:
                 const char *usb_serial;
 
                 if (sd_device_get_sysattr_value(dev_usb, "serial", &usb_serial) >= 0) {
-                        const unsigned char *p;
-
                         /* http://msdn.microsoft.com/en-us/library/windows/hardware/gg487321.aspx */
-                        for (p = (unsigned char *) usb_serial; *p != '\0'; p++)
+                        for (const unsigned char *p = (unsigned char*) usb_serial; *p != '\0'; p++)
                                 if (*p < 0x20 || *p > 0x7f || *p == ',') {
                                         usb_serial = NULL;
                                         break;
index 3d563547190cebce00464a44d6ead24a4b8fa40e..00279ba3d87dd30715d049024fbde829f0be173b 100644 (file)
 /* wire protocol magic must match */
 #define UDEV_CTRL_MAGIC                                0xdead1dea
 
-struct udev_ctrl_msg_wire {
+typedef struct UdevCtrlMessageWire {
         char version[16];
         unsigned magic;
-        enum udev_ctrl_msg_type type;
-        union udev_ctrl_msg_value value;
-};
+        UdevCtrlMessageType type;
+        UdevCtrlMessageValue value;
+} UdevCtrlMessageWire;
 
-struct udev_ctrl {
+struct UdevCtrl {
         unsigned n_ref;
         int sock;
         int sock_connect;
@@ -47,9 +47,9 @@ struct udev_ctrl {
         void *userdata;
 };
 
-int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd) {
+int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd) {
         _cleanup_close_ int sock = -1;
-        struct udev_ctrl *uctrl;
+        UdevCtrl *uctrl;
 
         assert(ret);
 
@@ -59,11 +59,11 @@ int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd) {
                         return log_error_errno(errno, "Failed to create socket: %m");
         }
 
-        uctrl = new(struct udev_ctrl, 1);
+        uctrl = new(UdevCtrl, 1);
         if (!uctrl)
                 return -ENOMEM;
 
-        *uctrl = (struct udev_ctrl) {
+        *uctrl = (UdevCtrl) {
                 .n_ref = 1,
                 .sock = fd >= 0 ? fd : TAKE_FD(sock),
                 .sock_connect = -1,
@@ -81,7 +81,7 @@ int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd) {
         return 0;
 }
 
-int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
+int udev_ctrl_enable_receiving(UdevCtrl *uctrl) {
         int r;
 
         assert(uctrl);
@@ -107,7 +107,7 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) {
         return 0;
 }
 
-static void udev_ctrl_disconnect(struct udev_ctrl *uctrl) {
+static void udev_ctrl_disconnect(UdevCtrl *uctrl) {
         if (!uctrl)
                 return;
 
@@ -115,7 +115,7 @@ static void udev_ctrl_disconnect(struct udev_ctrl *uctrl) {
         uctrl->sock_connect = safe_close(uctrl->sock_connect);
 }
 
-static struct udev_ctrl *udev_ctrl_free(struct udev_ctrl *uctrl) {
+static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) {
         assert(uctrl);
 
         udev_ctrl_disconnect(uctrl);
@@ -127,9 +127,9 @@ static struct udev_ctrl *udev_ctrl_free(struct udev_ctrl *uctrl) {
         return mfree(uctrl);
 }
 
-DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl, udev_ctrl, udev_ctrl_free);
+DEFINE_TRIVIAL_REF_UNREF_FUNC(UdevCtrl, udev_ctrl, udev_ctrl_free);
 
-int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
+int udev_ctrl_cleanup(UdevCtrl *uctrl) {
         if (!uctrl)
                 return 0;
         if (uctrl->cleanup_socket)
@@ -137,7 +137,7 @@ int udev_ctrl_cleanup(struct udev_ctrl *uctrl) {
         return 0;
 }
 
-int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event) {
+int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event) {
         int r;
 
         assert_return(uctrl, -EINVAL);
@@ -154,25 +154,25 @@ int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event) {
         return 0;
 }
 
-sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl) {
+sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl) {
         assert(uctrl);
 
         return uctrl->event_source;
 }
 
-static void udev_ctrl_disconnect_and_listen_again(struct udev_ctrl *uctrl) {
+static void udev_ctrl_disconnect_and_listen_again(UdevCtrl *uctrl) {
         udev_ctrl_disconnect(uctrl);
         udev_ctrl_unref(uctrl);
         (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_ON);
         /* We don't return NULL here because uctrl is not freed */
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct udev_ctrl*, udev_ctrl_disconnect_and_listen_again, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UdevCtrl*, udev_ctrl_disconnect_and_listen_again, NULL);
 
 static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        _cleanup_(udev_ctrl_disconnect_and_listen_againp) struct udev_ctrl *uctrl = NULL;
-        struct udev_ctrl_msg_wire msg_wire;
-        struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(struct udev_ctrl_msg_wire));
+        _cleanup_(udev_ctrl_disconnect_and_listen_againp) UdevCtrl *uctrl = NULL;
+        UdevCtrlMessageWire msg_wire;
+        struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(UdevCtrlMessageWire));
         CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
         struct msghdr smsg = {
                 .msg_iov = &iov,
@@ -235,7 +235,7 @@ static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32
 }
 
 static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        struct udev_ctrl *uctrl = userdata;
+        UdevCtrl *uctrl = userdata;
         _cleanup_close_ int sock = -1;
         struct ucred ucred;
         int r;
@@ -282,7 +282,7 @@ static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents,
         return 0;
 }
 
-int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
+int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata) {
         int r;
 
         assert(uctrl);
@@ -309,8 +309,8 @@ int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void
         return 0;
 }
 
-int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf) {
-        struct udev_ctrl_msg_wire ctrl_msg_wire = {
+int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf) {
+        UdevCtrlMessageWire ctrl_msg_wire = {
                 .version = "udev-" STRINGIFY(PROJECT_VERSION),
                 .magic = UDEV_CTRL_MAGIC,
                 .type = type,
@@ -339,7 +339,7 @@ int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int in
         return 0;
 }
 
-int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout) {
+int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) {
         _cleanup_(sd_event_source_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL;
         int r;
 
index 680fbf7bff1de84d4712f63e95ca358198a13200..ca80c2aa4e0d96b327288c79070a2e6f0b3e95f3 100644 (file)
@@ -6,9 +6,9 @@
 #include "macro.h"
 #include "time-util.h"
 
-struct udev_ctrl;
+typedef struct UdevCtrl UdevCtrl;
 
-enum udev_ctrl_msg_type {
+typedef enum UdevCtrlMessageType {
         _UDEV_CTRL_END_MESSAGES,
         UDEV_CTRL_SET_LOG_LEVEL,
         UDEV_CTRL_STOP_EXEC_QUEUE,
@@ -18,62 +18,62 @@ enum udev_ctrl_msg_type {
         UDEV_CTRL_SET_CHILDREN_MAX,
         UDEV_CTRL_PING,
         UDEV_CTRL_EXIT,
-};
+} UdevCtrlMessageType;
 
-union udev_ctrl_msg_value {
+typedef union UdevCtrlMessageValue {
         int intval;
         char buf[256];
-};
+} UdevCtrlMessageValue;
 
-typedef int (*udev_ctrl_handler_t)(struct udev_ctrl *udev_ctrl, enum udev_ctrl_msg_type type,
-                                   const union udev_ctrl_msg_value *value, void *userdata);
+typedef int (*udev_ctrl_handler_t)(UdevCtrl *udev_ctrl, UdevCtrlMessageType type,
+                                   const UdevCtrlMessageValue *value, void *userdata);
 
-int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd);
-static inline int udev_ctrl_new(struct udev_ctrl **ret) {
+int udev_ctrl_new_from_fd(UdevCtrl **ret, int fd);
+static inline int udev_ctrl_new(UdevCtrl **ret) {
         return udev_ctrl_new_from_fd(ret, -1);
 }
 
-int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl);
-struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl);
-struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl);
-int udev_ctrl_cleanup(struct udev_ctrl *uctrl);
-int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event);
-int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata);
-sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl);
+int udev_ctrl_enable_receiving(UdevCtrl *uctrl);
+UdevCtrl *udev_ctrl_ref(UdevCtrl *uctrl);
+UdevCtrl *udev_ctrl_unref(UdevCtrl *uctrl);
+int udev_ctrl_cleanup(UdevCtrl *uctrl);
+int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event);
+int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata);
+sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl);
 
-int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout);
+int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout);
 
-int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf);
-static inline int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority) {
+int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf);
+static inline int udev_ctrl_send_set_log_level(UdevCtrl *uctrl, int priority) {
         return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL);
 }
 
-static inline int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_stop_exec_queue(UdevCtrl *uctrl) {
         return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL);
 }
 
-static inline int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_start_exec_queue(UdevCtrl *uctrl) {
         return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL);
 }
 
-static inline int udev_ctrl_send_reload(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_reload(UdevCtrl *uctrl) {
         return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL);
 }
 
-static inline int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key) {
+static inline int udev_ctrl_send_set_env(UdevCtrl *uctrl, const char *key) {
         return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key);
 }
 
-static inline int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count) {
+static inline int udev_ctrl_send_set_children_max(UdevCtrl *uctrl, int count) {
         return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL);
 }
 
-static inline int udev_ctrl_send_ping(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_ping(UdevCtrl *uctrl) {
         return udev_ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL);
 }
 
-static inline int udev_ctrl_send_exit(struct udev_ctrl *uctrl) {
+static inline int udev_ctrl_send_exit(UdevCtrl *uctrl) {
         return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL);
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(UdevCtrl*, udev_ctrl_unref);
index d269345259e8ba9c3f4955ca756acf0c08b0be3e..2179c8d254b6f8274a6dc7315318528ed975ef1a 100644 (file)
@@ -429,7 +429,7 @@ static ssize_t udev_event_subst_format(
                 strpcpy(&s, l, val);
                 break;
         default:
-                assert_not_reached("Unknown format substitution type");
+                assert_not_reached();
         }
 
         return s - dest;
index bf997fc0eda59f0586fe21d99cb0658c8aed3a64..cb7de261d5aa2621e547423b75efb05ff831ebdd 100644 (file)
@@ -1154,7 +1154,7 @@ static void rule_resolve_goto(UdevRuleFile *rule_file) {
                 if (!FLAGS_SET(line->type, LINE_HAS_GOTO))
                         continue;
 
-                LIST_FOREACH_AFTER(rule_lines, i, line)
+                LIST_FOREACH(rule_lines, i, line->rule_lines_next)
                         if (streq_ptr(i->label, line->goto_label)) {
                                 line->goto_line = i;
                                 break;
@@ -1374,7 +1374,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
                         }
                 break;
         default:
-                assert_not_reached("Invalid match type");
+                assert_not_reached();
         }
 
         return token->op == (match ? OP_MATCH : OP_NOMATCH);
@@ -1405,7 +1405,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
                 value = vbuf;
                 break;
         default:
-                assert_not_reached("Invalid attribute substitution type");
+                assert_not_reached();
         }
 
         /* remove trailing whitespace, if not asked to match for it */
@@ -1607,7 +1607,7 @@ static int udev_rule_apply_token_to_event(
                 else if (streq(k, "virt"))
                         val = virtualization_to_string(detect_virtualization());
                 else
-                        assert_not_reached("Invalid CONST key");
+                        assert_not_reached();
                 return token_match_string(token, val);
         }
         case TK_M_TAG:
@@ -2233,7 +2233,7 @@ static int udev_rule_apply_token_to_event(
                 /* do nothing for events. */
                 break;
         default:
-                assert_not_reached("Invalid token type");
+                assert_not_reached();
         }
 
         return true;
index 437ac9b832cd8e9ad462e857ceffe9f7030adaae..06c61e5c07c68bdd9dcdd7bce0ed143e8919afc5 100644 (file)
@@ -48,7 +48,7 @@ static int help(void) {
 }
 
 int control_main(int argc, char *argv[], void *userdata) {
-        _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
+        _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
         usec_t timeout = 60 * USEC_PER_SEC;
         int c, r;
 
@@ -171,7 +171,7 @@ int control_main(int argc, char *argv[], void *userdata) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unknown option.");
+                        assert_not_reached();
                 }
 
         if (optind < argc)
index 9414269fb80cbf6b381d2311db252b07de50a58b..e9db2847cf1f6d94c576ab855fb0b8a190195ba5 100644 (file)
@@ -71,7 +71,7 @@ static int parse_argv(int argc, char *argv[]) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         return 1;
index fa7f04f14ce8cda0721dd426fece6d2c550f2290..1ea89c16cc50cc95b0494aa5442e35cb3beac55e 100644 (file)
@@ -328,7 +328,7 @@ static int query_device(QueryType query, sd_device* device) {
                 return print_record(device);
         }
 
-        assert_not_reached("unknown query type");
+        assert_not_reached();
         return 0;
 }
 
@@ -456,7 +456,7 @@ int info_main(int argc, char *argv[], void *userdata) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         if (action == ACTION_DEVICE_ID_FILE) {
@@ -508,7 +508,7 @@ int info_main(int argc, char *argv[], void *userdata) {
                 else if (action == ACTION_ATTRIBUTE_WALK)
                         r = print_device_chain(device);
                 else
-                        assert_not_reached("Unknown action");
+                        assert_not_reached();
                 if (r < 0)
                         return r;
         }
index 00b03c550d568e9be1e9c6df52f818bce3c1050b..76aaf7c42e54b22ef8d6f1159df15d45855bee5b 100644 (file)
@@ -179,7 +179,7 @@ static int parse_argv(int argc, char *argv[]) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unknown option.");
+                        assert_not_reached();
                 }
 
         if (!arg_print_kernel && !arg_print_udev) {
index 2c61c2d8b02e920d2c60c3aaf9cbe749305b1f15..6da9439bd28af29ae3fed7afe94b55d6b165ad39 100644 (file)
@@ -81,7 +81,7 @@ static int parse_argv(int argc, char *argv[]) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unknown option.");
+                        assert_not_reached();
                 }
         }
 
@@ -176,7 +176,7 @@ int settle_main(int argc, char *argv[], void *userdata) {
 
         /* guarantee that the udev daemon isn't pre-processing */
         if (getuid() == 0) {
-                _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
+                _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
 
                 if (udev_ctrl_new(&uctrl) >= 0) {
                         r = udev_ctrl_send_ping(uctrl);
index 008d6f29143be7e0a9c1b9234bc75a9fab1e1bd2..cae2950c8f5f8bc7aa8ad20db60b990620995f48 100644 (file)
 #include "udevadm.h"
 #include "udevadm-util.h"
 
+static sd_device_action_t arg_action = SD_DEVICE_ADD;
 static const char *arg_command = NULL;
 static const char *arg_syspath = NULL;
 
 static int help(void) {
         printf("%s test-builtin [OPTIONS] COMMAND DEVPATH\n\n"
                "Test a built-in command.\n\n"
-               "  -h --help     Print this message\n"
-               "  -V --version  Print version of the program\n\n"
+               "  -h --help               Print this message\n"
+               "  -V --version            Print version of the program\n\n"
+               "  -a --action=ACTION|help Set action string\n"
                "Commands:\n",
                program_invocation_short_name);
 
@@ -29,15 +31,23 @@ static int help(void) {
 
 static int parse_argv(int argc, char *argv[]) {
         static const struct option options[] = {
-                { "version", no_argument, NULL, 'V' },
-                { "help",    no_argument, NULL, 'h' },
+                { "action",  required_argument, NULL, 'a' },
+                { "version", no_argument,       NULL, 'V' },
+                { "help",    no_argument,       NULL, 'h' },
                 {}
         };
 
-        int c;
+        int r, c;
 
-        while ((c = getopt_long(argc, argv, "Vh", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "a:Vh", options, NULL)) >= 0)
                 switch (c) {
+                case 'a':
+                        r = parse_device_action(optarg, &arg_action);
+                        if (r < 0)
+                                return log_error_errno(r, "Invalid action '%s'", optarg);
+                        if (r == 0)
+                                return 0;
+                        break;
                 case 'V':
                         return print_version();
                 case 'h':
@@ -45,7 +55,7 @@ static int parse_argv(int argc, char *argv[]) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
         arg_command = argv[optind++];
@@ -81,7 +91,7 @@ int builtin_main(int argc, char *argv[], void *userdata) {
                 goto finish;
         }
 
-        r = find_device(arg_syspath, "/sys", &dev);
+        r = find_device_with_action(arg_syspath, arg_action, &dev);
         if (r < 0) {
                 log_error_errno(r, "Failed to open device '%s': %m", arg_syspath);
                 goto finish;
index fbac719fa066a1955a6c97cc6883b41691bd7c05..01057e12563b1d11b6d991bfbb454b2b5152d1ec 100644 (file)
 #include "strxcpyx.h"
 #include "udev-builtin.h"
 #include "udev-event.h"
+#include "udevadm-util.h"
 #include "udevadm.h"
 
-static const char *arg_action = "add";
+static sd_device_action_t arg_action = SD_DEVICE_ADD;
 static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
-static char arg_syspath[UDEV_PATH_SIZE] = {};
+static const char *arg_syspath = NULL;
 
 static int help(void) {
 
@@ -49,25 +50,17 @@ static int parse_argv(int argc, char *argv[]) {
                 {}
         };
 
-        int c;
+        int r, c;
 
         while ((c = getopt_long(argc, argv, "a:N:Vh", options, NULL)) >= 0)
                 switch (c) {
-                case 'a': {
-                        sd_device_action_t a;
-
-                        if (streq(optarg, "help")) {
-                                dump_device_action_table();
+                case 'a':
+                        r = parse_device_action(optarg, &arg_action);
+                        if (r < 0)
+                                return log_error_errno(r, "Invalid action '%s'", optarg);
+                        if (r == 0)
                                 return 0;
-                        }
-
-                        a = device_action_from_string(optarg);
-                        if (a < 0)
-                                return log_error_errno(a, "Invalid action '%s'", optarg);
-
-                        arg_action = device_action_to_string(a);
                         break;
-                }
                 case 'N':
                         arg_resolve_name_timing = resolve_name_timing_from_string(optarg);
                         if (arg_resolve_name_timing < 0)
@@ -81,18 +74,12 @@ static int parse_argv(int argc, char *argv[]) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
 
-        if (!argv[optind])
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "syspath parameter missing.");
-
-        /* add /sys if needed */
-        if (!path_startswith(argv[optind], "/sys"))
-                strscpyl(arg_syspath, sizeof(arg_syspath), "/sys", argv[optind], NULL);
-        else
-                strscpy(arg_syspath, sizeof(arg_syspath), argv[optind]);
+        arg_syspath = argv[optind];
+        if (!arg_syspath)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "syspath parameter missing.");
 
         return 1;
 }
@@ -127,7 +114,7 @@ int test_main(int argc, char *argv[], void *userdata) {
                 goto out;
         }
 
-        r = device_new_from_synthetic_event(&dev, arg_syspath, arg_action);
+        r = find_device_with_action(arg_syspath, arg_action, &dev);
         if (r < 0) {
                 log_error_errno(r, "Failed to open device '%s': %m", arg_syspath);
                 goto out;
index 984ec1b84b25ea16a81be06cfef5661c71e32dc2..7c121c369bcb86b67337c1912563fbab2b779f9c 100644 (file)
@@ -312,17 +312,13 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                         else
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg);
                         break;
-                case 'c': {
-                        if (streq(optarg, "help")) {
-                                dump_device_action_table();
+                case 'c':
+                        r = parse_device_action(optarg, &action);
+                        if (r < 0)
+                                return log_error_errno(r, "Unknown action '%s'", optarg);
+                        if (r == 0)
                                 return 0;
-                        }
-
-                        action = device_action_from_string(optarg);
-                        if (action < 0)
-                                return log_error_errno(action, "Unknown action '%s'", optarg);
                         break;
-                }
                 case 's':
                         r = sd_device_enumerator_add_match_subsystem(e, optarg, true);
                         if (r < 0)
@@ -416,12 +412,12 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unknown option");
+                        assert_not_reached();
                 }
         }
 
         if (ping) {
-                _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
+                _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL;
 
                 r = udev_ctrl_new(&uctrl);
                 if (r < 0)
@@ -482,7 +478,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                         return log_error_errno(r, "Failed to scan devices: %m");
                 break;
         default:
-                assert_not_reached("Unknown device type");
+                assert_not_reached();
         }
 
         r = exec_list(e, action, settle_hashmap);
index 10191d88ba9dbc22967c3687c44f65e4e2c62deb..9649e5a2075b9672913152f5c654043dc8550956 100644 (file)
@@ -93,3 +93,39 @@ int find_device(const char *id, const char *prefix, sd_device **ret) {
 
         return find_device_from_path(id, ret);
 }
+
+int find_device_with_action(const char *id, sd_device_action_t action, sd_device **ret) {
+        _cleanup_free_ char *path = NULL;
+
+        assert(id);
+        assert(ret);
+        assert(action >= 0 && action < _SD_DEVICE_ACTION_MAX);
+
+        if (!path_startswith(id, "/sys")) {
+                path = path_join("/sys", id);
+                if (!path)
+                        return -ENOMEM;
+                id = path;
+        }
+
+        return device_new_from_synthetic_event(ret, id, device_action_to_string(action));
+}
+
+int parse_device_action(const char *str, sd_device_action_t *action) {
+        sd_device_action_t a;
+
+        assert(str);
+        assert(action);
+
+        if (streq(str, "help")) {
+                dump_device_action_table();
+                return 0;
+        }
+
+        a = device_action_from_string(str);
+        if (a < 0)
+                return a;
+
+        *action = a;
+        return 1;
+}
index 91587c5492ad9fc1cb44292da30d69390f1051f5..7fb4556413475b2b7ed44a203a35f72d522bebcf 100644 (file)
@@ -4,3 +4,5 @@
 #include "sd-device.h"
 
 int find_device(const char *id, const char *prefix, sd_device **ret);
+int find_device_with_action(const char *id, sd_device_action_t action, sd_device **ret);
+int parse_device_action(const char *str, sd_device_action_t *action);
index e55ae4bd54cca6064edc12f86459d84b5e2002ab..ba17d9348b6865947f039e426134bcf376c7b141 100644 (file)
@@ -77,7 +77,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         return 1; /* work to do */
index 13ac7c83b525d83f3d13f4dcad8e2fe066e16610..4df08f21c5c66bbc4142b01ec6ba52cd3de2486c 100644 (file)
@@ -77,10 +77,13 @@ static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC;
 static int arg_timeout_signal = SIGKILL;
 static bool arg_blockdev_read_only = false;
 
+typedef struct Event Event;
+typedef struct Worker Worker;
+
 typedef struct Manager {
         sd_event *event;
         Hashmap *workers;
-        LIST_HEAD(struct event, events);
+        LIST_HEAD(Event, events);
         const char *cgroup;
         pid_t pid; /* the process that originally allocated the manager object */
         int log_level;
@@ -91,7 +94,7 @@ typedef struct Manager {
         sd_netlink *rtnl;
 
         sd_device_monitor *monitor;
-        struct udev_ctrl *ctrl;
+        UdevCtrl *ctrl;
         int worker_watch[2];
 
         /* used by udev-watch */
@@ -106,54 +109,52 @@ typedef struct Manager {
         bool exit;
 } Manager;
 
-enum event_state {
+typedef enum EventState {
         EVENT_UNDEF,
         EVENT_QUEUED,
         EVENT_RUNNING,
-};
+} EventState;
 
-struct event {
+typedef struct Event {
         Manager *manager;
-        struct worker *worker;
-        enum event_state state;
+        Worker *worker;
+        EventState state;
 
         sd_device *dev;
         sd_device *dev_kernel; /* clone of originally received device */
 
         uint64_t seqnum;
-        uint64_t delaying_seqnum;
+        uint64_t blocker_seqnum;
 
         sd_event_source *timeout_warning_event;
         sd_event_source *timeout_event;
 
-        LIST_FIELDS(struct event, event);
-};
-
-static void event_queue_cleanup(Manager *manager, enum event_state type);
+        LIST_FIELDS(Event, event);
+} Event;
 
-enum worker_state {
+typedef enum WorkerState {
         WORKER_UNDEF,
         WORKER_RUNNING,
         WORKER_IDLE,
         WORKER_KILLED,
         WORKER_KILLING,
-};
+} WorkerState;
 
-struct worker {
+typedef struct Worker {
         Manager *manager;
         pid_t pid;
         sd_device_monitor *monitor;
-        enum worker_state state;
-        struct event *event;
-};
+        WorkerState state;
+        Event *event;
+} Worker;
 
 /* passed from worker to main process */
-struct worker_message {
-};
+typedef struct WorkerMessage {
+} WorkerMessage;
 
-static void event_free(struct event *event) {
+static Event *event_free(Event *event) {
         if (!event)
-                return;
+                return NULL;
 
         assert(event->manager);
 
@@ -170,13 +171,24 @@ static void event_free(struct event *event) {
         /* only clean up the queue from the process that created it */
         if (LIST_IS_EMPTY(event->manager->events) &&
             event->manager->pid == getpid_cached())
-                if (unlink("/run/udev/queue") < 0)
-                        log_warning_errno(errno, "Failed to unlink /run/udev/queue: %m");
+                if (unlink("/run/udev/queue") < 0 && errno != ENOENT)
+                        log_warning_errno(errno, "Failed to unlink /run/udev/queue, ignoring: %m");
 
-        free(event);
+        return mfree(event);
 }
 
-static struct worker* worker_free(struct worker *worker) {
+static void event_queue_cleanup(Manager *manager, EventState match_state) {
+        Event *event, *tmp;
+
+        LIST_FOREACH_SAFE(event, event, tmp, manager->events) {
+                if (match_state != EVENT_UNDEF && match_state != event->state)
+                        continue;
+
+                event_free(event);
+        }
+}
+
+static Worker *worker_free(Worker *worker) {
         if (!worker)
                 return NULL;
 
@@ -189,11 +201,52 @@ static struct worker* worker_free(struct worker *worker) {
         return mfree(worker);
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct worker *, worker_free);
-DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(worker_hash_op, void, trivial_hash_func, trivial_compare_func, struct worker, worker_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Worker*, worker_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(worker_hash_op, void, trivial_hash_func, trivial_compare_func, Worker, worker_free);
 
-static int worker_new(struct worker **ret, Manager *manager, sd_device_monitor *worker_monitor, pid_t pid) {
-        _cleanup_(worker_freep) struct worker *worker = NULL;
+static void manager_clear_for_worker(Manager *manager) {
+        assert(manager);
+
+        manager->inotify_event = sd_event_source_unref(manager->inotify_event);
+        manager->kill_workers_event = sd_event_source_unref(manager->kill_workers_event);
+
+        manager->event = sd_event_unref(manager->event);
+
+        manager->workers = hashmap_free(manager->workers);
+        event_queue_cleanup(manager, EVENT_UNDEF);
+
+        manager->monitor = sd_device_monitor_unref(manager->monitor);
+        manager->ctrl = udev_ctrl_unref(manager->ctrl);
+
+        manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
+}
+
+static Manager* manager_free(Manager *manager) {
+        if (!manager)
+                return NULL;
+
+        udev_builtin_exit();
+
+        if (manager->pid == getpid_cached())
+                udev_ctrl_cleanup(manager->ctrl);
+
+        manager_clear_for_worker(manager);
+
+        sd_netlink_unref(manager->rtnl);
+
+        hashmap_free_free_free(manager->properties);
+        udev_rules_free(manager->rules);
+
+        safe_close(manager->inotify_fd);
+        safe_close_pair(manager->worker_watch);
+
+        return mfree(manager);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+
+static int worker_new(Worker **ret, Manager *manager, sd_device_monitor *worker_monitor, pid_t pid) {
+        _cleanup_(worker_freep) Worker *worker = NULL;
         int r;
 
         assert(ret);
@@ -204,11 +257,11 @@ static int worker_new(struct worker **ret, Manager *manager, sd_device_monitor *
         /* close monitor, but keep address around */
         device_monitor_disconnect(worker_monitor);
 
-        worker = new(struct worker, 1);
+        worker = new(Worker, 1);
         if (!worker)
                 return -ENOMEM;
 
-        *worker = (struct worker) {
+        *worker = (Worker) {
                 .manager = manager,
                 .monitor = sd_device_monitor_ref(worker_monitor),
                 .pid = pid,
@@ -223,99 +276,78 @@ static int worker_new(struct worker **ret, Manager *manager, sd_device_monitor *
         return 0;
 }
 
-static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
-        struct event *event = userdata;
-
-        assert(event);
-        assert(event->worker);
-
-        kill_and_sigcont(event->worker->pid, arg_timeout_signal);
-        event->worker->state = WORKER_KILLED;
-
-        log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum);
-
-        return 1;
-}
+static void manager_kill_workers(Manager *manager, bool force) {
+        Worker *worker;
 
-static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
-        struct event *event = userdata;
+        assert(manager);
 
-        assert(event);
-        assert(event->worker);
+        HASHMAP_FOREACH(worker, manager->workers) {
+                if (worker->state == WORKER_KILLED)
+                        continue;
 
-        log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time", event->worker->pid, event->seqnum);
+                if (worker->state == WORKER_RUNNING && !force) {
+                        worker->state = WORKER_KILLING;
+                        continue;
+                }
 
-        return 1;
+                worker->state = WORKER_KILLED;
+                (void) kill(worker->pid, SIGTERM);
+        }
 }
 
-static void worker_attach_event(struct worker *worker, struct event *event) {
-        sd_event *e;
-
-        assert(worker);
-        assert(worker->manager);
-        assert(event);
-        assert(!event->worker);
-        assert(!worker->event);
-
-        worker->state = WORKER_RUNNING;
-        worker->event = event;
-        event->state = EVENT_RUNNING;
-        event->worker = worker;
-
-        e = worker->manager->event;
+static void manager_exit(Manager *manager) {
+        assert(manager);
 
-        (void) sd_event_add_time_relative(e, &event->timeout_warning_event, CLOCK_MONOTONIC,
-                                          udev_warn_timeout(arg_event_timeout_usec), USEC_PER_SEC,
-                                          on_event_timeout_warning, event);
+        manager->exit = true;
 
-        (void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
-                                          arg_event_timeout_usec, USEC_PER_SEC,
-                                          on_event_timeout, event);
-}
+        sd_notify(false,
+                  "STOPPING=1\n"
+                  "STATUS=Starting shutdown...");
 
-static void manager_clear_for_worker(Manager *manager) {
-        assert(manager);
+        /* close sources of new events and discard buffered events */
+        manager->ctrl = udev_ctrl_unref(manager->ctrl);
 
         manager->inotify_event = sd_event_source_unref(manager->inotify_event);
-        manager->kill_workers_event = sd_event_source_unref(manager->kill_workers_event);
-
-        manager->event = sd_event_unref(manager->event);
-
-        manager->workers = hashmap_free(manager->workers);
-        event_queue_cleanup(manager, EVENT_UNDEF);
+        manager->inotify_fd = safe_close(manager->inotify_fd);
 
         manager->monitor = sd_device_monitor_unref(manager->monitor);
-        manager->ctrl = udev_ctrl_unref(manager->ctrl);
 
-        manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
+        /* discard queued events and kill workers */
+        event_queue_cleanup(manager, EVENT_QUEUED);
+        manager_kill_workers(manager, true);
 }
 
-static Manager* manager_free(Manager *manager) {
-        if (!manager)
-                return NULL;
+/* reload requested, HUP signal received, rules changed, builtin changed */
+static void manager_reload(Manager *manager) {
 
-        udev_builtin_exit();
+        assert(manager);
 
-        if (manager->pid == getpid_cached())
-                udev_ctrl_cleanup(manager->ctrl);
+        sd_notify(false,
+                  "RELOADING=1\n"
+                  "STATUS=Flushing configuration...");
 
-        manager_clear_for_worker(manager);
+        manager_kill_workers(manager, false);
+        manager->rules = udev_rules_free(manager->rules);
+        udev_builtin_exit();
 
-        sd_netlink_unref(manager->rtnl);
+        sd_notifyf(false,
+                   "READY=1\n"
+                   "STATUS=Processing with %u children at max", arg_children_max);
+}
 
-        hashmap_free_free_free(manager->properties);
-        udev_rules_free(manager->rules);
+static int on_kill_workers_event(sd_event_source *s, uint64_t usec, void *userdata) {
+        Manager *manager = userdata;
 
-        safe_close(manager->inotify_fd);
-        safe_close_pair(manager->worker_watch);
+        assert(manager);
 
-        return mfree(manager);
-}
+        log_debug("Cleanup idle workers");
+        manager_kill_workers(manager, false);
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+        return 1;
+}
 
 static int worker_send_message(int fd) {
-        struct worker_message message = {};
+        WorkerMessage message = {};
 
         return loop_write(fd, &message, sizeof(message), false);
 }
@@ -591,9 +623,59 @@ static int worker_main(Manager *_manager, sd_device_monitor *monitor, sd_device
         return 0;
 }
 
-static int worker_spawn(Manager *manager, struct event *event) {
+static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+        Event *event = userdata;
+
+        assert(event);
+        assert(event->worker);
+
+        kill_and_sigcont(event->worker->pid, arg_timeout_signal);
+        event->worker->state = WORKER_KILLED;
+
+        log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum);
+
+        return 1;
+}
+
+static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
+        Event *event = userdata;
+
+        assert(event);
+        assert(event->worker);
+
+        log_device_warning(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" is taking a long time", event->worker->pid, event->seqnum);
+
+        return 1;
+}
+
+static void worker_attach_event(Worker *worker, Event *event) {
+        sd_event *e;
+
+        assert(worker);
+        assert(worker->manager);
+        assert(event);
+        assert(!event->worker);
+        assert(!worker->event);
+
+        worker->state = WORKER_RUNNING;
+        worker->event = event;
+        event->state = EVENT_RUNNING;
+        event->worker = worker;
+
+        e = worker->manager->event;
+
+        (void) sd_event_add_time_relative(e, &event->timeout_warning_event, CLOCK_MONOTONIC,
+                                          udev_warn_timeout(arg_event_timeout_usec), USEC_PER_SEC,
+                                          on_event_timeout_warning, event);
+
+        (void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
+                                          arg_event_timeout_usec, USEC_PER_SEC,
+                                          on_event_timeout, event);
+}
+
+static int worker_spawn(Manager *manager, Event *event) {
         _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *worker_monitor = NULL;
-        struct worker *worker;
+        Worker *worker;
         pid_t pid;
         int r;
 
@@ -635,16 +717,18 @@ static int worker_spawn(Manager *manager, struct event *event) {
         return 0;
 }
 
-static void event_run(Manager *manager, struct event *event) {
+static int event_run(Event *event) {
         static bool log_children_max_reached = true;
-        struct worker *worker;
+        Manager *manager;
+        Worker *worker;
         int r;
 
-        assert(manager);
         assert(event);
+        assert(event->manager);
 
         log_device_uevent(event->dev, "Device ready for processing");
 
+        manager = event->manager;
         HASHMAP_FOREACH(worker, manager->workers) {
                 if (worker->state != WORKER_IDLE)
                         continue;
@@ -658,109 +742,72 @@ static void event_run(Manager *manager, struct event *event) {
                         continue;
                 }
                 worker_attach_event(worker, event);
-                return;
+                return 1; /* event is now processing. */
         }
 
         if (hashmap_size(manager->workers) >= arg_children_max) {
-
                 /* Avoid spamming the debug logs if the limit is already reached and
                  * many events still need to be processed */
                 if (log_children_max_reached && arg_children_max > 1) {
                         log_debug("Maximum number (%u) of children reached.", hashmap_size(manager->workers));
                         log_children_max_reached = false;
                 }
-                return;
+                return 0; /* no free worker */
         }
 
         /* Re-enable the debug message for the next batch of events */
         log_children_max_reached = true;
 
         /* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
-           and, until the next SELinux policy changes, we safe further reloads in future children */
+         * and, until the next SELinux policy changes, we safe further reloads in future children */
         mac_selinux_maybe_reload();
 
         /* start new worker and pass initial device */
-        worker_spawn(manager, event);
-}
-
-static int event_queue_insert(Manager *manager, sd_device *dev) {
-        _cleanup_(sd_device_unrefp) sd_device *clone = NULL;
-        struct event *event;
-        uint64_t seqnum;
-        int r;
-
-        assert(manager);
-        assert(dev);
-
-        /* only one process can add events to the queue */
-        assert(manager->pid == getpid_cached());
-
-        /* We only accepts devices received by device monitor. */
-        r = sd_device_get_seqnum(dev, &seqnum);
+        r = worker_spawn(manager, event);
         if (r < 0)
                 return r;
 
-        /* Save original device to restore the state on failures. */
-        r = device_shallow_clone(dev, &clone);
-        if (r < 0)
-                return r;
-
-        r = device_copy_properties(clone, dev);
-        if (r < 0)
-                return r;
-
-        event = new(struct event, 1);
-        if (!event)
-                return -ENOMEM;
-
-        *event = (struct event) {
-                .manager = manager,
-                .dev = sd_device_ref(dev),
-                .dev_kernel = TAKE_PTR(clone),
-                .seqnum = seqnum,
-                .state = EVENT_QUEUED,
-        };
-
-        if (LIST_IS_EMPTY(manager->events)) {
-                r = touch("/run/udev/queue");
-                if (r < 0)
-                        log_warning_errno(r, "Failed to touch /run/udev/queue: %m");
-        }
-
-        LIST_APPEND(event, manager->events, event);
+        return 1; /* event is now processing. */
+}
 
-        log_device_uevent(dev, "Device is queued");
+static int event_is_blocked(Event *event) {
+        const char *subsystem, *devpath, *devpath_old = NULL;
+        dev_t devnum = makedev(0, 0);
+        Event *loop_event;
+        size_t devpath_len;
+        int r, ifindex = 0;
+        bool is_block;
 
-        return 0;
-}
+        /* lookup event for identical, parent, child device */
 
-static void manager_kill_workers(Manager *manager, bool force) {
-        struct worker *worker;
+        assert(event);
+        assert(event->manager);
+        assert(event->blocker_seqnum <= event->seqnum);
 
-        assert(manager);
+        if (event->blocker_seqnum == event->seqnum)
+                /* we have checked previously and no blocker found */
+                return false;
 
-        HASHMAP_FOREACH(worker, manager->workers) {
-                if (worker->state == WORKER_KILLED)
+        LIST_FOREACH(event, loop_event, event->manager->events) {
+                /* we already found a later event, earlier cannot block us, no need to check again */
+                if (loop_event->seqnum < event->blocker_seqnum)
                         continue;
 
-                if (worker->state == WORKER_RUNNING && !force) {
-                        worker->state = WORKER_KILLING;
-                        continue;
-                }
+                /* event we checked earlier still exists, no need to check again */
+                if (loop_event->seqnum == event->blocker_seqnum)
+                        return true;
 
-                worker->state = WORKER_KILLED;
-                (void) kill(worker->pid, SIGTERM);
+                /* found ourself, no later event can block us */
+                if (loop_event->seqnum >= event->seqnum)
+                        goto no_blocker;
+
+                /* found event we have not checked */
+                break;
         }
-}
 
-/* lookup event for identical, parent, child device */
-static int is_device_busy(Manager *manager, struct event *event) {
-        const char *subsystem, *devpath, *devpath_old = NULL;
-        dev_t devnum = makedev(0, 0);
-        struct event *loop_event;
-        size_t devpath_len;
-        int r, ifindex = 0;
-        bool is_block;
+        assert(loop_event);
+        assert(loop_event->seqnum > event->blocker_seqnum &&
+               loop_event->seqnum < event->seqnum);
 
         r = sd_device_get_subsystem(event->dev, &subsystem);
         if (r < 0)
@@ -787,21 +834,13 @@ static int is_device_busy(Manager *manager, struct event *event) {
                 return r;
 
         /* check if queue contains events we depend on */
-        LIST_FOREACH(event, loop_event, manager->events) {
+        LIST_FOREACH(event, loop_event, loop_event) {
                 size_t loop_devpath_len, common;
                 const char *loop_devpath;
 
-                /* we already found a later event, earlier cannot block us, no need to check again */
-                if (loop_event->seqnum < event->delaying_seqnum)
-                        continue;
-
-                /* event we checked earlier still exists, no need to check again */
-                if (loop_event->seqnum == event->delaying_seqnum)
-                        return true;
-
                 /* found ourself, no later event can block us */
                 if (loop_event->seqnum >= event->seqnum)
-                        break;
+                        goto no_blocker;
 
                 /* check major/minor */
                 if (major(devnum) != 0) {
@@ -813,7 +852,7 @@ static int is_device_busy(Manager *manager, struct event *event) {
 
                         if (sd_device_get_devnum(loop_event->dev, &d) >= 0 &&
                             devnum == d && is_block == streq(s, "block"))
-                                goto set_delaying_seqnum;
+                                break;
                 }
 
                 /* check network device ifindex */
@@ -822,7 +861,7 @@ static int is_device_busy(Manager *manager, struct event *event) {
 
                         if (sd_device_get_ifindex(loop_event->dev, &i) >= 0 &&
                             ifindex == i)
-                                goto set_delaying_seqnum;
+                                break;
                 }
 
                 if (sd_device_get_devpath(loop_event->dev, &loop_devpath) < 0)
@@ -830,7 +869,7 @@ static int is_device_busy(Manager *manager, struct event *event) {
 
                 /* check our old name */
                 if (devpath_old && streq(devpath_old, loop_devpath))
-                        goto set_delaying_seqnum;
+                        break;
 
                 loop_devpath_len = strlen(loop_devpath);
 
@@ -843,80 +882,32 @@ static int is_device_busy(Manager *manager, struct event *event) {
 
                 /* identical device event found */
                 if (devpath_len == loop_devpath_len)
-                        goto set_delaying_seqnum;
+                        break;
 
                 /* parent device event found */
                 if (devpath[common] == '/')
-                        goto set_delaying_seqnum;
+                        break;
 
                 /* child device event found */
                 if (loop_devpath[common] == '/')
-                        goto set_delaying_seqnum;
+                        break;
         }
 
-        return false;
+        assert(loop_event);
 
-set_delaying_seqnum:
         log_device_debug(event->dev, "SEQNUM=%" PRIu64 " blocked by SEQNUM=%" PRIu64,
                          event->seqnum, loop_event->seqnum);
 
-        event->delaying_seqnum = loop_event->seqnum;
+        event->blocker_seqnum = loop_event->seqnum;
         return true;
-}
-
-static void manager_exit(Manager *manager) {
-        assert(manager);
-
-        manager->exit = true;
-
-        sd_notify(false,
-                  "STOPPING=1\n"
-                  "STATUS=Starting shutdown...");
-
-        /* close sources of new events and discard buffered events */
-        manager->ctrl = udev_ctrl_unref(manager->ctrl);
-
-        manager->inotify_event = sd_event_source_unref(manager->inotify_event);
-        manager->inotify_fd = safe_close(manager->inotify_fd);
-
-        manager->monitor = sd_device_monitor_unref(manager->monitor);
-
-        /* discard queued events and kill workers */
-        event_queue_cleanup(manager, EVENT_QUEUED);
-        manager_kill_workers(manager, true);
-}
-
-/* reload requested, HUP signal received, rules changed, builtin changed */
-static void manager_reload(Manager *manager) {
-
-        assert(manager);
-
-        sd_notify(false,
-                  "RELOADING=1\n"
-                  "STATUS=Flushing configuration...");
-
-        manager_kill_workers(manager, false);
-        manager->rules = udev_rules_free(manager->rules);
-        udev_builtin_exit();
-
-        sd_notifyf(false,
-                   "READY=1\n"
-                   "STATUS=Processing with %u children at max", arg_children_max);
-}
 
-static int on_kill_workers_event(sd_event_source *s, uint64_t usec, void *userdata) {
-        Manager *manager = userdata;
-
-        assert(manager);
-
-        log_debug("Cleanup idle workers");
-        manager_kill_workers(manager, false);
-
-        return 1;
+no_blocker:
+        event->blocker_seqnum = event->seqnum;
+        return false;
 }
 
-static void event_queue_start(Manager *manager) {
-        struct event *event;
+static int event_queue_start(Manager *manager) {
+        Event *event, *event_next;
         usec_t usec;
         int r;
 
@@ -924,12 +915,12 @@ static void event_queue_start(Manager *manager) {
 
         if (LIST_IS_EMPTY(manager->events) ||
             manager->exit || manager->stop_exec_queue)
-                return;
+                return 0;
 
         assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &usec) >= 0);
         /* check for changed config, every 3 seconds at most */
         if (manager->last_usec == 0 ||
-            usec - manager->last_usec > 3 * USEC_PER_SEC) {
+            usec > usec_add(manager->last_usec, 3 * USEC_PER_SEC)) {
                 if (udev_rules_check_timestamp(manager->rules) ||
                     udev_builtin_validate())
                         manager_reload(manager);
@@ -945,33 +936,111 @@ static void event_queue_start(Manager *manager) {
 
         if (!manager->rules) {
                 r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
-                if (r < 0) {
-                        log_warning_errno(r, "Failed to read udev rules: %m");
-                        return;
-                }
+                if (r < 0)
+                        return log_warning_errno(r, "Failed to read udev rules: %m");
         }
 
-        LIST_FOREACH(event, event, manager->events) {
+        LIST_FOREACH_SAFE(event, event, event_next, manager->events) {
                 if (event->state != EVENT_QUEUED)
                         continue;
 
-                /* do not start event if parent or child event is still running */
-                if (is_device_busy(manager, event) != 0)
+                /* do not start event if parent or child event is still running or queued */
+                r = event_is_blocked(event);
+                if (r < 0) {
+                        sd_device_action_t a = _SD_DEVICE_ACTION_INVALID;
+
+                        (void) sd_device_get_action(event->dev, &a);
+                        log_device_warning_errno(event->dev, r,
+                                                 "Failed to check event dependency, "
+                                                 "skipping event (SEQNUM=%"PRIu64", ACTION=%s)",
+                                                 event->seqnum,
+                                                 strna(device_action_to_string(a)));
+
+                        event_free(event);
+                        return r;
+                }
+                if (r > 0)
                         continue;
 
-                event_run(manager, event);
+                r = event_run(event);
+                if (r <= 0)
+                        return r;
         }
+
+        return 0;
 }
 
-static void event_queue_cleanup(Manager *manager, enum event_state match_type) {
-        struct event *event, *tmp;
+static int event_queue_insert(Manager *manager, sd_device *dev) {
+        _cleanup_(sd_device_unrefp) sd_device *clone = NULL;
+        Event *event;
+        uint64_t seqnum;
+        int r;
 
-        LIST_FOREACH_SAFE(event, event, tmp, manager->events) {
-                if (match_type != EVENT_UNDEF && match_type != event->state)
-                        continue;
+        assert(manager);
+        assert(dev);
 
-                event_free(event);
+        /* only one process can add events to the queue */
+        assert(manager->pid == getpid_cached());
+
+        /* We only accepts devices received by device monitor. */
+        r = sd_device_get_seqnum(dev, &seqnum);
+        if (r < 0)
+                return r;
+
+        /* Save original device to restore the state on failures. */
+        r = device_shallow_clone(dev, &clone);
+        if (r < 0)
+                return r;
+
+        r = device_copy_properties(clone, dev);
+        if (r < 0)
+                return r;
+
+        event = new(Event, 1);
+        if (!event)
+                return -ENOMEM;
+
+        *event = (Event) {
+                .manager = manager,
+                .dev = sd_device_ref(dev),
+                .dev_kernel = TAKE_PTR(clone),
+                .seqnum = seqnum,
+                .state = EVENT_QUEUED,
+        };
+
+        if (LIST_IS_EMPTY(manager->events)) {
+                r = touch("/run/udev/queue");
+                if (r < 0)
+                        log_warning_errno(r, "Failed to touch /run/udev/queue, ignoring: %m");
+        }
+
+        LIST_APPEND(event, manager->events, event);
+
+        log_device_uevent(dev, "Device is queued");
+
+        return 0;
+}
+
+static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
+        Manager *manager = userdata;
+        int r;
+
+        assert(manager);
+
+        DEVICE_TRACE_POINT(kernel_uevent_received, dev);
+
+        device_ensure_usec_initialized(dev, NULL);
+
+        r = event_queue_insert(manager, dev);
+        if (r < 0) {
+                log_device_error_errno(dev, r, "Failed to insert device into event queue: %m");
+                return 1;
         }
+
+        /* we have fresh events, try to schedule them */
+        event_queue_start(manager);
+
+        return 1;
 }
 
 static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
@@ -980,7 +1049,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
         assert(manager);
 
         for (;;) {
-                struct worker_message msg;
+                WorkerMessage msg;
                 struct iovec iovec = {
                         .iov_base = &msg,
                         .iov_len = sizeof(msg),
@@ -994,7 +1063,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
                 };
                 ssize_t size;
                 struct ucred *ucred;
-                struct worker *worker;
+                Worker *worker;
 
                 size = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT);
                 if (size == -EINTR)
@@ -1007,7 +1076,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
 
                 cmsg_close_all(&msghdr);
 
-                if (size != sizeof(struct worker_message)) {
+                if (size != sizeof(WorkerMessage)) {
                         log_warning("Ignoring worker message with invalid size %zi bytes", size);
                         continue;
                 }
@@ -1041,30 +1110,8 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
         return 1;
 }
 
-static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
-        Manager *manager = userdata;
-        int r;
-
-        assert(manager);
-
-        DEVICE_TRACE_POINT(kernel_uevent_received, dev);
-
-        device_ensure_usec_initialized(dev, NULL);
-
-        r = event_queue_insert(manager, dev);
-        if (r < 0) {
-                log_device_error_errno(dev, r, "Failed to insert device into event queue: %m");
-                return 1;
-        }
-
-        /* we have fresh events, try to schedule them */
-        event_queue_start(manager);
-
-        return 1;
-}
-
 /* receive the udevd message from userspace */
-static int on_ctrl_msg(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, const union udev_ctrl_msg_value *value, void *userdata) {
+static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrlMessageValue *value, void *userdata) {
         Manager *manager = userdata;
         int r;
 
@@ -1357,7 +1404,7 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi
         for (;;) {
                 pid_t pid;
                 int status;
-                struct worker *worker;
+                Worker *worker;
 
                 pid = waitpid(-1, &status, WNOHANG);
                 if (pid <= 0)
@@ -1663,7 +1710,7 @@ static int parse_argv(int argc, char *argv[]) {
                 case '?':
                         return -EINVAL;
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
 
                 }
         }
index 26b634ef5b4977e1fa07df182c1ddf658654f25b..c2312c790998d9f669d31acf3dabd32281722ae2 100644 (file)
@@ -52,7 +52,7 @@ int main(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
         device = argv[optind];
index 8db0c34fb5be8f11c3466862bd891020e5dc879c..af494d3075b2ad9ad6ec907fd040a3edc772d15f 100644 (file)
@@ -90,7 +90,7 @@ static int show_user(UserRecord *ur, Table *table) {
                 break;
 
         default:
-                assert_not_reached("Unexpected output mode");
+                assert_not_reached();
         }
 
         return 0;
@@ -240,7 +240,7 @@ static int show_group(GroupRecord *gr, Table *table) {
                 break;
 
         default:
-                assert_not_reached("Unexpected display mode");
+                assert_not_reached();
         }
 
         return 0;
@@ -384,7 +384,7 @@ static int show_membership(const char *user, const char *group, Table *table) {
                 break;
 
         default:
-                assert_not_reached("Unexpected output mode");
+                assert_not_reached();
         }
 
         return 0;
@@ -420,7 +420,7 @@ static int display_memberships(int argc, char *argv[], void *userdata) {
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to enumerate groups of user: %m");
                         } else
-                                assert_not_reached("Unexpected verb");
+                                assert_not_reached();
 
                         for (;;) {
                                 _cleanup_free_ char *user = NULL, *group = NULL;
@@ -762,7 +762,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
         }
 
index c3eb99ce9c190c248c3cb2ce2f832c65800a682b..4520d2fd16e12ec9156402662b7c08bbe4b8b898 100644 (file)
@@ -37,7 +37,6 @@ typedef struct LookupParameters {
 
 static int add_nss_service(JsonVariant **v) {
         _cleanup_(json_variant_unrefp) JsonVariant *status = NULL, *z = NULL;
-        char buf[SD_ID128_STRING_MAX];
         sd_id128_t mid;
         int r;
 
@@ -54,7 +53,7 @@ static int add_nss_service(JsonVariant **v) {
                 return r;
 
         status = json_variant_ref(json_variant_by_key(*v, "status"));
-        z = json_variant_ref(json_variant_by_key(status, sd_id128_to_string(mid, buf)));
+        z = json_variant_ref(json_variant_by_key(status, SD_ID128_TO_STRING(mid)));
 
         if (json_variant_by_key(z, "service"))
                 return 0;
@@ -63,7 +62,7 @@ static int add_nss_service(JsonVariant **v) {
         if (r < 0)
                 return r;
 
-        r = json_variant_set_field(&status, buf, z);
+        r = json_variant_set_field(&status, SD_ID128_TO_STRING(mid), z);
         if (r < 0)
                 return r;
 
index fd95e6f304b1527286d729c41baca4980e6835ee..1c68d283646412be33cb70cc654bb828f337fb18 100644 (file)
@@ -194,7 +194,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 static int determine_devices(void) {
         _cleanup_free_ void *m = NULL;
         sd_id128_t root_uuid, verity_uuid;
-        char ids[ID128_UUID_STRING_MAX];
         size_t l;
         int r;
 
@@ -217,7 +216,7 @@ static int determine_devices(void) {
         if (!arg_data_what) {
                 memcpy(&root_uuid, m, sizeof(root_uuid));
 
-                arg_data_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(root_uuid, ids));
+                arg_data_what = path_join("/dev/disk/by-partuuid", ID128_TO_UUID_STRING(root_uuid));
                 if (!arg_data_what)
                         return log_oom();
         }
@@ -225,7 +224,7 @@ static int determine_devices(void) {
         if (!arg_hash_what) {
                 memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
 
-                arg_hash_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(verity_uuid, ids));
+                arg_hash_what = path_join("/dev/disk/by-partuuid", ID128_TO_UUID_STRING(verity_uuid));
                 if (!arg_hash_what)
                         return log_oom();
         }
index 34208dcd87659e5da4501e169aa9856fd5344783..e58bae45d2293958c5bd4c7dcf6893dd02124b78 100644 (file)
@@ -30,7 +30,7 @@ static int help(void) {
 
         printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]\n"
                "%s detach VOLUME\n\n"
-               "Attaches or detaches an integrity protected block device.\n"
+               "Attach or detach an integrity protected block device.\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                program_invocation_short_name,
@@ -130,7 +130,10 @@ static int run(int argc, char *argv[]) {
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
         int r;
 
-        if (argc <= 1)
+        if (argc <= 1 ||
+            strv_contains(strv_skip(argv, 1), "--help") ||
+            strv_contains(strv_skip(argv, 1), "-h") ||
+            streq(argv[1], "help"))
                 return help();
 
         if (argc < 3)
index f21d3f54b1315288f992de007a55f5ae58706eb3..501cdca0b6e4d2351f083a06c8e3707b941e5bc7 100644 (file)
@@ -416,8 +416,7 @@ int xdg_autostart_format_exec_start(
                         if (!escaped)
                                 return log_oom();
 
-                        free_and_replace(exec_split[n], escaped);
-                        n++;
+                        free_and_replace(exec_split[n++], escaped);
                         continue;
                 }
 
@@ -457,8 +456,7 @@ int xdg_autostart_format_exec_start(
                 if (!quoted)
                         return log_oom();
 
-                free_and_replace(exec_split[n], quoted);
-                n++;
+                free_and_replace(exec_split[n++], quoted);
         }
         for (; exec_split[n]; n++)
                 exec_split[n] = mfree(exec_split[n]);
index 6f3db5955484836dd27a6434230747c9ff4d9e6a..b058f9364bee9e18ade68544d7f900d1044c1e05 100644 (file)
@@ -23,5 +23,5 @@ custom_target(
 
 if install_sysconfdir
         meson.add_install_script('sh', '-c',
-                                 mkdir_p.format(join_paths(sysconfdir, 'sysctl.d')))
+                                 mkdir_p.format(sysconfdir / 'sysctl.d'))
 endif
index 352f00682b66dcfac7f4b6b14f3a7b92c62fd770..faba5ba0c49282866765da5335f46e98f5c9a279 100755 (executable)
@@ -12,6 +12,10 @@ test_append_files() {
     (
         local workspace="${1:?}"
 
+        # On openSUSE the static linked version of busybox is named "busybox-static".
+        busybox="$(type -P busybox-static || type -P busybox)"
+        inst_simple "$busybox" "$(dirname $busybox)/busybox"
+
         if selinuxenabled >/dev/null; then
             dracut_install selinuxenabled
             cp -ar /etc/selinux "$workspace/etc/selinux"
diff --git a/test/TEST-62-RESTRICT-IFACES/Makefile b/test/TEST-62-RESTRICT-IFACES/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-62-RESTRICT-IFACES/test.sh b/test/TEST-62-RESTRICT-IFACES/test.sh
new file mode 100755 (executable)
index 0000000..b2829d2
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+TEST_NO_NSPAWN=1
+
+set -e
+TEST_DESCRIPTION="test RestrictNetworkInterfaces="
+. $TEST_BASE_DIR/test-functions
+
+do_test "$@" 62
index b2b7b262940b2a9de85e56a0af8911cf866d7c58..b6f34b47ecf13607149bb9b806086c8d4eed4ccc 100755 (executable)
@@ -7,18 +7,10 @@ set -o pipefail
 root="${1:?Usage $0 container-root}"
 mkdir -p "$root"
 mkdir "$root/bin"
-cp $(type -P busybox) "$root/bin"
 
-os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)
-ID_LIKE=$(awk -F= '$1=="ID_LIKE" { print $2 ;}' $os_release)
-if [[ "$ID_LIKE" = *"suse"* ]]; then
-    mkdir -p "$root/lib"
-    mkdir -p "$root/lib64"
-    for lib in $(find /lib*/ld*); do
-        [[ -d $root/$(dirname $lib) ]] || mkdir -p $root/$(dirname $lib)
-        cp $lib $root/$lib
-    done
-fi
+# On openSUSE the static linked version of busybox is named "busybox-static".
+busybox="$(type -P busybox-static || type -P busybox)"
+cp "$busybox" "$root/bin/busybox"
 
 mkdir -p "$root/usr/lib"
 touch "$root/usr/lib/os-release"
@@ -30,6 +22,7 @@ ln -s busybox "$root/bin/ps"
 ln -s busybox "$root/bin/ip"
 ln -s busybox "$root/bin/seq"
 ln -s busybox "$root/bin/sleep"
+ln -s busybox "$root/bin/usleep"
 ln -s busybox "$root/bin/test"
 
 mkdir -p "$root/sbin"
index 112a81930f7ff434405e28c9409e2c47616bb25e..b5cffb1a271fb365932d5dd545086d84daeb111f 100644 (file)
@@ -36,6 +36,7 @@ TCPSegmentationOffload=
 TCP6SegmentationOffload=
 UDPSegmentationOffload=
 GenericReceiveOffload=
+GenericReceiveOffloadHardware=
 LargeReceiveOffload=
 RxChannels=
 TxChannels=
@@ -51,3 +52,25 @@ TxFlowControl=
 AutoNegotiationFlowControl=
 GenericSegmentOffloadMaxBytes=
 GenericSegmentOffloadMaxSegments=
+RxCoalesceSec=
+RxMaxCoalescedFrames=
+RxCoalesceIrqSec=
+RxMaxCoalescedIrqFrames=
+TxCoalesceSec=
+TxMaxCoalescedFrames=
+TxCoalesceIrqSec=
+TxMaxCoalescedIrqFrames=
+StatisticsBlockCoalesceSec=
+UseAdaptiveRxCoalesce=
+UseAdaptiveTxCoalesce=
+CoalescePacketRateLow=
+RxCoalesceLowSec=
+RxMaxCoalescedLowFrames=
+TxCoalesceLowSec=
+TxMaxCoalescedLowFrames=
+CoalescePacketRateHigh=
+RxCoalesceHighSec=
+RxMaxCoalescedHighFrames=
+TxCoalesceHighSec=
+TxMaxCoalescedHighFrames=
+CoalescePacketRateSampleIntervalSec=
index f1305a04b2bdaeb2ab77831558afb2554f98be95..a3711cb77d9c487ee24b6c8de89657260f246882 100644 (file)
@@ -107,6 +107,7 @@ VendorClassIdentifier=
 Hostname=
 DUIDType=
 UseHostname=
+Label=
 CriticalConnection=
 DUIDRawData=
 RequestBroadcast=
@@ -257,8 +258,18 @@ MUDURL=
 [CAN]
 SamplePoint=
 BitRate=
+TimeQuantaNSec=
+PropagationSegment=
+PhaseBufferSegment1=
+PhaseBufferSegment2=
+SyncJumpWidth=
 DataSamplePoint=
 DataBitRate=
+DataTimeQuantaNSec=
+DataPropagationSegment=
+DataPhaseBufferSegment1=
+DataPhaseBufferSegment2=
+DataSyncJumpWidth=
 FDMode=
 FDNonISO=
 RestartSec=
@@ -266,6 +277,10 @@ TripleSampling=
 BusErrorReporting=
 Termination=
 ListenOnly=
+Loopback=
+OneShot=
+PresumeACK=
+ClassicDataLengthCode=
 [Address]
 DuplicateAddressDetection=
 AutoJoin=
@@ -307,6 +322,7 @@ EmitDNS=
 EmitDomains=
 Managed=
 OtherInformation=
+UplinkInterface=
 [IPv6PrefixDelegation]
 RouterPreference=
 DNSLifetimeSec=
@@ -326,6 +342,7 @@ Label=
 Prefix=
 [IPv6AcceptRA]
 UseDomains=
+UseMTU=
 RouteTable=
 RouteMetric=
 UseDNS=
index 3039d1c0cdcf9890a79965f64dcbe9c76528193b..494c7545e48e10c6e08e42888c59a026b9b039a0 100644 (file)
@@ -886,6 +886,7 @@ RemoveIPC=
 ReserveVT=
 RestrictAddressFamilies=
 RestrictNamespaces=
+RestrictNetworkInterfaces=
 RestrictRealtime=
 RestrictSUIDSGID=
 RuntimeDirectory=
index d161c81ff685a021eb53537d7635d9c52ea69587..fd82fc5348e8f9aff040d705bfa6ce7732acc2b8 100644 (file)
@@ -144,6 +144,7 @@ RemoveIPC=
 RestartKillSignal=
 RestrictAddressFamilies=
 RestrictNamespaces=
+RestrictNetworkInterfaces=
 RestrictRealtime=
 RestrictSUIDSGID=
 RootDirectory=
index 7e69cf816b6a2dfedb63de7ca39b2ff184760671..ab49083311075221d96ed2cda7b28fb12a94ab29 100644 (file)
@@ -48,6 +48,7 @@ MemoryMin=
 MemorySwapMax=
 NetClass=
 RestartKillSignal=
+RestrictNetworkInterfaces=
 RuntimeMaxSec=
 SendSIGHUP=
 SendSIGKILL=
index 35d1f2a104f266f0604d35b6c9110161815446ec..6a80bbcb2fefea41606b8d70c0313bbc192d66dd 100644 (file)
@@ -275,6 +275,7 @@ RestartPreventExitStatus=
 RestartSec=
 RestrictAddressFamilies=
 RestrictNamespaces=
+RestrictNetworkInterfaces=
 RestrictRealtime=
 RestrictSUIDSGID=
 RootDirectory=
index 789ac8f0db54f01dd05e4eba4cb084ac43d80b1a..17bd431db7d7367031413595ce1896688f9cc7e2 100644 (file)
@@ -44,6 +44,7 @@ MemoryMax=
 MemoryMin=
 MemorySwapMax=
 NetClass=
+RestrictNetworkInterfaces=
 Slice=
 SocketBindAllow=
 SocketBindDeny=
index 1835167cfba18d3e8154885784e9edbdd40f76dc..1a79a0dfd1428f4f33a9576663e11cd806116968 100644 (file)
@@ -180,6 +180,7 @@ RemoveOnStop=
 RestartKillSignal=
 RestrictAddressFamilies=
 RestrictNamespaces=
+RestrictNetworkInterfaces=
 RestrictRealtime=
 RestrictSUIDSGID=
 ReusePort=
index 814d066faced786d305f80d0cd8a700483cb0e47..204e172514711982212cf81192304f691be29830 100644 (file)
@@ -141,6 +141,7 @@ RemoveIPC=
 RestartKillSignal=
 RestrictAddressFamilies=
 RestrictNamespaces=
+RestrictNetworkInterfaces=
 RestrictRealtime=
 RestrictSUIDSGID=
 RootDirectory=
index 47c7f4d49a51a194ddee2f0e1fb3bbb2539e5f7d..a21230a4a8875d660cfc0b673c48aa4be1794f38 100644 (file)
@@ -140,12 +140,14 @@ if want_tests != 'false' and dmi_arches.contains(host_machine.cpu_family())
         endif
 
         foreach p : out.stdout().split()
-                source = join_paths(project_source_root, p)
+                source = project_source_root / p
                 name = 'dmidecode_' + p.split('/')[-1].split('.')[0]
 
                 test(name,
                      udev_dmi_memory_id_test,
-                     args : [dmi_memory_id_path, source, source + '.txt'])
+                     args : [udev_prog_paths['dmi_memory_id'],
+                             source,
+                             source + '.txt'])
         endforeach
 endif
 
index d5e6f4a5b134b8e1ba0d10470ef0ee3f57438872..436d9e0ef33a1abd6d4e562b3b013ce2ac2cd11b 100644 (file)
@@ -83,13 +83,11 @@ TOOLS_DIR="$SOURCE_DIR/tools"
 export TEST_BASE_DIR TEST_UNITS_DIR SOURCE_DIR TOOLS_DIR
 
 # note that find-build-dir.sh will return $BUILD_DIR if provided, else it will try to find it
-if ! BUILD_DIR="$("$TOOLS_DIR"/find-build-dir.sh)"; then
-    if get_bool "${NO_BUILD:=}"; then
-        BUILD_DIR="$SOURCE_DIR"
-    else
-        echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2
-        exit 1
-    fi
+if get_bool "${NO_BUILD:=}"; then
+    BUILD_DIR="$SOURCE_DIR"
+elif ! BUILD_DIR="$("$TOOLS_DIR"/find-build-dir.sh)"; then
+    echo "ERROR: no build found, please set BUILD_DIR or use NO_BUILD" >&2
+    exit 1
 fi
 
 PATH_TO_INIT="$ROOTLIBDIR/systemd"
@@ -126,7 +124,6 @@ BASICTOOLS=(
     base64
     basename
     bash
-    busybox
     capsh
     cat
     chmod
@@ -229,7 +226,7 @@ is_built_with_asan() {
 
     # Borrowed from https://github.com/google/oss-fuzz/blob/cd9acd02f9d3f6e80011cc1e9549be526ce5f270/infra/base-images/base-runner/bad_build_check#L182
     local _asan_calls
-    _asan_calls="$(objdump -dC "$SYSTEMD_JOURNALD" | grep -E "callq?\s+[0-9a-f]+\s+<__asan" -c)"
+    _asan_calls="$(objdump -dC "$SYSTEMD_JOURNALD" | grep -E "(callq?|brasl?|bl)\s.+__asan" -c)"
     if ((_asan_calls < 1000)); then
         return 1
     else
@@ -348,12 +345,14 @@ run_qemu() {
             [ "$ARCH" ] || ARCH=$(uname -m)
             case $ARCH in
                 ppc64*)
-                KERNEL_BIN="/boot/vmlinux-$KERNEL_VER"
-                CONSOLE=hvc0
-                ;;
+                    # Ubuntu ppc64* calls the kernel binary as vmlinux-*, RHEL/CentOS
+                    # uses the "standard" vmlinuz- prefix
+                    [[ -e "/boot/vmlinux-$KERNEL_VER" ]] && KERNEL_BIN="/boot/vmlinux-$KERNEL_VER" || KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER"
+                    CONSOLE=hvc0
+                    ;;
                 *)
-                KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER"
-                ;;
+                    KERNEL_BIN="/boot/vmlinuz-$KERNEL_VER"
+                    ;;
             esac
         fi
     fi
@@ -625,8 +624,9 @@ EOF
 
         export initdir="$TESTDIR/app1"
         mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
-        grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app1"
-        echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app1"
+        grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2"
+        echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app2"
+        setfattr -n user.extension-release.strict -v false "$initdir/usr/lib/extension-release.d/extension-release.app2"
         cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF
 [Service]
 Type=oneshot
@@ -637,7 +637,7 @@ EOF
 #!/bin/bash
 set -e
 test -e /usr/lib/os-release
-cat /usr/lib/extension-release.d/extension-release.app1
+cat /usr/lib/extension-release.d/extension-release.app2
 EOF
         chmod +x "$initdir/opt/script1.sh"
         echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file"
@@ -665,12 +665,14 @@ setup_basic_environment() {
     install_fs_tools
     install_modules
     install_plymouth
+    install_haveged
     install_debug_tools
     install_ld_so_conf
     install_testuser
     has_user_dbus_socket && install_user_dbus
     setup_selinux
     strip_binaries
+    instmods veth
     install_depmod_files
     generate_module_dependencies
     if get_bool "$IS_BUILT_WITH_ASAN"; then
@@ -934,11 +936,52 @@ install_debian_systemd() {
     done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2)
 }
 
+install_suse_systemd() {
+    local testsdir=/usr/lib/systemd/tests
+    local pkgs
+
+    dinfo "Install SUSE systemd"
+
+    pkgs=(
+        systemd
+        systemd-container
+        systemd-coredump
+        systemd-experimental
+        systemd-journal-remote
+        systemd-portable
+        udev
+    )
+
+    for p in "${pkgs[@]}"; do
+        rpm -q "$p" &>/dev/null || continue
+
+        ddebug "Install files from package $p"
+        while read -r f; do
+            [ -e "$f" ] || continue
+            [ -d "$f" ] && continue
+            inst "$f"
+        done < <(rpm -ql "$p")
+    done
+
+    # we only need testsdata dir as well as the unit tests (for
+    # TEST-02-UNITTESTS) in the image.
+    dinfo "Install unit tests and testdata directory"
+
+    mkdir -p "$initdir/$testsdir"
+    cp "$testsdir"/test-* "$initdir/$testsdir/"
+    cp -a "$testsdir/testdata" "$initdir/$testsdir/"
+
+    # On openSUSE, these dirs are not created at package install for now on.
+    mkdir -p "$initdir/var/log/journal/remote"
+}
+
 install_distro_systemd() {
     dinfo "Install distro systemd"
 
     if get_bool "$LOOKS_LIKE_DEBIAN"; then
         install_debian_systemd
+    elif get_bool "$LOOKS_LIKE_SUSE"; then
+        install_suse_systemd
     else
         dfatal "NO_BUILD not supported for this distro"
         exit 1
@@ -956,8 +999,6 @@ install_systemd() {
     # remove unneeded documentation
     rm -fr "$initdir"/usr/share/{man,doc}
 
-    get_bool "$LOOKS_LIKE_SUSE" && setup_suse
-
     # enable debug logging in PID1
     echo LogLevel=debug >>"$initdir/etc/systemd/system.conf"
     # store coredumps in journal
@@ -1186,6 +1227,9 @@ check_result_common() {
             setfacl -m "user:${SUDO_USER:?}:r-X" "${TESTDIR:?}/"failed
         fi
         ret=1
+    elif get_bool "$TIMED_OUT"; then
+        echo "(timeout)" >"${TESTDIR:?}/failed"
+        ret=2
     elif [ -e "$workspace/testok" ]; then
         # …/testok always counts (but with lower priority than …/failed)
         ret=0
@@ -1194,9 +1238,6 @@ check_result_common() {
         echo "${TESTNAME:?} was skipped:"
         cat "$workspace/skipped"
         ret=0
-    elif get_bool "$TIMED_OUT"; then
-        echo "(timeout)" >"${TESTDIR:?}/failed"
-        ret=2
     else
         echo "(failed; see logs)" >"${TESTDIR:?}/failed"
         ret=3
@@ -1388,6 +1429,16 @@ install_plymouth() {
     # fi
 }
 
+install_haveged() {
+    # If haveged is installed and probably included in initrd, it needs to be
+    # installed in the image too.
+    if [ -x /usr/sbin/haveged ]; then
+        dinfo "Install haveged files"
+        inst /usr/sbin/haveged
+        inst /usr/lib/systemd/system/haveged.service
+    fi
+}
+
 install_ld_so_conf() {
     dinfo "Install /etc/ld.so.conf*"
     cp -a /etc/ld.so.conf* "${initdir:?}/etc"
@@ -1549,7 +1600,7 @@ install_pam() {
         paths+=(/lib*/security)
     fi
 
-    for d in /etc/pam.d /etc/security /usr/lib/pam.d; do
+    for d in /etc/pam.d /etc/security /usr/{etc,lib}/pam.d; do
         [ -d "$d" ] && paths+=("$d")
     done
 
@@ -1563,6 +1614,13 @@ install_pam() {
 
     # set empty root password for easy debugging
     sed -i 's/^root:x:/root::/' "${initdir:?}/etc/passwd"
+
+    # And make sure pam_unix will accept it by making sure that
+    # the PAM module has the nullok option.
+    for d in /etc/pam.d /usr/{etc,lib}/pam.d; do
+        [ -d "$initdir/$d" ] || continue
+        sed -i '/^auth.*pam_unix.so/s/$/ nullok/' "$initdir/$d"/*
+    done
 }
 
 install_keymaps() {
@@ -1681,6 +1739,11 @@ inst_libs() {
 
     while read -r line; do
         [[ "$line" = 'not a dynamic executable' ]] && break
+        # Skip a harmless error when running the tests on a system with a significantly
+        # older systemd version (ldd tries to resolve the unprefixed RPATH for libsystemd.so.0,
+        # which is in this case older than the already installed libsystemd.so.0 in $initdir).
+        # The issue is triggered by installing test dependencies in install_missing_libraries().
+        [[ "$line" =~ libsystemd.so.*:\ version\ .*\ not\ found ]] && continue
 
         if [[ "$line" =~ $so_regex ]]; then
             file="${BASH_REMATCH[1]}"
@@ -2343,7 +2406,7 @@ instmods() {
                 else
                     (
                         [[ "$mpargs" ]] && echo "$mpargs"
-                        find "$mod_dir" -path "*/${mod#=}/*" -type f -printf '%f\n'
+                        find "$mod_dir" -path "*/${mod#=}/*" -name "*.ko*" -type f -printf '%f\n'
                     ) | instmods
                 fi
                 ;;
@@ -2399,12 +2462,6 @@ instmods() {
     return 0
 }
 
-setup_suse() {
-    ln -fs ../usr/bin/systemctl "${initdir:?}/bin/"
-    ln -fs ../usr/lib/systemd "$initdir/lib/"
-    inst_simple "/usr/lib/systemd/system/haveged.service"
-}
-
 _umount_dir() {
     local mountpoint="${1:?}"
     if mountpoint -q "$mountpoint"; then
diff --git a/test/test-network/conf/25-vxlan-ipv6.netdev b/test/test-network/conf/25-vxlan-ipv6.netdev
new file mode 100644 (file)
index 0000000..9533925
--- /dev/null
@@ -0,0 +1,7 @@
+[NetDev]
+Name=vxlan97
+Kind=vxlan
+
+[VXLAN]
+VNI=4831583
+Local=fe80::281:8eff:fef0:73aa
diff --git a/test/test-network/conf/26-bridge-issue-20373.netdev b/test/test-network/conf/26-bridge-issue-20373.netdev
new file mode 100644 (file)
index 0000000..105012a
--- /dev/null
@@ -0,0 +1,12 @@
+[NetDev]
+Name=bridge99
+Kind=bridge
+
+[Bridge]
+MulticastQuerier=yes
+MulticastSnooping=yes
+Priority=10
+STP=yes
+ForwardDelaySec=5
+MulticastIGMPVersion=2
+VLANFiltering=yes
diff --git a/test/test-network/conf/26-bridge-vlan-master-issue-20373.network b/test/test-network/conf/26-bridge-vlan-master-issue-20373.network
new file mode 100644 (file)
index 0000000..a74ff55
--- /dev/null
@@ -0,0 +1,20 @@
+[Match]
+Name=bridge99
+
+[Network]
+VLAN=vlan99
+IPForward=yes
+ConfigureWithoutCarrier=yes
+LLDP=yes
+IPv6AcceptRA=false
+
+[Bridge]
+Learning=yes
+MulticastRouter=no
+UseBPDU=yes
+
+[BridgeVLAN]
+VLAN=100
+
+[BridgeVLAN]
+VLAN=600
diff --git a/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network b/test/test-network/conf/26-bridge-vlan-slave-issue-20373.network
new file mode 100644 (file)
index 0000000..5a3c88c
--- /dev/null
@@ -0,0 +1,29 @@
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+IPForward=yes
+Bridge=bridge99
+LinkLocalAddressing=no
+EmitLLDP=nearest-bridge
+LLDP=yes
+
+[Link]
+RequiredForOnline=no
+
+[Bridge]
+Learning=yes
+MulticastRouter=query
+UseBPDU=yes
+
+[BridgeVLAN]
+VLAN=100
+EgressUntagged=100
+PVID=100
+
+[BridgeVLAN]
+VLAN=560
+
+[BridgeVLAN]
+VLAN=600
index 41d6ea40faefc0be07de928efbfa531b96b357e6..5352956e7e5d42d2516739192f893686159c65f6 100644 (file)
@@ -14,3 +14,4 @@ Hostname=test-hostname
 ClientIdentifier=mac
 VendorClassIdentifier=SusantVendorTest
 RouteTable=211
+Label=test-label
index 0b75063dc130c1d69c9eb7dbb0617f67d7c876b9..ecd91cd19c22fa39c7652f5cd349f968227d822a 100644 (file)
@@ -14,4 +14,4 @@ DNS=9.9.9.9
 
 [DHCPServerStaticLease]
 MACAddress=12:34:56:78:9a:bc
-Address=10.1.1.3
+Address=10.1.1.200
index 7a1cfc65e19f0d7467cc46dd089ea7bef562d2bd..4c8903513919890526253827e1792a77a488287f 100644 (file)
@@ -13,3 +13,7 @@ Prefix=2002:da8:1:0::/64
 [DHCPServer]
 DNS=192.168.5.1
 NTP=192.168.5.1
+
+[IPv6SendRA]
+EmitDNS=no
+EmitDomains=no
index ce7a76f70c5e114bc509d71833c395fe440c86af..be0d2e40c56580067c4233dd535e605d83bceb6c 100644 (file)
@@ -9,3 +9,4 @@ IPv6AcceptRA=yes
 RouterDenyList=2001::1
 PrefixDenyList=2001:db8:0:2::
 RouteDenyList=2001:db1:fff::
+UseDomains=yes
index 58883658f31d10e1695a75da29fb51ea264aa40b..e18ecaf29054005fb30f63443dbd8fe15064df83 100644 (file)
@@ -11,3 +11,4 @@ PrefixAllowList=2001:db8:0:1:: 2001:db8:0:1::
 PrefixDenyList=2001:db8:0:1:: 2001:db8:0:1::
 RouteAllowList=2001:db0:fff:: 2001:db0:fff::
 RouteDenyList=2001:db0:fff:: 2001:db0:fff::
+UseDomains=yes
index cfb03f50c47b2353ef7536bae512c5978ed7a646..72ad808b7187bcd24a90091abf29b1baab1b9fbc 100644 (file)
@@ -5,6 +5,9 @@ Name=veth99
 DHCP=no
 IPv6SendRA=yes
 
+[IPv6SendRA]
+UplinkInterface=dummy98
+
 [IPv6Prefix]
 Prefix=2001:db8:0:1::/64
 
diff --git a/test/test-network/conf/ipv6ra-uplink.network b/test/test-network/conf/ipv6ra-uplink.network
new file mode 100644 (file)
index 0000000..9146def
--- /dev/null
@@ -0,0 +1,8 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:db8:1:1::1/64
+DNS=2001:db8:1:1::2
+Domains=example.com
diff --git a/test/test-network/conf/vxlan-ipv6.network b/test/test-network/conf/vxlan-ipv6.network
new file mode 100644 (file)
index 0000000..8ecff08
--- /dev/null
@@ -0,0 +1,24 @@
+[Match]
+Name=vxlan97
+
+[Network]
+IPv6AcceptRA=no
+LinkLocalAddressing=yes
+
+[BridgeFDB]
+MACAddress=00:00:00:00:00:00
+Destination=fe80::27c:16ff:fec0:6c74
+OutgoingInterface=test1
+VNI=4831583
+
+[BridgeFDB]
+MACAddress=00:00:00:00:00:00
+Destination=fe80::2a2:e4ff:fef9:2269
+OutgoingInterface=test1
+VNI=4831583
+
+[BridgeFDB]
+MACAddress=00:00:00:00:00:00
+Destination=fe80::23b:d2ff:fe95:967f
+OutgoingInterface=test1
+VNI=4831583
index aaffb646358ab64598a84b22cea71bfd641b5138..d76be59dd12c2de39b29a93b4caa2e4bc71b16b5 100644 (file)
@@ -5,3 +5,4 @@ Name=test1
 IPv6AcceptRA=false
 LinkLocalAddressing=yes
 VXLAN=vxlan99
+VXLAN=vxlan97
index dc31d133c4e99c1489118df1b40de5ee73c58282..83878614231622ee08c8ad9fcd268238befc9cde 100755 (executable)
@@ -674,9 +674,9 @@ class NetworkctlTests(unittest.TestCase, Utilities):
 
         output = check_output('ip -4 address show dev dummy98')
         print(output)
-        self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
-        self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
-        self.assertRegex(output, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
+        self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
+        self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
+        self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
 
         check_output('ip address del 10.1.2.3/16 dev dummy98')
         check_output('ip address del 10.1.2.4/16 dev dummy98')
@@ -687,9 +687,30 @@ class NetworkctlTests(unittest.TestCase, Utilities):
 
         output = check_output('ip -4 address show dev dummy98')
         print(output)
-        self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
-        self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
-        self.assertRegex(output, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98')
+        self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
+        self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
+        self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
+
+        remove_unit_from_networkd_path(['25-address-static.network'])
+
+        check_output(*networkctl_cmd, 'reload', env=env)
+        self.wait_operstate('dummy98', 'degraded', setup_state='unmanaged')
+
+        output = check_output('ip -4 address show dev dummy98')
+        print(output)
+        self.assertNotIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
+        self.assertNotIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
+        self.assertNotIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
+
+        copy_unit_to_networkd_unit_path('25-address-static.network')
+        check_output(*networkctl_cmd, 'reload', env=env)
+        self.wait_online(['dummy98:routable'])
+
+        output = check_output('ip -4 address show dev dummy98')
+        print(output)
+        self.assertIn('inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98', output)
+        self.assertIn('inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98', output)
+        self.assertIn('inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98', output)
 
     def test_reload(self):
         start_networkd(3)
@@ -854,6 +875,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         'vtitun98',
         'vtitun99',
         'vxcan99',
+        'vxlan97',
         'vxlan98',
         'vxlan99',
         'wg97',
@@ -944,6 +966,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '25-vti-tunnel.netdev',
         '25-vxcan.netdev',
         '25-vxlan-independent.netdev',
+        '25-vxlan-ipv6.netdev',
         '25-vxlan.netdev',
         '25-wireguard-23-peers.netdev',
         '25-wireguard-23-peers.network',
@@ -974,6 +997,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         'sit.network',
         'vti6.network',
         'vti.network',
+        'vxlan-ipv6.network',
         'vxlan-test1.network',
         'vxlan.network',
         'xfrm.network',
@@ -1159,6 +1183,25 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
                 self.assertRegex(output, ' mtu 2000 ')
                 self.assertRegex(output, 'macvlan mode ' + mode + ' ')
 
+                rc = call("ip link del test1")
+                self.assertEqual(rc, 0)
+                time.sleep(1)
+
+                rc = call("ip link add test1 type dummy")
+                self.assertEqual(rc, 0)
+                time.sleep(1)
+
+                self.wait_online(['macvlan99:degraded', 'test1:degraded'])
+
+                output = check_output('ip -d link show test1')
+                print(output)
+                self.assertRegex(output, ' mtu 2000 ')
+
+                output = check_output('ip -d link show macvlan99')
+                print(output)
+                self.assertRegex(output, ' mtu 2000 ')
+                self.assertRegex(output, 'macvlan mode ' + mode + ' ')
+
     @expectedFailureIfModuleIsNotAvailable('ipvlan')
     def test_ipvlan(self):
         for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]:
@@ -1635,36 +1678,43 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
 
     def test_vxlan(self):
         copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network',
+                                        '25-vxlan-ipv6.netdev', 'vxlan-ipv6.network',
                                         '25-vxlan-independent.netdev', 'netdev-link-local-addressing-yes.network',
                                         '11-dummy.netdev', 'vxlan-test1.network')
         start_networkd()
 
-        self.wait_online(['test1:degraded', 'vxlan99:degraded', 'vxlan98:degraded'])
+        self.wait_online(['test1:degraded', 'vxlan99:degraded', 'vxlan98:degraded', 'vxlan97:degraded'])
 
         output = check_output('ip -d link show vxlan99')
         print(output)
-        self.assertRegex(output, '999')
-        self.assertRegex(output, '5555')
-        self.assertRegex(output, 'l2miss')
-        self.assertRegex(output, 'l3miss')
-        self.assertRegex(output, 'udpcsum')
-        self.assertRegex(output, 'udp6zerocsumtx')
-        self.assertRegex(output, 'udp6zerocsumrx')
-        self.assertRegex(output, 'remcsumtx')
-        self.assertRegex(output, 'remcsumrx')
-        self.assertRegex(output, 'gbp')
+        self.assertIn('999', output)
+        self.assertIn('5555', output)
+        self.assertIn('l2miss', output)
+        self.assertIn('l3miss', output)
+        self.assertIn('udpcsum', output)
+        self.assertIn('udp6zerocsumtx', output)
+        self.assertIn('udp6zerocsumrx', output)
+        self.assertIn('remcsumtx', output)
+        self.assertIn('remcsumrx', output)
+        self.assertIn('gbp', output)
 
         output = check_output('bridge fdb show dev vxlan99')
         print(output)
-        self.assertRegex(output, '00:11:22:33:44:55 dst 10.0.0.5 self permanent')
-        self.assertRegex(output, '00:11:22:33:44:66 dst 10.0.0.6 self permanent')
-        self.assertRegex(output, '00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent')
+        self.assertIn('00:11:22:33:44:55 dst 10.0.0.5 self permanent', output)
+        self.assertIn('00:11:22:33:44:66 dst 10.0.0.6 self permanent', output)
+        self.assertIn('00:11:22:33:44:77 dst 10.0.0.7 via test1 self permanent', output)
 
         output = check_output(*networkctl_cmd, '-n', '0', 'status', 'vxlan99', env=env)
         print(output)
-        self.assertRegex(output, 'VNI: 999')
-        self.assertRegex(output, 'Destination Port: 5555')
-        self.assertRegex(output, 'Underlying Device: test1')
+        self.assertIn('VNI: 999', output)
+        self.assertIn('Destination Port: 5555', output)
+        self.assertIn('Underlying Device: test1', output)
+
+        output = check_output('bridge fdb show dev vxlan97')
+        print(output)
+        self.assertIn('00:00:00:00:00:00 dst fe80::23b:d2ff:fe95:967f via test1 self permanent', output)
+        self.assertIn('00:00:00:00:00:00 dst fe80::27c:16ff:fec0:6c74 via test1 self permanent', output)
+        self.assertIn('00:00:00:00:00:00 dst fe80::2a2:e4ff:fef9:2269 via test1 self permanent', output)
 
     def test_macsec(self):
         copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
@@ -3415,21 +3465,29 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
     links = [
         'bridge99',
         'dummy98',
-        'test1']
+        'test1',
+        'vlan99',
+    ]
 
     units = [
         '11-dummy.netdev',
         '12-dummy.netdev',
+        '21-vlan.netdev',
+        '21-vlan.network',
         '26-bridge.netdev',
         '26-bridge-configure-without-carrier.network',
+        '26-bridge-issue-20373.netdev',
         '26-bridge-mdb-master.network',
         '26-bridge-mdb-slave.network',
         '26-bridge-slave-interface-1.network',
         '26-bridge-slave-interface-2.network',
+        '26-bridge-vlan-master-issue-20373.network',
         '26-bridge-vlan-master.network',
+        '26-bridge-vlan-slave-issue-20373.network',
         '26-bridge-vlan-slave.network',
         'bridge99-ignore-carrier-loss.network',
-        'bridge99.network']
+        'bridge99.network'
+    ]
 
     routing_policy_rule_tables = ['100']
 
@@ -3464,6 +3522,25 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
             self.assertRegex(output, f'{i}')
         self.assertNotRegex(output, '4095')
 
+    def test_bridge_vlan_issue_20373(self):
+        copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-vlan-slave-issue-20373.network',
+                                        '26-bridge-issue-20373.netdev', '26-bridge-vlan-master-issue-20373.network',
+                                        '21-vlan.netdev', '21-vlan.network')
+        start_networkd()
+        self.wait_online(['test1:enslaved', 'bridge99:degraded', 'vlan99:routable'])
+
+        output = check_output('bridge vlan show dev test1')
+        print(output)
+        self.assertIn('100 PVID Egress Untagged', output)
+        self.assertIn('560', output)
+        self.assertIn('600', output)
+
+        output = check_output('bridge vlan show dev bridge99')
+        print(output)
+        self.assertIn('1 PVID Egress Untagged', output)
+        self.assertIn('100', output)
+        self.assertIn('600', output)
+
     def test_bridge_mdb(self):
         copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-mdb-slave.network',
                                         '26-bridge.netdev', '26-bridge-mdb-master.network')
@@ -3642,7 +3719,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
 
         output = check_output('ip rule list table 100')
         print(output)
-        self.assertIn('0:      from all to 8.8.8.8 lookup 100', output)
+        self.assertIn('from all to 8.8.8.8 lookup 100', output)
 
 class NetworkdLLDPTests(unittest.TestCase, Utilities):
     links = ['veth99']
@@ -3805,7 +3882,7 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
 
         output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
         print(output)
-        self.assertIn('10.1.1.3 (DHCP4 via 10.1.1.1)', output)
+        self.assertIn('10.1.1.200 (DHCP4 via 10.1.1.1)', output)
 
 class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
     links = [
@@ -4073,6 +4150,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, '12:34:56:78:9a:bc')
         self.assertRegex(output, '192.168.5')
         self.assertRegex(output, '1492')
+        self.assertRegex(output, 'test-label')
 
         print('## ip route show table main dev veth99')
         output = check_output('ip route show table main dev veth99')
@@ -4685,14 +4763,19 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
 
 class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
-    links = ['veth99']
+    links = [
+        'dummy98',
+        'veth99',
+    ]
 
     units = [
+        '12-dummy.netdev',
         '25-veth.netdev',
         'ipv6ra-prefix-client-deny-list.network',
         'ipv6ra-prefix-client.network',
-        'ipv6ra-prefix.network'
-        ]
+        'ipv6ra-prefix.network',
+        'ipv6ra-uplink.network',
+    ]
 
     def setUp(self):
         remove_links(self.links)
@@ -4705,10 +4788,11 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
         stop_networkd(show_logs=True)
 
     def test_ipv6_route_prefix(self):
-        copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client.network', 'ipv6ra-prefix.network')
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client.network', 'ipv6ra-prefix.network',
+                                        '12-dummy.netdev', 'ipv6ra-uplink.network')
 
         start_networkd()
-        self.wait_online(['veth99:routable', 'veth-peer:routable'])
+        self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
 
         output = check_output('ip address show dev veth-peer')
         print(output)
@@ -4727,11 +4811,20 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
         self.assertNotIn('inet6 2001:db8:0:1:', output)
         self.assertIn('inet6 2001:db8:0:2:', output)
 
+        output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env)
+        print(output)
+        self.assertRegex(output, '2001:db8:1:1::2')
+
+        output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env)
+        print(output)
+        self.assertIn('example.com', output)
+
     def test_ipv6_route_prefix_deny_list(self):
-        copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client-deny-list.network', 'ipv6ra-prefix.network')
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client-deny-list.network', 'ipv6ra-prefix.network',
+                                        '12-dummy.netdev', 'ipv6ra-uplink.network')
 
         start_networkd()
-        self.wait_online(['veth99:routable', 'veth-peer:routable'])
+        self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
 
         output = check_output('ip address show dev veth-peer')
         print(output)
@@ -4750,6 +4843,14 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
         self.assertNotIn('inet6 2001:db8:0:1:', output)
         self.assertIn('inet6 2001:db8:0:2:', output)
 
+        output = check_output(*resolvectl_cmd, 'dns', 'veth-peer', env=env)
+        print(output)
+        self.assertRegex(output, '2001:db8:1:1::2')
+
+        output = check_output(*resolvectl_cmd, 'domain', 'veth-peer', env=env)
+        print(output)
+        self.assertIn('example.com', output)
+
 class NetworkdMTUTests(unittest.TestCase, Utilities):
     links = ['dummy98']
 
index 24f0da35abd197430c9c1bf4b7c474eddb1b1486..f81a9e3ace22b942bf66a099ce819cf2443c0586 100644 (file)
@@ -4,4 +4,13 @@ Description=TEST-10-ISSUE-2467
 [Service]
 ExecStartPre=rm -f /failed /testok
 Type=oneshot
-ExecStart=sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test10.socket; printf x >test.file; socat -t20 OPEN:test.file UNIX-CONNECT:/run/test.ctl; >/testok'
+ExecStart=rm -f /tmp/nonexistent
+ExecStart=systemctl start test10.socket
+ExecStart=sh -x -c 'printf x >test.file'
+ExecStart=-socat -T20 OPEN:test.file UNIX-CONNECT:/run/test.ctl
+# TriggerLimitIntervalSec= by default is set to 2s. A "sleep 10" should give
+# systemd enough time even on slower machines, to reach the trigger limit.
+ExecStart=sleep 10
+ExecStart=sh -x -c 'test "$(systemctl show test10.socket -P ActiveState)" = failed'
+ExecStart=sh -x -c 'test "$(systemctl show test10.socket -P Result)" = trigger-limit-hit'
+ExecStart=sh -x -c 'echo OK >/testok'
index 8f3c0b2df83df90d08a728b7b1cd97e531cc22cc..3f5a542738634471aa6ef7bfcd9c86ba5f84cd7c 100755 (executable)
@@ -30,19 +30,13 @@ if unshare -U sh -c :; then
     is_user_ns_supported=yes
 fi
 
-SUSE_OPTS=()
-ID_LIKE=$(awk -F= '$1=="ID_LIKE" { print $2 ;}' /etc/os-release)
-if [[ "$ID_LIKE" = *"suse"* ]]; then
-    SUSE_OPTS+=(--bind /lib64 --bind /usr/lib64)
-fi
-
 function check_bind_tmp_path {
     # https://github.com/systemd/systemd/issues/4789
     local _root="/var/lib/machines/testsuite-13.bind-tmp-path"
     rm -rf "$_root"
     /usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
     : >/tmp/bind
-    systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
+    systemd-nspawn --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
 }
 
 function check_norbind {
@@ -54,15 +48,15 @@ function check_norbind {
     mount -t tmpfs tmpfs /tmp/binddir/subdir
     echo -n "inner" >/tmp/binddir/subdir/file
     /usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
-    systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
+    systemd-nspawn --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
 }
 
 function check_notification_socket {
     # https://github.com/systemd/systemd/issues/4944
     local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/host/notify'
     # /testsuite-13.nc-container is prepared by test.sh
-    systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd"
-    systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd"
+    systemd-nspawn --register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd"
+    systemd-nspawn --register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd"
 }
 
 function check_os_release {
@@ -84,7 +78,7 @@ if echo test >>/run/host/os-release; then exit 1; fi
         echo MARKER=1 >>/etc/os-release
     fi
 
-    systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
+    systemd-nspawn --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
 
     if grep -q MARKER /etc/os-release; then
         rm /etc/os-release
@@ -93,7 +87,7 @@ if echo test >>/run/host/os-release; then exit 1; fi
 }
 
 function check_machinectl_bind {
-    local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep 0.5; done; exit 1;'
+    local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; usleep 500000; done; exit 1;'
 
     cat >/run/systemd/system/nspawn_machinectl_bind.service <<EOF
 [Service]
@@ -138,16 +132,16 @@ function run {
     local _root="/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3"
     rm -rf "$_root"
     /usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --private-network -b
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -b
 
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -U -b; then
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -U -b; then
         [[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1
     else
         [[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1
     fi
 
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" --private-network -U -b; then
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -U -b; then
         [[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1
     else
         [[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1
@@ -167,21 +161,21 @@ function run {
     # --network-namespace-path and network-related options cannot be used together
     for netopt in "${_net_opts[@]}"; do
         echo "$_netns_opt in combination with $netopt should fail"
-        if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b "$_netns_opt" "$netopt"; then
+        if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b "$_netns_opt" "$netopt"; then
             echo >&2 "unexpected pass"
             return 1
         fi
     done
 
     # allow combination of --network-namespace-path and --private-network
-    if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" -b "$_netns_opt" --private-network; then
+    if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b "$_netns_opt" --private-network; then
         return 1
     fi
 
     # test --network-namespace-path works with a network namespace created by "ip netns"
     ip netns add nspawn_test
     _netns_opt="--network-namespace-path=/run/netns/nspawn_test"
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn "${SUSE_OPTS[@]}" --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
     local r=$?
     ip netns del nspawn_test
 
index 3408e6d71acb86167fae93e97caf84667035a920..77bb6db15a70944cc50639ecdbc032af3c4c1f04 100755 (executable)
@@ -5,6 +5,11 @@ set -eux
 set -o pipefail
 
 export SYSTEMD_LOG_LEVEL=debug
+mkdir -p /run/systemd/system/systemd-portabled.service.d/
+cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
+[Service]
+Environment=SYSTEMD_LOG_LEVEL=debug
+EOF
 
 portablectl attach --now --runtime /usr/share/minimal_0.raw app0
 
@@ -63,24 +68,31 @@ portablectl detach --now --enable --runtime /tmp/minimal_1 app0
 
 portablectl list | grep -q -F "No images."
 
-root="/usr/share/minimal_0.raw"
-app1="/usr/share/app1.raw"
+portablectl attach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0
 
-portablectl attach --now --runtime --extension ${app1} ${root} app1
+systemctl is-active app0.service
+
+portablectl reattach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
+
+systemctl is-active app0.service
+
+portablectl detach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
+
+portablectl attach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
 
 systemctl is-active app1.service
 
-portablectl reattach --now --runtime --extension ${app1} ${root} app1
+portablectl reattach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
 
 systemctl is-active app1.service
 
-portablectl detach --now --runtime --extension ${app1} ${root} app1
+portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
 
 # portablectl also works with directory paths rather than images
 
 mkdir /tmp/rootdir /tmp/app1 /tmp/overlay
-mount ${app1} /tmp/app1
-mount ${root} /tmp/rootdir
+mount /usr/share/app1.raw /tmp/app1
+mount /usr/share/minimal_0.raw /tmp/rootdir
 mount -t overlay overlay -o lowerdir=/tmp/app1:/tmp/rootdir /tmp/overlay
 
 portablectl attach --copy=symlink --now --runtime /tmp/overlay app1
index 081a8bcb953d2458bd3fd363fad320261c3de5f9..9125918ea9db75cc3c133fc44618887711c040c7 100755 (executable)
@@ -235,7 +235,7 @@ systemd-run -P --property ExtensionImages=/usr/share/app0.raw --property RootIma
 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.app1"
+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"
 cat >/run/systemd/system/testservice-50e.service <<EOF
 [Service]
diff --git a/test/units/testsuite-62-1.service b/test/units/testsuite-62-1.service
new file mode 100644 (file)
index 0000000..b8e15c9
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-all-pings-work
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=
+Type=oneshot
diff --git a/test/units/testsuite-62-2.service b/test/units/testsuite-62-2.service
new file mode 100644 (file)
index 0000000..51328b0
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-allow-list
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=veth1
+Type=oneshot
diff --git a/test/units/testsuite-62-3.service b/test/units/testsuite-62-3.service
new file mode 100644 (file)
index 0000000..54ab196
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-deny-list
+[Service]
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=~veth0
+RestrictNetworkInterfaces=~veth1
+Type=oneshot
diff --git a/test/units/testsuite-62-4.service b/test/units/testsuite-62-4.service
new file mode 100644 (file)
index 0000000..1d267a9
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-empty-assigment
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=
+Type=oneshot
diff --git a/test/units/testsuite-62-5.service b/test/units/testsuite-62-5.service
new file mode 100644 (file)
index 0000000..b69485e
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-invert-assigment
+[Service]
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0
+RestrictNetworkInterfaces=veth0 veth1
+RestrictNetworkInterfaces=~veth0
+Type=oneshot
diff --git a/test/units/testsuite-62.service b/test/units/testsuite-62.service
new file mode 100644 (file)
index 0000000..faaa2c8
--- /dev/null
@@ -0,0 +1,6 @@
+Description=TEST-62-RESTRICT-IFACES
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-62.sh b/test/units/testsuite-62.sh
new file mode 100755 (executable)
index 0000000..9b22d79
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+setup() {
+    systemd-analyze log-level debug
+    systemd-analyze log-target console
+
+    for i in `seq 0 3`;
+    do
+        ip netns del ns${i} || true
+        ip link del veth${i} || true
+        ip netns add ns${i}
+        ip link add veth${i} type veth peer name veth${i}_
+        ip link set veth${i}_ netns ns${i}
+        ip -n ns${i} link set dev veth${i}_ up
+        ip -n ns${i} link set dev lo up
+        ip -n ns${i} addr add "192.168.113."$((4*i+1))/30 dev veth${i}_
+        ip link set dev veth${i} up
+        ip addr add "192.168.113."$((4*i+2))/30 dev veth${i}
+    done
+}
+
+teardown() {
+    set +e
+
+    for i in `seq 0 3`;
+    do
+        ip netns del ns${i}
+        ip link del veth${i}
+    done
+
+    systemd-analyze log-level info
+}
+
+KERNEL_VERSION="$(uname -r)"
+KERNEL_MAJOR="${KERNEL_VERSION%%.*}"
+KERNEL_MINOR="${KERNEL_VERSION#$KERNEL_MAJOR.}"
+KERNEL_MINOR="${KERNEL_MINOR%%.*}"
+
+MAJOR_REQUIRED=5
+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
+fi
+
+trap teardown EXIT
+setup
+
+systemctl start --wait testsuite-62-1.service
+systemctl start --wait testsuite-62-2.service
+systemctl start --wait testsuite-62-3.service
+systemctl start --wait testsuite-62-4.service
+systemctl start --wait testsuite-62-5.service
+
+echo OK > /testok
+
+exit 0
index 6f8ff09fc8cbd73b4df674e7358814d2b20462c8..de36f5743d0921b11665fba1b9e29e70e871c2bd 100644 (file)
@@ -37,8 +37,7 @@ foreach pair : in_files
                 # do nothing
         elif pair[1] == '' or conf.get(pair[1]) == 1
                 custom_target(
-                        # XXX: workaround for old meson. Drop when upgrading.
-                        'tmpfiles+' + pair[0],
+                        pair[0],
                         input : pair[0] + '.in',
                         output: pair[0],
                         command : [meson_render_jinja2, config_h, '@INPUT@'],
@@ -53,6 +52,5 @@ endforeach
 
 if enable_tmpfiles and install_sysconfdir
         meson.add_install_script(
-                'sh', '-c',
-                mkdir_p.format(join_paths(sysconfdir, 'tmpfiles.d')))
+                'sh', '-c', mkdir_p.format(sysconfdir / 'tmpfiles.d'))
 endif
diff --git a/units/factory-reset.target b/units/factory-reset.target
new file mode 100644 (file)
index 0000000..99f383d
--- /dev/null
@@ -0,0 +1,12 @@
+#  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=Target that triggers factory reset. Does nothing by default.
+Documentation=man:systemd.special(7)
index 17e9ead9c19a1079accc28667249a8d286794f4f..9398df721ea7aba1eb6980dc7c2493a8a4adfc4a 100644 (file)
@@ -19,6 +19,7 @@ units = [
          'sysinit.target.wants/'],
         ['emergency.target',                    ''],
         ['exit.target',                         ''],
+        ['factory-reset.target',                ''],
         ['final.target',                        ''],
         ['first-boot-complete.target',          ''],
         ['getty.target',                        '',
@@ -307,17 +308,17 @@ install_data('user-.slice.d/10-defaults.conf',
 
 if install_sysconfdir
         meson.add_install_script(meson_make_symlink,
-                                 join_paths(pkgsysconfdir, 'user'),
-                                 join_paths(sysconfdir, 'xdg/systemd/user'))
+                                 pkgsysconfdir / 'user',
+                                 sysconfdir / 'xdg/systemd/user')
 endif
 meson.add_install_script(meson_make_symlink,
-                         join_paths(dbussystemservicedir, 'org.freedesktop.systemd1.service'),
-                         join_paths(dbussessionservicedir, 'org.freedesktop.systemd1.service'))
+                         dbussystemservicedir / 'org.freedesktop.systemd1.service',
+                         dbussessionservicedir / 'org.freedesktop.systemd1.service')
 if conf.get('HAVE_SYSV_COMPAT') == 1
         foreach i : [1, 2, 3, 4, 5]
                 meson.add_install_script(
                         'sh', '-c',
-                        mkdir_p.format(join_paths(systemunitdir, 'runlevel@0@.target.wants'.format(i))))
+                        mkdir_p.format(systemunitdir / 'runlevel@0@.target.wants'.format(i)))
         endforeach
 endif
 
diff --git a/units/systemd-boot-update.service b/units/systemd-boot-update.service
new file mode 100644 (file)
index 0000000..61ff127
--- /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=Automatic Boot Loader Update
+Documentation=man:bootctl(1)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=local-fs.target
+Before=sysinit.target shutdown.target systemd-update-done.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=bootctl --no-variables --graceful update
+
+[Install]
+WantedBy=sysinit.target
index e962954f06c0baaa2bc1f47ea51904ceaabb6c47..7aee6463bd54c5cac86379c5640fbf78826a800e 100644 (file)
@@ -11,7 +11,7 @@
 Description=Cleanup of Temporary Directories
 Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8)
 DefaultDependencies=no
-Conflicts=shutdown.target
+Conflicts=shutdown.target initrd-switch-root.service
 After=local-fs.target time-set.target
 Before=shutdown.target
 
index 3f028d25337770ed6bb25b6e1228106bf80037c5..bc29dbc8c9c1793a514f0d61faa81ddcf51389e8 100644 (file)
@@ -11,7 +11,7 @@
 Description=Create Volatile Files and Directories
 Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8)
 DefaultDependencies=no
-Conflicts=shutdown.target
+Conflicts=shutdown.target initrd-switch-root.service
 After=local-fs.target systemd-sysusers.service systemd-journald.service
 Before=sysinit.target shutdown.target
 RefuseManualStop=yes