]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #20257 from bluca/seqno
authorLuca Boccassi <luca.boccassi@microsoft.com>
Tue, 31 Aug 2021 08:06:33 +0000 (09:06 +0100)
committerGitHub <noreply@github.com>
Tue, 31 Aug 2021 08:06:33 +0000 (09:06 +0100)
Use new diskseq block device property

649 files changed:
.github/workflows/mkosi.yml
.github/workflows/test_mkosi_boot.py
.mkosi/mkosi.opensuse
NEWS
README
TODO
catalog/systemd.catalog.in
docs/DISCOVERABLE_PARTITIONS.md
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-keyboard.hwdb
hwdb.d/60-sensor.hwdb
hwdb.d/70-pointingstick.hwdb
hwdb.d/80-ieee1394-unit-function.hwdb
man/bootctl.xml
man/homectl.xml
man/hwdb-usb-device.c
man/loader.conf.xml
man/logind.conf.xml
man/machinectl.xml
man/nss-myhostname.xml
man/nss-resolve.xml
man/org.freedesktop.systemd1.xml
man/os-release.xml
man/path-documents.c
man/portablectl.xml
man/rules/meson.build
man/sd-id128.xml
man/sd_id128_randomize.xml
man/sd_id128_to_string.xml
man/systemd-analyze.xml
man/systemd-boot.xml
man/systemd-gpt-auto-generator.xml
man/systemd-nspawn.xml
man/systemd-run.xml
man/systemd-sysext.xml
man/systemd-sysusers.xml
man/systemd-timesyncd.service.xml
man/systemd-veritysetup@.service.xml
man/systemd.exec.xml
man/systemd.link.xml
man/systemd.network.xml
man/systemd.resource-control.xml
man/systemd.special.xml
man/timesyncd.conf.xml
man/udevadm.xml
meson.build
meson_options.txt
po/LINGUAS
po/de.po
po/es.po
po/fi.po [new file with mode: 0644]
po/pt_BR.po
po/si.po
po/zh_TW.po
presets/90-systemd.preset
shell-completion/bash/systemd-analyze
shell-completion/bash/udevadm
shell-completion/zsh/_systemd-analyze
shell-completion/zsh/_udevadm
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/genetlink.h [new file with mode: 0644]
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/meson.build
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/automount.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/path.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/socket.h
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/string-util-fundamental.h
src/gpt-auto-generator/gpt-auto-generator.c
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/cat.c
src/journal/journalctl.c
src/journal/journald-server.c
src/journal/journald-stream.c
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/generic-netlink.c [deleted file]
src/libsystemd/sd-netlink/generic-netlink.h [deleted file]
src/libsystemd/sd-netlink/netlink-genl.c [new file with mode: 0644]
src/libsystemd/sd-netlink/netlink-genl.h [new file with mode: 0644]
src/libsystemd/sd-netlink/netlink-internal.h
src/libsystemd/sd-netlink/netlink-message-nfnl.c [moved from src/libsystemd/sd-netlink/nfnl-message.c with 68% similarity]
src/libsystemd/sd-netlink/netlink-message-rtnl.c [moved from src/libsystemd/sd-netlink/rtnl-message.c with 98% similarity]
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-slot.c
src/libsystemd/sd-netlink/netlink-socket.c
src/libsystemd/sd-netlink/netlink-types-genl.c [new file with mode: 0644]
src/libsystemd/sd-netlink/netlink-types-internal.h [new file with mode: 0644]
src/libsystemd/sd-netlink/netlink-types-nfnl.c [new file with mode: 0644]
src/libsystemd/sd-netlink/netlink-types-rtnl.c [new file with mode: 0644]
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-netlink/netlink-types.h
src/libsystemd/sd-netlink/netlink-util.c
src/libsystemd/sd-netlink/netlink-util.h
src/libsystemd/sd-netlink/sd-netlink.c
src/libsystemd/sd-netlink/test-netlink.c
src/libsystemd/sd-network/sd-network.c
src/libsystemd/sd-resolve/sd-resolve.c
src/libsystemd/sd-resolve/test-resolve.c
src/libudev/test-libudev.c
src/locale/keymap-util.c
src/locale/localectl.c
src/login/inhibit.c
src/login/loginctl.c
src/login/logind-action.c
src/login/logind-action.h
src/login/logind-dbus.c
src/login/logind-session-device.c
src/login/meson.build
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/batadv.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/netdev/wireguard.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-common.h
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/test-network-tables.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/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/exec-util.c
src/shared/firewall-util-nft.c
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/json.h
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/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/wifi-util.c
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/sd-bus-vtable.h
src/systemd/sd-id128.h
src/systemd/sd-messages.h
src/systemd/sd-netlink.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/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
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/TEST-63-ISSUE-17433/Makefile [new symlink]
test/TEST-63-ISSUE-17433/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-execute/exec-mount-apivfs-no.service [new file with mode: 0644]
test/test-functions
test/test-network/conf/25-nexthop.network
test/test-network/conf/25-route-static.network
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-allow-list.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/testsuite-10.units/test10.service
test/testsuite-63.units/test63.path [new file with mode: 0644]
test/testsuite-63.units/test63.service [new file with mode: 0644]
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]
test/units/testsuite-63.service [new file with mode: 0644]
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 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/NEWS b/NEWS
index d79f859fa1a8ca359f80d50bbc84ce7f27891e6a..3c1d90046212456ad5f58c8bbf6974b1f04d9854 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3867,6 +3867,9 @@ CHANGES WITH 240:
 
           Consult the kernel documentation for details on this sysctl:
           https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
+          
+        * The v239 change to turn on "net.ipv4.tcp_ecn" by default has been
+          reverted.
 
         * CPUAccounting=yes no longer enables the CPU controller when using
           kernel 4.15+ and the unified cgroup hierarchy, as required accounting
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 17dff7331dc7ca68a19441e25235720647a1bf72..51be74aa92245cc620795253a377d8d85fe69ba1 100644 (file)
--- a/TODO
+++ b/TODO
@@ -83,7 +83,16 @@ Janitorial Clean-ups:
 
 Features:
 
-* use credentials logic/TPM2 logic to store homed signing key
+* PAM: pick auf one authentication token from credentials
+
+* 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.
 
 * New udev block device symlink names:
   /dev/disk/by-parttypelabel/<pttype>/<ptlabel>. Use case: if pt label is used
@@ -1019,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
@@ -1194,48 +1199,36 @@ Features:
   - when homed is in use, maybe start the user session manager in a mount namespace with MS_SLAVE,
     so that mounts propagate down but not up - eg, user A setting up a backup volume
     doesn't mean user B sees it
-
-* homed: during login resize fs automatically towards size goal. Specifically,
-  resize to diskSize if possible, but leave a certain amount (configured by a
-  new value diskLeaveFreeSize) of space free on the backing fs.
-
-* homed: permit multiple user record signing keys to be used locally, and pick
-  the right one for signing records automatically depending on a pre-existing
-  signature
-
-* homed: add a way to "adopt" a home directory, i.e. strip foreign signatures
-  and insert a local signature instead.
-
-* homed: as an extension to the directory+subvolume backend: if located on
-  especially marked fs, then sync down password into LUKS header of that fs,
-  and always verify passwords against it too. Bootstrapping is a problem
-  though: if no one is logged in (or no other user even exists yet), how do you
-  unlock the volume in order to create the first user and add the first pw.
-
-* homed: support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt
-
-* homed: maybe pre-create ~/.cache as subvol so that it can have separate quota
-  easily?
-
-* homed: if kernel 5.12 uid mapping mounts exist, use that instead of recursive
-  chowns.
-
-* add a switch to homectl (maybe called --first-boot) where it will check if
-  any non-system users exist, and if not prompts interactively for basic user
-  info, mimicking systemd-firstboot. Then, place this in a service that runs
-  after systemd-homed, but before gdm and friends, as a simple, barebones
-  fallback logic to get a regular user created on uninitialized systems.
-
-* homed: store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with
-  systemd-cryptsetup, so that it can unlock homed volumes
-
-* homed: try to unmount in regular intervals when home dir was busy when we
-  tried because idle.
-
-* homed: keep an fd to the homedir open at all times, to keep the fs pinned
-  (autofs and such) while user is logged in.
-
-* when we resize disks (homed?) always round up to 4K sectors, not 512K
+  - use credentials logic/TPM2 logic to store homed signing key
+  - during login resize fs automatically towards size goal. Specifically,
+    resize to diskSize if possible, but leave a certain amount (configured by a
+    new value diskLeaveFreeSize) of space free on the backing fs.
+  - permit multiple user record signing keys to be used locally, and pick
+    the right one for signing records automatically depending on a pre-existing
+    signature
+  - add a way to "adopt" a home directory, i.e. strip foreign signatures
+    and insert a local signature instead.
+  - as an extension to the directory+subvolume backend: if located on
+    especially marked fs, then sync down password into LUKS header of that fs,
+    and always verify passwords against it too. Bootstrapping is a problem
+    though: if no one is logged in (or no other user even exists yet), how do you
+    unlock the volume in order to create the first user and add the first pw.
+  - support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt
+  - maybe pre-create ~/.cache as subvol so that it can have separate quota
+    easily?
+  - if kernel 5.12 uid mapping mounts exist, use that instead of recursive
+    chowns.
+  - add a switch to homectl (maybe called --first-boot) where it will check if
+    any non-system users exist, and if not prompts interactively for basic user
+    info, mimicking systemd-firstboot. Then, place this in a service that runs
+    after systemd-homed, but before gdm and friends, as a simple, barebones
+    fallback logic to get a regular user created on uninitialized systems.
+  - store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with
+    systemd-cryptsetup, so that it can unlock homed volumes
+  - try to unmount in regular intervals when home dir was busy when we
+    tried because idle.
+  - keep an fd to the homedir open at all times, to keep the fs pinned
+    (autofs and such) while user is logged in.
 
 * add a new switch --auto-definitions=yes/no or so to systemd-repart. If
   specified, synthesize a definition automatically if we can: enlarge last
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 4caa086dc5b32061db51edb1b73f8af3a54221c2..bd4cb24602b681ca84112e7de1229bfd49e222fb 100644 (file)
@@ -49,7 +49,7 @@ Interface](https://systemd.io/BOOT_LOADER_INTERFACE).
 | `77055800-792c-4f94-b39a-98c91b762bb6` | _Root Partition (LoongArch 64-bit)_ | ditto | ditto |
 | `60d5a7fe-8e7d-435c-b714-3dd8162144e1` | _Root Partition (RISC-V 32-bit)_ | ditto | ditto |
 | `72ec70a6-cf74-40e6-bd49-4bda08e8f224` | _Root Partition (RISC-V 64-bit)_ | ditto | ditto |
-| `d13c5d3b-b5d1-422a-b29f-9454fdc89d76` | _Root Verity Partition (x86)_ | A dm-verity superblock followed by hash data | On systems with matching architecture, contains dm-verity integrity hash data for the matching root partition. If this feature is used the partition UUID of the root partition should be the first 128bit of the root hash of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the final 128bit of it, so that the root partition and its verity partition can be discovered easily, simply by specifying the root hash. |
+| `d13c5d3b-b5d1-422a-b29f-9454fdc89d76` | _Root Verity Partition (x86)_ | A dm-verity superblock followed by hash data | Contains dm-verity integrity hash data for the matching root partition. If this feature is used the partition UUID of the root partition should be the first 128 bits of the root hash of the dm-verity hash data, and the partition UUID of this dm-verity partition should be the final 128 bits of it, so that the root partition and its verity partition can be discovered easily, simply by specifying the root hash. |
 | `2c7357ed-ebd2-46d9-aec1-23d437ec2bf5` | _Root Verity Partition (x86-64)_ | ditto | ditto |
 | `7386cdf2-203c-47a9-a498-f2ecce45a2d6` | _Root Verity Partition (32-bit ARM)_ | ditto | ditto |
 | `df3300ce-d69f-4c92-978c-9bfb0f38d820` | _Root Verity Partition (64-bit ARM/AArch64)_ | ditto | ditto |
@@ -75,7 +75,7 @@ Interface](https://systemd.io/BOOT_LOADER_INTERFACE).
 | `8f1056be-9b05-47c4-81d6-be53128e5b54` | _`/usr/` Verity Partition (RISC-V 64-bit)_ | ditto | ditto |
 | `933ac7e1-2eb4-4f13-b844-0e14e2aef915` | _Home Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/home/`.  If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/home`. |
 | `3b8f8425-20e0-4f3b-907f-1a25a76f98e8` | _Server Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/srv/`.  If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/srv`. |
-| `4d21b016-b534-45c2-a9fb-5c16e091fd2d` | _Variable Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/` — under the condition that its partition UUID matches the first 128 bit of `HMAC-SHA256(machine-id, 0x4d21b016b53445c2a9fb5c16e091fd2d)` (i.e. the SHA256 HMAC hash of the binary type UUID keyed by the machine ID as read from [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html). This special requirement is made because `/var/` (unlike the other partition types listed here) is inherently private to a specific installation and cannot possibly be shared between multiple OS installations on the same disk, and thus should be bound to a specific instance of the OS, identified by its machine ID. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`. |
+| `4d21b016-b534-45c2-a9fb-5c16e091fd2d` | _Variable Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/` — under the condition that its partition UUID matches the first 128 bits of `HMAC-SHA256(machine-id, 0x4d21b016b53445c2a9fb5c16e091fd2d)` (i.e. the SHA256 HMAC hash of the binary type UUID keyed by the machine ID as read from [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html). This special requirement is made because `/var/` (unlike the other partition types listed here) is inherently private to a specific installation and cannot possibly be shared between multiple OS installations on the same disk, and thus should be bound to a specific instance of the OS, identified by its machine ID. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/var`. |
 | `7ec6f557-3bc5-4aca-b293-16ef5df639d1` | _Temporary Data Partition_ | Any native, optionally in LUKS | The first partition with this type UUID on the disk containing the root partition is automatically mounted to `/var/tmp/`.  If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/tmp`. Note that the intended mount point is indeed `/var/tmp/`, not `/tmp/`. The latter is typically maintained in memory via <tt>tmpfs</tt> and does not require a partition on disk. In some cases it might be desirable to make `/tmp/` persistent too, in which case it is recommended to make it a symlink or bind mount to `/var/tmp/`, thus not requiring its own partition type UUID. |
 | `0657fd6d-a4ab-43c4-84e5-0933c84b4f4f` | _Swap_ | Swap, optionally in LUKS | All swap partitions on the disk containing the root partition are automatically enabled. If the partition is encrypted with LUKS, the device mapper file will be named `/dev/mapper/swap`. This partition type predates the Discoverable Partitions Specification. |
 | `0fc63daf-8483-4772-8e79-3d69d8477de4` | _Generic Linux Data Partitions_ | Any native, optionally in LUKS | No automatic mounting takes place for other Linux data partitions. This partition type should be used for all partitions that carry Linux file systems. The installer needs to mount them explicitly via entries in <tt>/etc/fstab</tt>. Optionally, these partitions may be encrypted with LUKS. This partition type predates the Discoverable Partitions Specification. |
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 2e0614369709db552a08a181c507b249d3dfd0bf..374a77a5d3d00303c368318d3d6f3f17cf7ba1fc 100644 (file)
@@ -709,6 +709,10 @@ evdev:name:gpio-keys:phys:gpio-keys/input0:ev:3:dmi:*:svnHewlett-Packard:pnHPStr
 evdev:name:gpio-keys:phys:gpio-keys/input0:ev:23:dmi:*:svnHewlett-Packard:pnHPStream7Tablet:*
  KEYBOARD_KEY_0=unknown
 
+# HP Omen 15
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pnOMENLaptop15*:pvr*
+ KEYBOARD_KEY_a1=!calc
+
 ##########################################################
 # Huawei
 ##########################################################
index 9859ce5b124097f811dc2fdb48f47dc467922c59..0424f06fb7e2421e4c9cf3b2577fb9bf97874fa9 100644 (file)
@@ -240,6 +240,10 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo*:pnP1D6_C109K:*
 sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrY13D_KB133.103:bd06/01/2018:svnHampoo:pnDefaultstring:pvrV100:rvnHampoo:rnY13D_KB133:rvrV100:cvnDefaultstring:ct9:cvrDefaultstring:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
 
+# Chuwi SurBook Mini (CWI540)
+sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo*:pnC3W6_AP108_4GB:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+
 #########################################
 # Connect
 #########################################
@@ -403,6 +407,16 @@ sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd03/20/201
 sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/25/2017:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
  ACCEL_LOCATION=base
 
+#########################################
+# Hometech
+########################################
+
+# Nobody bothered to use Linux on any device of this manufacturer
+# so current marks might be too general and need fixes.
+# These values are based on Wi101 model.
+sensor:modalias:acpi:BMA250E*:dmi:*:svnInsyde*:pni101c:*
+ ACCEL_MOUNT_MATRIX=0,1,0;-1,0,0;-1,0,0
+
 #########################################
 # HP
 #########################################
index b427f21ea54cd21536e69825730c1faff3e7dc13..6039119b6a14c83fb547e47ec285f2108b7b0efd 100644 (file)
@@ -43,6 +43,7 @@
 #   udevadm info /dev/input/eventXX.
 #
 # Allowed properties are:
+#   ID_INPUT_POINTINGSTICK
 #   POINTINGSTICK_CONST_ACCEL (deprecated)
 #   POINTINGSTICK_SENSITIVITY
 #
 
 # Sort by brand, model
 
+##########################################
+# Generic
+##########################################
+evdev:name:*[tT]rack[pP]oint*:*
+ ID_INPUT_POINTINGSTICK=1
+
 #########################################
 # Dell
 #########################################
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 d05c3f34d07d827a825b412f6782ff1b018dc7a0..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>
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 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 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 a7c60183aefe1b8b136bf922d9549a629c64413a..15608c437a390b3e1e242c91cadb5127a797d559 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>
       main system. Additionally, the presence of that file means that the system is in the initrd phase.
       <filename>/etc/os-release</filename> should be symlinked to <filename>/etc/initrd-release</filename>
       (or vice versa), so programs that only look for <filename>/etc/os-release</filename> (as described
-      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>
+      above) work correctly.</para>
+
+      <para>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>
+      plays the same role for extension images as <filename>os-release</filename> for the main system, and
+      follows the 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 identify the extension and to allow the operating system to verify that the extension image
+      matches the base OS. This is typically implemented by checking that the <varname>ID=</varname> options
+      match, and either <varname>SYSEXT_LEVEL=</varname> exists and matches too, or if it is not present,
+      <varname>VERSION_ID=</varname> exists and matches. This ensures ABI/API compatibility between the
+      layers and prevents merging of an incompatible image in an overlay.</para>
+
+      <para>In the <filename>extension-release.<replaceable>IMAGE</replaceable></filename> filename, the
+      <replaceable>IMAGE</replaceable> part must exactly match the file name of the containing image with the
+      suffix removed. 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, it is possible to relax this check: if exactly one
+      file whose name matches <literal><filename>extension-release.*</filename></literal> is present in this
+      directory, and the file 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>, it will be used instead.</para>
+
+      <para>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>
 
 
           <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 +467,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 a6c1f9371ae22d9318963cec2eb6e04fbadc1fa1..082d6c29fb4636ad697bf4b193e14deb37928459 100644 (file)
@@ -1,9 +1,17 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <sd-path.h>
 
 int main(void) {
+  int r;
   char *t;
 
-  sd_path_lookup(SD_PATH_USER_DOCUMENTS, NULL, &t);
+  r = sd_path_lookup(SD_PATH_USER_DOCUMENTS, NULL, &t);
+  if (r < 0)
+    return EXIT_FAILURE;
+
   printf("~/Documents: %s\n", t);
+  free(t);
+
+  return EXIT_SUCCESS;
 }
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 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 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 798faa59c830cf0366ef78768fcc8fa286194694..a67eedb51d2d0dea68f28937dd676bda46864777 100644 (file)
       For more information, see <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
     </para>
 
+    <para>The root partition can be specified by symlinking <filename>/run/systemd/volatile-root</filename>
+    to <filename>/dev/block/$major:$minor</filename>. This is especially useful if the root mount has been
+    replaced by some form of volatile file system (overlayfs).
+    </para>
+
     <para>Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP
     is mounted to <filename>/boot/</filename> (except if an Extended Boot Loader partition exists, see
     below), unless a mount point directory <filename>/efi/</filename> exists, in which case it is mounted
index 3623ef015a19c1fd468360f70ebd17205b5c7f47..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>
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 f8a9f47539f1c817c50a8546bf66b423774d051b..3ceb9fe7de11551ab7d9dfed1366cb6629bc6a58 100644 (file)
@@ -92,7 +92,7 @@
 
     <para>During boot OS extension images are activated automatically, if the
     <filename>systemd-sysext.service</filename> is enabled. Note that this service runs only after the
-    underlying file systems where system extensions are searched are mounted. This means they are not
+    underlying file systems where system extensions may be located have been mounted. This means they are not
     suitable for shipping resources that are processed by subsystems running in earliest boot. Specifically,
     OS extension images are not suitable for shipping system services or
     <citerefentry><refentrytitle>systemd-sysusers</refentrytitle><manvolnum>8</manvolnum></citerefentry>
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 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 008cbe9af1d4edd596c443bdfd1b5ed4964fb35f..eadfc024213ca2327b6fb87efc0ca00903eaf4df 100644 (file)
         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,
index ee4356ac3d66b6f14e021eb49168db1b70dd9059..638a1522cd3875fa7ec8539b586e807b5f9baef6 100644 (file)
       <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>LargeReceiveOffload=</varname></term>
+        <term><varname>GenericReceiveOffloadHardware=</varname></term>
         <listitem>
-          <para>Takes a boolean. If set to true, the Large Receive Offload (LRO) is enabled.
-          When unset, the kernel's default will be used.</para>
+          <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>RxChannels=</varname></term>
+        <term><varname>LargeReceiveOffload=</varname></term>
         <listitem>
-          <para>Sets the number of receive channels (a number between 1 and 4294967295) .</para>
+          <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>
-        <listitem>
-          <para>Sets the number of transmit channels (a number between 1 and 4294967295).</para>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
         <term><varname>OtherChannels=</varname></term>
-        <listitem>
-          <para>Sets the number of other channels (a number between 1 and 4294967295).</para>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
         <term><varname>CombinedChannels=</varname></term>
         <listitem>
-          <para>Sets the number of combined set 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>RxBufferSize=</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>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
         <term><varname>RxMiniBufferSize=</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>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
         <term><varname>RxJumboBufferSize=</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>
-        </listitem>
-      </varlistentry>
-      <varlistentry>
         <term><varname>TxBufferSize=</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>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>RxFlowControl=</varname></term>
         <listitem>
-          <para>Takes a boolean. When set, enables the receive flow control, also known as the ethernet
+          <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>
         <term><varname>TxFlowControl=</varname></term>
         <listitem>
-          <para>Takes a boolean. When set, enables the transmit flow control, also known as the ethernet
+          <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>
         <term><varname>AutoNegotiationFlowControl=</varname></term>
         <listitem>
-          <para>Takes a boolean. When set, the auto negotiation enables the interface to exchange state
+          <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>
           accept. An unsigned integer in the range 1…65535. Defaults to unset.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>UseAdaptiveRxCoalesce=</varname></term>
+        <term><varname>UseAdaptiveTxCoalesce=</varname></term>
+        <listitem>
+          <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>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>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>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>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>CoalescePacketRateLow=</varname></term>
+        <term><varname>CoalescePacketRateHigh=</varname></term>
+        <listitem>
+          <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>CoalescePacketRateSampleIntervalSec=</varname></term>
+        <listitem>
+          <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>StatisticsBlockCoalesceSec=</varname></term>
+        <listitem>
+          <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>
 
     </variablelist>
   </refsect1>
index b8bb8aedf4625a720a03a274f260fef263253e51..5b19a4b48a21ac8fabc47807450e35f9c5946eb7 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>
             has been unsuccessful for some time. (IPv4 link-local address autoconfiguration will usually
             happen in parallel with repeated attempts to acquire a DHCPv4 lease).</para>
 
-            <para>Defaults to <option>no</option> when <varname>Bridge=yes</varname> is set, and
+            <para>Defaults to <option>no</option> when <varname>Bridge=</varname> is set or when the specified
+            <varname>MACVLAN=</varname>/<varname>MACVTAP=</varname> has <varname>Mode=passthru</varname>, or
             <option>ipv6</option> otherwise.</para>
           </listitem>
         </varlistentry>
@@ -1053,7 +1056,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 +1242,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>
@@ -1770,6 +1776,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>
@@ -1869,8 +1884,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>
 
@@ -1950,7 +1966,8 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
         <varlistentry>
           <term><varname>DenyList=</varname></term>
           <listitem>
-            <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are
+            <para>A whitespace-separated list of IPv4 addresses. Each address can optionally take a
+            prefix length after <literal>/</literal>. DHCP offers from servers in the list are
             rejected. Note that if <varname>AllowList=</varname> is configured then
             <varname>DenyList=</varname> is ignored.</para>
           </listitem>
@@ -1959,7 +1976,8 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
         <varlistentry>
           <term><varname>AllowList=</varname></term>
           <listitem>
-            <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are
+            <para>A whitespace-separated list of IPv4 addresses. Each address can optionally take a
+            prefix length after <literal>/</literal>. DHCP offers from servers in the list are
             accepted.</para>
           </listitem>
         </varlistentry>
@@ -2250,6 +2268,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>
@@ -2269,50 +2295,56 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
         <varlistentry>
           <term><varname>RouterDenyList=</varname></term>
           <listitem>
-            <para>A whitespace-separated list of IPv6 router addresses. Any information advertised by
-            the listed router is ignored.</para>
+            <para>A whitespace-separated list of IPv6 router addresses. Each address can optionally
+            take a prefix length after <literal>/</literal>. Any information advertised by the listed
+            router is ignored.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>RouterAllowList=</varname></term>
           <listitem>
-            <para>A whitespace-separated list of IPv6 router addresses. Only information advertised by
-            the listed router is accepted. Note that if <varname>RouterAllowList=</varname> is
-            configured then <varname>RouterDenyList=</varname> is ignored.</para>
+            <para>A whitespace-separated list of IPv6 router addresses. Each address can optionally
+            take a prefix length after <literal>/</literal>. Only information advertised by the listed
+            router is accepted. Note that if <varname>RouterAllowList=</varname> is configured then
+            <varname>RouterDenyList=</varname> is ignored.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>PrefixDenyList=</varname></term>
           <listitem>
-            <para>A whitespace-separated list of IPv6 prefixes. IPv6 prefixes supplied via router
-            advertisements in the list are ignored.</para>
+            <para>A whitespace-separated list of IPv6 prefixes. Each prefix can optionally take its
+            prefix length after <literal>/</literal>. IPv6 prefixes supplied via router advertisements
+            in the list are ignored.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>PrefixAllowList=</varname></term>
           <listitem>
-            <para>A whitespace-separated list of IPv6 prefixes. IPv6 prefixes supplied via router
-            advertisements in the list are allowed. Note that if <varname>PrefixAllowList=</varname> is
-            configured then <varname>PrefixDenyList=</varname> is ignored.</para>
+            <para>A whitespace-separated list of IPv6 prefixes. Each prefix can optionally take its
+            prefix length after <literal>/</literal>. IPv6 prefixes supplied via router advertisements
+            in the list are allowed. Note that if <varname>PrefixAllowList=</varname> is configured
+            then <varname>PrefixDenyList=</varname> is ignored.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>RouteDenyList=</varname></term>
           <listitem>
-            <para>A whitespace-separated list of IPv6 route prefixes. IPv6 route prefixes supplied via
-            router advertisements in the list are ignored.</para>
+            <para>A whitespace-separated list of IPv6 route prefixes. Each prefix can optionally take
+            its prefix length after <literal>/</literal>. IPv6 route prefixes supplied via router
+            advertisements in the list are ignored.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>RouteAllowList=</varname></term>
           <listitem>
-            <para>A whitespace-separated list of IPv6 route prefixes. IPv6 route prefixes supplied via
-            router advertisements in the list are allowed. Note that if <varname>RouteAllowList=</varname> is
+            <para>A whitespace-separated list of IPv6 route prefixes. Each prefix can optionally take
+            its prefix length after <literal>/</literal>. IPv6 route prefixes supplied via router
+            advertisements in the list are allowed. Note that if <varname>RouteAllowList=</varname> is
             configured then <varname>RouteDenyList=</varname> is ignored.</para>
           </listitem>
         </varlistentry>
@@ -2382,12 +2414,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>
@@ -2586,18 +2618,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 +2647,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 +2989,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 +3020,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 +3061,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 +3094,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 f5cbe688ee86c3ce9daff38d2329431fa299c934..a8c8fad9efcbbcdee1deb9903ee128a88e02b449 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 assignments 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 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 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 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 27dcf956f3e3c28fb8ba01c7a9a8999c69f4933b..6e1a8b1e50e7578bd6025651d5f71fda81bbd209 100644 (file)
@@ -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',
@@ -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],
@@ -1784,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
 
 ############################################################
@@ -3799,7 +3830,7 @@ summary({
 # LDFLAGS:  ${OUR_LDFLAGS} ${LDFLAGS}
 
 if conf.get('ENABLE_EFI') == 1
-        summary({'efi arch' : efi_arch},
+        summary({'EFI arch' : efi_arch},
                 section : 'Extensible Firmware Interface')
 
         if have_gnu_efi
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 b5b60ef985c36f0f5433d8c4b59f82694b169171..28b051f5ffe102d797cad0a826f2a21af4992d00 100644 (file)
@@ -31,3 +31,4 @@ pa
 kab
 si
 nl
+fi
index 8a4de65a336a52d59c73b3db2e1106d2088585c4..6de91018dd6a50571e8f1e83e284383561e98ef0 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -5,13 +5,14 @@
 # Benjamin Steinwender <b@stbe.at>, 2014.
 # Bernd Homuth <dev@hmt.im>, 2015.
 # Fabian Affolter <mail@fabian-affolter.ch>, 2020.
+# Ettore Atalan <atalanttore@googlemail.com>, 2021.
 msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2020-12-13 14:36+0000\n"
-"Last-Translator: Fabian Affolter <mail@fabian-affolter.ch>\n"
+"PO-Revision-Date: 2021-08-23 18:04+0000\n"
+"Last-Translator: Ettore Atalan <atalanttore@googlemail.com>\n"
 "Language-Team: German <https://translate.fedoraproject.org/projects/systemd/"
 "master/de/>\n"
 "Language: de\n"
@@ -19,7 +20,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.3.2\n"
+"X-Generator: Weblate 4.8\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -476,10 +477,8 @@ msgstr ""
 "anforderte es zu unterbinden."
 
 #: src/login/org.freedesktop.login1.policy:235
-#, fuzzy
-#| msgid "Hibernate the system"
 msgid "Halt the system"
-msgstr "Den Ruhezustand des Systems aktivieren"
+msgstr "Das System anhalten"
 
 #: src/login/org.freedesktop.login1.policy:236
 #, fuzzy
@@ -689,7 +688,7 @@ msgstr "Legitimierung ist zum Einstellen einer Nachricht an alle notwendig"
 
 #: src/login/org.freedesktop.login1.policy:406
 msgid "Change Session"
-msgstr ""
+msgstr "Sitzung ändern"
 
 #: src/login/org.freedesktop.login1.policy:407
 #, fuzzy
@@ -778,7 +777,7 @@ msgstr ""
 
 #: src/network/org.freedesktop.network1.policy:22
 msgid "Set NTP servers"
-msgstr ""
+msgstr "NTP-Server festlegen"
 
 #: src/network/org.freedesktop.network1.policy:23
 #, fuzzy
@@ -789,7 +788,7 @@ msgstr "Legitimierung ist zum Festlegen der Systemzeit notwendig."
 #: src/network/org.freedesktop.network1.policy:33
 #: src/resolve/org.freedesktop.resolve1.policy:44
 msgid "Set DNS servers"
-msgstr ""
+msgstr "DNS-Server festlegen"
 
 #: src/network/org.freedesktop.network1.policy:34
 #: src/resolve/org.freedesktop.resolve1.policy:45
@@ -813,7 +812,7 @@ msgstr "Legitimierung ist zum Stoppen von »$(unit)« notwendig."
 #: src/network/org.freedesktop.network1.policy:55
 #: src/resolve/org.freedesktop.resolve1.policy:66
 msgid "Set default route"
-msgstr ""
+msgstr "Standardroute festlegen"
 
 #: src/network/org.freedesktop.network1.policy:56
 #: src/resolve/org.freedesktop.resolve1.policy:67
@@ -825,7 +824,7 @@ msgstr "Legitimierung ist zum Festlegen des lokalen Rechnernamens notwendig"
 #: src/network/org.freedesktop.network1.policy:66
 #: src/resolve/org.freedesktop.resolve1.policy:77
 msgid "Enable/disable LLMNR"
-msgstr ""
+msgstr "LLMNR aktivieren/deaktivieren"
 
 #: src/network/org.freedesktop.network1.policy:67
 #: src/resolve/org.freedesktop.resolve1.policy:78
@@ -838,7 +837,7 @@ msgstr ""
 #: src/network/org.freedesktop.network1.policy:77
 #: src/resolve/org.freedesktop.resolve1.policy:88
 msgid "Enable/disable multicast DNS"
-msgstr ""
+msgstr "Multicast-DNS aktivieren/deaktivieren"
 
 #: src/network/org.freedesktop.network1.policy:78
 #: src/resolve/org.freedesktop.resolve1.policy:89
@@ -850,7 +849,7 @@ msgstr "Legitimierung ist zum Anmelden am lokalen Rechner notwendig."
 #: src/network/org.freedesktop.network1.policy:88
 #: src/resolve/org.freedesktop.resolve1.policy:99
 msgid "Enable/disable DNS over TLS"
-msgstr ""
+msgstr "DNS over TLS aktivieren/deaktivieren"
 
 #: src/network/org.freedesktop.network1.policy:89
 #: src/resolve/org.freedesktop.resolve1.policy:100
@@ -862,7 +861,7 @@ msgstr "Legitimierung ist zum Festlegen des lokalen Rechnernamens notwendig"
 #: src/network/org.freedesktop.network1.policy:99
 #: src/resolve/org.freedesktop.resolve1.policy:110
 msgid "Enable/disable DNSSEC"
-msgstr ""
+msgstr "DNSSEC aktivieren/deaktivieren"
 
 #: src/network/org.freedesktop.network1.policy:100
 #: src/resolve/org.freedesktop.resolve1.policy:111
@@ -875,7 +874,7 @@ msgstr ""
 #: src/network/org.freedesktop.network1.policy:110
 #: src/resolve/org.freedesktop.resolve1.policy:121
 msgid "Set DNSSEC Negative Trust Anchors"
-msgstr ""
+msgstr "Negative Vertrauensanker für DNSSEC festlegen"
 
 #: src/network/org.freedesktop.network1.policy:111
 #: src/resolve/org.freedesktop.resolve1.policy:122
@@ -888,7 +887,7 @@ msgstr ""
 
 #: src/network/org.freedesktop.network1.policy:121
 msgid "Revert NTP settings"
-msgstr ""
+msgstr "NTP-Einstellungen zurücksetzen"
 
 #: src/network/org.freedesktop.network1.policy:122
 #, fuzzy
@@ -898,7 +897,7 @@ msgstr "Legitimierung ist zum Festlegen der Systemzeit notwendig."
 
 #: src/network/org.freedesktop.network1.policy:132
 msgid "Revert DNS settings"
-msgstr ""
+msgstr "DNS-Einstellungen zurücksetzen"
 
 #: src/network/org.freedesktop.network1.policy:133
 #, fuzzy
@@ -918,7 +917,7 @@ msgstr "Legitimierung ist zum Einstellen einer Nachricht an alle notwendig"
 
 #: src/network/org.freedesktop.network1.policy:154
 msgid "Renew dynamic addresses"
-msgstr ""
+msgstr "Dynamische Adressen erneuern"
 
 #: src/network/org.freedesktop.network1.policy:155
 #, fuzzy
@@ -928,7 +927,7 @@ msgstr "Legitimierung ist zum Einstellen einer Nachricht an alle notwendig"
 
 #: src/network/org.freedesktop.network1.policy:165
 msgid "Reload network settings"
-msgstr ""
+msgstr "Netzwerkeinstellungen neu laden"
 
 #: src/network/org.freedesktop.network1.policy:166
 #, fuzzy
@@ -938,7 +937,7 @@ msgstr "Legitimierung ist zum erneuten Laden des systemd-Zustands notwendig."
 
 #: src/network/org.freedesktop.network1.policy:176
 msgid "Reconfigure network interface"
-msgstr ""
+msgstr "Netzwerkschnittstelle neu konfigurieren"
 
 #: src/network/org.freedesktop.network1.policy:177
 #, fuzzy
@@ -987,7 +986,7 @@ msgstr ""
 
 #: src/resolve/org.freedesktop.resolve1.policy:22
 msgid "Register a DNS-SD service"
-msgstr ""
+msgstr "Einen DNS-SD-Dienst registrieren"
 
 #: src/resolve/org.freedesktop.resolve1.policy:23
 #, fuzzy
@@ -997,7 +996,7 @@ msgstr "Legitimierung ist zum Einstellen einer Nachricht an alle notwendig"
 
 #: src/resolve/org.freedesktop.resolve1.policy:33
 msgid "Unregister a DNS-SD service"
-msgstr ""
+msgstr "Einen DNS-SD-Dienst deregistrieren"
 
 #: src/resolve/org.freedesktop.resolve1.policy:34
 #, fuzzy
@@ -1007,7 +1006,7 @@ msgstr "Legitimierung ist zum Einstellen einer Nachricht an alle notwendig"
 
 #: src/resolve/org.freedesktop.resolve1.policy:132
 msgid "Revert name resolution settings"
-msgstr ""
+msgstr "Namensauflösungseinstellungen zurücksetzen"
 
 #: src/resolve/org.freedesktop.resolve1.policy:133
 #, fuzzy
index 9406cea8d30fc9fcb818c4fa40f4a13dd29f6c17..c7fc862d1c06a2ad67fab63781f8ad0019e084fa 100644 (file)
--- a/po/es.po
+++ b/po/es.po
@@ -4,15 +4,15 @@
 # Alex Puchades <alex94puchades@gmail.com>, 2015.
 # Daniel Mustieles <daniel.mustieles@gmail.com>, 2015.
 # Álex Puchades <alex94puchades@gmail.com>, 2015.
-# Adolfo Jayme Barrientos <fitoschido@gmail.com>, 2020.
+# Adolfo Jayme Barrientos <fitoschido@gmail.com>, 2020, 2021.
 # Emilio Herrera <ehespinosa57@gmail.com>, 2021.
 msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2021-06-08 09:04+0000\n"
-"Last-Translator: Emilio Herrera <ehespinosa57@gmail.com>\n"
+"PO-Revision-Date: 2021-08-26 18:05+0000\n"
+"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
 "Language-Team: Spanish <https://translate.fedoraproject.org/projects/systemd/"
 "master/es/>\n"
 "Language: es\n"
@@ -20,7 +20,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.6.2\n"
+"X-Generator: Weblate 4.8\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -96,40 +96,37 @@ msgstr "Comprobar las credenciales de un área home"
 msgid ""
 "Authentication is required to check credentials against a user's home area."
 msgstr ""
-"Se requiere autenticación para comprobar las credenciales contra un área "
-"home de usuario."
+"Necesita autenticarse para comprobar las credenciales del espacio personal "
+"de un usuario."
 
 #: src/home/org.freedesktop.home1.policy:43
 msgid "Update a home area"
-msgstr "Actualizar un área home"
+msgstr "Actualizar un espacio personal"
 
 #: src/home/org.freedesktop.home1.policy:44
 msgid "Authentication is required to update a user's home area."
-msgstr "Se requiere autenticación para actualizar un área home de usuario."
+msgstr ""
+"Necesita autenticarse para actualizar el espacio personal de un usuario."
 
 #: src/home/org.freedesktop.home1.policy:53
 msgid "Resize a home area"
-msgstr ""
+msgstr "Redimensionar un espacio personal"
 
 #: src/home/org.freedesktop.home1.policy:54
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
 msgid "Authentication is required to resize a user's home area."
-msgstr "Se requiere autenticación para establecer un muro de texto"
+msgstr ""
+"Necesita autenticarse para redimensionar el espacio personal de un usuario."
 
 #: src/home/org.freedesktop.home1.policy:63
 msgid "Change password of a home area"
-msgstr ""
+msgstr "Cambiar contraseña de un espacio personal"
 
 #: 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 ""
-"Se requiere autenticación para administrar las sesiones activas, usuarios y "
-"puestos de trabajo."
+"Necesita autenticarse para cambiar la contraseña del espacio personal de un "
+"usuario."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set hostname"
@@ -161,13 +158,11 @@ msgstr "Necesita autenticarse para establecer la información de sistema local."
 
 #: src/hostname/org.freedesktop.hostname1.policy:51
 msgid "Get product UUID"
-msgstr ""
+msgstr "Obtener UUID del producto"
 
 #: src/hostname/org.freedesktop.hostname1.policy:52
-#, fuzzy
-#| msgid "Authentication is required to reload '$(unit)'."
 msgid "Authentication is required to get product UUID."
-msgstr "Se requiere autenticación para recargar '$(unit)'."
+msgstr "Necesita autenticarse para obtener el UUID de un producto."
 
 #: src/import/org.freedesktop.import1.policy:22
 msgid "Import a VM or container image"
@@ -582,13 +577,12 @@ msgstr "Necesita autenticarse para bloquear/desbloquear sesiones activas."
 
 #: src/login/org.freedesktop.login1.policy:352
 msgid "Set the reboot \"reason\" in the kernel"
-msgstr ""
+msgstr "Establecer la «razón» de reinicio en el núcleo"
 
 #: src/login/org.freedesktop.login1.policy:353
-#, fuzzy
-#| msgid "Authentication is required to set the system timezone."
 msgid "Authentication is required to set the reboot \"reason\" in the kernel."
-msgstr "Se requiere autenticación para establecer la zona horaria del sistema."
+msgstr ""
+"Necesita autenticarse para establecer la «razón» de reinicio en el núcleo."
 
 #: src/login/org.freedesktop.login1.policy:363
 #, fuzzy
@@ -607,35 +601,27 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:374
 msgid "Indicate to the boot loader to boot to the boot loader menu"
-msgstr ""
+msgstr "Indicar al cargador de arranque que inicie el menú de selección"
 
 #: src/login/org.freedesktop.login1.policy:375
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to indicate to the firmware to boot to setup "
-#| "interface."
 msgid ""
 "Authentication is required to indicate to the boot loader to boot to the "
 "boot loader menu."
 msgstr ""
-"Se requiere autenticación para indicar al firmware que arranque la interfaz "
-"de configuración."
+"Necesita autenticarse para indicar al cargador de arranque que inicie el "
+"menú de selección."
 
 #: src/login/org.freedesktop.login1.policy:385
 msgid "Indicate to the boot loader to boot a specific entry"
-msgstr ""
+msgstr "Indicar al cargador de arranque que inicie una entrada concreta"
 
 #: src/login/org.freedesktop.login1.policy:386
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to indicate to the firmware to boot to setup "
-#| "interface."
 msgid ""
 "Authentication is required to indicate to the boot loader to boot into a "
 "specific boot loader entry."
 msgstr ""
-"Se requiere autenticación para indicar al firmware que arranque la interfaz "
-"de configuración."
+"Necesita autenticarse para indicar al cargador de arranque que inicie una "
+"entrada concreta."
 
 #: src/login/org.freedesktop.login1.policy:396
 msgid "Set a wall message"
@@ -647,13 +633,11 @@ msgstr "Necesita autenticarse para establecer un muro de texto"
 
 #: src/login/org.freedesktop.login1.policy:406
 msgid "Change Session"
-msgstr ""
+msgstr "Cambiar sesión"
 
 #: src/login/org.freedesktop.login1.policy:407
-#, fuzzy
-#| msgid "Authentication is required to set the local hostname."
 msgid "Authentication is required to change the virtual terminal."
-msgstr "Se requiere autenticación para establecer el nombre del equipo local."
+msgstr "Necesita autenticarse para cambiar la terminal virtual."
 
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
@@ -665,11 +649,11 @@ msgstr "Necesita autenticarse para conectarse a un contenedor local."
 
 #: src/machine/org.freedesktop.machine1.policy:32
 msgid "Log into the local host"
-msgstr "Conectarse al equipo local"
+msgstr "Acceder al anfitrión local"
 
 #: src/machine/org.freedesktop.machine1.policy:33
 msgid "Authentication is required to log into the local host."
-msgstr "Necesita autenticarse para conectarse al equipo local."
+msgstr "Necesita autenticarse para acceder al anfitrión local."
 
 #: src/machine/org.freedesktop.machine1.policy:42
 msgid "Acquire a shell in a local container"
@@ -734,173 +718,145 @@ msgstr ""
 
 #: src/network/org.freedesktop.network1.policy:22
 msgid "Set NTP servers"
-msgstr ""
+msgstr "Establecer servidores NTP"
 
 #: src/network/org.freedesktop.network1.policy:23
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
 msgid "Authentication is required to set NTP servers."
-msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+msgstr "Necesita autenticarse para establecer servidores NTP."
 
 #: src/network/org.freedesktop.network1.policy:33
 #: src/resolve/org.freedesktop.resolve1.policy:44
 msgid "Set DNS servers"
-msgstr ""
+msgstr "Establecer servidores DNS"
 
 #: src/network/org.freedesktop.network1.policy:34
 #: src/resolve/org.freedesktop.resolve1.policy:45
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
 msgid "Authentication is required to set DNS servers."
-msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+msgstr "Necesita autenticarse para establecer servidores DNS."
 
 #: src/network/org.freedesktop.network1.policy:44
 #: src/resolve/org.freedesktop.resolve1.policy:55
 msgid "Set domains"
-msgstr ""
+msgstr "Establecer dominios"
 
 #: src/network/org.freedesktop.network1.policy:45
 #: src/resolve/org.freedesktop.resolve1.policy:56
-#, fuzzy
-#| msgid "Authentication is required to stop '$(unit)'."
 msgid "Authentication is required to set domains."
-msgstr "Se requiere autenticación para detener '$(unit)'."
+msgstr "Necesita autenticarse para establecer dominios."
 
 #: src/network/org.freedesktop.network1.policy:55
 #: src/resolve/org.freedesktop.resolve1.policy:66
 msgid "Set default route"
-msgstr ""
+msgstr "Establecer ruta predeterminada"
 
 #: src/network/org.freedesktop.network1.policy:56
 #: src/resolve/org.freedesktop.resolve1.policy:67
-#, fuzzy
-#| msgid "Authentication is required to set the local hostname."
 msgid "Authentication is required to set default route."
-msgstr "Se requiere autenticación para establecer el nombre del equipo local."
+msgstr "Necesita autenticarse para establecer la ruta predeterminada."
 
 #: src/network/org.freedesktop.network1.policy:66
 #: src/resolve/org.freedesktop.resolve1.policy:77
 msgid "Enable/disable LLMNR"
-msgstr ""
+msgstr "Activar/desactivar LLMNR"
 
 #: src/network/org.freedesktop.network1.policy:67
 #: src/resolve/org.freedesktop.resolve1.policy:78
-#, fuzzy
-#| msgid "Authentication is required to hibernate the system."
 msgid "Authentication is required to enable or disable LLMNR."
-msgstr "Se requiere autenticación para hibernar el sistema."
+msgstr "Necesita autenticarse para activar o desactivar LLMNR."
 
 #: src/network/org.freedesktop.network1.policy:77
 #: src/resolve/org.freedesktop.resolve1.policy:88
 msgid "Enable/disable multicast DNS"
-msgstr ""
+msgstr "Activar/desactivar DNS multidifusión"
 
 #: src/network/org.freedesktop.network1.policy:78
 #: src/resolve/org.freedesktop.resolve1.policy:89
-#, fuzzy
-#| msgid "Authentication is required to log into the local host."
 msgid "Authentication is required to enable or disable multicast DNS."
-msgstr "Se requiere autenticación para conectarse al equipo local."
+msgstr "Necesita autenticarse para activar o desactivar DNS multidifusión."
 
 #: src/network/org.freedesktop.network1.policy:88
 #: src/resolve/org.freedesktop.resolve1.policy:99
 msgid "Enable/disable DNS over TLS"
-msgstr ""
+msgstr "Activar/desactivar DNS por TLS"
 
 #: src/network/org.freedesktop.network1.policy:89
 #: src/resolve/org.freedesktop.resolve1.policy:100
-#, fuzzy
-#| msgid "Authentication is required to set the local hostname."
 msgid "Authentication is required to enable or disable DNS over TLS."
-msgstr "Se requiere autenticación para establecer el nombre del equipo local."
+msgstr "Necesita autenticarse para activar o desactivar DNS por TLS."
 
 #: src/network/org.freedesktop.network1.policy:99
 #: src/resolve/org.freedesktop.resolve1.policy:110
 msgid "Enable/disable DNSSEC"
-msgstr ""
+msgstr "Activar/desactivar DNSSEC"
 
 #: src/network/org.freedesktop.network1.policy:100
 #: src/resolve/org.freedesktop.resolve1.policy:111
-#, fuzzy
-#| msgid "Authentication is required to hibernate the system."
 msgid "Authentication is required to enable or disable DNSSEC."
-msgstr "Se requiere autenticación para hibernar el sistema."
+msgstr "Necesita autenticarse para activar o desactivar DNSSEC."
 
 #: src/network/org.freedesktop.network1.policy:110
 #: src/resolve/org.freedesktop.resolve1.policy:121
 msgid "Set DNSSEC Negative Trust Anchors"
-msgstr ""
+msgstr "Establecer anclas de confianza negativas de DNSSEC"
 
 #: src/network/org.freedesktop.network1.policy:111
 #: src/resolve/org.freedesktop.resolve1.policy:122
-#, fuzzy
-#| msgid "Authentication is required to set the system locale."
 msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
-msgstr "Se requiere autenticación para establecer la región del sistema."
+msgstr ""
+"Necesita autenticarse para establecer las anclas de confianza negativas de "
+"DNSSEC."
 
 #: src/network/org.freedesktop.network1.policy:121
 msgid "Revert NTP settings"
-msgstr ""
+msgstr "Revertir configuración de NTP"
 
 #: src/network/org.freedesktop.network1.policy:122
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
 msgid "Authentication is required to reset NTP settings."
-msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+msgstr "Necesita autenticarse para restablecer la configuración de NTP."
 
 #: src/network/org.freedesktop.network1.policy:132
 msgid "Revert DNS settings"
-msgstr ""
+msgstr "Revertir configuración de DNS"
 
 #: src/network/org.freedesktop.network1.policy:133
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
 msgid "Authentication is required to reset DNS settings."
-msgstr "Se requiere autenticación para establecer la fecha y hora del sistema."
+msgstr "Necesita autenticarse para restablecer la configuración de DNS."
 
 #: src/network/org.freedesktop.network1.policy:143
 msgid "DHCP server sends force renew message"
-msgstr ""
+msgstr "El servidor DCHP envía un mensaje de renovación forzada"
 
 #: 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 "Se requiere autenticación para establecer un muro de texto"
+msgstr "Necesita autenticarse para enviar el mensaje de renovación forzada."
 
 #: src/network/org.freedesktop.network1.policy:154
 msgid "Renew dynamic addresses"
-msgstr ""
+msgstr "Renovar direcciones dinámicas"
 
 #: src/network/org.freedesktop.network1.policy:155
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
 msgid "Authentication is required to renew dynamic addresses."
-msgstr "Se requiere autenticación para establecer un muro de texto"
+msgstr "Necesita autenticarse para renovar las direcciones dinámicas."
 
 #: src/network/org.freedesktop.network1.policy:165
 msgid "Reload network settings"
-msgstr ""
+msgstr "Recargar configuración de red"
 
 #: src/network/org.freedesktop.network1.policy:166
-#, fuzzy
-#| msgid "Authentication is required to reload the systemd state."
 msgid "Authentication is required to reload network settings."
-msgstr "Se requiere autenticación para recargar el estado de systemd."
+msgstr "Necesita autenticarse para recargar la configuración de red."
 
 #: src/network/org.freedesktop.network1.policy:176
 msgid "Reconfigure network interface"
-msgstr ""
+msgstr "Reconfigurar interfaz de red"
 
 #: 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 "Se requiere autenticación para reiniciar el sistema."
+msgstr "Necesita autenticarse para reconfigurar la interfaz de red."
 
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
-msgstr ""
+msgstr "Inspeccionar una imagen de servicio portátil"
 
 #: src/portable/org.freedesktop.portable1.policy:14
 #, fuzzy
@@ -912,7 +868,7 @@ msgstr ""
 
 #: src/portable/org.freedesktop.portable1.policy:23
 msgid "Attach or detach a portable service image"
-msgstr ""
+msgstr "Adjuntar o desadjuntar una imagen de servicio portátil"
 
 #: src/portable/org.freedesktop.portable1.policy:24
 #, fuzzy
@@ -925,7 +881,7 @@ msgstr ""
 
 #: src/portable/org.freedesktop.portable1.policy:34
 msgid "Delete or modify portable service image"
-msgstr ""
+msgstr "Eliminar o modificar imagen de servicio portátil"
 
 #: src/portable/org.freedesktop.portable1.policy:35
 #, fuzzy
@@ -938,7 +894,7 @@ msgstr ""
 
 #: src/resolve/org.freedesktop.resolve1.policy:22
 msgid "Register a DNS-SD service"
-msgstr ""
+msgstr "Registrar un servicio DNS-SD"
 
 #: src/resolve/org.freedesktop.resolve1.policy:23
 #, fuzzy
@@ -948,7 +904,7 @@ msgstr "Se requiere autenticación para establecer un muro de texto"
 
 #: src/resolve/org.freedesktop.resolve1.policy:33
 msgid "Unregister a DNS-SD service"
-msgstr ""
+msgstr "Desregistrar un servicio DNS-SD"
 
 #: src/resolve/org.freedesktop.resolve1.policy:34
 #, fuzzy
@@ -958,7 +914,7 @@ msgstr "Se requiere autenticación para establecer un muro de texto"
 
 #: src/resolve/org.freedesktop.resolve1.policy:132
 msgid "Revert name resolution settings"
-msgstr ""
+msgstr "Revertir configuración de resolución de nombres"
 
 #: src/resolve/org.freedesktop.resolve1.policy:133
 #, fuzzy
diff --git a/po/fi.po b/po/fi.po
new file mode 100644 (file)
index 0000000..baf77cb
--- /dev/null
+++ b/po/fi.po
@@ -0,0 +1,859 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the systemd package.
+# Jan Kuparinen <copper_fin@hotmail.com>, 2021.
+msgid ""
+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-08-26 18:05+0000\n"
+"Last-Translator: Jan Kuparinen <copper_fin@hotmail.com>\n"
+"Language-Team: Finnish <https://translate.fedoraproject.org/projects/systemd/"
+"master/fi/>\n"
+"Language: fi\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.8\n"
+
+#: src/core/org.freedesktop.systemd1.policy.in:22
+msgid "Send passphrase back to system"
+msgstr "Lähetä tunnuslause takaisin järjestelmään"
+
+#: src/core/org.freedesktop.systemd1.policy.in:23
+msgid ""
+"Authentication is required to send the entered passphrase back to the system."
+msgstr ""
+"Todennus vaaditaan jotta syötetty tunnuslause lähetetään takaisin "
+"järjestelmään."
+
+#: src/core/org.freedesktop.systemd1.policy.in:33
+msgid "Manage system services or other units"
+msgstr "Hallinnoi järjestelmäpalveluja tai muita yksiköitä"
+
+#: src/core/org.freedesktop.systemd1.policy.in:34
+msgid "Authentication is required to manage system services or other units."
+msgstr ""
+"Todennus vaaditaan järjestelmäpalvelujen tai muiden yksiköiden hallintaan."
+
+#: src/core/org.freedesktop.systemd1.policy.in:43
+msgid "Manage system service or unit files"
+msgstr "Hallitse järjestelmäpalvelu- tai yksikkötiedostoja"
+
+#: src/core/org.freedesktop.systemd1.policy.in:44
+msgid "Authentication is required to manage system service or unit files."
+msgstr ""
+"Todennus vaaditaan järjestelmän palvelu- tai yksikkötiedostojen hallintaan."
+
+#: src/core/org.freedesktop.systemd1.policy.in:54
+msgid "Set or unset system and service manager environment variables"
+msgstr "Aseta tai poista järjestelmän ja palvelunhallinnan ympäristömuuttujia"
+
+#: src/core/org.freedesktop.systemd1.policy.in:55
+msgid ""
+"Authentication is required to set or unset system and service manager "
+"environment variables."
+msgstr ""
+"Todennus vaaditaan järjestelmän ja palvelunhallinnan ympäristömuuttujien "
+"asettamiseen tai poistamiseen."
+
+#: src/core/org.freedesktop.systemd1.policy.in:64
+msgid "Reload the systemd state"
+msgstr "Lataa järjestelmätila uudelleen"
+
+#: src/core/org.freedesktop.systemd1.policy.in:65
+msgid "Authentication is required to reload the systemd state."
+msgstr "Todennus vaaditaan, jotta järjestelmätila voidaan ladata uudelleen."
+
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Luo kotialue"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "Todennus vaaditaan käyttäjän kotialueen luomiseksi."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Poista kotialue"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "Todennus vaaditaan käyttäjän kotialueen poistamiseksi."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Tarkista kotialueen valtuudet"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"Todennus vaaditaan, jotta käyttäjän kotialueen valtuuksia voi tarkistaa."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Päivitä kotialue"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr ""
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr ""
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr ""
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:20
+msgid "Set hostname"
+msgstr "Määritä isäntänimi"
+
+#: src/hostname/org.freedesktop.hostname1.policy:21
+msgid "Authentication is required to set the local hostname."
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:30
+msgid "Set static hostname"
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:31
+msgid ""
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:41
+msgid "Set machine information"
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:42
+msgid "Authentication is required to set local machine information."
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:51
+msgid "Get product UUID"
+msgstr ""
+
+#: src/hostname/org.freedesktop.hostname1.policy:52
+msgid "Authentication is required to get product UUID."
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:22
+msgid "Import a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:23
+msgid "Authentication is required to import a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:32
+msgid "Export a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:33
+msgid "Authentication is required to export a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:42
+msgid "Download a VM or container image"
+msgstr ""
+
+#: src/import/org.freedesktop.import1.policy:43
+msgid "Authentication is required to download a VM or container image"
+msgstr ""
+
+#: src/locale/org.freedesktop.locale1.policy:22
+msgid "Set system locale"
+msgstr ""
+
+#: src/locale/org.freedesktop.locale1.policy:23
+msgid "Authentication is required to set the system locale."
+msgstr ""
+
+#: src/locale/org.freedesktop.locale1.policy:33
+msgid "Set system keyboard settings"
+msgstr ""
+
+#: src/locale/org.freedesktop.locale1.policy:34
+msgid "Authentication is required to set the system keyboard settings."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:22
+msgid "Allow applications to inhibit system shutdown"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:23
+msgid ""
+"Authentication is required for an application to inhibit system shutdown."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:33
+msgid "Allow applications to delay system shutdown"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:34
+msgid "Authentication is required for an application to delay system shutdown."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:44
+msgid "Allow applications to inhibit system sleep"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:45
+msgid "Authentication is required for an application to inhibit system sleep."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:55
+msgid "Allow applications to delay system sleep"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:56
+msgid "Authentication is required for an application to delay system sleep."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:65
+msgid "Allow applications to inhibit automatic system suspend"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:66
+msgid ""
+"Authentication is required for an application to inhibit automatic system "
+"suspend."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:75
+msgid "Allow applications to inhibit system handling of the power key"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:76
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the power key."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:86
+msgid "Allow applications to inhibit system handling of the suspend key"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:87
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the suspend key."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:97
+msgid "Allow applications to inhibit system handling of the hibernate key"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:98
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the hibernate key."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:107
+msgid "Allow applications to inhibit system handling of the lid switch"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:108
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the lid switch."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:117
+msgid "Allow applications to inhibit system handling of the reboot key"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:118
+msgid ""
+"Authentication is required for an application to inhibit system handling of "
+"the reboot key."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:128
+msgid "Allow non-logged-in user to run programs"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:129
+msgid "Explicit request is required to run programs as a non-logged-in user."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:138
+msgid "Allow non-logged-in users to run programs"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:139
+msgid "Authentication is required to run programs as a non-logged-in user."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:148
+msgid "Allow attaching devices to seats"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:149
+msgid "Authentication is required to attach a device to a seat."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:159
+msgid "Flush device to seat attachments"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:160
+msgid "Authentication is required to reset how devices are attached to seats."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:169
+msgid "Power off the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:170
+msgid "Authentication is required to power off the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:180
+msgid "Power off the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:181
+msgid ""
+"Authentication is required to power off the system while other users are "
+"logged in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:191
+msgid "Power off the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:192
+msgid ""
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:202
+msgid "Reboot the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:203
+msgid "Authentication is required to reboot the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:213
+msgid "Reboot the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:214
+msgid ""
+"Authentication is required to reboot the system while other users are logged "
+"in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:224
+msgid "Reboot the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:225
+msgid ""
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:235
+msgid "Halt the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:236
+msgid "Authentication is required to halt the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:246
+msgid "Halt the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:247
+msgid ""
+"Authentication is required to halt the system while other users are logged "
+"in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:257
+msgid "Halt the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:258
+msgid ""
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:268
+msgid "Suspend the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:269
+msgid "Authentication is required to suspend the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:278
+msgid "Suspend the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:279
+msgid ""
+"Authentication is required to suspend the system while other users are "
+"logged in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:289
+msgid "Suspend the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:290
+msgid ""
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:300
+msgid "Hibernate the system"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:301
+msgid "Authentication is required to hibernate the system."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:310
+msgid "Hibernate the system while other users are logged in"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:311
+msgid ""
+"Authentication is required to hibernate the system while other users are "
+"logged in."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:321
+msgid "Hibernate the system while an application is inhibiting this"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:322
+msgid ""
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:332
+msgid "Manage active sessions, users and seats"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:333
+msgid "Authentication is required to manage active sessions, users and seats."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:342
+msgid "Lock or unlock active sessions"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:343
+msgid "Authentication is required to lock or unlock active sessions."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:352
+msgid "Set the reboot \"reason\" in the kernel"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:353
+msgid "Authentication is required to set the reboot \"reason\" in the kernel."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:363
+msgid "Indicate to the firmware to boot to setup interface"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:364
+msgid ""
+"Authentication is required to indicate to the firmware to boot to setup "
+"interface."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:374
+msgid "Indicate to the boot loader to boot to the boot loader menu"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:375
+msgid ""
+"Authentication is required to indicate to the boot loader to boot to the "
+"boot loader menu."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:385
+msgid "Indicate to the boot loader to boot a specific entry"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:386
+msgid ""
+"Authentication is required to indicate to the boot loader to boot into a "
+"specific boot loader entry."
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Set a wall message"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:397
+msgid "Authentication is required to set a wall message"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:406
+msgid "Change Session"
+msgstr ""
+
+#: src/login/org.freedesktop.login1.policy:407
+msgid "Authentication is required to change the virtual terminal."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:22
+msgid "Log into a local container"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:23
+msgid "Authentication is required to log into a local container."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:32
+msgid "Log into the local host"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:33
+msgid "Authentication is required to log into the local host."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:42
+msgid "Acquire a shell in a local container"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:43
+msgid "Authentication is required to acquire a shell in a local container."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:53
+msgid "Acquire a shell on the local host"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:54
+msgid "Authentication is required to acquire a shell on the local host."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:64
+msgid "Acquire a pseudo TTY in a local container"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:65
+msgid ""
+"Authentication is required to acquire a pseudo TTY in a local container."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:74
+msgid "Acquire a pseudo TTY on the local host"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:75
+msgid "Authentication is required to acquire a pseudo TTY on the local host."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:84
+msgid "Manage local virtual machines and containers"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:85
+msgid ""
+"Authentication is required to manage local virtual machines and containers."
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:95
+msgid "Manage local virtual machine and container images"
+msgstr ""
+
+#: src/machine/org.freedesktop.machine1.policy:96
+msgid ""
+"Authentication is required to manage local virtual machine and container "
+"images."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:22
+msgid "Set NTP servers"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:23
+msgid "Authentication is required to set NTP servers."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:33
+#: src/resolve/org.freedesktop.resolve1.policy:44
+msgid "Set DNS servers"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:34
+#: src/resolve/org.freedesktop.resolve1.policy:45
+msgid "Authentication is required to set DNS servers."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:44
+#: src/resolve/org.freedesktop.resolve1.policy:55
+msgid "Set domains"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:45
+#: src/resolve/org.freedesktop.resolve1.policy:56
+msgid "Authentication is required to set domains."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:55
+#: src/resolve/org.freedesktop.resolve1.policy:66
+msgid "Set default route"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:56
+#: src/resolve/org.freedesktop.resolve1.policy:67
+msgid "Authentication is required to set default route."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:66
+#: src/resolve/org.freedesktop.resolve1.policy:77
+msgid "Enable/disable LLMNR"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:67
+#: src/resolve/org.freedesktop.resolve1.policy:78
+msgid "Authentication is required to enable or disable LLMNR."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:77
+#: src/resolve/org.freedesktop.resolve1.policy:88
+msgid "Enable/disable multicast DNS"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:78
+#: src/resolve/org.freedesktop.resolve1.policy:89
+msgid "Authentication is required to enable or disable multicast DNS."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:88
+#: src/resolve/org.freedesktop.resolve1.policy:99
+msgid "Enable/disable DNS over TLS"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:89
+#: src/resolve/org.freedesktop.resolve1.policy:100
+msgid "Authentication is required to enable or disable DNS over TLS."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:99
+#: src/resolve/org.freedesktop.resolve1.policy:110
+msgid "Enable/disable DNSSEC"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:100
+#: src/resolve/org.freedesktop.resolve1.policy:111
+msgid "Authentication is required to enable or disable DNSSEC."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:110
+#: src/resolve/org.freedesktop.resolve1.policy:121
+msgid "Set DNSSEC Negative Trust Anchors"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:111
+#: src/resolve/org.freedesktop.resolve1.policy:122
+msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:121
+msgid "Revert NTP settings"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:122
+msgid "Authentication is required to reset NTP settings."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:132
+msgid "Revert DNS settings"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:133
+msgid "Authentication is required to reset DNS settings."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "DHCP server sends force renew message"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to send force renew message."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Renew dynamic addresses"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to renew dynamic addresses."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reload network settings"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reload network settings."
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:176
+msgid "Reconfigure network interface"
+msgstr ""
+
+#: src/network/org.freedesktop.network1.policy:177
+msgid "Authentication is required to reconfigure network interface."
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:13
+msgid "Inspect a portable service image"
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:14
+msgid "Authentication is required to inspect a portable service image."
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:23
+msgid "Attach or detach a portable service image"
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:24
+msgid ""
+"Authentication is required to attach or detach a portable service image."
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:34
+msgid "Delete or modify portable service image"
+msgstr ""
+
+#: src/portable/org.freedesktop.portable1.policy:35
+msgid ""
+"Authentication is required to delete or modify a portable service image."
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:22
+msgid "Register a DNS-SD service"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:23
+msgid "Authentication is required to register a DNS-SD service"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:33
+msgid "Unregister a DNS-SD service"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:34
+msgid "Authentication is required to unregister a DNS-SD service"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:132
+msgid "Revert name resolution settings"
+msgstr ""
+
+#: src/resolve/org.freedesktop.resolve1.policy:133
+msgid "Authentication is required to reset name resolution settings."
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:22
+msgid "Set system time"
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:23
+msgid "Authentication is required to set the system time."
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:33
+msgid "Set system timezone"
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:34
+msgid "Authentication is required to set the system timezone."
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:43
+msgid "Set RTC to local timezone or UTC"
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:44
+msgid ""
+"Authentication is required to control whether the RTC stores the local or "
+"UTC time."
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:53
+msgid "Turn network time synchronization on or off"
+msgstr ""
+
+#: src/timedate/org.freedesktop.timedate1.policy:54
+msgid ""
+"Authentication is required to control whether network time synchronization "
+"shall be enabled."
+msgstr ""
+
+#: src/core/dbus-unit.c:359
+msgid "Authentication is required to start '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:360
+msgid "Authentication is required to stop '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:361
+msgid "Authentication is required to reload '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:362 src/core/dbus-unit.c:363
+msgid "Authentication is required to restart '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:535
+msgid ""
+"Authentication is required to send a UNIX signal to the processes of "
+"'$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:566
+msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:599
+msgid "Authentication is required to set properties on '$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:708
+msgid ""
+"Authentication is required to delete files and directories associated with "
+"'$(unit)'."
+msgstr ""
+
+#: src/core/dbus-unit.c:757
+msgid ""
+"Authentication is required to freeze or thaw the processes of '$(unit)' unit."
+msgstr ""
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 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 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
diff --git a/src/basic/linux/genetlink.h b/src/basic/linux/genetlink.h
new file mode 100644 (file)
index 0000000..d83f214
--- /dev/null
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI__LINUX_GENERIC_NETLINK_H
+#define _UAPI__LINUX_GENERIC_NETLINK_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+#define GENL_NAMSIZ    16      /* length of family name */
+
+#define GENL_MIN_ID    NLMSG_MIN_TYPE
+#define GENL_MAX_ID    1023
+
+struct genlmsghdr {
+       __u8    cmd;
+       __u8    version;
+       __u16   reserved;
+};
+
+#define GENL_HDRLEN    NLMSG_ALIGN(sizeof(struct genlmsghdr))
+
+#define GENL_ADMIN_PERM                0x01
+#define GENL_CMD_CAP_DO                0x02
+#define GENL_CMD_CAP_DUMP      0x04
+#define GENL_CMD_CAP_HASPOL    0x08
+#define GENL_UNS_ADMIN_PERM    0x10
+
+/*
+ * List of reserved static generic netlink identifiers:
+ */
+#define GENL_ID_CTRL           NLMSG_MIN_TYPE
+#define GENL_ID_VFS_DQUOT      (NLMSG_MIN_TYPE + 1)
+#define GENL_ID_PMCRAID                (NLMSG_MIN_TYPE + 2)
+/* must be last reserved + 1 */
+#define GENL_START_ALLOC       (NLMSG_MIN_TYPE + 3)
+
+/**************************************************************************
+ * Controller
+ **************************************************************************/
+
+enum {
+       CTRL_CMD_UNSPEC,
+       CTRL_CMD_NEWFAMILY,
+       CTRL_CMD_DELFAMILY,
+       CTRL_CMD_GETFAMILY,
+       CTRL_CMD_NEWOPS,
+       CTRL_CMD_DELOPS,
+       CTRL_CMD_GETOPS,
+       CTRL_CMD_NEWMCAST_GRP,
+       CTRL_CMD_DELMCAST_GRP,
+       CTRL_CMD_GETMCAST_GRP, /* unused */
+       CTRL_CMD_GETPOLICY,
+       __CTRL_CMD_MAX,
+};
+
+#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1)
+
+enum {
+       CTRL_ATTR_UNSPEC,
+       CTRL_ATTR_FAMILY_ID,
+       CTRL_ATTR_FAMILY_NAME,
+       CTRL_ATTR_VERSION,
+       CTRL_ATTR_HDRSIZE,
+       CTRL_ATTR_MAXATTR,
+       CTRL_ATTR_OPS,
+       CTRL_ATTR_MCAST_GROUPS,
+       CTRL_ATTR_POLICY,
+       CTRL_ATTR_OP_POLICY,
+       CTRL_ATTR_OP,
+       __CTRL_ATTR_MAX,
+};
+
+#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1)
+
+enum {
+       CTRL_ATTR_OP_UNSPEC,
+       CTRL_ATTR_OP_ID,
+       CTRL_ATTR_OP_FLAGS,
+       __CTRL_ATTR_OP_MAX,
+};
+
+#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+
+enum {
+       CTRL_ATTR_MCAST_GRP_UNSPEC,
+       CTRL_ATTR_MCAST_GRP_NAME,
+       CTRL_ATTR_MCAST_GRP_ID,
+       __CTRL_ATTR_MCAST_GRP_MAX,
+};
+
+enum {
+       CTRL_ATTR_POLICY_UNSPEC,
+       CTRL_ATTR_POLICY_DO,
+       CTRL_ATTR_POLICY_DUMP,
+
+       __CTRL_ATTR_POLICY_DUMP_MAX,
+       CTRL_ATTR_POLICY_DUMP_MAX = __CTRL_ATTR_POLICY_DUMP_MAX - 1
+};
+
+#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
+
+
+#endif /* _UAPI__LINUX_GENERIC_NETLINK_H */
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 666537868bdd193a3099b8bbcb6ade8bf28634a3..6d755ab6465f75b6fc0e3557672c3748433e359d 100644 (file)
@@ -83,6 +83,7 @@ basic_sources = files('''
         linux/can/vxcan.h
         linux/fib_rules.h
         linux/fou.h
+        linux/genetlink.h
         linux/hdlc/ioctl.h
         linux/if.h
         linux/if_addr.h
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..a21981616b5966abe5f441deb21bf0652eb8bf76 100644 (file)
@@ -630,7 +630,11 @@ static int check_x_access(const char *path, int *ret_fd) {
                 return r;
 
         r = access_fd(fd, X_OK);
-        if (r < 0)
+        if (r == -ENOSYS) {
+                /* /proc is not mounted. Fallback to access(). */
+                if (access(path, X_OK) < 0)
+                        return -errno;
+        } else if (r < 0)
                 return r;
 
         if (ret_fd)
@@ -639,31 +643,54 @@ 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 last_error, r;
-        const char *p = NULL;
+static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) {
+        _cleanup_close_ int fd = -1;
+        _cleanup_free_ char *path_name = NULL;
+        int r;
 
         assert(name);
 
-        if (is_path(name)) {
-                _cleanup_close_ int fd = -1;
-
-                r = check_x_access(name, ret_fd ? &fd : 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;
 
-                if (ret_filename) {
-                        r = path_make_absolute_cwd(name, ret_filename);
-                        if (r < 0)
-                                return r;
-                }
+                name = path_name;
+        }
 
-                if (ret_fd)
-                        *ret_fd = TAKE_FD(fd);
+        r = check_x_access(name, ret_fd ? &fd : NULL);
+        if (r < 0)
+                return r;
 
-                return 0;
+        if (ret_filename) {
+                r = path_make_absolute_cwd(name, ret_filename);
+                if (r < 0)
+                        return r;
         }
 
+        if (ret_fd)
+                *ret_fd = TAKE_FD(fd);
+
+        return 0;
+}
+
+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;
+
+        assert(name);
+
+        if (is_path(name))
+                return find_executable_impl(name, root, ret_filename, ret_fd);
+
         if (use_path_envvar)
                 /* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
                  * binary. */
@@ -676,7 +703,6 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
         /* Resolve a single-component name to a full path */
         for (;;) {
                 _cleanup_free_ char *element = NULL;
-                _cleanup_close_ int fd = -1;
 
                 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
                 if (r < 0)
@@ -690,7 +716,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
                 if (!path_extend(&element, name))
                         return -ENOMEM;
 
-                r = check_x_access(element, ret_fd ? &fd : NULL);
+                r = find_executable_impl(element, root, ret_filename, ret_fd);
                 if (r < 0) {
                         /* PATH entries which we don't have access to are ignored, as per tradition. */
                         if (r != -EACCES)
@@ -699,11 +725,6 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
                 }
 
                 /* Found it! */
-                if (ret_filename)
-                        *ret_filename = path_simplify(TAKE_PTR(element));
-                if (ret_fd)
-                        *ret_fd = TAKE_FD(fd);
-
                 return 0;
         }
 
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..a844c1151afda2563fb7e0c67d104a096a4a91fe 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;                                                   \
         })
@@ -277,6 +277,28 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) {
 int socket_bind_to_ifname(int fd, const char *ifname);
 int socket_bind_to_ifindex(int fd, int ifindex);
 
+/* Define a 64bit version of timeval/timespec in any case, even on 32bit userspace. */
+struct timeval_large {
+        uint64_t tvl_sec, tvl_usec;
+};
+struct timespec_large {
+        uint64_t tvl_sec, tvl_nsec;
+};
+
+/* glibc duplicates timespec/timeval on certain 32bit archs, once in 32bit and once in 64bit.
+ * See __convert_scm_timestamps() in glibc souce code. Hence, we need additional buffer space for them
+ * to prevent from recvmsg_safe() returning -EXFULL. */
+#define CMSG_SPACE_TIMEVAL                                              \
+        ((sizeof(struct timeval) == sizeof(struct timeval_large)) ?     \
+         CMSG_SPACE(sizeof(struct timeval)) :                           \
+         CMSG_SPACE(sizeof(struct timeval)) +                           \
+         CMSG_SPACE(sizeof(struct timeval_large)))
+#define CMSG_SPACE_TIMESPEC                                             \
+        ((sizeof(struct timespec) == sizeof(struct timespec_large)) ?   \
+         CMSG_SPACE(sizeof(struct timespec)) :                          \
+         CMSG_SPACE(sizeof(struct timespec)) +                          \
+         CMSG_SPACE(sizeof(struct timespec_large)))
+
 ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags);
 
 int socket_get_family(int fd, int *ret);
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 0a96a72d6ecce87ce9d334f36e20e29670d5d9dd..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
@@ -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())
@@ -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 30226b9bde6cbe556172a345c399f381235bab82..11eb352b9b56b6a34fbd050fa35c2af1f618a3be 100644 (file)
@@ -813,12 +813,6 @@ static int automount_start(Unit *u) {
         if (r < 0)
                 return r;
 
-        r = unit_test_start_limit(u);
-        if (r < 0) {
-                automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
-                return r;
-        }
-
         r = unit_acquire_invocation_id(u);
         if (r < 0)
                 return r;
@@ -1064,6 +1058,21 @@ static bool automount_supported(void) {
         return supported;
 }
 
+static int automount_test_start_limit(Unit *u) {
+        Automount *a = AUTOMOUNT(u);
+        int r;
+
+        assert(a);
+
+        r = unit_test_start_limit(u);
+        if (r < 0) {
+                automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
+                return r;
+        }
+
+        return 0;
+}
+
 static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
         [AUTOMOUNT_SUCCESS]                       = "success",
         [AUTOMOUNT_FAILURE_RESOURCES]             = "resources",
@@ -1126,4 +1135,6 @@ const UnitVTable automount_vtable = {
                         [JOB_FAILED]     = "Failed to unset automount %s.",
                 },
         },
+
+        .test_start_limit = automount_test_start_limit,
 };
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..6eb135785b17c7cf7cbf78e96aeae72611b17ea7 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();
         }
 }
 
@@ -705,19 +705,51 @@ static void job_emit_done_message(Unit *u, uint32_t job_id, JobType t, JobResult
 
         if (!console_only) {  /* Skip printing if output goes to the console, and job_print_status_message()
                                * will actually print something to the console. */
-
+                Condition *c;
                 const char *mid = job_done_mid(t, result);  /* mid may be NULL. log_unit_struct() will ignore it. */
-                const char *msg_fmt = strjoina("MESSAGE=", format);
 
-                DISABLE_WARNING_FORMAT_NONLITERAL;
-                log_unit_struct(u, job_done_messages[result].log_level,
-                                msg_fmt, ident,
-                                "JOB_ID=%" PRIu32, job_id,
-                                "JOB_TYPE=%s", job_type_to_string(t),
-                                "JOB_RESULT=%s", job_result_to_string(result),
-                                LOG_UNIT_INVOCATION_ID(u),
-                                mid);
-                REENABLE_WARNING;
+                c = t == JOB_START && result == JOB_DONE ? unit_find_failed_condition(u) : NULL;
+                if (c) {
+                        /* Special case units that were skipped because of a failed condition check so that
+                         * we can add more information to the message. */
+                        if (c->trigger)
+                                log_unit_struct(
+                                        u,
+                                        job_done_messages[result].log_level,
+                                        "MESSAGE=%s was skipped because all trigger condition checks failed.",
+                                        ident,
+                                        "JOB_ID=%" PRIu32, job_id,
+                                        "JOB_TYPE=%s", job_type_to_string(t),
+                                        "JOB_RESULT=%s", job_result_to_string(result),
+                                        LOG_UNIT_INVOCATION_ID(u),
+                                        mid);
+                        else
+                                log_unit_struct(
+                                        u,
+                                        job_done_messages[result].log_level,
+                                        "MESSAGE=%s was skipped because of a failed condition check (%s=%s%s).",
+                                        ident,
+                                        condition_type_to_string(c->type),
+                                        c->negate ? "!" : "",
+                                        c->parameter,
+                                        "JOB_ID=%" PRIu32, job_id,
+                                        "JOB_TYPE=%s", job_type_to_string(t),
+                                        "JOB_RESULT=%s", job_result_to_string(result),
+                                        LOG_UNIT_INVOCATION_ID(u),
+                                        mid);
+                } else {
+                        const char *msg_fmt = strjoina("MESSAGE=", format);
+
+                        DISABLE_WARNING_FORMAT_NONLITERAL;
+                        log_unit_struct(u, job_done_messages[result].log_level,
+                                        msg_fmt, ident,
+                                        "JOB_ID=%" PRIu32, job_id,
+                                        "JOB_TYPE=%s", job_type_to_string(t),
+                                        "JOB_RESULT=%s", job_result_to_string(result),
+                                        LOG_UNIT_INVOCATION_ID(u),
+                                        mid);
+                        REENABLE_WARNING;
+                }
         }
 
         if (do_console) {
@@ -781,7 +813,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 +881,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 24dfe9fc06b831f2ed7d1cf5fe557a45b9a65cf1..b767b0ee6c5eae0c32a289c641f52f74b8917ba3 100644 (file)
@@ -1437,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);
@@ -1537,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 */
@@ -1555,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 */
@@ -1727,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);
@@ -1736,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");
 
@@ -3672,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();
 
@@ -3704,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;
@@ -3731,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;
 
@@ -4111,7 +4115,7 @@ static void manager_unref_uid_internal(
         /* A generic implementation, covering both manager_unref_uid() and manager_unref_gid(), under the assumption
          * that uid_t and gid_t are actually defined the same way, with the same validity rules.
          *
-         * We store a hashmap where the UID/GID is they key and the value is a 32bit reference counter, whose highest
+         * We store a hashmap where the key is the UID/GID and the value is a 32bit reference counter, whose highest
          * bit is used as flag for marking UIDs/GIDs whose IPC objects to remove when the last reference to the UID/GID
          * is dropped. The flag is set to on, once at least one reference from a unit where RemoveIPC= is set is added
          * on a UID/GID. It is reset when the UID's/GID's reference counter drops to 0 again. */
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 1941081972bf5ed172688549f87688594ba79a9d..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',
@@ -195,7 +202,7 @@ foreach item : in_files
                 output: file,
                 command : [meson_render_jinja2, config_h, '@INPUT@'],
                 capture : true,
-                install : dir != 'no',
+                install : (dir == pkgsysconfdir and install_sysconfdir_samples) or (dir != pkgsysconfdir and dir != 'no'),
                 install_dir : dir)
 endforeach
 
index 608ead5bc43232cbb90f36a018bec989225835a0..35b56426d4106b13bec5dc53fa974f5a633600cc 100644 (file)
@@ -1167,12 +1167,6 @@ static int mount_start(Unit *u) {
 
         assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED));
 
-        r = unit_test_start_limit(u);
-        if (r < 0) {
-                mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
-                return r;
-        }
-
         r = unit_acquire_invocation_id(u);
         if (r < 0)
                 return r;
@@ -1223,7 +1217,7 @@ static int mount_stop(Unit *u) {
                 return 0;
 
         default:
-                assert_not_reached("Unexpected state.");
+                assert_not_reached();
         }
 }
 
@@ -1383,7 +1377,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 +1459,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 +1535,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;
@@ -2138,6 +2132,21 @@ static int mount_can_clean(Unit *u, ExecCleanMask *ret) {
         return exec_context_get_clean_mask(&m->exec_context, ret);
 }
 
+static int mount_test_start_limit(Unit *u) {
+        Mount *m = MOUNT(u);
+        int r;
+
+        assert(m);
+
+        r = unit_test_start_limit(u);
+        if (r < 0) {
+                mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
+                return r;
+        }
+
+        return 0;
+}
+
 static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
         [MOUNT_EXEC_MOUNT]   = "ExecMount",
         [MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
@@ -2235,4 +2244,6 @@ const UnitVTable mount_vtable = {
                         [JOB_TIMEOUT]    = "Timed out unmounting %s.",
                 },
         },
+
+        .test_start_limit = mount_test_start_limit,
 };
index 6b939e65e44b63354b3157998308a0b7ea40b64f..8d4388ded5cd865d1ac8a27cad3af707737bad43 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);
@@ -2498,7 +2498,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;
@@ -2514,7 +2513,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;
 
index 800524a3089b9565ca632b69eb3e38d058e1940a..693636b0ee44fa5f9cade896972152a234ebe651 100644 (file)
@@ -590,12 +590,6 @@ static int path_start(Unit *u) {
         if (r < 0)
                 return r;
 
-        r = unit_test_start_limit(u);
-        if (r < 0) {
-                path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
-                return r;
-        }
-
         r = unit_acquire_invocation_id(u);
         if (r < 0)
                 return r;
@@ -812,6 +806,21 @@ static void path_reset_failed(Unit *u) {
         p->result = PATH_SUCCESS;
 }
 
+static int path_test_start_limit(Unit *u) {
+        Path *p = PATH(u);
+        int r;
+
+        assert(p);
+
+        r = unit_test_start_limit(u);
+        if (r < 0) {
+                path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
+                return r;
+        }
+
+        return 0;
+}
+
 static const char* const path_type_table[_PATH_TYPE_MAX] = {
         [PATH_EXISTS]              = "PathExists",
         [PATH_EXISTS_GLOB]         = "PathExistsGlob",
@@ -866,4 +875,6 @@ const UnitVTable path_vtable = {
         .reset_failed = path_reset_failed,
 
         .bus_set_property = bus_path_set_property,
+
+        .test_start_limit = path_test_start_limit,
 };
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..9d8eef1f7428792e448ebdf2214a96c0d070e83a 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;
 
@@ -2438,13 +2436,6 @@ static int service_start(Unit *u) {
 
         assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED));
 
-        /* Make sure we don't enter a busy loop of some kind. */
-        r = unit_test_start_limit(u);
-        if (r < 0) {
-                service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false);
-                return r;
-        }
-
         r = unit_acquire_invocation_id(u);
         if (r < 0)
                 return r;
@@ -2780,7 +2771,7 @@ int service_deserialize_exec_command(
                                 return -ENOMEM;
                         break;
                 default:
-                        assert_not_reached("Logic error in exec command deserialization");
+                        assert_not_reached();
                 }
         }
 
@@ -3392,7 +3383,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 +3514,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 +3692,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 +3747,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 +3787,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 +3848,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 +3903,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;
@@ -4447,6 +4438,22 @@ static const char *service_finished_job(Unit *u, JobType t, JobResult result) {
         return NULL;
 }
 
+static int service_test_start_limit(Unit *u) {
+        Service *s = SERVICE(u);
+        int r;
+
+        assert(s);
+
+        /* Make sure we don't enter a busy loop of some kind. */
+        r = unit_test_start_limit(u);
+        if (r < 0) {
+                service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false);
+                return r;
+        }
+
+        return 0;
+}
+
 static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
         [SERVICE_RESTART_NO]          = "no",
         [SERVICE_RESTART_ON_SUCCESS]  = "on-success",
@@ -4610,4 +4617,6 @@ const UnitVTable service_vtable = {
                 },
                 .finished_job = service_finished_job,
         },
+
+        .test_start_limit = service_test_start_limit,
 };
index 8a2a68845092a7243a910dedf6dbeb966d621dd9..177068eed49f0104de556d4bc29a532382ab19f6 100644 (file)
@@ -34,6 +34,7 @@
 #include "process-util.h"
 #include "selinux-util.h"
 #include "serialize.h"
+#include "service.h"
 #include "signal-util.h"
 #include "smack-util.h"
 #include "socket.h"
@@ -433,7 +434,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 +452,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 +559,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 +915,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 +1725,7 @@ static int socket_open_fds(Socket *orig_s) {
                         break;
                 }
                 default:
-                        assert_not_reached("Unknown port type");
+                        assert_not_reached();
                 }
         }
 
@@ -2512,12 +2513,6 @@ static int socket_start(Unit *u) {
 
         assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED));
 
-        r = unit_test_start_limit(u);
-        if (r < 0) {
-                socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
-                return r;
-        }
-
         r = unit_acquire_invocation_id(u);
         if (r < 0)
                 return r;
@@ -3071,7 +3066,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 +3144,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 +3221,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;
@@ -3424,6 +3419,21 @@ static int socket_can_clean(Unit *u, ExecCleanMask *ret) {
         return exec_context_get_clean_mask(&s->exec_context, ret);
 }
 
+static int socket_test_start_limit(Unit *u) {
+        Socket *s = SOCKET(u);
+        int r;
+
+        assert(s);
+
+        r = unit_test_start_limit(u);
+        if (r < 0) {
+                socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
+                return r;
+        }
+
+        return 0;
+}
+
 static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
         [SOCKET_EXEC_START_PRE]   = "ExecStartPre",
         [SOCKET_EXEC_START_CHOWN] = "ExecStartChown",
@@ -3550,4 +3560,6 @@ const UnitVTable socket_vtable = {
                         [JOB_TIMEOUT]    = "Timed out stopping %s.",
                 },
         },
+
+        .test_start_limit = socket_test_start_limit,
 };
index a65195f2aac890d4cc7cdf74f4574362410bdc33..6813bdcf8c8e1faed413937db02d638e5b01ae5d 100644 (file)
@@ -5,7 +5,6 @@ typedef struct Socket Socket;
 typedef struct SocketPeer SocketPeer;
 
 #include "mount.h"
-#include "service.h"
 #include "socket-util.h"
 #include "unit.h"
 
index d7089f1c55c78b07e4fd15353f1eff3d040bad92..29c63118ac6c6185c6fce1e1795bdbedcd86c2f1 100644 (file)
@@ -932,12 +932,6 @@ static int swap_start(Unit *u) {
                 if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING)
                         return -EAGAIN;
 
-        r = unit_test_start_limit(u);
-        if (r < 0) {
-                swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT);
-                return r;
-        }
-
         r = unit_acquire_invocation_id(u);
         if (r < 0)
                 return r;
@@ -979,7 +973,7 @@ static int swap_stop(Unit *u) {
                 return 0;
 
         default:
-                assert_not_reached("Unexpected state.");
+                assert_not_reached();
         }
 }
 
@@ -1073,7 +1067,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 +1112,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 +1163,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 +1317,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);
 
@@ -1588,6 +1582,21 @@ static int swap_can_clean(Unit *u, ExecCleanMask *ret) {
         return exec_context_get_clean_mask(&s->exec_context, ret);
 }
 
+static int swap_test_start_limit(Unit *u) {
+        Swap *s = SWAP(u);
+        int r;
+
+        assert(s);
+
+        r = unit_test_start_limit(u);
+        if (r < 0) {
+                swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT);
+                return r;
+        }
+
+        return 0;
+}
+
 static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
         [SWAP_EXEC_ACTIVATE]   = "ExecActivate",
         [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
@@ -1683,4 +1692,6 @@ const UnitVTable swap_vtable = {
                         [JOB_TIMEOUT]    = "Timed out deactivating swap %s.",
                 },
         },
+
+        .test_start_limit = swap_test_start_limit,
 };
index 2c7f304cdd27c081db7c7115fbc0f68efb3bfd86..8853121c005666407b233c930845bf6435327486 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);
@@ -627,12 +627,6 @@ static int timer_start(Unit *u) {
         if (r < 0)
                 return r;
 
-        r = unit_test_start_limit(u);
-        if (r < 0) {
-                timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
-                return r;
-        }
-
         r = unit_acquire_invocation_id(u);
         if (r < 0)
                 return r;
@@ -797,7 +791,7 @@ static void timer_trigger_notify(Unit *u, Unit *other) {
                 break;
 
         default:
-                assert_not_reached("Unknown timer state");
+                assert_not_reached();
         }
 }
 
@@ -890,6 +884,21 @@ static int timer_can_clean(Unit *u, ExecCleanMask *ret) {
         return 0;
 }
 
+static int timer_test_start_limit(Unit *u) {
+        Timer *t = TIMER(u);
+        int r;
+
+        assert(t);
+
+        r = unit_test_start_limit(u);
+        if (r < 0) {
+                timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
+                return r;
+        }
+
+        return 0;
+}
+
 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
         [TIMER_ACTIVE]        = "OnActiveSec",
         [TIMER_BOOT]          = "OnBootSec",
@@ -949,4 +958,6 @@ const UnitVTable timer_vtable = {
         .timezone_change = timer_timezone_change,
 
         .bus_set_property = bus_timer_set_property,
+
+        .test_start_limit = timer_test_start_limit,
 };
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..1a76abf8a16386741d6afcf06d63ffd8fba380d4 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);
 
@@ -1718,7 +1727,7 @@ static bool unit_test_condition(Unit *u) {
         r = manager_get_effective_environment(u->manager, &env);
         if (r < 0) {
                 log_unit_error_errno(u, r, "Failed to determine effective environment: %m");
-                u->condition_result = CONDITION_ERROR;
+                u->condition_result = true;
         } else
                 u->condition_result = condition_test_list(
                                 u->conditions,
@@ -1848,6 +1857,13 @@ int unit_start(Unit *u) {
 
         assert(u);
 
+        /* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */
+        if (UNIT_VTABLE(u)->test_start_limit) {
+                int r = UNIT_VTABLE(u)->test_start_limit(u);
+                if (r < 0)
+                        return r;
+        }
+
         /* If this is already started, then this will succeed. Note that this will even succeed if this unit
          * is not startable by the user. This is relied on to detect when we need to wait for units and when
          * waiting is finished. */
@@ -2591,7 +2607,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 +2999,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 +3066,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 +3192,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 +3214,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 +3334,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 +3601,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 +3623,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 +3642,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 +4492,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 +4581,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 +5039,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 +5530,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;
@@ -5806,6 +5848,25 @@ int unit_thaw_vtable_common(Unit *u) {
         return unit_cgroup_freezer_action(u, FREEZER_THAW);
 }
 
+Condition *unit_find_failed_condition(Unit *u) {
+        Condition *c, *failed_trigger = NULL;
+        bool has_succeeded_trigger = false;
+
+        if (u->condition_result)
+                return NULL;
+
+        LIST_FOREACH(conditions, c, u->conditions)
+                if (c->trigger) {
+                        if (c->result == CONDITION_SUCCEEDED)
+                                 has_succeeded_trigger = true;
+                        else if (!failed_trigger)
+                                 failed_trigger = c;
+                } else if (c->result != CONDITION_SUCCEEDED)
+                        return c;
+
+        return failed_trigger && !has_succeeded_trigger ? failed_trigger : NULL;
+}
+
 static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
         [COLLECT_INACTIVE] = "inactive",
         [COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
index 48074d8ca5b680b6723d79c043a4c97bc068b459..80d56aefc1cc985989b2c447839d368e17379d7c 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;
@@ -652,6 +658,10 @@ typedef struct UnitVTable {
          * of this type will immediately fail. */
         bool (*supported)(void);
 
+        /* If this function is set, it's invoked first as part of starting a unit to allow start rate
+         * limiting checks to occur before we do anything else. */
+        int (*test_start_limit)(Unit *u);
+
         /* The strings to print in status messages */
         UnitStatusMessageFormats status_message_formats;
 
@@ -974,6 +984,8 @@ void unit_thawed(Unit *u);
 int unit_freeze_vtable_common(Unit *u);
 int unit_thaw_vtable_common(Unit *u);
 
+Condition *unit_find_failed_condition(Unit *u);
+
 /* Macros which append UNIT= or USER_UNIT= to the message */
 
 #define log_unit_full_errno_zerook(unit, level, error, ...)             \
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..f855a370ffc66ca5f70f5b648e7c5389eb66fb48 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) {
@@ -297,6 +299,8 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name,
                                             program_header->p_offset,
                                             program_header->p_filesz,
                                             ELF_T_NHDR);
+                if (!data)
+                        continue;
 
                 Elf *memelf = elf_memory(data->d_buf, data->d_size);
                 if (!memelf)
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..c1408a8
--- /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 function (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..3b3fe2c
--- /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 function (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..a0e1ccb
--- /dev/null
@@ -0,0 +1,157 @@
+/* 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_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, "Failed to 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 806199720518dbae78f16b163e05e88fc5e7f233..d174fcb714ec7f91845b08502b292da2baeb1a2e 100644 (file)
@@ -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 f0d5da8d18b71cb3234fe4116b97eb917bf139b3..a42b138a8044ff61a759275296c062070d4ab767 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);
 
@@ -819,7 +819,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 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 4d0364fceebaa9f83555faf420df4e1122dd555d..64ca9bb2f9069772f3dbe3995c89dff2e85412d3 100644 (file)
@@ -765,41 +765,36 @@ static int enumerate_partitions(dev_t devnum) {
 }
 
 static int add_mounts(void) {
-        dev_t devno;
+        _cleanup_free_ char *p = NULL;
         int r;
+        dev_t devno;
 
-        r = get_block_device_harder("/", &devno);
-        if (r == -EUCLEAN)
-                return btrfs_log_dev_root(LOG_ERR, r, "root file system");
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine block device of root file system: %m");
-        if (r == 0) { /* Not backed by block device */
-                r = get_block_device_harder("/usr", &devno);
+        /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
+         * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
+         * here. */
+        r = readlink_malloc("/run/systemd/volatile-root", &p);
+        if (r == -ENOENT) { /* volatile-root not found */
+                r = get_block_device_harder("/", &devno);
                 if (r == -EUCLEAN)
-                        return btrfs_log_dev_root(LOG_ERR, r, "/usr");
+                        return btrfs_log_dev_root(LOG_ERR, r, "root file system");
                 if (r < 0)
-                        return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
-                if (r == 0) {
-                        _cleanup_free_ char *p = NULL;
-                        mode_t m;
-
-                        /* If the root mount has been replaced by some form of volatile file system (overlayfs), the
-                         * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that
-                         * here. */
-                        r = readlink_malloc("/run/systemd/volatile-root", &p);
-                        if (r == -ENOENT) {
-                                log_debug("Neither root nor /usr file system are on a (single) block device.");
-                                return 0;
-                        }
+                        return log_error_errno(r, "Failed to determine block device of root file system: %m");
+                if (r == 0) { /* Not backed by block device */
+                        r = get_block_device_harder("/usr", &devno);
+                        if (r == -EUCLEAN)
+                                return btrfs_log_dev_root(LOG_ERR, r, "/usr");
                         if (r < 0)
-                                return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
-
-                        r = device_path_parse_major_minor(p, &m, &devno);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to parse major/minor device node: %m");
-                        if (!S_ISBLK(m))
-                                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
+                                return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
                 }
+        } else if (r < 0)
+                return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
+        else {
+                mode_t m;
+                r = device_path_parse_major_minor(p, &m, &devno);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse major/minor device node: %m");
+                if (!S_ISBLK(m))
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type.");
         }
 
         return enumerate_partitions(devno);
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..2321a31021205e7ed0c1a15a9dec9adcdae42b5b 100644 (file)
 #include "strv.h"
 #include "tmpfile-util.h"
 
-/* Round down to the nearest 1K size. Note that Linux generally handles block devices with 512 blocks only,
- * but actually doesn't accept uneven numbers in many cases. To avoid any confusion around this we'll
- * strictly round disk sizes down to the next 1K boundary. */
-#define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(1023))
+/* Round down to the nearest 4K size. Given that newer hardware generally prefers 4K sectors, let's align our
+ * partitions to that too. In the worst case we'll waste 3.5K per partition that way, but I think I can live
+ * with that. */
+#define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(4095))
+
+/* Rounds up to the nearest 4K boundary. Returns UINT64_MAX on overflow */
+#define DISK_SIZE_ROUND_UP(x)                                           \
+        ({                                                              \
+                uint64_t _x = (x);                                      \
+                _x > UINT64_MAX - 4095U ? UINT64_MAX : (_x + 4095U) & ~UINT64_C(4095); \
+        })
+
 
 int run_mark_dirty(int fd, bool b) {
         char x = '1';
@@ -1487,10 +1495,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 +1534,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");
 
@@ -1619,9 +1628,8 @@ static int make_partition_table(
         _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
-        uint64_t offset, size;
+        uint64_t offset, size, first_lba, start, last_lba, end;
         sd_id128_t disk_uuid;
-        char uuids[ID128_UUID_STRING_MAX];
         int r;
 
         assert(fd >= 0);
@@ -1660,23 +1668,37 @@ static int make_partition_table(
         if (r < 0)
                 return log_error_errno(r, "Failed to set partition type: %m");
 
-        r = fdisk_partition_start_follow_default(p, 1);
-        if (r < 0)
-                return log_error_errno(r, "Failed to place partition at beginning of space: %m");
-
         r = fdisk_partition_partno_follow_default(p, 1);
         if (r < 0)
                 return log_error_errno(r, "Failed to place partition at first free partition index: %m");
 
-        r = fdisk_partition_end_follow_default(p, 1);
+        first_lba = fdisk_get_first_lba(c); /* Boundary where usable space starts */
+        assert(first_lba <= UINT64_MAX/512);
+        start = DISK_SIZE_ROUND_UP(first_lba * 512); /* Round up to multiple of 4K */
+
+        if (start == UINT64_MAX)
+                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Overflow while rounding up start LBA.");
+
+        last_lba = fdisk_get_last_lba(c); /* One sector before boundary where usable space ends */
+        assert(last_lba < UINT64_MAX/512);
+        end = DISK_SIZE_ROUND_DOWN((last_lba + 1) * 512); /* Round down to multiple of 4K */
+
+        if (end <= start)
+                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Resulting partition size zero or negative.");
+
+        r = fdisk_partition_set_start(p, start / 512);
         if (r < 0)
-                return log_error_errno(r, "Failed to make partition cover all free space: %m");
+                return log_error_errno(r, "Failed to place partition at offset %" PRIu64 ": %m", start);
+
+        r = fdisk_partition_set_size(p, (end - start) / 512);
+        if (r < 0)
+                return log_error_errno(r, "Failed to end partition at offset %" PRIu64 ": %m", end);
 
         r = fdisk_partition_set_name(p, label);
         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");
 
@@ -2692,6 +2714,8 @@ int home_resize_luks(
 
                 new_image_size = old_image_size; /* we can't resize physical block devices */
         } else {
+                uint64_t new_image_size_rounded;
+
                 r = stat_verify_regular(&st);
                 if (r < 0)
                         return log_error_errno(r, "Image %s is not a block device nor regular file: %m", ip);
@@ -2702,11 +2726,16 @@ int home_resize_luks(
                  * apply onto the loopback file as a whole. When we operate on block devices we instead apply
                  * to the partition itself only. */
 
-                new_image_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
-                if (new_image_size == old_image_size) {
+                new_image_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size);
+
+                if (old_image_size == h->disk_size ||
+                    old_image_size == new_image_size_rounded) {
+                        /* If exact match, or a match after we rounded down, don't do a thing */
                         log_info("Image size already matching, skipping operation.");
                         return 0;
                 }
+
+                new_image_size = new_image_size_rounded;
         }
 
         r = home_prepare_luks(h, already_activated, whole_disk, cache, setup, &header_home);
@@ -2730,15 +2759,21 @@ int home_resize_luks(
                 if (new_image_size <= partition_table_extra)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than partition table metadata.");
 
-                new_partition_size = new_image_size - partition_table_extra;
+                new_partition_size = DISK_SIZE_ROUND_DOWN(new_image_size - partition_table_extra);
         } else {
+                uint64_t new_partition_size_rounded;
+
                 assert(S_ISBLK(st.st_mode));
 
-                new_partition_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
-                if (new_partition_size == setup->partition_size) {
+                new_partition_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size);
+
+                if (h->disk_size == setup->partition_size ||
+                    new_partition_size_rounded == setup->partition_size) {
                         log_info("Partition size already matching, skipping operation.");
                         return 0;
                 }
+
+                new_partition_size = new_partition_size_rounded;
         }
 
         if ((UINT64_MAX - setup->partition_offset) < new_partition_size ||
@@ -2752,7 +2787,7 @@ int home_resize_luks(
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than crypto payload offset?");
 
         old_fs_size = (setup->partition_size / 512U - crypto_offset) * 512U;
-        new_fs_size = (new_partition_size / 512U - crypto_offset) * 512U;
+        new_fs_size = DISK_SIZE_ROUND_DOWN((new_partition_size / 512U - crypto_offset) * 512U);
 
         /* Before we start doing anything, let's figure out if we actually can */
         resize_type = can_resize_fs(setup->root_fd, old_fs_size, new_fs_size);
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..26c8d66067981bbb1857552eb0c2180a7c1de314 100644 (file)
@@ -43,7 +43,7 @@ static int help(void) {
                "     --version    Show package version\n"
                "  -s --strict     When updating, return non-zero exit value on any parsing error\n"
                "     --usr        Generate in " UDEVLIBEXECDIR " instead of /etc/udev\n"
-               "  -r --root=PATH  Alternative root path in the filesystem\n\n"
+               "  -r --root=PATH  Alternative root path in the filesystem\n"
                "\nSee the %s for details.\n",
                program_invocation_short_name,
                ansi_highlight(),
@@ -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 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..9de31c2be07073e6225438a8b933598df7c3fa8a 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;
 
@@ -1262,7 +1269,7 @@ int server_process_datagram(
          * identical to NAME_MAX. For now we use that, but this should be updated one day when the final
          * limit is known. */
         CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
-                         CMSG_SPACE(sizeof(struct timeval)) +
+                         CMSG_SPACE_TIMEVAL +
                          CMSG_SPACE(sizeof(int)) + /* fd */
                          CMSG_SPACE(NAME_MAX) /* selinux label */) control;
 
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 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..823be0f2752b31cca47cfc457e1048836a7bf697 100644 (file)
@@ -149,7 +149,7 @@ int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *ret_dst,
                   triple_timestamp *ret_timestamp) {
 
         CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
-                         CMSG_SPACE(sizeof(struct timeval))) control;
+                         CMSG_SPACE_TIMEVAL) control;
         struct iovec iov = {};
         union sockaddr_union sa = {};
         struct msghdr msg = {
@@ -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 479b81b352045e4ad3cac9d48317bd634f499ae7..8ec871f6a445ab6d8d65600d462112f10dee8d05 100644 (file)
@@ -132,19 +132,23 @@ libsystemd_sources = files('''
         sd-device/sd-device.c
         sd-hwdb/hwdb-internal.h
         sd-hwdb/sd-hwdb.c
-        sd-netlink/generic-netlink.c
-        sd-netlink/generic-netlink.h
+        sd-netlink/netlink-genl.c
+        sd-netlink/netlink-genl.h
         sd-netlink/netlink-internal.h
+        sd-netlink/netlink-message-nfnl.c
+        sd-netlink/netlink-message-rtnl.c
         sd-netlink/netlink-message.c
         sd-netlink/netlink-slot.c
         sd-netlink/netlink-slot.h
         sd-netlink/netlink-socket.c
+        sd-netlink/netlink-types-genl.c
+        sd-netlink/netlink-types-internal.h
+        sd-netlink/netlink-types-nfnl.c
+        sd-netlink/netlink-types-rtnl.c
         sd-netlink/netlink-types.c
         sd-netlink/netlink-types.h
         sd-netlink/netlink-util.c
         sd-netlink/netlink-util.h
-        sd-netlink/nfnl-message.c
-        sd-netlink/rtnl-message.c
         sd-netlink/sd-netlink.c
         sd-network/network-util.c
         sd-network/network-util.h
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 62012665bd2591fc7e3f9d68697c6ad97aa6fb0c..3c54897536a6b87f41d2732fe22af0ff18bb5a24 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) {
@@ -2171,7 +2171,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;
@@ -2190,8 +2189,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;
 }
diff --git a/src/libsystemd/sd-netlink/generic-netlink.c b/src/libsystemd/sd-netlink/generic-netlink.c
deleted file mode 100644 (file)
index 69f1a0c..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <linux/genetlink.h>
-
-#include "sd-netlink.h"
-
-#include "alloc-util.h"
-#include "generic-netlink.h"
-#include "netlink-internal.h"
-
-typedef struct {
-        const char* name;
-        uint8_t version;
-} genl_family;
-
-static const genl_family genl_families[] = {
-        [SD_GENL_ID_CTRL]   = { .name = "",          .version = 1 },
-        [SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 },
-        [SD_GENL_FOU]       = { .name = "fou",       .version = 1 },
-        [SD_GENL_L2TP]      = { .name = "l2tp",      .version = 1 },
-        [SD_GENL_MACSEC]    = { .name = "macsec",    .version = 1 },
-        [SD_GENL_NL80211]   = { .name = "nl80211",   .version = 1 },
-        [SD_GENL_BATADV]    = { .name = "batadv",    .version = 1 },
-};
-
-int sd_genl_socket_open(sd_netlink **ret) {
-        return netlink_open_family(ret, NETLINK_GENERIC);
-}
-
-static int genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        const NLType *genl_cmd_type, *nl_type;
-        const NLTypeSystem *type_system;
-        size_t size;
-        int r;
-
-        assert(nl);
-        assert(nl->protocol == NETLINK_GENERIC);
-        assert(ret);
-
-        r = type_system_get_type(&genl_family_type_system_root, &genl_cmd_type, family);
-        if (r < 0)
-                return r;
-
-        r = message_new_empty(nl, &m);
-        if (r < 0)
-                return r;
-
-        size = NLMSG_SPACE(sizeof(struct genlmsghdr));
-        m->hdr = malloc0(size);
-        if (!m->hdr)
-                return -ENOMEM;
-
-        m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
-
-        type_get_type_system(genl_cmd_type, &type_system);
-
-        r = type_system_get_type(type_system, &nl_type, cmd);
-        if (r < 0)
-                return r;
-
-        m->hdr->nlmsg_len = size;
-        m->hdr->nlmsg_type = nlmsg_type;
-
-        type_get_type_system(nl_type, &m->containers[0].type_system);
-
-        *(struct genlmsghdr *) NLMSG_DATA(m->hdr) = (struct genlmsghdr) {
-                .cmd = cmd,
-                .version = genl_families[family].version,
-        };
-
-        *ret = TAKE_PTR(m);
-
-        return 0;
-}
-
-static int lookup_nlmsg_type(sd_netlink *nl, sd_genl_family_t family, uint16_t *ret) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        uint16_t u;
-        void *v;
-        int r;
-
-        assert(nl);
-        assert(nl->protocol == NETLINK_GENERIC);
-        assert(ret);
-
-        if (family == SD_GENL_ID_CTRL) {
-                *ret = GENL_ID_CTRL;
-                return 0;
-        }
-
-        v = hashmap_get(nl->genl_family_to_nlmsg_type, INT_TO_PTR(family));
-        if (v) {
-                *ret = PTR_TO_UINT(v);
-                return 0;
-        }
-
-        r = genl_message_new(nl, SD_GENL_ID_CTRL, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &req);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, genl_families[family].name);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call(nl, req, 0, &reply);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, &u);
-        if (r < 0)
-                return r;
-
-        r = hashmap_ensure_put(&nl->genl_family_to_nlmsg_type, NULL, INT_TO_PTR(family), UINT_TO_PTR(u));
-        if (r < 0)
-                return r;
-
-        r = hashmap_ensure_put(&nl->nlmsg_type_to_genl_family, NULL, UINT_TO_PTR(u), INT_TO_PTR(family));
-        if (r < 0)
-                return r;
-
-        *ret = u;
-        return 0;
-}
-
-int sd_genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint8_t cmd, sd_netlink_message **ret) {
-        uint16_t nlmsg_type = 0;  /* Unnecessary initialization to appease gcc */
-        int r;
-
-        assert_return(nl, -EINVAL);
-        assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        r = lookup_nlmsg_type(nl, family, &nlmsg_type);
-        if (r < 0)
-                return r;
-
-        return genl_message_new(nl, family, nlmsg_type, cmd, ret);
-}
-
-int nlmsg_type_to_genl_family(const sd_netlink *nl, uint16_t nlmsg_type, sd_genl_family_t *ret) {
-        void *p;
-
-        assert(nl);
-        assert(nl->protocol == NETLINK_GENERIC);
-        assert(ret);
-
-        if (nlmsg_type == NLMSG_ERROR)
-                *ret = SD_GENL_ERROR;
-        else if (nlmsg_type == NLMSG_DONE)
-                *ret = SD_GENL_DONE;
-        else if (nlmsg_type == GENL_ID_CTRL)
-                *ret = SD_GENL_ID_CTRL;
-        else {
-                p = hashmap_get(nl->nlmsg_type_to_genl_family, UINT_TO_PTR(nlmsg_type));
-                if (!p)
-                        return -EOPNOTSUPP;
-
-                *ret = PTR_TO_INT(p);
-        }
-
-        return 0;
-}
-
-int sd_genl_message_get_family(sd_netlink *nl, sd_netlink_message *m, sd_genl_family_t *ret) {
-        uint16_t nlmsg_type;
-        int r;
-
-        assert_return(nl, -EINVAL);
-        assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
-        assert_return(m, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        r = sd_netlink_message_get_type(m, &nlmsg_type);
-        if (r < 0)
-                return r;
-
-        return nlmsg_type_to_genl_family(nl, nlmsg_type, ret);
-}
diff --git a/src/libsystemd/sd-netlink/generic-netlink.h b/src/libsystemd/sd-netlink/generic-netlink.h
deleted file mode 100644 (file)
index fd04614..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "sd-netlink.h"
-
-int nlmsg_type_to_genl_family(const sd_netlink *nl, uint16_t type, sd_genl_family_t *ret);
diff --git a/src/libsystemd/sd-netlink/netlink-genl.c b/src/libsystemd/sd-netlink/netlink-genl.c
new file mode 100644 (file)
index 0000000..f6f13ae
--- /dev/null
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <linux/genetlink.h>
+
+#include "sd-netlink.h"
+
+#include "alloc-util.h"
+#include "netlink-genl.h"
+#include "netlink-internal.h"
+#include "netlink-types.h"
+
+typedef struct GenericNetlinkFamily {
+        sd_netlink *genl;
+
+        const NLTypeSystem *type_system;
+
+        uint16_t id; /* a.k.a nlmsg_type */
+        char *name;
+        uint32_t version;
+        uint32_t additional_header_size;
+        Hashmap *multicast_group_by_name;
+} GenericNetlinkFamily;
+
+static const GenericNetlinkFamily nlctrl_static = {
+        .id = GENL_ID_CTRL,
+        .name = (char*) CTRL_GENL_NAME,
+        .version = 0x01,
+};
+
+static GenericNetlinkFamily *genl_family_free(GenericNetlinkFamily *f) {
+        if (!f)
+                return NULL;
+
+        if (f->genl) {
+                if (f->id > 0)
+                        hashmap_remove(f->genl->genl_family_by_id, UINT_TO_PTR(f->id));
+                if (f->name)
+                        hashmap_remove(f->genl->genl_family_by_name, f->name);
+        }
+
+        free(f->name);
+        hashmap_free(f->multicast_group_by_name);
+
+        return mfree(f);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(GenericNetlinkFamily*, genl_family_free);
+
+void genl_clear_family(sd_netlink *nl) {
+        assert(nl);
+
+        nl->genl_family_by_name = hashmap_free_with_destructor(nl->genl_family_by_name, genl_family_free);
+        nl->genl_family_by_id = hashmap_free_with_destructor(nl->genl_family_by_id, genl_family_free);
+}
+
+static int genl_family_new(
+                sd_netlink *nl,
+                const char *expected_family_name,
+                const NLTypeSystem *type_system,
+                sd_netlink_message *message,
+                const GenericNetlinkFamily **ret) {
+
+        _cleanup_(genl_family_freep) GenericNetlinkFamily *f = NULL;
+        const char *family_name;
+        uint8_t cmd;
+        int r;
+
+        assert(nl);
+        assert(expected_family_name);
+        assert(type_system);
+        assert(message);
+        assert(ret);
+
+        f = new(GenericNetlinkFamily, 1);
+        if (!f)
+                return -ENOMEM;
+
+        *f = (GenericNetlinkFamily) {
+                .type_system = type_system,
+        };
+
+        if (sd_netlink_message_is_error(message)) {
+                int e;
+
+                /* Kernel does not support the genl family? To prevent from resolving the family name
+                 * again, let's store the family with zero id to indicate that. */
+
+                e = sd_netlink_message_get_errno(message);
+                if (e >= 0) /* Huh? */
+                        e = -EOPNOTSUPP;
+
+                f->name = strdup(expected_family_name);
+                if (!f->name)
+                        return e;
+
+                if (hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f) < 0)
+                        return e;
+
+                f->genl = nl;
+                TAKE_PTR(f);
+                return e;
+        }
+
+        r = sd_genl_message_get_family_name(nl, message, &family_name);
+        if (r < 0)
+                return r;
+
+        if (!streq(family_name, CTRL_GENL_NAME))
+                return -EINVAL;
+
+        r = sd_genl_message_get_command(nl, message, &cmd);
+        if (r < 0)
+                return r;
+
+        if (cmd != CTRL_CMD_NEWFAMILY)
+                return -EINVAL;
+
+        r = sd_netlink_message_read_u16(message, CTRL_ATTR_FAMILY_ID, &f->id);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_read_string_strdup(message, CTRL_ATTR_FAMILY_NAME, &f->name);
+        if (r < 0)
+                return r;
+
+        if (!streq(f->name, expected_family_name))
+                return -EINVAL;
+
+        r = sd_netlink_message_read_u32(message, CTRL_ATTR_VERSION, &f->version);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_read_u32(message, CTRL_ATTR_HDRSIZE, &f->additional_header_size);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_enter_container(message, CTRL_ATTR_MCAST_GROUPS);
+        if (r >= 0) {
+                for (uint16_t i = 0; i < UINT16_MAX; i++) {
+                        _cleanup_free_ char *group_name = NULL;
+                        uint32_t group_id;
+
+                        r = sd_netlink_message_enter_array(message, i + 1);
+                        if (r == -ENODATA)
+                                break;
+                        if (r < 0)
+                                return r;
+
+                        r = sd_netlink_message_read_u32(message, CTRL_ATTR_MCAST_GRP_ID, &group_id);
+                        if (r < 0)
+                                return r;
+
+                        r = sd_netlink_message_read_string_strdup(message, CTRL_ATTR_MCAST_GRP_NAME, &group_name);
+                        if (r < 0)
+                                return r;
+
+                        r = sd_netlink_message_exit_container(message);
+                        if (r < 0)
+                                return r;
+
+                        if (group_id == 0) {
+                                log_debug("sd-netlink: received multicast group '%s' for generic netlink family '%s' with id == 0, ignoring",
+                                          group_name, f->name);
+                                continue;
+                        }
+
+                        r = hashmap_ensure_put(&f->multicast_group_by_name, &string_hash_ops_free, group_name, UINT32_TO_PTR(group_id));
+                        if (r < 0)
+                                return r;
+
+                        TAKE_PTR(group_name);
+                }
+
+                r = sd_netlink_message_exit_container(message);
+                if (r < 0)
+                        return r;
+        }
+
+        r = hashmap_ensure_put(&nl->genl_family_by_id, NULL, UINT_TO_PTR(f->id), f);
+        if (r < 0)
+                return r;
+
+        r = hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f);
+        if (r < 0) {
+                hashmap_remove(nl->genl_family_by_id, UINT_TO_PTR(f->id));
+                return r;
+        }
+
+        f->genl = nl;
+        *ret = TAKE_PTR(f);
+        return 0;
+}
+
+static const NLTypeSystem *genl_family_get_type_system(const GenericNetlinkFamily *family) {
+        assert(family);
+
+        if (family->type_system)
+                return family->type_system;
+
+        return genl_get_type_system_by_name(family->name);
+}
+
+static int genl_message_new(
+                sd_netlink *nl,
+                const GenericNetlinkFamily *family,
+                uint8_t cmd,
+                sd_netlink_message **ret) {
+
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        const NLTypeSystem *type_system;
+        int r;
+
+        assert(nl);
+        assert(nl->protocol == NETLINK_GENERIC);
+        assert(family);
+        assert(ret);
+
+        type_system = genl_family_get_type_system(family);
+        if (!type_system)
+                return -EOPNOTSUPP;
+
+        r = message_new_full(nl, family->id, type_system,
+                             sizeof(struct genlmsghdr) + family->additional_header_size, &m);
+        if (r < 0)
+                return r;
+
+        *(struct genlmsghdr *) NLMSG_DATA(m->hdr) = (struct genlmsghdr) {
+                .cmd = cmd,
+                .version = family->version,
+        };
+
+        *ret = TAKE_PTR(m);
+        return 0;
+}
+
+static int genl_family_get_by_name_internal(
+                sd_netlink *nl,
+                const GenericNetlinkFamily *ctrl,
+                const char *name,
+                const GenericNetlinkFamily **ret) {
+
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+        const NLTypeSystem *type_system;
+        int r;
+
+        assert(nl);
+        assert(nl->protocol == NETLINK_GENERIC);
+        assert(ctrl);
+        assert(name);
+        assert(ret);
+
+        type_system = genl_get_type_system_by_name(name);
+        if (!type_system)
+                return -EOPNOTSUPP;
+
+        r = genl_message_new(nl, ctrl, CTRL_CMD_GETFAMILY, &req);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, name);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call(nl, req, 0, &reply);
+        if (r < 0)
+                return r;
+
+        return genl_family_new(nl, name, type_system, reply, ret);
+}
+
+static int genl_family_get_by_name(sd_netlink *nl, const char *name, const GenericNetlinkFamily **ret) {
+        const GenericNetlinkFamily *f, *ctrl;
+        int r;
+
+        assert(nl);
+        assert(nl->protocol == NETLINK_GENERIC);
+        assert(name);
+        assert(ret);
+
+        f = hashmap_get(nl->genl_family_by_name, name);
+        if (f) {
+                if (f->id == 0) /* kernel does not support the family. */
+                        return -EOPNOTSUPP;
+
+                *ret = f;
+                return 0;
+        }
+
+        if (streq(name, CTRL_GENL_NAME))
+                return genl_family_get_by_name_internal(nl, &nlctrl_static, CTRL_GENL_NAME, ret);
+
+        ctrl = hashmap_get(nl->genl_family_by_name, CTRL_GENL_NAME);
+        if (!ctrl) {
+                r = genl_family_get_by_name_internal(nl, &nlctrl_static, CTRL_GENL_NAME, &ctrl);
+                if (r < 0)
+                        return r;
+        }
+
+        return genl_family_get_by_name_internal(nl, ctrl, name, ret);
+}
+
+static int genl_family_get_by_id(sd_netlink *nl, uint16_t id, const GenericNetlinkFamily **ret) {
+        const GenericNetlinkFamily *f;
+
+        assert(nl);
+        assert(nl->protocol == NETLINK_GENERIC);
+        assert(ret);
+
+        f = hashmap_get(nl->genl_family_by_id, UINT_TO_PTR(id));
+        if (f) {
+                *ret = f;
+                return 0;
+        }
+
+        if (id == GENL_ID_CTRL) {
+                *ret = &nlctrl_static;
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
+int genl_get_type_system_and_header_size(
+                sd_netlink *nl,
+                uint16_t id,
+                const NLTypeSystem **ret_type_system,
+                size_t *ret_header_size) {
+
+        const GenericNetlinkFamily *f;
+        int r;
+
+        assert(nl);
+        assert(nl->protocol == NETLINK_GENERIC);
+
+        r = genl_family_get_by_id(nl, id, &f);
+        if (r < 0)
+                return r;
+
+        if (ret_type_system) {
+                const NLTypeSystem *t;
+
+                t = genl_family_get_type_system(f);
+                if (!t)
+                        return -EOPNOTSUPP;
+
+                *ret_type_system = t;
+        }
+        if (ret_header_size)
+                *ret_header_size = sizeof(struct genlmsghdr) + f->additional_header_size;
+        return 0;
+}
+
+int sd_genl_message_new(sd_netlink *nl, const char *family_name, uint8_t cmd, sd_netlink_message **ret) {
+        const GenericNetlinkFamily *family;
+        int r;
+
+        assert_return(nl, -EINVAL);
+        assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+        assert_return(family_name, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = genl_family_get_by_name(nl, family_name, &family);
+        if (r < 0)
+                return r;
+
+        return genl_message_new(nl, family, cmd, ret);
+}
+
+int sd_genl_message_get_family_name(sd_netlink *nl, sd_netlink_message *m, const char **ret) {
+        const GenericNetlinkFamily *family;
+        uint16_t nlmsg_type;
+        int r;
+
+        assert_return(nl, -EINVAL);
+        assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+        assert_return(m, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = sd_netlink_message_get_type(m, &nlmsg_type);
+        if (r < 0)
+                return r;
+
+        r = genl_family_get_by_id(nl, nlmsg_type, &family);
+        if (r < 0)
+                return r;
+
+        *ret = family->name;
+        return 0;
+}
+
+int sd_genl_message_get_command(sd_netlink *nl, sd_netlink_message *m, uint8_t *ret) {
+        struct genlmsghdr *h;
+        uint16_t nlmsg_type;
+        size_t size;
+        int r;
+
+        assert_return(nl, -EINVAL);
+        assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+        assert_return(m, -EINVAL);
+        assert_return(m->protocol == NETLINK_GENERIC, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = sd_netlink_message_get_type(m, &nlmsg_type);
+        if (r < 0)
+                return r;
+
+        r = genl_get_type_system_and_header_size(nl, nlmsg_type, NULL, &size);
+        if (r < 0)
+                return r;
+
+        if (m->hdr->nlmsg_len < NLMSG_LENGTH(size))
+                return -EBADMSG;
+
+        h = NLMSG_DATA(m->hdr);
+
+        *ret = h->cmd;
+        return 0;
+}
+
+static int genl_family_get_multicast_group_id_by_name(const GenericNetlinkFamily *f, const char *name, uint32_t *ret) {
+        void *p;
+
+        assert(f);
+        assert(name);
+
+        p = hashmap_get(f->multicast_group_by_name, name);
+        if (!p)
+                return -ENOENT;
+
+        if (ret)
+                *ret = PTR_TO_UINT32(p);
+        return 0;
+}
+
+int sd_genl_add_match(
+                sd_netlink *nl,
+                sd_netlink_slot **ret_slot,
+                const char *family_name,
+                const char *multicast_group_name,
+                uint8_t command,
+                sd_netlink_message_handler_t callback,
+                sd_netlink_destroy_t destroy_callback,
+                void *userdata,
+                const char *description) {
+
+        const GenericNetlinkFamily *f;
+        uint32_t multicast_group_id;
+        int r;
+
+        assert_return(nl, -EINVAL);
+        assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(family_name, -EINVAL);
+        assert_return(multicast_group_name, -EINVAL);
+
+        /* If command == 0, then all commands belonging to the multicast group trigger the callback. */
+
+        r = genl_family_get_by_name(nl, family_name, &f);
+        if (r < 0)
+                return r;
+
+        r = genl_family_get_multicast_group_id_by_name(f, multicast_group_name, &multicast_group_id);
+        if (r < 0)
+                return r;
+
+        return netlink_add_match_internal(nl, ret_slot, &multicast_group_id, 1, f->id, command,
+                                          callback, destroy_callback, userdata, description);
+}
+
+int sd_genl_socket_open(sd_netlink **ret) {
+        return netlink_open_family(ret, NETLINK_GENERIC);
+}
diff --git a/src/libsystemd/sd-netlink/netlink-genl.h b/src/libsystemd/sd-netlink/netlink-genl.h
new file mode 100644 (file)
index 0000000..b06be05
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-netlink.h"
+
+#define CTRL_GENL_NAME "nlctrl"
+
+void genl_clear_family(sd_netlink *nl);
index 49c7dd773dce934d683f6945f179cc3cfc02bfc9..050edaec78cb8ccac4ac11284d5d2798c96baf55 100644 (file)
 #include "prioq.h"
 #include "time-util.h"
 
-#define RTNL_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
+#define NETLINK_DEFAULT_TIMEOUT_USEC ((usec_t) (25 * USEC_PER_SEC))
 
-#define RTNL_RQUEUE_MAX 64*1024
+#define NETLINK_RQUEUE_MAX 64*1024
 
-#define RTNL_CONTAINER_DEPTH 32
+#define NETLINK_CONTAINER_DEPTH 32
 
 struct reply_callback {
         sd_netlink_message_handler_t callback;
@@ -25,7 +25,10 @@ struct reply_callback {
 
 struct match_callback {
         sd_netlink_message_handler_t callback;
+        uint32_t *groups;
+        size_t n_groups;
         uint16_t type;
+        uint8_t cmd; /* used by genl */
 
         LIST_FIELDS(struct match_callback, match_callbacks);
 };
@@ -95,8 +98,8 @@ struct sd_netlink {
         sd_event_source *exit_event_source;
         sd_event *event;
 
-        Hashmap *genl_family_to_nlmsg_type;
-        Hashmap *nlmsg_type_to_genl_family;
+        Hashmap *genl_family_by_name;
+        Hashmap *genl_family_by_id;
 };
 
 struct netlink_attribute {
@@ -118,7 +121,7 @@ struct sd_netlink_message {
         int protocol;
 
         struct nlmsghdr *hdr;
-        struct netlink_container containers[RTNL_CONTAINER_DEPTH];
+        struct netlink_container containers[NETLINK_CONTAINER_DEPTH];
         unsigned n_containers; /* number of containers */
         bool sealed:1;
         bool broadcast:1;
@@ -126,10 +129,22 @@ struct sd_netlink_message {
         sd_netlink_message *next; /* next in a chain of multi-part messages */
 };
 
-int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type);
-int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret);
+int message_new_empty(sd_netlink *nl, sd_netlink_message **ret);
+int message_new_full(
+                sd_netlink *nl,
+                uint16_t nlmsg_type,
+                const NLTypeSystem *type_system,
+                size_t header_size,
+                sd_netlink_message **ret);
+int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type);
+int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret);
+uint32_t message_get_serial(sd_netlink_message *m);
+void message_seal(sd_netlink_message *m);
 
 int netlink_open_family(sd_netlink **ret, int family);
+bool netlink_pid_changed(sd_netlink *nl);
+int netlink_rqueue_make_room(sd_netlink *nl);
+int netlink_rqueue_partial_make_room(sd_netlink *nl);
 
 int socket_open(int family);
 int socket_bind(sd_netlink *nl);
@@ -139,9 +154,18 @@ int socket_write_message(sd_netlink *nl, sd_netlink_message *m);
 int socket_writev_message(sd_netlink *nl, sd_netlink_message **m, size_t msgcount);
 int socket_read_message(sd_netlink *nl);
 
-int rtnl_rqueue_make_room(sd_netlink *rtnl);
-int rtnl_rqueue_partial_make_room(sd_netlink *rtnl);
-
-/* Make sure callbacks don't destroy the rtnl connection */
-#define NETLINK_DONT_DESTROY(rtnl) \
-        _cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##rtnl = sd_netlink_ref(rtnl)
+int netlink_add_match_internal(
+                sd_netlink *nl,
+                sd_netlink_slot **ret_slot,
+                const uint32_t *groups,
+                size_t n_groups,
+                uint16_t type,
+                uint8_t cmd,
+                sd_netlink_message_handler_t callback,
+                sd_netlink_destroy_t destroy_callback,
+                void *userdata,
+                const char *description);
+
+/* Make sure callbacks don't destroy the netlink connection */
+#define NETLINK_DONT_DESTROY(nl) \
+        _cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##nl = sd_netlink_ref(nl)
similarity index 68%
rename from src/libsystemd/sd-netlink/nfnl-message.c
rename to src/libsystemd/sd-netlink/netlink-message-nfnl.c
index 5f669f750b63b1b8bd289c3a6ec07397711edeea..9b24f7404244f6b41ab58049da33119bc5913b01 100644 (file)
 #include "format-util.h"
 #include "netlink-internal.h"
 #include "netlink-types.h"
-#include "netlink-util.h"
 #include "socket-util.h"
-#include "util.h"
 
-static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t type, uint16_t flags) {
+static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t msg_type, uint16_t flags) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        const NLType *nl_type;
-        size_t size;
         int r;
 
         assert_return(nfnl, -EINVAL);
+        assert_return(ret, -EINVAL);
 
-        r = type_system_root_get_type(nfnl, &nl_type, NFNL_SUBSYS_NFTABLES);
+        r = message_new(nfnl, &m, NFNL_SUBSYS_NFTABLES << 8 | msg_type);
         if (r < 0)
                 return r;
 
-        if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
-                return -EINVAL;
-
-        r = message_new_empty(nfnl, &m);
-        if (r < 0)
-                return r;
-
-        size = NLMSG_SPACE(type_get_size(nl_type));
-
-        assert(size >= sizeof(struct nlmsghdr));
-        m->hdr = malloc0(size);
-        if (!m->hdr)
-                return -ENOMEM;
-
-        m->hdr->nlmsg_flags = NLM_F_REQUEST | flags;
-
-        type_get_type_system(nl_type, &m->containers[0].type_system);
-
-        r = type_system_get_type_system(m->containers[0].type_system,
-                                        &m->containers[0].type_system,
-                                        type);
-        if (r < 0)
-                return r;
-
-        m->hdr->nlmsg_len = size;
-        m->hdr->nlmsg_type = NFNL_SUBSYS_NFTABLES << 8 | type;
+        m->hdr->nlmsg_flags |= flags;
 
         *(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
                 .nfgen_family = family,
@@ -66,11 +38,11 @@ static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int famil
         return 0;
 }
 
-static int sd_nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, int v) {
+static int nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, uint16_t msg_type) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = message_new(nfnl, &m, v);
+        r = message_new(nfnl, &m, NFNL_SUBSYS_NONE << 8 | msg_type);
         if (r < 0)
                 return r;
 
@@ -81,26 +53,31 @@ static int sd_nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, int
         };
 
         *ret = TAKE_PTR(m);
-        return r;
+        return 0;
 }
 
 int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret) {
-        return sd_nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_BEGIN);
+        return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_BEGIN);
 }
 
 int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret) {
-        return sd_nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_END);
+        return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_END);
 }
 
-int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret,
-                                      int family,
-                                      const char *table, const char *chain,
-                                      const char *type,
-                                      uint8_t hook, int prio) {
+int sd_nfnl_nft_message_new_basechain(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *table,
+                const char *chain,
+                const char *type,
+                uint8_t hook,
+                int prio) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWCHAIN, NLM_F_CREATE | NLM_F_ACK);
+        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWCHAIN, NLM_F_CREATE);
         if (r < 0)
                 return r;
 
@@ -136,12 +113,16 @@ int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret
         return 0;
 }
 
-int sd_nfnl_nft_message_del_table(sd_netlink *nfnl, sd_netlink_message **ret,
-                                  int family, const char *table) {
+int sd_nfnl_nft_message_del_table(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *table) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = nft_message_new(nfnl, &m, family, NFT_MSG_DELTABLE, NLM_F_CREATE | NLM_F_ACK);
+        r = nft_message_new(nfnl, &m, family, NFT_MSG_DELTABLE, NLM_F_CREATE);
         if (r < 0)
                 return r;
 
@@ -153,12 +134,16 @@ int sd_nfnl_nft_message_del_table(sd_netlink *nfnl, sd_netlink_message **ret,
         return r;
 }
 
-int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret,
-                                  int family, const char *table, uint16_t flags) {
+int sd_nfnl_nft_message_new_table(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *table) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWTABLE, NLM_F_CREATE | flags);
+        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL);
         if (r < 0)
                 return r;
 
@@ -170,12 +155,17 @@ int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret,
         return r;
 }
 
-int sd_nfnl_nft_message_new_rule(sd_netlink *nfnl, sd_netlink_message **ret,
-                                 int family, const char *table, const char *chain) {
+int sd_nfnl_nft_message_new_rule(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *table,
+                const char *chain) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWRULE, NLM_F_CREATE | NLM_F_ACK);
+        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWRULE, NLM_F_CREATE);
         if (r < 0)
                 return r;
 
@@ -191,13 +181,19 @@ int sd_nfnl_nft_message_new_rule(sd_netlink *nfnl, sd_netlink_message **ret,
         return r;
 }
 
-int sd_nfnl_nft_message_new_set(sd_netlink *nfnl, sd_netlink_message **ret,
-                                int family, const char *table, const char *set_name,
-                                uint32_t set_id, uint32_t klen) {
+int sd_nfnl_nft_message_new_set(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *table,
+                const char *set_name,
+                uint32_t set_id,
+                uint32_t klen) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSET, NLM_F_CREATE | NLM_F_ACK);
+        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSET, NLM_F_CREATE);
         if (r < 0)
                 return r;
 
@@ -216,16 +212,22 @@ int sd_nfnl_nft_message_new_set(sd_netlink *nfnl, sd_netlink_message **ret,
         r = sd_netlink_message_append_u32(m, NFTA_SET_KEY_LEN, htobe32(klen));
         if (r < 0)
                 return r;
+
         *ret = TAKE_PTR(m);
         return r;
 }
 
-int sd_nfnl_nft_message_new_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
-                                           int family, const char *table, const char *set_name) {
+int sd_nfnl_nft_message_new_setelems_begin(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *table,
+                const char *set_name) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSETELEM, NLM_F_CREATE | NLM_F_ACK);
+        r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSETELEM, NLM_F_CREATE);
         if (r < 0)
                 return r;
 
@@ -245,12 +247,17 @@ int sd_nfnl_nft_message_new_setelems_begin(sd_netlink *nfnl, sd_netlink_message
         return r;
 }
 
-int sd_nfnl_nft_message_del_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
-                                           int family, const char *table, const char *set_name) {
+int sd_nfnl_nft_message_del_setelems_begin(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *table,
+                const char *set_name) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = nft_message_new(nfnl, &m, family, NFT_MSG_DELSETELEM, NLM_F_ACK);
+        r = nft_message_new(nfnl, &m, family, NFT_MSG_DELSETELEM, 0);
         if (r < 0)
                 return r;
 
@@ -271,7 +278,9 @@ int sd_nfnl_nft_message_del_setelems_begin(sd_netlink *nfnl, sd_netlink_message
 }
 
 static int sd_nfnl_add_data(sd_netlink_message *m, uint16_t attr, const void *data, uint32_t dlen) {
-        int r = sd_netlink_message_open_container(m, attr);
+        int r;
+
+        r = sd_netlink_message_open_container(m, attr);
         if (r < 0)
                 return r;
 
@@ -282,9 +291,14 @@ static int sd_nfnl_add_data(sd_netlink_message *m, uint16_t attr, const void *da
         return sd_netlink_message_close_container(m); /* attr */
 }
 
-int sd_nfnl_nft_message_add_setelem(sd_netlink_message *m, uint32_t num,
-                                    const void *key, uint32_t klen,
-                                    const void *data, uint32_t dlen) {
+int sd_nfnl_nft_message_add_setelem(
+                sd_netlink_message *m,
+                uint32_t num,
+                const void *key,
+                uint32_t klen,
+                const void *data,
+                uint32_t dlen) {
+
         int r;
 
         r = sd_netlink_message_open_array(m, num);
@@ -301,7 +315,8 @@ int sd_nfnl_nft_message_add_setelem(sd_netlink_message *m, uint32_t num,
                         goto cancel;
         }
 
-        return r;
+        return 0;
+
 cancel:
         sd_netlink_message_cancel_array(m);
         return r;
similarity index 98%
rename from src/libsystemd/sd-netlink/rtnl-message.c
rename to src/libsystemd/sd-netlink/netlink-message-rtnl.c
index e771b95e0848bf5064d3592a4a94306d21766b7a..313857699a91d9c24c817c896c91a71c0b703ba0 100644 (file)
@@ -271,13 +271,13 @@ int sd_rtnl_message_new_route(sd_netlink *rtnl, sd_netlink_message **ret,
 }
 
 int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret,
-                                uint16_t nhmsg_type, int nh_family,
+                                uint16_t nlmsg_type, int nh_family,
                                 unsigned char nh_protocol) {
         struct nhmsg *nhm;
         int r;
 
-        assert_return(rtnl_message_type_is_nexthop(nhmsg_type), -EINVAL);
-        switch(nhmsg_type) {
+        assert_return(rtnl_message_type_is_nexthop(nlmsg_type), -EINVAL);
+        switch(nlmsg_type) {
         case RTM_DELNEXTHOP:
                 assert_return(nh_family == AF_UNSPEC, -EINVAL);
                 _fallthrough_;
@@ -288,15 +288,15 @@ 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);
 
-        r = message_new(rtnl, ret, nhmsg_type);
+        r = message_new(rtnl, ret, nlmsg_type);
         if (r < 0)
                 return r;
 
-        if (nhmsg_type == RTM_NEWNEXTHOP)
+        if (nlmsg_type == RTM_NEWNEXTHOP)
                 (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
 
         nhm = NLMSG_DATA((*ret)->hdr);
@@ -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 473d74b01a157939c86192816db52eb846c68c58..e422d5699c83306ff341383c62859d80a4bc0bdb 100644 (file)
 #define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
 #define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
 
-int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
+int message_new_empty(sd_netlink *nl, sd_netlink_message **ret) {
         sd_netlink_message *m;
 
-        assert_return(ret, -EINVAL);
+        assert(nl);
+        assert(ret);
 
-        /* Note that 'rtnl' is currently unused, if we start using it internally
-           we must take care to avoid problems due to mutual references between
-           buses and their queued messages. See sd-bus.
-         */
+        /* Note that 'nl' is currently unused, if we start using it internally we must take care to
+         * avoid problems due to mutual references between buses and their queued messages. See sd-bus. */
 
         m = new(sd_netlink_message, 1);
         if (!m)
@@ -36,48 +35,80 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
 
         *m = (sd_netlink_message) {
                 .n_ref = 1,
-                .protocol = rtnl->protocol,
+                .protocol = nl->protocol,
                 .sealed = false,
         };
 
         *ret = m;
-
         return 0;
 }
 
-int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) {
+int message_new_full(
+                sd_netlink *nl,
+                uint16_t nlmsg_type,
+                const NLTypeSystem *type_system,
+                size_t header_size,
+                sd_netlink_message **ret) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        const NLType *nl_type;
         size_t size;
         int r;
 
-        assert_return(rtnl, -EINVAL);
+        assert(nl);
+        assert(type_system);
+        assert(ret);
 
-        r = type_system_root_get_type(rtnl, &nl_type, type);
-        if (r < 0)
-                return r;
-
-        if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
-                return -EINVAL;
+        size = NLMSG_SPACE(header_size);
+        assert(size >= sizeof(struct nlmsghdr));
 
-        r = message_new_empty(rtnl, &m);
+        r = message_new_empty(nl, &m);
         if (r < 0)
                 return r;
 
-        size = NLMSG_SPACE(type_get_size(nl_type));
+        m->containers[0].type_system = type_system;
 
-        assert(size >= sizeof(struct nlmsghdr));
         m->hdr = malloc0(size);
         if (!m->hdr)
                 return -ENOMEM;
 
         m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
-
-        type_get_type_system(nl_type, &m->containers[0].type_system);
         m->hdr->nlmsg_len = size;
-        m->hdr->nlmsg_type = type;
+        m->hdr->nlmsg_type = nlmsg_type;
 
         *ret = TAKE_PTR(m);
+        return 0;
+}
+
+int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type) {
+        const NLTypeSystem *type_system;
+        size_t size;
+        int r;
+
+        assert_return(nl, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = type_system_root_get_type_system_and_header_size(nl, type, &type_system, &size);
+        if (r < 0)
+                return r;
+
+        return message_new_full(nl, type, type_system, size, ret);
+}
+
+int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret) {
+        struct nlmsgerr *err;
+        int r;
+
+        assert(error <= 0);
+
+        r = message_new(nl, ret, NLMSG_ERROR);
+        if (r < 0)
+                return r;
+
+        message_seal(*ret);
+        (*ret)->hdr->nlmsg_seq = serial;
+
+        err = NLMSG_DATA((*ret)->hdr);
+        err->error = error;
 
         return 0;
 }
@@ -184,13 +215,12 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
 
 static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) {
         const NLType *type;
-        int r;
 
         assert(m);
 
-        r = type_system_get_type(m->containers[m->n_containers].type_system, &type, attribute_type);
-        if (r < 0)
-                return r;
+        type = type_system_get_type(m->containers[m->n_containers].type_system, attribute_type);
+        if (!type)
+                return -EOPNOTSUPP;
 
         if (type_get_type(type) != data_type)
                 return -EINVAL;
@@ -538,7 +568,7 @@ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
         /* m->containers[m->n_containers + 1] is accessed both in read and write. Prevent access out of bound */
-        assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -ERANGE);
+        assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
 
         r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED);
         if (r < 0) {
@@ -553,22 +583,23 @@ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type
                 if (r < 0)
                         return r;
 
-                r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type);
-                if (r < 0)
-                        return r;
+                type_system_union = type_system_get_type_system_union(
+                                m->containers[m->n_containers].type_system,
+                                type);
+                if (!type_system_union)
+                        return -EOPNOTSUPP;
 
-                r = type_system_union_protocol_get_type_system(type_system_union,
-                                                               &m->containers[m->n_containers + 1].type_system,
-                                                               family);
-                if (r < 0)
-                        return r;
-        } else {
-                r = type_system_get_type_system(m->containers[m->n_containers].type_system,
-                                                &m->containers[m->n_containers + 1].type_system,
-                                                type);
-                if (r < 0)
-                        return r;
-        }
+                m->containers[m->n_containers + 1].type_system =
+                        type_system_union_get_type_system_by_protocol(
+                                type_system_union,
+                                family);
+        } else
+                m->containers[m->n_containers + 1].type_system =
+                        type_system_get_type_system(
+                                m->containers[m->n_containers].type_system,
+                                type);
+        if (!m->containers[m->n_containers + 1].type_system)
+                return -EOPNOTSUPP;
 
         r = add_rtattr(m, type | NLA_F_NESTED, NULL, size);
         if (r < 0)
@@ -585,19 +616,22 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
-        assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -ERANGE);
+        assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
 
-        r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type);
-        if (r < 0)
-                return r;
+        type_system_union = type_system_get_type_system_union(
+                        m->containers[m->n_containers].type_system,
+                        type);
+        if (!type_system_union)
+                return -EOPNOTSUPP;
 
-        r = type_system_union_get_type_system(type_system_union,
-                                              &m->containers[m->n_containers + 1].type_system,
-                                              key);
-        if (r < 0)
-                return r;
+        m->containers[m->n_containers + 1].type_system =
+                type_system_union_get_type_system_by_string(
+                        type_system_union,
+                        key);
+        if (!m->containers[m->n_containers + 1].type_system)
+                return -EOPNOTSUPP;
 
-        r = sd_netlink_message_append_string(m, type_system_union->match, key);
+        r = sd_netlink_message_append_string(m, type_system_union_get_match_attribute(type_system_union), key);
         if (r < 0)
                 return r;
 
@@ -628,7 +662,7 @@ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
-        assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -ERANGE);
+        assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
 
         r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
         if (r < 0)
@@ -673,7 +707,7 @@ static int netlink_message_read_internal(
         assert_return(m, -EINVAL);
         assert_return(m->sealed, -EPERM);
 
-        assert(m->n_containers < RTNL_CONTAINER_DEPTH);
+        assert(m->n_containers < NETLINK_CONTAINER_DEPTH);
 
         if (!m->containers[m->n_containers].attributes)
                 return -ENODATA;
@@ -1008,26 +1042,26 @@ int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container
         int r;
 
         assert_return(m, -EINVAL);
-        assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL);
+        assert_return(m->n_containers < NETLINK_CONTAINER_DEPTH, -EINVAL);
 
-        r = type_system_get_type(m->containers[m->n_containers].type_system,
-                                 &nl_type,
-                                 container_type);
-        if (r < 0)
-                return r;
+        nl_type = type_system_get_type(
+                        m->containers[m->n_containers].type_system,
+                        container_type);
+        if (!nl_type)
+                return -EOPNOTSUPP;
 
         if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
                 return -EINVAL;
 
-        r = type_system_get_type_system(m->containers[m->n_containers].type_system,
-                                        &type_system,
-                                        container_type);
-        if (r < 0)
-                return r;
+        type_system = type_system_get_type_system(
+                        m->containers[m->n_containers].type_system,
+                        container_type);
+        if (!type_system)
+                return -EOPNOTSUPP;
 
-        r = type_system_get_type(type_system, &nl_type, type_id);
-        if (r < 0)
-                return r;
+        nl_type = type_system_get_type(type_system, type_id);
+        if (!nl_type)
+                return -EOPNOTSUPP;
 
         if (type_get_type(nl_type) != NETLINK_TYPE_STRING)
                 return -EINVAL;
@@ -1079,7 +1113,7 @@ static int netlink_container_parse(sd_netlink_message *m,
                         return -ENOMEM;
 
                 if (attributes[type].offset != 0)
-                        log_debug("rtnl: message parse - overwriting repeated attribute");
+                        log_debug("sd-netlink: message parse - overwriting repeated attribute");
 
                 attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
                 attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED;
@@ -1104,66 +1138,67 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
         int r;
 
         assert_return(m, -EINVAL);
-        assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -EINVAL);
+        assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
 
-        r = type_system_get_type(m->containers[m->n_containers].type_system,
-                                 &nl_type,
-                                 type_id);
-        if (r < 0)
-                return r;
+        nl_type = type_system_get_type(
+                        m->containers[m->n_containers].type_system,
+                        type_id);
+        if (!nl_type)
+                return -EOPNOTSUPP;
 
         type = type_get_type(nl_type);
 
         if (type == NETLINK_TYPE_NESTED) {
-                r = type_system_get_type_system(m->containers[m->n_containers].type_system,
-                                                &type_system,
-                                                type_id);
-                if (r < 0)
-                        return r;
+                type_system = type_system_get_type_system(
+                                m->containers[m->n_containers].type_system,
+                                type_id);
+                if (!type_system)
+                        return -EOPNOTSUPP;
         } else if (type == NETLINK_TYPE_UNION) {
                 const NLTypeSystemUnion *type_system_union;
 
-                r = type_system_get_type_system_union(m->containers[m->n_containers].type_system,
-                                                      &type_system_union,
-                                                      type_id);
-                if (r < 0)
-                        return r;
+                type_system_union = type_system_get_type_system_union(
+                                 m->containers[m->n_containers].type_system,
+                                 type_id);
+                if (!type_system_union)
+                        return -EOPNOTSUPP;
 
-                switch (type_system_union->match_type) {
-                case NL_MATCH_SIBLING:
-                {
+                switch (type_system_union_get_match_type(type_system_union)) {
+                case NL_MATCH_SIBLING: {
                         const char *key;
 
-                        r = sd_netlink_message_read_string(m, type_system_union->match, &key);
+                        r = sd_netlink_message_read_string(
+                                        m,
+                                        type_system_union_get_match_attribute(type_system_union),
+                                        &key);
                         if (r < 0)
                                 return r;
 
-                        r = type_system_union_get_type_system(type_system_union,
-                                                              &type_system,
-                                                              key);
-                        if (r < 0)
-                                return r;
+                        type_system = type_system_union_get_type_system_by_string(
+                                        type_system_union,
+                                        key);
+                        if (!type_system)
+                                return -EOPNOTSUPP;
 
                         break;
                 }
-                case NL_MATCH_PROTOCOL:
-                {
+                case NL_MATCH_PROTOCOL: {
                         int family;
 
                         r = sd_rtnl_message_get_family(m, &family);
                         if (r < 0)
                                 return r;
 
-                        r = type_system_union_protocol_get_type_system(type_system_union,
-                                                                       &type_system,
-                                                                       family);
-                        if (r < 0)
-                                return r;
+                        type_system = type_system_union_get_type_system_by_protocol(
+                                        type_system_union,
+                                        family);
+                        if (!type_system)
+                                return -EOPNOTSUPP;
 
                         break;
                 }
                 default:
-                        assert_not_reached("sd-netlink: invalid type system union type");
+                        assert_not_reached();
                 }
         } else
                 return -EINVAL;
@@ -1195,7 +1230,7 @@ int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type_id
         int r;
 
         assert_return(m, -EINVAL);
-        assert_return(m->n_containers < (RTNL_CONTAINER_DEPTH - 1), -EINVAL);
+        assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
 
         r = netlink_message_read_internal(m, type_id, &container, NULL);
         if (r < 0)
@@ -1231,7 +1266,7 @@ int sd_netlink_message_exit_container(sd_netlink_message *m) {
         return 0;
 }
 
-uint32_t rtnl_message_get_serial(sd_netlink_message *m) {
+uint32_t message_get_serial(sd_netlink_message *m) {
         assert(m);
         assert(m->hdr);
 
@@ -1280,18 +1315,15 @@ static int netlink_message_parse_error(sd_netlink_message *m) {
                                        NLMSG_PAYLOAD(m->hdr, hlen));
 }
 
-int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl) {
-        const NLType *nl_type;
-        uint16_t type;
+int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) {
         size_t size;
         int r;
 
         assert_return(m, -EINVAL);
-        assert_return(genl || m->protocol != NETLINK_GENERIC, -EINVAL);
+        assert_return(nl, -EINVAL);
 
         /* don't allow appending to message once parsed */
-        if (!m->sealed)
-                rtnl_message_seal(m);
+        message_seal(m);
 
         for (unsigned i = 1; i <= m->n_containers; i++)
                 m->containers[i].attributes = mfree(m->containers[i].attributes);
@@ -1304,37 +1336,22 @@ int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl) {
 
         assert(m->hdr);
 
-        r = type_system_root_get_type(genl, &nl_type, m->hdr->nlmsg_type);
+        r = type_system_root_get_type_system_and_header_size(nl, m->hdr->nlmsg_type,
+                                                             &m->containers[0].type_system, &size);
         if (r < 0)
                 return r;
 
-        type = type_get_type(nl_type);
-        size = type_get_size(nl_type);
-
-        if (type == NETLINK_TYPE_NESTED) {
-                const NLTypeSystem *type_system;
-
-                type_get_type_system(nl_type, &type_system);
-
-                m->containers[0].type_system = type_system;
-
-                if (sd_netlink_message_is_error(m))
-                        r = netlink_message_parse_error(m);
-                else
-                        r = netlink_container_parse(m,
-                                                    &m->containers[m->n_containers],
-                                                    (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
-                                                    NLMSG_PAYLOAD(m->hdr, size));
-                if (r < 0)
-                        return r;
-        }
+        if (sd_netlink_message_is_error(m))
+                return netlink_message_parse_error(m);
 
-        return 0;
+        return netlink_container_parse(m,
+                                       &m->containers[0],
+                                       (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
+                                       NLMSG_PAYLOAD(m->hdr, size));
 }
 
-void rtnl_message_seal(sd_netlink_message *m) {
+void message_seal(sd_netlink_message *m) {
         assert(m);
-        assert(!m->sealed);
 
         m->sealed = true;
 }
index b6de545fe26fc6506dd5ab6dda02f6976256581b..34f527d07f5949fa7b65fe5af6f8a4b8859ea641 100644 (file)
@@ -70,29 +70,15 @@ void netlink_slot_disconnect(sd_netlink_slot *slot, bool unref) {
         case NETLINK_MATCH_CALLBACK:
                 LIST_REMOVE(match_callbacks, nl->match_callbacks, &slot->match_callback);
 
-                switch (slot->match_callback.type) {
-                case RTM_NEWLINK:
-                case RTM_DELLINK:
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_LINK);
-
-                        break;
-                case RTM_NEWADDR:
-                case RTM_DELADDR:
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_IFADDR);
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_IFADDR);
-
-                        break;
-                case RTM_NEWROUTE:
-                case RTM_DELROUTE:
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_ROUTE);
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_ROUTE);
-
-                        break;
-                }
+                for (size_t i = 0; i < slot->match_callback.n_groups; i++)
+                        (void) socket_broadcast_group_unref(nl, slot->match_callback.groups[i]);
+
+                slot->match_callback.n_groups = 0;
+                slot->match_callback.groups = mfree(slot->match_callback.groups);
 
                 break;
         default:
-                assert_not_reached("Wut? Unknown slot type?");
+                assert_not_reached();
         }
 
         slot->type = _NETLINK_SLOT_INVALID;
index 21ec00cca073e6048d2edde0283ac841efff5359..15c2789beee457eeec2cd255c2117de701be279c 100644 (file)
@@ -238,7 +238,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_gr
 
         n = recvmsg_safe(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
         if (n == -ENOBUFS)
-                return log_debug_errno(n, "rtnl: kernel receive buffer overrun");
+                return log_debug_errno(n, "sd-netlink: kernel receive buffer overrun");
         if (IN_SET(n, -EAGAIN, -EINTR))
                 return 0;
         if (n < 0)
@@ -246,7 +246,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_gr
 
         if (sender.nl.nl_pid != 0) {
                 /* not from the kernel, ignore */
-                log_debug("rtnl: ignoring message from PID %"PRIu32, sender.nl.nl_pid);
+                log_debug("sd-netlink: ignoring message from PID %"PRIu32, sender.nl.nl_pid);
 
                 if (peek) {
                         /* drop the message */
@@ -276,7 +276,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_gr
  * If nothing useful was received 0 is returned.
  * On failure, a negative error code is returned.
  */
-int socket_read_message(sd_netlink *rtnl) {
+int socket_read_message(sd_netlink *nl) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *first = NULL;
         bool multi_part = false, done = false;
         size_t len, allocated;
@@ -285,25 +285,25 @@ int socket_read_message(sd_netlink *rtnl) {
         unsigned i = 0;
         int r;
 
-        assert(rtnl);
-        assert(rtnl->rbuffer);
+        assert(nl);
+        assert(nl->rbuffer);
 
         /* read nothing, just get the pending message size */
-        r = socket_recv_message(rtnl->fd, &iov, NULL, true);
+        r = socket_recv_message(nl->fd, &iov, NULL, true);
         if (r <= 0)
                 return r;
         else
                 len = (size_t) r;
 
         /* make room for the pending message */
-        if (!greedy_realloc((void **)&rtnl->rbuffer, len, sizeof(uint8_t)))
+        if (!greedy_realloc((void**) &nl->rbuffer, len, sizeof(uint8_t)))
                 return -ENOMEM;
 
-        allocated = MALLOC_SIZEOF_SAFE(rtnl->rbuffer);
-        iov = IOVEC_MAKE(rtnl->rbuffer, allocated);
+        allocated = MALLOC_SIZEOF_SAFE(nl->rbuffer);
+        iov = IOVEC_MAKE(nl->rbuffer, allocated);
 
         /* read the pending message */
-        r = socket_recv_message(rtnl->fd, &iov, &group, false);
+        r = socket_recv_message(nl->fd, &iov, &group, false);
         if (r <= 0)
                 return r;
         else
@@ -313,22 +313,22 @@ int socket_read_message(sd_netlink *rtnl) {
                 /* message did not fit in read buffer */
                 return -EIO;
 
-        if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
+        if (NLMSG_OK(nl->rbuffer, len) && nl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
                 multi_part = true;
 
-                for (i = 0; i < rtnl->rqueue_partial_size; i++)
-                        if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
-                            rtnl->rbuffer->nlmsg_seq) {
-                                first = rtnl->rqueue_partial[i];
+                for (i = 0; i < nl->rqueue_partial_size; i++)
+                        if (message_get_serial(nl->rqueue_partial[i]) ==
+                            nl->rbuffer->nlmsg_seq) {
+                                first = nl->rqueue_partial[i];
                                 break;
                         }
         }
 
-        for (struct nlmsghdr *new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
+        for (struct nlmsghdr *new_msg = nl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
                 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-                const NLType *nl_type;
+                size_t size;
 
-                if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
+                if (!group && new_msg->nlmsg_pid != nl->sockaddr.nl.nl_pid)
                         /* not broadcast and not for us */
                         continue;
 
@@ -346,7 +346,7 @@ int socket_read_message(sd_netlink *rtnl) {
                 }
 
                 /* check that we support this message type */
-                r = type_system_root_get_type(rtnl, &nl_type, new_msg->nlmsg_type);
+                r = type_system_root_get_type_system_and_header_size(nl, new_msg->nlmsg_type, NULL, &size);
                 if (r < 0) {
                         if (r == -EOPNOTSUPP)
                                 log_debug("sd-netlink: ignored message with unknown type: %i",
@@ -356,12 +356,12 @@ int socket_read_message(sd_netlink *rtnl) {
                 }
 
                 /* check that the size matches the message type */
-                if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
+                if (new_msg->nlmsg_len < NLMSG_LENGTH(size)) {
                         log_debug("sd-netlink: message is shorter than expected, dropping");
                         continue;
                 }
 
-                r = message_new_empty(rtnl, &m);
+                r = message_new_empty(nl, &m);
                 if (r < 0)
                         return r;
 
@@ -372,7 +372,7 @@ int socket_read_message(sd_netlink *rtnl) {
                         return -ENOMEM;
 
                 /* seal and parse the top-level message */
-                r = sd_netlink_message_rewind(m, rtnl);
+                r = sd_netlink_message_rewind(m, nl);
                 if (r < 0)
                         return r;
 
@@ -390,31 +390,31 @@ int socket_read_message(sd_netlink *rtnl) {
 
         if (!multi_part || done) {
                 /* we got a complete message, push it on the read queue */
-                r = rtnl_rqueue_make_room(rtnl);
+                r = netlink_rqueue_make_room(nl);
                 if (r < 0)
                         return r;
 
-                rtnl->rqueue[rtnl->rqueue_size++] = TAKE_PTR(first);
+                nl->rqueue[nl->rqueue_size++] = TAKE_PTR(first);
 
-                if (multi_part && (i < rtnl->rqueue_partial_size)) {
+                if (multi_part && (i < nl->rqueue_partial_size)) {
                         /* remove the message form the partial read queue */
-                        memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
-                                sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
-                        rtnl->rqueue_partial_size--;
+                        memmove(nl->rqueue_partial + i, nl->rqueue_partial + i + 1,
+                                sizeof(sd_netlink_message*) * (nl->rqueue_partial_size - i - 1));
+                        nl->rqueue_partial_size--;
                 }
 
                 return 1;
         } else {
                 /* we only got a partial multi-part message, push it on the
                    partial read queue */
-                if (i < rtnl->rqueue_partial_size)
-                        rtnl->rqueue_partial[i] = TAKE_PTR(first);
+                if (i < nl->rqueue_partial_size)
+                        nl->rqueue_partial[i] = TAKE_PTR(first);
                 else {
-                        r = rtnl_rqueue_partial_make_room(rtnl);
+                        r = netlink_rqueue_partial_make_room(nl);
                         if (r < 0)
                                 return r;
 
-                        rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = TAKE_PTR(first);
+                        nl->rqueue_partial[nl->rqueue_partial_size++] = TAKE_PTR(first);
                 }
 
                 return 0;
diff --git a/src/libsystemd/sd-netlink/netlink-types-genl.c b/src/libsystemd/sd-netlink/netlink-types-genl.c
new file mode 100644 (file)
index 0000000..0e8783b
--- /dev/null
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <linux/batman_adv.h>
+#include <linux/fou.h>
+#include <linux/genetlink.h>
+#include <linux/if.h>
+#include <linux/if_macsec.h>
+#include <linux/l2tp.h>
+#include <linux/nl80211.h>
+#include <linux/wireguard.h>
+
+#include "netlink-genl.h"
+#include "netlink-types-internal.h"
+
+/***************** genl ctrl type systems *****************/
+static const NLType genl_ctrl_mcast_group_types[] = {
+        [CTRL_ATTR_MCAST_GRP_NAME]  = { .type = NETLINK_TYPE_STRING },
+        [CTRL_ATTR_MCAST_GRP_ID]    = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(genl_ctrl_mcast_group);
+
+static const NLType genl_ctrl_ops_types[] = {
+        [CTRL_ATTR_OP_ID]           = { .type = NETLINK_TYPE_U32 },
+        [CTRL_ATTR_OP_FLAGS]        = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(genl_ctrl_ops);
+
+static const NLType genl_ctrl_types[] = {
+        [CTRL_ATTR_FAMILY_ID]    = { .type = NETLINK_TYPE_U16 },
+        [CTRL_ATTR_FAMILY_NAME]  = { .type = NETLINK_TYPE_STRING },
+        [CTRL_ATTR_VERSION]      = { .type = NETLINK_TYPE_U32 },
+        [CTRL_ATTR_HDRSIZE]      = { .type = NETLINK_TYPE_U32 },
+        [CTRL_ATTR_MAXATTR]      = { .type = NETLINK_TYPE_U32 },
+        [CTRL_ATTR_OPS]          = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_ops_type_system },
+        [CTRL_ATTR_MCAST_GROUPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_mcast_group_type_system },
+        /*
+        [CTRL_ATTR_POLICY]       = { .type = NETLINK_TYPE_NESTED, },
+        [CTRL_ATTR_OP_POLICY]    = { .type = NETLINK_TYPE_NESTED, }
+        */
+        [CTRL_ATTR_OP]           = { .type = NETLINK_TYPE_U32 },
+};
+
+/***************** genl batadv type systems *****************/
+static const NLType genl_batadv_types[] = {
+        [BATADV_ATTR_VERSION]                       = { .type = NETLINK_TYPE_STRING },
+        [BATADV_ATTR_ALGO_NAME]                     = { .type = NETLINK_TYPE_STRING },
+        [BATADV_ATTR_MESH_IFINDEX]                  = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_MESH_IFNAME]                   = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
+        [BATADV_ATTR_MESH_ADDRESS]                  = { .size = ETH_ALEN },
+        [BATADV_ATTR_HARD_IFINDEX]                  = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_HARD_IFNAME]                   = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
+        [BATADV_ATTR_HARD_ADDRESS]                  = { .size = ETH_ALEN },
+        [BATADV_ATTR_ORIG_ADDRESS]                  = { .size = ETH_ALEN },
+        [BATADV_ATTR_TPMETER_RESULT]                = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_TPMETER_TEST_TIME]             = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_TPMETER_BYTES]                 = { .type = NETLINK_TYPE_U64 },
+        [BATADV_ATTR_TPMETER_COOKIE]                = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_PAD]                           = { .type = NETLINK_TYPE_UNSPEC },
+        [BATADV_ATTR_ACTIVE]                        = { .type = NETLINK_TYPE_FLAG },
+        [BATADV_ATTR_TT_ADDRESS]                    = { .size = ETH_ALEN },
+        [BATADV_ATTR_TT_TTVN]                       = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_TT_LAST_TTVN]                  = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_TT_CRC32]                      = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_TT_VID]                        = { .type = NETLINK_TYPE_U16 },
+        [BATADV_ATTR_TT_FLAGS]                      = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_FLAG_BEST]                     = { .type = NETLINK_TYPE_FLAG },
+        [BATADV_ATTR_LAST_SEEN_MSECS]               = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_NEIGH_ADDRESS]                 = { .size = ETH_ALEN },
+        [BATADV_ATTR_TQ]                            = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_THROUGHPUT]                    = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_BANDWIDTH_UP]                  = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_BANDWIDTH_DOWN]                = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_ROUTER]                        = { .size = ETH_ALEN },
+        [BATADV_ATTR_BLA_OWN]                       = { .type = NETLINK_TYPE_FLAG },
+        [BATADV_ATTR_BLA_ADDRESS]                   = { .size = ETH_ALEN },
+        [BATADV_ATTR_BLA_VID]                       = { .type = NETLINK_TYPE_U16 },
+        [BATADV_ATTR_BLA_BACKBONE]                  = { .size = ETH_ALEN },
+        [BATADV_ATTR_BLA_CRC]                       = { .type = NETLINK_TYPE_U16 },
+        [BATADV_ATTR_DAT_CACHE_IP4ADDRESS]          = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_DAT_CACHE_HWADDRESS]           = { .size = ETH_ALEN },
+        [BATADV_ATTR_DAT_CACHE_VID]                 = { .type = NETLINK_TYPE_U16 },
+        [BATADV_ATTR_MCAST_FLAGS]                   = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_MCAST_FLAGS_PRIV]              = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_VLANID]                        = { .type = NETLINK_TYPE_U16 },
+        [BATADV_ATTR_AGGREGATED_OGMS_ENABLED]       = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_AP_ISOLATION_ENABLED]          = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_ISOLATION_MARK]                = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_ISOLATION_MASK]                = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_BONDING_ENABLED]               = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_FRAGMENTATION_ENABLED]         = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_GW_BANDWIDTH_DOWN]             = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_GW_BANDWIDTH_UP]               = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_GW_MODE]                       = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_GW_SEL_CLASS]                  = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_HOP_PENALTY]                   = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_LOG_LEVEL]                     = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED]  = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_MULTICAST_FANOUT]              = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_NETWORK_CODING_ENABLED]        = { .type = NETLINK_TYPE_U8 },
+        [BATADV_ATTR_ORIG_INTERVAL]                 = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_ELP_INTERVAL]                  = { .type = NETLINK_TYPE_U32 },
+        [BATADV_ATTR_THROUGHPUT_OVERRIDE]           = { .type = NETLINK_TYPE_U32 },
+};
+
+/***************** genl fou type systems *****************/
+static const NLType genl_fou_types[] = {
+        [FOU_ATTR_PORT]              = { .type = NETLINK_TYPE_U16 },
+        [FOU_ATTR_AF]                = { .type = NETLINK_TYPE_U8 },
+        [FOU_ATTR_IPPROTO]           = { .type = NETLINK_TYPE_U8 },
+        [FOU_ATTR_TYPE]              = { .type = NETLINK_TYPE_U8 },
+        [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
+        [FOU_ATTR_LOCAL_V4]          = { .type = NETLINK_TYPE_IN_ADDR },
+        [FOU_ATTR_PEER_V4]           = { .type = NETLINK_TYPE_IN_ADDR },
+        [FOU_ATTR_LOCAL_V6]          = { .type = NETLINK_TYPE_IN_ADDR },
+        [FOU_ATTR_PEER_V6]           = { .type = NETLINK_TYPE_IN_ADDR},
+        [FOU_ATTR_PEER_PORT]         = { .type = NETLINK_TYPE_U16},
+        [FOU_ATTR_IFINDEX]           = { .type = NETLINK_TYPE_U32},
+};
+
+/***************** genl l2tp type systems *****************/
+static const NLType genl_l2tp_types[] = {
+        [L2TP_ATTR_PW_TYPE]           = { .type = NETLINK_TYPE_U16 },
+        [L2TP_ATTR_ENCAP_TYPE]        = { .type = NETLINK_TYPE_U16 },
+        [L2TP_ATTR_OFFSET]            = { .type = NETLINK_TYPE_U16 },
+        [L2TP_ATTR_DATA_SEQ]          = { .type = NETLINK_TYPE_U16 },
+        [L2TP_ATTR_L2SPEC_TYPE]       = { .type = NETLINK_TYPE_U8 },
+        [L2TP_ATTR_L2SPEC_LEN]        = { .type = NETLINK_TYPE_U8 },
+        [L2TP_ATTR_PROTO_VERSION]     = { .type = NETLINK_TYPE_U8 },
+        [L2TP_ATTR_IFNAME]            = { .type = NETLINK_TYPE_STRING },
+        [L2TP_ATTR_CONN_ID]           = { .type = NETLINK_TYPE_U32 },
+        [L2TP_ATTR_PEER_CONN_ID]      = { .type = NETLINK_TYPE_U32 },
+        [L2TP_ATTR_SESSION_ID]        = { .type = NETLINK_TYPE_U32 },
+        [L2TP_ATTR_PEER_SESSION_ID]   = { .type = NETLINK_TYPE_U32 },
+        [L2TP_ATTR_UDP_CSUM]          = { .type = NETLINK_TYPE_U8 },
+        [L2TP_ATTR_VLAN_ID]           = { .type = NETLINK_TYPE_U16 },
+        [L2TP_ATTR_RECV_SEQ]          = { .type = NETLINK_TYPE_U8 },
+        [L2TP_ATTR_SEND_SEQ]          = { .type = NETLINK_TYPE_U8 },
+        [L2TP_ATTR_LNS_MODE]          = { .type = NETLINK_TYPE_U8 },
+        [L2TP_ATTR_USING_IPSEC]       = { .type = NETLINK_TYPE_U8 },
+        [L2TP_ATTR_FD]                = { .type = NETLINK_TYPE_U32 },
+        [L2TP_ATTR_IP_SADDR]          = { .type = NETLINK_TYPE_IN_ADDR },
+        [L2TP_ATTR_IP_DADDR]          = { .type = NETLINK_TYPE_IN_ADDR },
+        [L2TP_ATTR_UDP_SPORT]         = { .type = NETLINK_TYPE_U16 },
+        [L2TP_ATTR_UDP_DPORT]         = { .type = NETLINK_TYPE_U16 },
+        [L2TP_ATTR_IP6_SADDR]         = { .type = NETLINK_TYPE_IN_ADDR },
+        [L2TP_ATTR_IP6_DADDR]         = { .type = NETLINK_TYPE_IN_ADDR },
+        [L2TP_ATTR_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_FLAG },
+        [L2TP_ATTR_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_FLAG },
+};
+
+/***************** genl macsec type systems *****************/
+static const NLType genl_macsec_rxsc_types[] = {
+        [MACSEC_RXSC_ATTR_SCI] = { .type = NETLINK_TYPE_U64 },
+};
+
+DEFINE_TYPE_SYSTEM(genl_macsec_rxsc);
+
+static const NLType genl_macsec_sa_types[] = {
+        [MACSEC_SA_ATTR_AN]     = { .type = NETLINK_TYPE_U8 },
+        [MACSEC_SA_ATTR_ACTIVE] = { .type = NETLINK_TYPE_U8 },
+        [MACSEC_SA_ATTR_PN]     = { .type = NETLINK_TYPE_U32 },
+        [MACSEC_SA_ATTR_KEYID]  = { .size = MACSEC_KEYID_LEN },
+        [MACSEC_SA_ATTR_KEY]    = { .size = MACSEC_MAX_KEY_LEN },
+};
+
+DEFINE_TYPE_SYSTEM(genl_macsec_sa);
+
+static const NLType genl_macsec_types[] = {
+        [MACSEC_ATTR_IFINDEX]     = { .type = NETLINK_TYPE_U32 },
+        [MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsc_type_system },
+        [MACSEC_ATTR_SA_CONFIG]   = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_type_system },
+};
+
+/***************** genl nl80211 type systems *****************/
+static const NLType genl_nl80211_types[] = {
+        [NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+        [NL80211_ATTR_MAC]     = { .type = NETLINK_TYPE_ETHER_ADDR },
+        [NL80211_ATTR_SSID]    = { .type = NETLINK_TYPE_STRING },
+        [NL80211_ATTR_IFTYPE]  = { .type = NETLINK_TYPE_U32 },
+};
+
+/***************** genl wireguard type systems *****************/
+static const NLType genl_wireguard_allowedip_types[] = {
+        [WGALLOWEDIP_A_FAMILY]    = { .type = NETLINK_TYPE_U16 },
+        [WGALLOWEDIP_A_IPADDR]    = { .type = NETLINK_TYPE_IN_ADDR },
+        [WGALLOWEDIP_A_CIDR_MASK] = { .type = NETLINK_TYPE_U8 },
+};
+
+DEFINE_TYPE_SYSTEM(genl_wireguard_allowedip);
+
+static const NLType genl_wireguard_peer_types[] = {
+        [WGPEER_A_PUBLIC_KEY]                    = { .size = WG_KEY_LEN },
+        [WGPEER_A_FLAGS]                         = { .type = NETLINK_TYPE_U32 },
+        [WGPEER_A_PRESHARED_KEY]                 = { .size = WG_KEY_LEN },
+        [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U16 },
+        [WGPEER_A_ENDPOINT]                      = { .type = NETLINK_TYPE_SOCKADDR },
+        [WGPEER_A_ALLOWEDIPS]                    = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_allowedip_type_system },
+};
+
+DEFINE_TYPE_SYSTEM(genl_wireguard_peer);
+
+static const NLType genl_wireguard_types[] = {
+        [WGDEVICE_A_IFINDEX]     = { .type = NETLINK_TYPE_U32 },
+        [WGDEVICE_A_IFNAME]      = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
+        [WGDEVICE_A_FLAGS]       = { .type = NETLINK_TYPE_U32 },
+        [WGDEVICE_A_PRIVATE_KEY] = { .size = WG_KEY_LEN },
+        [WGDEVICE_A_LISTEN_PORT] = { .type = NETLINK_TYPE_U16 },
+        [WGDEVICE_A_FWMARK]      = { .type = NETLINK_TYPE_U32 },
+        [WGDEVICE_A_PEERS]       = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system },
+};
+
+/***************** genl families *****************/
+static const NLTypeSystemUnionElement genl_type_systems[] = {
+        { .name = CTRL_GENL_NAME,    .type_system = TYPE_SYSTEM_FROM_TYPE(genl_ctrl),      },
+        { .name = BATADV_NL_NAME,    .type_system = TYPE_SYSTEM_FROM_TYPE(genl_batadv),    },
+        { .name = FOU_GENL_NAME,     .type_system = TYPE_SYSTEM_FROM_TYPE(genl_fou),       },
+        { .name = L2TP_GENL_NAME,    .type_system = TYPE_SYSTEM_FROM_TYPE(genl_l2tp),      },
+        { .name = MACSEC_GENL_NAME,  .type_system = TYPE_SYSTEM_FROM_TYPE(genl_macsec),    },
+        { .name = NL80211_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_nl80211),   },
+        { .name = WG_GENL_NAME,      .type_system = TYPE_SYSTEM_FROM_TYPE(genl_wireguard), },
+};
+
+/* This is the root type system union, so match_attribute is not necessary. */
+DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(genl, 0);
+
+const NLTypeSystem *genl_get_type_system_by_name(const char *name) {
+        return type_system_union_get_type_system_by_string(&genl_type_system_union, name);
+}
diff --git a/src/libsystemd/sd-netlink/netlink-types-internal.h b/src/libsystemd/sd-netlink/netlink-types-internal.h
new file mode 100644 (file)
index 0000000..16da6aa
--- /dev/null
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro.h"
+#include "netlink-types.h"
+
+struct NLType {
+        uint16_t type;
+        size_t size;
+        const NLTypeSystem *type_system;
+        const NLTypeSystemUnion *type_system_union;
+};
+
+struct NLTypeSystem {
+        uint16_t count;
+        const NLType *types;
+};
+
+typedef struct NLTypeSystemUnionElement {
+        union {
+                int protocol;
+                const char *name;
+        };
+        NLTypeSystem type_system;
+} NLTypeSystemUnionElement;
+
+struct NLTypeSystemUnion {
+        size_t count;
+        const NLTypeSystemUnionElement *elements;
+        NLMatchType match_type;
+        uint16_t match_attribute;
+};
+
+#define TYPE_SYSTEM_FROM_TYPE(name)                                     \
+        { .count = ELEMENTSOF(name##_types), .types = name##_types }
+#define DEFINE_TYPE_SYSTEM(name)                                        \
+        static const NLTypeSystem name##_type_system = TYPE_SYSTEM_FROM_TYPE(name)
+
+#define _DEFINE_TYPE_SYSTEM_UNION(name, type, attr)                     \
+        static const NLTypeSystemUnion name##_type_system_union = {     \
+                .count = ELEMENTSOF(name##_type_systems),               \
+                .elements = name##_type_systems,                        \
+                .match_type = type,                                     \
+                .match_attribute = attr,                                \
+        }
+#define DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(name)           \
+        _DEFINE_TYPE_SYSTEM_UNION(name, NL_MATCH_PROTOCOL, 0)
+#define DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(name, attr)      \
+        _DEFINE_TYPE_SYSTEM_UNION(name, NL_MATCH_SIBLING, attr)
diff --git a/src/libsystemd/sd-netlink/netlink-types-nfnl.c b/src/libsystemd/sd-netlink/netlink-types-nfnl.c
new file mode 100644 (file)
index 0000000..1ba134a
--- /dev/null
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nfnetlink.h>
+
+#include "netlink-types-internal.h"
+#include "string-table.h"
+
+static const NLType nfnl_nft_table_types[] = {
+        [NFTA_TABLE_NAME]  = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+        [NFTA_TABLE_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_table);
+
+static const NLType nfnl_nft_chain_hook_types[] = {
+        [NFTA_HOOK_HOOKNUM]  = { .type = NETLINK_TYPE_U32 },
+        [NFTA_HOOK_PRIORITY] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_HOOK_DEV]      = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_chain_hook);
+
+static const NLType nfnl_nft_chain_types[] = {
+        [NFTA_CHAIN_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+        [NFTA_CHAIN_NAME]  = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+        [NFTA_CHAIN_HOOK]  = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_hook_type_system },
+        [NFTA_CHAIN_TYPE]  = { .type = NETLINK_TYPE_STRING, .size = 16 },
+        [NFTA_CHAIN_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_chain);
+
+static const NLType nfnl_nft_expr_meta_types[] = {
+        [NFTA_META_DREG] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_META_KEY]  = { .type = NETLINK_TYPE_U32 },
+        [NFTA_META_SREG] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_expr_payload_types[] = {
+        [NFTA_PAYLOAD_DREG]   = { .type = NETLINK_TYPE_U32 },
+        [NFTA_PAYLOAD_BASE]   = { .type = NETLINK_TYPE_U32 },
+        [NFTA_PAYLOAD_OFFSET] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_PAYLOAD_LEN]    = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_expr_nat_types[] = {
+        [NFTA_NAT_TYPE]          = { .type = NETLINK_TYPE_U32 },
+        [NFTA_NAT_FAMILY]        = { .type = NETLINK_TYPE_U32 },
+        [NFTA_NAT_REG_ADDR_MIN]  = { .type = NETLINK_TYPE_U32 },
+        [NFTA_NAT_REG_ADDR_MAX]  = { .type = NETLINK_TYPE_U32 },
+        [NFTA_NAT_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_NAT_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_NAT_FLAGS]         = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_data_types[] = {
+        [NFTA_DATA_VALUE] = { .type = NETLINK_TYPE_BINARY },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_data);
+
+static const NLType nfnl_nft_expr_bitwise_types[] = {
+        [NFTA_BITWISE_SREG] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_BITWISE_DREG] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_BITWISE_LEN]  = { .type = NETLINK_TYPE_U32 },
+        [NFTA_BITWISE_MASK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+        [NFTA_BITWISE_XOR]  = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+};
+
+static const NLType nfnl_nft_expr_cmp_types[] = {
+        [NFTA_CMP_SREG] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_CMP_OP]   = { .type = NETLINK_TYPE_U32 },
+        [NFTA_CMP_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+};
+
+static const NLType nfnl_nft_expr_fib_types[] = {
+        [NFTA_FIB_DREG]   = { .type = NETLINK_TYPE_U32 },
+        [NFTA_FIB_RESULT] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_FIB_FLAGS]  = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_expr_lookup_types[] = {
+        [NFTA_LOOKUP_SET]   = { .type = NETLINK_TYPE_STRING },
+        [NFTA_LOOKUP_SREG]  = { .type = NETLINK_TYPE_U32 },
+        [NFTA_LOOKUP_DREG]  = { .type = NETLINK_TYPE_U32 },
+        [NFTA_LOOKUP_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType nfnl_nft_expr_masq_types[] = {
+        [NFTA_MASQ_FLAGS]         = { .type = NETLINK_TYPE_U32 },
+        [NFTA_MASQ_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
+        [NFTA_MASQ_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLTypeSystemUnionElement nfnl_expr_data_type_systems[] = {
+        { .name = "bitwise", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_bitwise), },
+        { .name = "cmp",     .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_cmp),     },
+        { .name = "fib",     .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_fib),     },
+        { .name = "lookup",  .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_lookup),  },
+        { .name = "masq",    .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_masq),    },
+        { .name = "meta",    .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_meta),    },
+        { .name = "nat",     .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_nat),     },
+        { .name = "payload", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_payload), },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(nfnl_expr_data, NFTA_EXPR_NAME);
+
+static const NLType nfnl_nft_rule_expr_types[] = {
+        [NFTA_EXPR_NAME] = { .type = NETLINK_TYPE_STRING, .size = 16 },
+        [NFTA_EXPR_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &nfnl_expr_data_type_system_union },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_rule_expr);
+
+static const NLType nfnl_nft_rule_types[] = {
+        [NFTA_RULE_TABLE]       = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+        [NFTA_RULE_CHAIN]       = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+        [NFTA_RULE_EXPRESSIONS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_expr_type_system }
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_rule);
+
+static const NLType nfnl_nft_set_types[] = {
+        [NFTA_SET_TABLE]      = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+        [NFTA_SET_NAME]       = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+        [NFTA_SET_FLAGS]      = { .type = NETLINK_TYPE_U32 },
+        [NFTA_SET_KEY_TYPE]   = { .type = NETLINK_TYPE_U32 },
+        [NFTA_SET_KEY_LEN]    = { .type = NETLINK_TYPE_U32 },
+        [NFTA_SET_DATA_TYPE]  = { .type = NETLINK_TYPE_U32 },
+        [NFTA_SET_DATA_LEN]   = { .type = NETLINK_TYPE_U32 },
+        [NFTA_SET_POLICY]     = { .type = NETLINK_TYPE_U32 },
+        [NFTA_SET_ID]         = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_set);
+
+static const NLType nfnl_nft_setelem_types[] = {
+        [NFTA_SET_ELEM_KEY]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+        [NFTA_SET_ELEM_DATA]  = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+        [NFTA_SET_ELEM_FLAGS] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_setelem);
+
+static const NLType nfnl_nft_setelem_list_types[] = {
+        [NFTA_SET_ELEM_LIST_TABLE]    = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+        [NFTA_SET_ELEM_LIST_SET]      = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
+        [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_type_system },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_nft_setelem_list);
+
+static const NLType nfnl_subsys_nft_types [] = {
+        [NFT_MSG_DELTABLE]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system,        .size = sizeof(struct nfgenmsg) },
+        [NFT_MSG_NEWTABLE]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system,        .size = sizeof(struct nfgenmsg) },
+        [NFT_MSG_NEWCHAIN]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_type_system,        .size = sizeof(struct nfgenmsg) },
+        [NFT_MSG_NEWRULE]    = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_type_system,         .size = sizeof(struct nfgenmsg) },
+        [NFT_MSG_NEWSET]     = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_set_type_system,          .size = sizeof(struct nfgenmsg) },
+        [NFT_MSG_NEWSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
+        [NFT_MSG_DELSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_subsys_nft);
+
+static const NLType nfnl_msg_batch_types [] = {
+        [NFNL_BATCH_GENID] = { .type = NETLINK_TYPE_U32 }
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_msg_batch);
+
+static const NLType nfnl_subsys_none_types[] = {
+        [NFNL_MSG_BATCH_BEGIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
+        [NFNL_MSG_BATCH_END]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl_subsys_none);
+
+static const NLType nfnl_types[] = {
+        [NFNL_SUBSYS_NONE]     = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_subsys_none_type_system },
+        [NFNL_SUBSYS_NFTABLES] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_subsys_nft_type_system },
+};
+
+DEFINE_TYPE_SYSTEM(nfnl);
+
+const NLType *nfnl_get_type(uint16_t nlmsg_type) {
+        const NLTypeSystem *subsys;
+
+        subsys = type_system_get_type_system(&nfnl_type_system, nlmsg_type >> 8);
+        if (!subsys)
+                return NULL;
+
+        return type_system_get_type(subsys, nlmsg_type & ((1U << 8) - 1));
+}
diff --git a/src/libsystemd/sd-netlink/netlink-types-rtnl.c b/src/libsystemd/sd-netlink/netlink-types-rtnl.c
new file mode 100644 (file)
index 0000000..75046e9
--- /dev/null
@@ -0,0 +1,872 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <linux/batman_adv.h>
+#include <linux/can/netlink.h>
+#include <linux/can/vxcan.h>
+#include <linux/fib_rules.h>
+#include <linux/fou.h>
+#include <linux/if.h>
+#include <linux/if_addr.h>
+#include <linux/if_addrlabel.h>
+#include <linux/if_bridge.h>
+#include <linux/if_link.h>
+#include <linux/if_macsec.h>
+#include <linux/if_tunnel.h>
+#include <linux/ip.h>
+#include <linux/l2tp.h>
+#include <linux/netlink.h>
+#include <linux/nexthop.h>
+#include <linux/nl80211.h>
+#include <linux/pkt_sched.h>
+#include <linux/rtnetlink.h>
+#include <linux/veth.h>
+#include <linux/wireguard.h>
+
+#include "sd-netlink.h"
+
+#include "netlink-types-internal.h"
+#include "string-table.h"
+
+/* Maximum ARP IP target defined in kernel */
+#define BOND_MAX_ARP_TARGETS    16
+
+typedef enum {
+        BOND_ARP_TARGETS_0,
+        BOND_ARP_TARGETS_1,
+        BOND_ARP_TARGETS_2,
+        BOND_ARP_TARGETS_3,
+        BOND_ARP_TARGETS_4,
+        BOND_ARP_TARGETS_5,
+        BOND_ARP_TARGETS_6,
+        BOND_ARP_TARGETS_7,
+        BOND_ARP_TARGETS_8,
+        BOND_ARP_TARGETS_9,
+        BOND_ARP_TARGETS_10,
+        BOND_ARP_TARGETS_11,
+        BOND_ARP_TARGETS_12,
+        BOND_ARP_TARGETS_13,
+        BOND_ARP_TARGETS_14,
+        BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS,
+} BondArpTargets;
+
+static const NLTypeSystem rtnl_link_type_system;
+
+static const NLType rtnl_link_info_data_batadv_types[] = {
+        [IFLA_BATADV_ALGO_NAME] = { .type = NETLINK_TYPE_STRING, .size = 20 },
+};
+
+static const NLType rtnl_link_info_data_veth_types[] = {
+        [VETH_INFO_PEER]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+};
+
+static const NLType rtnl_link_info_data_vxcan_types[] = {
+        [VXCAN_INFO_PEER]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
+};
+
+static const NLType rtnl_link_info_data_ipvlan_types[] = {
+        [IFLA_IPVLAN_MODE]  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPVLAN_FLAGS]  = { .type = NETLINK_TYPE_U16 },
+};
+
+static const NLType rtnl_macvlan_macaddr_types[] = {
+        [IFLA_MACVLAN_MACADDR] = { .type = NETLINK_TYPE_ETHER_ADDR },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_macvlan_macaddr);
+
+static const NLType rtnl_link_info_data_macvlan_types[] = {
+        [IFLA_MACVLAN_MODE]              = { .type = NETLINK_TYPE_U32 },
+        [IFLA_MACVLAN_FLAGS]             = { .type = NETLINK_TYPE_U16 },
+        [IFLA_MACVLAN_MACADDR_MODE]      = { .type = NETLINK_TYPE_U32 },
+        [IFLA_MACVLAN_MACADDR_DATA]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_macvlan_macaddr_type_system },
+        [IFLA_MACVLAN_MACADDR_COUNT]     = { .type = NETLINK_TYPE_U32 },
+        [IFLA_MACVLAN_BC_QUEUE_LEN]      = { .type = NETLINK_TYPE_U32 },
+        [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = { .type = NETLINK_TYPE_REJECT },
+};
+
+static const NLType rtnl_link_info_data_bridge_types[] = {
+        [IFLA_BR_FORWARD_DELAY]              = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_HELLO_TIME]                 = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_MAX_AGE]                    = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_AGEING_TIME]                = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_STP_STATE]                  = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_PRIORITY]                   = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_VLAN_FILTERING]             = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_VLAN_PROTOCOL]              = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_GROUP_FWD_MASK]             = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_ROOT_PORT]                  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_ROOT_PATH_COST]             = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_TOPOLOGY_CHANGE]            = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_TOPOLOGY_CHANGE_DETECTED]   = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_HELLO_TIMER]                = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_TCN_TIMER]                  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_TOPOLOGY_CHANGE_TIMER]      = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_GC_TIMER]                   = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BR_GROUP_ADDR]                 = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_FDB_FLUSH]                  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_MCAST_ROUTER]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_MCAST_SNOOPING]             = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_MCAST_QUERY_USE_IFADDR]     = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_MCAST_QUERIER]              = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_MCAST_HASH_ELASTICITY]      = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_MCAST_HASH_MAX]             = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_MCAST_LAST_MEMBER_CNT]      = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BR_MCAST_STARTUP_QUERY_CNT]    = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_MCAST_LAST_MEMBER_INTVL]    = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BR_MCAST_MEMBERSHIP_INTVL]     = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BR_MCAST_QUERIER_INTVL]        = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BR_MCAST_QUERY_INTVL]          = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BR_MCAST_STARTUP_QUERY_INTVL]  = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BR_NF_CALL_IPTABLES]           = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_NF_CALL_IP6TABLES]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_NF_CALL_ARPTABLES]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BR_VLAN_DEFAULT_PVID]          = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BR_MCAST_IGMP_VERSION]         = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_vlan_qos_map_types[] = {
+        [IFLA_VLAN_QOS_MAPPING]        = { .size = sizeof(struct ifla_vlan_qos_mapping) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_vlan_qos_map);
+
+static const NLType rtnl_link_info_data_vlan_types[] = {
+        [IFLA_VLAN_ID]          = { .type = NETLINK_TYPE_U16 },
+        [IFLA_VLAN_FLAGS]       = { .size = sizeof(struct ifla_vlan_flags) },
+        [IFLA_VLAN_EGRESS_QOS]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
+        [IFLA_VLAN_INGRESS_QOS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
+        [IFLA_VLAN_PROTOCOL]    = { .type = NETLINK_TYPE_U16 },
+};
+
+static const NLType rtnl_link_info_data_vxlan_types[] = {
+        [IFLA_VXLAN_ID]                = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VXLAN_GROUP]             = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_VXLAN_LINK]              = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VXLAN_LOCAL]             = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_VXLAN_TTL]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_TOS]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_LEARNING]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_AGEING]            = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VXLAN_LIMIT]             = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VXLAN_PORT_RANGE]        = { .type = NETLINK_TYPE_U32},
+        [IFLA_VXLAN_PROXY]             = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_RSC]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_L2MISS]            = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_L3MISS]            = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_PORT]              = { .type = NETLINK_TYPE_U16 },
+        [IFLA_VXLAN_GROUP6]            = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_VXLAN_LOCAL6]            = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_VXLAN_UDP_CSUM]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_REMCSUM_TX]        = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_REMCSUM_RX]        = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_GBP]               = { .type = NETLINK_TYPE_FLAG },
+        [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
+        [IFLA_VXLAN_COLLECT_METADATA]  = { .type = NETLINK_TYPE_U8 },
+        [IFLA_VXLAN_LABEL]             = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VXLAN_GPE]               = { .type = NETLINK_TYPE_FLAG },
+        [IFLA_VXLAN_TTL_INHERIT]       = { .type = NETLINK_TYPE_FLAG },
+        [IFLA_VXLAN_DF]                = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_bond_arp_target_types[] = {
+        [BOND_ARP_TARGETS_0]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_1]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_2]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_3]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_4]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_5]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_6]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_7]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_8]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_9]        = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_10]       = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_11]       = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_12]       = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_13]       = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_14]       = { .type = NETLINK_TYPE_U32 },
+        [BOND_ARP_TARGETS_MAX]      = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_bond_arp_target);
+
+static const NLType rtnl_link_info_data_bond_types[] = {
+        [IFLA_BOND_MODE]                = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BOND_ACTIVE_SLAVE]        = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_MIIMON]              = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_UPDELAY]             = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_DOWNDELAY]           = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_USE_CARRIER]         = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BOND_ARP_INTERVAL]        = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_ARP_IP_TARGET]       = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_arp_target_type_system },
+        [IFLA_BOND_ARP_VALIDATE]        = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_ARP_ALL_TARGETS]     = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_PRIMARY]             = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_PRIMARY_RESELECT]    = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BOND_FAIL_OVER_MAC]       = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BOND_XMIT_HASH_POLICY]    = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BOND_RESEND_IGMP]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_NUM_PEER_NOTIF]      = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BOND_ALL_SLAVES_ACTIVE]   = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BOND_MIN_LINKS]           = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_LP_INTERVAL]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_PACKETS_PER_SLAVE]   = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BOND_AD_LACP_RATE]        = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BOND_AD_SELECT]           = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BOND_AD_INFO]             = { .type = NETLINK_TYPE_NESTED },
+        [IFLA_BOND_AD_ACTOR_SYS_PRIO]   = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BOND_AD_USER_PORT_KEY]    = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BOND_AD_ACTOR_SYSTEM]     = { .type = NETLINK_TYPE_ETHER_ADDR },
+        [IFLA_BOND_TLB_DYNAMIC_LB]      = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_link_info_data_iptun_types[] = {
+        [IFLA_IPTUN_LINK]                = { .type = NETLINK_TYPE_U32 },
+        [IFLA_IPTUN_LOCAL]               = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_IPTUN_REMOTE]              = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_IPTUN_TTL]                 = { .type = NETLINK_TYPE_U8 },
+        [IFLA_IPTUN_TOS]                 = { .type = NETLINK_TYPE_U8 },
+        [IFLA_IPTUN_PMTUDISC]            = { .type = NETLINK_TYPE_U8 },
+        [IFLA_IPTUN_FLAGS]               = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_PROTO]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_IPTUN_6RD_PREFIX]          = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_IPTUN_6RD_RELAY_PREFIX]    = { .type = NETLINK_TYPE_U32 },
+        [IFLA_IPTUN_6RD_PREFIXLEN]       = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_ENCAP_TYPE]          = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_ENCAP_FLAGS]         = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_ENCAP_SPORT]         = { .type = NETLINK_TYPE_U16 },
+        [IFLA_IPTUN_ENCAP_DPORT]         = { .type = NETLINK_TYPE_U16 },
+};
+
+static  const NLType rtnl_link_info_data_ipgre_types[] = {
+        [IFLA_GRE_LINK]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_IFLAGS]       = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_OFLAGS]       = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_IKEY]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_OKEY]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_LOCAL]        = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_GRE_REMOTE]       = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_GRE_TTL]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GRE_TOS]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GRE_PMTUDISC]     = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GRE_FLOWINFO]     = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_FLAGS]        = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GRE_ENCAP_TYPE]   = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_ENCAP_FLAGS]  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_ENCAP_SPORT]  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_ENCAP_DPORT]  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GRE_ERSPAN_INDEX] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_link_info_data_ipvti_types[] = {
+        [IFLA_VTI_LINK]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VTI_IKEY]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VTI_OKEY]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VTI_LOCAL]        = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_VTI_REMOTE]       = { .type = NETLINK_TYPE_IN_ADDR },
+};
+
+static const NLType rtnl_link_info_data_ip6tnl_types[] = {
+        [IFLA_IPTUN_LINK]                = { .type = NETLINK_TYPE_U32 },
+        [IFLA_IPTUN_LOCAL]               = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_IPTUN_REMOTE]              = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_IPTUN_TTL]                 = { .type = NETLINK_TYPE_U8 },
+        [IFLA_IPTUN_FLAGS]               = { .type = NETLINK_TYPE_U32 },
+        [IFLA_IPTUN_PROTO]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_IPTUN_ENCAP_LIMIT]         = { .type = NETLINK_TYPE_U8 },
+        [IFLA_IPTUN_FLOWINFO]            = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_link_info_data_vrf_types[] = {
+        [IFLA_VRF_TABLE]                 = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_link_info_data_geneve_types[] = {
+        [IFLA_GENEVE_ID]                = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GENEVE_TTL]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GENEVE_TOS]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GENEVE_PORT]              = { .type = NETLINK_TYPE_U16 },
+        [IFLA_GENEVE_REMOTE]            = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_GENEVE_REMOTE6]           = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_GENEVE_UDP_CSUM]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GENEVE_LABEL]             = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GENEVE_TTL_INHERIT]       = { .type = NETLINK_TYPE_U8 },
+        [IFLA_GENEVE_DF]                = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_link_info_data_can_types[] = {
+        [IFLA_CAN_BITTIMING]            = { .size = sizeof(struct can_bittiming) },
+        [IFLA_CAN_RESTART_MS]           = { .type = NETLINK_TYPE_U32 },
+        [IFLA_CAN_CTRLMODE]             = { .size = sizeof(struct can_ctrlmode) },
+        [IFLA_CAN_TERMINATION]          = { .type = NETLINK_TYPE_U16 },
+};
+
+static const NLType rtnl_link_info_data_macsec_types[] = {
+        [IFLA_MACSEC_SCI]            = { .type = NETLINK_TYPE_U64 },
+        [IFLA_MACSEC_PORT]           = { .type = NETLINK_TYPE_U16 },
+        [IFLA_MACSEC_ICV_LEN]        = { .type = NETLINK_TYPE_U8 },
+        [IFLA_MACSEC_CIPHER_SUITE]   = { .type = NETLINK_TYPE_U64 },
+        [IFLA_MACSEC_WINDOW]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_MACSEC_ENCODING_SA]    = { .type = NETLINK_TYPE_U8 },
+        [IFLA_MACSEC_ENCRYPT]        = { .type = NETLINK_TYPE_U8 },
+        [IFLA_MACSEC_PROTECT]        = { .type = NETLINK_TYPE_U8 },
+        [IFLA_MACSEC_INC_SCI]        = { .type = NETLINK_TYPE_U8 },
+        [IFLA_MACSEC_ES]             = { .type = NETLINK_TYPE_U8 },
+        [IFLA_MACSEC_SCB]            = { .type = NETLINK_TYPE_U8 },
+        [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NETLINK_TYPE_U8 },
+        [IFLA_MACSEC_VALIDATION]     = { .type = NETLINK_TYPE_U8 },
+};
+
+static const NLType rtnl_link_info_data_xfrm_types[] = {
+        [IFLA_XFRM_LINK]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_XFRM_IF_ID]        = { .type = NETLINK_TYPE_U32 }
+};
+
+static const NLType rtnl_link_info_data_bareudp_types[] = {
+        [IFLA_BAREUDP_PORT]            = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BAREUDP_ETHERTYPE]       = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BAREUDP_SRCPORT_MIN]     = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BAREUDP_MULTIPROTO_MODE] = { .type = NETLINK_TYPE_FLAG },
+};
+
+static const NLTypeSystemUnionElement rtnl_link_info_data_type_systems[] = {
+        { .name = "bareudp",   .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_bareudp), },
+        { .name = "batadv",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_batadv),  },
+        { .name = "bond",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_bond),    },
+        { .name = "bridge",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_bridge),  },
+        { .name = "can",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_can),     },
+        { .name = "dummy",                                                                        },
+        { .name = "erspan",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre),   },
+        { .name = "geneve",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_geneve),  },
+        { .name = "gre",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre),   },
+        { .name = "gretap",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre),   },
+        { .name = "ifb",                                                                          },
+        { .name = "ip6gre",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre),   },
+        { .name = "ip6gretap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipgre),   },
+        { .name = "ip6tnl",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ip6tnl),  },
+        { .name = "ipip",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_iptun),   },
+        { .name = "ipvlan",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvlan),  },
+        { .name = "ipvtap",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvlan),  },
+        { .name = "macsec",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_macsec),  },
+        { .name = "macvlan",   .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_macvlan), },
+        { .name = "macvtap",   .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_macvlan), },
+        { .name = "netdevsim",                                                                    },
+        { .name = "nlmon",                                                                        },
+        { .name = "sit",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_iptun),   },
+        { .name = "vcan",                                                                         },
+        { .name = "veth",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_veth),    },
+        { .name = "vlan",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vlan),    },
+        { .name = "vrf",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vrf),     },
+        { .name = "vti",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvti),   },
+        { .name = "vti6",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvti),   },
+        { .name = "vxcan",     .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vxcan),   },
+        { .name = "vxlan",     .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vxlan),   },
+        { .name = "wireguard",                                                                    },
+        { .name = "xfrm",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_xfrm),    },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(rtnl_link_info_data, IFLA_INFO_KIND);
+
+static const NLType rtnl_link_info_types[] = {
+        [IFLA_INFO_KIND]        = { .type = NETLINK_TYPE_STRING },
+        [IFLA_INFO_DATA]        = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union },
+/*
+        [IFLA_INFO_XSTATS],
+        [IFLA_INFO_SLAVE_KIND]  = { .type = NETLINK_TYPE_STRING },
+        [IFLA_INFO_SLAVE_DATA]  = { .type = NETLINK_TYPE_NESTED },
+*/
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_link_info);
+
+static const struct NLType rtnl_prot_info_bridge_port_types[] = {
+        [IFLA_BRPORT_STATE]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_COST]                = { .type = NETLINK_TYPE_U32 },
+        [IFLA_BRPORT_PRIORITY]            = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BRPORT_MODE]                = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_GUARD]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_PROTECT]             = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_FAST_LEAVE]          = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_LEARNING]            = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_UNICAST_FLOOD]       = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_PROXYARP]            = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_LEARNING_SYNC]       = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_PROXYARP_WIFI]       = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_ROOT_ID]             = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_BRIDGE_ID]           = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_DESIGNATED_PORT]     = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BRPORT_DESIGNATED_COST]     = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BRPORT_ID]                  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BRPORT_NO]                  = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BRPORT_TOPOLOGY_CHANGE_ACK] = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_CONFIG_PENDING]      = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_MESSAGE_AGE_TIMER]   = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BRPORT_FORWARD_DELAY_TIMER] = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BRPORT_HOLD_TIMER]          = { .type = NETLINK_TYPE_U64 },
+        [IFLA_BRPORT_FLUSH]               = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_MULTICAST_ROUTER]    = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_PAD]                 = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_MCAST_FLOOD]         = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_MCAST_TO_UCAST]      = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_VLAN_TUNNEL]         = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_BCAST_FLOOD]         = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_GROUP_FWD_MASK]      = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BRPORT_NEIGH_SUPPRESS]      = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_ISOLATED]            = { .type = NETLINK_TYPE_U8 },
+        [IFLA_BRPORT_BACKUP_PORT]         = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLTypeSystemUnionElement rtnl_prot_info_type_systems[] = {
+        { .protocol = AF_BRIDGE, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_prot_info_bridge_port), },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(rtnl_prot_info);
+
+static const struct NLType rtnl_af_spec_inet6_types[] = {
+        [IFLA_INET6_FLAGS]              = { .type = NETLINK_TYPE_U32 },
+/*
+        IFLA_INET6_CONF,
+        IFLA_INET6_STATS,
+        IFLA_INET6_MCAST,
+        IFLA_INET6_CACHEINFO,
+        IFLA_INET6_ICMP6STATS,
+*/
+        [IFLA_INET6_TOKEN]              = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFLA_INET6_ADDR_GEN_MODE]      = { .type = NETLINK_TYPE_U8 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_af_spec_inet6);
+
+static const NLType rtnl_af_spec_unspec_types[] = {
+        [AF_INET6] =    { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system },
+};
+
+static const NLType rtnl_af_spec_bridge_types[] = {
+        [IFLA_BRIDGE_FLAGS]     = { .type = NETLINK_TYPE_U16 },
+        [IFLA_BRIDGE_VLAN_INFO] = { .size = sizeof(struct bridge_vlan_info) },
+};
+
+static const NLTypeSystemUnionElement rtnl_af_spec_type_systems[] = {
+        { .protocol = AF_UNSPEC, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_af_spec_unspec), },
+        { .protocol = AF_BRIDGE, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_af_spec_bridge), },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(rtnl_af_spec);
+
+static const NLType rtnl_prop_list_types[] = {
+        [IFLA_ALT_IFNAME]       = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_prop_list);
+
+static const NLType rtnl_vf_vlan_list_types[] = {
+        [IFLA_VF_VLAN_INFO]  = { .size = sizeof(struct ifla_vf_vlan_info) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_vf_vlan_list);
+
+static const NLType rtnl_vf_info_types[] = {
+        [IFLA_VF_MAC]           = { .size = sizeof(struct ifla_vf_mac) },
+        [IFLA_VF_VLAN]          = { .size = sizeof(struct ifla_vf_vlan) },
+        [IFLA_VF_VLAN_LIST]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_list_type_system},
+        [IFLA_VF_TX_RATE]       = { .size = sizeof(struct ifla_vf_tx_rate) },
+        [IFLA_VF_SPOOFCHK]      = { .size = sizeof(struct ifla_vf_spoofchk) },
+        [IFLA_VF_RATE]          = { .size = sizeof(struct ifla_vf_rate) },
+        [IFLA_VF_LINK_STATE]    = { .size = sizeof(struct ifla_vf_link_state) },
+        [IFLA_VF_RSS_QUERY_EN]  = { .size = sizeof(struct ifla_vf_rss_query_en) },
+        [IFLA_VF_TRUST]         = { .size = sizeof(struct ifla_vf_trust) },
+        [IFLA_VF_IB_NODE_GUID]  = { .size = sizeof(struct ifla_vf_guid) },
+        [IFLA_VF_IB_PORT_GUID]  = { .size = sizeof(struct ifla_vf_guid) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_vf_info);
+
+static const NLType rtnl_vfinfo_list_types[] = {
+        [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_info_type_system },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_vfinfo_list);
+
+static const NLType rtnl_link_types[] = {
+        [IFLA_ADDRESS]          = { .type = NETLINK_TYPE_ETHER_ADDR },
+        [IFLA_BROADCAST]        = { .type = NETLINK_TYPE_ETHER_ADDR },
+        [IFLA_IFNAME]           = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
+        [IFLA_MTU]              = { .type = NETLINK_TYPE_U32 },
+        [IFLA_LINK]             = { .type = NETLINK_TYPE_U32 },
+        [IFLA_QDISC]            = { .type = NETLINK_TYPE_STRING },
+        [IFLA_STATS]            = { .size = sizeof(struct rtnl_link_stats) },
+/*
+        [IFLA_COST],
+        [IFLA_PRIORITY],
+*/
+        [IFLA_MASTER]           = { .type = NETLINK_TYPE_U32 },
+/*
+        [IFLA_WIRELESS],
+*/
+        [IFLA_PROTINFO]         = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union },
+        [IFLA_TXQLEN]           = { .type = NETLINK_TYPE_U32 },
+/*
+        [IFLA_MAP]              = { .len = sizeof(struct rtnl_link_ifmap) },
+*/
+        [IFLA_WEIGHT]           = { .type = NETLINK_TYPE_U32 },
+        [IFLA_OPERSTATE]        = { .type = NETLINK_TYPE_U8 },
+        [IFLA_LINKMODE]         = { .type = NETLINK_TYPE_U8 },
+        [IFLA_LINKINFO]         = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system },
+        [IFLA_NET_NS_PID]       = { .type = NETLINK_TYPE_U32 },
+        [IFLA_IFALIAS]          = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 },
+        [IFLA_NUM_VF]           = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VFINFO_LIST]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vfinfo_list_type_system },
+        [IFLA_STATS64]          = { .size = sizeof(struct rtnl_link_stats64) },
+/*
+        [IFLA_VF_PORTS]         = { .type = NETLINK_TYPE_NESTED },
+        [IFLA_PORT_SELF]        = { .type = NETLINK_TYPE_NESTED },
+*/
+        [IFLA_AF_SPEC]          = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_af_spec_type_system_union },
+/*
+        [IFLA_VF_PORTS],
+        [IFLA_PORT_SELF],
+*/
+        [IFLA_GROUP]            = { .type = NETLINK_TYPE_U32 },
+        [IFLA_NET_NS_FD]        = { .type = NETLINK_TYPE_U32 },
+        [IFLA_EXT_MASK]         = { .type = NETLINK_TYPE_U32 },
+        [IFLA_PROMISCUITY]      = { .type = NETLINK_TYPE_U32 },
+        [IFLA_NUM_TX_QUEUES]    = { .type = NETLINK_TYPE_U32 },
+        [IFLA_NUM_RX_QUEUES]    = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GSO_MAX_SEGS]     = { .type = NETLINK_TYPE_U32 },
+        [IFLA_GSO_MAX_SIZE]     = { .type = NETLINK_TYPE_U32 },
+        [IFLA_CARRIER]          = { .type = NETLINK_TYPE_U8 },
+/*
+        [IFLA_PHYS_PORT_ID]     = { .type = NETLINK_TYPE_BINARY, .len = MAX_PHYS_PORT_ID_LEN },
+*/
+        [IFLA_MIN_MTU]          = { .type = NETLINK_TYPE_U32 },
+        [IFLA_MAX_MTU]          = { .type = NETLINK_TYPE_U32 },
+        [IFLA_PROP_LIST]        = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_prop_list_type_system },
+        [IFLA_ALT_IFNAME]       = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_link);
+
+/* IFA_FLAGS was defined in kernel 3.14, but we still support older
+ * kernels where IFA_MAX is lower. */
+static const NLType rtnl_address_types[] = {
+        [IFA_ADDRESS]           = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFA_LOCAL]             = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFA_LABEL]             = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
+        [IFA_BROADCAST]         = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFA_ANYCAST]           = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFA_CACHEINFO]         = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) },
+        [IFA_MULTICAST]         = { .type = NETLINK_TYPE_IN_ADDR },
+        [IFA_FLAGS]             = { .type = NETLINK_TYPE_U32 },
+        [IFA_RT_PRIORITY]       = { .type = NETLINK_TYPE_U32 },
+        [IFA_TARGET_NETNSID]    = { .type = NETLINK_TYPE_S32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_address);
+
+/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
+
+static const NLType rtnl_route_metrics_types[] = {
+        [RTAX_MTU]                = { .type = NETLINK_TYPE_U32 },
+        [RTAX_WINDOW]             = { .type = NETLINK_TYPE_U32 },
+        [RTAX_RTT]                = { .type = NETLINK_TYPE_U32 },
+        [RTAX_RTTVAR]             = { .type = NETLINK_TYPE_U32 },
+        [RTAX_SSTHRESH]           = { .type = NETLINK_TYPE_U32 },
+        [RTAX_CWND]               = { .type = NETLINK_TYPE_U32 },
+        [RTAX_ADVMSS]             = { .type = NETLINK_TYPE_U32 },
+        [RTAX_REORDERING]         = { .type = NETLINK_TYPE_U32 },
+        [RTAX_HOPLIMIT]           = { .type = NETLINK_TYPE_U32 },
+        [RTAX_INITCWND]           = { .type = NETLINK_TYPE_U32 },
+        [RTAX_FEATURES]           = { .type = NETLINK_TYPE_U32 },
+        [RTAX_RTO_MIN]            = { .type = NETLINK_TYPE_U32 },
+        [RTAX_INITRWND]           = { .type = NETLINK_TYPE_U32 },
+        [RTAX_QUICKACK]           = { .type = NETLINK_TYPE_U32 },
+        [RTAX_CC_ALGO]            = { .type = NETLINK_TYPE_U32 },
+        [RTAX_FASTOPEN_NO_COOKIE] = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_route_metrics);
+
+static const NLType rtnl_route_types[] = {
+        [RTA_DST]               = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
+        [RTA_SRC]               = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
+        [RTA_IIF]               = { .type = NETLINK_TYPE_U32 },
+        [RTA_OIF]               = { .type = NETLINK_TYPE_U32 },
+        [RTA_GATEWAY]           = { .type = NETLINK_TYPE_IN_ADDR },
+        [RTA_PRIORITY]          = { .type = NETLINK_TYPE_U32 },
+        [RTA_PREFSRC]           = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
+        [RTA_METRICS]           = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_metrics_type_system},
+        [RTA_MULTIPATH]         = { .size = sizeof(struct rtnexthop) },
+        [RTA_FLOW]              = { .type = NETLINK_TYPE_U32 }, /* 6? */
+        [RTA_CACHEINFO]         = { .size = sizeof(struct rta_cacheinfo) },
+        [RTA_TABLE]             = { .type = NETLINK_TYPE_U32 },
+        [RTA_MARK]              = { .type = NETLINK_TYPE_U32 },
+        [RTA_MFC_STATS]         = { .type = NETLINK_TYPE_U64 },
+        [RTA_VIA]               = { /* See struct rtvia */ },
+        [RTA_NEWDST]            = { .type = NETLINK_TYPE_U32 },
+        [RTA_PREF]              = { .type = NETLINK_TYPE_U8 },
+        [RTA_ENCAP_TYPE]        = { .type = NETLINK_TYPE_U16 },
+        [RTA_ENCAP]             = { .type = NETLINK_TYPE_NESTED }, /* Multiple type systems i.e. LWTUNNEL_ENCAP_MPLS/LWTUNNEL_ENCAP_IP/LWTUNNEL_ENCAP_ILA etc... */
+        [RTA_EXPIRES]           = { .type = NETLINK_TYPE_U32 },
+        [RTA_UID]               = { .type = NETLINK_TYPE_U32 },
+        [RTA_TTL_PROPAGATE]     = { .type = NETLINK_TYPE_U8 },
+        [RTA_IP_PROTO]          = { .type = NETLINK_TYPE_U8 },
+        [RTA_SPORT]             = { .type = NETLINK_TYPE_U16 },
+        [RTA_DPORT]             = { .type = NETLINK_TYPE_U16 },
+        [RTA_NH_ID]             = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_route);
+
+static const NLType rtnl_neigh_types[] = {
+        [NDA_DST]               = { .type = NETLINK_TYPE_IN_ADDR },
+        [NDA_LLADDR]            = { /* struct ether_addr, struct in_addr, or struct in6_addr */ },
+        [NDA_CACHEINFO]         = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) },
+        [NDA_PROBES]            = { .type = NETLINK_TYPE_U32 },
+        [NDA_VLAN]              = { .type = NETLINK_TYPE_U16 },
+        [NDA_PORT]              = { .type = NETLINK_TYPE_U16 },
+        [NDA_VNI]               = { .type = NETLINK_TYPE_U32 },
+        [NDA_IFINDEX]           = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_neigh);
+
+static const NLType rtnl_addrlabel_types[] = {
+        [IFAL_ADDRESS]         = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
+        [IFAL_LABEL]           = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_addrlabel);
+
+static const NLType rtnl_routing_policy_rule_types[] = {
+        [FRA_DST]                 = { .type = NETLINK_TYPE_IN_ADDR },
+        [FRA_SRC]                 = { .type = NETLINK_TYPE_IN_ADDR },
+        [FRA_IIFNAME]             = { .type = NETLINK_TYPE_STRING },
+        [FRA_GOTO]                = { .type = NETLINK_TYPE_U32 },
+        [FRA_PRIORITY]            = { .type = NETLINK_TYPE_U32 },
+        [FRA_FWMARK]              = { .type = NETLINK_TYPE_U32 },
+        [FRA_FLOW]                = { .type = NETLINK_TYPE_U32 },
+        [FRA_TUN_ID]              = { .type = NETLINK_TYPE_U64 },
+        [FRA_SUPPRESS_IFGROUP]    = { .type = NETLINK_TYPE_U32 },
+        [FRA_SUPPRESS_PREFIXLEN]  = { .type = NETLINK_TYPE_U32 },
+        [FRA_TABLE]               = { .type = NETLINK_TYPE_U32 },
+        [FRA_FWMASK]              = { .type = NETLINK_TYPE_U32 },
+        [FRA_OIFNAME]             = { .type = NETLINK_TYPE_STRING },
+        [FRA_PAD]                 = { .type = NETLINK_TYPE_U32 },
+        [FRA_L3MDEV]              = { .type = NETLINK_TYPE_U8 },
+        [FRA_UID_RANGE]           = { .size = sizeof(struct fib_rule_uid_range) },
+        [FRA_PROTOCOL]            = { .type = NETLINK_TYPE_U8 },
+        [FRA_IP_PROTO]            = { .type = NETLINK_TYPE_U8 },
+        [FRA_SPORT_RANGE]         = { .size = sizeof(struct fib_rule_port_range) },
+        [FRA_DPORT_RANGE]         = { .size = sizeof(struct fib_rule_port_range) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_routing_policy_rule);
+
+static const NLType rtnl_nexthop_types[] = {
+        [NHA_ID]                  = { .type = NETLINK_TYPE_U32 },
+        [NHA_GROUP]               = { /* array of struct nexthop_grp */ },
+        [NHA_GROUP_TYPE]          = { .type = NETLINK_TYPE_U16 },
+        [NHA_BLACKHOLE]           = { .type = NETLINK_TYPE_FLAG },
+        [NHA_OIF]                 = { .type = NETLINK_TYPE_U32 },
+        [NHA_GATEWAY]             = { .type = NETLINK_TYPE_IN_ADDR },
+        [NHA_ENCAP_TYPE]          = { .type = NETLINK_TYPE_U16 },
+        [NHA_ENCAP]               = { .type = NETLINK_TYPE_NESTED },
+        [NHA_GROUPS]              = { .type = NETLINK_TYPE_FLAG },
+        [NHA_MASTER]              = { .type = NETLINK_TYPE_U32 },
+        [NHA_FDB]                 = { .type = NETLINK_TYPE_FLAG },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_nexthop);
+
+static const NLType rtnl_tca_option_data_cake_types[] = {
+        [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
+        [TCA_CAKE_OVERHEAD]    = { .type = NETLINK_TYPE_S32 },
+        [TCA_CAKE_MPU]         = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_codel_types[] = {
+        [TCA_CODEL_TARGET]        = { .type = NETLINK_TYPE_U32 },
+        [TCA_CODEL_LIMIT]         = { .type = NETLINK_TYPE_U32 },
+        [TCA_CODEL_INTERVAL]      = { .type = NETLINK_TYPE_U32 },
+        [TCA_CODEL_ECN]           = { .type = NETLINK_TYPE_U32 },
+        [TCA_CODEL_CE_THRESHOLD]  = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_drr_types[] = {
+        [TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_ets_quanta_types[] = {
+        [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32, },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_tca_option_data_ets_quanta);
+
+static const NLType rtnl_tca_option_data_ets_prio_types[] = {
+        [TCA_ETS_PRIOMAP_BAND] = { .type = NETLINK_TYPE_U8, },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_tca_option_data_ets_prio);
+
+static const NLType rtnl_tca_option_data_ets_types[] = {
+        [TCA_ETS_NBANDS]      = { .type = NETLINK_TYPE_U8 },
+        [TCA_ETS_NSTRICT]     = { .type = NETLINK_TYPE_U8 },
+        [TCA_ETS_QUANTA]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_quanta_type_system },
+        [TCA_ETS_PRIOMAP]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_prio_type_system },
+        [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_fq_types[] = {
+        [TCA_FQ_PLIMIT]             = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_FLOW_PLIMIT]        = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_QUANTUM]            = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_INITIAL_QUANTUM]    = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_RATE_ENABLE]        = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_FLOW_DEFAULT_RATE]  = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_FLOW_MAX_RATE]      = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_BUCKETS_LOG]        = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_FLOW_REFILL_DELAY]  = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_LOW_RATE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_CE_THRESHOLD]       = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_ORPHAN_MASK]        = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_fq_codel_types[] = {
+        [TCA_FQ_CODEL_TARGET]          = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_CODEL_LIMIT]           = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_CODEL_INTERVAL]        = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_CODEL_ECN]             = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_CODEL_FLOWS]           = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_CODEL_QUANTUM]         = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_CODEL_CE_THRESHOLD]    = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NETLINK_TYPE_U32 },
+        [TCA_FQ_CODEL_MEMORY_LIMIT]    = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_fq_pie_types[] = {
+        [TCA_FQ_PIE_LIMIT]   = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_gred_types[] = {
+        [TCA_GRED_DPS] = { .size = sizeof(struct tc_gred_sopt) },
+};
+
+static const NLType rtnl_tca_option_data_hhf_types[] = {
+        [TCA_HHF_BACKLOG_LIMIT] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_htb_types[] = {
+        [TCA_HTB_PARMS]  = { .size = sizeof(struct tc_htb_opt) },
+        [TCA_HTB_INIT]   = { .size = sizeof(struct tc_htb_glob) },
+        [TCA_HTB_CTAB]   = { .size = TC_RTAB_SIZE },
+        [TCA_HTB_RTAB]   = { .size = TC_RTAB_SIZE },
+        [TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 },
+        [TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 },
+};
+
+static const NLType rtnl_tca_option_data_pie_types[] = {
+        [TCA_PIE_LIMIT]   = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_qfq_types[] = {
+        [TCA_QFQ_WEIGHT] = { .type = NETLINK_TYPE_U32 },
+        [TCA_QFQ_LMAX]   = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_sfb_types[] = {
+        [TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) },
+};
+
+static const NLType rtnl_tca_option_data_tbf_types[] = {
+        [TCA_TBF_PARMS]   = { .size = sizeof(struct tc_tbf_qopt) },
+        [TCA_TBF_RTAB]    = { .size = TC_RTAB_SIZE },
+        [TCA_TBF_PTAB]    = { .size = TC_RTAB_SIZE },
+        [TCA_TBF_RATE64]  = { .type = NETLINK_TYPE_U64 },
+        [TCA_TBF_PRATE64] = { .type = NETLINK_TYPE_U64 },
+        [TCA_TBF_BURST]   = { .type = NETLINK_TYPE_U32 },
+        [TCA_TBF_PBURST]  = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLTypeSystemUnionElement rtnl_tca_option_data_type_systems[] = {
+        { .name = "cake",     .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_cake),     },
+        { .name = "codel",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_codel),    },
+        { .name = "drr",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_drr),      },
+        { .name = "ets",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_ets),      },
+        { .name = "fq",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_fq),       },
+        { .name = "fq_codel", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_fq_codel), },
+        { .name = "fq_pie",   .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_fq_pie),   },
+        { .name = "gred",     .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_gred),     },
+        { .name = "hhf",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_hhf),      },
+        { .name = "htb",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_htb),      },
+        { .name = "pie",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_pie),      },
+        { .name = "qfq",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_qfq),      },
+        { .name = "sfb",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_sfb),      },
+        { .name = "tbf",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_tbf),      },
+};
+
+DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(rtnl_tca_option_data, TCA_KIND);
+
+static const NLType rtnl_tca_types[] = {
+        [TCA_KIND]           = { .type = NETLINK_TYPE_STRING },
+        [TCA_OPTIONS]        = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_tca_option_data_type_system_union },
+        [TCA_INGRESS_BLOCK]  = { .type = NETLINK_TYPE_U32 },
+        [TCA_EGRESS_BLOCK]   = { .type = NETLINK_TYPE_U32 },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_tca);
+
+static const NLType rtnl_mdb_types[] = {
+        [MDBA_SET_ENTRY]     = { .size = sizeof(struct br_port_msg) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl_mdb);
+
+static const NLType rtnl_types[] = {
+        [RTM_NEWLINK]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system,                .size = sizeof(struct ifinfomsg) },
+        [RTM_DELLINK]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system,                .size = sizeof(struct ifinfomsg) },
+        [RTM_GETLINK]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system,                .size = sizeof(struct ifinfomsg) },
+        [RTM_SETLINK]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system,                .size = sizeof(struct ifinfomsg) },
+        [RTM_NEWLINKPROP]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system,                .size = sizeof(struct ifinfomsg) },
+        [RTM_DELLINKPROP]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system,                .size = sizeof(struct ifinfomsg) },
+        [RTM_GETLINKPROP]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system,                .size = sizeof(struct ifinfomsg) },
+        [RTM_NEWADDR]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system,             .size = sizeof(struct ifaddrmsg) },
+        [RTM_DELADDR]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system,             .size = sizeof(struct ifaddrmsg) },
+        [RTM_GETADDR]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system,             .size = sizeof(struct ifaddrmsg) },
+        [RTM_NEWROUTE]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system,               .size = sizeof(struct rtmsg) },
+        [RTM_DELROUTE]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system,               .size = sizeof(struct rtmsg) },
+        [RTM_GETROUTE]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system,               .size = sizeof(struct rtmsg) },
+        [RTM_NEWNEIGH]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system,               .size = sizeof(struct ndmsg) },
+        [RTM_DELNEIGH]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system,               .size = sizeof(struct ndmsg) },
+        [RTM_GETNEIGH]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system,               .size = sizeof(struct ndmsg) },
+        [RTM_NEWADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system,           .size = sizeof(struct ifaddrlblmsg) },
+        [RTM_DELADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system,           .size = sizeof(struct ifaddrlblmsg) },
+        [RTM_GETADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system,           .size = sizeof(struct ifaddrlblmsg) },
+        [RTM_NEWRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
+        [RTM_DELRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
+        [RTM_GETRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
+        [RTM_NEWNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system,             .size = sizeof(struct nhmsg) },
+        [RTM_DELNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system,             .size = sizeof(struct nhmsg) },
+        [RTM_GETNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system,             .size = sizeof(struct nhmsg) },
+        [RTM_NEWQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system,                 .size = sizeof(struct tcmsg) },
+        [RTM_DELQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system,                 .size = sizeof(struct tcmsg) },
+        [RTM_GETQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system,                 .size = sizeof(struct tcmsg) },
+        [RTM_NEWTCLASS]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system,                 .size = sizeof(struct tcmsg) },
+        [RTM_DELTCLASS]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system,                 .size = sizeof(struct tcmsg) },
+        [RTM_GETTCLASS]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system,                 .size = sizeof(struct tcmsg) },
+        [RTM_NEWMDB]       = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system,                 .size = sizeof(struct br_port_msg) },
+        [RTM_DELMDB]       = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system,                 .size = sizeof(struct br_port_msg) },
+        [RTM_GETMDB]       = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system,                 .size = sizeof(struct br_port_msg) },
+};
+
+DEFINE_TYPE_SYSTEM(rtnl);
+
+const NLType *rtnl_get_type(uint16_t nlmsg_type) {
+        return type_system_get_type(&rtnl_type_system, nlmsg_type);
+}
index beb926d40be57b422cfba836b6196252b5f0d28e..051dac95c21c9e8979b90b472bac2a4ebcaf79e9 100644 (file)
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include <netinet/in.h>
-#include <stdint.h>
-#include <sys/socket.h>
-#include <linux/can/vxcan.h>
 #include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <linux/genetlink.h>
-#include <linux/ip.h>
-#include <linux/if.h>
-#include <linux/batman_adv.h>
-#include <linux/can/netlink.h>
-#include <linux/fib_rules.h>
-#include <linux/fou.h>
-#include <linux/if_addr.h>
-#include <linux/if_addrlabel.h>
-#include <linux/if_bridge.h>
-#include <linux/if_link.h>
-#include <linux/if_macsec.h>
-#include <linux/if_tunnel.h>
-#include <linux/l2tp.h>
-#include <linux/netfilter/nf_tables.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/nexthop.h>
-#include <linux/nl80211.h>
-#include <linux/pkt_sched.h>
-#include <linux/veth.h>
-#include <linux/wireguard.h>
 
-#include "sd-netlink.h"
-
-#include "generic-netlink.h"
-#include "hashmap.h"
-#include "macro.h"
+#include "netlink-genl.h"
 #include "netlink-internal.h"
-#include "netlink-types.h"
-#include "string-table.h"
-#include "util.h"
-
-/* Maximum ARP IP target defined in kernel */
-#define BOND_MAX_ARP_TARGETS    16
-
-typedef enum {
-        BOND_ARP_TARGETS_0,
-        BOND_ARP_TARGETS_1,
-        BOND_ARP_TARGETS_2,
-        BOND_ARP_TARGETS_3,
-        BOND_ARP_TARGETS_4,
-        BOND_ARP_TARGETS_5,
-        BOND_ARP_TARGETS_6,
-        BOND_ARP_TARGETS_7,
-        BOND_ARP_TARGETS_8,
-        BOND_ARP_TARGETS_9,
-        BOND_ARP_TARGETS_10,
-        BOND_ARP_TARGETS_11,
-        BOND_ARP_TARGETS_12,
-        BOND_ARP_TARGETS_13,
-        BOND_ARP_TARGETS_14,
-        BOND_ARP_TARGETS_MAX = BOND_MAX_ARP_TARGETS,
-} BondArpTargets;
-
-struct NLType {
-        uint16_t type;
-        size_t size;
-        const NLTypeSystem *type_system;
-        const NLTypeSystemUnion *type_system_union;
-};
-
-struct NLTypeSystem {
-        uint16_t count;
-        const NLType *types;
-};
-
-static const NLTypeSystem rtnl_link_type_system;
+#include "netlink-types-internal.h"
 
 static const NLType empty_types[1] = {
         /* fake array to avoid .types==NULL, which denotes invalid type-systems */
 };
 
-static const NLTypeSystem empty_type_system = {
-        .count = 0,
-        .types = empty_types,
-};
-
-static const NLType rtnl_link_info_data_batadv_types[] = {
-        [IFLA_BATADV_ALGO_NAME] = { .type = NETLINK_TYPE_STRING, .size = 20 },
-};
-
-static const NLType rtnl_link_info_data_veth_types[] = {
-        [VETH_INFO_PEER]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-};
-
-static const NLType rtnl_link_info_data_vxcan_types[] = {
-        [VXCAN_INFO_PEER]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-};
-
-static const NLType rtnl_link_info_data_ipvlan_types[] = {
-        [IFLA_IPVLAN_MODE]  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_IPVLAN_FLAGS]  = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_macvlan_macaddr_types[] = {
-        [IFLA_MACVLAN_MACADDR] = { .type = NETLINK_TYPE_ETHER_ADDR },
-};
-
-static const NLTypeSystem rtnl_macvlan_macaddr_type_system = {
-        .count = ELEMENTSOF(rtnl_macvlan_macaddr_types),
-        .types = rtnl_macvlan_macaddr_types,
-};
-
-static const NLType rtnl_link_info_data_macvlan_types[] = {
-        [IFLA_MACVLAN_MODE]              = { .type = NETLINK_TYPE_U32 },
-        [IFLA_MACVLAN_FLAGS]             = { .type = NETLINK_TYPE_U16 },
-        [IFLA_MACVLAN_MACADDR_MODE]      = { .type = NETLINK_TYPE_U32 },
-        [IFLA_MACVLAN_MACADDR_DATA]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_macvlan_macaddr_type_system },
-        [IFLA_MACVLAN_MACADDR_COUNT]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_MACVLAN_BC_QUEUE_LEN]      = { .type = NETLINK_TYPE_U32 },
-        [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = { .type = NETLINK_TYPE_REJECT },
-};
-
-static const NLType rtnl_link_info_data_bridge_types[] = {
-        [IFLA_BR_FORWARD_DELAY]              = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BR_HELLO_TIME]                 = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BR_MAX_AGE]                    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BR_AGEING_TIME]                = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BR_STP_STATE]                  = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BR_PRIORITY]                   = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_VLAN_FILTERING]             = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_VLAN_PROTOCOL]              = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_GROUP_FWD_MASK]             = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_ROOT_PORT]                  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_ROOT_PATH_COST]             = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BR_TOPOLOGY_CHANGE]            = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_TOPOLOGY_CHANGE_DETECTED]   = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_HELLO_TIMER]                = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_TCN_TIMER]                  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_TOPOLOGY_CHANGE_TIMER]      = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_GC_TIMER]                   = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_GROUP_ADDR]                 = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_FDB_FLUSH]                  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_MCAST_ROUTER]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_MCAST_SNOOPING]             = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_MCAST_QUERY_USE_IFADDR]     = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_MCAST_QUERIER]              = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_MCAST_HASH_ELASTICITY]      = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BR_MCAST_HASH_MAX]             = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_MCAST_LAST_MEMBER_CNT]      = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BR_MCAST_STARTUP_QUERY_CNT]    = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_MCAST_LAST_MEMBER_INTVL]    = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_MCAST_MEMBERSHIP_INTVL]     = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_MCAST_QUERIER_INTVL]        = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_MCAST_QUERY_INTVL]          = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_MCAST_STARTUP_QUERY_INTVL]  = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_NF_CALL_IPTABLES]           = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_NF_CALL_IP6TABLES]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_NF_CALL_ARPTABLES]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_VLAN_DEFAULT_PVID]          = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BR_MCAST_IGMP_VERSION]         = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_vlan_qos_map_types[] = {
-        [IFLA_VLAN_QOS_MAPPING]        = { .size = sizeof(struct ifla_vlan_qos_mapping) },
-};
-
-static const NLTypeSystem rtnl_vlan_qos_map_type_system = {
-        .count = ELEMENTSOF(rtnl_vlan_qos_map_types),
-        .types = rtnl_vlan_qos_map_types,
-};
-
-static const NLType rtnl_link_info_data_vlan_types[] = {
-        [IFLA_VLAN_ID]          = { .type = NETLINK_TYPE_U16 },
-        [IFLA_VLAN_FLAGS]       = { .size = sizeof(struct ifla_vlan_flags) },
-        [IFLA_VLAN_EGRESS_QOS]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
-        [IFLA_VLAN_INGRESS_QOS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
-        [IFLA_VLAN_PROTOCOL]    = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_link_info_data_vxlan_types[] = {
-        [IFLA_VXLAN_ID]                = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VXLAN_GROUP]             = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_VXLAN_LINK]              = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VXLAN_LOCAL]             = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_VXLAN_TTL]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_TOS]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_LEARNING]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_AGEING]            = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VXLAN_LIMIT]             = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VXLAN_PORT_RANGE]        = { .type = NETLINK_TYPE_U32},
-        [IFLA_VXLAN_PROXY]             = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_RSC]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_L2MISS]            = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_L3MISS]            = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_PORT]              = { .type = NETLINK_TYPE_U16 },
-        [IFLA_VXLAN_GROUP6]            = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_VXLAN_LOCAL6]            = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_VXLAN_UDP_CSUM]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_REMCSUM_TX]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_REMCSUM_RX]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_GBP]               = { .type = NETLINK_TYPE_FLAG },
-        [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
-        [IFLA_VXLAN_COLLECT_METADATA]  = { .type = NETLINK_TYPE_U8 },
-        [IFLA_VXLAN_LABEL]             = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VXLAN_GPE]               = { .type = NETLINK_TYPE_FLAG },
-        [IFLA_VXLAN_TTL_INHERIT]       = { .type = NETLINK_TYPE_FLAG },
-        [IFLA_VXLAN_DF]                = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_bond_arp_target_types[] = {
-        [BOND_ARP_TARGETS_0]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_1]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_2]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_3]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_4]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_5]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_6]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_7]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_8]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_9]        = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_10]       = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_11]       = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_12]       = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_13]       = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_14]       = { .type = NETLINK_TYPE_U32 },
-        [BOND_ARP_TARGETS_MAX]      = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_bond_arp_type_system = {
-        .count = ELEMENTSOF(rtnl_bond_arp_target_types),
-        .types = rtnl_bond_arp_target_types,
-};
-
-static const NLType rtnl_link_info_data_bond_types[] = {
-        [IFLA_BOND_MODE]                = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_ACTIVE_SLAVE]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_MIIMON]              = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_UPDELAY]             = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_DOWNDELAY]           = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_USE_CARRIER]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_ARP_INTERVAL]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_ARP_IP_TARGET]       = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_arp_type_system },
-        [IFLA_BOND_ARP_VALIDATE]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_ARP_ALL_TARGETS]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_PRIMARY]             = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_PRIMARY_RESELECT]    = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_FAIL_OVER_MAC]       = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_XMIT_HASH_POLICY]    = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_RESEND_IGMP]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_NUM_PEER_NOTIF]      = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_ALL_SLAVES_ACTIVE]   = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_MIN_LINKS]           = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_LP_INTERVAL]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_PACKETS_PER_SLAVE]   = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BOND_AD_LACP_RATE]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_AD_SELECT]           = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_AD_INFO]             = { .type = NETLINK_TYPE_NESTED },
-        [IFLA_BOND_AD_ACTOR_SYS_PRIO]   = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BOND_AD_USER_PORT_KEY]    = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BOND_AD_ACTOR_SYSTEM]     = { .type = NETLINK_TYPE_ETHER_ADDR },
-        [IFLA_BOND_TLB_DYNAMIC_LB]      = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_link_info_data_iptun_types[] = {
-        [IFLA_IPTUN_LINK]                = { .type = NETLINK_TYPE_U32 },
-        [IFLA_IPTUN_LOCAL]               = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_IPTUN_REMOTE]              = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_IPTUN_TTL]                 = { .type = NETLINK_TYPE_U8 },
-        [IFLA_IPTUN_TOS]                 = { .type = NETLINK_TYPE_U8 },
-        [IFLA_IPTUN_PMTUDISC]            = { .type = NETLINK_TYPE_U8 },
-        [IFLA_IPTUN_FLAGS]               = { .type = NETLINK_TYPE_U16 },
-        [IFLA_IPTUN_PROTO]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_IPTUN_6RD_PREFIX]          = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_IPTUN_6RD_RELAY_PREFIX]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_IPTUN_6RD_PREFIXLEN]       = { .type = NETLINK_TYPE_U16 },
-        [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 },
-        [IFLA_IPTUN_ENCAP_TYPE]          = { .type = NETLINK_TYPE_U16 },
-        [IFLA_IPTUN_ENCAP_FLAGS]         = { .type = NETLINK_TYPE_U16 },
-        [IFLA_IPTUN_ENCAP_SPORT]         = { .type = NETLINK_TYPE_U16 },
-        [IFLA_IPTUN_ENCAP_DPORT]         = { .type = NETLINK_TYPE_U16 },
-};
-
-static  const NLType rtnl_link_info_data_ipgre_types[] = {
-        [IFLA_GRE_LINK]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_IFLAGS]       = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GRE_OFLAGS]       = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GRE_IKEY]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_OKEY]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_LOCAL]        = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_GRE_REMOTE]       = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_GRE_TTL]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GRE_TOS]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GRE_PMTUDISC]     = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GRE_FLOWINFO]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_FLAGS]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_ENCAP_TYPE]   = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GRE_ENCAP_FLAGS]  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GRE_ENCAP_SPORT]  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GRE_ENCAP_DPORT]  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GRE_ERSPAN_INDEX] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_ipvti_types[] = {
-        [IFLA_VTI_LINK]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VTI_IKEY]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VTI_OKEY]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VTI_LOCAL]        = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_VTI_REMOTE]       = { .type = NETLINK_TYPE_IN_ADDR },
-};
-
-static const NLType rtnl_link_info_data_ip6tnl_types[] = {
-        [IFLA_IPTUN_LINK]                = { .type = NETLINK_TYPE_U32 },
-        [IFLA_IPTUN_LOCAL]               = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_IPTUN_REMOTE]              = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_IPTUN_TTL]                 = { .type = NETLINK_TYPE_U8 },
-        [IFLA_IPTUN_FLAGS]               = { .type = NETLINK_TYPE_U32 },
-        [IFLA_IPTUN_PROTO]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_IPTUN_ENCAP_LIMIT]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_IPTUN_FLOWINFO]            = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_vrf_types[] = {
-        [IFLA_VRF_TABLE]                 = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_geneve_types[] = {
-        [IFLA_GENEVE_ID]                = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GENEVE_TTL]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GENEVE_TOS]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GENEVE_PORT]              = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GENEVE_REMOTE]            = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_GENEVE_REMOTE6]           = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_GENEVE_UDP_CSUM]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GENEVE_LABEL]             = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GENEVE_TTL_INHERIT]       = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GENEVE_DF]                = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_link_info_data_can_types[] = {
-        [IFLA_CAN_BITTIMING]            = { .size = sizeof(struct can_bittiming) },
-        [IFLA_CAN_RESTART_MS]           = { .type = NETLINK_TYPE_U32 },
-        [IFLA_CAN_CTRLMODE]             = { .size = sizeof(struct can_ctrlmode) },
-        [IFLA_CAN_TERMINATION]          = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_link_info_data_macsec_types[] = {
-        [IFLA_MACSEC_SCI]            = { .type = NETLINK_TYPE_U64 },
-        [IFLA_MACSEC_PORT]           = { .type = NETLINK_TYPE_U16 },
-        [IFLA_MACSEC_ICV_LEN]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_MACSEC_CIPHER_SUITE]   = { .type = NETLINK_TYPE_U64 },
-        [IFLA_MACSEC_WINDOW]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_MACSEC_ENCODING_SA]    = { .type = NETLINK_TYPE_U8 },
-        [IFLA_MACSEC_ENCRYPT]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_MACSEC_PROTECT]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_MACSEC_INC_SCI]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_MACSEC_ES]             = { .type = NETLINK_TYPE_U8 },
-        [IFLA_MACSEC_SCB]            = { .type = NETLINK_TYPE_U8 },
-        [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NETLINK_TYPE_U8 },
-        [IFLA_MACSEC_VALIDATION]     = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_link_info_data_xfrm_types[] = {
-        [IFLA_XFRM_LINK]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_XFRM_IF_ID]        = { .type = NETLINK_TYPE_U32 }
-};
-
-static const NLType rtnl_link_info_data_bareudp_types[] = {
-        [IFLA_BAREUDP_PORT]            = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BAREUDP_ETHERTYPE]       = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BAREUDP_SRCPORT_MIN]     = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BAREUDP_MULTIPROTO_MODE] = { .type = NETLINK_TYPE_FLAG },
-};
-
-/* these strings must match the .kind entries in the kernel */
-static const char* const nl_union_link_info_data_table[] = {
-        [NL_UNION_LINK_INFO_DATA_BOND] = "bond",
-        [NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge",
-        [NL_UNION_LINK_INFO_DATA_VLAN] = "vlan",
-        [NL_UNION_LINK_INFO_DATA_VETH] = "veth",
-        [NL_UNION_LINK_INFO_DATA_DUMMY] = "dummy",
-        [NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan",
-        [NL_UNION_LINK_INFO_DATA_MACVTAP] = "macvtap",
-        [NL_UNION_LINK_INFO_DATA_IPVLAN] = "ipvlan",
-        [NL_UNION_LINK_INFO_DATA_IPVTAP] = "ipvtap",
-        [NL_UNION_LINK_INFO_DATA_VXLAN] = "vxlan",
-        [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] = "ipip",
-        [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] = "gre",
-        [NL_UNION_LINK_INFO_DATA_ERSPAN] = "erspan",
-        [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] = "gretap",
-        [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] = "ip6gre",
-        [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = "ip6gretap",
-        [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] = "sit",
-        [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] = "vti",
-        [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] = "vti6",
-        [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl",
-        [NL_UNION_LINK_INFO_DATA_VRF] = "vrf",
-        [NL_UNION_LINK_INFO_DATA_VCAN] = "vcan",
-        [NL_UNION_LINK_INFO_DATA_GENEVE] = "geneve",
-        [NL_UNION_LINK_INFO_DATA_VXCAN] = "vxcan",
-        [NL_UNION_LINK_INFO_DATA_WIREGUARD] = "wireguard",
-        [NL_UNION_LINK_INFO_DATA_NETDEVSIM] = "netdevsim",
-        [NL_UNION_LINK_INFO_DATA_CAN] = "can",
-        [NL_UNION_LINK_INFO_DATA_MACSEC] = "macsec",
-        [NL_UNION_LINK_INFO_DATA_NLMON] = "nlmon",
-        [NL_UNION_LINK_INFO_DATA_XFRM] = "xfrm",
-        [NL_UNION_LINK_INFO_DATA_IFB] = "ifb",
-        [NL_UNION_LINK_INFO_DATA_BAREUDP] = "bareudp",
-        [NL_UNION_LINK_INFO_DATA_BATADV] = "batadv",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
-
-static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
-        [NL_UNION_LINK_INFO_DATA_BOND] =             { .count = ELEMENTSOF(rtnl_link_info_data_bond_types),
-                                                       .types = rtnl_link_info_data_bond_types },
-        [NL_UNION_LINK_INFO_DATA_BRIDGE] =           { .count = ELEMENTSOF(rtnl_link_info_data_bridge_types),
-                                                       .types = rtnl_link_info_data_bridge_types },
-        [NL_UNION_LINK_INFO_DATA_VLAN] =             { .count = ELEMENTSOF(rtnl_link_info_data_vlan_types),
-                                                       .types = rtnl_link_info_data_vlan_types },
-        [NL_UNION_LINK_INFO_DATA_VETH] =             { .count = ELEMENTSOF(rtnl_link_info_data_veth_types),
-                                                       .types = rtnl_link_info_data_veth_types },
-        [NL_UNION_LINK_INFO_DATA_MACVLAN] =          { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
-                                                       .types = rtnl_link_info_data_macvlan_types },
-        [NL_UNION_LINK_INFO_DATA_MACVTAP] =          { .count = ELEMENTSOF(rtnl_link_info_data_macvlan_types),
-                                                       .types = rtnl_link_info_data_macvlan_types },
-        [NL_UNION_LINK_INFO_DATA_IPVLAN] =           { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types),
-                                                       .types = rtnl_link_info_data_ipvlan_types },
-        [NL_UNION_LINK_INFO_DATA_IPVTAP] =           { .count = ELEMENTSOF(rtnl_link_info_data_ipvlan_types),
-                                                       .types = rtnl_link_info_data_ipvlan_types },
-        [NL_UNION_LINK_INFO_DATA_VXLAN] =            { .count = ELEMENTSOF(rtnl_link_info_data_vxlan_types),
-                                                       .types = rtnl_link_info_data_vxlan_types },
-        [NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL] =      { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types),
-                                                       .types = rtnl_link_info_data_iptun_types },
-        [NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL] =     { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
-                                                       .types = rtnl_link_info_data_ipgre_types },
-        [NL_UNION_LINK_INFO_DATA_ERSPAN] =           { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
-                                                       .types = rtnl_link_info_data_ipgre_types },
-        [NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL] =  { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
-                                                       .types = rtnl_link_info_data_ipgre_types },
-        [NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL] =    { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
-                                                       .types = rtnl_link_info_data_ipgre_types },
-        [NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL] = { .count = ELEMENTSOF(rtnl_link_info_data_ipgre_types),
-                                                       .types = rtnl_link_info_data_ipgre_types },
-        [NL_UNION_LINK_INFO_DATA_SIT_TUNNEL] =       { .count = ELEMENTSOF(rtnl_link_info_data_iptun_types),
-                                                       .types = rtnl_link_info_data_iptun_types },
-        [NL_UNION_LINK_INFO_DATA_VTI_TUNNEL] =       { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types),
-                                                       .types = rtnl_link_info_data_ipvti_types },
-        [NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL] =      { .count = ELEMENTSOF(rtnl_link_info_data_ipvti_types),
-                                                       .types = rtnl_link_info_data_ipvti_types },
-        [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] =    { .count = ELEMENTSOF(rtnl_link_info_data_ip6tnl_types),
-                                                       .types = rtnl_link_info_data_ip6tnl_types },
-        [NL_UNION_LINK_INFO_DATA_VRF] =              { .count = ELEMENTSOF(rtnl_link_info_data_vrf_types),
-                                                       .types = rtnl_link_info_data_vrf_types },
-        [NL_UNION_LINK_INFO_DATA_GENEVE] =           { .count = ELEMENTSOF(rtnl_link_info_data_geneve_types),
-                                                       .types = rtnl_link_info_data_geneve_types },
-        [NL_UNION_LINK_INFO_DATA_VXCAN] =            { .count = ELEMENTSOF(rtnl_link_info_data_vxcan_types),
-                                                       .types = rtnl_link_info_data_vxcan_types },
-        [NL_UNION_LINK_INFO_DATA_CAN] =              { .count = ELEMENTSOF(rtnl_link_info_data_can_types),
-                                                       .types = rtnl_link_info_data_can_types },
-        [NL_UNION_LINK_INFO_DATA_MACSEC] =           { .count = ELEMENTSOF(rtnl_link_info_data_macsec_types),
-                                                       .types = rtnl_link_info_data_macsec_types },
-        [NL_UNION_LINK_INFO_DATA_XFRM] =             { .count = ELEMENTSOF(rtnl_link_info_data_xfrm_types),
-                                                       .types = rtnl_link_info_data_xfrm_types },
-        [NL_UNION_LINK_INFO_DATA_BAREUDP] =          { .count = ELEMENTSOF(rtnl_link_info_data_bareudp_types),
-                                                       .types = rtnl_link_info_data_bareudp_types },
-        [NL_UNION_LINK_INFO_DATA_BATADV] =           { .count = ELEMENTSOF(rtnl_link_info_data_batadv_types),
-                                                       .types = rtnl_link_info_data_batadv_types },
-};
-
-static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
-        .num = _NL_UNION_LINK_INFO_DATA_MAX,
-        .lookup = nl_union_link_info_data_from_string,
-        .type_systems = rtnl_link_info_data_type_systems,
-        .match_type = NL_MATCH_SIBLING,
-        .match = IFLA_INFO_KIND,
-};
-
-static const NLType rtnl_link_info_types[] = {
-        [IFLA_INFO_KIND]        = { .type = NETLINK_TYPE_STRING },
-        [IFLA_INFO_DATA]        = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union },
-/*
-        [IFLA_INFO_XSTATS],
-        [IFLA_INFO_SLAVE_KIND]  = { .type = NETLINK_TYPE_STRING },
-        [IFLA_INFO_SLAVE_DATA]  = { .type = NETLINK_TYPE_NESTED },
-*/
-};
-
-static const NLTypeSystem rtnl_link_info_type_system = {
-        .count = ELEMENTSOF(rtnl_link_info_types),
-        .types = rtnl_link_info_types,
-};
-
-static const struct NLType rtnl_prot_info_bridge_port_types[] = {
-        [IFLA_BRPORT_STATE]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_COST]                = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRPORT_PRIORITY]            = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRPORT_MODE]                = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_GUARD]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_PROTECT]             = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_FAST_LEAVE]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_LEARNING]            = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_UNICAST_FLOOD]       = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_PROXYARP]            = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_LEARNING_SYNC]       = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_PROXYARP_WIFI]       = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_ROOT_ID]             = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_BRIDGE_ID]           = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_DESIGNATED_PORT]     = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRPORT_DESIGNATED_COST]     = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRPORT_ID]                  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRPORT_NO]                  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRPORT_TOPOLOGY_CHANGE_ACK] = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_CONFIG_PENDING]      = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_MESSAGE_AGE_TIMER]   = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BRPORT_FORWARD_DELAY_TIMER] = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BRPORT_HOLD_TIMER]          = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BRPORT_FLUSH]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_MULTICAST_ROUTER]    = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_PAD]                 = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_MCAST_FLOOD]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_MCAST_TO_UCAST]      = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_VLAN_TUNNEL]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_BCAST_FLOOD]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_GROUP_FWD_MASK]      = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRPORT_NEIGH_SUPPRESS]      = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_ISOLATED]            = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_BACKUP_PORT]         = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_prot_info_type_systems[] = {
-        [AF_BRIDGE] =   { .count = ELEMENTSOF(rtnl_prot_info_bridge_port_types),
-                          .types = rtnl_prot_info_bridge_port_types },
-};
-
-static const NLTypeSystemUnion rtnl_prot_info_type_system_union = {
-        .num = AF_MAX,
-        .type_systems = rtnl_prot_info_type_systems,
-        .match_type = NL_MATCH_PROTOCOL,
-};
-
-static const struct NLType rtnl_af_spec_inet6_types[] = {
-        [IFLA_INET6_FLAGS]              = { .type = NETLINK_TYPE_U32 },
-/*
-        IFLA_INET6_CONF,
-        IFLA_INET6_STATS,
-        IFLA_INET6_MCAST,
-        IFLA_INET6_CACHEINFO,
-        IFLA_INET6_ICMP6STATS,
-*/
-        [IFLA_INET6_TOKEN]              = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFLA_INET6_ADDR_GEN_MODE]      = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLTypeSystem rtnl_af_spec_inet6_type_system = {
-        .count = ELEMENTSOF(rtnl_af_spec_inet6_types),
-        .types = rtnl_af_spec_inet6_types,
-};
-
-static const NLType rtnl_af_spec_unspec_types[] = {
-        [AF_INET6] =    { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_af_spec_inet6_type_system },
-};
-
-static const NLType rtnl_af_spec_bridge_types[] = {
-        [IFLA_BRIDGE_FLAGS]     = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRIDGE_VLAN_INFO] = { .size = sizeof(struct bridge_vlan_info) },
-};
-
-static const NLTypeSystem rtnl_af_spec_type_systems[] = {
-        [AF_UNSPEC] = { .count = ELEMENTSOF(rtnl_af_spec_unspec_types),
-                        .types = rtnl_af_spec_unspec_types },
-        [AF_BRIDGE] = { .count = ELEMENTSOF(rtnl_af_spec_bridge_types),
-                        .types = rtnl_af_spec_bridge_types },
-};
-
-static const NLTypeSystemUnion rtnl_af_spec_type_system_union = {
-        .num = AF_MAX,
-        .type_systems = rtnl_af_spec_type_systems,
-        .match_type = NL_MATCH_PROTOCOL,
-};
-
-static const NLType rtnl_prop_list_types[] = {
-        [IFLA_ALT_IFNAME]       = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
-};
-
-static const NLTypeSystem rtnl_prop_list_type_system = {
-        .count = ELEMENTSOF(rtnl_prop_list_types),
-        .types = rtnl_prop_list_types,
-};
-
-static const NLType rtnl_vf_vlan_list_types[] = {
-        [IFLA_VF_VLAN_INFO]  = { .size = sizeof(struct ifla_vf_vlan_info) },
-};
-
-static const NLTypeSystem rtnl_vf_vlan_type_system = {
-        .count = ELEMENTSOF(rtnl_vf_vlan_list_types),
-        .types = rtnl_vf_vlan_list_types,
-};
-
-static const NLType rtnl_vf_vlan_info_types[] = {
-        [IFLA_VF_MAC]           = { .size = sizeof(struct ifla_vf_mac) },
-        [IFLA_VF_VLAN]          = { .size = sizeof(struct ifla_vf_vlan) },
-        [IFLA_VF_VLAN_LIST]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_type_system},
-        [IFLA_VF_TX_RATE]       = { .size = sizeof(struct ifla_vf_tx_rate) },
-        [IFLA_VF_SPOOFCHK]      = { .size = sizeof(struct ifla_vf_spoofchk) },
-        [IFLA_VF_RATE]          = { .size = sizeof(struct ifla_vf_rate) },
-        [IFLA_VF_LINK_STATE]    = { .size = sizeof(struct ifla_vf_link_state) },
-        [IFLA_VF_RSS_QUERY_EN]  = { .size = sizeof(struct ifla_vf_rss_query_en) },
-        [IFLA_VF_TRUST]         = { .size = sizeof(struct ifla_vf_trust) },
-        [IFLA_VF_IB_NODE_GUID]  = { .size = sizeof(struct ifla_vf_guid) },
-        [IFLA_VF_IB_PORT_GUID]  = { .size = sizeof(struct ifla_vf_guid) },
-};
-
-static const NLTypeSystem rtnl_vf_vlan_info_type_system = {
-        .count = ELEMENTSOF(rtnl_vf_vlan_info_types),
-        .types = rtnl_vf_vlan_info_types,
-};
-
-static const NLType rtnl_link_io_srv_types[] = {
-        [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_info_type_system },
-};
-
-static const NLTypeSystem rtnl_io_srv_type_system = {
-        .count = ELEMENTSOF(rtnl_link_io_srv_types),
-        .types = rtnl_link_io_srv_types,
-};
-
-static const NLType rtnl_link_types[] = {
-        [IFLA_ADDRESS]          = { .type = NETLINK_TYPE_ETHER_ADDR },
-        [IFLA_BROADCAST]        = { .type = NETLINK_TYPE_ETHER_ADDR },
-        [IFLA_IFNAME]           = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
-        [IFLA_MTU]              = { .type = NETLINK_TYPE_U32 },
-        [IFLA_LINK]             = { .type = NETLINK_TYPE_U32 },
-        [IFLA_QDISC]            = { .type = NETLINK_TYPE_STRING },
-        [IFLA_STATS]            = { .size = sizeof(struct rtnl_link_stats) },
-/*
-        [IFLA_COST],
-        [IFLA_PRIORITY],
-*/
-        [IFLA_MASTER]           = { .type = NETLINK_TYPE_U32 },
-/*
-        [IFLA_WIRELESS],
-*/
-        [IFLA_PROTINFO]         = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union },
-        [IFLA_TXQLEN]           = { .type = NETLINK_TYPE_U32 },
-/*
-        [IFLA_MAP]              = { .len = sizeof(struct rtnl_link_ifmap) },
-*/
-        [IFLA_WEIGHT]           = { .type = NETLINK_TYPE_U32 },
-        [IFLA_OPERSTATE]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_LINKMODE]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_LINKINFO]         = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system },
-        [IFLA_NET_NS_PID]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_IFALIAS]          = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 },
-        [IFLA_NUM_VF]           = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VFINFO_LIST]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_io_srv_type_system },
-        [IFLA_STATS64]          = { .size = sizeof(struct rtnl_link_stats64) },
-/*
-        [IFLA_VF_PORTS]         = { .type = NETLINK_TYPE_NESTED },
-        [IFLA_PORT_SELF]        = { .type = NETLINK_TYPE_NESTED },
-*/
-        [IFLA_AF_SPEC]          = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_af_spec_type_system_union },
-/*
-        [IFLA_VF_PORTS],
-        [IFLA_PORT_SELF],
-*/
-        [IFLA_GROUP]            = { .type = NETLINK_TYPE_U32 },
-        [IFLA_NET_NS_FD]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_EXT_MASK]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_PROMISCUITY]      = { .type = NETLINK_TYPE_U32 },
-        [IFLA_NUM_TX_QUEUES]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_NUM_RX_QUEUES]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GSO_MAX_SEGS]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GSO_MAX_SIZE]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_CARRIER]          = { .type = NETLINK_TYPE_U8 },
-/*
-        [IFLA_PHYS_PORT_ID]     = { .type = NETLINK_TYPE_BINARY, .len = MAX_PHYS_PORT_ID_LEN },
-*/
-        [IFLA_MIN_MTU]          = { .type = NETLINK_TYPE_U32 },
-        [IFLA_MAX_MTU]          = { .type = NETLINK_TYPE_U32 },
-        [IFLA_PROP_LIST]        = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_prop_list_type_system },
-        [IFLA_ALT_IFNAME]       = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
-};
-
-static const NLTypeSystem rtnl_link_type_system = {
-        .count = ELEMENTSOF(rtnl_link_types),
-        .types = rtnl_link_types,
-};
-
-/* IFA_FLAGS was defined in kernel 3.14, but we still support older
- * kernels where IFA_MAX is lower. */
-static const NLType rtnl_address_types[] = {
-        [IFA_ADDRESS]           = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFA_LOCAL]             = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFA_LABEL]             = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
-        [IFA_BROADCAST]         = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFA_ANYCAST]           = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFA_CACHEINFO]         = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) },
-        [IFA_MULTICAST]         = { .type = NETLINK_TYPE_IN_ADDR },
-        [IFA_FLAGS]             = { .type = NETLINK_TYPE_U32 },
-        [IFA_RT_PRIORITY]       = { .type = NETLINK_TYPE_U32 },
-        [IFA_TARGET_NETNSID]    = { .type = NETLINK_TYPE_S32 },
-};
-
-static const NLTypeSystem rtnl_address_type_system = {
-        .count = ELEMENTSOF(rtnl_address_types),
-        .types = rtnl_address_types,
-};
-
-/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
-
-static const NLType rtnl_route_metrics_types[] = {
-        [RTAX_MTU]                = { .type = NETLINK_TYPE_U32 },
-        [RTAX_WINDOW]             = { .type = NETLINK_TYPE_U32 },
-        [RTAX_RTT]                = { .type = NETLINK_TYPE_U32 },
-        [RTAX_RTTVAR]             = { .type = NETLINK_TYPE_U32 },
-        [RTAX_SSTHRESH]           = { .type = NETLINK_TYPE_U32 },
-        [RTAX_CWND]               = { .type = NETLINK_TYPE_U32 },
-        [RTAX_ADVMSS]             = { .type = NETLINK_TYPE_U32 },
-        [RTAX_REORDERING]         = { .type = NETLINK_TYPE_U32 },
-        [RTAX_HOPLIMIT]           = { .type = NETLINK_TYPE_U32 },
-        [RTAX_INITCWND]           = { .type = NETLINK_TYPE_U32 },
-        [RTAX_FEATURES]           = { .type = NETLINK_TYPE_U32 },
-        [RTAX_RTO_MIN]            = { .type = NETLINK_TYPE_U32 },
-        [RTAX_INITRWND]           = { .type = NETLINK_TYPE_U32 },
-        [RTAX_QUICKACK]           = { .type = NETLINK_TYPE_U32 },
-        [RTAX_CC_ALGO]            = { .type = NETLINK_TYPE_U32 },
-        [RTAX_FASTOPEN_NO_COOKIE] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_route_metrics_type_system = {
-        .count = ELEMENTSOF(rtnl_route_metrics_types),
-        .types = rtnl_route_metrics_types,
-};
-
-static const NLType rtnl_route_types[] = {
-        [RTA_DST]               = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
-        [RTA_SRC]               = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
-        [RTA_IIF]               = { .type = NETLINK_TYPE_U32 },
-        [RTA_OIF]               = { .type = NETLINK_TYPE_U32 },
-        [RTA_GATEWAY]           = { .type = NETLINK_TYPE_IN_ADDR },
-        [RTA_PRIORITY]          = { .type = NETLINK_TYPE_U32 },
-        [RTA_PREFSRC]           = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
-        [RTA_METRICS]           = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_metrics_type_system},
-        [RTA_MULTIPATH]         = { .size = sizeof(struct rtnexthop) },
-        [RTA_FLOW]              = { .type = NETLINK_TYPE_U32 }, /* 6? */
-        [RTA_CACHEINFO]         = { .size = sizeof(struct rta_cacheinfo) },
-        [RTA_TABLE]             = { .type = NETLINK_TYPE_U32 },
-        [RTA_MARK]              = { .type = NETLINK_TYPE_U32 },
-        [RTA_MFC_STATS]         = { .type = NETLINK_TYPE_U64 },
-        [RTA_VIA]               = { /* See struct rtvia */ },
-        [RTA_NEWDST]            = { .type = NETLINK_TYPE_U32 },
-        [RTA_PREF]              = { .type = NETLINK_TYPE_U8 },
-        [RTA_ENCAP_TYPE]        = { .type = NETLINK_TYPE_U16 },
-        [RTA_ENCAP]             = { .type = NETLINK_TYPE_NESTED }, /* Multiple type systems i.e. LWTUNNEL_ENCAP_MPLS/LWTUNNEL_ENCAP_IP/LWTUNNEL_ENCAP_ILA etc... */
-        [RTA_EXPIRES]           = { .type = NETLINK_TYPE_U32 },
-        [RTA_UID]               = { .type = NETLINK_TYPE_U32 },
-        [RTA_TTL_PROPAGATE]     = { .type = NETLINK_TYPE_U8 },
-        [RTA_IP_PROTO]          = { .type = NETLINK_TYPE_U8 },
-        [RTA_SPORT]             = { .type = NETLINK_TYPE_U16 },
-        [RTA_DPORT]             = { .type = NETLINK_TYPE_U16 },
-        [RTA_NH_ID]             = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_route_type_system = {
-        .count = ELEMENTSOF(rtnl_route_types),
-        .types = rtnl_route_types,
-};
-
-static const NLType rtnl_neigh_types[] = {
-        [NDA_DST]               = { .type = NETLINK_TYPE_IN_ADDR },
-        [NDA_LLADDR]            = { /* struct ether_addr, struct in_addr, or struct in6_addr */ },
-        [NDA_CACHEINFO]         = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) },
-        [NDA_PROBES]            = { .type = NETLINK_TYPE_U32 },
-        [NDA_VLAN]              = { .type = NETLINK_TYPE_U16 },
-        [NDA_PORT]              = { .type = NETLINK_TYPE_U16 },
-        [NDA_VNI]               = { .type = NETLINK_TYPE_U32 },
-        [NDA_IFINDEX]           = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_neigh_type_system = {
-        .count = ELEMENTSOF(rtnl_neigh_types),
-        .types = rtnl_neigh_types,
-};
-
-static const NLType rtnl_addrlabel_types[] = {
-        [IFAL_ADDRESS]         = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
-        [IFAL_LABEL]           = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_addrlabel_type_system = {
-        .count = ELEMENTSOF(rtnl_addrlabel_types),
-        .types = rtnl_addrlabel_types,
-};
-
-static const NLType rtnl_routing_policy_rule_types[] = {
-        [FRA_DST]                 = { .type = NETLINK_TYPE_IN_ADDR },
-        [FRA_SRC]                 = { .type = NETLINK_TYPE_IN_ADDR },
-        [FRA_IIFNAME]             = { .type = NETLINK_TYPE_STRING },
-        [FRA_GOTO]                = { .type = NETLINK_TYPE_U32 },
-        [FRA_PRIORITY]            = { .type = NETLINK_TYPE_U32 },
-        [FRA_FWMARK]              = { .type = NETLINK_TYPE_U32 },
-        [FRA_FLOW]                = { .type = NETLINK_TYPE_U32 },
-        [FRA_TUN_ID]              = { .type = NETLINK_TYPE_U64 },
-        [FRA_SUPPRESS_IFGROUP]    = { .type = NETLINK_TYPE_U32 },
-        [FRA_SUPPRESS_PREFIXLEN]  = { .type = NETLINK_TYPE_U32 },
-        [FRA_TABLE]               = { .type = NETLINK_TYPE_U32 },
-        [FRA_FWMASK]              = { .type = NETLINK_TYPE_U32 },
-        [FRA_OIFNAME]             = { .type = NETLINK_TYPE_STRING },
-        [FRA_PAD]                 = { .type = NETLINK_TYPE_U32 },
-        [FRA_L3MDEV]              = { .type = NETLINK_TYPE_U8 },
-        [FRA_UID_RANGE]           = { .size = sizeof(struct fib_rule_uid_range) },
-        [FRA_PROTOCOL]            = { .type = NETLINK_TYPE_U8 },
-        [FRA_IP_PROTO]            = { .type = NETLINK_TYPE_U8 },
-        [FRA_SPORT_RANGE]         = { .size = sizeof(struct fib_rule_port_range) },
-        [FRA_DPORT_RANGE]         = { .size = sizeof(struct fib_rule_port_range) },
-};
-
-static const NLTypeSystem rtnl_routing_policy_rule_type_system = {
-        .count = ELEMENTSOF(rtnl_routing_policy_rule_types),
-        .types = rtnl_routing_policy_rule_types,
-};
-
-static const NLType rtnl_nexthop_types[] = {
-        [NHA_ID]                  = { .type = NETLINK_TYPE_U32 },
-        [NHA_GROUP]               = { /* array of struct nexthop_grp */ },
-        [NHA_GROUP_TYPE]          = { .type = NETLINK_TYPE_U16 },
-        [NHA_BLACKHOLE]           = { .type = NETLINK_TYPE_FLAG },
-        [NHA_OIF]                 = { .type = NETLINK_TYPE_U32 },
-        [NHA_GATEWAY]             = { .type = NETLINK_TYPE_IN_ADDR },
-        [NHA_ENCAP_TYPE]          = { .type = NETLINK_TYPE_U16 },
-        [NHA_ENCAP]               = { .type = NETLINK_TYPE_NESTED },
-        [NHA_GROUPS]              = { .type = NETLINK_TYPE_FLAG },
-        [NHA_MASTER]              = { .type = NETLINK_TYPE_U32 },
-        [NHA_FDB]                 = { .type = NETLINK_TYPE_FLAG },
-};
-
-static const NLTypeSystem rtnl_nexthop_type_system = {
-       .count = ELEMENTSOF(rtnl_nexthop_types),
-       .types = rtnl_nexthop_types,
-};
-
-static const NLType rtnl_tca_option_data_cake_types[] = {
-        [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
-        [TCA_CAKE_OVERHEAD]    = { .type = NETLINK_TYPE_S32 },
-        [TCA_CAKE_MPU]         = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_codel_types[] = {
-        [TCA_CODEL_TARGET]        = { .type = NETLINK_TYPE_U32 },
-        [TCA_CODEL_LIMIT]         = { .type = NETLINK_TYPE_U32 },
-        [TCA_CODEL_INTERVAL]      = { .type = NETLINK_TYPE_U32 },
-        [TCA_CODEL_ECN]           = { .type = NETLINK_TYPE_U32 },
-        [TCA_CODEL_CE_THRESHOLD]  = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_drr_types[] = {
-        [TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_ets_quanta_types[] = {
-        [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32, },
-};
-
-static const NLTypeSystem rtnl_tca_option_data_ets_quanta_type_system = {
-        .count = ELEMENTSOF(rtnl_tca_option_data_ets_quanta_types),
-        .types = rtnl_tca_option_data_ets_quanta_types,
-};
-
-static const NLType rtnl_tca_option_data_ets_prio_types[] = {
-        [TCA_ETS_PRIOMAP_BAND] = { .type = NETLINK_TYPE_U8, },
-};
-
-static const NLTypeSystem rtnl_tca_option_data_ets_prio_type_system = {
-        .count = ELEMENTSOF(rtnl_tca_option_data_ets_prio_types),
-        .types = rtnl_tca_option_data_ets_prio_types,
-};
-
-static const NLType rtnl_tca_option_data_ets_types[] = {
-        [TCA_ETS_NBANDS]      = { .type = NETLINK_TYPE_U8 },
-        [TCA_ETS_NSTRICT]     = { .type = NETLINK_TYPE_U8 },
-        [TCA_ETS_QUANTA]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_quanta_type_system },
-        [TCA_ETS_PRIOMAP]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_prio_type_system },
-        [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_fq_types[] = {
-        [TCA_FQ_PLIMIT]             = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_FLOW_PLIMIT]        = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_QUANTUM]            = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_INITIAL_QUANTUM]    = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_RATE_ENABLE]        = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_FLOW_DEFAULT_RATE]  = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_FLOW_MAX_RATE]      = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_BUCKETS_LOG]        = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_FLOW_REFILL_DELAY]  = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_LOW_RATE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_CE_THRESHOLD]       = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_ORPHAN_MASK]        = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_fq_codel_types[] = {
-        [TCA_FQ_CODEL_TARGET]          = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_CODEL_LIMIT]           = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_CODEL_INTERVAL]        = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_CODEL_ECN]             = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_CODEL_FLOWS]           = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_CODEL_QUANTUM]         = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_CODEL_CE_THRESHOLD]    = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NETLINK_TYPE_U32 },
-        [TCA_FQ_CODEL_MEMORY_LIMIT]    = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_fq_pie_types[] = {
-        [TCA_FQ_PIE_LIMIT]   = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_gred_types[] = {
-        [TCA_GRED_DPS] = { .size = sizeof(struct tc_gred_sopt) },
-};
-
-static const NLType rtnl_tca_option_data_hhf_types[] = {
-        [TCA_HHF_BACKLOG_LIMIT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_htb_types[] = {
-        [TCA_HTB_PARMS]  = { .size = sizeof(struct tc_htb_opt) },
-        [TCA_HTB_INIT]   = { .size = sizeof(struct tc_htb_glob) },
-        [TCA_HTB_CTAB]   = { .size = TC_RTAB_SIZE },
-        [TCA_HTB_RTAB]   = { .size = TC_RTAB_SIZE },
-        [TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 },
-        [TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 },
-};
-
-static const NLType rtnl_tca_option_data_pie_types[] = {
-        [TCA_PIE_LIMIT]   = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_qfq_types[] = {
-        [TCA_QFQ_WEIGHT] = { .type = NETLINK_TYPE_U32 },
-        [TCA_QFQ_LMAX]   = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_sfb_types[] = {
-        [TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) },
-};
-
-static const NLType rtnl_tca_option_data_tbf_types[] = {
-        [TCA_TBF_PARMS]   = { .size = sizeof(struct tc_tbf_qopt) },
-        [TCA_TBF_RTAB]    = { .size = TC_RTAB_SIZE },
-        [TCA_TBF_PTAB]    = { .size = TC_RTAB_SIZE },
-        [TCA_TBF_RATE64]  = { .type = NETLINK_TYPE_U64 },
-        [TCA_TBF_PRATE64] = { .type = NETLINK_TYPE_U64 },
-        [TCA_TBF_BURST]   = { .type = NETLINK_TYPE_U32 },
-        [TCA_TBF_PBURST]  = { .type = NETLINK_TYPE_U32 },
-};
-
-static const char* const nl_union_tca_option_data_table[] = {
-        [NL_UNION_TCA_OPTION_DATA_CAKE] = "cake",
-        [NL_UNION_TCA_OPTION_DATA_CODEL] = "codel",
-        [NL_UNION_TCA_OPTION_DATA_DRR] = "drr",
-        [NL_UNION_TCA_OPTION_DATA_ETS] = "ets",
-        [NL_UNION_TCA_OPTION_DATA_FQ] = "fq",
-        [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = "fq_codel",
-        [NL_UNION_TCA_OPTION_DATA_FQ_PIE] = "fq_pie",
-        [NL_UNION_TCA_OPTION_DATA_GRED] = "gred",
-        [NL_UNION_TCA_OPTION_DATA_HHF] = "hhf",
-        [NL_UNION_TCA_OPTION_DATA_HTB] = "htb",
-        [NL_UNION_TCA_OPTION_DATA_PIE] = "pie",
-        [NL_UNION_TCA_OPTION_DATA_QFQ] = "qfq",
-        [NL_UNION_TCA_OPTION_DATA_SFB] = "sfb",
-        [NL_UNION_TCA_OPTION_DATA_TBF] = "tbf",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(nl_union_tca_option_data, NLUnionTCAOptionData);
-
-static const NLTypeSystem rtnl_tca_option_data_type_systems[] = {
-        [NL_UNION_TCA_OPTION_DATA_CAKE] =        { .count = ELEMENTSOF(rtnl_tca_option_data_cake_types),
-                                                   .types = rtnl_tca_option_data_cake_types },
-        [NL_UNION_TCA_OPTION_DATA_CODEL] =       { .count = ELEMENTSOF(rtnl_tca_option_data_codel_types),
-                                                   .types = rtnl_tca_option_data_codel_types },
-        [NL_UNION_TCA_OPTION_DATA_DRR] =         { .count = ELEMENTSOF(rtnl_tca_option_data_drr_types),
-                                                   .types = rtnl_tca_option_data_drr_types },
-        [NL_UNION_TCA_OPTION_DATA_ETS] =         { .count = ELEMENTSOF(rtnl_tca_option_data_ets_types),
-                                                   .types = rtnl_tca_option_data_ets_types },
-        [NL_UNION_TCA_OPTION_DATA_FQ] =          { .count = ELEMENTSOF(rtnl_tca_option_data_fq_types),
-                                                   .types = rtnl_tca_option_data_fq_types },
-        [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] =    { .count = ELEMENTSOF(rtnl_tca_option_data_fq_codel_types),
-                                                   .types = rtnl_tca_option_data_fq_codel_types },
-        [NL_UNION_TCA_OPTION_DATA_FQ_PIE] =      { .count = ELEMENTSOF(rtnl_tca_option_data_fq_pie_types),
-                                                   .types = rtnl_tca_option_data_fq_pie_types },
-        [NL_UNION_TCA_OPTION_DATA_GRED] =        { .count = ELEMENTSOF(rtnl_tca_option_data_gred_types),
-                                                   .types = rtnl_tca_option_data_gred_types },
-        [NL_UNION_TCA_OPTION_DATA_HHF] =         { .count = ELEMENTSOF(rtnl_tca_option_data_hhf_types),
-                                                   .types = rtnl_tca_option_data_hhf_types },
-        [NL_UNION_TCA_OPTION_DATA_HTB] =         { .count = ELEMENTSOF(rtnl_tca_option_data_htb_types),
-                                                   .types = rtnl_tca_option_data_htb_types },
-        [NL_UNION_TCA_OPTION_DATA_PIE] =         { .count = ELEMENTSOF(rtnl_tca_option_data_pie_types),
-                                                   .types = rtnl_tca_option_data_pie_types },
-        [NL_UNION_TCA_OPTION_DATA_QFQ] =         { .count = ELEMENTSOF(rtnl_tca_option_data_qfq_types),
-                                                   .types = rtnl_tca_option_data_qfq_types },
-        [NL_UNION_TCA_OPTION_DATA_SFB] =         { .count = ELEMENTSOF(rtnl_tca_option_data_sfb_types),
-                                                   .types = rtnl_tca_option_data_sfb_types },
-        [NL_UNION_TCA_OPTION_DATA_TBF] =         { .count = ELEMENTSOF(rtnl_tca_option_data_tbf_types),
-                                                   .types = rtnl_tca_option_data_tbf_types },
-};
-
-static const NLTypeSystemUnion rtnl_tca_option_data_type_system_union = {
-        .num = _NL_UNION_TCA_OPTION_DATA_MAX,
-        .lookup = nl_union_tca_option_data_from_string,
-        .type_systems = rtnl_tca_option_data_type_systems,
-        .match_type = NL_MATCH_SIBLING,
-        .match = TCA_KIND,
-};
-
-static const NLType rtnl_tca_types[] = {
-        [TCA_KIND]           = { .type = NETLINK_TYPE_STRING },
-        [TCA_OPTIONS]        = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_tca_option_data_type_system_union },
-        [TCA_INGRESS_BLOCK]  = { .type = NETLINK_TYPE_U32 },
-        [TCA_EGRESS_BLOCK]   = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem rtnl_tca_type_system = {
-        .count = ELEMENTSOF(rtnl_tca_types),
-        .types = rtnl_tca_types,
-};
-
-static const NLType mdb_types[] = {
-        [MDBA_SET_ENTRY]     = { .size = sizeof(struct br_port_msg) },
-};
-
-static const NLTypeSystem rtnl_mdb_type_system = {
-        .count = ELEMENTSOF(mdb_types),
-        .types = mdb_types,
-};
+DEFINE_TYPE_SYSTEM(empty);
 
 static const NLType error_types[] = {
         [NLMSGERR_ATTR_MSG]  = { .type = NETLINK_TYPE_STRING },
         [NLMSGERR_ATTR_OFFS] = { .type = NETLINK_TYPE_U32 },
 };
 
-static const NLTypeSystem error_type_system = {
-        .count = ELEMENTSOF(error_types),
-        .types = error_types,
-};
-
-static const NLType rtnl_types[] = {
-        [NLMSG_DONE]       = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
-        [NLMSG_ERROR]      = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
-        [RTM_NEWLINK]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-        [RTM_DELLINK]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-        [RTM_GETLINK]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-        [RTM_SETLINK]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-        [RTM_NEWLINKPROP]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-        [RTM_DELLINKPROP]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-        [RTM_GETLINKPROP]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-        [RTM_NEWADDR]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
-        [RTM_DELADDR]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
-        [RTM_GETADDR]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
-        [RTM_NEWROUTE]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
-        [RTM_DELROUTE]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
-        [RTM_GETROUTE]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
-        [RTM_NEWNEIGH]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
-        [RTM_DELNEIGH]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
-        [RTM_GETNEIGH]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
-        [RTM_NEWADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
-        [RTM_DELADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
-        [RTM_GETADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
-        [RTM_NEWRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
-        [RTM_DELRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
-        [RTM_GETRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
-        [RTM_NEWNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
-        [RTM_DELNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
-        [RTM_GETNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
-        [RTM_NEWQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
-        [RTM_DELQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
-        [RTM_GETQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
-        [RTM_NEWTCLASS]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
-        [RTM_DELTCLASS]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
-        [RTM_GETTCLASS]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
-        [RTM_NEWMDB]       = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
-        [RTM_DELMDB]       = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
-        [RTM_GETMDB]       = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
-};
-
-const NLTypeSystem rtnl_type_system_root = {
-        .count = ELEMENTSOF(rtnl_types),
-        .types = rtnl_types,
-};
-
-static const NLType genl_wireguard_allowedip_types[] = {
-        [WGALLOWEDIP_A_FAMILY] = { .type = NETLINK_TYPE_U16 },
-        [WGALLOWEDIP_A_IPADDR] = { .type = NETLINK_TYPE_IN_ADDR },
-        [WGALLOWEDIP_A_CIDR_MASK] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLTypeSystem genl_wireguard_allowedip_type_system = {
-        .count = ELEMENTSOF(genl_wireguard_allowedip_types),
-        .types = genl_wireguard_allowedip_types,
-};
-
-static const NLType genl_wireguard_peer_types[] = {
-        [WGPEER_A_PUBLIC_KEY] = { .size = WG_KEY_LEN  },
-        [WGPEER_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
-        [WGPEER_A_PRESHARED_KEY] = { .size = WG_KEY_LEN },
-        [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U16 },
-        [WGPEER_A_ENDPOINT] = { .type = NETLINK_TYPE_SOCKADDR },
-        [WGPEER_A_ALLOWEDIPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_allowedip_type_system },
-};
-
-static const NLTypeSystem genl_wireguard_peer_type_system = {
-        .count = ELEMENTSOF(genl_wireguard_peer_types),
-        .types = genl_wireguard_peer_types,
-};
-
-static const NLType genl_wireguard_set_device_types[] = {
-        [WGDEVICE_A_IFINDEX] = { .type = NETLINK_TYPE_U32 },
-        [WGDEVICE_A_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
-        [WGDEVICE_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
-        [WGDEVICE_A_PRIVATE_KEY] = { .size = WG_KEY_LEN },
-        [WGDEVICE_A_LISTEN_PORT] = { .type = NETLINK_TYPE_U16 },
-        [WGDEVICE_A_FWMARK] = { .type = NETLINK_TYPE_U32 },
-        [WGDEVICE_A_PEERS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system },
-};
-
-static const NLTypeSystem genl_wireguard_set_device_type_system = {
-        .count = ELEMENTSOF(genl_wireguard_set_device_types),
-        .types = genl_wireguard_set_device_types,
-};
-
-static const NLType genl_wireguard_cmds[] = {
-        [WG_CMD_SET_DEVICE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_set_device_type_system },
-};
-
-static const NLTypeSystem genl_wireguard_type_system = {
-        .count = ELEMENTSOF(genl_wireguard_cmds),
-        .types = genl_wireguard_cmds,
-};
-
-static const NLType genl_mcast_group_types[] = {
-        [CTRL_ATTR_MCAST_GRP_NAME]  = { .type = NETLINK_TYPE_STRING },
-        [CTRL_ATTR_MCAST_GRP_ID]    = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem genl_mcast_group_type_system = {
-        .count = ELEMENTSOF(genl_mcast_group_types),
-        .types = genl_mcast_group_types,
-};
-
-static const NLType genl_get_family_types[] = {
-        [CTRL_ATTR_FAMILY_NAME]  = { .type = NETLINK_TYPE_STRING },
-        [CTRL_ATTR_FAMILY_ID]    = { .type = NETLINK_TYPE_U16 },
-        [CTRL_ATTR_MCAST_GROUPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_mcast_group_type_system },
-};
-
-static const NLTypeSystem genl_get_family_type_system = {
-        .count = ELEMENTSOF(genl_get_family_types),
-        .types = genl_get_family_types,
-};
-
-static const NLType genl_ctrl_id_ctrl_cmds[] = {
-        [CTRL_CMD_GETFAMILY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system },
-};
-
-static const NLTypeSystem genl_ctrl_id_ctrl_type_system = {
-        .count = ELEMENTSOF(genl_ctrl_id_ctrl_cmds),
-        .types = genl_ctrl_id_ctrl_cmds,
-};
-
-static const NLType genl_fou_types[] = {
-        [FOU_ATTR_PORT]              = { .type = NETLINK_TYPE_U16 },
-        [FOU_ATTR_AF]                = { .type = NETLINK_TYPE_U8 },
-        [FOU_ATTR_IPPROTO]           = { .type = NETLINK_TYPE_U8 },
-        [FOU_ATTR_TYPE]              = { .type = NETLINK_TYPE_U8 },
-        [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
-        [FOU_ATTR_LOCAL_V4]          = { .type = NETLINK_TYPE_IN_ADDR },
-        [FOU_ATTR_PEER_V4]           = { .type = NETLINK_TYPE_IN_ADDR },
-        [FOU_ATTR_LOCAL_V6]          = { .type = NETLINK_TYPE_IN_ADDR },
-        [FOU_ATTR_PEER_V6]           = { .type = NETLINK_TYPE_IN_ADDR},
-        [FOU_ATTR_PEER_PORT]         = { .type = NETLINK_TYPE_U16},
-        [FOU_ATTR_IFINDEX]           = { .type = NETLINK_TYPE_U32},
-};
-
-static const NLTypeSystem genl_fou_type_system = {
-        .count = ELEMENTSOF(genl_fou_types),
-        .types = genl_fou_types,
-};
-
-static const NLType genl_fou_cmds[] = {
-        [FOU_CMD_ADD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
-        [FOU_CMD_DEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
-        [FOU_CMD_GET] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_type_system },
-};
-
-static const NLTypeSystem genl_fou_cmds_type_system = {
-        .count = ELEMENTSOF(genl_fou_cmds),
-        .types = genl_fou_cmds,
-};
-
-static const NLType genl_l2tp_types[] = {
-        [L2TP_ATTR_PW_TYPE]           = { .type = NETLINK_TYPE_U16 },
-        [L2TP_ATTR_ENCAP_TYPE]        = { .type = NETLINK_TYPE_U16 },
-        [L2TP_ATTR_OFFSET]            = { .type = NETLINK_TYPE_U16 },
-        [L2TP_ATTR_DATA_SEQ]          = { .type = NETLINK_TYPE_U16 },
-        [L2TP_ATTR_L2SPEC_TYPE]       = { .type = NETLINK_TYPE_U8 },
-        [L2TP_ATTR_L2SPEC_LEN]        = { .type = NETLINK_TYPE_U8 },
-        [L2TP_ATTR_PROTO_VERSION]     = { .type = NETLINK_TYPE_U8 },
-        [L2TP_ATTR_IFNAME]            = { .type = NETLINK_TYPE_STRING },
-        [L2TP_ATTR_CONN_ID]           = { .type = NETLINK_TYPE_U32 },
-        [L2TP_ATTR_PEER_CONN_ID]      = { .type = NETLINK_TYPE_U32 },
-        [L2TP_ATTR_SESSION_ID]        = { .type = NETLINK_TYPE_U32 },
-        [L2TP_ATTR_PEER_SESSION_ID]   = { .type = NETLINK_TYPE_U32 },
-        [L2TP_ATTR_UDP_CSUM]          = { .type = NETLINK_TYPE_U8 },
-        [L2TP_ATTR_VLAN_ID]           = { .type = NETLINK_TYPE_U16 },
-        [L2TP_ATTR_RECV_SEQ]          = { .type = NETLINK_TYPE_U8 },
-        [L2TP_ATTR_SEND_SEQ]          = { .type = NETLINK_TYPE_U8 },
-        [L2TP_ATTR_LNS_MODE]          = { .type = NETLINK_TYPE_U8 },
-        [L2TP_ATTR_USING_IPSEC]       = { .type = NETLINK_TYPE_U8 },
-        [L2TP_ATTR_FD]                = { .type = NETLINK_TYPE_U32 },
-        [L2TP_ATTR_IP_SADDR]          = { .type = NETLINK_TYPE_IN_ADDR },
-        [L2TP_ATTR_IP_DADDR]          = { .type = NETLINK_TYPE_IN_ADDR },
-        [L2TP_ATTR_UDP_SPORT]         = { .type = NETLINK_TYPE_U16 },
-        [L2TP_ATTR_UDP_DPORT]         = { .type = NETLINK_TYPE_U16 },
-        [L2TP_ATTR_IP6_SADDR]         = { .type = NETLINK_TYPE_IN_ADDR },
-        [L2TP_ATTR_IP6_DADDR]         = { .type = NETLINK_TYPE_IN_ADDR },
-        [L2TP_ATTR_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_FLAG },
-        [L2TP_ATTR_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_FLAG },
-};
-
-static const NLTypeSystem genl_l2tp_type_system = {
-        .count = ELEMENTSOF(genl_l2tp_types),
-        .types = genl_l2tp_types,
-};
-
-static const NLType genl_l2tp[]   = {
-        [L2TP_CMD_TUNNEL_CREATE]  = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
-        [L2TP_CMD_TUNNEL_DELETE]  = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
-        [L2TP_CMD_TUNNEL_MODIFY]  = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
-        [L2TP_CMD_TUNNEL_GET]     = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
-        [L2TP_CMD_SESSION_CREATE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
-        [L2TP_CMD_SESSION_DELETE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
-        [L2TP_CMD_SESSION_MODIFY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
-        [L2TP_CMD_SESSION_GET]    = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system },
-};
-
-static const NLTypeSystem genl_l2tp_tunnel_session_type_system = {
-        .count = ELEMENTSOF(genl_l2tp),
-        .types = genl_l2tp,
-};
-
-static const NLType genl_rxsc_types[] = {
-        [MACSEC_RXSC_ATTR_SCI] = { .type = NETLINK_TYPE_U64 },
-};
-
-static const NLTypeSystem genl_rxsc_config_type_system = {
-        .count = ELEMENTSOF(genl_rxsc_types),
-        .types = genl_rxsc_types,
-};
-
-static const NLType genl_macsec_rxsc_types[] = {
-        [MACSEC_ATTR_IFINDEX]     = { .type = NETLINK_TYPE_U32 },
-        [MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_rxsc_config_type_system },
-};
-
-static const NLTypeSystem genl_macsec_rxsc_type_system = {
-        .count = ELEMENTSOF(genl_macsec_rxsc_types),
-        .types = genl_macsec_rxsc_types,
-};
-
-static const NLType genl_macsec_sa_config_types[] = {
-        [MACSEC_SA_ATTR_AN]     = { .type = NETLINK_TYPE_U8 },
-        [MACSEC_SA_ATTR_ACTIVE] = { .type = NETLINK_TYPE_U8 },
-        [MACSEC_SA_ATTR_PN]     = { .type = NETLINK_TYPE_U32 },
-        [MACSEC_SA_ATTR_KEYID]  = { .size = MACSEC_KEYID_LEN },
-        [MACSEC_SA_ATTR_KEY]    = { .size = MACSEC_MAX_KEY_LEN },
-};
+DEFINE_TYPE_SYSTEM(error);
 
-static const NLTypeSystem genl_macsec_sa_config_type_system = {
-        .count = ELEMENTSOF(genl_macsec_sa_config_types),
-        .types = genl_macsec_sa_config_types,
+static const NLType basic_types[] = {
+        [NLMSG_DONE]  = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system },
+        [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
 };
 
-static const NLType genl_macsec_rxsa_types[] = {
-        [MACSEC_ATTR_IFINDEX]   = { .type = NETLINK_TYPE_U32 },
-        [MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_config_type_system },
-};
-
-static const NLTypeSystem genl_macsec_rxsa_type_system = {
-        .count = ELEMENTSOF(genl_macsec_rxsa_types),
-        .types = genl_macsec_rxsa_types,
-};
-
-static const NLType genl_macsec_sa_types[] = {
-        [MACSEC_ATTR_IFINDEX]     = { .type = NETLINK_TYPE_U32 },
-        [MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_rxsc_config_type_system },
-        [MACSEC_ATTR_SA_CONFIG]   = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_config_type_system },
-};
-
-static const NLTypeSystem genl_macsec_sa_type_system = {
-        .count = ELEMENTSOF(genl_macsec_sa_types),
-        .types = genl_macsec_sa_types,
-};
-
-static const NLType genl_macsec[]   = {
-        [MACSEC_CMD_ADD_RXSC]  = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsc_type_system },
-        [MACSEC_CMD_ADD_TXSA]  = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsa_type_system},
-        [MACSEC_CMD_ADD_RXSA]  = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_type_system },
-};
-
-static const NLTypeSystem genl_macsec_device_type_system = {
-        .count = ELEMENTSOF(genl_macsec),
-        .types = genl_macsec,
-};
-
-static const NLType genl_nl80211_types[] = {
-        [NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
-        [NL80211_ATTR_MAC]     = { .type = NETLINK_TYPE_ETHER_ADDR },
-        [NL80211_ATTR_SSID]    = { .type = NETLINK_TYPE_STRING },
-        [NL80211_ATTR_IFTYPE]  = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem genl_nl80211_type_system = {
-        .count = ELEMENTSOF(genl_nl80211_types),
-        .types = genl_nl80211_types,
-};
-
-static const NLType genl_nl80211_cmds[] = {
-        [NL80211_CMD_GET_WIPHY]     = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_SET_WIPHY]     = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_NEW_WIPHY]     = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_DEL_WIPHY]     = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_GET_INTERFACE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_SET_INTERFACE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_NEW_INTERFACE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_DEL_INTERFACE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_GET_STATION]   = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_SET_STATION]   = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_NEW_STATION]   = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-        [NL80211_CMD_DEL_STATION]   = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system },
-};
-
-static const NLTypeSystem genl_nl80211_cmds_type_system = {
-        .count = ELEMENTSOF(genl_nl80211_cmds),
-        .types = genl_nl80211_cmds,
-};
-
-static const NLType genl_batadv_types[] = {
-        [BATADV_ATTR_VERSION]                       = { .type = NETLINK_TYPE_STRING },
-        [BATADV_ATTR_ALGO_NAME]                     = { .type = NETLINK_TYPE_STRING },
-        [BATADV_ATTR_MESH_IFINDEX]                  = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_MESH_IFNAME]                   = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
-        [BATADV_ATTR_MESH_ADDRESS]                  = { .size = ETH_ALEN },
-        [BATADV_ATTR_HARD_IFINDEX]                  = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_HARD_IFNAME]                   = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
-        [BATADV_ATTR_HARD_ADDRESS]                  = { .size = ETH_ALEN },
-        [BATADV_ATTR_ORIG_ADDRESS]                  = { .size = ETH_ALEN },
-        [BATADV_ATTR_TPMETER_RESULT]                = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_TPMETER_TEST_TIME]             = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_TPMETER_BYTES]                 = { .type = NETLINK_TYPE_U64 },
-        [BATADV_ATTR_TPMETER_COOKIE]                = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_PAD]                           = { .type = NETLINK_TYPE_UNSPEC },
-        [BATADV_ATTR_ACTIVE]                        = { .type = NETLINK_TYPE_FLAG },
-        [BATADV_ATTR_TT_ADDRESS]                    = { .size = ETH_ALEN },
-        [BATADV_ATTR_TT_TTVN]                       = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_TT_LAST_TTVN]                  = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_TT_CRC32]                      = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_TT_VID]                        = { .type = NETLINK_TYPE_U16 },
-        [BATADV_ATTR_TT_FLAGS]                      = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_FLAG_BEST]                     = { .type = NETLINK_TYPE_FLAG },
-        [BATADV_ATTR_LAST_SEEN_MSECS]               = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_NEIGH_ADDRESS]                 = { .size = ETH_ALEN },
-        [BATADV_ATTR_TQ]                            = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_THROUGHPUT]                    = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_BANDWIDTH_UP]                  = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_BANDWIDTH_DOWN]                = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_ROUTER]                        = { .size = ETH_ALEN },
-        [BATADV_ATTR_BLA_OWN]                       = { .type = NETLINK_TYPE_FLAG },
-        [BATADV_ATTR_BLA_ADDRESS]                   = { .size = ETH_ALEN },
-        [BATADV_ATTR_BLA_VID]                       = { .type = NETLINK_TYPE_U16 },
-        [BATADV_ATTR_BLA_BACKBONE]                  = { .size = ETH_ALEN },
-        [BATADV_ATTR_BLA_CRC]                       = { .type = NETLINK_TYPE_U16 },
-        [BATADV_ATTR_DAT_CACHE_IP4ADDRESS]          = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_DAT_CACHE_HWADDRESS]           = { .size = ETH_ALEN },
-        [BATADV_ATTR_DAT_CACHE_VID]                 = { .type = NETLINK_TYPE_U16 },
-        [BATADV_ATTR_MCAST_FLAGS]                   = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_MCAST_FLAGS_PRIV]              = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_VLANID]                        = { .type = NETLINK_TYPE_U16 },
-        [BATADV_ATTR_AGGREGATED_OGMS_ENABLED]       = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_AP_ISOLATION_ENABLED]          = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_ISOLATION_MARK]                = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_ISOLATION_MASK]                = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_BONDING_ENABLED]               = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_FRAGMENTATION_ENABLED]         = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_GW_BANDWIDTH_DOWN]             = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_GW_BANDWIDTH_UP]               = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_GW_MODE]                       = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_GW_SEL_CLASS]                  = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_HOP_PENALTY]                   = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_LOG_LEVEL]                     = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED]  = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_MULTICAST_FANOUT]              = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_NETWORK_CODING_ENABLED]        = { .type = NETLINK_TYPE_U8 },
-        [BATADV_ATTR_ORIG_INTERVAL]                 = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_ELP_INTERVAL]                  = { .type = NETLINK_TYPE_U32 },
-        [BATADV_ATTR_THROUGHPUT_OVERRIDE]           = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem genl_batadv_type_system = {
-        .count = ELEMENTSOF(genl_batadv_types),
-        .types = genl_batadv_types,
-};
-
-static const NLType genl_batadv_cmds[] = {
-        [BATADV_CMD_SET_MESH] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_batadv_type_system },
-};
-
-static const NLTypeSystem genl_batadv_cmds_type_system = {
-        .count = ELEMENTSOF(genl_batadv_cmds),
-        .types = genl_batadv_cmds,
-};
-
-static const NLType genl_families[] = {
-        [SD_GENL_ID_CTRL]   = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
-        [SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system },
-        [SD_GENL_FOU]       = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_cmds_type_system },
-        [SD_GENL_L2TP]      = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_tunnel_session_type_system },
-        [SD_GENL_MACSEC]    = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_device_type_system },
-        [SD_GENL_NL80211]   = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_cmds_type_system },
-        [SD_GENL_BATADV]    = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_batadv_cmds_type_system },
-};
-
-static const NLType nfnl_nft_table_types[] = {
-        [NFTA_TABLE_NAME]  = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
-        [NFTA_TABLE_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_nft_table_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_table_types),
-        .types = nfnl_nft_table_types,
-};
-
-static const NLType nfnl_nft_chain_hook_types[] = {
-        [NFTA_HOOK_HOOKNUM]  = { .type = NETLINK_TYPE_U32 },
-        [NFTA_HOOK_PRIORITY] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_HOOK_DEV]      = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
-};
-
-static const NLTypeSystem nfnl_nft_chain_hook_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_chain_hook_types),
-        .types = nfnl_nft_chain_hook_types,
-};
-
-static const NLType nfnl_nft_chain_types[] = {
-        [NFTA_CHAIN_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
-        [NFTA_CHAIN_NAME]  = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
-        [NFTA_CHAIN_HOOK]  = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_hook_type_system },
-        [NFTA_CHAIN_TYPE]  = { .type = NETLINK_TYPE_STRING, .size = 16 },
-        [NFTA_CHAIN_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_nft_chain_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_chain_types),
-        .types = nfnl_nft_chain_types,
-};
-
-static const NLType nfnl_nft_expr_meta_types[] = {
-        [NFTA_META_DREG] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_META_KEY]  = { .type = NETLINK_TYPE_U32 },
-        [NFTA_META_SREG] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_expr_payload_types[] = {
-        [NFTA_PAYLOAD_DREG]   = { .type = NETLINK_TYPE_U32 },
-        [NFTA_PAYLOAD_BASE]   = { .type = NETLINK_TYPE_U32 },
-        [NFTA_PAYLOAD_OFFSET] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_PAYLOAD_LEN]    = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_expr_nat_types[] = {
-        [NFTA_NAT_TYPE]          = { .type = NETLINK_TYPE_U32 },
-        [NFTA_NAT_FAMILY]        = { .type = NETLINK_TYPE_U32 },
-        [NFTA_NAT_REG_ADDR_MIN]  = { .type = NETLINK_TYPE_U32 },
-        [NFTA_NAT_REG_ADDR_MAX]  = { .type = NETLINK_TYPE_U32 },
-        [NFTA_NAT_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_NAT_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_NAT_FLAGS]         = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_data_types[] = {
-        [NFTA_DATA_VALUE] = { .type = NETLINK_TYPE_BINARY },
-};
-
-static const NLTypeSystem nfnl_nft_data_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_data_types),
-        .types = nfnl_nft_data_types,
-};
-
-static const NLType nfnl_nft_expr_bitwise_types[] = {
-        [NFTA_BITWISE_SREG] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_BITWISE_DREG] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_BITWISE_LEN]  = { .type = NETLINK_TYPE_U32 },
-        [NFTA_BITWISE_MASK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
-        [NFTA_BITWISE_XOR]  = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
-};
-
-static const NLType nfnl_nft_expr_cmp_types[] = {
-        [NFTA_CMP_SREG] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_CMP_OP]   = { .type = NETLINK_TYPE_U32 },
-        [NFTA_CMP_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
-};
-
-static const NLType nfnl_nft_expr_fib_types[] = {
-        [NFTA_FIB_DREG]   = { .type = NETLINK_TYPE_U32 },
-        [NFTA_FIB_RESULT] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_FIB_FLAGS]  = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_expr_lookup_types[] = {
-        [NFTA_LOOKUP_SET]   = { .type = NETLINK_TYPE_STRING },
-        [NFTA_LOOKUP_SREG]  = { .type = NETLINK_TYPE_U32 },
-        [NFTA_LOOKUP_DREG]  = { .type = NETLINK_TYPE_U32 },
-        [NFTA_LOOKUP_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType nfnl_nft_expr_masq_types[] = {
-        [NFTA_MASQ_FLAGS]         = { .type = NETLINK_TYPE_U32 },
-        [NFTA_MASQ_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
-        [NFTA_MASQ_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_expr_data_type_systems[] = {
-        [NL_UNION_NFT_EXPR_DATA_BITWISE]   =  { .count = ELEMENTSOF(nfnl_nft_expr_bitwise_types),
-                                                .types = nfnl_nft_expr_bitwise_types },
-        [NL_UNION_NFT_EXPR_DATA_CMP]       =  { .count = ELEMENTSOF(nfnl_nft_expr_cmp_types),
-                                                .types = nfnl_nft_expr_cmp_types },
-        [NL_UNION_NFT_EXPR_DATA_FIB]       =  { .count = ELEMENTSOF(nfnl_nft_expr_fib_types),
-                                                .types = nfnl_nft_expr_fib_types },
-        [NL_UNION_NFT_EXPR_DATA_LOOKUP]    =  { .count = ELEMENTSOF(nfnl_nft_expr_lookup_types),
-                                                .types = nfnl_nft_expr_lookup_types },
-        [NL_UNION_NFT_EXPR_DATA_MASQ]      =  { .count = ELEMENTSOF(nfnl_nft_expr_masq_types),
-                                                .types = nfnl_nft_expr_masq_types },
-        [NL_UNION_NFT_EXPR_DATA_META]      =  { .count = ELEMENTSOF(nfnl_nft_expr_meta_types),
-                                                .types = nfnl_nft_expr_meta_types },
-        [NL_UNION_NFT_EXPR_DATA_NAT]       =  { .count = ELEMENTSOF(nfnl_nft_expr_nat_types),
-                                                .types = nfnl_nft_expr_nat_types },
-        [NL_UNION_NFT_EXPR_DATA_PAYLOAD]   =  { .count = ELEMENTSOF(nfnl_nft_expr_payload_types),
-                                                .types = nfnl_nft_expr_payload_types },
-};
-
-static const char* const nl_union_nft_expr_data_table[] = {
-        [NL_UNION_NFT_EXPR_DATA_BITWISE] = "bitwise",
-        [NL_UNION_NFT_EXPR_DATA_CMP]     = "cmp",
-        [NL_UNION_NFT_EXPR_DATA_LOOKUP]  = "lookup",
-        [NL_UNION_NFT_EXPR_DATA_META]    = "meta",
-        [NL_UNION_NFT_EXPR_DATA_FIB]     = "fib",
-        [NL_UNION_NFT_EXPR_DATA_MASQ]    = "masq",
-        [NL_UNION_NFT_EXPR_DATA_NAT]     = "nat",
-        [NL_UNION_NFT_EXPR_DATA_PAYLOAD] = "payload",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(nl_union_nft_expr_data, NLUnionNFTExprData);
-
-static const NLTypeSystemUnion nfnl_nft_data_expr_type_system_union = {
-        .num = _NL_UNION_NFT_EXPR_DATA_MAX,
-        .lookup = nl_union_nft_expr_data_from_string,
-        .type_systems = nfnl_expr_data_type_systems,
-        .match_type = NL_MATCH_SIBLING,
-        .match = NFTA_EXPR_NAME,
-};
-
-static const NLType nfnl_nft_rule_expr_types[] = {
-        [NFTA_EXPR_NAME] = { .type = NETLINK_TYPE_STRING, .size = 16 },
-        [NFTA_EXPR_DATA] = { .type = NETLINK_TYPE_UNION,
-                             .type_system_union = &nfnl_nft_data_expr_type_system_union },
-};
-
-static const NLTypeSystem nfnl_nft_rule_expr_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_rule_expr_types),
-        .types = nfnl_nft_rule_expr_types,
-};
-
-static const NLType nfnl_nft_rule_types[] = {
-        [NFTA_RULE_TABLE]       = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
-        [NFTA_RULE_CHAIN]       = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
-        [NFTA_RULE_EXPRESSIONS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_expr_type_system }
-};
-
-static const NLTypeSystem nfnl_nft_rule_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_rule_types),
-        .types = nfnl_nft_rule_types,
-};
-
-static const NLType nfnl_nft_set_types[] = {
-        [NFTA_SET_TABLE]      = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
-        [NFTA_SET_NAME]       = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
-        [NFTA_SET_FLAGS]      = { .type = NETLINK_TYPE_U32 },
-        [NFTA_SET_KEY_TYPE]   = { .type = NETLINK_TYPE_U32 },
-        [NFTA_SET_KEY_LEN]    = { .type = NETLINK_TYPE_U32 },
-        [NFTA_SET_DATA_TYPE]  = { .type = NETLINK_TYPE_U32 },
-        [NFTA_SET_DATA_LEN]   = { .type = NETLINK_TYPE_U32 },
-        [NFTA_SET_POLICY]     = { .type = NETLINK_TYPE_U32 },
-        [NFTA_SET_ID]         = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_nft_set_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_set_types),
-        .types = nfnl_nft_set_types,
-};
-
-static const NLType nfnl_nft_setelem_types[] = {
-        [NFTA_SET_ELEM_KEY]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
-        [NFTA_SET_ELEM_DATA]  = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
-        [NFTA_SET_ELEM_FLAGS] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystem nfnl_nft_setelem_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_setelem_types),
-        .types = nfnl_nft_setelem_types,
-};
-
-static const NLType nfnl_nft_setelem_list_types[] = {
-        [NFTA_SET_ELEM_LIST_TABLE]    = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
-        [NFTA_SET_ELEM_LIST_SET]      = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
-        [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_type_system },
-};
-
-static const NLTypeSystem nfnl_nft_setelem_list_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_setelem_list_types),
-        .types = nfnl_nft_setelem_list_types,
-};
-
-static const NLType nfnl_nft_msg_types [] = {
-        [NFT_MSG_DELTABLE]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
-        [NFT_MSG_NEWTABLE]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
-        [NFT_MSG_NEWCHAIN]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_type_system, .size = sizeof(struct nfgenmsg) },
-        [NFT_MSG_NEWRULE]    = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_type_system, .size = sizeof(struct nfgenmsg) },
-        [NFT_MSG_NEWSET]     = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_set_type_system, .size = sizeof(struct nfgenmsg) },
-        [NFT_MSG_NEWSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
-        [NFT_MSG_DELSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
-};
-
-static const NLTypeSystem nfnl_nft_msg_type_system = {
-        .count = ELEMENTSOF(nfnl_nft_msg_types),
-        .types = nfnl_nft_msg_types,
-};
-
-static const NLType nfnl_msg_batch_types [] = {
-        [NFNL_BATCH_GENID] = { .type = NETLINK_TYPE_U32 }
-};
-
-static const NLTypeSystem nfnl_msg_batch_type_system = {
-        .count = ELEMENTSOF(nfnl_msg_batch_types),
-        .types = nfnl_msg_batch_types,
-};
-
-static const NLType nfnl_types[] = {
-        [NLMSG_DONE]           = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
-        [NLMSG_ERROR]          = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
-        [NFNL_MSG_BATCH_BEGIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
-        [NFNL_MSG_BATCH_END]   = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
-        [NFNL_SUBSYS_NFTABLES] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_msg_type_system, .size = sizeof(struct nfgenmsg) },
-};
-
-const NLTypeSystem nfnl_type_system_root = {
-        .count = ELEMENTSOF(nfnl_types),
-        .types = nfnl_types,
-};
-
-/* Mainly used when sending message */
-const NLTypeSystem genl_family_type_system_root = {
-        .count = ELEMENTSOF(genl_families),
-        .types = genl_families,
-};
-
-static const NLType genl_types[] = {
-        [SD_GENL_ERROR]   = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
-        [SD_GENL_DONE]    = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system },
-        [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system, .size = sizeof(struct genlmsghdr) },
-        [SD_GENL_NL80211] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system, .size = sizeof(struct genlmsghdr) },
-};
-
-/* Mainly used when message received */
-const NLTypeSystem genl_type_system_root = {
-        .count = ELEMENTSOF(genl_types),
-        .types = genl_types,
-};
+DEFINE_TYPE_SYSTEM(basic);
 
 uint16_t type_get_type(const NLType *type) {
         assert(type);
@@ -1700,153 +36,127 @@ size_t type_get_size(const NLType *type) {
         return type->size;
 }
 
-void type_get_type_system(const NLType *nl_type, const NLTypeSystem **ret) {
+const NLTypeSystem *type_get_type_system(const NLType *nl_type) {
         assert(nl_type);
-        assert(ret);
         assert(nl_type->type == NETLINK_TYPE_NESTED);
         assert(nl_type->type_system);
-
-        *ret = nl_type->type_system;
+        return nl_type->type_system;
 }
 
-void type_get_type_system_union(const NLType *nl_type, const NLTypeSystemUnion **ret) {
+const NLTypeSystemUnion *type_get_type_system_union(const NLType *nl_type) {
         assert(nl_type);
-        assert(ret);
         assert(nl_type->type == NETLINK_TYPE_UNION);
         assert(nl_type->type_system_union);
-
-        *ret = nl_type->type_system_union;
-}
-
-uint16_t type_system_get_count(const NLTypeSystem *type_system) {
-        assert(type_system);
-        return type_system->count;
+        return nl_type->type_system_union;
 }
 
-const NLTypeSystem *type_system_get_root(int protocol) {
-        switch (protocol) {
-                case NETLINK_GENERIC:
-                        return &genl_type_system_root;
-                case NETLINK_NETFILTER:
-                        return &nfnl_type_system_root;
-                default: /* NETLINK_ROUTE: */
-                        return &rtnl_type_system_root;
-        }
-}
+int type_system_root_get_type_system_and_header_size(
+                sd_netlink *nl,
+                uint16_t type,
+                const NLTypeSystem **ret_type_system,
+                size_t *ret_header_size) {
 
-int type_system_root_get_type(sd_netlink *nl, const NLType **ret, uint16_t type) {
-        sd_genl_family_t family;
         const NLType *nl_type;
-        int r;
-
-        if (!nl)
-                return type_system_get_type(&rtnl_type_system_root, ret, type);
-
-        if (nl->protocol != NETLINK_GENERIC)
-                return type_system_get_type(type_system_get_root(nl->protocol), ret, type);
 
-        r = nlmsg_type_to_genl_family(nl, type, &family);
-        if (r < 0)
-                return r;
+        assert(nl);
 
-        if (family >= genl_type_system_root.count)
+        if (IN_SET(type, NLMSG_DONE, NLMSG_ERROR))
+                nl_type = type_system_get_type(&basic_type_system, type);
+        else
+                switch(nl->protocol) {
+                case NETLINK_ROUTE:
+                        nl_type = rtnl_get_type(type);
+                        break;
+                case NETLINK_NETFILTER:
+                        nl_type = nfnl_get_type(type);
+                        break;
+                case NETLINK_GENERIC:
+                        return genl_get_type_system_and_header_size(nl, type, ret_type_system, ret_header_size);
+                default:
+                        return -EOPNOTSUPP;
+                }
+        if (!nl_type)
                 return -EOPNOTSUPP;
 
-        nl_type = &genl_type_system_root.types[family];
-
-        if (nl_type->type == NETLINK_TYPE_UNSPEC)
+        if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
                 return -EOPNOTSUPP;
 
-        *ret = nl_type;
-
+        if (ret_type_system)
+                *ret_type_system = type_get_type_system(nl_type);
+        if (ret_header_size)
+                *ret_header_size = type_get_size(nl_type);
         return 0;
 }
 
-int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) {
+const NLType *type_system_get_type(const NLTypeSystem *type_system, uint16_t type) {
         const NLType *nl_type;
 
-        assert(ret);
         assert(type_system);
         assert(type_system->types);
 
         if (type >= type_system->count)
-                return -EOPNOTSUPP;
+                return NULL;
 
         nl_type = &type_system->types[type];
 
         if (nl_type->type == NETLINK_TYPE_UNSPEC)
-                return -EOPNOTSUPP;
-
-        *ret = nl_type;
+                return NULL;
 
-        return 0;
+        return nl_type;
 }
 
-int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type) {
+const NLTypeSystem *type_system_get_type_system(const NLTypeSystem *type_system, uint16_t type) {
         const NLType *nl_type;
-        int r;
 
-        assert(ret);
+        nl_type = type_system_get_type(type_system, type);
+        if (!nl_type)
+                return NULL;
 
-        r = type_system_get_type(type_system, &nl_type, type);
-        if (r < 0)
-                return r;
-
-        type_get_type_system(nl_type, ret);
-        return 0;
+        return type_get_type_system(nl_type);
 }
 
-int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type) {
+const NLTypeSystemUnion *type_system_get_type_system_union(const NLTypeSystem *type_system, uint16_t type) {
         const NLType *nl_type;
-        int r;
 
-        assert(ret);
+        nl_type = type_system_get_type(type_system, type);
+        if (!nl_type)
+                return NULL;
 
-        r = type_system_get_type(type_system, &nl_type, type);
-        if (r < 0)
-                return r;
+        return type_get_type_system_union(nl_type);
+}
 
-        type_get_type_system_union(nl_type, ret);
-        return 0;
+NLMatchType type_system_union_get_match_type(const NLTypeSystemUnion *type_system_union) {
+        assert(type_system_union);
+        return type_system_union->match_type;
 }
 
-int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key) {
-        int type;
+uint16_t type_system_union_get_match_attribute(const NLTypeSystemUnion *type_system_union) {
+        assert(type_system_union);
+        assert(type_system_union->match_type == NL_MATCH_SIBLING);
+        return type_system_union->match_attribute;
+}
 
+const NLTypeSystem *type_system_union_get_type_system_by_string(const NLTypeSystemUnion *type_system_union, const char *key) {
         assert(type_system_union);
+        assert(type_system_union->elements);
         assert(type_system_union->match_type == NL_MATCH_SIBLING);
-        assert(type_system_union->lookup);
-        assert(type_system_union->type_systems);
-        assert(ret);
         assert(key);
 
-        type = type_system_union->lookup(key);
-        if (type < 0)
-                return -EOPNOTSUPP;
-
-        assert(type < type_system_union->num);
-
-        *ret = &type_system_union->type_systems[type];
+        for (size_t i = 0; i < type_system_union->count; i++)
+                if (streq(type_system_union->elements[i].name, key))
+                        return &type_system_union->elements[i].type_system;
 
-        return 0;
+        return NULL;
 }
 
-int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol) {
-        const NLTypeSystem *type_system;
-
+const NLTypeSystem *type_system_union_get_type_system_by_protocol(const NLTypeSystemUnion *type_system_union, uint16_t protocol) {
         assert(type_system_union);
-        assert(type_system_union->type_systems);
+        assert(type_system_union->elements);
         assert(type_system_union->match_type == NL_MATCH_PROTOCOL);
-        assert(ret);
-
-        if (protocol >= type_system_union->num)
-                return -EOPNOTSUPP;
 
-        type_system = &type_system_union->type_systems[protocol];
-        if (!type_system->types)
-                return -EOPNOTSUPP;
-
-        *ret = type_system;
+        for (size_t i = 0; i < type_system_union->count; i++)
+                if (type_system_union->elements[i].protocol == protocol)
+                        return &type_system_union->elements[i].type_system;
 
-        return 0;
+        return NULL;
 }
index 316d7f1b87755f7ec1f809d36eeaf05108ba7b9d..d481f3072fbec6149ca4b9a429fa1dd4e716b0d3 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include "macro.h"
+#include "sd-netlink.h"
 
 enum {
         NETLINK_TYPE_UNSPEC,
@@ -35,105 +35,30 @@ typedef struct NLTypeSystemUnion NLTypeSystemUnion;
 typedef struct NLTypeSystem NLTypeSystem;
 typedef struct NLType NLType;
 
-struct NLTypeSystemUnion {
-        int num;
-        NLMatchType match_type;
-        uint16_t match;
-        int (*lookup)(const char *);
-        const NLTypeSystem *type_systems;
-};
-
-extern const NLTypeSystem genl_family_type_system_root;
+const NLType *rtnl_get_type(uint16_t nlmsg_type);
+const NLType *nfnl_get_type(uint16_t nlmsg_type);
+const NLTypeSystem *genl_get_type_system_by_name(const char *name);
+int genl_get_type_system_and_header_size(
+                sd_netlink *nl,
+                uint16_t id,
+                const NLTypeSystem **ret_type_system,
+                size_t *ret_header_size);
 
 uint16_t type_get_type(const NLType *type);
 size_t type_get_size(const NLType *type);
-void type_get_type_system(const NLType *type, const NLTypeSystem **ret);
-void type_get_type_system_union(const NLType *type, const NLTypeSystemUnion **ret);
-
-const NLTypeSystem* type_system_get_root(int protocol);
-uint16_t type_system_get_count(const NLTypeSystem *type_system);
-int type_system_root_get_type(sd_netlink *nl, const NLType **ret, uint16_t type);
-int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type);
-int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type);
-int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type);
-int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key);
-int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol);
-
-typedef enum NLUnionLinkInfoData {
-        NL_UNION_LINK_INFO_DATA_BOND,
-        NL_UNION_LINK_INFO_DATA_BRIDGE,
-        NL_UNION_LINK_INFO_DATA_VLAN,
-        NL_UNION_LINK_INFO_DATA_VETH,
-        NL_UNION_LINK_INFO_DATA_DUMMY,
-        NL_UNION_LINK_INFO_DATA_MACVLAN,
-        NL_UNION_LINK_INFO_DATA_MACVTAP,
-        NL_UNION_LINK_INFO_DATA_IPVLAN,
-        NL_UNION_LINK_INFO_DATA_IPVTAP,
-        NL_UNION_LINK_INFO_DATA_VXLAN,
-        NL_UNION_LINK_INFO_DATA_IPIP_TUNNEL,
-        NL_UNION_LINK_INFO_DATA_IPGRE_TUNNEL,
-        NL_UNION_LINK_INFO_DATA_ERSPAN,
-        NL_UNION_LINK_INFO_DATA_IPGRETAP_TUNNEL,
-        NL_UNION_LINK_INFO_DATA_IP6GRE_TUNNEL,
-        NL_UNION_LINK_INFO_DATA_IP6GRETAP_TUNNEL,
-        NL_UNION_LINK_INFO_DATA_SIT_TUNNEL,
-        NL_UNION_LINK_INFO_DATA_VTI_TUNNEL,
-        NL_UNION_LINK_INFO_DATA_VTI6_TUNNEL,
-        NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL,
-        NL_UNION_LINK_INFO_DATA_VRF,
-        NL_UNION_LINK_INFO_DATA_VCAN,
-        NL_UNION_LINK_INFO_DATA_GENEVE,
-        NL_UNION_LINK_INFO_DATA_VXCAN,
-        NL_UNION_LINK_INFO_DATA_WIREGUARD,
-        NL_UNION_LINK_INFO_DATA_NETDEVSIM,
-        NL_UNION_LINK_INFO_DATA_CAN,
-        NL_UNION_LINK_INFO_DATA_MACSEC,
-        NL_UNION_LINK_INFO_DATA_NLMON,
-        NL_UNION_LINK_INFO_DATA_XFRM,
-        NL_UNION_LINK_INFO_DATA_IFB,
-        NL_UNION_LINK_INFO_DATA_BAREUDP,
-        NL_UNION_LINK_INFO_DATA_BATADV,
-        _NL_UNION_LINK_INFO_DATA_MAX,
-        _NL_UNION_LINK_INFO_DATA_INVALID = -EINVAL,
-} NLUnionLinkInfoData;
-
-const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_;
-NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_;
-
-typedef enum NLUnionTCAOptionData {
-        NL_UNION_TCA_OPTION_DATA_CAKE,
-        NL_UNION_TCA_OPTION_DATA_CODEL,
-        NL_UNION_TCA_OPTION_DATA_DRR,
-        NL_UNION_TCA_OPTION_DATA_ETS,
-        NL_UNION_TCA_OPTION_DATA_FQ,
-        NL_UNION_TCA_OPTION_DATA_FQ_CODEL,
-        NL_UNION_TCA_OPTION_DATA_FQ_PIE,
-        NL_UNION_TCA_OPTION_DATA_GRED,
-        NL_UNION_TCA_OPTION_DATA_HHF,
-        NL_UNION_TCA_OPTION_DATA_HTB,
-        NL_UNION_TCA_OPTION_DATA_PIE,
-        NL_UNION_TCA_OPTION_DATA_QFQ,
-        NL_UNION_TCA_OPTION_DATA_SFB,
-        NL_UNION_TCA_OPTION_DATA_TBF,
-        _NL_UNION_TCA_OPTION_DATA_MAX,
-        _NL_UNION_TCA_OPTION_DATA_INVALID = -EINVAL,
-} NLUnionTCAOptionData;
-
-const char *nl_union_tca_option_data_to_string(NLUnionTCAOptionData p) _const_;
-NLUnionTCAOptionData nl_union_tca_option_data_from_string(const char *p) _pure_;
+const NLTypeSystem *type_get_type_system(const NLType *type);
+const NLTypeSystemUnion *type_get_type_system_union(const NLType *type);
 
-typedef enum NLUnionNFTExprData {
-        NL_UNION_NFT_EXPR_DATA_BITWISE,
-        NL_UNION_NFT_EXPR_DATA_CMP,
-        NL_UNION_NFT_EXPR_DATA_FIB,
-        NL_UNION_NFT_EXPR_DATA_LOOKUP,
-        NL_UNION_NFT_EXPR_DATA_PAYLOAD,
-        NL_UNION_NFT_EXPR_DATA_MASQ,
-        NL_UNION_NFT_EXPR_DATA_META,
-        NL_UNION_NFT_EXPR_DATA_NAT,
-        _NL_UNION_NFT_EXPR_DATA_MAX,
-        _NL_UNION_NFT_EXPR_DATA_INVALID = -EINVAL,
-} NLUnionNFTExprData;
+int type_system_root_get_type_system_and_header_size(
+                sd_netlink *nl,
+                uint16_t type,
+                const NLTypeSystem **ret_type_system,
+                size_t *ret_header_size);
 
-const char *nl_union_nft_expr_data_to_string(NLUnionNFTExprData p) _const_;
-NLUnionNFTExprData nl_union_nft_expr_data_from_string(const char *p) _pure_;
+const NLType *type_system_get_type(const NLTypeSystem *type_system, uint16_t type);
+const NLTypeSystem *type_system_get_type_system(const NLTypeSystem *type_system, uint16_t type);
+const NLTypeSystemUnion *type_system_get_type_system_union(const NLTypeSystem *type_system, uint16_t type);
+NLMatchType type_system_union_get_match_type(const NLTypeSystemUnion *type_system_union);
+uint16_t type_system_union_get_match_attribute(const NLTypeSystemUnion *type_system_union);
+const NLTypeSystem *type_system_union_get_type_system_by_string(const NLTypeSystemUnion *type_system_union, const char *key);
+const NLTypeSystem *type_system_union_get_type_system_by_protocol(const NLTypeSystemUnion *type_system_union, uint16_t protocol);
index 1211145fbf84a7c8820a92399b7a699d2a2fd603..886bb48210a8d2b169b79498112b81fc920d440e 100644 (file)
@@ -400,25 +400,6 @@ int rtnl_get_link_info(sd_netlink **rtnl, int ifindex, unsigned short *ret_iftyp
         return 0;
 }
 
-int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) {
-        struct nlmsgerr *err;
-        int r;
-
-        assert(error <= 0);
-
-        r = message_new(rtnl, ret, NLMSG_ERROR);
-        if (r < 0)
-                return r;
-
-        rtnl_message_seal(*ret);
-        (*ret)->hdr->nlmsg_seq = serial;
-
-        err = NLMSG_DATA((*ret)->hdr);
-        err->error = error;
-
-        return 0;
-}
-
 int rtnl_log_parse_error(int r) {
         return log_error_errno(r, "Failed to parse netlink message: %m");
 }
index a5d725655dd4cce280bb7b175b854be7b0ab3062..5208dd94e4088a80d29510d4ab9738e8eb407ad3 100644 (file)
@@ -29,10 +29,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(MultipathRoute*, multipath_route_free);
 
 int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret);
 
-int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret);
-uint32_t rtnl_message_get_serial(sd_netlink_message *m);
-void rtnl_message_seal(sd_netlink_message *m);
-
 static inline bool rtnl_message_type_is_neigh(uint16_t type) {
         return IN_SET(type, RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH);
 }
index f8a1bde6a6b127183211c2cf3e8dc4b2cd8d97c7..2d0c940b5a5d3688ed47961dd60e03f4b2bbddf8 100644 (file)
@@ -9,27 +9,26 @@
 #include "hashmap.h"
 #include "io-util.h"
 #include "macro.h"
+#include "netlink-genl.h"
 #include "netlink-internal.h"
 #include "netlink-slot.h"
-#include "netlink-util.h"
 #include "process-util.h"
 #include "socket-util.h"
 #include "string-util.h"
-#include "util.h"
 
 /* Some really high limit, to catch programming errors */
 #define REPLY_CALLBACKS_MAX UINT16_MAX
 
-static int sd_netlink_new(sd_netlink **ret) {
-        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+static int netlink_new(sd_netlink **ret) {
+        _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
 
         assert_return(ret, -EINVAL);
 
-        rtnl = new(sd_netlink, 1);
-        if (!rtnl)
+        nl = new(sd_netlink, 1);
+        if (!nl)
                 return -ENOMEM;
 
-        *rtnl = (sd_netlink) {
+        *nl = (sd_netlink) {
                 .n_ref = 1,
                 .fd = -1,
                 .sockaddr.nl.nl_family = AF_NETLINK,
@@ -61,60 +60,48 @@ static int sd_netlink_new(sd_netlink **ret) {
                 .serial = (uint32_t) (now(CLOCK_MONOTONIC) % UINT32_MAX) + 1,
         };
 
-        /* We guarantee that the read buffer has at least space for
-         * a message header */
-        if (!greedy_realloc((void**)&rtnl->rbuffer, sizeof(struct nlmsghdr), sizeof(uint8_t)))
+        /* We guarantee that the read buffer has at least space for a message header */
+        if (!greedy_realloc((void**) &nl->rbuffer, sizeof(struct nlmsghdr), sizeof(uint8_t)))
                 return -ENOMEM;
 
-        *ret = TAKE_PTR(rtnl);
-
+        *ret = TAKE_PTR(nl);
         return 0;
 }
 
-int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) {
-        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+int sd_netlink_new_from_fd(sd_netlink **ret, int fd) {
+        _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
         socklen_t addrlen;
         int r;
 
         assert_return(ret, -EINVAL);
 
-        r = sd_netlink_new(&rtnl);
+        r = netlink_new(&nl);
         if (r < 0)
                 return r;
 
-        addrlen = sizeof(rtnl->sockaddr);
+        addrlen = sizeof(nl->sockaddr);
 
-        r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
+        r = getsockname(fd, &nl->sockaddr.sa, &addrlen);
         if (r < 0)
                 return -errno;
 
-        if (rtnl->sockaddr.nl.nl_family != AF_NETLINK)
+        if (nl->sockaddr.nl.nl_family != AF_NETLINK)
                 return -EINVAL;
 
-        rtnl->fd = fd;
-
-        *ret = TAKE_PTR(rtnl);
+        nl->fd = fd;
 
+        *ret = TAKE_PTR(nl);
         return 0;
 }
 
-static bool rtnl_pid_changed(const sd_netlink *rtnl) {
-        assert(rtnl);
-
-        /* We don't support people creating an rtnl connection and
-         * keeping it around over a fork(). Let's complain. */
-
-        return rtnl->original_pid != getpid_cached();
-}
-
 int sd_netlink_open_fd(sd_netlink **ret, int fd) {
-        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL;
         int r, protocol;
 
         assert_return(ret, -EINVAL);
         assert_return(fd >= 0, -EBADF);
 
-        r = sd_netlink_new(&rtnl);
+        r = netlink_new(&nl);
         if (r < 0)
                 return r;
 
@@ -122,8 +109,8 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) {
         if (r < 0)
                 return r;
 
-        rtnl->fd = fd;
-        rtnl->protocol = protocol;
+        nl->fd = fd;
+        nl->protocol = protocol;
 
         r = setsockopt_int(fd, SOL_NETLINK, NETLINK_EXT_ACK, true);
         if (r < 0)
@@ -133,14 +120,14 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) {
         if (r < 0)
                 log_debug_errno(r, "sd-netlink: Failed to enable NETLINK_GET_STRICT_CHK option, ignoring: %m");
 
-        r = socket_bind(rtnl);
+        r = socket_bind(nl);
         if (r < 0) {
-                rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */
-                rtnl->protocol = -1;
+                nl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */
+                nl->protocol = -1;
                 return r;
         }
 
-        *ret = TAKE_PTR(rtnl);
+        *ret = TAKE_PTR(nl);
 
         return 0;
 }
@@ -165,91 +152,101 @@ int sd_netlink_open(sd_netlink **ret) {
         return netlink_open_family(ret, NETLINK_ROUTE);
 }
 
-int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) {
-        assert_return(rtnl, -EINVAL);
-        assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+bool netlink_pid_changed(sd_netlink *nl) {
+        assert(nl);
 
-        return fd_inc_rcvbuf(rtnl->fd, size);
+        /* We don't support people creating an nl connection and
+         * keeping it around over a fork(). Let's complain. */
+
+        return nl->original_pid != getpid_cached();
 }
 
-static sd_netlink *netlink_free(sd_netlink *rtnl) {
+int sd_netlink_inc_rcvbuf(sd_netlink *nl, size_t size) {
+        assert_return(nl, -EINVAL);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
+
+        return fd_inc_rcvbuf(nl->fd, size);
+}
+
+static sd_netlink *netlink_free(sd_netlink *nl) {
         sd_netlink_slot *s;
         unsigned i;
 
-        assert(rtnl);
+        assert(nl);
 
-        for (i = 0; i < rtnl->rqueue_size; i++)
-                sd_netlink_message_unref(rtnl->rqueue[i]);
-        free(rtnl->rqueue);
+        for (i = 0; i < nl->rqueue_size; i++)
+                sd_netlink_message_unref(nl->rqueue[i]);
+        free(nl->rqueue);
 
-        for (i = 0; i < rtnl->rqueue_partial_size; i++)
-                sd_netlink_message_unref(rtnl->rqueue_partial[i]);
-        free(rtnl->rqueue_partial);
+        for (i = 0; i < nl->rqueue_partial_size; i++)
+                sd_netlink_message_unref(nl->rqueue_partial[i]);
+        free(nl->rqueue_partial);
 
-        free(rtnl->rbuffer);
+        free(nl->rbuffer);
 
-        while ((s = rtnl->slots)) {
+        while ((s = nl->slots)) {
                 assert(s->floating);
                 netlink_slot_disconnect(s, true);
         }
-        hashmap_free(rtnl->reply_callbacks);
-        prioq_free(rtnl->reply_callbacks_prioq);
+        hashmap_free(nl->reply_callbacks);
+        prioq_free(nl->reply_callbacks_prioq);
 
-        sd_event_source_unref(rtnl->io_event_source);
-        sd_event_source_unref(rtnl->time_event_source);
-        sd_event_unref(rtnl->event);
+        sd_event_source_unref(nl->io_event_source);
+        sd_event_source_unref(nl->time_event_source);
+        sd_event_unref(nl->event);
 
-        hashmap_free(rtnl->broadcast_group_refs);
+        hashmap_free(nl->broadcast_group_refs);
 
-        hashmap_free(rtnl->genl_family_to_nlmsg_type);
-        hashmap_free(rtnl->nlmsg_type_to_genl_family);
+        genl_clear_family(nl);
 
-        safe_close(rtnl->fd);
-        return mfree(rtnl);
+        safe_close(nl->fd);
+        return mfree(nl);
 }
 
 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_netlink, sd_netlink, netlink_free);
 
-static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) {
+static void netlink_seal_message(sd_netlink *nl, sd_netlink_message *m) {
         uint32_t picked;
 
-        assert(rtnl);
-        assert(!rtnl_pid_changed(rtnl));
+        assert(nl);
+        assert(!netlink_pid_changed(nl));
         assert(m);
         assert(m->hdr);
 
         /* Avoid collisions with outstanding requests */
         do {
-                picked = rtnl->serial;
+                picked = nl->serial;
 
                 /* Don't use seq == 0, as that is used for broadcasts, so we would get confused by replies to
                    such messages */
-                rtnl->serial = rtnl->serial == UINT32_MAX ? 1 : rtnl->serial + 1;
+                nl->serial = nl->serial == UINT32_MAX ? 1 : nl->serial + 1;
 
-        } while (hashmap_contains(rtnl->reply_callbacks, UINT32_TO_PTR(picked)));
+        } while (hashmap_contains(nl->reply_callbacks, UINT32_TO_PTR(picked)));
 
         m->hdr->nlmsg_seq = picked;
-        rtnl_message_seal(m);
+        message_seal(m);
 }
 
-int sd_netlink_send(sd_netlink *nl,
-                    sd_netlink_message *message,
-                    uint32_t *serial) {
+int sd_netlink_send(
+                sd_netlink *nl,
+                sd_netlink_message *message,
+                uint32_t *serial) {
+
         int r;
 
         assert_return(nl, -EINVAL);
-        assert_return(!rtnl_pid_changed(nl), -ECHILD);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
         assert_return(message, -EINVAL);
         assert_return(!message->sealed, -EPERM);
 
-        rtnl_seal_message(nl, message);
+        netlink_seal_message(nl, message);
 
         r = socket_write_message(nl, message);
         if (r < 0)
                 return r;
 
         if (serial)
-                *serial = rtnl_message_get_serial(message);
+                *serial = message_get_serial(message);
 
         return 1;
 }
@@ -264,7 +261,7 @@ int sd_netlink_sendv(
         int r;
 
         assert_return(nl, -EINVAL);
-        assert_return(!rtnl_pid_changed(nl), -ECHILD);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
         assert_return(messages, -EINVAL);
         assert_return(msgcount > 0, -EINVAL);
 
@@ -277,9 +274,9 @@ int sd_netlink_sendv(
         for (unsigned i = 0; i < msgcount; i++) {
                 assert_return(!messages[i]->sealed, -EPERM);
 
-                rtnl_seal_message(nl, messages[i]);
+                netlink_seal_message(nl, messages[i]);
                 if (serials)
-                        serials[i] = rtnl_message_get_serial(messages[i]);
+                        serials[i] = message_get_serial(messages[i]);
         }
 
         r = socket_writev_message(nl, messages, msgcount);
@@ -292,45 +289,45 @@ int sd_netlink_sendv(
         return r;
 }
 
-int rtnl_rqueue_make_room(sd_netlink *rtnl) {
-        assert(rtnl);
+int netlink_rqueue_make_room(sd_netlink *nl) {
+        assert(nl);
 
-        if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX)
+        if (nl->rqueue_size >= NETLINK_RQUEUE_MAX)
                 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
-                                       "rtnl: exhausted the read queue size (%d)",
-                                       RTNL_RQUEUE_MAX);
+                                       "sd-netlink: exhausted the read queue size (%d)",
+                                       NETLINK_RQUEUE_MAX);
 
-        if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_size + 1))
+        if (!GREEDY_REALLOC(nl->rqueue, nl->rqueue_size + 1))
                 return -ENOMEM;
 
         return 0;
 }
 
-int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) {
-        assert(rtnl);
+int netlink_rqueue_partial_make_room(sd_netlink *nl) {
+        assert(nl);
 
-        if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX)
+        if (nl->rqueue_partial_size >= NETLINK_RQUEUE_MAX)
                 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
-                                       "rtnl: exhausted the partial read queue size (%d)",
-                                       RTNL_RQUEUE_MAX);
+                                       "sd-netlink: exhausted the partial read queue size (%d)",
+                                       NETLINK_RQUEUE_MAX);
 
-        if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_size + 1))
+        if (!GREEDY_REALLOC(nl->rqueue_partial, nl->rqueue_partial_size + 1))
                 return -ENOMEM;
 
         return 0;
 }
 
-static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) {
+static int dispatch_rqueue(sd_netlink *nl, sd_netlink_message **message) {
         int r;
 
-        assert(rtnl);
+        assert(nl);
         assert(message);
 
-        if (rtnl->rqueue_size <= 0) {
+        if (nl->rqueue_size <= 0) {
                 /* Try to read a new message */
-                r = socket_read_message(rtnl);
+                r = socket_read_message(nl);
                 if (r == -ENOBUFS) { /* FIXME: ignore buffer overruns for now */
-                        log_debug_errno(r, "Got ENOBUFS from netlink socket, ignoring.");
+                        log_debug_errno(r, "sd-netlink: Got ENOBUFS from netlink socket, ignoring.");
                         return 1;
                 }
                 if (r <= 0)
@@ -338,23 +335,23 @@ static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) {
         }
 
         /* Dispatch a queued message */
-        *message = rtnl->rqueue[0];
-        rtnl->rqueue_size--;
-        memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size);
+        *message = nl->rqueue[0];
+        nl->rqueue_size--;
+        memmove(nl->rqueue, nl->rqueue + 1, sizeof(sd_netlink_message*) * nl->rqueue_size);
 
         return 1;
 }
 
-static int process_timeout(sd_netlink *rtnl) {
+static int process_timeout(sd_netlink *nl) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         struct reply_callback *c;
         sd_netlink_slot *slot;
         usec_t n;
         int r;
 
-        assert(rtnl);
+        assert(nl);
 
-        c = prioq_peek(rtnl->reply_callbacks_prioq);
+        c = prioq_peek(nl->reply_callbacks_prioq);
         if (!c)
                 return 0;
 
@@ -362,17 +359,17 @@ static int process_timeout(sd_netlink *rtnl) {
         if (c->timeout > n)
                 return 0;
 
-        r = rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, c->serial, &m);
+        r = message_new_synthetic_error(nl, -ETIMEDOUT, c->serial, &m);
         if (r < 0)
                 return r;
 
-        assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
+        assert_se(prioq_pop(nl->reply_callbacks_prioq) == c);
         c->timeout = 0;
-        hashmap_remove(rtnl->reply_callbacks, UINT32_TO_PTR(c->serial));
+        hashmap_remove(nl->reply_callbacks, UINT32_TO_PTR(c->serial));
 
         slot = container_of(c, sd_netlink_slot, reply_callback);
 
-        r = c->callback(rtnl, m, slot->userdata);
+        r = c->callback(nl, m, slot->userdata);
         if (r < 0)
                 log_debug_errno(r, "sd-netlink: timedout callback %s%s%sfailed: %m",
                                 slot->description ? "'" : "",
@@ -385,23 +382,23 @@ static int process_timeout(sd_netlink *rtnl) {
         return 1;
 }
 
-static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
+static int process_reply(sd_netlink *nl, sd_netlink_message *m) {
         struct reply_callback *c;
         sd_netlink_slot *slot;
         uint32_t serial;
         uint16_t type;
         int r;
 
-        assert(rtnl);
+        assert(nl);
         assert(m);
 
-        serial = rtnl_message_get_serial(m);
-        c = hashmap_remove(rtnl->reply_callbacks, UINT32_TO_PTR(serial));
+        serial = message_get_serial(m);
+        c = hashmap_remove(nl->reply_callbacks, UINT32_TO_PTR(serial));
         if (!c)
                 return 0;
 
         if (c->timeout != 0) {
-                prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
+                prioq_remove(nl->reply_callbacks_prioq, c, &c->prioq_idx);
                 c->timeout = 0;
         }
 
@@ -414,7 +411,7 @@ static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
 
         slot = container_of(c, sd_netlink_slot, reply_callback);
 
-        r = c->callback(rtnl, m, slot->userdata);
+        r = c->callback(nl, m, slot->userdata);
         if (r < 0)
                 log_debug_errno(r, "sd-netlink: reply callback %s%s%sfailed: %m",
                                 slot->description ? "'" : "",
@@ -427,26 +424,37 @@ static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
         return 1;
 }
 
-static int process_match(sd_netlink *rtnl, sd_netlink_message *m) {
+static int process_match(sd_netlink *nl, sd_netlink_message *m) {
         struct match_callback *c;
-        sd_netlink_slot *slot;
         uint16_t type;
+        uint8_t cmd;
         int r;
 
-        assert(rtnl);
+        assert(nl);
         assert(m);
 
         r = sd_netlink_message_get_type(m, &type);
         if (r < 0)
                 return r;
 
-        LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
-                if (type != c->type)
+        if (m->protocol == NETLINK_GENERIC) {
+                r = sd_genl_message_get_command(nl, m, &cmd);
+                if (r < 0)
+                        return r;
+        } else
+                cmd = 0;
+
+        LIST_FOREACH(match_callbacks, c, nl->match_callbacks) {
+                sd_netlink_slot *slot;
+
+                if (c->type != type)
+                        continue;
+                if (c->cmd != 0 && c->cmd != cmd)
                         continue;
 
                 slot = container_of(c, sd_netlink_slot, match_callback);
 
-                r = c->callback(rtnl, m, slot->userdata);
+                r = c->callback(nl, m, slot->userdata);
                 if (r < 0)
                         log_debug_errno(r, "sd-netlink: match callback %s%s%sfailed: %m",
                                         slot->description ? "'" : "",
@@ -459,28 +467,28 @@ static int process_match(sd_netlink *rtnl, sd_netlink_message *m) {
         return 1;
 }
 
-static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) {
+static int process_running(sd_netlink *nl, sd_netlink_message **ret) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        assert(rtnl);
+        assert(nl);
 
-        r = process_timeout(rtnl);
+        r = process_timeout(nl);
         if (r != 0)
                 goto null_message;
 
-        r = dispatch_rqueue(rtnl, &m);
+        r = dispatch_rqueue(nl, &m);
         if (r < 0)
                 return r;
         if (!m)
                 goto null_message;
 
         if (sd_netlink_message_is_broadcast(m)) {
-                r = process_match(rtnl, m);
+                r = process_match(nl, m);
                 if (r != 0)
                         goto null_message;
         } else {
-                r = process_reply(rtnl, m);
+                r = process_reply(nl, m);
                 if (r != 0)
                         goto null_message;
         }
@@ -500,17 +508,17 @@ null_message:
         return r;
 }
 
-int sd_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) {
-        NETLINK_DONT_DESTROY(rtnl);
+int sd_netlink_process(sd_netlink *nl, sd_netlink_message **ret) {
+        NETLINK_DONT_DESTROY(nl);
         int r;
 
-        assert_return(rtnl, -EINVAL);
-        assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
-        assert_return(!rtnl->processing, -EBUSY);
+        assert_return(nl, -EINVAL);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
+        assert_return(!nl->processing, -EBUSY);
 
-        rtnl->processing = true;
-        r = process_running(rtnl, ret);
-        rtnl->processing = false;
+        nl->processing = true;
+        r = process_running(nl, ret);
+        nl->processing = false;
 
         return r;
 }
@@ -520,18 +528,18 @@ static usec_t calc_elapse(uint64_t usec) {
                 return 0;
 
         if (usec == 0)
-                usec = RTNL_DEFAULT_TIMEOUT;
+                usec = NETLINK_DEFAULT_TIMEOUT_USEC;
 
         return usec_add(now(CLOCK_MONOTONIC), usec);
 }
 
-static int rtnl_poll(sd_netlink *rtnl, bool need_more, usec_t timeout_usec) {
+static int netlink_poll(sd_netlink *nl, bool need_more, usec_t timeout_usec) {
         usec_t m = USEC_INFINITY;
         int r, e;
 
-        assert(rtnl);
+        assert(nl);
 
-        e = sd_netlink_get_events(rtnl);
+        e = sd_netlink_get_events(nl);
         if (e < 0)
                 return e;
 
@@ -545,14 +553,14 @@ static int rtnl_poll(sd_netlink *rtnl, bool need_more, usec_t timeout_usec) {
                 /* Caller wants to process if there is something to
                  * process, but doesn't care otherwise */
 
-                r = sd_netlink_get_timeout(rtnl, &until);
+                r = sd_netlink_get_timeout(nl, &until);
                 if (r < 0)
                         return r;
 
                 m = usec_sub_unsigned(until, now(CLOCK_MONOTONIC));
         }
 
-        r = fd_wait_for_event(rtnl->fd, e, MIN(m, timeout_usec));
+        r = fd_wait_for_event(nl->fd, e, MIN(m, timeout_usec));
         if (r <= 0)
                 return r;
 
@@ -561,12 +569,12 @@ static int rtnl_poll(sd_netlink *rtnl, bool need_more, usec_t timeout_usec) {
 
 int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
         assert_return(nl, -EINVAL);
-        assert_return(!rtnl_pid_changed(nl), -ECHILD);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
 
         if (nl->rqueue_size > 0)
                 return 0;
 
-        return rtnl_poll(nl, false, timeout_usec);
+        return netlink_poll(nl, false, timeout_usec);
 }
 
 static int timeout_compare(const void *a, const void *b) {
@@ -597,7 +605,7 @@ int sd_netlink_call_async(
         assert_return(nl, -EINVAL);
         assert_return(m, -EINVAL);
         assert_return(callback, -EINVAL);
-        assert_return(!rtnl_pid_changed(nl), -ECHILD);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
 
         if (hashmap_size(nl->reply_callbacks) >= REPLY_CALLBACKS_MAX)
                 return -ERANGE;
@@ -647,7 +655,7 @@ int sd_netlink_call_async(
 }
 
 int sd_netlink_read(
-                sd_netlink *rtnl,
+                sd_netlink *nl,
                 uint32_t serial,
                 uint64_t usec,
                 sd_netlink_message **ret) {
@@ -655,29 +663,29 @@ int sd_netlink_read(
         usec_t timeout;
         int r;
 
-        assert_return(rtnl, -EINVAL);
-        assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+        assert_return(nl, -EINVAL);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
 
         timeout = calc_elapse(usec);
 
         for (;;) {
                 usec_t left;
 
-                for (unsigned i = 0; i < rtnl->rqueue_size; i++) {
+                for (unsigned i = 0; i < nl->rqueue_size; i++) {
                         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL;
                         uint32_t received_serial;
                         uint16_t type;
 
-                        received_serial = rtnl_message_get_serial(rtnl->rqueue[i]);
+                        received_serial = message_get_serial(nl->rqueue[i]);
                         if (received_serial != serial)
                                 continue;
 
-                        incoming = rtnl->rqueue[i];
+                        incoming = nl->rqueue[i];
 
                         /* found a match, remove from rqueue and return it */
-                        memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
-                                sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1));
-                        rtnl->rqueue_size--;
+                        memmove(nl->rqueue + i, nl->rqueue + i + 1,
+                                sizeof(sd_netlink_message*) * (nl->rqueue_size - i - 1));
+                        nl->rqueue_size--;
 
                         r = sd_netlink_message_get_errno(incoming);
                         if (r < 0)
@@ -697,7 +705,7 @@ int sd_netlink_read(
                         return 1;
                 }
 
-                r = socket_read_message(rtnl);
+                r = socket_read_message(nl);
                 if (r < 0)
                         return r;
                 if (r > 0)
@@ -715,7 +723,7 @@ int sd_netlink_read(
                 } else
                         left = USEC_INFINITY;
 
-                r = rtnl_poll(rtnl, true, left);
+                r = netlink_poll(nl, true, left);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -724,7 +732,7 @@ int sd_netlink_read(
 }
 
 int sd_netlink_call(
-                sd_netlink *rtnl,
+                sd_netlink *nl,
                 sd_netlink_message *message,
                 uint64_t usec,
                 sd_netlink_message **ret) {
@@ -732,37 +740,37 @@ int sd_netlink_call(
         uint32_t serial;
         int r;
 
-        assert_return(rtnl, -EINVAL);
-        assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+        assert_return(nl, -EINVAL);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
         assert_return(message, -EINVAL);
 
-        r = sd_netlink_send(rtnl, message, &serial);
+        r = sd_netlink_send(nl, message, &serial);
         if (r < 0)
                 return r;
 
-        return sd_netlink_read(rtnl, serial, usec, ret);
+        return sd_netlink_read(nl, serial, usec, ret);
 }
 
-int sd_netlink_get_events(sd_netlink *rtnl) {
-        assert_return(rtnl, -EINVAL);
-        assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+int sd_netlink_get_events(sd_netlink *nl) {
+        assert_return(nl, -EINVAL);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
 
-        return rtnl->rqueue_size == 0 ? POLLIN : 0;
+        return nl->rqueue_size == 0 ? POLLIN : 0;
 }
 
-int sd_netlink_get_timeout(sd_netlink *rtnl, uint64_t *timeout_usec) {
+int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
         struct reply_callback *c;
 
-        assert_return(rtnl, -EINVAL);
+        assert_return(nl, -EINVAL);
         assert_return(timeout_usec, -EINVAL);
-        assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+        assert_return(!netlink_pid_changed(nl), -ECHILD);
 
-        if (rtnl->rqueue_size > 0) {
+        if (nl->rqueue_size > 0) {
                 *timeout_usec = 0;
                 return 1;
         }
 
-        c = prioq_peek(rtnl->reply_callbacks_prioq);
+        c = prioq_peek(nl->reply_callbacks_prioq);
         if (!c) {
                 *timeout_usec = UINT64_MAX;
                 return 0;
@@ -774,12 +782,12 @@ int sd_netlink_get_timeout(sd_netlink *rtnl, uint64_t *timeout_usec) {
 }
 
 static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
-        sd_netlink *rtnl = userdata;
+        sd_netlink *nl = userdata;
         int r;
 
-        assert(rtnl);
+        assert(nl);
 
-        r = sd_netlink_process(rtnl, NULL);
+        r = sd_netlink_process(nl, NULL);
         if (r < 0)
                 return r;
 
@@ -787,12 +795,12 @@ static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userd
 }
 
 static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_netlink *rtnl = userdata;
+        sd_netlink *nl = userdata;
         int r;
 
-        assert(rtnl);
+        assert(nl);
 
-        r = sd_netlink_process(rtnl, NULL);
+        r = sd_netlink_process(nl, NULL);
         if (r < 0)
                 return r;
 
@@ -800,192 +808,212 @@ static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
 }
 
 static int prepare_callback(sd_event_source *s, void *userdata) {
-        sd_netlink *rtnl = userdata;
+        sd_netlink *nl = userdata;
         int r, e;
         usec_t until;
 
         assert(s);
-        assert(rtnl);
+        assert(nl);
 
-        e = sd_netlink_get_events(rtnl);
+        e = sd_netlink_get_events(nl);
         if (e < 0)
                 return e;
 
-        r = sd_event_source_set_io_events(rtnl->io_event_source, e);
+        r = sd_event_source_set_io_events(nl->io_event_source, e);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_get_timeout(rtnl, &until);
+        r = sd_netlink_get_timeout(nl, &until);
         if (r < 0)
                 return r;
         if (r > 0) {
                 int j;
 
-                j = sd_event_source_set_time(rtnl->time_event_source, until);
+                j = sd_event_source_set_time(nl->time_event_source, until);
                 if (j < 0)
                         return j;
         }
 
-        r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
+        r = sd_event_source_set_enabled(nl->time_event_source, r > 0);
         if (r < 0)
                 return r;
 
         return 1;
 }
 
-int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t priority) {
+int sd_netlink_attach_event(sd_netlink *nl, sd_event *event, int64_t priority) {
         int r;
 
-        assert_return(rtnl, -EINVAL);
-        assert_return(!rtnl->event, -EBUSY);
+        assert_return(nl, -EINVAL);
+        assert_return(!nl->event, -EBUSY);
 
-        assert(!rtnl->io_event_source);
-        assert(!rtnl->time_event_source);
+        assert(!nl->io_event_source);
+        assert(!nl->time_event_source);
 
         if (event)
-                rtnl->event = sd_event_ref(event);
+                nl->event = sd_event_ref(event);
         else {
-                r = sd_event_default(&rtnl->event);
+                r = sd_event_default(&nl->event);
                 if (r < 0)
                         return r;
         }
 
-        r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
+        r = sd_event_add_io(nl->event, &nl->io_event_source, nl->fd, 0, io_callback, nl);
         if (r < 0)
                 goto fail;
 
-        r = sd_event_source_set_priority(rtnl->io_event_source, priority);
+        r = sd_event_source_set_priority(nl->io_event_source, priority);
         if (r < 0)
                 goto fail;
 
-        r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
+        r = sd_event_source_set_description(nl->io_event_source, "netlink-receive-message");
         if (r < 0)
                 goto fail;
 
-        r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
+        r = sd_event_source_set_prepare(nl->io_event_source, prepare_callback);
         if (r < 0)
                 goto fail;
 
-        r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
+        r = sd_event_add_time(nl->event, &nl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, nl);
         if (r < 0)
                 goto fail;
 
-        r = sd_event_source_set_priority(rtnl->time_event_source, priority);
+        r = sd_event_source_set_priority(nl->time_event_source, priority);
         if (r < 0)
                 goto fail;
 
-        r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
+        r = sd_event_source_set_description(nl->time_event_source, "netlink-timer");
         if (r < 0)
                 goto fail;
 
         return 0;
 
 fail:
-        sd_netlink_detach_event(rtnl);
+        sd_netlink_detach_event(nl);
         return r;
 }
 
-int sd_netlink_detach_event(sd_netlink *rtnl) {
-        assert_return(rtnl, -EINVAL);
-        assert_return(rtnl->event, -ENXIO);
+int sd_netlink_detach_event(sd_netlink *nl) {
+        assert_return(nl, -EINVAL);
+        assert_return(nl->event, -ENXIO);
 
-        rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
+        nl->io_event_source = sd_event_source_unref(nl->io_event_source);
 
-        rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
+        nl->time_event_source = sd_event_source_unref(nl->time_event_source);
 
-        rtnl->event = sd_event_unref(rtnl->event);
+        nl->event = sd_event_unref(nl->event);
 
         return 0;
 }
 
-int sd_netlink_add_match(
-                sd_netlink *rtnl,
+int netlink_add_match_internal(
+                sd_netlink *nl,
                 sd_netlink_slot **ret_slot,
+                const uint32_t *groups,
+                size_t n_groups,
                 uint16_t type,
+                uint8_t cmd,
                 sd_netlink_message_handler_t callback,
                 sd_netlink_destroy_t destroy_callback,
                 void *userdata,
                 const char *description) {
+
         _cleanup_free_ sd_netlink_slot *slot = NULL;
         int r;
 
-        assert_return(rtnl, -EINVAL);
-        assert_return(callback, -EINVAL);
-        assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
+        assert(groups);
+        assert(n_groups > 0);
+
+        for (size_t i = 0; i < n_groups; i++) {
+                r = socket_broadcast_group_ref(nl, groups[i]);
+                if (r < 0)
+                        return r;
+        }
 
-        r = netlink_slot_allocate(rtnl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback), userdata, description, &slot);
+        r = netlink_slot_allocate(nl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback),
+                                  userdata, description, &slot);
         if (r < 0)
                 return r;
 
+        slot->match_callback.groups = newdup(uint32_t, groups, n_groups);
+        if (!slot->match_callback.groups)
+                return -ENOMEM;
+
+        slot->match_callback.n_groups = n_groups;
         slot->match_callback.callback = callback;
         slot->match_callback.type = type;
+        slot->match_callback.cmd = cmd;
+
+        LIST_PREPEND(match_callbacks, nl->match_callbacks, &slot->match_callback);
+
+        /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
+        slot->destroy_callback = destroy_callback;
+
+        if (ret_slot)
+                *ret_slot = slot;
+
+        TAKE_PTR(slot);
+        return 0;
+}
+
+int sd_netlink_add_match(
+                sd_netlink *rtnl,
+                sd_netlink_slot **ret_slot,
+                uint16_t type,
+                sd_netlink_message_handler_t callback,
+                sd_netlink_destroy_t destroy_callback,
+                void *userdata,
+                const char *description) {
+
+        static const uint32_t
+                address_groups[]  = { RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, },
+                link_groups[]     = { RTNLGRP_LINK, },
+                neighbor_groups[] = { RTNLGRP_NEIGH, },
+                nexthop_groups[]  = { RTNLGRP_NEXTHOP, },
+                route_groups[]    = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, },
+                rule_groups[]     = { RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_RULE, };
+        const uint32_t *groups;
+        size_t n_groups;
+
+        assert_return(rtnl, -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(!netlink_pid_changed(rtnl), -ECHILD);
 
         switch (type) {
                 case RTM_NEWLINK:
                 case RTM_DELLINK:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK);
-                        if (r < 0)
-                                return r;
-
+                        groups = link_groups;
+                        n_groups = ELEMENTSOF(link_groups);
                         break;
                 case RTM_NEWADDR:
                 case RTM_DELADDR:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR);
-                        if (r < 0)
-                                return r;
-
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR);
-                        if (r < 0)
-                                return r;
-
+                        groups = address_groups;
+                        n_groups = ELEMENTSOF(address_groups);
                         break;
                 case RTM_NEWNEIGH:
                 case RTM_DELNEIGH:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEIGH);
-                        if (r < 0)
-                                return r;
-
+                        groups = neighbor_groups;
+                        n_groups = ELEMENTSOF(neighbor_groups);
                         break;
                 case RTM_NEWROUTE:
                 case RTM_DELROUTE:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE);
-                        if (r < 0)
-                                return r;
-
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE);
-                        if (r < 0)
-                                return r;
+                        groups = route_groups;
+                        n_groups = ELEMENTSOF(route_groups);
                         break;
                 case RTM_NEWRULE:
                 case RTM_DELRULE:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_RULE);
-                        if (r < 0)
-                                return r;
-
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_RULE);
-                        if (r < 0)
-                                return r;
+                        groups = rule_groups;
+                        n_groups = ELEMENTSOF(rule_groups);
                         break;
                 case RTM_NEWNEXTHOP:
                 case RTM_DELNEXTHOP:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEXTHOP);
-                        if (r < 0)
-                                return r;
-                break;
-
+                        groups = nexthop_groups;
+                        n_groups = ELEMENTSOF(nexthop_groups);
+                        break;
                 default:
                         return -EOPNOTSUPP;
         }
 
-        LIST_PREPEND(match_callbacks, rtnl->match_callbacks, &slot->match_callback);
-
-        /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
-        slot->destroy_callback = destroy_callback;
-
-        if (ret_slot)
-                *ret_slot = slot;
-
-        TAKE_PTR(slot);
-
-        return 0;
+        return netlink_add_match_internal(rtnl, ret_slot, groups, n_groups, type, 0, callback,
+                                          destroy_callback, userdata, description);
 }
index 41a72804774d32a20d8951384d513157e9996085..c5a5e512a203f1c2d2f2254a5d517d06ea9aeb2f 100644 (file)
@@ -2,31 +2,40 @@
 
 #include <net/if.h>
 #include <netinet/ether.h>
+#include <netinet/in.h>
+#include <linux/fou.h>
 #include <linux/genetlink.h>
+#include <linux/if_macsec.h>
+#include <linux/l2tp.h>
+#include <linux/nl80211.h>
 
 #include "sd-netlink.h"
 
 #include "alloc-util.h"
 #include "ether-addr-util.h"
 #include "macro.h"
+#include "netlink-genl.h"
+#include "netlink-internal.h"
 #include "netlink-util.h"
 #include "socket-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
-#include "util.h"
+#include "tests.h"
 
 static void test_message_link_bridge(sd_netlink *rtnl) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
         uint32_t cost;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_NEWLINK, 1) >= 0);
         assert_se(sd_rtnl_message_link_set_family(message, AF_BRIDGE) >= 0);
         assert_se(sd_netlink_message_open_container(message, IFLA_PROTINFO) >= 0);
         assert_se(sd_netlink_message_append_u32(message, IFLA_BRPORT_COST, 10) >= 0);
         assert_se(sd_netlink_message_close_container(message) >= 0);
 
-        assert_se(sd_netlink_message_rewind(message, NULL) >= 0);
+        assert_se(sd_netlink_message_rewind(message, rtnl) >= 0);
 
         assert_se(sd_netlink_message_enter_container(message, IFLA_PROTINFO) >= 0);
         assert_se(sd_netlink_message_read_u32(message, IFLA_BRPORT_COST, &cost) >= 0);
@@ -40,6 +49,8 @@ static void test_link_configure(sd_netlink *rtnl, int ifindex) {
         const char *name_out;
         struct ether_addr mac_out;
 
+        log_debug("/* %s */", __func__);
+
         /* we'd really like to test NEWLINK, but let's not mess with the running kernel */
         assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, ifindex) >= 0);
 
@@ -57,6 +68,8 @@ static void test_link_get(sd_netlink *rtnl, int ifindex) {
         uint32_t u32_data;
         struct ether_addr eth_data;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0);
         assert_se(m);
 
@@ -83,6 +96,8 @@ static void test_address_get(sd_netlink *rtnl, int ifindex) {
         struct ifa_cacheinfo cache;
         const char *label;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_rtnl_message_new_addr(rtnl, &m, RTM_GETADDR, ifindex, AF_INET) >= 0);
         assert_se(m);
         assert_se(sd_netlink_message_request_dump(m, true) >= 0);
@@ -100,6 +115,8 @@ static void test_route(sd_netlink *rtnl) {
         uint32_t index = 2, u32_data;
         int r;
 
+        log_debug("/* %s */", __func__);
+
         r = sd_rtnl_message_new_route(rtnl, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC);
         if (r < 0) {
                 log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
@@ -120,7 +137,7 @@ static void test_route(sd_netlink *rtnl) {
                 return;
         }
 
-        assert_se(sd_netlink_message_rewind(req, NULL) >= 0);
+        assert_se(sd_netlink_message_rewind(req, rtnl) >= 0);
 
         assert_se(sd_netlink_message_read_in_addr(req, RTA_GATEWAY, &addr_data) >= 0);
         assert_se(addr_data.s_addr == addr.s_addr);
@@ -134,6 +151,8 @@ static void test_route(sd_netlink *rtnl) {
 static void test_multiple(void) {
         sd_netlink *rtnl1, *rtnl2;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_netlink_open(&rtnl1) >= 0);
         assert_se(sd_netlink_open(&rtnl2) >= 0);
 
@@ -164,6 +183,8 @@ static void test_event_loop(int ifindex) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         char *ifname;
 
+        log_debug("/* %s */", __func__);
+
         ifname = strdup("lo2");
         assert_se(ifname);
 
@@ -194,6 +215,8 @@ static void test_async(int ifindex) {
         const char *description;
         char *ifname;
 
+        log_debug("/* %s */", __func__);
+
         ifname = strdup("lo");
         assert_se(ifname);
 
@@ -225,6 +248,8 @@ static void test_slot_set(int ifindex) {
         const char *description;
         char *ifname;
 
+        log_debug("/* %s */", __func__);
+
         ifname = strdup("lo");
         assert_se(ifname);
 
@@ -303,6 +328,8 @@ static void test_async_destroy_callback(int ifindex) {
         _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL;
         char *ifname;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(t = new(struct test_async_object, 1));
         assert_se(ifname = strdup("lo"));
         *t = (struct test_async_object) {
@@ -371,6 +398,8 @@ static void test_pipe(int ifindex) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m1 = NULL, *m2 = NULL;
         int counter = 0;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_netlink_open(&rtnl) >= 0);
 
         assert_se(sd_rtnl_message_new_link(rtnl, &m1, RTM_GETLINK, ifindex) >= 0);
@@ -396,6 +425,8 @@ static void test_container(sd_netlink *rtnl) {
         uint32_t u32_data;
         const char *string_data;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0) >= 0);
 
         assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0);
@@ -406,7 +437,7 @@ static void test_container(sd_netlink *rtnl) {
         assert_se(sd_netlink_message_close_container(m) >= 0);
         assert_se(sd_netlink_message_close_container(m) == -EINVAL);
 
-        assert_se(sd_netlink_message_rewind(m, NULL) >= 0);
+        assert_se(sd_netlink_message_rewind(m, rtnl) >= 0);
 
         assert_se(sd_netlink_message_enter_container(m, IFLA_LINKINFO) >= 0);
         assert_se(sd_netlink_message_read_string(m, IFLA_INFO_KIND, &string_data) >= 0);
@@ -429,6 +460,8 @@ static void test_match(void) {
         _cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *s1 = NULL, *s2 = NULL;
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_netlink_open(&rtnl) >= 0);
 
         assert_se(sd_netlink_add_match(rtnl, &s1, RTM_NEWLINK, link_handler, NULL, NULL, NULL) >= 0);
@@ -445,6 +478,8 @@ static void test_get_addresses(sd_netlink *rtnl) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
         sd_netlink_message *m;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC) >= 0);
         assert_se(sd_netlink_message_request_dump(req, true) >= 0);
         assert_se(sd_netlink_call(rtnl, req, 0, &reply) >= 0);
@@ -472,7 +507,9 @@ static void test_get_addresses(sd_netlink *rtnl) {
 static void test_message(sd_netlink *rtnl) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
 
-        assert_se(rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, 1, &m) >= 0);
+        log_debug("/* %s */", __func__);
+
+        assert_se(message_new_synthetic_error(rtnl, -ETIMEDOUT, 1, &m) >= 0);
         assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT);
 }
 
@@ -480,8 +517,10 @@ static void test_array(void) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_genl_socket_open(&genl) >= 0);
-        assert_se(sd_genl_message_new(genl, SD_GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &m) >= 0);
+        assert_se(sd_genl_message_new(genl, CTRL_GENL_NAME, CTRL_CMD_GETFAMILY, &m) >= 0);
 
         assert_se(sd_netlink_message_open_container(m, CTRL_ATTR_MCAST_GROUPS) >= 0);
         for (unsigned i = 0; i < 10; i++) {
@@ -496,7 +535,7 @@ static void test_array(void) {
         }
         assert_se(sd_netlink_message_close_container(m) >= 0);
 
-        rtnl_message_seal(m);
+        message_seal(m);
         assert_se(sd_netlink_message_rewind(m, genl) >= 0);
 
         assert_se(sd_netlink_message_enter_container(m, CTRL_ATTR_MCAST_GROUPS) >= 0);
@@ -522,6 +561,8 @@ static void test_strv(sd_netlink *rtnl) {
         _cleanup_strv_free_ char **names_in = NULL, **names_out;
         const char *p;
 
+        log_debug("/* %s */", __func__);
+
         assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINKPROP, 1) >= 0);
 
         for (unsigned i = 0; i < 10; i++) {
@@ -535,8 +576,8 @@ static void test_strv(sd_netlink *rtnl) {
         assert_se(sd_netlink_message_append_strv(m, IFLA_ALT_IFNAME, names_in) >= 0);
         assert_se(sd_netlink_message_close_container(m) >= 0);
 
-        rtnl_message_seal(m);
-        assert_se(sd_netlink_message_rewind(m, NULL) >= 0);
+        message_seal(m);
+        assert_se(sd_netlink_message_rewind(m, rtnl) >= 0);
 
         assert_se(sd_netlink_message_read_strv(m, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names_out) >= 0);
         assert_se(strv_equal(names_in, names_out));
@@ -547,6 +588,84 @@ static void test_strv(sd_netlink *rtnl) {
         assert_se(sd_netlink_message_exit_container(m) >= 0);
 }
 
+static int genl_ctrl_match_callback(sd_netlink *genl, sd_netlink_message *m, void *userdata) {
+        const char *name;
+        uint16_t id;
+        uint8_t cmd;
+
+        assert(genl);
+        assert(m);
+
+        assert_se(sd_genl_message_get_family_name(genl, m, &name) >= 0);
+        assert_se(streq(name, CTRL_GENL_NAME));
+
+        assert_se(sd_genl_message_get_command(genl, m, &cmd) >= 0);
+
+        switch (cmd) {
+        case CTRL_CMD_NEWFAMILY:
+        case CTRL_CMD_DELFAMILY:
+                assert_se(sd_netlink_message_read_string(m, CTRL_ATTR_FAMILY_NAME, &name) >= 0);
+                assert_se(sd_netlink_message_read_u16(m, CTRL_ATTR_FAMILY_ID, &id) >= 0);
+                log_debug("%s: %s (id=%"PRIu16") family is %s.",
+                          __func__, name, id, cmd == CTRL_CMD_NEWFAMILY ? "added" : "removed");
+                break;
+        case CTRL_CMD_NEWMCAST_GRP:
+        case CTRL_CMD_DELMCAST_GRP:
+                assert_se(sd_netlink_message_read_string(m, CTRL_ATTR_FAMILY_NAME, &name) >= 0);
+                assert_se(sd_netlink_message_read_u16(m, CTRL_ATTR_FAMILY_ID, &id) >= 0);
+                log_debug("%s: multicast group for %s (id=%"PRIu16") family is %s.",
+                          __func__, name, id, cmd == CTRL_CMD_NEWMCAST_GRP ? "added" : "removed");
+                break;
+        default:
+                log_debug("%s: received nlctrl message with unknown command '%"PRIu8"'.", __func__, cmd);
+        }
+
+        return 0;
+}
+
+static void test_genl(void) {
+        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+        _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        const char *name;
+        uint8_t cmd;
+        int r;
+
+        log_debug("/* %s */", __func__);
+
+        assert_se(sd_genl_socket_open(&genl) >= 0);
+        assert_se(sd_event_default(&event) >= 0);
+        assert_se(sd_netlink_attach_event(genl, event, 0) >= 0);
+
+        assert_se(sd_genl_message_new(genl, CTRL_GENL_NAME, CTRL_CMD_GETFAMILY, &m) >= 0);
+        assert_se(sd_genl_message_get_family_name(genl, m, &name) >= 0);
+        assert_se(streq(name, CTRL_GENL_NAME));
+        assert_se(sd_genl_message_get_command(genl, m, &cmd) >= 0);
+        assert_se(cmd == CTRL_CMD_GETFAMILY);
+
+        assert_se(sd_genl_add_match(genl, NULL, CTRL_GENL_NAME, "notify", 0, genl_ctrl_match_callback, NULL, NULL, "genl-ctrl-notify") >= 0);
+
+        m = sd_netlink_message_unref(m);
+        assert_se(sd_genl_message_new(genl, "should-not-exist", CTRL_CMD_GETFAMILY, &m) < 0);
+        assert_se(sd_genl_message_new(genl, "should-not-exist", CTRL_CMD_GETFAMILY, &m) == -EOPNOTSUPP);
+
+        /* These families may not be supported by kernel. Hence, ignore results. */
+        (void) sd_genl_message_new(genl, FOU_GENL_NAME, 0, &m);
+        m = sd_netlink_message_unref(m);
+        (void) sd_genl_message_new(genl, L2TP_GENL_NAME, 0, &m);
+        m = sd_netlink_message_unref(m);
+        (void) sd_genl_message_new(genl, MACSEC_GENL_NAME, 0, &m);
+        m = sd_netlink_message_unref(m);
+        (void) sd_genl_message_new(genl, NL80211_GENL_NAME, 0, &m);
+
+        for (;;) {
+                r = sd_event_run(event, 500 * USEC_PER_MSEC);
+                assert_se(r >= 0);
+                if (r == 0)
+                        return;
+        }
+}
+
 int main(void) {
         sd_netlink *rtnl;
         sd_netlink_message *m;
@@ -555,6 +674,8 @@ int main(void) {
         int if_loopback;
         uint16_t type;
 
+        test_setup_logging(LOG_DEBUG);
+
         test_match();
         test_multiple();
 
@@ -605,5 +726,7 @@ int main(void) {
         assert_se((r = sd_netlink_message_unref(r)) == NULL);
         assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL);
 
+        test_genl();
+
         return EXIT_SUCCESS;
 }
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 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 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 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 8c20e6be65cc8a38b96c2e7a075d2fee57c0dd1f..b637adc9a2fbc75916ce6a3e7e486ca8673e416c 100644 (file)
@@ -67,7 +67,7 @@ pam_systemd_c = files('pam_systemd.c')
 
 enable_logind = conf.get('ENABLE_LOGIND') == 1
 in_files = [
-        ['logind.conf',        pkgsysconfdir, enable_logind],
+        ['logind.conf',        pkgsysconfdir, enable_logind and install_sysconfdir_samples],
         ['70-uaccess.rules',   udevrulesdir,  enable_logind and conf.get('HAVE_ACL') == 1],
         ['71-seat.rules',      udevrulesdir,  enable_logind],
         ['73-seat-late.rules', udevrulesdir,  enable_logind],
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..8b1cb247e94eab29e436996717692bfc6003bc67 100644 (file)
@@ -329,9 +329,12 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
+        if (arg_user)
+                arg_ask_password = false;
+
         if (arg_user && arg_transport != BUS_TRANSPORT_LOCAL)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Execution in user context is not supported on non-local systems.");
@@ -1530,7 +1533,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 1f899e90f67abff9d02873608bb2f59e206b3df4..1d12fc79cc270d14061a2611efa5dfb4cfc91b41 100644 (file)
@@ -122,7 +122,7 @@ static int netdev_batadv_post_create(NetDev *netdev, Link *link, sd_netlink_mess
         b = BATADV(netdev);
         assert(b);
 
-        r = sd_genl_message_new(netdev->manager->genl, SD_GENL_BATADV, BATADV_CMD_SET_MESH, &message);
+        r = sd_genl_message_new(netdev->manager->genl, BATADV_NL_NAME, BATADV_CMD_SET_MESH, &message);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
 
index 6863257a58948099a218f778b50c9df73bceab34..bc4c108a22eec0456b16fbc59a58b8bd25ad687a 100644 (file)
@@ -36,7 +36,7 @@ static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **r
 
         assert(t);
 
-        r = sd_genl_message_new(netdev->manager->genl, SD_GENL_FOU, FOU_CMD_ADD, &m);
+        r = sd_genl_message_new(netdev->manager->genl, FOU_GENL_NAME, FOU_CMD_ADD, &m);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
 
@@ -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..a04fa6ac46ad5df0edebcf9f1eb4c253a5324024 100644 (file)
@@ -104,7 +104,7 @@ static int netdev_l2tp_fill_message_tunnel(NetDev *netdev, union in_addr_union *
 
         assert(t);
 
-        r = sd_genl_message_new(netdev->manager->genl, SD_GENL_L2TP, L2TP_CMD_TUNNEL_CREATE, &m);
+        r = sd_genl_message_new(netdev->manager->genl, L2TP_GENL_NAME, L2TP_CMD_TUNNEL_CREATE, &m);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
 
@@ -195,7 +195,7 @@ static int netdev_l2tp_fill_message_session(NetDev *netdev, L2tpSession *session
         assert(session);
         assert(session->tunnel);
 
-        r = sd_genl_message_new(netdev->manager->genl, SD_GENL_L2TP, L2TP_CMD_SESSION_CREATE, &m);
+        r = sd_genl_message_new(netdev->manager->genl, L2TP_GENL_NAME, L2TP_CMD_SESSION_CREATE, &m);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
 
@@ -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..3b0c9408045253805a5a2d7ee948860b356f250a 100644 (file)
@@ -224,7 +224,7 @@ static int netdev_macsec_fill_message(NetDev *netdev, int command, sd_netlink_me
         assert(netdev);
         assert(netdev->ifindex > 0);
 
-        r = sd_genl_message_new(netdev->manager->genl, SD_GENL_MACSEC, command, &m);
+        r = sd_genl_message_new(netdev->manager->genl, MACSEC_GENL_NAME, command, &m);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m");
 
@@ -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 b8fc492d4fcc91c881777a1ee8da39b644b5373d..1e90cad2e7129672f158c60104c79d3d7c8b6ad5 100644 (file)
@@ -229,7 +229,7 @@ static int wireguard_set_interface(NetDev *netdev) {
 
                 message = sd_netlink_message_unref(message);
 
-                r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message);
+                r = sd_genl_message_new(netdev->manager->genl, WG_GENL_NAME, WG_CMD_SET_DEVICE, &message);
                 if (r < 0)
                         return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
 
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..25725f4f28c59a43a84fea0ec55b01247c171ec6 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,18 +195,38 @@ 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;
 }
 
+bool address_is_filtered(int family, const union in_addr_union *address, uint8_t prefixlen, Set *allow_list, Set *deny_list) {
+        struct in_addr_prefix *p;
+
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(address);
+
+        if (allow_list) {
+                SET_FOREACH(p, allow_list)
+                        if (p->family == family &&
+                            p->prefixlen <= prefixlen &&
+                            in_addr_prefix_covers(family, &p->address, p->prefixlen, address) > 0)
+                                return false;
+
+                return true;
+        }
+
+        SET_FOREACH(p, deny_list)
+                if (p->family == family &&
+                    in_addr_prefix_intersect(family, &p->address, p->prefixlen, address, prefixlen) > 0)
+                        return true;
+
+        return false;
+}
+
 int config_parse_dhcp(
                 const char* unit,
                 const char *filename,
@@ -511,16 +484,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 +517,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 +1010,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 +1040,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 +1135,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 +1165,72 @@ 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);
 }
+
+int config_parse_address_filter(
+                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) {
+
+        Set **list = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(IN_SET(ltype, AF_INET, AF_INET6));
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *list = set_free(*list);
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *n = NULL;
+                _cleanup_free_ struct in_addr_prefix *a = NULL;
+                struct in_addr_prefix prefix;
+
+                r = extract_first_word(&p, &n, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse NDisc %s=, ignoring assignment: %s",
+                                   lvalue, rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                r = in_addr_prefix_from_string(n, ltype, &prefix.address, &prefix.prefixlen);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "NDisc %s= entry is invalid, ignoring assignment: %s",
+                                   lvalue, n);
+                        continue;
+                }
+
+                prefix.family = ltype;
+                a = newdup(struct in_addr_prefix, &prefix, 1);
+                if (!a)
+                        return log_oom();
+
+                r = set_ensure_consume(list, &in_addr_prefix_hash_ops_free, TAKE_PTR(a));
+                if (r < 0)
+                        return log_oom();
+                if (r == 0)
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "%s %s= entry is duplicated, ignoring assignment: %s",
+                                   section, lvalue, n);
+        }
+}
index 316f5cf10b22be3260434a0c9f0c3ed1b07af82a..236640e9e0be060f2909353322dfee8129b91a16 100644 (file)
@@ -5,6 +5,8 @@
 
 #include "conf-parser.h"
 #include "dhcp-identifier.h"
+#include "in-addr-util.h"
+#include "set.h"
 #include "time-util.h"
 
 #define DHCP_ROUTE_METRIC 1024
@@ -63,6 +65,14 @@ static inline const DUID *link_get_dhcp6_duid(Link *link) {
 int dhcp_configure_duid(Link *link, const DUID *duid);
 int manager_request_product_uuid(Manager *m);
 
+bool address_is_filtered(int family, const union in_addr_union *address, uint8_t prefixlen, Set *allow_list, Set *deny_list);
+static inline bool in4_address_is_filtered(const struct in_addr *address, Set *allow_list, Set *deny_list) {
+        return address_is_filtered(AF_INET, &(union in_addr_union) { .in = *address }, 32, allow_list, deny_list);
+}
+static inline bool in6_prefix_is_filtered(const struct in6_addr *prefix, uint8_t prefixlen, Set *allow_list, Set *deny_list) {
+        return address_is_filtered(AF_INET6, &(union in_addr_union) { .in6 = *prefix }, prefixlen, allow_list, deny_list);
+}
+
 const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
 DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
 
@@ -85,3 +95,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_network_duid_type);
 CONFIG_PARSER_PROTOTYPE(config_parse_duid_rawdata);
 CONFIG_PARSER_PROTOTYPE(config_parse_manager_duid_rawdata);
 CONFIG_PARSER_PROTOTYPE(config_parse_network_duid_rawdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_address_filter);
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..dcc16e0a5d276e334b249de8e990aa5d8a99b989 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;
 
@@ -1119,7 +1125,7 @@ static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) {
         return r;
 }
 
-static int dhcp_server_is_deny_listed(Link *link, sd_dhcp_client *client) {
+static int dhcp_server_is_filtered(Link *link, sd_dhcp_client *client) {
         sd_dhcp_lease *lease;
         struct in_addr addr;
         int r;
@@ -1136,39 +1142,16 @@ static int dhcp_server_is_deny_listed(Link *link, sd_dhcp_client *client) {
         if (r < 0)
                 return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m");
 
-        if (set_contains(link->network->dhcp_deny_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
-                log_struct(LOG_DEBUG,
-                           LOG_LINK_INTERFACE(link),
-                           LOG_LINK_MESSAGE(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" found in deny-list, ignoring offer",
-                                            IPV4_ADDRESS_FMT_VAL(addr)));
-                return true;
-        }
-
-        return false;
-}
-
-static int dhcp_server_is_allow_listed(Link *link, sd_dhcp_client *client) {
-        sd_dhcp_lease *lease;
-        struct in_addr addr;
-        int r;
-
-        assert(link);
-        assert(link->network);
-        assert(client);
-
-        r = sd_dhcp_client_get_lease(client, &lease);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to get DHCP lease: %m");
-
-        r = sd_dhcp_lease_get_server_identifier(lease, &addr);
-        if (r < 0)
-                return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m");
+        if (in4_address_is_filtered(&addr, link->network->dhcp_allow_listed_ip, link->network->dhcp_deny_listed_ip)) {
+                if (DEBUG_LOGGING) {
+                        if (link->network->dhcp_allow_listed_ip)
+                                log_link_debug(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" not found in allow-list, ignoring offer.",
+                                               IPV4_ADDRESS_FMT_VAL(addr));
+                        else
+                                log_link_debug(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" found in deny-list, ignoring offer.",
+                                               IPV4_ADDRESS_FMT_VAL(addr));
+                }
 
-        if (set_contains(link->network->dhcp_allow_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
-                log_struct(LOG_DEBUG,
-                           LOG_LINK_INTERFACE(link),
-                           LOG_LINK_MESSAGE(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" found in allow-list, accepting offer",
-                                            IPV4_ADDRESS_FMT_VAL(addr)));
                 return true;
         }
 
@@ -1261,19 +1244,13 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                         }
                         break;
                 case SD_DHCP_CLIENT_EVENT_SELECTING:
-                        if (!set_isempty(link->network->dhcp_allow_listed_ip)) {
-                                r = dhcp_server_is_allow_listed(link, client);
-                                if (r < 0)
-                                        return r;
-                                if (r == 0)
-                                        return -ENOMSG;
-                        } else {
-                                r = dhcp_server_is_deny_listed(link, client);
-                                if (r < 0)
-                                        return r;
-                                if (r != 0)
-                                        return -ENOMSG;
+                        r = dhcp_server_is_filtered(link, client);
+                        if (r < 0) {
+                                link_enter_failed(link);
+                                return r;
                         }
+                        if (r > 0)
+                                return -ENOMSG;
                         break;
 
                 case SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE:
@@ -1311,7 +1288,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 +1296,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 +1328,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 +1344,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 +1362,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 +1392,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 +1413,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 +1429,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 +1511,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 +1519,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 +1527,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 +1538,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 +1603,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 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");
 
-        return sd_dhcp_client_start(link->dhcp_client);
+        log_link_debug(link, "Requested configuring of the DHCPv4 client.");
+        return 0;
 }
 
 int config_parse_dhcp_max_attempts(
@@ -1703,64 +1730,6 @@ int config_parse_dhcp_max_attempts(
         return 0;
 }
 
-int config_parse_dhcp_acl_ip_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) {
-
-        Network *network = data;
-        Set **acl;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        acl = STR_IN_SET(lvalue, "DenyList", "BlackList") ? &network->dhcp_deny_listed_ip : &network->dhcp_allow_listed_ip;
-
-        if (isempty(rvalue)) {
-                *acl = set_free(*acl);
-                return 0;
-        }
-
-        for (const char *p = rvalue;;) {
-                _cleanup_free_ char *n = NULL;
-                union in_addr_union ip;
-
-                r = extract_first_word(&p, &n, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Failed to parse DHCP '%s=' IP address, ignoring assignment: %s",
-                                   lvalue, rvalue);
-                        return 0;
-                }
-                if (r == 0)
-                        return 0;
-
-                r = in_addr_from_string(AF_INET, n, &ip);
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "DHCP '%s=' IP address is invalid, ignoring assignment: %s", lvalue, n);
-                        continue;
-                }
-
-                r = set_ensure_put(acl, NULL, UINT32_TO_PTR(ip.in.s_addr));
-                if (r < 0)
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Failed to store DHCP '%s=' IP address '%s', ignoring assignment: %m", lvalue, n);
-        }
-}
-
 int config_parse_dhcp_ip_service_type(
                 const char *unit,
                 const char *filename,
@@ -1788,7 +1757,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 +1768,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 +1797,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..339372963d01eadc84f2c4bd8e9c4644182960e2 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,16 @@ 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 97a4cafad41fa49b150bee4a6452da7f877f3bb8..59313bcdfd6c8949f1da752d2fab8d82ba313338 100644 (file)
@@ -507,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) ||
@@ -522,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),
@@ -594,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;
         }
@@ -618,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");
         }
 
@@ -654,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) {
@@ -694,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;
 }
 
@@ -979,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);
 
@@ -1003,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);
@@ -1131,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;
 
@@ -1147,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;
 
@@ -1155,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)
@@ -1228,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);
@@ -1257,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);
@@ -1288,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;
@@ -1564,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) {
@@ -1947,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");
 
@@ -2481,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..1c796486a1bfbbfd465542a82736183e4fa72a5b 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");
 
@@ -900,13 +917,15 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get route destination address: %m");
 
-        if ((!set_isempty(link->network->ndisc_allow_listed_route_prefix) &&
-             !set_contains(link->network->ndisc_allow_listed_route_prefix, &dst)) ||
-            set_contains(link->network->ndisc_deny_listed_route_prefix, &dst)) {
+        r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to get route prefix length: %m");
+
+        if (in6_prefix_is_filtered(&dst, prefixlen, link->network->ndisc_allow_listed_route_prefix, link->network->ndisc_deny_listed_route_prefix)) {
                 if (DEBUG_LOGGING) {
                         _cleanup_free_ char *buf = NULL;
 
-                        (void) in6_addr_to_string(&dst, &buf);
+                        (void) in6_addr_prefix_to_string(&dst, prefixlen, &buf);
                         if (!set_isempty(link->network->ndisc_allow_listed_route_prefix))
                                 log_link_debug(link, "Route prefix '%s' is not in allow list, ignoring", strna(buf));
                         else
@@ -929,10 +948,6 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
                 return 0;
         }
 
-        r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Failed to get route prefix length: %m");
-
         r = sd_ndisc_router_route_get_preference(rt, &preference);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m");
@@ -1028,7 +1043,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 +1054,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 +1141,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)
@@ -1167,6 +1182,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
                 switch (type) {
 
                 case SD_NDISC_OPTION_PREFIX_INFORMATION: {
+                        unsigned prefixlen;
                         struct in6_addr a;
                         uint8_t flags;
 
@@ -1174,13 +1190,15 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
                         if (r < 0)
                                 return log_link_error_errno(link, r, "Failed to get prefix address: %m");
 
-                        if ((!set_isempty(link->network->ndisc_allow_listed_prefix) &&
-                             !set_contains(link->network->ndisc_allow_listed_prefix, &a)) ||
-                            set_contains(link->network->ndisc_deny_listed_prefix, &a)) {
+                        r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Failed to get prefix length: %m");
+
+                        if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
                                 if (DEBUG_LOGGING) {
                                         _cleanup_free_ char *b = NULL;
 
-                                        (void) in6_addr_to_string(&a, &b);
+                                        (void) in6_addr_prefix_to_string(&a, prefixlen, &b);
                                         if (!set_isempty(link->network->ndisc_allow_listed_prefix))
                                                 log_link_debug(link, "Prefix '%s' is not in allow list, ignoring", strna(b));
                                         else
@@ -1250,9 +1268,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
 
-        if ((!set_isempty(link->network->ndisc_allow_listed_router) &&
-             !set_contains(link->network->ndisc_allow_listed_router, &router)) ||
-            set_contains(link->network->ndisc_deny_listed_router, &router)) {
+        if (in6_prefix_is_filtered(&router, 128, link->network->ndisc_allow_listed_router, link->network->ndisc_deny_listed_router)) {
                 if (DEBUG_LOGGING) {
                         _cleanup_free_ char *buf = NULL;
 
@@ -1350,7 +1366,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 +1410,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 +1447,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);
@@ -1466,70 +1485,6 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 ipv6_token_compare_func,
                 free);
 
-int config_parse_ndisc_address_filter(
-                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) {
-
-        Set **list = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if (isempty(rvalue)) {
-                *list = set_free_free(*list);
-                return 0;
-        }
-
-        for (const char *p = rvalue;;) {
-                _cleanup_free_ char *n = NULL;
-                _cleanup_free_ struct in6_addr *a = NULL;
-                union in_addr_union ip;
-
-                r = extract_first_word(&p, &n, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Failed to parse NDisc %s=, ignoring assignment: %s",
-                                   lvalue, rvalue);
-                        return 0;
-                }
-                if (r == 0)
-                        return 0;
-
-                r = in_addr_from_string(AF_INET6, n, &ip);
-                if (r < 0) {
-                        log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "NDisc %s= entry is invalid, ignoring assignment: %s",
-                                   lvalue, n);
-                        continue;
-                }
-
-                a = newdup(struct in6_addr, &ip.in6, 1);
-                if (!a)
-                        return log_oom();
-
-                r = set_ensure_consume(list, &in6_addr_hash_ops, TAKE_PTR(a));
-                if (r < 0)
-                        return log_oom();
-                if (r == 0)
-                        log_syntax(unit, LOG_WARNING, filename, line, 0,
-                                   "NDisc %s= entry is duplicated, ignoring assignment: %s",
-                                   lvalue, n);
-        }
-}
-
 int config_parse_address_generation_type(
                 const char *unit,
                 const char *filename,
index 2ff9a8969d6bc8a0eb512825bb5897c1b131191c..bb32a68690c6492dabab59f5081adc39f27b9680 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));
 }
@@ -77,7 +58,6 @@ int ndisc_start(Link *link);
 void ndisc_vacuum(Link *link);
 void ndisc_flush(Link *link);
 
-CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_address_filter);
 CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_use_domains);
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..405ddccc8b53863818474acfbb93d853cf85a4d1 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)
@@ -226,8 +227,8 @@ DHCPv4.UseTimezone,                          config_parse_bool,
 DHCPv4.ListenPort,                           config_parse_uint16,                                      0,                             offsetof(Network, dhcp_client_port)
 DHCPv4.SendRelease,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_send_release)
 DHCPv4.SendDecline,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_send_decline)
-DHCPv4.DenyList,                             config_parse_dhcp_acl_ip_address,                         0,                             0
-DHCPv4.AllowList,                            config_parse_dhcp_acl_ip_address,                         0,                             0
+DHCPv4.DenyList,                             config_parse_address_filter,                              AF_INET,                       offsetof(Network, dhcp_deny_listed_ip)
+DHCPv4.AllowList,                            config_parse_address_filter,                              AF_INET,                       offsetof(Network, dhcp_allow_listed_ip)
 DHCPv4.IPServiceType,                        config_parse_dhcp_ip_service_type,                        0,                             offsetof(Network, dhcp_ip_service_type)
 DHCPv4.SendOption,                           config_parse_dhcp_send_option,                            AF_INET,                       offsetof(Network, dhcp_client_send_options)
 DHCPv4.SendVendorOption,                     config_parse_dhcp_send_option,                            0,                             offsetof(Network, dhcp_client_send_vendor_options)
@@ -255,17 +256,18 @@ 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
-IPv6AcceptRA.RouterAllowList,                config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_allow_listed_router)
-IPv6AcceptRA.RouterDenyList,                 config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_deny_listed_router)
-IPv6AcceptRA.PrefixAllowList,                config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_allow_listed_prefix)
-IPv6AcceptRA.PrefixDenyList,                 config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_deny_listed_prefix)
-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)
+IPv6AcceptRA.RouterAllowList,                config_parse_address_filter,                              AF_INET6,                      offsetof(Network, ndisc_allow_listed_router)
+IPv6AcceptRA.RouterDenyList,                 config_parse_address_filter,                              AF_INET6,                      offsetof(Network, ndisc_deny_listed_router)
+IPv6AcceptRA.PrefixAllowList,                config_parse_address_filter,                              AF_INET6,                      offsetof(Network, ndisc_allow_listed_prefix)
+IPv6AcceptRA.PrefixDenyList,                 config_parse_address_filter,                              AF_INET6,                      offsetof(Network, ndisc_deny_listed_prefix)
+IPv6AcceptRA.RouteAllowList,                 config_parse_address_filter,                              AF_INET6,                      offsetof(Network, ndisc_allow_listed_route_prefix)
+IPv6AcceptRA.RouteDenyList,                  config_parse_address_filter,                              AF_INET6,                      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
@@ -481,7 +498,7 @@ IPv6PrefixDelegation.DNS,                    config_parse_radv_dns,
 IPv6PrefixDelegation.EmitDomains,            config_parse_bool,                                        0,                             offsetof(Network, router_emit_domains)
 IPv6PrefixDelegation.Domains,                config_parse_radv_search_domains,                         0,                             0
 IPv6PrefixDelegation.DNSLifetimeSec,         config_parse_sec,                                         0,                             offsetof(Network, router_dns_lifetime_usec)
-DHCPv4.BlackList,                            config_parse_dhcp_acl_ip_address,                         0,                             0
+DHCPv4.BlackList,                            config_parse_address_filter,                              AF_INET,                       offsetof(Network, dhcp_deny_listed_ip)
 DHCP.ClientIdentifier,                       config_parse_dhcp_client_identifier,                      0,                             offsetof(Network, dhcp_client_identifier)
 DHCP.UseDNS,                                 config_parse_dhcp_use_dns,                                0,                             0
 DHCP.UseNTP,                                 config_parse_dhcp_use_ntp,                                0,                             0
@@ -509,8 +526,8 @@ DHCP.ForceDHCPv6PDOtherInformation,          config_parse_bool,
 DHCPv4.UseDomainName,                        config_parse_dhcp_use_domains,                            0,                             0
 DHCPv4.CriticalConnection,                   config_parse_tristate,                                    0,                             offsetof(Network, dhcp_critical)
 DHCPv6.RouteMetric,                          config_parse_dhcp_route_metric,                           0,                             0
-IPv6AcceptRA.DenyList,                       config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_deny_listed_prefix)
-IPv6AcceptRA.BlackList,                      config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_deny_listed_prefix)
+IPv6AcceptRA.DenyList,                       config_parse_address_filter,                              AF_INET6,                      offsetof(Network, ndisc_deny_listed_prefix)
+IPv6AcceptRA.BlackList,                      config_parse_address_filter,                              AF_INET6,                      offsetof(Network, ndisc_deny_listed_prefix)
 TrafficControlQueueingDiscipline.Parent,                        config_parse_qdisc_parent,             _QDISC_KIND_INVALID,           0
 TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec,       config_parse_network_emulator_delay,   0,                             0
 TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_network_emulator_delay,   0,                             0
index 850b4f449e10a825fd1c35485420e589b6df1361..cdca91c7c2e78838c33f7bcdc41694729a142948 100644 (file)
@@ -13,6 +13,7 @@
 #include "hostname-util.h"
 #include "in-addr-util.h"
 #include "net-condition.h"
+#include "netdev/macvlan.h"
 #include "networkd-address-label.h"
 #include "networkd-address.h"
 #include "networkd-bridge-fdb.h"
@@ -170,8 +171,33 @@ int network_verify(Network *network) {
                 network->routes_by_section = hashmap_free_with_destructor(network->routes_by_section, route_free);
         }
 
-        if (network->link_local < 0)
-                network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
+        if (network->link_local < 0) {
+                network->link_local = ADDRESS_FAMILY_IPV6;
+
+                if (network->bridge)
+                        network->link_local = ADDRESS_FAMILY_NO;
+                else {
+                        NetDev *netdev;
+
+                        HASHMAP_FOREACH(netdev, network->stacked_netdevs) {
+                                MacVlan *m;
+
+                                if (netdev->kind == NETDEV_KIND_MACVLAN)
+                                        m = MACVLAN(netdev);
+                                else if (netdev->kind == NETDEV_KIND_MACVTAP)
+                                        m = MACVTAP(netdev);
+                                else
+                                        continue;
+
+                                if (m->mode == NETDEV_MACVLAN_MODE_PASSTHRU)
+                                        network->link_local = ADDRESS_FAMILY_NO;
+
+                                /* There won't be a passthru MACVLAN/MACVTAP if there's already one in another mode */
+                                break;
+                        }
+                }
+        }
+
         if (network->ipv6ll_address_gen_mode == IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE)
                 SET_FLAG(network->link_local, ADDRESS_FAMILY_IPV6, false);
 
@@ -400,25 +426,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 +607,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);
@@ -604,12 +627,12 @@ static Network *network_free(Network *network) {
 
         ordered_set_free(network->router_search_domains);
         free(network->router_dns);
-        set_free_free(network->ndisc_deny_listed_router);
-        set_free_free(network->ndisc_allow_listed_router);
-        set_free_free(network->ndisc_deny_listed_prefix);
-        set_free_free(network->ndisc_allow_listed_prefix);
-        set_free_free(network->ndisc_deny_listed_route_prefix);
-        set_free_free(network->ndisc_allow_listed_route_prefix);
+        set_free(network->ndisc_deny_listed_router);
+        set_free(network->ndisc_allow_listed_router);
+        set_free(network->ndisc_deny_listed_prefix);
+        set_free(network->ndisc_allow_listed_prefix);
+        set_free(network->ndisc_deny_listed_route_prefix);
+        set_free(network->ndisc_allow_listed_route_prefix);
 
         free(network->batadv_name);
         free(network->bridge_name);
@@ -640,6 +663,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 +742,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 +753,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 +884,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 +915,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 +930,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 +1212,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..5584d450255851cacdc0e4053dd80a4aea727945 100644 (file)
@@ -793,12 +793,7 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
                 }
         }
 
-        if (nexthop->onlink <= 0 &&
-            in_addr_is_set(nexthop->family, &nexthop->gw) &&
-            !manager_address_is_reachable(link->manager, nexthop->family, &nexthop->gw))
-                return false;
-
-        return true;
+        return gateway_is_ready(link, nexthop->onlink, nexthop->family, &nexthop->gw);
 }
 
 int request_process_nexthop(Request *req) {
@@ -1012,7 +1007,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 +1222,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..a364ca45b79a66a5be7b27daed1d403307600999 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;
 
@@ -750,6 +750,8 @@ static bool prefix_route_address_is_reachable(const Address *a, int family, cons
 
         if (a->family != family)
                 return false;
+        if (!address_is_ready(a))
+                return false;
         if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE))
                 return false;
         if (in_addr_is_set(a->family, &a->in_addr_peer))
@@ -763,37 +765,34 @@ static bool prefix_route_address_is_reachable(const Address *a, int family, cons
                         FAMILY_ADDRESS_SIZE(family) * 8) > 0;
 }
 
-bool manager_address_is_reachable(Manager *manager, int family, const union in_addr_union *address) {
-        Link *link;
+static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) {
+        Route *route;
 
-        assert(manager);
+        assert(link);
+        assert(link->manager);
         assert(IN_SET(family, AF_INET, AF_INET6));
         assert(address);
 
-        HASHMAP_FOREACH(link, manager->links_by_index) {
-                Route *route;
 
-                SET_FOREACH(route, link->routes)
-                        if (route_address_is_reachable(route, family, address))
-                                return true;
-                SET_FOREACH(route, link->routes_foreign)
-                        if (route_address_is_reachable(route, family, address))
-                                return true;
-        }
+        SET_FOREACH(route, link->routes)
+                if (route_address_is_reachable(route, family, address))
+                        return true;
+        SET_FOREACH(route, link->routes_foreign)
+                if (route_address_is_reachable(route, family, address))
+                        return true;
 
         /* If we do not manage foreign routes, then there may exist a prefix route we do not know,
          * which was created on configuring an address. Hence, also check the addresses. */
-        if (!manager->manage_foreign_routes)
-                HASHMAP_FOREACH(link, manager->links_by_index) {
-                        Address *a;
-
-                        SET_FOREACH(a, link->addresses)
-                                if (prefix_route_address_is_reachable(a, family, address))
-                                        return true;
-                        SET_FOREACH(a, link->addresses_foreign)
-                                if (prefix_route_address_is_reachable(a, family, address))
-                                        return true;
-                }
+        if (!link->manager->manage_foreign_routes) {
+                Address *a;
+
+                SET_FOREACH(a, link->addresses)
+                        if (prefix_route_address_is_reachable(a, family, address))
+                                return true;
+                SET_FOREACH(a, link->addresses_foreign)
+                        if (prefix_route_address_is_reachable(a, family, address))
+                                return true;
+        }
 
         return false;
 }
@@ -1689,6 +1688,22 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
         return 0;
 }
 
+bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_union *gw) {
+        assert(link);
+        assert(gw);
+
+        if (onlink > 0)
+                return true;
+
+        if (!in_addr_is_set(family, gw))
+                return true;
+
+        if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6))
+                return true;
+
+        return link_address_is_reachable(link, family, gw);
+}
+
 static int route_is_ready_to_configure(const Route *route, Link *link) {
         MultipathRoute *m;
         NextHop *nh = NULL;
@@ -1732,19 +1747,13 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
                         return r;
         }
 
-        if (route->gateway_onlink <= 0 &&
-            in_addr_is_set(route->gw_family, &route->gw) > 0 &&
-            !manager_address_is_reachable(link->manager, route->gw_family, &route->gw))
+        if (!gateway_is_ready(link, route->gateway_onlink, route->gw_family, &route->gw))
                 return false;
 
         ORDERED_SET_FOREACH(m, route->multipath_routes) {
                 union in_addr_union a = m->gateway.address;
                 Link *l = NULL;
 
-                if (route->gateway_onlink <= 0 &&
-                    !manager_address_is_reachable(link->manager, m->gateway.family, &a))
-                        return false;
-
                 if (m->ifname) {
                         if (link_get_by_name(link->manager, m->ifname, &l) < 0)
                                 return false;
@@ -1756,6 +1765,9 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
                 }
                 if (l && !link_is_ready_to_configure(l, true))
                         return false;
+
+                if (!gateway_is_ready(l ?: link, route->gateway_onlink, m->gateway.family, &a))
+                        return false;
         }
 
         return true;
@@ -1881,7 +1893,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 +2347,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 +2592,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 +2826,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..235a91f08d59c41ce2f966474976f6001781793e 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);
@@ -77,8 +78,8 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li
 int route_remove(const Route *route, Manager *manager, Link *link);
 
 int link_has_route(Link *link, const Route *route);
-bool manager_address_is_reachable(Manager *manager, int family, const union in_addr_union *address);
 int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret);
+bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_union *gw);
 
 int link_drop_routes(Link *link);
 int link_drop_foreign_routes(Link *link);
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 f55e524ae99ef2eac7ecbda0275f4374ac6cea1c..b7b536f3b542530abf311bfdbd5b1c72f305a474 100644 (file)
@@ -8,7 +8,6 @@
 #include "lldp-internal.h"
 #include "macvlan.h"
 #include "ndisc-internal.h"
-#include "netlink-internal.h"
 #include "networkd-link.h"
 #include "networkd-network.h"
 #include "networkd-util.h"
@@ -35,7 +34,6 @@ int main(int argc, char **argv) {
         /* test_table(link_state, LINK_STATE);  — not a reversible mapping */
         test_table(lldp_mode, LLDP_MODE);
         test_table(netdev_kind, NETDEV_KIND);
-        test_table(nl_union_link_info_data, NL_UNION_LINK_INFO_DATA);
         test_table(radv_prefix_delegation, RADV_PREFIX_DELEGATION);
         test_table(lldp_event, SD_LLDP_EVENT);
         test_table(ndisc_event, SD_NDISC_EVENT);
@@ -48,7 +46,6 @@ int main(int argc, char **argv) {
         assert_cc(sizeof(sd_lldp_event_t) == sizeof(int64_t));
         assert_cc(sizeof(sd_ndisc_event_t) == sizeof(int64_t));
         assert_cc(sizeof(sd_dhcp_lease_server_type_t) == sizeof(int64_t));
-        assert_cc(sizeof(sd_genl_family_t) == sizeof(int64_t));
 
         return EXIT_SUCCESS;
 }
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 98b0b0486b16c57fbf9b76a0bec96c5f2281ffd6..f3d06d70d44470a8561be74247d67cbeafbf2581 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 899a74073d6d43eeac32f7e92ab40fdddf80d67e..4044c523e10338d417a304364ce710821d3a62e1 100644 (file)
@@ -482,7 +482,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..664153137dfdf7685c6198f2d4965bde50ccf4ef 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,9 +504,13 @@ static int parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
+        /* If we are talking to the per-user instance PolicyKit isn't going to help */
+        if (arg_user)
+                arg_ask_password = false;
+
         with_trigger = !!arg_path_property || !!arg_socket_property || arg_with_timer;
 
         /* currently, only single trigger (path, socket, timer) unit can be created simultaneously */
@@ -1155,7 +1160,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 +1531,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 +1633,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 1711dd647afdf71450d53cf07148d908340cc33b..b96a082aa358ac1f6cb60ba5f7460228917e4d57 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 1df1bdc3cd0ba30c10ab7a14a48e225a50fe9e54..99a2f62e4a7eb5ac61f67e4da4de608215167abd 100644 (file)
@@ -2565,13 +2565,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;
@@ -2588,17 +2588,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;
@@ -2652,11 +2641,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..af3b917c75cbf23cfda45c1edc460ae648c09a7f 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,53 +932,127 @@ 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 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(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);
+        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;
-        }
-        if (k < 1) {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Invalid %s= value, ignoring: %s", lvalue, rvalue);
+
+        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;
-        }
 
-        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;
-        }
+        ecmd.cmd = ETHTOOL_SCOALESCE;
+        r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
+        if (r < 0)
+                return -errno;
 
         return 0;
 }
@@ -1015,7 +1112,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 +1124,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 +1134,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 +1158,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 +1228,82 @@ 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;
+}
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 cffa3fe96e7644ce63f6a1cec37958f64c523c62..dc4214d162b458dcdf0234b25713ef21e3870084 100644 (file)
@@ -529,21 +529,27 @@ int fork_agent(const char *name, int except[], size_t n_except, pid_t *ret_pid,
                  * stdin around. */
                 fd = open("/dev/tty", O_WRONLY);
                 if (fd < 0) {
-                        log_error_errno(errno, "Failed to open /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
+                        if (errno != -ENXIO) {
+                                log_error_errno(errno, "Failed to open /dev/tty: %m");
+                                _exit(EXIT_FAILURE);
+                        }
 
-                if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
-                        log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
+                        /* If we get ENXIO here we have no controlling TTY even though stdout/stderr are
+                         * connected to a TTY. That's a weird setup, but let's handle it gracefully: let's
+                         * skip the forking of the agents, given the TTY setup is not in order. */
+                } else {
+                        if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
+                                log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+                                _exit(EXIT_FAILURE);
+                        }
 
-                if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
-                        log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
+                        if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
+                                log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+                                _exit(EXIT_FAILURE);
+                        }
 
-                safe_close_above_stdio(fd);
+                        fd = safe_close_above_stdio(fd);
+                }
         }
 
         (void) rlimit_nofile_safe();
index e68978d777f1c9319644e0371b3e10a0f55579d2..d6beaf20d8a839f10554c897232285def08d8be4 100644 (file)
@@ -649,7 +649,7 @@ static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
         msgcnt++;
         assert(msgcnt < NFT_INIT_MSGS);
         /* Set F_EXCL so table add fails if the table already exists. */
-        r = sd_nfnl_nft_message_new_table(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME, NLM_F_EXCL | NLM_F_ACK);
+        r = sd_nfnl_nft_message_new_table(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME);
         if (r < 0)
                 goto out_unref;
 
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..02829929602c6c23ef6844ab6bf654615c52f8e4 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) {
@@ -3650,17 +3648,17 @@ int json_buildv(JsonVariant **ret, va_list ap) {
                 }
 
                 case _JSON_BUILD_ID128: {
-                        sd_id128_t id;
+                        const sd_id128_t *id;
 
                         if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
                                 r = -EINVAL;
                                 goto finish;
                         }
 
-                        id = va_arg(ap, sd_id128_t);
+                        assert_se(id = va_arg(ap, sd_id128_t*));
 
                         if (current->n_suppress == 0) {
-                                r = json_variant_new_id128(&add, id);
+                                r = json_variant_new_id128(&add, *id);
                                 if (r < 0)
                                         goto finish;
                         }
index c679f8f1ab9c655320823aa20d71323a63f16c1a..3912fbd9cca9c7c079d8c0a272cf7197a8ec7b44 100644 (file)
@@ -239,26 +239,26 @@ enum {
         _JSON_BUILD_MAX,
 };
 
-#define JSON_BUILD_STRING(s) _JSON_BUILD_STRING, ({ const char *_x = s; _x; })
-#define JSON_BUILD_INTEGER(i) _JSON_BUILD_INTEGER, ({ intmax_t _x = i; _x; })
-#define JSON_BUILD_UNSIGNED(u) _JSON_BUILD_UNSIGNED, ({ uintmax_t _x = u; _x; })
-#define JSON_BUILD_REAL(d) _JSON_BUILD_REAL, ({ long double _x = d; _x; })
-#define JSON_BUILD_BOOLEAN(b) _JSON_BUILD_BOOLEAN, ({ bool _x = b; _x; })
+#define JSON_BUILD_STRING(s) _JSON_BUILD_STRING, (const char*) { s }
+#define JSON_BUILD_INTEGER(i) _JSON_BUILD_INTEGER, (intmax_t) { i }
+#define JSON_BUILD_UNSIGNED(u) _JSON_BUILD_UNSIGNED, (uintmax_t) { u }
+#define JSON_BUILD_REAL(d) _JSON_BUILD_REAL, (long double) { d }
+#define JSON_BUILD_BOOLEAN(b) _JSON_BUILD_BOOLEAN, (bool) { b }
 #define JSON_BUILD_ARRAY(...) _JSON_BUILD_ARRAY_BEGIN, __VA_ARGS__, _JSON_BUILD_ARRAY_END
 #define JSON_BUILD_EMPTY_ARRAY _JSON_BUILD_ARRAY_BEGIN, _JSON_BUILD_ARRAY_END
 #define JSON_BUILD_OBJECT(...) _JSON_BUILD_OBJECT_BEGIN, __VA_ARGS__, _JSON_BUILD_OBJECT_END
 #define JSON_BUILD_EMPTY_OBJECT _JSON_BUILD_OBJECT_BEGIN, _JSON_BUILD_OBJECT_END
-#define JSON_BUILD_PAIR(n, ...) _JSON_BUILD_PAIR, ({ const char *_x = n; _x; }), __VA_ARGS__
-#define JSON_BUILD_PAIR_CONDITION(c, n, ...) _JSON_BUILD_PAIR_CONDITION, ({ bool _x = c; _x; }), ({ const char *_x = n; _x; }), __VA_ARGS__
+#define JSON_BUILD_PAIR(n, ...) _JSON_BUILD_PAIR, (const char*) { n }, __VA_ARGS__
+#define JSON_BUILD_PAIR_CONDITION(c, n, ...) _JSON_BUILD_PAIR_CONDITION, (bool) { c }, (const char*) { n }, __VA_ARGS__
 #define JSON_BUILD_NULL _JSON_BUILD_NULL
-#define JSON_BUILD_VARIANT(v) _JSON_BUILD_VARIANT, ({ JsonVariant *_x = v; _x; })
-#define JSON_BUILD_VARIANT_ARRAY(v, n) _JSON_BUILD_VARIANT_ARRAY, ({ JsonVariant **_x = v; _x; }), ({ size_t _y = n; _y; })
-#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, ({ const char *_x = l; _x; })
-#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, ({ char **_x = l; _x; })
-#define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, ({ const void *_x = p; _x; }), ({ size_t _y = n; _y; })
-#define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, ({ const void *_x = p; _x; }), ({ size_t _y = n; _y; })
-#define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, ({ sd_id128_t _x = id; _x; })
-#define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, ({ const void *_x = v; _x; }), ({ size_t _y = n; _y; })
+#define JSON_BUILD_VARIANT(v) _JSON_BUILD_VARIANT, (JsonVariant*) { v }
+#define JSON_BUILD_VARIANT_ARRAY(v, n) _JSON_BUILD_VARIANT_ARRAY, (JsonVariant **) { v }, (size_t) { n }
+#define JSON_BUILD_LITERAL(l) _JSON_BUILD_LITERAL, (const char*) { l }
+#define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, (char**) { l }
+#define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, (const void*) { p }, (size_t) { n }
+#define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n }
+#define JSON_BUILD_ID128(id) _JSON_BUILD_ID128, (const sd_id128_t*) { &(id) }
+#define JSON_BUILD_BYTE_ARRAY(v, n) _JSON_BUILD_BYTE_ARRAY, (const void*) { v }, (size_t) { n }
 
 int json_build(JsonVariant **ret, ...);
 int json_buildv(JsonVariant **ret, va_list ap);
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 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 b05e1aa0df184c07ef375120b176d98568b03710..5891208076414192fcd26343078edde029e6bf56 100644 (file)
@@ -1,17 +1,18 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "log.h"
+#include "string-util.h"
 #include "wifi-util.h"
 
 int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftype, char **ssid) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
-        sd_genl_family_t family;
+        const char *family;
         int r;
 
         assert(genl);
         assert(ifindex > 0);
 
-        r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_INTERFACE, &m);
+        r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &m);
         if (r < 0)
                 return log_debug_errno(r, "Failed to create generic netlink message: %m");
 
@@ -38,11 +39,11 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp
         if (r < 0)
                 return log_debug_errno(r, "Failed to get information about wifi interface %d: %m", ifindex);
 
-        r = sd_genl_message_get_family(genl, reply, &family);
+        r = sd_genl_message_get_family_name(genl, reply, &family);
         if (r < 0)
                 return log_debug_errno(r, "Failed to determine genl family: %m");
-        if (family != SD_GENL_NL80211) {
-                log_debug("Received message of unexpected genl family %" PRIi64 ", ignoring.", family);
+        if (!streq(family, NL80211_GENL_NAME)) {
+                log_debug("Received message of unexpected genl family '%s', ignoring.", family);
                 goto nodata;
         }
 
@@ -75,14 +76,14 @@ nodata:
 
 int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
-        sd_genl_family_t family;
+        const char *family;
         int r;
 
         assert(genl);
         assert(ifindex > 0);
         assert(bssid);
 
-        r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_STATION, &m);
+        r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &m);
         if (r < 0)
                 return log_debug_errno(r, "Failed to create generic netlink message: %m");
 
@@ -106,11 +107,11 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
         if (r < 0)
                 return log_debug_errno(r, "Failed to get information about wifi station: %m");
 
-        r = sd_genl_message_get_family(genl, reply, &family);
+        r = sd_genl_message_get_family_name(genl, reply, &family);
         if (r < 0)
                 return log_debug_errno(r, "Failed to determine genl family: %m");
-        if (family != SD_GENL_NL80211) {
-                log_debug("Received message of unexpected genl family %" PRIi64 ", ignoring.", family);
+        if (!streq(family, NL80211_GENL_NAME)) {
+                log_debug("Received message of unexpected genl family '%s', ignoring.", family);
                 goto nodata;
         }
 
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 0d1f1c5218a5a5242e75d77fa3598eba42b32307..f15d4dd61da32edfe541a8377b4607ea5cbd0471 100644 (file)
@@ -567,7 +567,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
                         break;
                 }
                 default:
-                        assert_not_reached("Unsupported image type");
+                        assert_not_reached();
                 }
 
                 r = validate_version(
@@ -958,7 +958,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..e2a8aef11470c8ed652e279bc52c1fd1074a2845 100644 (file)
@@ -922,9 +922,14 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         return -EINVAL;
 
                 default:
-                        assert_not_reached("Unhandled option");
+                        assert_not_reached();
                 }
 
+        /* If we are in --user mode, there's no point in talking to PolicyKit or the infra to query system
+         * passwords */
+        if (arg_scope != UNIT_FILE_SYSTEM)
+                arg_ask_password = false;
+
         if (arg_transport == BUS_TRANSPORT_REMOTE && arg_scope != UNIT_FILE_SYSTEM)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Cannot access user instance remotely.");
@@ -1184,7 +1189,7 @@ static int run(int argc, char *argv[]) {
 
         case _ACTION_INVALID:
         default:
-                assert_not_reached("Unknown action");
+                assert_not_reached();
         }
 
 finish:
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 b7e1a90958465175a4afa5200396ece599f6e9bc..5965780fbb725d7982303a66fcf036b5b334f961 100644 (file)
 _SD_BEGIN_DECLARATIONS;
 
 typedef struct sd_netlink sd_netlink;
-typedef struct sd_genl_socket sd_genl_socket;
 typedef struct sd_netlink_message sd_netlink_message;
 typedef struct sd_netlink_slot sd_netlink_slot;
 
-typedef enum sd_genl_family_t {
-        SD_GENL_ERROR,
-        SD_GENL_DONE,
-        SD_GENL_ID_CTRL,
-        SD_GENL_WIREGUARD,
-        SD_GENL_FOU,
-        SD_GENL_L2TP,
-        SD_GENL_MACSEC,
-        SD_GENL_NL80211,
-        SD_GENL_BATADV,
-        _SD_GENL_FAMILY_MAX,
-        _SD_GENL_FAMILY_INVALID = -EINVAL,
-        _SD_ENUM_FORCE_S64(GENL_FAMILY)
-} sd_genl_family_t;
-
 /* callback */
-
 typedef int (*sd_netlink_message_handler_t)(sd_netlink *nl, sd_netlink_message *m, void *userdata);
 typedef _sd_destroy_t sd_netlink_destroy_t;
 
 /* bus */
-int sd_netlink_new_from_netlink(sd_netlink **nl, int fd);
+int sd_netlink_new_from_fd(sd_netlink **nl, int fd);
 int sd_netlink_open(sd_netlink **nl);
 int sd_netlink_open_fd(sd_netlink **nl, int fd);
 int sd_netlink_inc_rcvbuf(sd_netlink *nl, const size_t size);
@@ -86,6 +69,7 @@ int sd_netlink_add_match(sd_netlink *nl, sd_netlink_slot **ret_slot, uint16_t ma
 int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority);
 int sd_netlink_detach_event(sd_netlink *nl);
 
+/* message */
 int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data);
 int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned short type, char * const *data);
 int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type);
@@ -129,7 +113,7 @@ int sd_netlink_message_exit_container(sd_netlink_message *m);
 int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type);
 int sd_netlink_message_cancel_array(sd_netlink_message *m);
 
-int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl);
+int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl);
 
 sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m);
 
@@ -182,12 +166,12 @@ int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char
 int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len);
 int sd_rtnl_message_route_get_type(sd_netlink_message *m, unsigned char *type);
 
-int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nhmsg_type, int nh_family, unsigned char nh_protocol);
+int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int nh_family, unsigned char nh_protocol);
 int sd_rtnl_message_nexthop_set_flags(sd_netlink_message *m, uint8_t flags);
 int sd_rtnl_message_nexthop_get_family(sd_netlink_message *m, uint8_t *family);
 int sd_rtnl_message_nexthop_get_protocol(sd_netlink_message *m, uint8_t *protocol);
 
-int sd_rtnl_message_new_neigh(sd_netlink *nl, sd_netlink_message **ret, uint16_t msg_type, int index, int nda_family);
+int sd_rtnl_message_new_neigh(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type, int index, int nda_family);
 int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags);
 int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state);
 int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family);
@@ -224,13 +208,13 @@ int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle);
 int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex);
 
 /* nfnl */
-int sd_nfnl_socket_open(sd_netlink **nl);
+int sd_nfnl_socket_open(sd_netlink **ret);
 int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret);
 int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret);
 int sd_nfnl_nft_message_del_table(sd_netlink *nfnl, sd_netlink_message **ret,
                                   int family, const char *table);
 int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret,
-                                  int family, const char *table, uint16_t nl_flags);
+                                  int family, const char *table);
 int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret,
                                       int family, const char *table, const char *chain,
                                       const char *type, uint8_t hook, int prio);
@@ -250,13 +234,19 @@ int sd_nfnl_nft_message_add_setelem(sd_netlink_message *m,
 int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m);
 
 /* genl */
-int sd_genl_socket_open(sd_netlink **nl);
-int sd_genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint8_t cmd, sd_netlink_message **ret);
-int sd_genl_message_get_family(sd_netlink *nl, sd_netlink_message *m, sd_genl_family_t *ret);
+int sd_genl_socket_open(sd_netlink **ret);
+int sd_genl_message_new(sd_netlink *genl, const char *family_name, uint8_t cmd, sd_netlink_message **ret);
+int sd_genl_message_get_family_name(sd_netlink *genl, sd_netlink_message *m, const char **ret);
+int sd_genl_message_get_command(sd_netlink *genl, sd_netlink_message *m, uint8_t *ret);
+int sd_genl_add_match(sd_netlink *nl, sd_netlink_slot **ret_slot, const char *family_name,
+                      const char *multicast_group_name, uint8_t command,
+                      sd_netlink_message_handler_t callback,
+                      sd_netlink_destroy_t destroy_callback,
+                      void *userdata, const char *description);
 
 /* slot */
-sd_netlink_slot *sd_netlink_slot_ref(sd_netlink_slot *nl);
-sd_netlink_slot *sd_netlink_slot_unref(sd_netlink_slot *nl);
+sd_netlink_slot *sd_netlink_slot_ref(sd_netlink_slot *slot);
+sd_netlink_slot *sd_netlink_slot_unref(sd_netlink_slot *slot);
 
 sd_netlink *sd_netlink_slot_get_netlink(sd_netlink_slot *slot);
 void *sd_netlink_slot_get_userdata(sd_netlink_slot *slot);
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 07625ad38c17755d837c6d6331ad1f19afb8aa3e..46b497672fb8e746324557f824bd2269dfa9ad73 100644 (file)
@@ -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..092c78f2b9d250d4a5566fa482493fca69b69ea5 100644 (file)
@@ -4,9 +4,13 @@
 #include <sys/prctl.h>
 #include <sys/types.h>
 
+#include "sd-event.h"
+
 #include "capability-util.h"
 #include "cpu-set-util.h"
+#include "dropin.h"
 #include "errno-list.h"
+#include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
 #include "macro.h"
 #include "missing_prctl.h"
 #include "mkdir.h"
 #include "path-util.h"
+#include "process-util.h"
 #include "rm-rf.h"
 #if HAVE_SECCOMP
 #include "seccomp-util.h"
 #endif
 #include "service.h"
+#include "signal-util.h"
+#include "static-destruct.h"
 #include "stat-util.h"
 #include "tests.h"
 #include "unit.h"
 #include "util.h"
 #include "virt.h"
 
+static char *user_runtime_unit_dir = NULL;
 static bool can_unshare;
 
+STATIC_DESTRUCTOR_REGISTER(user_runtime_unit_dir, freep);
+
 typedef void (*test_function_t)(Manager *m);
 
 static int cld_dumped_to_killed(int code) {
@@ -79,16 +89,15 @@ static void check_main_result(const char *file, unsigned line, const char *func,
         exec_status_dump(&service->main_exec_status, stdout, "\t");
 
         if (cld_dumped_to_killed(service->main_exec_status.code) != cld_dumped_to_killed(code_expected)) {
-                log_error("%s:%u:%s %s: exit code %d, expected %d",
-                          file, line, func,
-                          unit->id,
+                log_error("%s:%u:%s %s: can_unshare=%s: exit code %d, expected %d",
+                          file, line, func, unit->id, yes_no(can_unshare),
                           service->main_exec_status.code, code_expected);
                 abort();
         }
 
         if (service->main_exec_status.status != status_expected) {
-                log_error("%s:%u:%s: %s: exit status %d, expected %d",
-                          file, line, func, unit->id,
+                log_error("%s:%u:%s: %s: can_unshare=%s: exit status %d, expected %d",
+                          file, line, func, unit->id, yes_no(can_unshare),
                           service->main_exec_status.status, status_expected);
                 abort();
         }
@@ -106,9 +115,8 @@ static void check_service_result(const char *file, unsigned line, const char *fu
         service = SERVICE(unit);
 
         if (service->result != result_expected) {
-                log_error("%s:%u:%s: %s: service end result %s, expected %s",
-                          file, line, func,
-                          unit->id,
+                log_error("%s:%u:%s: %s: can_unshare=%s: service end result %s, expected %s",
+                          file, line, func, unit->id, yes_no(can_unshare),
                           service_result_to_string(service->result),
                           service_result_to_string(result_expected));
                 abort();
@@ -408,6 +416,190 @@ static void test_exec_inaccessiblepaths(Manager *m) {
         test(m, "exec-inaccessiblepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
 }
 
+static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        char **result = userdata;
+        char buf[4096];
+        ssize_t l;
+
+        assert(s);
+        assert(fd >= 0);
+
+        l = read(fd, buf, sizeof(buf) - 1);
+        if (l < 0) {
+                if (errno == EAGAIN)
+                        goto reenable;
+
+                return 0;
+        }
+        if (l == 0)
+                return 0;
+
+        buf[l] = '\0';
+        if (result)
+                assert_se(strextend(result, buf));
+        else
+                log_error("ldd: %s", buf);
+
+reenable:
+        /* Re-enable the event source if we did not encounter EOF */
+        assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0);
+        return 0;
+}
+
+static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+        pid_t *pid = userdata;
+
+        assert(pid);
+
+        (void) kill(*pid, SIGKILL);
+
+        return 1;
+}
+
+static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
+        int ret = -EIO;
+
+        assert(si);
+
+        if (si->si_code == CLD_EXITED)
+                ret = si->si_status;
+
+        sd_event_exit(sd_event_source_get_event(s), ret);
+        return 1;
+}
+
+static int find_libraries(const char *exec, char ***ret) {
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *sigchld_source = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *stdout_source = NULL;
+        _cleanup_(sd_event_source_unrefp) sd_event_source *stderr_source = NULL;
+        _cleanup_close_pair_ int outpipe[2] = {-1, -1}, errpipe[2] = {-1, -1};
+        _cleanup_strv_free_ char **libraries = NULL;
+        _cleanup_free_ char *result = NULL;
+        pid_t pid;
+        int r;
+
+        assert(exec);
+        assert(ret);
+
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
+
+        assert_se(pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) == 0);
+        assert_se(pipe2(errpipe, O_NONBLOCK|O_CLOEXEC) == 0);
+
+        r = safe_fork("(spawn-ldd)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+        assert_se(r >= 0);
+        if (r == 0) {
+                if (rearrange_stdio(-1, outpipe[1], errpipe[1]) < 0)
+                        _exit(EXIT_FAILURE);
+
+                (void) close_all_fds(NULL, 0);
+
+                execlp("ldd", "ldd", exec, NULL);
+                _exit(EXIT_FAILURE);
+        }
+
+        outpipe[1] = safe_close(outpipe[1]);
+        errpipe[1] = safe_close(errpipe[1]);
+
+        assert_se(sd_event_new(&e) >= 0);
+
+        assert_se(sd_event_add_time_relative(e, NULL, CLOCK_MONOTONIC,
+                                             10 * USEC_PER_SEC, USEC_PER_SEC, on_spawn_timeout, &pid) >= 0);
+        assert_se(sd_event_add_io(e, &stdout_source, outpipe[0], EPOLLIN, on_spawn_io, &result) >= 0);
+        assert_se(sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT) >= 0);
+        assert_se(sd_event_add_io(e, &stderr_source, errpipe[0], EPOLLIN, on_spawn_io, NULL) >= 0);
+        assert_se(sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT) >= 0);
+        assert_se(sd_event_add_child(e, &sigchld_source, pid, WEXITED, on_spawn_sigchld, NULL) >= 0);
+        /* SIGCHLD should be processed after IO is complete */
+        assert_se(sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1) >= 0);
+
+        assert_se(sd_event_loop(e) >= 0);
+
+        _cleanup_strv_free_ char **v = NULL;
+        assert_se(strv_split_newlines_full(&v, result, 0) >= 0);
+
+        char **q;
+        STRV_FOREACH(q, v) {
+                _cleanup_free_ char *word = NULL;
+                const char *p = *q;
+
+                r = extract_first_word(&p, &word, NULL, 0);
+                assert_se(r >= 0);
+                if (r == 0)
+                        continue;
+
+                if (path_is_absolute(word)) {
+                        assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
+                        continue;
+                }
+
+                word = mfree(word);
+                r = extract_first_word(&p, &word, NULL, 0);
+                assert_se(r >= 0);
+                if (r == 0)
+                        continue;
+
+                if (!streq_ptr(word, "=>"))
+                        continue;
+
+                word = mfree(word);
+                r = extract_first_word(&p, &word, NULL, 0);
+                assert_se(r >= 0);
+                if (r == 0)
+                        continue;
+
+                if (path_is_absolute(word)) {
+                        assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
+                        continue;
+                }
+        }
+
+        *ret = TAKE_PTR(libraries);
+        return 0;
+}
+
+static void test_exec_mount_apivfs(Manager *m) {
+        _cleanup_free_ char *fullpath_touch = NULL, *fullpath_test = NULL, *data = NULL;
+        _cleanup_strv_free_ char **libraries = NULL, **libraries_test = NULL;
+        int r;
+
+        assert(user_runtime_unit_dir);
+
+        r = find_executable("touch", &fullpath_touch);
+        if (r < 0) {
+                log_notice_errno(r, "Skipping %s, could not find 'touch' command: %m", __func__);
+                return;
+        }
+        r = find_executable("test", &fullpath_test);
+        if (r < 0) {
+                log_notice_errno(r, "Skipping %s, could not find 'test' command: %m", __func__);
+                return;
+        }
+
+        assert_se(find_libraries(fullpath_touch, &libraries) >= 0);
+        assert_se(find_libraries(fullpath_test, &libraries_test) >= 0);
+        assert_se(strv_extend_strv(&libraries, libraries_test, true) >= 0);
+
+        assert_se(strextend(&data, "[Service]\n"));
+        assert_se(strextend(&data, "ExecStart=", fullpath_touch, " /aaa\n"));
+        assert_se(strextend(&data, "ExecStart=", fullpath_test, " -f /aaa\n"));
+        assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n"));
+        assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n"));
+
+        char **p;
+        STRV_FOREACH(p, libraries)
+                assert_se(strextend(&data, "BindReadOnlyPaths=", *p, "\n"));
+
+        assert_se(write_drop_in(user_runtime_unit_dir, "exec-mount-apivfs-no.service", 10, "bind-mount", data) >= 0);
+
+        assert_se(mkdir_p("/tmp/test-exec-mount-apivfs-no/root", 0755) >= 0);
+
+        test(m, "exec-mount-apivfs-no.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+
+        (void) rm_rf("/tmp/test-exec-mount-apivfs-no/root", REMOVE_ROOT|REMOVE_PHYSICAL);
+}
+
 static void test_exec_noexecpaths(Manager *m) {
 
         test(m, "exec-noexecpaths-simple.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
@@ -844,7 +1036,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))
@@ -871,6 +1063,7 @@ int main(int argc, char *argv[]) {
                 entry(test_exec_ignoresigpipe),
                 entry(test_exec_inaccessiblepaths),
                 entry(test_exec_ioschedulingclass),
+                entry(test_exec_mount_apivfs),
                 entry(test_exec_noexecpaths),
                 entry(test_exec_oomscoreadjust),
                 entry(test_exec_passenvironment),
@@ -931,10 +1124,12 @@ int main(int argc, char *argv[]) {
         if (r == -ENOMEDIUM)
                 return log_tests_skipped("cgroupfs not available");
 
-        _cleanup_free_ char *unit_dir = NULL;
+        _cleanup_free_ char *unit_dir = NULL, *unit_paths = NULL;
         assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
-        assert_se(set_unit_path(unit_dir) >= 0);
         assert_se(runtime_dir = setup_fake_runtime_dir());
+        assert_se(user_runtime_unit_dir = path_join(runtime_dir, "systemd/user"));
+        assert_se(unit_paths = strjoin(unit_dir, ":", user_runtime_unit_dir));
+        assert_se(set_unit_path(unit_paths) >= 0);
 
         /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
          * cases, otherwise (and if they are present in the environment),
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..d7060aaa45c5c98a328a352d44bfd614465d954d 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_TIMESPEC) control;
         union sockaddr_union server_addr;
         struct msghdr msghdr = {
                 .msg_iov = &iov,
@@ -467,6 +471,8 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
 
                 switch (cmsg->cmsg_type) {
                 case SCM_TIMESTAMPNS:
+                        assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct timespec)));
+
                         recv_time = (struct timespec *) CMSG_DATA(cmsg);
                         break;
                 }
@@ -591,7 +597,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 +947,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 +1111,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 +1140,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 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..4963ba2fae81485e6995e533f2af7a2cbd3a166c 100644 (file)
@@ -142,6 +142,8 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
                 .tx_flow_control = -1,
                 .autoneg_flow_control = -1,
                 .txqueuelen = UINT32_MAX,
+                .coalesce.use_adaptive_rx_coalesce = -1,
+                .coalesce.use_adaptive_tx_coalesce = -1,
         };
 
         for (i = 0; i < ELEMENTSOF(link->features); i++)
@@ -353,6 +355,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 +507,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 +575,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 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
diff --git a/test/TEST-63-ISSUE-17433/Makefile b/test/TEST-63-ISSUE-17433/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-63-ISSUE-17433/test.sh b/test/TEST-63-ISSUE-17433/test.sh
new file mode 100755 (executable)
index 0000000..c595a9f
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+set -e
+
+TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/17433"
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+do_test "$@"
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 a21230a4a8875d660cfc0b673c48aa4be1794f38..b8335fb50f002dd89fd19b0dc7cfee318fc692f4 100644 (file)
@@ -33,6 +33,8 @@ if install_tests
                        install_dir : testdata_dir)
         install_subdir('testsuite-52.units',
                        install_dir : testdata_dir)
+        install_subdir('testsuite-63.units',
+                       install_dir : testdata_dir)
 
         testsuite08_dir = testdata_dir + '/testsuite-08.units'
         install_data('testsuite-08.units/-.mount',
diff --git a/test/test-execute/exec-mount-apivfs-no.service b/test/test-execute/exec-mount-apivfs-no.service
new file mode 100644 (file)
index 0000000..0cf1f33
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=Test for find_executable() with MountAPIVFS=no
+
+[Service]
+Type=oneshot
+
+MountAPIVFS=false
+PrivateDevices=false
+PrivateMounts=true
+PrivateTmp=false
+PrivateUsers=false
+ProtectControlGroups=false
+ProtectKernelModules=false
+ProtectKernelTunables=false
+RootDirectory=/tmp/test-exec-mount-apivfs-no/root
index d5e6f4a5b134b8e1ba0d10470ef0ee3f57438872..a2b92aeba8d46b38227c899cb7ee7a4314bdb3a2 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
@@ -151,6 +148,7 @@ BASICTOOLS=(
     head
     ionice
     ip
+    ldd
     ln
     loadkeys
     login
@@ -229,7 +227,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 +346,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 +625,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 +638,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 +666,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 +937,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 +1000,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 +1228,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 +1239,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 +1430,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 +1601,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 +1615,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 +1740,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 +2407,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 +2463,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
index 6f54e3f0d5027c78739017187b7a0d94e538189a..3e87bd40a93d25678259348867de17ed55de1e98 100644 (file)
@@ -38,6 +38,10 @@ Id=7
 Family=ipv6
 Blackhole=yes
 
+[NextHop]
+Id=8
+Gateway=fe80::222:4dff:ff:ff:ff:ff
+
 [NextHop]
 Gateway=192.168.5.2
 
index 09aae8067ad9f7ee7047a0e0deda7269b21e06b9..94b2e8730fd973842cebe8fe487bfb2da6d5c26c 100644 (file)
@@ -17,6 +17,10 @@ Destination=2001:1234:5:9fff:ff:ff:ff:ff/128
 [Route]
 Gateway=2001:1234:5:8fff:ff:ff:ff:ff
 
+[Route]
+Destination=2001:1234:5:afff:ff:ff:ff:ff/128
+Gateway=fe80::222:4dff:ff:ff:ff:ff
+
 [Route]
 Destination=149.10.124.64
 Scope=link
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
diff --git a/test/test-network/conf/dhcp-client-allow-list.network b/test/test-network/conf/dhcp-client-allow-list.network
new file mode 100644 (file)
index 0000000..8228369
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+
+[DHCPv4]
+# DenyList= will be ignored
+AllowList=192.168.5.0/24 192.168.6.0/24
+DenyList=192.168.5.0/24
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..b046d9f992e203a88f8c9c6fbacb71e2fdc88939 100644 (file)
@@ -6,8 +6,8 @@ DHCP=no
 IPv6AcceptRA=yes
 
 [IPv6AcceptRA]
-# PrefixDenyList= and RouteDenyList= will be ignored.
-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::
+# PrefixDenyList= will be ignored.
+PrefixAllowList=2001:db8:0:1:: 2001:db8:0:1:: 2001:db8:0:1::/64
+PrefixDenyList=2001:db8:0:1::/64 2001:db8:0:1:: 2001:db8:0:3::/64
+RouteDenyList=2001:db1:fff::/64 2001:db1:fff:: 2001:db2:fff::/64
+UseDomains=yes
index cfb03f50c47b2353ef7536bae512c5978ed7a646..ae74c6e2c6f5145199c811b0c2df61e4d82fc328 100644 (file)
@@ -5,6 +5,9 @@ Name=veth99
 DHCP=no
 IPv6SendRA=yes
 
+[IPv6SendRA]
+UplinkInterface=dummy98
+
 [IPv6Prefix]
 Prefix=2001:db8:0:1::/64
 
@@ -12,6 +15,9 @@ Prefix=2001:db8:0:1::/64
 Prefix=2001:db8:0:2::/64
 Assign=yes
 
+[IPv6Prefix]
+Prefix=2001:db8:0:3::/64
+
 [IPv6RoutePrefix]
 Route=2001:db0:fff::/64
 LifetimeSec=1000
@@ -19,3 +25,7 @@ LifetimeSec=1000
 [IPv6RoutePrefix]
 Route=2001:db1:fff::/64
 LifetimeSec=1000
+
+[IPv6RoutePrefix]
+Route=2001:db2:fff::/64
+LifetimeSec=1000
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..02f889ea5ef6cbd8583861f54006bdabebe03709 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',
@@ -1131,7 +1155,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
                     f.write('[MACVTAP]\nMode=' + mode)
                 start_networkd()
 
-                self.wait_online(['macvtap99:degraded', 'test1:degraded'])
+                self.wait_online(['macvtap99:degraded',
+                                  'test1:carrier' if mode == 'passthru' else 'test1:degraded'])
 
                 output = check_output('ip -d link show macvtap99')
                 print(output)
@@ -1148,7 +1173,28 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
                     f.write('[MACVLAN]\nMode=' + mode)
                 start_networkd()
 
-                self.wait_online(['macvlan99:degraded', 'test1:degraded'])
+                self.wait_online(['macvlan99:degraded',
+                                  'test1:carrier' if mode == 'passthru' else '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 + ' ')
+
+                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:carrier' if mode == 'passthru' else 'test1:degraded'])
 
                 output = check_output('ip -d link show test1')
                 print(output)
@@ -1635,36 +1681,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',
@@ -2287,6 +2340,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         print(output)
         self.assertIn('2001:1234:5:8fff:ff:ff:ff:ff proto static', output)
         self.assertIn('2001:1234:5:8f63::1 proto kernel', output)
+        self.assertIn('2001:1234:5:afff:ff:ff:ff:ff via fe80:0:222:4dff:ff:ff:ff:ff proto static', output)
 
         print('### ip -6 route show default')
         output = check_output('ip -6 route show default')
@@ -2935,6 +2989,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
             self.assertIn('id 3 dev veth99', output)
             self.assertIn('id 4 dev veth99', output)
             self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
+            self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
             self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
 
             output = check_output('ip nexthop list dev dummy98')
@@ -3415,21 +3470,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 +3527,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 +3724,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 +3887,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 = [
@@ -3852,6 +3934,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         '25-veth.netdev',
         '25-vrf.netdev',
         '25-vrf.network',
+        'dhcp-client-allow-list.network',
         'dhcp-client-anonymize.network',
         'dhcp-client-decline.network',
         'dhcp-client-gateway-ipv4.network',
@@ -4073,6 +4156,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')
@@ -4684,15 +4768,30 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic veth99')
 
+    def test_dhcp_client_allow_list(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-decline.network', 'dhcp-client-allow-list.network')
+
+        start_networkd()
+        self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
+        output = check_output('ip -4 address show dev veth99 scope global dynamic')
+        print(output)
+        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,33 +4804,47 @@ 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)
         self.assertIn('inet6 2001:db8:0:1:', output)
         self.assertNotIn('inet6 2001:db8:0:2:', output)
+        self.assertNotIn('inet6 2001:db8:0:3:', output)
 
         output = check_output('ip -6 route show dev veth-peer')
         print(output)
         self.assertIn('2001:db8:0:1::/64 proto ra', output)
         self.assertNotIn('2001:db8:0:2::/64 proto ra', output)
+        self.assertNotIn('2001:db8:0:3::/64 proto ra', output)
         self.assertIn('2001:db0:fff::/64 via ', output)
         self.assertNotIn('2001:db1:fff::/64 via ', output)
+        self.assertNotIn('2001:db2:fff::/64 via ', output)
 
         output = check_output('ip address show dev veth99')
         print(output)
         self.assertNotIn('inet6 2001:db8:0:1:', output)
         self.assertIn('inet6 2001:db8:0:2:', output)
+        self.assertNotIn('inet6 2001:db8:0:3:', 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 +4863,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 d0be786b019a1948d9367d81ebe0b362f551adc0..2fb476b986d82fa7b154083116736825877890c4 100644 (file)
@@ -1,6 +1,9 @@
 [Unit]
 Requires=test10.socket
 ConditionPathExistsGlob=/tmp/nonexistent
+# Make sure we hit the socket trigger limit in the test and not the service start limit.
+StartLimitInterval=1000
+StartLimitBurst=1000
 
 [Service]
 ExecStart=true
diff --git a/test/testsuite-63.units/test63.path b/test/testsuite-63.units/test63.path
new file mode 100644 (file)
index 0000000..a6573bd
--- /dev/null
@@ -0,0 +1,2 @@
+[Path]
+PathExists=/tmp/test63
diff --git a/test/testsuite-63.units/test63.service b/test/testsuite-63.units/test63.service
new file mode 100644 (file)
index 0000000..c838018
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+ConditionPathExists=!/tmp/nonexistent
+
+[Service]
+ExecStart=true
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
diff --git a/test/units/testsuite-63.service b/test/units/testsuite-63.service
new file mode 100644 (file)
index 0000000..0412272
--- /dev/null
@@ -0,0 +1,16 @@
+[Unit]
+Description=TEST-63-ISSUE-17433
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+Type=oneshot
+ExecStart=rm -f /tmp/nonexistent
+ExecStart=systemctl start test63.path
+ExecStart=touch /tmp/test63
+# Make sure systemd has sufficient time to hit the start limit for test63.service.
+ExecStart=sleep 2
+ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = failed'
+ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = start-limit-hit'
+ExecStart=sh -x -c 'test "$(systemctl show test63.path -P ActiveState)" = failed'
+ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = unit-start-limit-hit'
+ExecStart=sh -x -c 'echo OK >/testok'
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 64da9efaafbf1cb8dd906b26392001dd67979186..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',                        '',
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