]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #24054 from keszybz/initrd-no-reload
authorFrantisek Sumsal <frantisek@sumsal.cz>
Thu, 18 Aug 2022 13:15:14 +0000 (13:15 +0000)
committerGitHub <noreply@github.com>
Thu, 18 Aug 2022 13:15:14 +0000 (13:15 +0000)
Don't do daemon-reload in the initrd

461 files changed:
.github/ISSUE_TEMPLATE/bug_report.yml
.github/ISSUE_TEMPLATE/feature_request.yml
.github/advanced-issue-labeler.yml
.github/pull_request_template.md.disabled [new file with mode: 0644]
.github/workflows/codeql-analysis.yml
.github/workflows/coverity.yml
.github/workflows/differential-shellcheck.yml [new file with mode: 0644]
.github/workflows/issue_labeler.yml
.github/workflows/linter.yml
.github/workflows/mkosi.yml
.github/workflows/requirements.txt
.github/workflows/run_mkosi.sh
.gitignore
.semaphore/semaphore-runner.sh
NEWS
README
TODO
docs/CREDENTIALS.md
docs/HACKING.md
docs/RELEASE.md
docs/TESTING_WITH_SANITIZERS.md
hwdb.d/60-keyboard.hwdb
hwdb.d/60-sensor.hwdb
hwdb.d/70-analyzers.hwdb
man/crypttab.xml
man/journalctl.xml
man/kernel-install.xml
man/loader.conf.xml
man/machinectl.xml
man/org.freedesktop.machine1.xml
man/org.freedesktop.oom1.xml
man/org.freedesktop.systemd1.xml
man/os-release.xml
man/rules/meson.build
man/system-or-user-ns.xml
man/systemctl.xml
man/systemd-boot.xml
man/systemd-creds.xml
man/systemd-cryptenroll.xml
man/systemd-dissect.xml
man/systemd-integritysetup-generator.xml
man/systemd-measure.xml [new file with mode: 0644]
man/systemd-repart.xml
man/systemd-resolved.service.xml
man/systemd-sleep.conf.xml
man/systemd-stub.xml
man/systemd-sysctl.service.xml
man/systemd-sysext.xml
man/systemd-system.conf.xml
man/systemd-sysusers.xml
man/systemd-tmpfiles.xml
man/systemd.exec.xml
man/systemd.link.xml
man/systemd.mount.xml
man/systemd.net-naming-scheme.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.resource-control.xml
man/systemd.unit.xml
man/systemd.xml
man/sysupdate.d.xml
man/tmpfiles.d.xml
man/udevadm.xml
meson.build
meson_options.txt
mkosi.build
mkosi.default.d/10-systemd.conf
mkosi.default.d/arch/10-mkosi.arch
mkosi.default.d/centos_epel/10-mkosi.centos_epel
mkosi.default.d/debian/10-mkosi.debian
mkosi.default.d/fedora/10-mkosi.fedora
mkosi.default.d/opensuse/10-mkosi.opensuse
mkosi.default.d/ubuntu/10-mkosi.ubuntu
mkosi.postinst
po/LINGUAS
po/hu.po
po/ko.po
po/meson.build
rules.d/60-persistent-storage.rules
rules.d/70-uaccess.rules.in
shell-completion/bash/systemctl.in
shell-completion/bash/systemd-cgtop
shell-completion/zsh/_systemctl.in
src/analyze/analyze-security.c
src/backlight/backlight.c
src/basic/alloc-util.h
src/basic/cgroup-util.h
src/basic/ether-addr-util.c
src/basic/ether-addr-util.h
src/basic/fd-util.c
src/basic/fs-util.c
src/basic/fs-util.h
src/basic/gcrypt-util.c
src/basic/hash-funcs.c
src/basic/hash-funcs.h
src/basic/hashmap.h
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/basic/macro.h
src/basic/math-util.h [new file with mode: 0644]
src/basic/meson.build
src/basic/missing_fs.h
src/basic/mountpoint-util.c
src/basic/path-util.c
src/basic/path-util.h
src/basic/process-util.c
src/basic/random-util.c
src/basic/set.h
src/basic/sigbus.c
src/basic/strv.h
src/basic/unit-def.c
src/basic/unit-def.h
src/basic/unit-file.c
src/basic/virt.c
src/boot/bootctl.c
src/boot/efi/boot.c
src/boot/efi/cpio.c
src/boot/efi/cpio.h
src/boot/efi/linux.c
src/boot/efi/measure.c
src/boot/efi/measure.h
src/boot/efi/meson.build
src/boot/efi/missing_efi.h
src/boot/efi/secure-boot.c
src/boot/efi/secure-boot.h
src/boot/efi/stub.c
src/boot/efi/ticks.c
src/boot/efi/util.c
src/boot/efi/util.h
src/boot/measure.c [new file with mode: 0644]
src/cgroups-agent/cgroups-agent.c
src/core/bpf/meson.build
src/core/bpf/restrict_ifaces/restrict-ifaces.bpf.c
src/core/cgroup.c
src/core/dbus-cgroup.c
src/core/dbus-manager.c
src/core/dbus-scope.c
src/core/dbus-unit.c
src/core/dbus-unit.h
src/core/dbus.c
src/core/device.c
src/core/device.h
src/core/execute.c
src/core/import-creds.c
src/core/job.c
src/core/job.h
src/core/kmod-setup.c
src/core/load-fragment-gperf.gperf.in
src/core/load-fragment.c
src/core/load-fragment.h
src/core/main.c
src/core/manager-serialize.c
src/core/manager.c
src/core/manager.h
src/core/mount.c
src/core/namespace.c
src/core/scope.c
src/core/scope.h
src/core/selinux-access.c
src/core/selinux-access.h
src/core/service.c
src/core/swap.c
src/core/system.conf.in
src/core/timer.c
src/core/unit-serialize.c
src/core/unit.c
src/core/unit.h
src/core/user.conf.in
src/coredump/coredump.c
src/coredump/coredumpctl.c
src/creds/creds.c
src/cryptenroll/cryptenroll.c
src/cryptsetup/cryptsetup-generator.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-util.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-util.h
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
src/cryptsetup/cryptsetup.c
src/dissect/dissect.c
src/firstboot/firstboot.c
src/fundamental/efivars-fundamental.h
src/fundamental/macro-fundamental.h
src/fundamental/meson.build
src/fundamental/tpm-pcr.c [new file with mode: 0644]
src/fundamental/tpm-pcr.h [new file with mode: 0644]
src/home/homed-home-bus.c
src/home/homework-cifs.c
src/home/homework-luks.c
src/home/homework-mount.c
src/home/homework.h
src/import/pull-common.h
src/integritysetup/integritysetup.c
src/journal/journalctl.c
src/journal/journald-server.c
src/journal/managed-journal-file.c
src/kernel-install/test-kernel-install.sh
src/libsystemd-network/dhcp-identifier.c
src/libsystemd-network/dhcp-identifier.h
src/libsystemd-network/dhcp-internal.h
src/libsystemd-network/dhcp-network.c
src/libsystemd-network/dhcp-protocol.h
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-protocol.c
src/libsystemd-network/dhcp6-protocol.h
src/libsystemd-network/fuzz-dhcp-client.c
src/libsystemd-network/fuzz-dhcp6-client.c
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd-network/sd-ipv4ll.c
src/libsystemd-network/test-acd.c
src/libsystemd-network/test-dhcp-client.c
src/libsystemd-network/test-ipv4ll-manual.c
src/libsystemd-network/test-ipv4ll.c
src/libsystemd/libsystemd.sym
src/libsystemd/sd-bus/bus-objects.c
src/libsystemd/sd-bus/bus-socket.c
src/libsystemd/sd-bus/test-bus-objects.c
src/libsystemd/sd-device/device-enumerator.c
src/libsystemd/sd-device/device-monitor.c
src/libsystemd/sd-device/device-private.h
src/libsystemd/sd-device/device-util.c
src/libsystemd/sd-device/device-util.h
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-device/test-sd-device-monitor.c
src/libsystemd/sd-device/test-sd-device.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/journal-send.c
src/libsystemd/sd-journal/journal-send.h
src/libsystemd/sd-netlink/netlink-genl.c
src/libsystemd/sd-netlink/netlink-internal.h
src/libsystemd/sd-netlink/netlink-message-nfnl.c
src/libsystemd/sd-netlink/netlink-message-rtnl.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-socket.c
src/libsystemd/sd-netlink/netlink-types-genl.c
src/libsystemd/sd-netlink/netlink-types-internal.h
src/libsystemd/sd-netlink/netlink-types-nfnl.c
src/libsystemd/sd-netlink/netlink-types-rtnl.c
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-netlink/netlink-types.h
src/libsystemd/sd-netlink/netlink-util.h
src/libsystemd/sd-netlink/test-netlink.c
src/locale/localed-util.c
src/login/loginctl.c
src/login/logind-dbus.c
src/login/logind-session-device.c
src/login/logind-session.c
src/login/logind-user.c
src/login/logind-user.h
src/login/logind-utmp.c
src/login/logind.c
src/login/pam_systemd.c
src/machine/machine-dbus.c
src/machine/machine-dbus.h
src/machine/machinectl.c
src/machine/machined-dbus.c
src/network/netdev/l2tp-tunnel.c
src/network/netdev/netdev-gperf.gperf
src/network/netdev/netdev-util.c
src/network/netdev/netdev.c
src/network/netdev/netdev.h
src/network/netdev/tuntap.c
src/network/netdev/tuntap.h
src/network/networkd-address.c
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-prefix-delegation.c
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-ipv4acd.c
src/network/networkd-ipv4acd.h
src/network/networkd-ipv4ll.c
src/network/networkd-ipv4ll.h
src/network/networkd-ipv6ll.c
src/network/networkd-ipv6ll.h
src/network/networkd-json.c
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-radv.c
src/network/networkd-route.c
src/network/networkd-setlink.c
src/network/networkd-sriov.c
src/network/networkd-sriov.h
src/network/networkd-util.h
src/network/networkd-wiphy.c
src/network/networkd-wiphy.h
src/network/tc/qdisc.c
src/network/tc/qdisc.h
src/network/tc/teql.c
src/oom/oomd-manager-bus.c
src/oom/oomd-manager.c
src/partition/repart.c
src/partition/test-repart.sh
src/portable/portablectl.c
src/portable/profile/trusted/service.conf
src/resolve/resolvectl.c
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-cache.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h
src/resolve/resolved-dnssd.c
src/resolve/resolved-mdns.c
src/run/run.c
src/shared/blockdev-util.c
src/shared/blockdev-util.h
src/shared/bus-locator.c
src/shared/bus-locator.h
src/shared/bus-print-properties.c
src/shared/bus-unit-procs.c
src/shared/bus-unit-util.c
src/shared/cgroup-setup.c
src/shared/cgroup-setup.h
src/shared/condition.c
src/shared/condition.h
src/shared/conf-parser.c
src/shared/conf-parser.h
src/shared/copy.c
src/shared/creds-util.c
src/shared/creds-util.h
src/shared/daemon-util.c [new file with mode: 0644]
src/shared/daemon-util.h
src/shared/dissect-image.c
src/shared/efi-loader.c
src/shared/efi-loader.h
src/shared/extension-release.c
src/shared/find-esp.c
src/shared/firewall-util-nft.c
src/shared/generator.c
src/shared/hwdb-util.c
src/shared/hwdb-util.h
src/shared/json.c
src/shared/loop-util.c
src/shared/loop-util.h
src/shared/meson.build
src/shared/mount-util.c
src/shared/netif-naming-scheme.c
src/shared/netif-naming-scheme.h
src/shared/pcre2-util.c
src/shared/pcre2-util.h
src/shared/sleep-config.c
src/shared/sleep-config.h
src/shared/smack-util.c
src/shared/tpm2-util.c
src/shared/udev-util.c
src/shared/user-record-show.c
src/shutdown/shutdown.c
src/sleep/sleep.c
src/sleep/sleep.conf
src/sulogin-shell/sulogin-shell.c
src/sysctl/sysctl.c
src/systemctl/systemctl-list-units.c
src/systemctl/systemctl-list-units.h
src/systemctl/systemctl-util.c
src/systemctl/systemctl.c
src/systemd/_sd-common.h
src/systemd/sd-device.h
src/systemd/sd-dhcp-client.h
src/systemd/sd-dhcp6-client.h
src/systemd/sd-messages.h
src/systemd/sd-netlink.h
src/sysusers/sysusers.c
src/test/meson.build
src/test/test-condition.c
src/test/test-copy.c
src/test/test-execve.c [new file with mode: 0644]
src/test/test-json.c
src/test/test-macro.c
src/test/test-math-util.c [new file with mode: 0644]
src/test/test-mountpoint-util.c
src/test/test-path-util.c
src/tmpfiles/tmpfiles.c
src/udev/cdrom_id/cdrom_id.c
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/udev-builtin-blkid.c
src/udev/udev-builtin-hwdb.c
src/udev/udev-builtin-kmod.c
src/udev/udev-builtin-net_id.c
src/udev/udev-builtin-net_setup_link.c
src/udev/udev-builtin.c
src/udev/udev-builtin.h
src/udev/udev-rules.c
src/udev/udev-rules.h
src/udev/udevadm-lock.c
src/udev/udevd.c
test/TEST-35-LOGIN/test.sh
test/TEST-54-CREDS/test.sh
test/TEST-70-TPM2/test.sh
test/TEST-76-SYSCTL/Makefile [new file with mode: 0644]
test/TEST-76-SYSCTL/test.sh [new file with mode: 0755]
test/fuzz/fuzz-link-parser/directives.link
test/fuzz/fuzz-netdev-parser/directives.netdev
test/fuzz/fuzz-network-parser/directives
test/fuzz/fuzz-unit-file/directives-all.service
test/fuzz/fuzz-unit-file/directives.service
test/mkosi.default.networkd-test
test/networkd-test.py
test/test-functions
test/test-network/conf/25-bridge99.network
test/test-network/conf/25-dhcp-client-with-ipv4ll.network
test/test-network/conf/25-qdisc-clsact-and-htb.network [deleted file]
test/test-network/conf/25-qdisc-clsact.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-codel.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-fq.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-fq_codel.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-gred.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-htb-fifo.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-ingress.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-netem-compat.network [moved from test/test-network/conf/25-qdisc-ingress-netem-compat.network with 84% similarity]
test/test-network/conf/25-qdisc-netem.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-qfq.network
test/test-network/conf/25-qdisc-sfb.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-sfq.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-tbf.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-teql.network [new file with mode: 0644]
test/test-network/conf/25-tap.netdev
test/test-network/conf/25-tun.netdev
test/test-network/conf/25-wireguard.network
test/test-network/conf/26-bridge-slave-interface-1.network
test/test-network/conf/26-bridge-slave-interface-2.network
test/test-network/conf/26-netdev-link-local-addressing-yes.network
test/test-network/systemd-networkd-tests.py
test/test-systemd-tmpfiles.py
test/units/assert.sh
test/units/testsuite-17.07.sh [new file with mode: 0755]
test/units/testsuite-19.sh
test/units/testsuite-35.sh
test/units/testsuite-50.sh
test/units/testsuite-53.sh
test/units/testsuite-54.sh
test/units/testsuite-60.sh
test/units/testsuite-64.sh
test/units/testsuite-67.sh
test/units/testsuite-70.sh
test/units/testsuite-76.service [new file with mode: 0644]
test/units/testsuite-76.sh [new file with mode: 0755]
tmpfiles.d/meson.build
tmpfiles.d/provision.conf [new file with mode: 0644]
tools/coverity.sh
tools/get-coverity.sh [deleted file]
units/console-getty.service.in
units/container-getty@.service.in
units/systemd-networkd.service.in
units/systemd-sysctl.service.in
units/systemd-sysusers.service
units/systemd-tmpfiles-clean.service
units/systemd-tmpfiles-setup-dev.service
units/systemd-tmpfiles-setup.service
units/user@0.service.d/10-login-barrier.conf

index f9c9705acaa661a13996eafbfd182f9cc292d594..5ea99d79d0e96dd8e719cf6ef8eb4f0716068f59 100644 (file)
@@ -106,6 +106,7 @@ body:
         - 'systemd-growfs'
         - 'systemd-homed'
         - 'systemd-hostnamed'
+        - 'systemd-hwdb'
         - 'systemd-import'
         - 'systemd-journal-gatewayd'
         - 'systemd-journal-remote'
index a526540f8e702bb394aed2b5de06e14c18b537aa..7f327e280eb9c328323266a635de0e1f98ff1209 100644 (file)
@@ -47,6 +47,7 @@ body:
         - 'systemd-growfs'
         - 'systemd-homed'
         - 'systemd-hostnamed'
+        - 'systemd-hwdb'
         - 'systemd-import'
         - 'systemd-journal-gatewayd'
         - 'systemd-journal-remote'
index fe1daff513e81f02db397887aa4f7a175233db7e..3ab23935471c2cd4f4cd264ba5c1c4b2b2373493 100644 (file)
@@ -15,7 +15,7 @@ policy:
   growfs: ['systemd-growfs']
   homed: ['systemd-homed', 'homectl', 'pam_systemd_home']
   hostname: ['systemd-hostnamed', 'hostnamectl']
-  hwdb: ['hardware database files']
+  hwdb: ['systemd-hwdb', 'hardware database files']
   import: ['systemd-import']
   journal: ['systemd-journald', 'journalctl']
   journal-remote: ['systemd-journal-remote', 'systemd-journal-upload', 'systemd-journal-gatewayd']
@@ -34,7 +34,7 @@ policy:
   rfkill: ['systemd-rfkill']
   rpm: ['rpm scriptlets']
   run: ['systemd-run']
-  sd-boot: ['bootctl', 'systemd-boot', 'systemd-stub']
+  sd-boot/sd-stub/bootctl: ['bootctl', 'systemd-boot', 'systemd-stub']
   sysctl: ['systemd-sysctl']
   sysext: ['systemd-sysext']
   systemctl: ['systemctl']
diff --git a/.github/pull_request_template.md.disabled b/.github/pull_request_template.md.disabled
new file mode 100644 (file)
index 0000000..a133346
--- /dev/null
@@ -0,0 +1,5 @@
+# CODE FREEZE NOTICE
+
+An -rc1 tag has been created and a release is being prepared, so please note that
+PRs introducing new features and APIs will be held back until the new version
+has been released.
index 1bef3925e9975649b0ea01d2dee321d49c756532..73bea19b13574dec4295e678546c73a21a7082fa 100644 (file)
@@ -43,7 +43,7 @@ jobs:
       uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
 
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@3f62b754e23e0dd60f91b744033e1dc1654c0ec6
+      uses: github/codeql-action/init@0c670bbf0414f39666df6ce8e718ec5662c21e03
       with:
         languages: ${{ matrix.language }}
         config-file: ./.github/codeql-config.yml
@@ -51,7 +51,7 @@ jobs:
     - run: sudo -E .github/workflows/unit_tests.sh SETUP
 
     - name: Autobuild
-      uses: github/codeql-action/autobuild@3f62b754e23e0dd60f91b744033e1dc1654c0ec6
+      uses: github/codeql-action/autobuild@0c670bbf0414f39666df6ce8e718ec5662c21e03
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@3f62b754e23e0dd60f91b744033e1dc1654c0ec6
+      uses: github/codeql-action/analyze@0c670bbf0414f39666df6ce8e718ec5662c21e03
index 904a6895fd4c64f2a2923746155f10fba0457c26..3fbebc6bbf1d0ce852247f9febfd3260330f02f0 100644 (file)
@@ -17,27 +17,14 @@ jobs:
     runs-on: ubuntu-22.04
     if: github.repository == 'systemd/systemd'
     env:
-      COVERITY_SCAN_BRANCH_PATTERN:     "${{ github.ref}}"
-      COVERITY_SCAN_NOTIFICATION_EMAIL: ""
-      COVERITY_SCAN_PROJECT_NAME:       "${{ github.repository }}"
-      # Set in repo settings -> secrets -> repository secrets
+      # Set in repo settings -> secrets -> actions
       COVERITY_SCAN_TOKEN:              "${{ secrets.COVERITY_SCAN_TOKEN }}"
-      CURRENT_REF:                      "${{ github.ref }}"
+      COVERITY_SCAN_NOTIFICATION_EMAIL: "${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }}"
     steps:
       - name: Repository checkout
         uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
-      # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
-      - name: Set the $COVERITY_SCAN_NOTIFICATION_EMAIL env variable
-        run: echo "COVERITY_SCAN_NOTIFICATION_EMAIL=$(git log -1 ${{ github.sha }} --pretty=\"%aE\")" >> "$GITHUB_ENV"
-      - name: Install Coverity tools
-        run: tools/get-coverity.sh
       # Reuse the setup phase of the unit test script to avoid code duplication
       - name: Install build dependencies
         run: sudo -E .github/workflows/unit_tests.sh SETUP
-      # Preconfigure with meson to prevent Coverity from capturing meson metadata
-      - name: Preconfigure the build directory
-        run: meson cov-build -Dman=false
-      - name: Build
-        run: tools/coverity.sh build
-      - name: Upload the results
-        run: tools/coverity.sh upload
+      - name: Build & upload the results
+        run: tools/coverity.sh
diff --git a/.github/workflows/differential-shellcheck.yml b/.github/workflows/differential-shellcheck.yml
new file mode 100644 (file)
index 0000000..154cf4a
--- /dev/null
@@ -0,0 +1,30 @@
+---
+# https://github.com/redhat-plumbers-in-action/differential-shellcheck#readme
+
+name: Differential ShellCheck
+on:
+  pull_request:
+    branches:
+      - main
+
+permissions:
+  contents: read
+
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+
+    permissions:
+      security-events: write
+      pull-requests: write
+
+    steps:
+      - name: Repository checkout
+        uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
+        with:
+          fetch-depth: 0
+
+      - name: Differential ShellCheck
+        uses: redhat-plumbers-in-action/differential-shellcheck@60360c0fb283149ed03ad16b66257a967c3e5231
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
index 64774e67cc30ff88b2798fd31fd520c4bc899963..094f72c4b97cef5f35b53811c6b16bcf1fbf3461 100644 (file)
@@ -22,7 +22,7 @@ jobs:
     steps:
       - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
 
-      - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd
+      - uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93
         with:
           node-version: '16'
 
index 25129f624d3595add2d23b07b85c30b7dee37e14..4c6a8d5e5a0ba873eefb434e94c28f2ebea6a1b0 100644 (file)
@@ -29,7 +29,7 @@ jobs:
           fetch-depth: 0
 
       - name: Lint Code Base
-        uses: github/super-linter/slim@a320804d310fdeb8d1a46c6c6c1e615d443b10c9
+        uses: github/super-linter/slim@2d64ac1c067c34beaf7d24cc12733cd46236f76e
         env:
           DEFAULT_BRANCH: main
           MULTI_STATUS: false
@@ -38,8 +38,7 @@ jobs:
           #              missing shebangs)
           #   - .*\.(in|SKELETON) - all template/skeleton files
           #                         except kernel-install
-          #   - tools/coverity\.sh - external file (with some modifications)
-          FILTER_REGEX_EXCLUDE: .*/(man/.*|([^k]|k(k|ek)*([^ek]|e[^kr]))*(k(k|ek)*e?)?\.(in|SKELETON)|tools/coverity\.sh)$
+          FILTER_REGEX_EXCLUDE: .*/(man/.*|([^k]|k(k|ek)*([^ek]|e[^kr]))*(k(k|ek)*e?)?\.(in|SKELETON))$
           VALIDATE_ALL_CODEBASE: false
           VALIDATE_BASH: true
           VALIDATE_GITHUB_ACTIONS: true
index 6f1897c9bfdf077a593c80ad101c9ad804b53f3a..dddeb5fcb324dd8fb493bdb33222ebf4a4edc704 100644 (file)
@@ -40,6 +40,8 @@ jobs:
             release: jammy
           - distro: fedora
             release: "36"
+          - distro: fedora
+            release: rawhide
           - distro: opensuse
             release: tumbleweed
           - distro: centos_epel
@@ -49,7 +51,7 @@ jobs:
 
     steps:
     - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
-    - uses: systemd/mkosi@679bcc98fb8fbe7b53ed809c7019992877e18c14
+    - uses: systemd/mkosi@f37c19e7217a41c52d9a9a8769e98496255e2e2d
 
     - name: Install
       run: sudo apt-get update && sudo apt-get install --no-install-recommends python3-pexpect python3-jinja2
index c97c178a5b4970b3cf9d47a63687417920c83351..5bc9cbef8ebb2b8968fd999e36f86ded376db72d 100644 (file)
@@ -1,6 +1,6 @@
-meson==0.62.2 \
-    --hash=sha256:a7669e4c4110b06b743d57cc5d6432591a6677ef2402139fe4f3d42ac13380b0 \
-    --hash=sha256:c245d2b39e1ce1d1968e0b7067771fd02ca1bade1990adb3cf4088375ba188c9
+meson==0.63.0 \
+    --hash=sha256:399f2ca3181ef257fe3adb2deaff46bc19435cb8b1e883f26db831ce32139820 \
+    --hash=sha256:3b51d451744c2bc71838524ec8d96cd4f8c4793d5b8d5d0d0a9c8a4f7c94cd6f
 ninja==1.10.2.3 \
     --hash=sha256:0560eea57199e41e86ac2c1af0108b63ae77c3ca4d05a9425a750e908135935a \
     --hash=sha256:21a1d84d4c7df5881bfd86c25cce4cf7af44ba2b8b255c57bc1c434ec30a2dfc \
index 153ebe3d3791fab1a5f14aa295bdc0f9441fcbbf..acdb3fcee1cdf59e6565f2852f2fa2731ea4b79b 100755 (executable)
@@ -20,7 +20,7 @@ ln -s /dev/null "$TEMP_EXTRA_TREE/etc/systemd/system/isc-dhcp-server6.service"
 
 for ((i = 0; i < 5; i++)); do
     EC=0
-    (sudo python3 -m mkosi --extra-tree="$TEMP_EXTRA_TREE" "$@") |& tee "$TEMPFILE" || EC=$?
+    (sudo timeout -k 30 10m python3 -m mkosi --extra-tree="$TEMP_EXTRA_TREE" "$@") |& tee "$TEMPFILE" || EC=$?
     if [[ $EC -eq 0 ]]; then
         # The command passed — let's return immediately
         break
index 7b6d0a376e976ada9d7b8ba250b4e472c6dd1a07..8aa363eac4cfbafb69077282dd0e9e0f703fafcd 100644 (file)
@@ -37,3 +37,4 @@ __pycache__/
 /mkosi.default.d/**/*local*.conf
 /tags
 .dir-locals-2.el
+.vscode/
index ba2181171814485369761bfc6b6d28845a4f90c1..98fd7b441121d9d27903fe39d3e9a48ff7c0954d 100755 (executable)
@@ -19,14 +19,7 @@ PHASES=(${@:-SETUP RUN})
 UBUNTU_RELEASE="$(lsb_release -cs)"
 
 create_container() {
-    # Create autopkgtest LXC image; this sometimes fails with "Unable to fetch
-    # GPG key from keyserver", so retry a few times with different keyservers.
-    for keyserver in "keys.openpgp.org" "" "keyserver.ubuntu.com" "keys.gnupg.net"; do
-        for retry in {1..5}; do
-            sudo lxc-create -n "$CONTAINER" -t download -- -d "$DISTRO" -r "$RELEASE" -a "$ARCH" ${keyserver:+--keyserver "$keyserver"} && break 2
-            sleep $((retry*retry))
-        done
-    done
+    sudo lxc-create -n "$CONTAINER" -t download -- -d "$DISTRO" -r "$RELEASE" -a "$ARCH"
 
     # unconfine the container, otherwise some tests fail
     echo 'lxc.apparmor.profile = unconfined' | sudo tee -a "/var/lib/lxc/$CONTAINER/config"
@@ -107,10 +100,10 @@ EOF
             # now build the package and run the tests
             rm -rf "$ARTIFACTS_DIR"
             # autopkgtest exits with 2 for "some tests skipped", accept that
-            "$AUTOPKGTEST_DIR/runner/autopkgtest" --env DEB_BUILD_OPTIONS=noudeb \
-                                                  --env TEST_UPSTREAM=1 ../systemd_*.dsc \
-                                                  -o "$ARTIFACTS_DIR" \
-                                                  -- lxc -s "$CONTAINER" \
+            sudo "$AUTOPKGTEST_DIR/runner/autopkgtest" --env DEB_BUILD_OPTIONS=noudeb \
+                                                       --env TEST_UPSTREAM=1 ../systemd_*.dsc \
+                                                       -o "$ARTIFACTS_DIR" \
+                                                       -- lxc -s "$CONTAINER" \
                 || [ $? -eq 2 ]
         ;;
         *)
diff --git a/NEWS b/NEWS
index 6476c0b308261f0dc4e07294b2262d0b6080eaa7..f2747b6acab1a877cfdf06dbc14d67ff34be5ca0 100644 (file)
--- a/NEWS
+++ b/NEWS
 systemd System and Service Manager
 
-CHANGES WITH 252:
-        Backwards-incompatible changes:
+CHANGES WITH 252 in spe:
+
+        Announcement of Future Feature Removal
+
+        * Please note that we intend to remove cgroupsv1 support from systemd
+          release after EOY 2023. If you run services that make explicit use of
+          cgroupsv1 features, please implement compatibility with cgroupsv2
+          sooner rather than later, if you haven't done so yet. Most of Linux
+          userspace has been ported over already.
+
+        New features:
+
+        * systemd-measure is a new helper to precalculate PCR measurements
+          to make it easier to set TPM2 policies.
+
+        Changes in systemd itself, i.e. the manager, and units
+
+        * The cpu controller is delegated to user manager units, and CPUWeight=
+          settings are applied to the top-level user slice units (app.slice,
+          background.slice, session.slice). This provides a degree of resource
+          isolation between different user services competing for the CPU.
+
+        * Systemd can optionally do a full preset in the "first boot" condition
+          (instead of just enable-only). This behaviour is controlled by the
+          compile-time option -Dfirst-boot-full-preset=. Right now it defaults
+          to 'false', but the plan is to switch it to 'true' for the subsequent
+          release.
+
+        * Systemd will set the taint flag 'support-ended' if it detects that
+          the os image is past its end-of-support date.
+
+        * Two new settings ConditionCredential= and AssertCredential= can
+          be used to skip or fail units if a certain credential is not provided.
+
+        * ConditionMemory= accepts size suffixes.
+
+        * DefaultSmackProcessLabel= can be used in system.conf and user.conf
+          to specify the smack label to use when not specified in a unit file.
+
+        * DefaultDeviceTimeoutSec= can be used system.conf and user.conf
+          to specify the default timeout for devices.
+
+        * C.UTF-8 is used as the default locale if nothing else has been configured.
+
+        Changes in sd-boot, bootctl, and the Boot Loader Specification:
+
+        * The Boot Loader Specification has been cleaned up and clarified.
+          Various corner cases in version string comparisons have been fixed
+          (e.g. comparisons for empty strings). Boot counting is now part of
+          the main specification.
+
+        * New PCRs measurements are set during boot: PCR 11 for the the
+          kernel+initrd combo, PCR 13 for any sysext images.
+
+        * The UEFI monotonic boot counter is now included in the random seed,
+          providing some additional entropy.
+
+        * Booting in EFI mixed mode (a 64-bit kernel over 32-bit UEFI firmware)
+          is now supported.
+
+        * bootctl gained a bunch of new options: '--all-architectures' to
+          install binaries for all supported EFI architectures, '--root=' and
+          '--image=' options to operate on a directory or disk image, and
+          '--install-source=' to specify the source for binaries to install.
+
+        * The sd-boot stub exports a StubFeatures flag, which is used by
+          bootctl to show features supported by the stub that was used to boot.
+
+        Changes in the hardware database:
+
+        * 'systemd-hwdb query' now supports the '--root' option.
+
+        Changes in systemctl:
+
+        * systemctl now supports '--state' and '--type' options for the 'show'
+          and 'status' verbs.
+
+        * systemctl gained a new verb 'list-automounts' to list automount
+          points.
+
+        Changes in systemd-networkd:
+
+        * networkd can set Linux NetLabel labels for integration with the
+          network control in security modules via a new NetLabel= option.
+
+        * networkd gained new options NFTSet=, IPv4NFTSet=, IPv6NFTSet= that
+          take names of nft sets as arguments. It will automatically add rules
+          for the subnets configured for an interface to those sets.
+
+        * The RapidCommit= is (re-)introduced to enable faster configuration
+          via DHCPv6 (RFC 3315).
+
+        Changes in systemd-nspawn:
+
+        * The --bind= and --overlay= options now support relative paths.
+
+        Changes in libsystemd and other libraries:
+
+        * libsystemd now exports the sd-netlink interface that provides
+          functions to send/receive/parse netlink and rtnl messages.
+
+        * libsystemd now exports sd_bus_error_setfv (a convenience function for
+          setting bus errors), sd_id128_string_equal (a convenience function
+          for identifier comparisons), sd_bus_message_read_strv_extend (a
+          function to incrementally read string arrays).
+
+        * Private shared libraries (libsystemd-shared-nnn.so,
+          libsystemd-core-nnn.so) are now installed into arch-specific
+          directories to allow multi-arch installs.
+
+        Changes in other components:
+
+        * sysusers and tmpfiles configuration can now be provided via the
+          credential mechanism.
+
+        * tmpfiles can read file contents to write from a credential (and a new
+          modifier char '^' to specify that the argument is a credential name).
+          This mechanism is used to automatically populate /etc/motd, /etc/issue,
+          and /etc/hosts from credentials.
+
+        * systemd-analyze gained a new verb 'compare-versions' that implements
+          comparisons for versions strings (similarly to 'rpmdev-vercmp' and
+          'dpkg --compare-versions').
+
+        * The pkgconfig and rpm macros files now export the directory for user
+          units as 'user_tmpfiles_dir' and '_user_tmpfilesdir'.
+
+        * Detection of Parallels and KubeVirt virtualization has been improved.
+
+        * os-release gained a new field SUPPORT_END=YYYY-MM-DD to inform the
+          user when their system will become unsupported.
+
+        * When performing suspend-then-hibernate, the system will estimate the
+          discharge rate and use that to set the delay until hibernation, and
+          will hibernate immediately instead of suspending when running from a
+          battery and the capacity is below 5%.
+
+        * systemd-sysctl gained a '--strict' option to fail when a sysctl
+          setting is unknown to the kernel.
+
+        * machinectl supports '--force' for the 'copy-to' and 'copy-from'
+          verbs.
+
+        * openssl is the default crypto backend for systemd-resolved. (gnutls
+          is still supported.)
+
+        Experimental features:
+
+        * BPF programs can now be compiled with bpf-gcc.
+
+        * sd-boot can automatically enroll SecureBoot keys from files found on
+          the ESP. This enrollment can be either automatic ('force' mode) or
+          controlled by the user ('manual' mode).
+
+        – Somewhere, sometime
 
-        * systemd-logind's background session class, which is by default used
-          for sessions started via cron, is no longer going to start systemd
-          --user instance. Note that because of that the user dbus instance
-          will not start either. Other session types remain unaffected.
 
 CHANGES WITH 251:
 
diff --git a/README b/README
index 0b9988ce20de8e537b8581531027923cfa25a314..4f9fcc7170d01913e721fa90832063acd6ae4623 100644 (file)
--- a/README
+++ b/README
@@ -207,7 +207,8 @@ REQUIREMENTS:
         python >= 3.5
         meson >= 0.53.2
         ninja
-        gcc, awk, sed, grep, and similar tools
+        gcc >= 4.7
+        awk, sed, grep, and similar tools
         clang >= 10.0, llvm >= 10.0 (optional, required to build BPF programs
                 from source code in C)
         gnu-efi >= 3.0.5 (optional, required for systemd-boot)
diff --git a/TODO b/TODO
index a09db24912f700904e590513f830892853ec85b9..6a04978b53ac248c8f29682a0b7ee2879ea5ceed 100644 (file)
--- a/TODO
+++ b/TODO
@@ -77,8 +77,108 @@ Janitorial Clean-ups:
 * rework mount.c and swap.c to follow proper state enumeration/deserialization
   semantics, like we do for device.c now
 
+Deprecations and removals:
+
+* Remove any support for booting without /usr pre-mounted in the initrd entirely.
+  Update INITRD_INTERFACE.md accordingly.
+
+* 2019-10 – Remove POINTINGSTICK_CONST_ACCEL references from the hwdb, see #9573
+
+* remove cgrouspv1 support EOY 2023. As per
+  https://lists.freedesktop.org/archives/systemd-devel/2022-July/048120.html
+  and then rework cgroupsv2 support around fds, i.e. keep one fd per active
+  unit around, and always operate on that, instead of cgroup fs paths.
+
+* drop support for kernels that lack ambient capabilities support (i.e. make
+  4.3 new baseline). Then drop support for "!!" modifier for ExecStart= which
+  is only supported for such old kernels.
+
+* drop support for getrandom()-less kernels. (GRND_INSECURE means once kernel
+  5.6 becomes our baseline). See
+  https://github.com/systemd/systemd/pull/24101#issuecomment-1193966468 for
+  details. Maybe before that: at taint-flags/warn about kernels that lack
+  getrandom()/environments where it is blocked.
+
+* drop support for LOOP_CONFIGURE-less loopback block devices, once kernel
+  baseline is 5.8.
+
+* drop fd_is_mount_point() fallback mess once we can rely on
+  STATX_ATTR_MOUNT_ROOT to exist i.e. kernel baseline 5.8
+
+* rework our PID tracking in services and so on, to be strictly based on pidfd,
+  once kernel baseline is 5.13.
+
+* ~2023: remove support for TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT
+
 Features:
 
+* sd-stub/sd-boot: write RNG seed to LINUX_EFI_RANDOM_SEED_TABLE_GUID config
+  table as well. (and possibly drop our efi var). Current kernels will pick up
+  the seed from there already, if EFI_RNG_PROTOCOL is not implemented by
+  firmware.
+
+* sd-boot: include domain specific hash string in hash function for random seed
+  plus sizes of everything. also include DMI/SMBIOS blob
+
+* accept a random seed via DMI/SMBIOS vendor string that is credited to the
+  kernel RNG, as cheap alternative to virtio-rng
+
+* sd-stub: invoke random seed logic the same way as in sd-boot, except if
+  random seed EFI variable is already set. That way, the variable set will be
+  set in all cases: if you just use sd-stub, or just sd-boot, or both.
+
+* sd-boot: we probably should include all BootXY EFI variable defined boot
+  entries in our menu, and then suppress ourselves. Benefit: instant
+  compatibility with all other OSes which register things there, in particular
+  on other disks. Always boot into them via NextBoot EFI variable, to not
+  affect PCR values.
+
+* systemd-measure tool:
+  - pre-calculate PCR 12 (command line) + PCR 13 (sysext) the same way we can precalculate PCR 11
+  - sign pre-calculated hashes in a way compatible with TPM2 PCR hash signature
+    policies, in a way they can be included in unified PE kernel images, and
+    made available to userspace. There, this should be consumed by
+    systemd-cryptsetup to implement PCR signature based TPM volume unlock
+    policies.
+
+* in sd-boot: load EFI drivers from a new PE section. That way, one can have a
+  "supercharged" sd-boot binary, that could carry ext4 drivers built-in.
+
+* sd-bus: document that sd_bus_process() only returns messages that non of the
+  filters/handlers installed on the connection took possession of.
+
+* sd-device: add an API for opening a child device, given a device object
+
+* sd-device: add an API for acquiring list of child devices, given a device
+  objects (i.e. all child dirents that dirs or symlinks to dirs)
+
+* sd-device: maybe pin the sysfs dir with an fd, during the entire runtime of
+  an sd_device, then always work based on that.
+
+* add small wrapper around qemu that implements sd_notify/AF_VSOCK + machined and
+  maybe some other stuff and boots it
+
+* maybe add new flags to gpt partition tables for rootfs and usrfs indicating
+  purpose, i.e. whether something is supposed to be bootable in a VM, on
+  baremetal, on an nspawn-style container, if it is a portable service image,
+  or a sysext for initrd, for host os, or for portable container. Then hook
+  portabled/… up to udev to watch block devices coming up with the flags set, and
+  use it.
+
+* portabled: read a credential "portable.extra" or so, that takes a list of
+  file system paths to enable on start.
+
+* sd-boot should look for information what to boot in SMBIOS, too, so that VM
+  managers can tell sd-boot what to boot into and suchlike
+
+* PID 1 should look for an SMBIOS variable that encodes an AF_VSOCK address it
+  should send sd_notify() ready notifications to. That way a VMM can boot up a
+  system, and generically know when it finished booting.
+
+* add "systemd-sysext identify" verb, that you can point on any file in /usr/
+  and that determines from which overlayfs layer it originates, which image, and with
+  what it was signed.
+
 * journald: generate recognizable log events whenever we shutdown journald
   cleanly, and when we migrate run → var. This way tools can verify that a
   previous boot terminated cleanly, because either of these two messages must
@@ -109,7 +209,7 @@ Features:
 * networkd/udevd: add a way to define additional .link, .network, .netdev files
   via the credentials logic.
 
-* fstab-generator: allow definining additional fstab-like mounts via
+* fstab-generator: allow defining additional fstab-like mounts via
   credentials (similar: crypttab-generator, verity-generator,
   integrity-generator)
 
@@ -120,6 +220,14 @@ Features:
 * resolved: allow defining additional /etc/hosts entries via a credential (it
   might make sense to then synthesize a new combined /etc/hosts file in /run
   and bind mount it on /etc/hosts for other clients that want to read it.
+  Similar, allow picking up DNS server IP addresses from credential.
+
+* repart: allow defining additional partitions via credential
+
+* tmpfiles: add snippet that provisions /etc/hosts, /etc/motd,
+  /root/.ssh/authorized_keys from credential
+
+* timesyncd: pick NTP server info from credential
 
 * define a JSON format for units, separating out unit definitions from unit
   runtime state. Then, expose it:
@@ -142,14 +250,11 @@ Features:
 * pam_systemd: on interactive logins, maybe show SUPPORT_END information at
   login time, á la motd
 
-* similar to the existing fw_cfg support, also support reading system
-  credentials from DMI vendor fields, as supported by qemu
-
-* sd-boot: instead of uncondtionally deriving the ESP to search boot loader
+* sd-boot: instead of unconditionally deriving the ESP to search boot loader
   spec entries in from the paths of sd-boot binary, let's optionally allow it
-  to be configured on sd-boot cmdline + efi var. Usecase: embedd sd-boot in the
+  to be configured on sd-boot cmdline + efi var. Usecase: embed sd-boot in the
   UEFI firmware (for example, ovmf supports that via qemu cmdline option), and
-  use it to load stuff from the ESP).
+  use it to load stuff from the ESP.
 
 * make tmpfiles read lines from creds, so that we can provision SSH host keys
   via creds. Similar: sysusers, sysctl, homed
@@ -194,8 +299,6 @@ Features:
 * ask dracut to generate usr= on the kernel cmdline so that we don't need to
   read /etc/fstab from the root fs from the initrd and do daemon-reload
 
-* document that process resource limits are bullshit
-
 * add PR_SET_DUMPABLE service setting
 
 * homed/userdb: maybe define a "companion" dir for home directories where apps
@@ -296,15 +399,6 @@ Features:
   cloud-init/ignitation and similar can parameterize the host with data they
   acquire.
 
-* Add ConditionCredentialExists= or so, that allows conditionalizing services
-  depending on whether a specific system credential is set. Usecase: a service
-  similar to the ssh keygen service that installs any SSH host key supplied via
-  system credentials into /etc/ssh.
-
-* drop support for kernels that lack ambient capabilities support (i.e. make
-  4.3 new baseline). Then drop support for "!!" modifier for ExecStart= which
-  is only supported for such old kernels
-
 * sd-event: compat wd reuse in inotify code: keep a set of removed watch
   descriptors, and clear this set piecemeal when we see the IN_IGNORED event
   for it, or when read() returns EAGAIN or on IN_Q_OVERFLOW. Then, whenever we
@@ -313,12 +407,6 @@ Features:
   case the same wd is reused multiple times before we start processing
   IN_IGNORED again)
 
-* sd-stub: set efi var indicating stub features, i.e. whether they pick up
-  creds, sysexts and so on. similar to existing variable of sd-boot
-
-* sd-stub: set efi vars declaring TPM PCRs we measured creds/cmdline + sysext
-  into (even if we hardcode them)
-
 * systemd-fstab-generator: support addition mount specifications via kernel
   cmdline. Usecase: invoke a VM, and mount a host homedir into it via
   virtio-fs.
@@ -341,10 +429,6 @@ Features:
   - sd-stub: automatically pick up microcode from ESP (/loader/microcode/*)
     and synthesize initrd from it, and measure it. Signing is not necessary, as
     microcode does that on its own. Pass as first initrd to kernel.
-  - sd-stub should measure the kernel/initrd/… into a separate PCR, so that we
-    have one PCR we can bind the encrypted creds to that is not effected by
-    anything else but what we drop in via kernel-install, i.e. by earlier EFI
-    code running (i.e. like PCR 4)
 
 * Add a new service type very similar to Type=notify, that goes one step
   further and extends the protocol to cover reloads. Specifically, SIGHUP will
@@ -588,7 +672,7 @@ Features:
   dep in the base OS image)
 
 * sysext: automatically activate sysext images dropped in via new sd-stub
-  sysext pickup logic.
+  sysext pickup logic. (must insist on verity + signature on those though)
 
 * add concept for "exitrd" as inverse of "initrd", that we can transition to at
   shutdown, and has similar security semantics. This should then take the place
@@ -636,9 +720,9 @@ Features:
   what must be read-only, what requires encryption, and what requires
   authentication.
 
-* in uefi stub: query firmware regarding which PCRs are being used, store that
-  in EFI var. then use this when enrolling TPM2 in cryptsetup to verify that
-  the selected PCRs actually are used by firmware.
+* in uefi stub: query firmware regarding which PCR banks are being used, store
+  that in EFI var. then use this when enrolling TPM2 in cryptsetup to verify
+  that the selected PCRs actually are used by firmware.
 
 * rework recursive read-only remount to use new mount API
 
@@ -805,9 +889,6 @@ Features:
 
 * if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it
 
-* Remove any support for booting without /usr pre-mounted in the initrd entirely.
-  Update INITRD_INTERFACE.md accordingly.
-
 * pid1: Move to tracking of main pid/control pid of units per pidfd
 
 * pid1: support new clone3() fork-into-cgroup feature
@@ -1533,7 +1614,6 @@ Features:
   - show whether UEFI audit mode is available
   - 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
-  - make it operate on loopback files, dissecting enough to find ESP to operate on
   - bootspec: properly support boot attempt counters when parsing entry file names
 
 * kernel-install:
index 766d84fdba9fcc70eb21c4e712101264e56d5029..f0d9865829f4b30c4e56739384d5c069d8e2a5c4 100644 (file)
@@ -50,10 +50,11 @@ purpose. Specifically, the following features are provided:
 6. Service credentials are placed in non-swappable memory. (If permissions
    allow it, via `ramfs`.)
 
-7. Credentials may be acquired from a hosting VM hypervisor (qemu `fw_cfg`), a
-   hosting container manager, the kernel command line, or from the UEFI
-   environment and the EFI System Partition (via `systemd-stub`). Such system
-   credentials may then be propagated into individual services as needed.
+7. Credentials may be acquired from a hosting VM hypervisor (SMBIOS OEM strings
+   or qemu `fw_cfg`), a hosting container manager, the kernel command line, or
+   from the UEFI environment and the EFI System Partition (via
+   `systemd-stub`). Such system credentials may then be propagated into
+   individual services as needed.
 
 8. Credentials are an effective way to pass parameters into services that run
    with `RootImage=` or `RootDirectory=` and thus cannot read these resources
@@ -254,10 +255,19 @@ services where they are ultimately consumed.
    the [Container Interface](CONTAINER_INTERFACE.md)
    documentation.
 
-2. Quite similar, qemu VMs can be invoked with `-fw_cfg
+2. Quite similar, VMs can be passed credentials via SMBIOS OEM strings (example
+   qemu command line switch `-smbios
+   type=11,value=io.systemd.credential:foo=bar` or `-smbios
+   type=11,value=io.systemd.credential.binary:foo=YmFyCg==`, the latter taking
+   a Base64 encoded argument to permit binary credentials being passed
+   in). Alternatively, qemu VMs can be invoked with `-fw_cfg
    name=opt/io.systemd.credentials/foo,string=bar` to pass credentials from
-   host through the hypervisor into the VM. (This specific switch would set
-   credential `foo` to `bar`.)
+   host through the hypervisor into the VM via qemu's `fw_cfg` mechanism. (All
+   three of these specific switches would set credential `foo` to `bar`.)
+   Passing credentials via the SMBIOS mechanism is typically preferable over
+   `fw_cfg` since it is faster and less specific to the chosen VMM
+   implementation. Moreover, `fw_cfg` has a 55 character limitation
+   on names passed that way. So some settings may not fit.
 
 3. Credentials can also be passed into a system via the kernel command line,
    via the `systemd.set-credential=` kernel command line option. Note though
@@ -297,7 +307,7 @@ qemu-system-x86_64 \
         -drive if=none,id=hd,file=test.raw,format=raw \
         -device virtio-scsi-pci,id=scsi \
         -device scsi-hd,drive=hd,bootindex=1 \
-        -fw_cfg name=opt/io.systemd.credentials/mycred,string=supersecret
+        -smbios type=11,value=io.systemd.credential:mycred=supersecret
 ```
 
 Either of these lines will boot a disk image `test.raw`, once as container via
@@ -363,8 +373,8 @@ qemu-system-x86_64 \
         -drive if=none,id=hd,file=test.raw,format=raw \
         -device virtio-scsi-pci,id=scsi \
         -device scsi-hd,drive=hd,bootindex=1 \
-        -fw_cfg name=opt/io.systemd.credentials/passwd.hashed-password.root,string=$(mkpasswd mysecret) \
-        -fw_cfg name=opt/io.systemd.credentials/firstboot.locale,string=C.UTF-8
+        -smbios type=11,value=io.systemd.credential:passwd.hashed-password.root=$(mkpasswd mysecret) \
+        -smbios type=11,value=io.systemd.credential:firstboot.locale=C.UTF-8
 ```
 
 ## Relevant Paths
@@ -386,3 +396,9 @@ in `/etc/credstore/`, `/run/credstore/`,
 `/usr/lib/credstore/`. `LoadCredentialEncrypted=` will also search
 `/etc/credstore.encrypted/` and similar directories. These directories are
 hence a great place to store credentials to load on the system.
+
+## Conditionalizing Services
+
+Sometimes it makes sense to conditionalize system services and invoke them only
+if the right system credential is passed to the system. use the
+`ConditionCredential=` and `AssertCredential=` unit file settings for that.
index 9e5313e07a2f5fcbf94290bcb1f0d52a82dae278..4b93741f3aa41c66526ec231e358c54a0ab41340 100644 (file)
@@ -38,9 +38,10 @@ from an upstream distribution in combination with a fresh build of the project
 in the local working directory. To make use of this, please install the
 `mkosi` package (if not packaged for your distro, it can be downloaded from
 the [GitHub repository](https://github.com/systemd/mkosi). `mkosi` will build an
-image for the host distro by default. It is sufficient to type `mkosi` in the
-systemd project directory to generate a disk image `image.raw` you can boot either
-in `systemd-nspawn` or in an UEFI-capable VM:
+image for the host distro by default. mkosi-13 or newer version is required.
+It is sufficient to type `mkosi` in the systemd project directory to generate
+a disk image `image.raw` you can boot either in `systemd-nspawn` or
+in an UEFI-capable VM:
 
 ```sh
 $ mkosi boot
@@ -66,7 +67,7 @@ reuse the host's package cache. To do this, create a mkosi override file in
 mkosi.default.d/ (e.g 20-local.conf) and add the following contents:
 
 ```
-[Packages]
+[Content]
 Cache=<full-path-to-package-manager-cache> # (e.g. /var/cache/dnf)
 ```
 
@@ -100,7 +101,7 @@ $ vim src/core/main.c             # or wherever you'd like to make your changes
 $ meson build                     # configure the build
 $ ninja -C build                  # build it locally, see if everything compiles fine
 $ meson test -C build             # run some simple regression tests
-$ sudo mkosi                      # build a test image
+$ sudo mkosi                      # mkosi-13 or newer required to build a test image
 $ sudo mkosi boot                 # boot up the test image
 $ git add -p                      # interactively put together your patch
 $ git commit                      # commit it
@@ -140,6 +141,11 @@ enabled that are suitable when hacking on systemd (such as internal
 documentation consistency checks). Those are not useful when compiling for
 distribution and can be disabled by setting `-Dmode=release`.
 
+## Sanitizers in mkosi
+
+See [Testing systemd using sanitizers](TESTING_WITH_SANITIZERS.md) for more information
+on how to build with sanitizers enabled in mkosi.
+
 ## Fuzzers
 
 systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically
@@ -224,7 +230,7 @@ mkosi's config. The easiest way to set the option is to create a file 20-local.c
 add the following contents:
 
 ```
-[Packages]
+[Content]
 IncludeDirectory=mkosi.includedir
 ```
 
@@ -233,7 +239,7 @@ We already configured clangd to map any paths in /usr/include in the build image
 host in the mkosi-clangd.sh script.
 
 We also need to make sure clangd is installed in the build image. To have mkosi install clangd in the build
-image, edit the 20-local.conf file we created earlier and add the following contents under the `[Packages]`
+image, edit the 20-local.conf file we created earlier and add the following contents under the `[Content]`
 section:
 
 ```
index acbfa8e5bb3e4cd4df05f2eb9ae11c0840c6234a..74998e138dbe293b7d68e5b06eef8aa60c25c0b4 100644 (file)
@@ -13,13 +13,15 @@ SPDX-License-Identifier: LGPL-2.1-or-later
 4. Update hwdb (`ninja -C build update-hwdb`, `ninja -C build update-hwdb-autosuspend`, commit separately).
 5. Update syscall numbers (`ninja -C build update-syscall-tables update-syscall-header`).
 6. [RC1] Update version and library numbers in `meson.build`
-7. Check dbus docs with `ninja -C build update-dbus-docs`
-8. Tag the release: `version=vXXX-rcY && git tag -s "${version}" -m "systemd ${version}"`
-9. Do `ninja -C build`
-10. Make sure that the version string and package string match: `build/systemctl --version`
-11. Upload the documentation: `ninja -C build doc-sync`
-12. [FINAL] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones)
-13. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate.
-14. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
-15. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released`)
-16. [FINAL] Push commits to stable, create an empty -stable branch: `git push systemd-stable --atomic origin/main:main origin/main:refs/heads/${version}-stable`, and change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches).
+7. [RC1] Rename `.github/pull_request_template.md.disabled` to `.github/pull_request_template.md` to display the warning about soft-freeze for new features
+8. [FINAL] Rename `.github/pull_request_template.md` to `.github/pull_request_template.md.disabled` to hide the warning about soft-freeze for new features
+9. Check dbus docs with `ninja -C build update-dbus-docs`
+10. Tag the release: `version=vXXX-rcY && git tag -s "${version}" -m "systemd ${version}"`
+11. Do `ninja -C build`
+12. Make sure that the version string and package string match: `build/systemctl --version`
+13. Upload the documentation: `ninja -C build doc-sync`
+14. [FINAL] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones)
+15. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate.
+16. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
+17. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released`)
+18. [FINAL] Push commits to stable, create an empty -stable branch: `git push systemd-stable --atomic origin/main:main origin/main:refs/heads/${version}-stable`, and change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches).
index 4f965c961785e5e432b325e720f11f23e0aafe0e..642e1f19c9776af811965cb3fcf8a1356c372598 100644 (file)
@@ -13,6 +13,22 @@ This is mostly done automagically by various CI systems for each PR, but you may
 want to do it locally as well. The process slightly varies depending on the
 compiler you want to use and which part of the test suite you want to run.
 
+## mkosi
+
+To build with sanitizers in mkosi, create a file 20-local.conf in mkosi.default.d/ and add the following
+contents:
+
+```
+[Content]
+Environment=SANITIZERS=address,undefined
+```
+
+The value of `SANITIZERS` is passed directly to meson's `b_sanitize` option, See
+https://mesonbuild.com/Builtin-options.html#base-options for the format expected by the option. Currently,
+only the sanitizers supported by gcc can be used, which are `address` and `undefined`.
+
+Note that this will only work with a recent version of mkosi (>= 14 or by running mkosi directly from source).
+
 ## gcc
 gcc compiles in sanitizer libraries dynamically by default, so you need to get
 the shared libraries first - on Fedora these are shipped as a separate packages
index 51c631929e9091f096fa2f770b9c645f78f6bc7b..095bb34cd873fc5c602a5abc6efd809cb47e6e27 100644 (file)
@@ -154,6 +154,10 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*8930:*
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*7750G:*
  KEYBOARD_KEY_e0=!pageup
 
+# Acer Aspire 3 A317-33
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspireA317-33:*
+ KEYBOARD_KEY_55=power
+
 # Acer Aspire One AO532h
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAO532h:*
  KEYBOARD_KEY_84=bluetooth
index b1a83e8561a97dd6a92006417845ee39fd983f50..7978675943c63bca5a980031070a1dec42246314 100644 (file)
@@ -339,6 +339,14 @@ sensor:modalias:acpi:INVN6500*:dmi:*svnDellInc.*:pnVenue8Pro3845:*
 sensor:modalias:acpi:INVN6500*:dmi:*svnDell*:pnVenue10Pro5055:*
  ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1
 
+#########################################
+# DERE
+#########################################
+
+# DBook D10
+sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInternational,LLC.:bvrJP2V1.05:bd04/27/2022:br1.5:efr1.3:svnDefaultstring:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
 #########################################
 # DEXP
 #########################################
@@ -398,27 +406,13 @@ sensor:modalias:acpi:KXCJ9000*:dmi:*:bvrZY-8-BI-PX4S70VTR400-X423B-005-D:*:rvnAM
 #########################################
 # Google Chromebooks
 #########################################
-sensor:modalias:platform:cros-ec-accel:dmi:*:svnGOOGLE:*
- ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
-
-# caroline board (Samsung Chromebook Pro) reports itself as svnGoogle
-sensor:modalias:platform:cros-ec-accel:dmi:*:svnGoogle:pnCaroline*:*
- ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
 
-# Dell Inspiron Chromebook 14 2-in-1
-sensor:modalias:platform:cros-ec-accel:dmi:*svnGoogle:pnVayne*:*
+# CrOS EC & kernel drivers internally correct for per-board sensor orientations,
+# but they return values in the inverse direction (Android & W3C specs vs HID).
+sensor:modalias:platform:cros-ec-accel:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
 
-# nocturne board (Google Pixel Slate)
-sensor:modalias:platform:cros-ec-accel:dmi:*Google_Nocturne*:*
- ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
-
-# rammus board (Asus Chromebook Flip C433)
-sensor:modalias:platform:cros-ec-accel:dmi:*svnGoogle:pnRammus*:*
- ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
-
-# Lenovo ThinkPad C13 Yoga
-sensor:modalias:platform:cros-ec-accel:dmi:*svnGoogle:pnMorphius*:*
+sensor:modalias:platform:cros-ec-accel-legacy:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
 
 #########################################
@@ -767,6 +761,10 @@ sensor:modalias:acpi:BOSC0200*:dmi:*svnONE-NETBOOKTECHNOLOGYCO*:pnOne-Mix3Pro:*
 sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.12:bd07/17/2019:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnDefaultstring:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
 
+# One-Netbook OneXPlayer Mini (and maybe others)
+sensor:modalias:acpi:BMI0160*:dmi:*:rnONEXPLAYER:rvrV01:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, -1
+
 #########################################
 # Peaq
 #########################################
index 0a1911507a22e608e5d53f4bb1372200825195df..821ebcb1ca2f30a43b6b4fb7db0d7fb941f2ef60 100644 (file)
@@ -1,13 +1,21 @@
 # This file is part of systemd.
 #
 # Database for signal analyzers (protocol analyzers, logic analyzers,
-# oscilloscopes, multimeters, bench power supplies, etc.) that should
-# be accessible to the seat owner.
+# oscilloscopes, multimeters, bench power supplies, etc.) or just
+# anything that has to do with electronics and that should be
+# accessible to the seat owner.
 #
 # Permitted keys:
 #   Specify if a device is a signal analyzer
 #   ID_SIGNAL_ANALYZER=1|0
 
+###########################################################
+# Greaseweazle
+###########################################################
+# Greaseweazle
+usb:v1209p4D69*
+ ID_SIGNAL_ANALYZER=1
+
 ###########################################################
 # Total Phase
 ###########################################################
index 22411166a8dfe31b7b24a684ff944a38b1068dac..a296949595ec47fce822d02b7d697deb604248db 100644 (file)
         <term><option>keyfile-timeout=</option></term>
 
         <listitem><para> Specifies the timeout for the device on
-        which the key file resides and falls back to a password if
-        it could not be mounted. See
+        which the key file resides or the device used as the key file,
+        and falls back to a password if it could not be accessed. See
         <citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         for key files on external devices.
         </para></listitem>
index 424acc9f163ebb2587ca1f92838ff9812aa75a48..97b781a4c7ff9b9b1dcf22c8bb4f001e6b6ccaf7 100644 (file)
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
 
-  <refentry id="journalctl"
-            xmlns:xi="http://www.w3.org/2001/XInclude">
-
-    <refentryinfo>
-      <title>journalctl</title>
-      <productname>systemd</productname>
-    </refentryinfo>
-
-    <refmeta>
-      <refentrytitle>journalctl</refentrytitle>
-      <manvolnum>1</manvolnum>
-    </refmeta>
-
-    <refnamediv>
-      <refname>journalctl</refname>
-      <refpurpose>Query the systemd journal</refpurpose>
-    </refnamediv>
-
-    <refsynopsisdiv>
-      <cmdsynopsis>
-        <command>journalctl</command>
-        <arg choice="opt" rep="repeat">OPTIONS</arg>
-        <arg choice="opt" rep="repeat">MATCHES</arg>
-      </cmdsynopsis>
-    </refsynopsisdiv>
-
-    <refsect1>
-      <title>Description</title>
-
-      <para><command>journalctl</command> may be used to query the
-      contents of the
-      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-      journal as written by
-      <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
-
-      <para>If called without parameters, it will show the full
-      contents of the journal, starting with the oldest entry
-      collected.</para>
-
-      <para>If one or more match arguments are passed, the output is
-      filtered accordingly. A match is in the format
-      <literal>FIELD=VALUE</literal>,
-      e.g. <literal>_SYSTEMD_UNIT=httpd.service</literal>, referring
-      to the components of a structured journal entry. See
-      <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-      for a list of well-known fields. If multiple matches are
-      specified matching different fields, the log entries are
-      filtered by both, i.e. the resulting output will show only
-      entries matching all the specified matches of this kind. If two
-      matches apply to the same field, then they are automatically
-      matched as alternatives, i.e. the resulting output will show
-      entries matching any of the specified matches for the same
-      field. Finally, the character <literal>+</literal> may appear
-      as a separate word between other terms on the command line. This
-      causes all matches before and after to be combined in a
-      disjunction (i.e. logical OR).</para>
-
-      <para>It is also possible to filter the entries by specifying an
-      absolute file path as an argument. The file path may be a file or
-      a symbolic link and the file must exist at the time of the query. If a
-      file path refers to an executable binary, an <literal>_EXE=</literal>
-      match for the canonicalized binary path is added to the query. If a
-      file path refers to an executable script, a <literal>_COMM=</literal>
-      match for the script name is added to the query. If a file path
-      refers to a device node, <literal>_KERNEL_DEVICE=</literal> matches for
-      the kernel name of the device and for each of its ancestor devices is
-      added to the query. Symbolic links are dereferenced, kernel names are
-      synthesized, and parent devices are identified from the environment at
-      the time of the query. In general, a device node is the best proxy for
-      an actual device, as log entries do not usually contain fields that
-      identify an actual device. For the resulting log entries to be correct
-      for the actual device, the relevant parts of the environment at the time
-      the entry was logged, in particular the actual device corresponding to
-      the device node, must have been the same as those at the time of the
-      query. Because device nodes generally change their corresponding devices
-      across reboots, specifying a device node path causes the resulting
-      entries to be restricted to those from the current boot.</para>
-
-      <para>Additional constraints may be added using options
-      <option>--boot</option>, <option>--unit=</option>, etc., to
-      further limit what entries will be shown (logical AND).</para>
-
-      <para>Output is interleaved from all accessible journal files, whether they are rotated or currently
-      being written, and regardless of whether they belong to the system itself or are accessible user
-      journals. The <option>--header</option> option can be used to identify which files
-      <emphasis>are</emphasis> being shown.</para>
-
-      <para>The set of journal files which will be used can be
-      modified using the <option>--user</option>,
-      <option>--system</option>, <option>--directory</option>, and
-      <option>--file</option> options, see below.</para>
-
-      <para>All users are granted access to their private per-user
-      journals. However, by default, only root and users who are
-      members of a few special groups are granted access to the system
-      journal and the journals of other users. Members of the groups
-      <literal>systemd-journal</literal>, <literal>adm</literal>, and
-      <literal>wheel</literal> can read all journal files. Note
-      that the two latter groups traditionally have additional
-      privileges specified by the distribution. Members of the
-      <literal>wheel</literal> group can often perform administrative
-      tasks.</para>
-
-      <para>The output is paged through <command>less</command> by
-      default, and long lines are "truncated" to screen width. The
-      hidden part can be viewed by using the left-arrow and
-      right-arrow keys. Paging can be disabled; see the
-      <option>--no-pager</option> option and the "Environment" section
-      below.</para>
-
-      <para>When outputting to a tty, lines are colored according to
-      priority: lines of level ERROR and higher are colored red; lines
-      of level NOTICE and higher are highlighted; lines of level DEBUG
-      are colored lighter grey; other lines are displayed normally.</para>
-    </refsect1>
-
-    <refsect1>
-      <title>Options</title>
-
-      <para>The following options are understood:</para>
-
-      <variablelist>
-        <varlistentry>
-          <term><option>--no-full</option></term>
-          <term><option>--full</option></term>
-          <term><option>-l</option></term>
-
-          <listitem><para>Ellipsize fields when they do not fit in
-          available columns.  The default is to show full fields,
-          allowing them to wrap or be truncated by the pager, if one
-          is used.</para>
-
-          <para>The old options
-          <option>-l</option>/<option>--full</option> are not useful
-          anymore, except to undo <option>--no-full</option>.</para>
-          </listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><option>-a</option></term>
-          <term><option>--all</option></term>
-
-          <listitem><para>Show all fields in full, even if they include unprintable characters or are very long. By
-          default, fields with unprintable characters are abbreviated as "blob data". (Note that the pager may escape
-          unprintable characters again.)</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><option>-f</option></term>
-          <term><option>--follow</option></term>
-
-          <listitem><para>Show only the most recent journal entries,
-          and continuously print new entries as they are appended to
-          the journal.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><option>-e</option></term>
-          <term><option>--pager-end</option></term>
-
-          <listitem><para>Immediately jump to the end of the journal
-          inside the implied pager tool. This implies
-          <option>-n1000</option> to guarantee that the pager will not
-          buffer logs of unbounded size. This may be overridden with
-          an explicit <option>-n</option> with some other numeric
-          value, while <option>-nall</option> will disable this cap.
-          Note that this option is only supported for the
-          <citerefentry project='man-pages'><refentrytitle>less</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-          pager.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><option>-n</option></term>
-          <term><option>--lines=</option></term>
-
-          <listitem><para>Show the most recent journal events and
-          limit the number of events shown. If
-          <option>--follow</option> is used, this option is
-          implied. The argument is a positive integer or
-          <literal>all</literal> to disable line limiting. The default
-          value is 10 if no argument is given.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><option>--no-tail</option></term>
-
-          <listitem><para>Show all stored output lines, even in follow
-          mode. Undoes the effect of <option>--lines=</option>.
-          </para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><option>-r</option></term>
-          <term><option>--reverse</option></term>
-
-          <listitem><para>Reverse output so that the newest entries
-          are displayed first.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><option>-o</option></term>
-          <term><option>--output=</option></term>
-
-          <listitem><para>Controls the formatting of the journal
-          entries that are shown. Takes one of the following
-          options:</para>
-          <variablelist>
-            <varlistentry>
-              <term>
-                <option>short</option>
-              </term>
-              <listitem>
-                <para>is the default and generates an output that is
-                mostly identical to the formatting of classic syslog
-                files, showing one line per journal entry.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>short-full</option>
-              </term>
-              <listitem>
-                <para>is very similar, but shows timestamps in the format the <option>--since=</option> and
-                <option>--until=</option> options accept. Unlike the timestamp information shown in
-                <option>short</option> output mode this mode includes weekday, year and timezone information in the
-                output, and is locale-independent.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>short-iso</option>
-              </term>
-              <listitem>
-                <para>is very similar, but shows ISO 8601 wallclock
-                timestamps.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>short-iso-precise</option>
-              </term>
-              <listitem>
-                <para>as for <option>short-iso</option> but includes full
-                microsecond precision.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>short-precise</option>
-              </term>
-              <listitem>
-                <para>is very similar, but shows classic syslog timestamps
-                with full microsecond precision.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>short-monotonic</option>
-              </term>
-              <listitem>
-                <para>is very similar, but shows monotonic timestamps
-                instead of wallclock timestamps.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>short-unix</option>
-              </term>
-              <listitem>
-                <para>is very similar, but shows seconds passed since January 1st 1970 UTC instead of wallclock
-                timestamps ("UNIX time"). The time is shown with microsecond accuracy.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>verbose</option>
-              </term>
-              <listitem>
-                <para>shows the full-structured entry items with all
-                fields.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>export</option>
-              </term>
-              <listitem>
-                <para>serializes the journal into a binary (but mostly
-                text-based) stream suitable for backups and network
-                transfer (see
-                <ulink url="https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-export-format">Journal Export Format</ulink>
-                for more information). To import the binary stream back
-                into native journald format use
-                <citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>json</option>
-              </term>
-              <listitem>
-                <para>formats entries as JSON objects, separated by newline characters (see <ulink
-                url="https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-json-format">Journal JSON Format</ulink> for more
-                information). Field values are generally encoded as JSON strings, with three exceptions:
-                <orderedlist>
-                  <listitem><para>Fields larger than 4096 bytes are encoded as <constant>null</constant> values. (This
-                  may be turned off by passing <option>--all</option>, but be aware that this may allocate overly long
-                  JSON objects.) </para></listitem>
-
-                  <listitem><para>Journal entries permit non-unique fields within the same log entry. JSON does not allow
-                  non-unique fields within objects. Due to this, if a non-unique field is encountered a JSON array is
-                  used as field value, listing all field values as elements.</para></listitem>
-
-                  <listitem><para>Fields containing non-printable or non-UTF8 bytes are encoded as arrays containing
-                  the raw bytes individually formatted as unsigned numbers.</para></listitem>
-                </orderedlist>
-
-                Note that this encoding is reversible (with the exception of the size limit).</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>json-pretty</option>
-              </term>
-              <listitem>
-                <para>formats entries as JSON data structures, but
-                formats them in multiple lines in order to make them
-                more readable by humans.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>json-sse</option>
-              </term>
-              <listitem>
-                <para>formats entries as JSON data structures, but wraps
-                them in a format suitable for
-                <ulink url="https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events">Server-Sent Events</ulink>.
-                </para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>json-seq</option>
-              </term>
-              <listitem>
-                <para>formats entries as JSON data structures, but prefixes them with an ASCII Record Separator
-                character (0x1E) and suffixes them with an ASCII Line Feed character (0x0A), in accordance with <ulink
-                url="https://tools.ietf.org/html/rfc7464">JavaScript Object Notation (JSON) Text Sequences </ulink>
-                (<literal>application/json-seq</literal>).
-                </para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>cat</option>
-              </term>
-              <listitem>
-                <para>generates a very terse output, only showing the actual message of each journal entry
-                with no metadata, not even a timestamp. If combined with the
-                <option>--output-fields=</option> option will output the listed fields for each log record,
-                instead of the message.</para>
-              </listitem>
-            </varlistentry>
-
-            <varlistentry>
-              <term>
-                <option>with-unit</option>
-              </term>
-              <listitem>
-                <para>similar to short-full, but prefixes the unit and
-                user unit names instead of the traditional syslog
-                identifier. Useful when using templated instances, as it
-                will include the arguments in the unit names.</para>
-              </listitem>
-            </varlistentry>
-          </variablelist>
-        </listitem>
+<refentry id="journalctl"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>journalctl</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>journalctl</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>journalctl</refname>
+    <refpurpose>Query the systemd journal</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>journalctl</command>
+      <arg choice="opt" rep="repeat">OPTIONS</arg>
+      <arg choice="opt" rep="repeat">MATCHES</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>journalctl</command> may be used to query the contents of the
+    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> journal as
+    written by
+    <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+    <para>If called without parameters, it will show the full contents of the journal, starting with the
+    oldest entry collected.</para>
+
+    <para>If one or more match arguments are passed, the output is filtered accordingly. A match is in the
+    format <literal>FIELD=VALUE</literal>, e.g. <literal>_SYSTEMD_UNIT=httpd.service</literal>, referring to
+    the components of a structured journal entry. See
+    <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    for a list of well-known fields. If multiple matches are specified matching different fields, the log
+    entries are filtered by both, i.e. the resulting output will show only entries matching all the specified
+    matches of this kind. If two matches apply to the same field, then they are automatically matched as
+    alternatives, i.e. the resulting output will show entries matching any of the specified matches for the
+    same field. Finally, the character <literal>+</literal> may appear as a separate word between other terms
+    on the command line. This causes all matches before and after to be combined in a disjunction
+    (i.e. logical OR).</para>
+
+    <para>It is also possible to filter the entries by specifying an absolute file path as an argument. The
+    file path may be a file or a symbolic link and the file must exist at the time of the query. If a file
+    path refers to an executable binary, an <literal>_EXE=</literal> match for the canonicalized binary path
+    is added to the query. If a file path refers to an executable script, a <literal>_COMM=</literal> match
+    for the script name is added to the query. If a file path refers to a device node,
+    <literal>_KERNEL_DEVICE=</literal> matches for the kernel name of the device and for each of its ancestor
+    devices is added to the query. Symbolic links are dereferenced, kernel names are synthesized, and parent
+    devices are identified from the environment at the time of the query. In general, a device node is the
+    best proxy for an actual device, as log entries do not usually contain fields that identify an actual
+    device. For the resulting log entries to be correct for the actual device, the relevant parts of the
+    environment at the time the entry was logged, in particular the actual device corresponding to the device
+    node, must have been the same as those at the time of the query. Because device nodes generally change
+    their corresponding devices across reboots, specifying a device node path causes the resulting entries to
+    be restricted to those from the current boot.</para>
+
+    <para>Additional constraints may be added using options <option>--boot</option>,
+    <option>--unit=</option>, etc., to further limit what entries will be shown (logical AND).</para>
+
+    <para>Output is interleaved from all accessible journal files, whether they are rotated or currently
+    being written, and regardless of whether they belong to the system itself or are accessible user
+    journals. The <option>--header</option> option can be used to identify which files
+    <emphasis>are</emphasis> being shown.</para>
+
+    <para>The set of journal files which will be used can be modified using the <option>--user</option>,
+    <option>--system</option>, <option>--directory</option>, and <option>--file</option> options, see
+    below.</para>
+
+    <para>All users are granted access to their private per-user journals. However, by default, only root and
+    users who are members of a few special groups are granted access to the system journal and the journals
+    of other users. Members of the groups <literal>systemd-journal</literal>, <literal>adm</literal>, and
+    <literal>wheel</literal> can read all journal files. Note that the two latter groups traditionally have
+    additional privileges specified by the distribution. Members of the <literal>wheel</literal> group can
+    often perform administrative tasks.</para>
+
+    <para>The output is paged through <command>less</command> by default, and long lines are "truncated" to
+    screen width. The hidden part can be viewed by using the left-arrow and right-arrow keys. Paging can be
+    disabled; see the <option>--no-pager</option> option and the "Environment" section below.</para>
+
+    <para>When outputting to a tty, lines are colored according to priority: lines of level ERROR and higher
+    are colored red; lines of level NOTICE and higher are highlighted; lines of level DEBUG are colored
+    lighter grey; other lines are displayed normally.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Source Options</title>
+
+    <para>The following options control where to read journal records from:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>--system</option></term>
+        <term><option>--user</option></term>
+
+        <listitem><para>Show messages from system services and the kernel (with
+        <option>--system</option>). Show messages from service of current user (with
+        <option>--user</option>).  If neither is specified, show all messages that the user can see.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--output-fields=</option></term>
+        <term><option>-M</option></term>
+        <term><option>--machine=</option></term>
 
-        <listitem><para>A comma separated list of the fields which should be included in the output. This has
-        an effect only for the output modes which would normally show all fields (<option>verbose</option>,
-        <option>export</option>, <option>json</option>, <option>json-pretty</option>,
-        <option>json-sse</option> and <option>json-seq</option>), as well as on <option>cat</option>. For the
-        former, the <literal>__CURSOR</literal>, <literal>__REALTIME_TIMESTAMP</literal>,
-        <literal>__MONOTONIC_TIMESTAMP</literal>, and <literal>_BOOT_ID</literal> fields are always
-        printed.</para></listitem>
+        <listitem><para>Show messages from a running, local container. Specify a container name to connect
+        to.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--utc</option></term>
+        <term><option>-m</option></term>
+        <term><option>--merge</option></term>
 
-        <listitem><para>Express time in Coordinated Universal Time
-        (UTC).</para></listitem>
+        <listitem><para>Show entries interleaved from all available journals, including remote
+        ones.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--no-hostname</option></term>
+        <term><option>-D <replaceable>DIR</replaceable></option></term>
+        <term><option>--directory=<replaceable>DIR</replaceable></option></term>
 
-        <listitem><para>Don't show the hostname field of log messages originating from the local host. This
-        switch has an effect only on the <option>short</option> family of output modes (see above).
-        </para>
+        <listitem><para>Takes a directory path as argument. If specified, journalctl will operate on the
+        specified journal directory <replaceable>DIR</replaceable> instead of the default runtime and system
+        journal paths.</para></listitem>
+      </varlistentry>
 
-        <para>Note: this option does not remove occurrences of the hostname from log entries themselves, so
-        it does not prevent the hostname from being visible in the logs.</para>
-        </listitem>
+      <varlistentry>
+        <term><option>--file=<replaceable>GLOB</replaceable></option></term>
+
+        <listitem><para>Takes a file glob as an argument. If specified, journalctl will operate on the
+        specified journal files matching <replaceable>GLOB</replaceable> instead of the default runtime and
+        system journal paths. May be specified multiple times, in which case files will be suitably
+        interleaved.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-x</option></term>
-        <term><option>--catalog</option></term>
+        <term><option>--root=<replaceable>ROOT</replaceable></option></term>
 
-        <listitem><para>Augment log lines with explanation texts from
-        the message catalog. This will add explanatory help texts to
-        log messages in the output where this is available. These
-        short help texts will explain the context of an error or log
-        event, possible solutions, as well as pointers to support
-        forums, developer documentation, and any other relevant
-        manuals. Note that help texts are not available for all
-        messages, but only for selected ones. For more information on
-        the message catalog, please refer to the
-        <ulink url="https://www.freedesktop.org/wiki/Software/systemd/catalog">Message Catalog Developer Documentation</ulink>.</para>
-
-        <para>Note: when attaching <command>journalctl</command>
-        output to bug reports, please do <emphasis>not</emphasis> use
-        <option>-x</option>.</para>
-        </listitem>
+        <listitem><para>Takes a directory path as an argument. If specified, <command>journalctl</command>
+        will operate on journal directories and catalog file hierarchy underneath the specified directory
+        instead of the root directory (e.g. <option>--update-catalog</option> will create
+        <filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>, and journal
+        files under <filename><replaceable>ROOT</replaceable>/run/journal/</filename> or
+        <filename><replaceable>ROOT</replaceable>/var/log/journal/</filename> will be displayed).
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-q</option></term>
-        <term><option>--quiet</option></term>
+        <term><option>--image=<replaceable>IMAGE</replaceable></option></term>
 
-        <listitem><para>Suppresses all informational messages
-        (i.e. "-- Journal begins at …", "-- Reboot --"),
-        any warning messages regarding
-        inaccessible system journals when run as a normal
-        user.</para></listitem>
+        <listitem><para>Takes a path to a disk image file or block device node. If specified,
+        <command>journalctl</command> will operate on the file system in the indicated disk image. This is
+        similar to <option>--root=</option> but operates on file systems stored in disk images or block
+        devices, thus providing an easy way to extract log data from disk images. The disk image should
+        either contain just a file system or a set of file systems within a GPT partition table, following
+        the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
+        Specification</ulink>. For further information on supported disk images, see
+        <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+        switch of the same name.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-m</option></term>
-        <term><option>--merge</option></term>
+        <term><option>--namespace=<replaceable>NAMESPACE</replaceable></option></term>
 
-        <listitem><para>Show entries interleaved from all available
-        journals, including remote ones.</para></listitem>
+        <listitem><para>Takes a journal namespace identifier string as argument. If not specified the data
+        collected by the default namespace is shown. If specified shows the log data of the specified
+        namespace instead. If the namespace is specified as <literal>*</literal> data from all namespaces is
+        shown, interleaved. If the namespace identifier is prefixed with <literal>+</literal> data from the
+        specified namespace and the default namespace is shown, interleaved, but no other. For details about
+        journal namespaces see
+        <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
 
+  <refsect1>
+    <title>Filtering Options</title>
+
+    <para>The following options control how to filter journal records:</para>
+
+    <variablelist>
       <varlistentry>
-        <term><option>-b <optional><optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional>|<constant>all</constant></optional></option></term>
-        <term><option>--boot<optional>=<optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional>|<constant>all</constant></optional></option></term>
+        <term><option>-S</option></term>
+        <term><option>--since=</option></term>
+        <term><option>-U</option></term>
+        <term><option>--until=</option></term>
 
-        <listitem><para>Show messages from a specific boot. This will
-        add a match for <literal>_BOOT_ID=</literal>.</para>
-
-        <para>The argument may be empty, in which case logs for the
-        current boot will be shown.</para>
-
-        <para>If the boot ID is omitted, a positive
-        <replaceable>offset</replaceable> will look up the boots
-        starting from the beginning of the journal, and an
-        equal-or-less-than zero <replaceable>offset</replaceable> will
-        look up boots starting from the end of the journal. Thus,
-        <constant>1</constant> means the first boot found in the
-        journal in chronological order, <constant>2</constant> the
-        second and so on; while <constant>-0</constant> is the last
-        boot, <constant>-1</constant> the boot before last, and so
-        on. An empty <replaceable>offset</replaceable> is equivalent
-        to specifying <constant>-0</constant>, except when the current
-        boot is not the last boot (e.g. because
-        <option>--directory</option> was specified to look at logs
-        from a different machine).</para>
-
-        <para>If the 32-character <replaceable>ID</replaceable> is
-        specified, it may optionally be followed by
-        <replaceable>offset</replaceable> which identifies the boot
-        relative to the one given by boot
-        <replaceable>ID</replaceable>. Negative values mean earlier
-        boots and positive values mean later boots. If
-        <replaceable>offset</replaceable> is not specified, a value of
-        zero is assumed, and the logs for the boot given by
-        <replaceable>ID</replaceable> are shown.</para>
-
-        <para>The special argument <constant>all</constant> can be
-        used to negate the effect of an earlier use of
-        <option>-b</option>.</para>
-        </listitem>
+        <listitem><para>Start showing entries on or newer than the specified date, or on or older than the
+        specified date, respectively. Date specifications should be of the format <literal>2012-10-30
+        18:17:16</literal>.  If the time part is omitted, <literal>00:00:00</literal> is assumed.  If only
+        the seconds component is omitted, <literal>:00</literal> is assumed. If the date component is
+        omitted, the current day is assumed. Alternatively the strings <literal>yesterday</literal>,
+        <literal>today</literal>, <literal>tomorrow</literal> are understood, which refer to 00:00:00 of the
+        day before the current day, the current day, or the day after the current day,
+        respectively. <literal>now</literal> refers to the current time. Finally, relative times may be
+        specified, prefixed with <literal>-</literal> or <literal>+</literal>, referring to times before or
+        after the current time, respectively. For complete time and date specification, see
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>. Note
+        that <option>--output=short-full</option> prints timestamps that follow precisely this format.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--list-boots</option></term>
+        <term><option>-c</option></term>
+        <term><option>--cursor=</option></term>
 
-        <listitem><para>Show a tabular list of boot numbers (relative to
-        the current boot), their IDs, and the timestamps of the first
-        and last message pertaining to the boot.</para></listitem>
+        <listitem><para>Start showing entries from the location in the journal specified by the passed
+        cursor.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-k</option></term>
-        <term><option>--dmesg</option></term>
+        <term><option>--after-cursor=</option></term>
 
-        <listitem><para>Show only kernel messages. This implies
-        <option>-b</option> and adds the match
-        <literal>_TRANSPORT=kernel</literal>.</para></listitem>
+        <listitem><para>Start showing entries from the location in the journal <emphasis>after</emphasis>
+        the location specified by the passed cursor.  The cursor is shown when the
+        <option>--show-cursor</option> option is used.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-t</option></term>
-        <term><option>--identifier=<replaceable>SYSLOG_IDENTIFIER</replaceable></option></term>
+        <term><option>--cursor-file=<replaceable>FILE</replaceable></option></term>
 
-        <listitem><para>Show messages for the specified syslog
-        identifier
-        <replaceable>SYSLOG_IDENTIFIER</replaceable>.</para>
+        <listitem><para>If <replaceable>FILE</replaceable> exists and contains a cursor, start showing
+        entries <emphasis>after</emphasis> this location.  Otherwise show entries according to the other
+        given options. At the end, write the cursor of the last entry to
+        <replaceable>FILE</replaceable>. Use this option to continually read the journal by sequentially
+        calling <command>journalctl</command>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-b <optional><optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional>|<constant>all</constant></optional></option></term>
+        <term><option>--boot<optional>=<optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional>|<constant>all</constant></optional></option></term>
+
+        <listitem><para>Show messages from a specific boot. This will add a match for
+        <literal>_BOOT_ID=</literal>.</para>
+
+        <para>The argument may be empty, in which case logs for the current boot will be shown.</para>
+
+        <para>If the boot ID is omitted, a positive <replaceable>offset</replaceable> will look up the boots
+        starting from the beginning of the journal, and an equal-or-less-than zero
+        <replaceable>offset</replaceable> will look up boots starting from the end of the journal. Thus,
+        <constant>1</constant> means the first boot found in the journal in chronological order,
+        <constant>2</constant> the second and so on; while <constant>-0</constant> is the last boot,
+        <constant>-1</constant> the boot before last, and so on. An empty <replaceable>offset</replaceable>
+        is equivalent to specifying <constant>-0</constant>, except when the current boot is not the last
+        boot (e.g. because <option>--directory</option> was specified to look at logs from a different
+        machine).</para>
+
+        <para>If the 32-character <replaceable>ID</replaceable> is specified, it may optionally be followed
+        by <replaceable>offset</replaceable> which identifies the boot relative to the one given by boot
+        <replaceable>ID</replaceable>. Negative values mean earlier boots and positive values mean later
+        boots. If <replaceable>offset</replaceable> is not specified, a value of zero is assumed, and the
+        logs for the boot given by <replaceable>ID</replaceable> are shown.</para>
 
-        <para>This parameter can be specified multiple
-        times.</para></listitem>
+        <para>The special argument <constant>all</constant> can be used to negate the effect of an earlier
+        use of <option>-b</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>-u</option></term>
         <term><option>--unit=<replaceable>UNIT</replaceable>|<replaceable>PATTERN</replaceable></option></term>
 
-        <listitem><para>Show messages for the specified systemd unit
-        <replaceable>UNIT</replaceable> (such as a service unit), or
-        for any of the units matched by
-        <replaceable>PATTERN</replaceable>.  If a pattern is
-        specified, a list of unit names found in the journal is
-        compared with the specified pattern and all that match are
-        used. For each unit name, a match is added for messages from
-        the unit
-        (<literal>_SYSTEMD_UNIT=<replaceable>UNIT</replaceable></literal>),
-        along with additional matches for messages from systemd and
-        messages about coredumps for the specified unit. A match
-        is also added for <literal>_SYSTEMD_SLICE=<replaceable>UNIT</replaceable></literal>,
-        such that if the provided <replaceable>UNIT</replaceable> is a
+        <listitem><para>Show messages for the specified systemd unit <replaceable>UNIT</replaceable> (such as
+        a service unit), or for any of the units matched by <replaceable>PATTERN</replaceable>.  If a pattern
+        is specified, a list of unit names found in the journal is compared with the specified pattern and
+        all that match are used. For each unit name, a match is added for messages from the unit
+        (<literal>_SYSTEMD_UNIT=<replaceable>UNIT</replaceable></literal>), along with additional matches for
+        messages from systemd and messages about coredumps for the specified unit. A match is also added for
+        <literal>_SYSTEMD_SLICE=<replaceable>UNIT</replaceable></literal>, such that if the provided
+        <replaceable>UNIT</replaceable> is a
         <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        unit, all logs of children of the slice will be shown.
-       </para>
+        unit, all logs of children of the slice will be shown.</para>
 
-        <para>This parameter can be specified multiple times.</para>
-        </listitem>
+        <para>This parameter can be specified multiple times.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--user-unit=</option></term>
 
-        <listitem><para>Show messages for the specified user session
-        unit. This will add a match for messages from the unit
-        (<literal>_SYSTEMD_USER_UNIT=</literal> and
-        <literal>_UID=</literal>) and additional matches for messages
-        from session systemd and messages about coredumps for the
-        specified unit. A match
-        is also added for <literal>_SYSTEMD_USER_SLICE=<replaceable>UNIT</replaceable></literal>,
-        such that if the provided <replaceable>UNIT</replaceable> is a
+        <listitem><para>Show messages for the specified user session unit. This will add a match for messages
+        from the unit (<literal>_SYSTEMD_USER_UNIT=</literal> and <literal>_UID=</literal>) and additional
+        matches for messages from session systemd and messages about coredumps for the specified unit. A
+        match is also added for <literal>_SYSTEMD_USER_SLICE=<replaceable>UNIT</replaceable></literal>, such
+        that if the provided <replaceable>UNIT</replaceable> is a
         <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         unit, all logs of children of the unit will be shown.</para>
 
-        <para>This parameter can be specified multiple times.</para>
-        </listitem>
+        <para>This parameter can be specified multiple times.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-t</option></term>
+        <term><option>--identifier=<replaceable>SYSLOG_IDENTIFIER</replaceable></option></term>
+
+        <listitem><para>Show messages for the specified syslog identifier
+        <replaceable>SYSLOG_IDENTIFIER</replaceable>.</para>
+
+        <para>This parameter can be specified multiple times.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>-p</option></term>
         <term><option>--priority=</option></term>
 
-        <listitem><para>Filter output by message priorities or
-        priority ranges. Takes either a single numeric or textual log
-        level (i.e. between 0/<literal>emerg</literal> and
-        7/<literal>debug</literal>), or a range of numeric/text log
-        levels in the form FROM..TO. The log levels are the usual
-        syslog log levels as documented in
-        <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-        i.e. <literal>emerg</literal> (0),
-        <literal>alert</literal> (1), <literal>crit</literal> (2),
-        <literal>err</literal> (3), <literal>warning</literal> (4),
-        <literal>notice</literal> (5), <literal>info</literal> (6),
-        <literal>debug</literal> (7). If a single log level is
-        specified, all messages with this log level or a lower (hence
-        more important) log level are shown. If a range is specified,
-        all messages within the range are shown, including both the
-        start and the end value of the range. This will add
-        <literal>PRIORITY=</literal> matches for the specified
+        <listitem><para>Filter output by message priorities or priority ranges. Takes either a single numeric
+        or textual log level (i.e. between 0/<literal>emerg</literal> and 7/<literal>debug</literal>), or a
+        range of numeric/text log levels in the form FROM..TO. The log levels are the usual syslog log levels
+        as documented in <citerefentry
+        project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+        i.e. <literal>emerg</literal> (0), <literal>alert</literal> (1), <literal>crit</literal> (2),
+        <literal>err</literal> (3), <literal>warning</literal> (4), <literal>notice</literal> (5),
+        <literal>info</literal> (6), <literal>debug</literal> (7). If a single log level is specified, all
+        messages with this log level or a lower (hence more important) log level are shown. If a range is
+        specified, all messages within the range are shown, including both the start and the end value of the
+        range. This will add <literal>PRIORITY=</literal> matches for the specified
         priorities.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--facility=</option></term>
 
-        <listitem><para>Filter output by syslog facility. Takes a comma-separated list of numbers or facility
-        names. The names are the usual syslog facilities as documented in
-        <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+        <listitem><para>Filter output by syslog facility. Takes a comma-separated list of numbers or
+        facility names. The names are the usual syslog facilities as documented in <citerefentry
+        project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
         <option>--facility=help</option> may be used to display a list of known facility names and exit.
         </para></listitem>
       </varlistentry>
         <term><option>-g</option></term>
         <term><option>--grep=</option></term>
 
-        <listitem><para>Filter output to entries where the <varname>MESSAGE=</varname>
-        field matches the specified regular expression. PERL-compatible regular expressions
-        are used, see
-        <citerefentry project='url'><refentrytitle url='http://pcre.org/current/doc/html/pcre2pattern.html'>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+        <listitem><para>Filter output to entries where the <varname>MESSAGE=</varname> field matches the
+        specified regular expression. PERL-compatible regular expressions are used, see <citerefentry
+        project='url'><refentrytitle
+        url='http://pcre.org/current/doc/html/pcre2pattern.html'>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry>
         for a detailed description of the syntax.</para>
 
-        <para>If the pattern is all lowercase, matching is case insensitive.
-        Otherwise, matching is case sensitive. This can be overridden with the
-        <option>--case-sensitive</option> option, see below.</para>
-        </listitem>
+        <para>If the pattern is all lowercase, matching is case insensitive.  Otherwise, matching is case
+        sensitive. This can be overridden with the <option>--case-sensitive</option> option, see
+        below.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--case-sensitive<optional>=BOOLEAN</optional></option></term>
 
-        <listitem><para>Make pattern matching case sensitive or case insensitive.</para>
-        </listitem>
+        <listitem><para>Make pattern matching case sensitive or case insensitive.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-c</option></term>
-        <term><option>--cursor=</option></term>
+        <term><option>-k</option></term>
+        <term><option>--dmesg</option></term>
 
-        <listitem><para>Start showing entries from the location in the
-        journal specified by the passed cursor.</para></listitem>
+        <listitem><para>Show only kernel messages. This implies <option>-b</option> and adds the match
+        <literal>_TRANSPORT=kernel</literal>.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Output Options</title>
+
+    <para>The following options control how journal records are printed:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>-o</option></term>
+        <term><option>--output=</option></term>
+
+        <listitem><para>Controls the formatting of the journal entries that are shown. Takes one of the
+        following options:</para>
+
+        <variablelist>
+          <varlistentry>
+            <term><option>short</option></term>
+            <listitem><para>is the default and generates an output that is mostly identical to the
+            formatting of classic syslog files, showing one line per journal entry.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>short-full</option></term>
+            <listitem><para>is very similar, but shows timestamps in the format the
+            <option>--since=</option> and <option>--until=</option> options accept. Unlike the timestamp
+            information shown in <option>short</option> output mode this mode includes weekday, year and
+            timezone information in the output, and is locale-independent.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>short-iso</option></term>
+            <listitem><para>is very similar, but shows ISO 8601 wallclock timestamps.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>short-iso-precise</option></term>
+            <listitem><para>as for <option>short-iso</option> but includes full microsecond
+            precision.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>short-precise</option></term>
+            <listitem><para>is very similar, but shows classic syslog timestamps with full microsecond
+            precision.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>short-monotonic</option></term>
+            <listitem><para>is very similar, but shows monotonic timestamps instead of wallclock
+            timestamps.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>short-unix</option></term>
+            <listitem><para>is very similar, but shows seconds passed since January 1st 1970 UTC instead of
+            wallclock timestamps ("UNIX time"). The time is shown with microsecond accuracy.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>verbose</option></term>
+            <listitem><para>shows the full-structured entry items with all fields.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>export</option></term>
+            <listitem><para>serializes the journal into a binary (but mostly text-based) stream suitable
+            for backups and network transfer (see <ulink
+            url="https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-export-format">Journal Export
+            Format</ulink> for more information). To import the binary stream back into native journald
+            format use
+            <citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>json</option></term>
+            <listitem><para>formats entries as JSON objects, separated by newline characters (see <ulink
+            url="https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-json-format">Journal JSON Format</ulink>
+            for more information). Field values are generally encoded as JSON strings, with three exceptions:
+            <orderedlist>
+              <listitem><para>Fields larger than 4096 bytes are encoded as <constant>null</constant>
+              values. (This may be turned off by passing <option>--all</option>, but be aware that this may
+              allocate overly long JSON objects.)</para></listitem>
+
+              <listitem><para>Journal entries permit non-unique fields within the same log entry. JSON does
+              not allow non-unique fields within objects. Due to this, if a non-unique field is encountered a
+              JSON array is used as field value, listing all field values as elements.</para></listitem>
+
+              <listitem><para>Fields containing non-printable or non-UTF8 bytes are encoded as arrays
+              containing the raw bytes individually formatted as unsigned numbers.</para></listitem>
+            </orderedlist>
+
+            Note that this encoding is reversible (with the exception of the size limit).</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>json-pretty</option></term>
+            <listitem><para>formats entries as JSON data structures, but formats them in multiple lines in
+            order to make them more readable by humans.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>json-sse</option></term>
+            <listitem><para>formats entries as JSON data structures, but wraps them in a format suitable for
+            <ulink
+            url="https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events">Server-Sent
+            Events</ulink>.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>json-seq</option></term>
+            <listitem><para>formats entries as JSON data structures, but prefixes them with an ASCII Record
+            Separator character (0x1E) and suffixes them with an ASCII Line Feed character (0x0A), in
+            accordance with <ulink url="https://tools.ietf.org/html/rfc7464">JavaScript Object Notation
+            (JSON) Text Sequences </ulink> (<literal>application/json-seq</literal>).</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>cat</option></term>
+            <listitem><para>generates a very terse output, only showing the actual message of each journal
+            entry with no metadata, not even a timestamp. If combined with the
+            <option>--output-fields=</option> option will output the listed fields for each log record,
+            instead of the message.</para></listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term><option>with-unit</option></term>
+            <listitem><para>similar to short-full, but prefixes the unit and user unit names instead of the
+            traditional syslog identifier. Useful when using templated instances, as it will include the
+            arguments in the unit names.</para></listitem>
+          </varlistentry>
+        </variablelist></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--cursor-file=<replaceable>FILE</replaceable></option></term>
+        <term><option>--output-fields=</option></term>
 
-        <listitem><para>If <replaceable>FILE</replaceable> exists and contains a
-        cursor, start showing entries <emphasis>after</emphasis> this location.
-        Otherwise the show entries according the other given options. At the end,
-        write the cursor of the last entry to <replaceable>FILE</replaceable>. Use
-        this option to continually read the journal by sequentially calling
-        <command>journalctl</command>.</para></listitem>
+        <listitem><para>A comma separated list of the fields which should be included in the output. This
+        has an effect only for the output modes which would normally show all fields
+        (<option>verbose</option>, <option>export</option>, <option>json</option>,
+        <option>json-pretty</option>, <option>json-sse</option> and <option>json-seq</option>), as well as
+        on <option>cat</option>. For the former, the <literal>__CURSOR</literal>,
+        <literal>__REALTIME_TIMESTAMP</literal>, <literal>__MONOTONIC_TIMESTAMP</literal>, and
+        <literal>_BOOT_ID</literal> fields are always printed.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--after-cursor=</option></term>
+        <term><option>-n</option></term>
+        <term><option>--lines=</option></term>
 
-        <listitem><para>Start showing entries from the location in the
-        journal <emphasis>after</emphasis> the location specified by
-        the passed cursor.  The cursor is shown when the
-        <option>--show-cursor</option> option is used.</para>
-        </listitem>
+        <listitem><para>Show the most recent journal events and limit the number of events shown. If
+        <option>--follow</option> is used, this option is implied. The argument is a positive integer or
+        <literal>all</literal> to disable line limiting. The default value is 10 if no argument is
+        given.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--show-cursor</option></term>
+        <term><option>-r</option></term>
+        <term><option>--reverse</option></term>
 
-        <listitem><para>The cursor is shown after the last entry after
-        two dashes:</para>
-        <programlisting>-- cursor: s=0639…</programlisting>
-        <para>The format of the cursor is private
-        and subject to change.</para></listitem>
+        <listitem><para>Reverse output so that the newest entries are displayed first.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-S</option></term>
-        <term><option>--since=</option></term>
-        <term><option>-U</option></term>
-        <term><option>--until=</option></term>
+        <term><option>--show-cursor</option></term>
 
-        <listitem><para>Start showing entries on or newer than the specified date, or on or older than the specified
-        date, respectively. Date specifications should be of the format <literal>2012-10-30 18:17:16</literal>.  If the
-        time part is omitted, <literal>00:00:00</literal> is assumed.  If only the seconds component is omitted,
-        <literal>:00</literal> is assumed. If the date component is omitted, the current day is assumed. Alternatively
-        the strings <literal>yesterday</literal>, <literal>today</literal>, <literal>tomorrow</literal> are understood,
-        which refer to 00:00:00 of the day before the current day, the current day, or the day after the current day,
-        respectively. <literal>now</literal> refers to the current time. Finally, relative times may be specified,
-        prefixed with <literal>-</literal> or <literal>+</literal>, referring to times before or after the current
-        time, respectively. For complete time and date specification, see
-        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>. Note that
-        <option>--output=short-full</option> prints timestamps that follow precisely this format.
-        </para>
-        </listitem>
+        <listitem><para>The cursor is shown after the last entry after two dashes:</para>
+        <programlisting>-- cursor: s=0639…</programlisting>
+        <para>The format of the cursor is private and subject to change.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-F</option></term>
-        <term><option>--field=</option></term>
+        <term><option>--utc</option></term>
 
-        <listitem><para>Print all possible data values the specified
-        field can take in all entries of the journal.</para></listitem>
+        <listitem><para>Express time in Coordinated Universal Time (UTC).</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-N</option></term>
-        <term><option>--fields</option></term>
+        <term><option>-x</option></term>
+        <term><option>--catalog</option></term>
 
-        <listitem><para>Print all field names currently used in all entries of the journal.</para></listitem>
+        <listitem><para>Augment log lines with explanation texts from the message catalog. This will add
+        explanatory help texts to log messages in the output where this is available. These short help texts
+        will explain the context of an error or log event, possible solutions, as well as pointers to support
+        forums, developer documentation, and any other relevant manuals. Note that help texts are not
+        available for all messages, but only for selected ones. For more information on the message catalog,
+        please refer to the <ulink url="https://www.freedesktop.org/wiki/Software/systemd/catalog">Message
+        Catalog Developer Documentation</ulink>.</para>
+
+        <para>Note: when attaching <command>journalctl</command> output to bug reports, please do
+        <emphasis>not</emphasis> use <option>-x</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--system</option></term>
-        <term><option>--user</option></term>
+        <term><option>--no-hostname</option></term>
 
-        <listitem><para>Show messages from system services and the
-        kernel (with <option>--system</option>). Show messages from
-        service of current user (with <option>--user</option>).  If
-        neither is specified, show all messages that the user can see.
-        </para></listitem>
+        <listitem><para>Don't show the hostname field of log messages originating from the local host. This
+        switch has an effect only on the <option>short</option> family of output modes (see above).</para>
+
+        <para>Note: this option does not remove occurrences of the hostname from log entries themselves, so
+        it does not prevent the hostname from being visible in the logs.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-M</option></term>
-        <term><option>--machine=</option></term>
+        <term><option>--no-full</option></term>
+        <term><option>--full</option></term>
+        <term><option>-l</option></term>
+
+        <listitem><para>Ellipsize fields when they do not fit in available columns.  The default is to show
+        full fields, allowing them to wrap or be truncated by the pager, if one is used.</para>
 
-        <listitem><para>Show messages from a running, local
-        container. Specify a container name to connect to.</para>
-        </listitem>
+        <para>The old options <option>-l</option>/<option>--full</option> are not useful anymore, except to
+        undo <option>--no-full</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>-D <replaceable>DIR</replaceable></option></term>
-        <term><option>--directory=<replaceable>DIR</replaceable></option></term>
+        <term><option>-a</option></term>
+        <term><option>--all</option></term>
 
-        <listitem><para>Takes a directory path as argument. If
-        specified, journalctl will operate on the specified journal
-        directory <replaceable>DIR</replaceable> instead of the
-        default runtime and system journal paths.</para></listitem>
+        <listitem><para>Show all fields in full, even if they include unprintable characters or are very
+        long. By default, fields with unprintable characters are abbreviated as "blob data". (Note that the
+        pager may escape unprintable characters again.)</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--file=<replaceable>GLOB</replaceable></option></term>
+        <term><option>-f</option></term>
+        <term><option>--follow</option></term>
 
-        <listitem><para>Takes a file glob as an argument. If
-        specified, journalctl will operate on the specified journal
-        files matching <replaceable>GLOB</replaceable> instead of the
-        default runtime and system journal paths. May be specified
-        multiple times, in which case files will be suitably
-        interleaved.</para></listitem>
+        <listitem><para>Show only the most recent journal entries, and continuously print new entries as
+        they are appended to the journal.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--root=<replaceable>ROOT</replaceable></option></term>
+        <term><option>--no-tail</option></term>
 
-        <listitem><para>Takes a directory path as an argument. If specified, <command>journalctl</command>
-        will operate on journal directories and catalog file hierarchy underneath the specified directory
-        instead of the root directory (e.g. <option>--update-catalog</option> will create
-        <filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>, and journal
-        files under <filename><replaceable>ROOT</replaceable>/run/journal/</filename> or
-        <filename><replaceable>ROOT</replaceable>/var/log/journal/</filename> will be displayed).
-        </para></listitem>
+        <listitem><para>Show all stored output lines, even in follow mode. Undoes the effect of
+        <option>--lines=</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--image=<replaceable>IMAGE</replaceable></option></term>
+        <term><option>-q</option></term>
+        <term><option>--quiet</option></term>
 
-        <listitem><para>Takes a path to a disk image file or block device node. If specified,
-        <command>journalctl</command> will operate on the file system in the indicated disk image. This is
-        similar to <option>--root=</option> but operates on file systems stored in disk images or block
-        devices, thus providing an easy way to extract log data from disk images. The disk image should
-        either contain just a file system or a set of file systems within a GPT partition table, following
-        the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
-        Specification</ulink>. For further information on supported disk images, see
-        <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
-        switch of the same name.</para></listitem>
+        <listitem><para>Suppresses all informational messages (i.e. "-- Journal begins at …", "-- Reboot
+        --"), any warning messages regarding inaccessible system journals when run as a normal
+        user.</para></listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Pager Control Options</title>
+
+    <para>The following options control page support:</para>
+
+    <variablelist>
+
+      <xi:include href="standard-options.xml" xpointer="no-pager" />
 
       <varlistentry>
-        <term><option>--namespace=<replaceable>NAMESPACE</replaceable></option></term>
+        <term><option>-e</option></term>
+        <term><option>--pager-end</option></term>
 
-        <listitem><para>Takes a journal namespace identifier string as argument. If not specified the data
-        collected by the default namespace is shown. If specified shows the log data of the specified
-        namespace instead. If the namespace is specified as <literal>*</literal> data from all namespaces is
-        shown, interleaved. If the namespace identifier is prefixed with <literal>+</literal> data from the
-        specified namespace and the default namespace is shown, interleaved, but no other. For details about
-        journal namespaces see
-        <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+        <listitem><para>Immediately jump to the end of the journal inside the implied pager tool. This
+        implies <option>-n1000</option> to guarantee that the pager will not buffer logs of unbounded
+        size. This may be overridden with an explicit <option>-n</option> with some other numeric value,
+        while <option>-nall</option> will disable this cap.  Note that this option is only supported for
+        the <citerefentry
+        project='man-pages'><refentrytitle>less</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        pager.</para></listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
 
-      <varlistentry>
-        <term><option>--header</option></term>
+  <refsect1>
+    <title>Forward Secure Sealing (FSS) Options</title>
 
-        <listitem><para>Instead of showing journal contents, show internal header information of the journal
-        fields accessed.</para>
+    <para>The following options make be used together with the <option>--setup-keys</option> command, see below.</para>
 
-        <para>This option is particularly useful when trying to identify out-of-order journal entries, as
-        happens for example when the machine is booted with the wrong system time.</para></listitem>
+    <variablelist>
+      <varlistentry>
+        <term><option>--interval=</option></term>
+
+        <listitem><para>Specifies the change interval for the sealing key when generating an FSS key pair
+        with <option>--setup-keys</option>. Shorter intervals increase CPU consumption but shorten the time
+        range of undetectable journal alterations. Defaults to 15min.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--disk-usage</option></term>
+        <term><option>--verify-key=</option></term>
 
-        <listitem><para>Shows the current disk usage of all journal
-        files. This shows the sum of the disk usage of all archived
-        and active journal files.</para></listitem>
+        <listitem><para>Specifies the FSS verification key to use for the <option>--verify</option>
+        operation.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--vacuum-size=</option></term>
-        <term><option>--vacuum-time=</option></term>
-        <term><option>--vacuum-files=</option></term>
+        <term><option>--force</option></term>
 
-        <listitem><para>Removes the oldest archived journal files until the disk space they use falls below the
-        specified size (specified with the usual <literal>K</literal>, <literal>M</literal>, <literal>G</literal> and
-        <literal>T</literal> suffixes), or all archived journal files contain no data older than the specified timespan
-        (specified with the usual <literal>s</literal>, <literal>m</literal>, <literal>h</literal>,
-        <literal>days</literal>, <literal>months</literal>, <literal>weeks</literal> and <literal>years</literal>
-        suffixes), or no more than the specified number of separate journal files remain. Note that running
-        <option>--vacuum-size=</option> has only an indirect effect on the output shown by
-        <option>--disk-usage</option>, as the latter includes active journal files, while the vacuuming operation only
-        operates on archived journal files. Similarly, <option>--vacuum-files=</option> might not actually reduce the
-        number of journal files to below the specified number, as it will not remove active journal
-        files.</para>
-
-        <para><option>--vacuum-size=</option>, <option>--vacuum-time=</option> and <option>--vacuum-files=</option>
-        may be combined in a single invocation to enforce any combination of a size, a time and a number of files limit
-        on the archived journal files. Specifying any of these three parameters as zero is equivalent to not enforcing
-        the specific limit, and is thus redundant.</para>
-
-        <para>These three switches may also be combined with <option>--rotate</option> into one command. If so, all
-        active files are rotated first, and the requested vacuuming operation is executed right after. The rotation has
-        the effect that all currently active files are archived (and potentially new, empty journal files opened as
-        replacement), and hence the vacuuming operation has the greatest effect as it can take all log data written so
-        far into account.</para></listitem>
+        <listitem><para>When <option>--setup-keys</option> is passed and Forward Secure Sealing (FSS) has
+        already been configured, recreate FSS keys.</para></listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
 
-      <varlistentry>
-        <term><option>--list-catalog
-        <optional><replaceable>128-bit-ID…</replaceable></optional>
-        </option></term>
+  <refsect1>
+    <title>Commands</title>
+
+    <para>The following commands are understood. If none is specified the default is to display journal records.</para>
 
-        <listitem><para>List the contents of the message catalog as a
-        table of message IDs, plus their short description strings.
-        </para>
+    <variablelist>
+      <varlistentry>
+        <term><option>-N</option></term>
+        <term><option>--fields</option></term>
 
-        <para>If any <replaceable>128-bit-ID</replaceable>s are
-        specified, only those entries are shown.</para>
-        </listitem>
+        <listitem><para>Print all field names currently used in all entries of the journal.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--dump-catalog
-        <optional><replaceable>128-bit-ID…</replaceable></optional>
-        </option></term>
-
-        <listitem><para>Show the contents of the message catalog, with
-        entries separated by a line consisting of two dashes and the
-        ID (the format is the same as <filename>.catalog</filename>
-        files).</para>
+        <term><option>-F</option></term>
+        <term><option>--field=</option></term>
 
-        <para>If any <replaceable>128-bit-ID</replaceable>s are
-        specified, only those entries are shown.</para>
-        </listitem>
+        <listitem><para>Print all possible data values the specified field can take in all entries of the
+        journal.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--update-catalog</option></term>
+        <term><option>--list-boots</option></term>
 
-        <listitem><para>Update the message catalog index. This command
-        needs to be executed each time new catalog files are
-        installed, removed, or updated to rebuild the binary catalog
-        index.</para></listitem>
+        <listitem><para>Show a tabular list of boot numbers (relative to the current boot), their IDs, and
+        the timestamps of the first and last message pertaining to the boot.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--setup-keys</option></term>
+        <term><option>--disk-usage</option></term>
 
-        <listitem><para>Instead of showing journal contents, generate
-        a new key pair for Forward Secure Sealing (FSS). This will
-        generate a sealing key and a verification key. The sealing key
-        is stored in the journal data directory and shall remain on
-        the host. The verification key should be stored
-        externally. Refer to the <option>Seal=</option> option in
-        <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        for information on Forward Secure Sealing and for a link to a
-        refereed scholarly paper detailing the cryptographic theory it
-        is based on.</para></listitem>
+        <listitem><para>Shows the current disk usage of all journal files. This shows the sum of the disk
+        usage of all archived and active journal files.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--force</option></term>
+        <term><option>--vacuum-size=</option></term>
+        <term><option>--vacuum-time=</option></term>
+        <term><option>--vacuum-files=</option></term>
+
+        <listitem><para>Removes the oldest archived journal files until the disk space they use falls below
+        the specified size (specified with the usual <literal>K</literal>, <literal>M</literal>,
+        <literal>G</literal> and <literal>T</literal> suffixes), or all archived journal files contain no
+        data older than the specified timespan (specified with the usual <literal>s</literal>,
+        <literal>m</literal>, <literal>h</literal>, <literal>days</literal>, <literal>months</literal>,
+        <literal>weeks</literal> and <literal>years</literal> suffixes), or no more than the specified
+        number of separate journal files remain. Note that running <option>--vacuum-size=</option> has only
+        an indirect effect on the output shown by <option>--disk-usage</option>, as the latter includes
+        active journal files, while the vacuuming operation only operates on archived journal
+        files. Similarly, <option>--vacuum-files=</option> might not actually reduce the number of journal
+        files to below the specified number, as it will not remove active journal files.</para>
 
-        <listitem><para>When <option>--setup-keys</option> is passed
-        and Forward Secure Sealing (FSS) has already been configured,
-        recreate FSS keys.</para></listitem>
+        <para><option>--vacuum-size=</option>, <option>--vacuum-time=</option> and
+        <option>--vacuum-files=</option> may be combined in a single invocation to enforce any combination
+        of a size, a time and a number of files limit on the archived journal files. Specifying any of
+        these three parameters as zero is equivalent to not enforcing the specific limit, and is thus
+        redundant.</para>
+
+        <para>These three switches may also be combined with <option>--rotate</option> into one command. If
+        so, all active files are rotated first, and the requested vacuuming operation is executed right
+        after. The rotation has the effect that all currently active files are archived (and potentially new,
+        empty journal files opened as replacement), and hence the vacuuming operation has the greatest effect
+        as it can take all log data written so far into account.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--interval=</option></term>
+        <term><option>--verify</option></term>
 
-        <listitem><para>Specifies the change interval for the sealing
-        key when generating an FSS key pair with
-        <option>--setup-keys</option>. Shorter intervals increase CPU
-        consumption but shorten the time range of undetectable journal
-        alterations. Defaults to 15min.</para></listitem>
+        <listitem><para>Check the journal file for internal consistency. If the file has been generated
+        with FSS enabled and the FSS verification key has been specified with
+        <option>--verify-key=</option>, authenticity of the journal file is verified.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--verify</option></term>
+        <term><option>--sync</option></term>
 
-        <listitem><para>Check the journal file for internal
-        consistency. If the file has been generated with FSS enabled and
-        the FSS verification key has been specified with
-        <option>--verify-key=</option>, authenticity of the journal file
-        is verified.</para></listitem>
+        <listitem><para>Asks the journal daemon to write all yet unwritten journal data to the backing file
+        system and synchronize all journals. This call does not return until the synchronization operation
+        is complete. This command guarantees that any log messages written before its invocation are safely
+        stored on disk at the time it returns.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--verify-key=</option></term>
+        <term><option>--relinquish-var</option></term>
 
-        <listitem><para>Specifies the FSS verification key to use for
-        the <option>--verify</option> operation.</para></listitem>
+        <listitem><para>Asks the journal daemon for the reverse operation to <option>--flush</option>: if
+        requested the daemon will write further log data to <filename>/run/log/journal/</filename> and
+        stops writing to <filename>/var/log/journal/</filename>. A subsequent call to
+        <option>--flush</option> causes the log output to switch back to
+        <filename>/var/log/journal/</filename>, see above.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--sync</option></term>
+        <term><option>--smart-relinquish-var</option></term>
 
-        <listitem><para>Asks the journal daemon to write all yet
-        unwritten journal data to the backing file system and
-        synchronize all journals. This call does not return until the
-        synchronization operation is complete. This command guarantees
-        that any log messages written before its invocation are safely
-        stored on disk at the time it returns.</para></listitem>
+        <listitem><para>Similar to <option>--relinquish-var</option> but executes no operation if the root
+        file system and <filename>/var/lib/journal/</filename> reside on the same mount point. This
+        operation is used during system shutdown in order to make the journal daemon stop writing data to
+        <filename>/var/log/journal/</filename> in case that directory is located on a mount point that
+        needs to be unmounted.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--relinquish-var</option></term>
+        <term><option>--rotate</option></term>
 
-        <listitem><para>Asks the journal daemon for the reverse operation to <option>--flush</option>: if
-        requested the daemon will write further log data to <filename>/run/log/journal/</filename> and stops
-        writing to <filename>/var/log/journal/</filename>. A subsequent call to <option>--flush</option>
-        causes the log output to switch back to <filename>/var/log/journal/</filename>, see
-        above.</para></listitem>
+        <listitem><para>Asks the journal daemon to rotate journal files. This call does not return until
+        the rotation operation is complete. Journal file rotation has the effect that all currently active
+        journal files are marked as archived and renamed, so that they are never written to in future. New
+        (empty) journal files are then created in their place. This operation may be combined with
+        <option>--vacuum-size=</option>, <option>--vacuum-time=</option> and
+        <option>--vacuum-file=</option> into a single command, see above.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--smart-relinquish-var</option></term>
+        <term><option>--header</option></term>
 
-        <listitem><para>Similar to <option>--relinquish-var</option> but executes no operation if the root file
-        system and <filename>/var/lib/journal/</filename> reside on the same mount point. This operation is
-        used during system shutdown in order to make the journal daemon stop writing data to
-        <filename>/var/log/journal/</filename> in case that directory is located on a mount point that needs
-        to be unmounted.</para></listitem>
+        <listitem><para>Instead of showing journal contents, show internal header information of the
+        journal fields accessed.</para>
+
+        <para>This option is particularly useful when trying to identify out-of-order journal entries, as
+        happens for example when the machine is booted with the wrong system time.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--rotate</option></term>
+        <term><option>--list-catalog <optional><replaceable>128-bit-ID…</replaceable></optional></option></term>
 
-        <listitem><para>Asks the journal daemon to rotate journal files. This call does not return until the rotation
-        operation is complete. Journal file rotation has the effect that all currently active journal files are marked
-        as archived and renamed, so that they are never written to in future. New (empty) journal files are then
-        created in their place. This operation may be combined with <option>--vacuum-size=</option>,
-        <option>--vacuum-time=</option> and <option>--vacuum-file=</option> into a single command, see
-        above.</para></listitem>
+        <listitem><para>List the contents of the message catalog as a table of message IDs, plus their
+        short description strings.</para>
+
+        <para>If any <replaceable>128-bit-ID</replaceable>s are specified, only those entries are
+        shown.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--dump-catalog <optional><replaceable>128-bit-ID…</replaceable></optional></option></term>
+
+        <listitem><para>Show the contents of the message catalog, with entries separated by a line
+        consisting of two dashes and the ID (the format is the same as <filename>.catalog</filename>
+        files).</para>
+
+        <para>If any <replaceable>128-bit-ID</replaceable>s are specified, only those entries are
+        shown.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--update-catalog</option></term>
+
+        <listitem><para>Update the message catalog index. This command needs to be executed each time new
+        catalog files are installed, removed, or updated to rebuild the binary catalog
+        index.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--setup-keys</option></term>
+
+        <listitem><para>Instead of showing journal contents, generate a new key pair for Forward Secure
+        Sealing (FSS). This will generate a sealing key and a verification key. The sealing key is stored in
+        the journal data directory and shall remain on the host. The verification key should be stored
+        externally. Refer to the <option>Seal=</option> option in
+        <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        information on Forward Secure Sealing and for a link to a refereed scholarly paper detailing the
+        cryptographic theory it is based on.</para></listitem>
       </varlistentry>
 
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
-      <xi:include href="standard-options.xml" xpointer="no-pager" />
     </variablelist>
   </refsect1>
 
   <refsect1>
     <title>Exit status</title>
 
-    <para>On success, 0 is returned; otherwise, a non-zero failure
-    code is returned.</para>
+    <para>On success, 0 is returned; otherwise, a non-zero failure code is returned.</para>
   </refsect1>
 
   <xi:include href="common-variables.xml" />
   <refsect1>
     <title>Examples</title>
 
-    <para>Without arguments, all collected logs are shown
-    unfiltered:</para>
+    <para>Without arguments, all collected logs are shown unfiltered:</para>
 
     <programlisting>journalctl</programlisting>
 
-    <para>With one match specified, all entries with a field matching
-    the expression are shown:</para>
+    <para>With one match specified, all entries with a field matching the expression are shown:</para>
 
     <programlisting>journalctl _SYSTEMD_UNIT=avahi-daemon.service
 journalctl _SYSTEMD_CGROUP=/user.slice/user-42.slice/session-c1.scope</programlisting>
 
-    <para>If two different fields are matched, only entries matching
-    both expressions at the same time are shown:</para>
+    <para>If two different fields are matched, only entries matching both expressions at the same time are
+    shown:</para>
 
     <programlisting>journalctl _SYSTEMD_UNIT=avahi-daemon.service _PID=28097</programlisting>
 
-    <para>If two matches refer to the same field, all entries matching
-    either expression are shown:</para>
+    <para>If two matches refer to the same field, all entries matching either expression are shown:</para>
 
     <programlisting>journalctl _SYSTEMD_UNIT=avahi-daemon.service _SYSTEMD_UNIT=dbus.service</programlisting>
 
-    <para>If the separator <literal>+</literal> is used, two
-    expressions may be combined in a logical OR. The following will
-    show all messages from the Avahi service process with the PID
-    28097 plus all messages from the D-Bus service (from any of its
-    processes):</para>
+    <para>If the separator <literal>+</literal> is used, two expressions may be combined in a logical OR. The
+    following will show all messages from the Avahi service process with the PID 28097 plus all messages from
+    the D-Bus service (from any of its processes):</para>
 
     <programlisting>journalctl _SYSTEMD_UNIT=avahi-daemon.service _PID=28097 + _SYSTEMD_UNIT=dbus.service</programlisting>
 
-    <para>To show all fields emitted <emphasis>by</emphasis> a unit and <emphasis>about</emphasis>
-    the unit, option <option>-u</option>/<option>--unit=</option> should be used.
-    <command>journalctl -u <replaceable>name</replaceable></command>
-    expands to a complex filter similar to
+    <para>To show all fields emitted <emphasis>by</emphasis> a unit and <emphasis>about</emphasis> the unit,
+    option <option>-u</option>/<option>--unit=</option> should be used. <command>journalctl -u
+    <replaceable>name</replaceable></command> expands to a complex filter similar to
+
     <programlisting>_SYSTEMD_UNIT=<replaceable>name</replaceable>.service
   + UNIT=<replaceable>name</replaceable>.service _PID=1
   + OBJECT_SYSTEMD_UNIT=<replaceable>name</replaceable>.service _UID=0
-  + COREDUMP_UNIT=<replaceable>name</replaceable>.service _UID=0 MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1
-    </programlisting>
-    (see <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-    for an explanation of those patterns).
-    </para>
+  + COREDUMP_UNIT=<replaceable>name</replaceable>.service _UID=0 MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1</programlisting>
+
+    (see
+    <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    for an explanation of those patterns).</para>
 
     <para>Show all logs generated by the D-Bus executable:</para>
 
@@ -1049,11 +908,9 @@ journalctl _SYSTEMD_CGROUP=/user.slice/user-42.slice/session-c1.scope</programli
 
     <programlisting>journalctl -k -b -1</programlisting>
 
-    <para>Show a live log display from a system service
-    <filename>apache.service</filename>:</para>
+    <para>Show a live log display from a system service <filename>apache.service</filename>:</para>
 
     <programlisting>journalctl -f -u apache</programlisting>
-
   </refsect1>
 
   <refsect1>
index 7ad7f86c6da2348475152cb4b5e8aabe508bd18b..f6a6c05126d4aceb223763855cc188cd9d8e892b 100644 (file)
 
       <para><varname>$KERNEL_INSTALL_MACHINE_ID</varname> is set for the plugins to the desired machine-id to
       use. It's always a 128-bit ID. Normally it's read from <filename>/etc/machine-id</filename>, but it can
-      also be overriden via <varname>$MACHINE_ID</varname> (see below).  If not specified via these methods a
+      also be overridden via <varname>$MACHINE_ID</varname> (see below).  If not specified via these methods a
       fallback value will generated by <command>kernel-install</command>, and used only for a single
       invocation.</para>
 
index 3606de5704b784d9bf3d1b9ed5d080437c0db4b7..3ee42cdb7369db82c277921cab40c1e829909478 100644 (file)
         Currently, only x86 is supported, where it uses the PC speaker.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term>secure-boot-enroll</term>
+
+        <listitem><para>Danger: this feature might soft-brick your device if used improperly.</para>
+
+        <para>Takes one of <literal>off</literal>, <literal>manual</literal> or <literal>force</literal>.
+        Controls the enrollment of secure boot keys. If set to <literal>off</literal>, no action whatsoever
+        is taken. If set to <literal>manual</literal> (the default) and the UEFI firmware is in setup-mode
+        then entries to manually enroll Secure Boot variables are created in the boot menu. If set to
+        <literal>force</literal>, in addition, if a directory named <filename>/loader/keys/auto/</filename>
+        exists on the ESP then the keys in that directory are enrolled automatically.</para>
+
+        <para>The different sets of variables can be set up under <filename>/loader/keys/<replaceable>NAME</replaceable></filename>
+        where <replaceable>NAME</replaceable> is the name that is going to be used as the name of the entry.
+        This allows to ship multiple sets of Secure Boot variables and choose which one to enroll at runtime.</para>
+
+        <para>Supported secure boot variables are one database for authorized images, one key exchange key (KEK)
+        and one platform key (PK). For more information, refer to the <ulink url="https://uefi.org/specifications">UEFI specification</ulink>,
+        under Secure Boot and Driver Signing. Another resource that describe the interplay of the different variables is the
+        <ulink url="https://edk2-docs.gitbook.io/understanding-the-uefi-secure-boot-chain/secure_boot_chain_in_uefi/uefi_secure_boot">
+        EDK2 documentation</ulink>.</para>
+
+        <para>A complete set of UEFI variable includes <filename>db.auth</filename>, <filename>KEK.auth</filename>
+        and <filename>PK.auth</filename>. Note that these files need to be authenticated UEFI variables. See
+        below for an example of how to generate them from regular X.509 keys.</para>
+
+        <programlisting>uuid=$(systemd-id128 new --uuid)
+for key in PK KEK db; do
+  openssl req -new -x509 -subj "/CN=${key}/" -keyout "${key}.key" -out "${key}.crt"
+  openssl x509 -outform DER -in "${key}.crt" -out "${key}.cer"
+  cert-to-efi-sig-list -g "${uuid}" "${key}.crt" "${key}.esl"
+done
+
+for key in MicWinProPCA2011_2011-10-19.crt MicCorUEFCA2011_2011-06-27.crt MicCorKEKCA2011_2011-06-24.crt; do
+  curl "https://www.microsoft.com/pkiops/certs/${key}" --output "${key}"
+  sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output "${key%crt}esl" "${key}"
+done
+
+# Optionally add Microsoft Windows Production CA 2011 (needed to boot into Windows).
+cat MicWinProPCA2011_2011-10-19.esl >> db.esl
+
+# Optionally add Microsoft Corporation UEFI CA 2011 (for firmware drivers / option ROMs
+# and third-party boot loaders (including shim). This is highly recommended on real
+# hardware as not including this may soft-brick your device (see next paragraph).
+cat MicCorUEFCA2011_2011-06-27.esl >> db.esl
+
+# Optionally add Microsoft Corporation KEK CA 2011. Recommended if either of the
+# Microsoft keys is used as the official UEFI revocation database is signed with this
+# key. The revocation database can be updated with <citerefentry><refentrytitle>fwupdmgr</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+cat MicCorKEKCA2011_2011-06-24.esl >> KEK.esl
+
+sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth
+sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth
+sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth
+        </programlisting>
+
+        <para>This feature is considered dangerous because even if all the required files are signed with the
+        keys being loaded, some files necessary for the system to function properly still won't be. This
+        is especially the case with Option ROMs (e.g. for storage controllers or graphics cards). See
+        <ulink url="https://github.com/Foxboron/sbctl/wiki/FAQ#option-rom">Secure Boot and Option ROMs</ulink>
+        for more details.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term>reboot-for-bitlocker</term>
 
index 7ecb8885a25a5e93e54ca2c37aeeff8d529f29a4..5bc82e5d1a741f6fbfeb0b56eb05fdb5f37cd8e8 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><command>copy-to</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
+        <term><command>copy-to</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>] <option>--force</option></term>
 
         <listitem><para>Copies files or directories from the host
         system into a running container. Takes a container name,
       </varlistentry>
 
       <varlistentry>
-        <term><command>copy-from</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
+        <term><command>copy-from</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>] <option>--force</option></term>
 
         <listitem><para>Copies files or directories from a container
         into the host system. Takes a container name, followed by the
index 946041acfc74846c3ccfe4645a8a80dd6cdbe913..c366c1b19a2ab6287383228007d5e3e744064ba1 100644 (file)
@@ -116,6 +116,14 @@ node /org/freedesktop/machine1 {
       CopyToMachine(in  s name,
                     in  s source,
                     in  s destination);
+      CopyFromMachineWithFlags(in  s name,
+                               in  s source,
+                               in  s destination,
+                               in  t flags);
+      CopyToMachineWithFlags(in  s name,
+                             in  s source,
+                             in  s destination,
+                             in  t flags);
       OpenMachineRootDirectory(in  s name,
                                out h fd);
       GetMachineUIDShift(in  s name,
@@ -176,6 +184,8 @@ node /org/freedesktop/machine1 {
 
     <!--method UnregisterMachine is not documented!-->
 
+    <!--method CopyToMachineWithFlags is not documented!-->
+
     <!--method OpenMachineRootDirectory is not documented!-->
 
     <!--method GetMachineUIDShift is not documented!-->
@@ -236,6 +246,10 @@ node /org/freedesktop/machine1 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="CopyToMachine()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="CopyFromMachineWithFlags()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CopyToMachineWithFlags()"/>
+
     <variablelist class="dbus-method" generated="True" extra-ref="OpenMachineRootDirectory()"/>
 
     <variablelist class="dbus-method" generated="True" extra-ref="GetMachineUIDShift()"/>
@@ -394,8 +408,10 @@ node /org/freedesktop/machine1 {
 
       <para><function>CopyFromMachine()</function> copies files or directories from a container into the
       host. It takes a container name, a source directory in the container and a destination directory on the
-      host as arguments. <function>CopyToMachine()</function> does the opposite and copies files from a source
-      directory on the host into a destination directory in the container.</para>
+      host as arguments.
+      <function>CopyToMachine()</function> does the opposite and copies files from a source
+      directory on the host into a destination directory in the container.
+      <function>CopyFromMachineWithFlags()</function> and <function>CopyToMachineWithFlags</function> do the same but take an additional flags argument.</para>
 
       <para><function>RemoveImage()</function> removes the image with the specified name.</para>
 
@@ -465,6 +481,12 @@ node /org/freedesktop/machine1/machine/rawhide {
                in  s destination);
       CopyTo(in  s source,
              in  s destination);
+      CopyFromWithFlags(in  s source,
+                        in  s destination,
+                        in  t flags);
+      CopyToWithFlags(in  s source,
+                      in  s destination,
+                      in  t flags);
       OpenRootDirectory(out h fd);
     properties:
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@@ -510,6 +532,10 @@ node /org/freedesktop/machine1/machine/rawhide {
 
     <!--method CopyTo is not documented!-->
 
+    <!--method CopyFromWithFlags is not documented!-->
+
+    <!--method CopyToWithFlags is not documented!-->
+
     <!--method OpenRootDirectory is not documented!-->
 
     <!--Autogenerated cross-references for systemd.directives, do not edit-->
@@ -540,6 +566,10 @@ node /org/freedesktop/machine1/machine/rawhide {
 
     <variablelist class="dbus-method" generated="True" extra-ref="CopyTo()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="CopyFromWithFlags()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CopyToWithFlags()"/>
+
     <variablelist class="dbus-method" generated="True" extra-ref="OpenRootDirectory()"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="Name"/>
index 838eb6738d8d3645b5dec24d5fa8a55069513e20..c6b8c7fb3d65fea3d156e370cfbc149d0f129c75 100644 (file)
@@ -39,6 +39,9 @@ node /org/freedesktop/oom1 {
   interface org.freedesktop.oom1.Manager {
     methods:
       DumpByFileDescriptor(out h fd);
+    signals:
+      Killed(s cgroup,
+             s reason);
   };
   interface org.freedesktop.DBus.Peer { ... };
   interface org.freedesktop.DBus.Introspectable { ... };
@@ -56,12 +59,38 @@ node /org/freedesktop/oom1 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="DumpByFileDescriptor()"/>
 
+    <variablelist class="dbus-signal" generated="True" extra-ref="Killed"/>
+
     <!--End of Autogenerated section-->
 
     <refsect2>
       <title>Methods</title>
 
-      <para>...</para>
+      <para><function>Killed</function> signal is sent when any cgroup is killed by oomd.</para>
+      <para>Note that more reasons will be added in the future, and the table below will be expanded accordingly.</para>
+      <table>
+        <title>Killing reasons</title>
+        <tgroup cols="2" align="left" colsep="1" rowsep="1">
+        <colspec colname="reason"/>
+        <colspec colname="description"/>
+        <thead>
+          <row>
+            <entry>Reason</entry>
+            <entry>Description</entry>
+          </row>
+        </thead>
+        <tbody>
+          <row>
+            <entry>memory-used</entry>
+            <entry>Application took too much memory and swap.</entry>
+          </row>
+          <row>
+            <entry>memory-pressure</entry>
+            <entry>Application took enough memory and swap to cause sufficient slowdown of other applications.</entry>
+          </row>
+        </tbody>
+        </tgroup>
+      </table>
     </refsect2>
   </refsect1>
 
index 7803cc9cf9334c125493029d7dbf0018bf139f78..120ffbc8ef08edd43ab0d37f2f4416057f2285ae 100644 (file)
@@ -426,6 +426,8 @@ node /org/freedesktop/systemd1 {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t DefaultTimeoutAbortUSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultDeviceTimeoutUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t DefaultRestartUSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly t DefaultStartLimitIntervalUSec = ...;
@@ -674,6 +676,8 @@ node /org/freedesktop/systemd1 {
 
     <!--property DefaultTimeoutAbortUSec is not documented!-->
 
+    <!--property DefaultDeviceTimeoutUSec is not documented!-->
+
     <!--property DefaultRestartUSec is not documented!-->
 
     <!--property DefaultStartLimitIntervalUSec is not documented!-->
@@ -1082,6 +1086,8 @@ node /org/freedesktop/systemd1 {
 
     <variablelist class="dbus-property" generated="True" extra-ref="DefaultTimeoutAbortUSec"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultDeviceTimeoutUSec"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="DefaultRestartUSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="DefaultStartLimitIntervalUSec"/>
@@ -1650,6 +1656,11 @@ node /org/freedesktop/systemd1 {
       that the root path is encoded as the empty string here (not as <literal>/</literal>!), so that it can be
       appended to <filename>/sys/fs/cgroup/systemd</filename> easily. This value will be set to the empty
       string for the host instance and some other string for container instances.</para>
+
+      <para><varname>AccessSELinuxContext</varname> contains the SELinux context that is used to control
+      access to the unit. It's read from the unit file when it is loaded and cached until the service manager
+      is reloaded. This property contains an empty string if SELinux is not used or if no label could be read
+      (for example because the unit is not backed by a file on disk).</para>
     </refsect2>
 
     <refsect2>
@@ -1783,6 +1794,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s Description = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s AccessSELinuxContext = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s LoadState = '...';
       readonly s ActiveState = '...';
       readonly s FreezerState = '...';
@@ -2090,6 +2103,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="Description"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="AccessSELinuxContext"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="LoadState"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="ActiveState"/>
index c5a53ae7eb596da304a0dee197c81f459e82064f..168c1675a9491d63a87d09ab98c06ea4c996d420 100644 (file)
           not</emphasis> provided.</para>
 
           <para>For example, <literal>SUPPORT_END=2001-01-01</literal> means that the system was supported
-          until the end of the last day of the previous millenium.</para></listitem>
+          until the end of the last day of the previous millennium.</para></listitem>
         </varlistentry>
 
         <varlistentry>
           determines the fallback hostname.</para></listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>ARCHITECTURE=</varname></term>
+          <listitem><para>A string that specifies which CPU architecture the userspace binaries require.
+          The architecture identifiers are the same as for <varname>ConditionArchitecture=</varname>
+          described in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          The field is optional and should only be used when just single architecture is supported.
+          It may provide redundant information when used in a GPT partition with a GUID type that already
+          encodes the architecture. If this is not the case, the architecture should be specified in
+          e.g., an extension image, to prevent an incompatible host from loading it.
+          </para></listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>SYSEXT_LEVEL=</varname></term>
 
           <listitem><para>Takes a space-separated list of one or more valid prefix match strings for the
           <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> logic. This field
           serves two purposes: it is informational, identifying portable service images as such (and thus
-          allowing them to be distinguished from other OS images, such as bootable system images). In is also
+          allowing them to be distinguished from other OS images, such as bootable system images). It is also
           used when a portable service image is attached: the specified or implied portable service prefix is
           checked against the list specified here, to enforce restrictions how images may be attached to a
           system.</para></listitem>
index 277ab5e5933fceba14730c5df4553ac73da0fd38..4f3fe0da7c74aa44727a11ab0704cccdcaacb716 100644 (file)
@@ -953,6 +953,7 @@ manpages = [
    'systemd-makefs',
    'systemd-mkswap@.service'],
   ''],
+ ['systemd-measure', '1', [], 'HAVE_GNU_EFI'],
  ['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'],
  ['systemd-mount', '1', ['systemd-umount'], ''],
  ['systemd-network-generator.service', '8', ['systemd-network-generator'], ''],
index 01d1dd022ca022c6e5a2d7fb32f1484f9099d449..7a302d5980fe1be4189dd30258e0d7f95e60db96 100644 (file)
@@ -8,9 +8,9 @@
 <refsect1>
 
 <para id="singular">This option is only available for system services, or for services running in per-user
- instances of the service manager when unprivileged user namespaces are available.</para>
+ instances of the service manager when <varname>PrivateUsers=</varname> is enabled.</para>
 
 <para id="plural">These options are only available for system services, or for services running in per-user
- instances of the service manager when unprivileged user namespaces are available.</para>
+ instances of the service manager when <varname>PrivateUsers=</varname> is enabled.</para>
 
 </refsect1>
index 42dd667aa9a59e152eacf33e62a07cdc2e93d607..6fd9d910d9b3818a8c8be1018d075574a5754fc2 100644 (file)
@@ -108,6 +108,25 @@ To show all installed unit files use 'systemctl list-unit-files'.</programlistin
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><command>list-automounts</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
+
+          <listitem>
+            <para>List automount units currently in memory, ordered by mount path.  If one or more
+            <replaceable>PATTERN</replaceable>s are specified, only automount units matching one of them are shown.
+            Produces output similar to
+            <programlisting>
+WHAT        WHERE                    MOUNTED IDLE TIMEOUT UNIT
+/dev/sdb1   /mnt/test                no      120s         mnt-test.automount
+binfmt_misc /proc/sys/fs/binfmt_misc yes     0            proc-sys-fs-binfmt_misc.automount
+
+2 automounts listed.</programlisting>
+            </para>
+
+            <para>Also see <option>--show-types</option>, <option>--all</option>, and <option>--state=</option>.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><command>list-sockets</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
 
@@ -198,25 +217,24 @@ Sun 2017-02-26 20:57:49 EST  2h 3min left  Sun 2017-02-26 11:56:36 EST  6h ago
             <option>--type=</option>, <option>--state=</option>, or <option>--failed</option> are used, units
             are additionally filtered by the TYPE and ACTIVE state.</para>
 
-            <para>This function is intended to generate human-readable
-            output. If you are looking for computer-parsable output,
-            use <command>show</command> instead. By default, this
-            function only shows 10 lines of output and ellipsizes
-            lines to fit in the terminal window. This can be changed
-            with <option>--lines</option> and <option>--full</option>,
-            see above. In addition, <command>journalctl
-            --unit=<replaceable>NAME</replaceable></command> or
-            <command>journalctl
-            --user-unit=<replaceable>NAME</replaceable></command> use
-            a similar filter for messages and might be more
-            convenient.
-            </para>
-
-            <para>systemd implicitly loads units as necessary, so just running the <command>status</command> will
-            attempt to load a file. The command is thus not useful for determining if something was already loaded or
-            not.  The units may possibly also be quickly unloaded after the operation is completed if there's no reason
-            to keep it in memory thereafter.
-            </para>
+            <para>This function is intended to generate human-readable output. If you are looking for
+            computer-parsable output, use <command>show</command> instead. By default, this function only
+            shows 10 lines of output and ellipsizes lines to fit in the terminal window. This can be changed
+            with <option>--lines</option> and <option>--full</option>, see above. In addition,
+            <command>journalctl --unit=<replaceable>NAME</replaceable></command> or <command>journalctl
+            --user-unit=<replaceable>NAME</replaceable></command> use a similar filter for messages and might
+            be more convenient.</para>
+
+            <para>Note that this operation only displays <emphasis>runtime</emphasis> status, i.e. information about
+            the current invocation of the unit (if it is running) or the most recent invocation (if it is not
+            running anymore, and has not been released from memory). Information about earlier invocations,
+            invocations from previous system boots, or prior invocations that have already been released from
+            memory may be retrieved via <command>journalctl --unit=</command>.</para>
+
+            <para>systemd implicitly loads units as necessary, so just running the <command>status</command>
+            will attempt to load a file. The command is thus not useful for determining if something was
+            already loaded or not.  The units may possibly also be quickly unloaded after the operation is
+            completed if there's no reason to keep it in memory thereafter.</para>
 
             <example>
               <title>Example output from systemctl status </title>
index 393c0312fa3c9831f615790e403f751fcbe7cdfc..99a749b32705db6dd0432b4aeacb4ce08e5ceaf1 100644 (file)
@@ -55,6 +55,9 @@
       <listitem><para>The EFI Shell binary, if installed.</para></listitem>
 
       <listitem><para>A reboot into the UEFI firmware setup option, if supported by the firmware.</para></listitem>
+
+      <listitem><para>Secure boot variables enrollement if the UEFI firmware is in setup-mode and files are provided
+      on the ESP.</para></listitem>
     </itemizedlist>
 
     <para><command>systemd-boot</command> supports the following features:</para>
@@ -91,6 +94,9 @@
       <listitem><para>The boot manager optionally reads a random seed from the ESP partition, combines it
       with a 'system token' stored in a persistent EFI variable and derives a random seed to use by the OS as
       entropy pool initialization, providing a full entropy pool during early boot.</para></listitem>
+
+      <listitem><para>The boot manager allows for secure boot variables to be enrolled if the UEFI firmware is
+      in setup-mode. Additionally, variables can be automatically enrolled if configured.</para></listitem>
     </itemizedlist>
 
     <para><citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     extension of the EFI architecture ID followed by <filename>.efi</filename> (e.g. for x86-64 this means a
     suffix of <filename>x64.efi</filename>). This may be used to automatically load file system drivers and
     similar, to extend the native firmware support.</para>
+
+    <para>Enrollment of Secure Boot variables can be performed manually or automatically if files are available
+    under <filename>/keys/<replaceable>NAME</replaceable>/{db,KEK,PK}.auth</filename>, <replaceable>NAME</replaceable>
+    being the display name for the set of variables in the menu. If one of the sets is named <filename>auto</filename>
+    then it might be enrolled automatically depending on whether <literal>secure-boot-enroll</literal> is set
+    to force or not.</para>
   </refsect1>
 
   <refsect1>
index d803b5c12717586c22828367ae914b30339e6967..7592961f63306b03030d8bca3d60205269fdb197 100644 (file)
         <term><command>has-tpm2</command></term>
 
         <listitem><para>Reports whether the system is equipped with a TPM2 device usable for protecting
-        credentials. If the a TPM2 device has been discovered, is supported, and is being used by firmware,
+        credentials. If a TPM2 device has been discovered, is supported, and is being used by firmware,
         by the OS kernel drivers and by userspace (i.e. systemd) this prints <literal>yes</literal> and exits
         with exit status zero. If no such device is discovered/supported/used, prints
         <literal>no</literal>. Otherwise prints <literal>partial</literal>. In either of these two cases
index 8e1b9857a72aaeb354ee03e89d587baf98d44524..afde0fad7e2575e6fe6d3bd17983b017e0e4b992 100644 (file)
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--unlock-key-file=</option><replaceable>PATH</replaceable></term>
+
+        <listitem><para>Use a file instead of a password/passphrase read from stdin to unlock the volume.
+        Expects the PATH to the file containing your key to unlock the volume. Currently there is nothing like
+        <option>--key-file-offset=</option> or <option>--key-file-size=</option> so this file has to only
+        contain the full key.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
 
                 <entry>The IMA project measures its runtime state into this PCR.</entry>
               </row>
 
+              <row>
+                <entry>11</entry>
+                <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. Unlike PCR 4 (where the same data should be measured into), this PCR value should be easy to pre-calculate, as this only contains static parts of the PE binary. Use this PCR to bind TPM policies to a specific kernel image, possibly with an embedded initial RAM disk (initrd).</entry>
+              </row>
+
               <row>
                 <entry>12</entry>
                 <entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any specified kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
               </row>
 
+              <row>
+                <entry>13</entry>
+                <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> images it loads and passed to the booted kernel into this PCR.</entry>
+              </row>
+
               <row>
                 <entry>14</entry>
                 <entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>
index 4cdac2b013fb757b57c74ebac47e30d109a4bd69..6430501bdf0e1ad756e3359ceb1737bbe85b7317 100644 (file)
@@ -28,6 +28,9 @@
     <cmdsynopsis>
       <command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--mount</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
     </cmdsynopsis>
+    <cmdsynopsis>
+      <command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--umount</option> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
+    </cmdsynopsis>
     <cmdsynopsis>
       <command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--copy-from</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="plain"><replaceable>PATH</replaceable></arg> <arg choice="opt"><replaceable>TARGET</replaceable></arg></command>
     </cmdsynopsis>
@@ -40,7 +43,7 @@
     <title>Description</title>
 
     <para><command>systemd-dissect</command> is a tool for introspecting and interacting with file system OS
-    disk images. It supports four different operations:</para>
+    disk images. It supports five different operations:</para>
 
     <orderedlist>
       <listitem><para>Show general OS image information, including the image's
       mount the included partitions according to their designation onto a directory and possibly
       sub-directories.</para></listitem>
 
+      <listitem><para>Unmount an OS image from a local directory. In this mode it will recursively unmount
+      the mounted partitions and remove the underlying loop device, including all the partition sub-devices.
+      </para></listitem>
+
       <listitem><para>Copy files and directories in and out of an OS image.</para></listitem>
     </orderedlist>
 
         multiple nested mounts are established. This command expects two arguments: a path to an image file
         and a path to a directory where to mount the image.</para>
 
-        <para>To unmount an OS image mounted like this use <citerefentry
-        project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
-        <option>-R</option> switch (for recursive operation), so that the OS image and all nested partition
-        mounts are unmounted.</para>
+        <para>To unmount an OS image mounted like this use the <option>--umount</option> operation.</para>
 
         <para>When the OS image contains LUKS encrypted or Verity integrity protected file systems
         appropriate volumes are automatically set up and marked for automatic disassembly when the image is
         <listitem><para>This is a shortcut for <option>--mount --mkdir</option>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--umount</option></term>
+        <term><option>-u</option></term>
+
+        <listitem><para>Unmount an OS image from the specified directory. This command expects one argument:
+        a directory where an OS image was mounted.</para>
+
+        <para>All mounted partitions will be recursively unmounted, and the underlying loop device will be
+        removed, along with all it's partition sub-devices.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-U</option></term>
+
+        <listitem><para>This is a shortcut for <option>--umount --rmdir</option>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--copy-from</option></term>
         <term><option>-x</option></term>
         unmounted again.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--rmdir</option></term>
+
+        <listitem><para>If combined with <option>--umount</option> the specified directory where the OS image
+        is mounted is removed after unmounting the OS image.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--discard=</option></term>
 
index 23eab015f6fdc291b5520a9bae0cd48fdceb26e5..44248b2e80159c0da557d5caba1ac452c1acc3e9 100644 (file)
@@ -41,7 +41,7 @@
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-integritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
-      <citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml
new file mode 100644 (file)
index 0000000..e6b8d31
--- /dev/null
@@ -0,0 +1,154 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+<refentry id="systemd-measure" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='HAVE_GNU_EFI'>
+
+  <refentryinfo>
+    <title>systemd-measure</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-measure</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-measure</refname>
+    <refpurpose>Pre-calculate expected TPM2 PCR values for booted unified kernel images</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>/usr/lib/systemd/systemd-measure <arg choice="opt" rep="repeat">OPTIONS</arg></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>Note: this command is experimental for now. While it is likely to become a regular component of
+    systemd, it might still change in behaviour and interface.</para>
+
+    <para><command>systemd-measure</command> is a tool that may be used to pre-calculate the expected TPM2
+    PCR 11 values that should be seen when a unified Linux kernel image based on
+    <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> is
+    booted up. It accepts paths to the ELF kernel image file, initial ram disk image file, devicetree file,
+    kernel command line file,
+    <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file, and
+    boot splash file that make up the unified kernel image, and determines the PCR values expected to be in
+    place after booting the image. Calculation starts with a zero-initialized PCR 11, and is executed in a
+    fashion compatible with what <filename>systemd-stub</filename> does at boot.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Commands</title>
+
+    <para>The following commands are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><command>status</command></term>
+
+        <listitem><para>This is the default command if none is specified. This queries the local system's
+        TPM2 PCR 11+12+13 values and displays them. The data is written in a similar format as the
+        <command>calculate</command> command below, and may be used to quickly compare expectation with
+        reality.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><command>calculate</command></term>
+
+        <listitem><para>Pre-calculate the expected value seen in PCR register 11 after boot-up of a unified
+        kernel image consisting of the components specified with <option>--linux=</option>,
+        <option>--osrel=</option>, <option>--cmdline=</option>, <option>--initrd=</option>,
+        <option>--splash=</option>, <option>--dtb=</option>, see below. Only <option>--linux=</option> is
+        mandatory.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>--linux=PATH</option></term>
+        <term><option>--osrel=PATH</option></term>
+        <term><option>--cmdline=PATH</option></term>
+        <term><option>--initrd=PATH</option></term>
+        <term><option>--splash=PATH</option></term>
+        <term><option>--dtb=PATH</option></term>
+
+        <listitem><para>When used with the <command>calculate</command> verb, configures the files to read
+        the unified kernel image components from. Each option corresponds with the equally named section in
+        the unified kernel PE file. The <option>--linux=</option> switch expects the path to the ELF kernel
+        file that the unified PE kernel will wrap. All switches except <option>--linux=</option> are
+        optional. Each option may be used at most once.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--bank=DIGEST</option></term>
+
+        <listitem><para>Controls the PCR banks to pre-calculate the PCR values for – in case
+        <command>calculate</command> is invoked –, or the banks to show in the <command>status</command>
+        output. May be used more then once to specify multiple banks. If not specified, defaults to the four
+        banks <literal>sha1</literal>, <literal>sha256</literal>, <literal>sha384</literal>,
+        <literal>sha512</literal>.</para></listitem>
+      </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Generate a unified kernel image, and calculate the expected TPM PCR 11 value</title>
+
+      <programlisting># objcopy \
+    --add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
+    --add-section .osrel=os-release.txt --change-section-vma .osrel=0x20000 \
+    --add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
+    --add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
+    --add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
+    --add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
+    /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
+    foo.efi
+# systemd-measure calculate \
+     --linux=vmlinux \
+     --osrel=os-release \
+     --cmdline=cmdline.txt \
+     --initrd=initrd.cpio \
+     --splash=splash.bmp \
+     --dtb=devicetree.dtb
+11:sha1=d775a7b4482450ac77e03ee19bda90bd792d6ec7
+11:sha256=bc6170f9ce28eb051ab465cd62be8cf63985276766cf9faf527ffefb66f45651
+11:sha384=1cf67dff4757e61e5a73d2a21a6694d668629bbc3761747d493f7f49ad720be02fd07263e1f93061243aec599d1ee4b4
+11:sha512=8e79acd3ddbbc8282e98091849c3530f996303c8ac8e87a3b2378b71c8b3a6e86d5c4f41ecea9e1517090c3e8ec0c714821032038f525f744960bcd082d937da
+</programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>Exit status</title>
+
+    <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+     </para>
+  </refsect1>
+
+</refentry>
index 380ba57884a3da0e03230c2b4713f7d6c1e932f2..c34244d14a7e0ff3df2590698f6b587ccd4bd30f 100644 (file)
@@ -71,7 +71,9 @@
 
     <orderedlist>
       <listitem><para>The <filename>repart.d/*.conf</filename> configuration files are loaded and parsed,
-      and ordered by filename (without the directory prefix).</para></listitem>
+      and ordered by filename (without the directory prefix). For each configuration file,
+      drop-in files are looked for in directories with same name as the configuration file
+      with a suffix ".d" added.</para></listitem>
 
       <listitem><para>The partition table already existing on the block device is loaded and
       parsed.</para></listitem>
         <listitem><para>Takes a file system path. If specified the <filename>*.conf</filename> files are read
         from the specified directory instead of searching in <filename>/usr/lib/repart.d/*.conf</filename>,
         <filename>/etc/repart.d/*.conf</filename>,
-        <filename>/run/repart.d/*.conf</filename>.</para></listitem>
+        <filename>/run/repart.d/*.conf</filename>.</para>
+
+        <para>This parameter can be specified multiple times.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index a7e08f0c80174fb8221af70e74558de68602c2f5..7f30fa6536275daf10aeb62b5ce5727d2c9f9354 100644 (file)
@@ -315,11 +315,11 @@ search foobar.com barbar.com
     <itemizedlist>
       <listitem><para><command>systemd-resolved</command> maintains the
       <filename>/run/systemd/resolve/stub-resolv.conf</filename> file for compatibility with traditional
-      Linux programs. This file may be symlinked from <filename>/etc/resolv.conf</filename>. This file lists
-      the 127.0.0.53 DNS stub (see above) as the only DNS server. It also contains a list of search domains
-      that are in use by systemd-resolved. The list of search domains is always kept up-to-date. Note that
-      <filename>/run/systemd/resolve/stub-resolv.conf</filename> should not be used directly by applications,
-      but only through a symlink from <filename>/etc/resolv.conf</filename>. This file may be symlinked from
+      Linux programs. This file lists the 127.0.0.53 DNS stub (see above) as the only DNS server. It also
+      contains a list of search domains that are in use by systemd-resolved. The list of search domains is
+      always kept up-to-date. Note that <filename>/run/systemd/resolve/stub-resolv.conf</filename> should not
+      be used directly by applications, but only through a symlink from
+      <filename>/etc/resolv.conf</filename>. This file may be symlinked from
       <filename>/etc/resolv.conf</filename> in order to connect all local clients that bypass local DNS APIs
       to <command>systemd-resolved</command> with correct search domains settings. This mode of operation is
       recommended.</para></listitem>
index 0c01ecf3ffcf9ae515c864abefe48b8c76988023..8b9ddd6e7dd04431f0fc7aa53f9be95f80ed476c 100644 (file)
         <term>suspend-then-hibernate</term>
 
         <listitem><para>A low power state where the system is initially suspended
-        (the state is stored in RAM). If not interrupted within the delay specified by
-        <command>HibernateDelaySec=</command>, the system will be woken using an RTC
-        alarm and hibernated (the state is then stored on disk).
+        (the state is stored in RAM) and then hibernated based on battery percentage
+        capacity. If the current battery capacity is higher than 5%, the system goes
+        back to suspend for interval calculated using battery discharge rate per hour.
+        Battery discharge rate per hour is stored in a file which is created after
+        initial suspend-resume cycle. The value is calculated using battery decreasing
+        charge level a timespan. In case of manual wakeup before RTC alarm, the timespan
+        is the duration for which system was suspended. If the file does not exist or
+        has invalid value, initial suspend duration is set to
+        <command>HibernateDelaySec=</command>.
+        After wakeup via an RTC alarm, the battery discharge rate per hour is again
+        estimated. If the current battery charge level is equal to or less than 5%,
+        the system will be hibernated (the state is then stored on disk) else the
+        system goes back to suspend for the amount of time it would take to fully
+        discharge the battery minus 30 minutes.
         </para></listitem>
       </varlistentry>
 
       </varlistentry>
       <varlistentry>
         <term><varname>HibernateDelaySec=</varname></term>
-
-        <listitem><para>The amount of time the system spends in suspend mode before the system is
-        automatically put into hibernate mode, when using
+        <listitem><para>The amount of time the system spends in suspend mode
+        before the RTC alarm wakes the system, before the battery discharge rate
+        can be estimated and used instead to calculate the suspension interval.
         <citerefentry><refentrytitle>systemd-suspend-then-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. Defaults
         to 2h.</para></listitem>
       </varlistentry>
index 92a20e259ed34c3814a4d8bba451b87d3761d34e..1e9bb5d631ba16308b49a5e62ca1a4709d49b184 100644 (file)
       <listitem><para>The ELF Linux kernel images will be looked for in the <literal>.linux</literal> PE
       section of the executed image.</para></listitem>
 
+      <listitem><para>OS release information, i.e. the
+      <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file of
+      the OS the kernel belongs to, in the <literal>.osrel</literal> PE section.</para></listitem>
+
       <listitem><para>The initial RAM disk (initrd) will be looked for in the <literal>.initrd</literal> PE
       section.</para></listitem>
 
@@ -76,6 +80,9 @@
     <para>If a DeviceTree is embedded in the <literal>.dtb</literal> section, it replaces an existing
     DeviceTree in the corresponding EFI configuration table. systemd-stub will ask the firmware via the
     <literal>EFI_DT_FIXUP_PROTOCOL</literal> for hardware specific fixups to the DeviceTree.</para>
+
+    <para>The contents of these six PE sections are measured into TPM PCR 11, that is otherwise not
+    used. Thus, it can be pre-calculated without too much effort.</para>
   </refsect1>
 
   <refsect1>
       images to the initrd. See
       <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
       details on system extension images. The generated <command>cpio</command> archive containing these
-      system extension images is measured into TPM PCR 4 (if a TPM is present).</para></listitem>
+      system extension images is measured into TPM PCR 13 (if a TPM is present).</para></listitem>
 
       <listitem><para>Files <filename>/loader/credentials/*.cred</filename> are packed up in a
       <command>cpio</command> archive and placed in the <filename>/.extra/global_credentials/</filename>
     core kernel, the embedded initrd and kernel command line (see above for a full list).</para>
 
     <para>Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means
-    every type of initrd will be measured twice: the initrd embedded in the kernel image will be measured to
-    both PCR 4 and PCR 9; the initrd synthesized from credentials will be measured to both PCR 12 and PCR 9;
-    the initrd synthesized from system extensions will be measured to both PCR 4 and PCR 9. Let's summarize
-    the OS resources and the PCRs they are measured to:</para>
+    every type of initrd will be measured two or three times: the initrd embedded in the kernel image will be
+    measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials will be measured to both PCR
+    9 and PCR 12; the initrd synthesized from system extensions will be measured to both PCR 4 and PCR
+    9. Let's summarize the OS resources and the PCRs they are measured to:</para>
 
     <table>
       <title>OS Resource PCR Summary</title>
 
           <row>
             <entry>Boot splash (embedded in the unified PE binary)</entry>
-            <entry>4</entry>
+            <entry>4 + 11</entry>
           </row>
 
           <row>
             <entry>Core kernel code (embedded in unified PE binary)</entry>
-            <entry>4</entry>
+            <entry>4 + 11</entry>
           </row>
 
           <row>
             <entry>Main initrd (embedded in unified PE binary)</entry>
-            <entry>4 + 9</entry>
+            <entry>4 + 9 + 11</entry>
           </row>
 
           <row>
             <entry>Default kernel command line (embedded in unified PE binary)</entry>
-            <entry>4</entry>
+            <entry>4 + 11</entry>
           </row>
 
           <row>
 
           <row>
             <entry>Credentials (synthesized initrd from companion files)</entry>
-            <entry>12 + 9</entry>
+            <entry>9 + 12</entry>
           </row>
 
           <row>
             <entry>System Extensions (synthesized initrd from companion files)</entry>
-            <entry>4 + 9</entry>
+            <entry>9 + 13</entry>
           </row>
         </tbody>
       </tgroup>
         <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view
         this data.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>StubPcrKernelImage</varname></term>
+
+        <listitem><para>The PCR register index the ELF kernel image/initial RAM disk image/boot
+        splash/devicetree database/embedded command line are measured into, formatted as decimal ASCII string
+        (i.e. <literal>11</literal>). This variable is set if a measurement was successfully completed, and
+        remains unset otherwise.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>StubPcrKernelParameters</varname></term>
+
+        <listitem><para>The PCR register index the kernel command line and credentials are measured into,
+        formatted as decimal ASCII string (i.e. <literal>12</literal>). This variable is set if a measurement
+        was successfully completed, and remains unset otherwise.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>StubPcrInitRDSysExts</varname></term>
+
+        <listitem><para>The PCR register index the systemd extensions for the initial RAM disk image, which
+        are picked up from the file system the kernel image is located on.  Formatted as decimal ASCII string
+        (i.e. <literal>13</literal>). This variable is set if a measurement was successfully completed, and
+        remains unset otherwise.</para></listitem>
+      </varlistentry>
     </variablelist>
 
     <para>Note that some of the variables above may also be set by the boot loader. The stub will only set
index 751aa2b09e07563579dd9e24156d37c51ed0ffe1..fede8b092d730036c662bbb6f34f8c87f7d06582 100644 (file)
           <para>Only apply rules with the specified prefix.</para>
         </listitem>
       </varlistentry>
+      <varlistentry id='strict'>
+        <term><option>--strict=</option></term>
+        <listitem>
+          <para>Always return non-zero exit code on failure (including invalid sysctl variable
+          name and insufficient permissions), unless the sysctl variable name is prefixed with a "-"
+          character.</para>
+        </listitem>
+      </varlistentry>
 
       <xi:include href="standard-options.xml" xpointer="cat-config" />
       <xi:include href="standard-options.xml" xpointer="no-pager" />
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>Credentials</title>
+
+    <para><command>systemd-sysctl</command> supports the service credentials logic as implemented by
+    <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
+    <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    details). The following credentials are used when passed in:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><literal>sysctl.extra</literal></term>
+
+        <listitem><para>The contents of this credential may contain additional lines to operate on. The
+        credential contents should follow the same format as any other <filename>sysctl.d/</filename> drop-in
+        configuration file. If this credential is passed it is processed after all of the drop-in files read
+        from the file system. The settings configured in the credential hence take precedence over those in
+        the file system.</para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>Note that by default the <filename>systemd-sysctl.service</filename> unit file is set up to inherit
+    the <literal>sysctl.extra</literal> credential from the service manager.</para>
+  </refsect1>
+
   <refsect1>
     <title>Examples</title>
 
@@ -122,7 +154,7 @@ kernel.core_pattern = |/usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t %P %I
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <citerefentry project='man-pages'><refentrytitle>sysctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>sysctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 60ae4aa8bf0d5c1f4d55a95b4525dc1053abda81..f4dd150a538eb62da08f11d2eabf0e47de23d113 100644 (file)
@@ -64,7 +64,7 @@
 
     <para>System extensions are supposed to be purely additive, i.e. they are supposed to include only files
     that do not exist in the underlying basic OS image. However, the underlying mechanism (overlayfs) also
-    allows removing files, but it is recommended not to make use of this.</para>
+    allows overlaying or removing files, but it is recommended not to make use of this.</para>
 
     <para>System extension images may be provided in the following formats:</para>
 
     accessed.</para>
 
     <para>Note that there is no concept of enabling/disabling installed system extension images: all
-    installed extension images are automatically activated at boot.</para>
+    installed extension images are automatically activated at boot. However, you can place an empty directory
+    named like the extension (no <filename>.raw</filename>) in <filename>/etc/extensions/</filename> to "mask"
+    an extension with the same name in a system folder with lower precedence.</para>
 
     <para>A simple mechanism for version compatibility is enforced: a system extension image must carry a
     <filename>/usr/lib/extension-release.d/extension-release.<replaceable>$name</replaceable></filename>
     file, which must match its image name, that is compared with the host <filename>os-release</filename>
-    file: the contained <varname>ID=</varname> fields have to match, as well as the
-    <varname>SYSEXT_LEVEL=</varname> field (if defined). If the latter is not defined, the
-    <varname>VERSION_ID=</varname> field has to match instead. System extensions should not ship a
-    <filename>/usr/lib/os-release</filename> file (as that would be merged into the host
-    <filename>/usr/</filename> tree, overriding the host OS version data, which is not desirable). The
-    <filename>extension-release</filename> file follows the same format and semantics, and carries the same
+    file: the contained <varname>ID=</varname> fields have to match unless <literal>_any</literal> is set
+    for the extension. If the extension <varname>ID=</varname> is not <literal>_any</literal>, the
+    <varname>SYSEXT_LEVEL=</varname> field (if defined) has to match. If the latter is not defined, the
+    <varname>VERSION_ID=</varname> field has to match instead. If the extension defines the
+    <varname>ARCHITECTURE=</varname> field and the value is not <literal>_any</literal> it has to match the kernel's
+    architecture reported by <citerefentry><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+    but the used architecture identifiers are the same as for <varname>ConditionArchitecture=</varname>
+    described in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+    System extensions should not ship a <filename>/usr/lib/os-release</filename> file (as that would be merged
+    into the host <filename>/usr/</filename> tree, overriding the host OS version data, which is not desirable).
+    The <filename>extension-release</filename> file follows the same format and semantics, and carries the same
     content, as the <filename>os-release</filename> file of the OS, but it describes the resources carried
     in the extension image.</para>
   </refsect1>
index ef311f1971fbb046b04ccee337f8894a4b0f1127..065bbd5a647091f4f3f80bf64398667c19a435b8 100644 (file)
         100ms.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>DefaultDeviceTimeoutSec=</varname></term>
+
+        <listitem><para>Configures the default timeout for waiting for devices. It can be changed per
+        device via the <varname>x-systemd.device-timeout=</varname> option in <filename>/etc/fstab</filename>
+        and <filename>/etc/crypttab</filename> (see
+        <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+        <citerefentry><refentrytitle>crypttab</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+        Defaults to 90s.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>DefaultStartLimitIntervalSec=</varname></term>
         <term><varname>DefaultStartLimitBurst=</varname></term>
index 7da7b18dcf95fac696693036c8c36cf24528f0e0..9011cdb7552f1bc3e78b74b78031d5c6c8112965 100644 (file)
 
         <listitem><para>Specifies the shell binary to use for the specified account when creating it.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><literal>sysusers.extra</literal></term>
+
+        <listitem><para>The contents of this credential may contain additional lines to operate on. The
+        credential contents should follow the same format as any other <filename>sysusers.d/</filename>
+        drop-in. If this credential is passed it is processed after all of the drop-in files read from the
+        file system.</para></listitem>
+      </varlistentry>
     </variablelist>
 
     <para>Note that by default the <filename>systemd-sysusers.service</filename> unit file is set up to
     inherit the <literal>passwd.hashed-password.root</literal>,
-    <literal>passwd.plaintext-password.root</literal> and <literal>passwd.shell.root</literal> credentials
-    from the service manager. Thus, when invoking a container with an unpopulated <filename>/etc/</filename>
-    for the first time it is possible to configure the root user's password to be <literal>systemd</literal>
-    like this:</para>
+    <literal>passwd.plaintext-password.root</literal>, <literal>passwd.shell.root</literal> and
+    <literal>sysusers.extra</literal> credentials from the service manager. Thus, when invoking a container
+    with an unpopulated <filename>/etc/</filename> 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=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
+    <para>Note again that the data specified in this credential 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
     exists.</para>
 
index 425ed23dd35e91793d89769c7011d9b6bcdb225c..92ab322ba0ebb67471a7f404f2c33ad63b0e42a5 100644 (file)
     <programlisting>systemd-tmpfiles --remove --create</programlisting>
   </refsect1>
 
+  <refsect1>
+    <title>Credentials</title>
+
+    <para><command>systemd-tmpfiles</command> supports the service credentials logic as implemented by
+    <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
+    <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    details). The following credentials are used when passed in:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><literal>tmpfiles.extra</literal></term>
+
+        <listitem><para> The contents of this credential may contain additional lines to operate on. The
+        credential contents should follow the same format as any other <filename>tmpfiles.d/</filename>
+        drop-in configuration file. If this credential is passed it is processed after all of the drop-in
+        files read from the file system. The lines in the credential can hence augment existing lines of the
+        OS, but not override them.</para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>Note that by default the <filename>systemd-tmpfiles-setup.service</filename> unit file (and related
+    unit files) is set up to inherit the <literal>tmpfiles.extra</literal> credential from the service
+    manager.</para>
+  </refsect1>
+
   <refsect1>
     <title>Environment</title>
 
index 7e062313dc0a1d9a54880f86a1b507c90ef76bea..f90a1e25d0d9d607d9a39af52ff205fb3bdb27a8 100644 (file)
@@ -819,13 +819,13 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
 
         <listitem><para>Set soft and hard limits on various resources for executed processes. See
         <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
-        details on the resource limit concept. Resource limits may be specified in two formats: either as
-        single value to set a specific soft and hard limit to the same value, or as colon-separated pair
-        <option>soft:hard</option> to set both limits individually (e.g. <literal>LimitAS=4G:16G</literal>).
-        Use the string <option>infinity</option> to configure no limit on a specific resource. The
-        multiplicative suffixes K, M, G, T, P and E (to the base 1024) may be used for resource limits
-        measured in bytes (e.g. <literal>LimitAS=16G</literal>). For the limits referring to time values, the
-        usual time units ms, s, min, h and so on may be used (see
+        details on the process resource limit concept. Process resource limits may be specified in two formats:
+        either as single value to set a specific soft and hard limit to the same value, or as colon-separated
+        pair <option>soft:hard</option> to set both limits individually
+        (e.g. <literal>LimitAS=4G:16G</literal>).  Use the string <option>infinity</option> to configure no
+        limit on a specific resource. The multiplicative suffixes K, M, G, T, P and E (to the base 1024) may
+        be used for resource limits measured in bytes (e.g. <literal>LimitAS=16G</literal>). For the limits
+        referring to time values, the usual time units ms, s, min, h and so on may be used (see
         <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
         details). Note that if no time unit is specified for <varname>LimitCPU=</varname> the default unit of
         seconds is implied, while for <varname>LimitRTTIME=</varname> the default unit of microseconds is
@@ -875,15 +875,17 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         <table>
           <title>Resource limit directives, their equivalent <command>ulimit</command> shell commands and the unit used</title>
 
-          <tgroup cols='3'>
+          <tgroup cols='4'>
             <colspec colname='directive' />
             <colspec colname='equivalent' />
             <colspec colname='unit' />
+            <colspec colname='notes' />
             <thead>
               <row>
                 <entry>Directive</entry>
                 <entry><command>ulimit</command> equivalent</entry>
                 <entry>Unit</entry>
+                <entry>Notes</entry>
               </row>
             </thead>
             <tbody>
@@ -891,81 +893,97 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
                 <entry>LimitCPU=</entry>
                 <entry>ulimit -t</entry>
                 <entry>Seconds</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitFSIZE=</entry>
                 <entry>ulimit -f</entry>
                 <entry>Bytes</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitDATA=</entry>
                 <entry>ulimit -d</entry>
                 <entry>Bytes</entry>
+                <entry>Don't use. This limits the allowed address range, not memory use! Defaults to unlimited and should not be lowered. To limit memory use, see <varname>MemoryMax=</varname> in <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
               </row>
               <row>
                 <entry>LimitSTACK=</entry>
                 <entry>ulimit -s</entry>
                 <entry>Bytes</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitCORE=</entry>
                 <entry>ulimit -c</entry>
                 <entry>Bytes</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitRSS=</entry>
                 <entry>ulimit -m</entry>
                 <entry>Bytes</entry>
+                <entry>Don't use. No effect on Linux.</entry>
               </row>
               <row>
                 <entry>LimitNOFILE=</entry>
                 <entry>ulimit -n</entry>
                 <entry>Number of File Descriptors</entry>
+                <entry>Don't use. Be careful when raising the soft limit above 1024, since <function>select()</function> cannot function with file descriptors above 1023 on Linux. Nowadays, the hard limit defaults to 524288, a very high value compared to historical defaults. Typically applications should increase their soft limit to the hard limit on their own, if they are OK with working with file descriptors above 1023, i.e. do not use <function>select()</function>. Note that file descriptors are nowadays accounted like any other form of memory, thus there should not be any need to lower the hard limit. Use <varname>MemoryMax=</varname> to control overall service memory use, including file descriptor memory.</entry>
               </row>
               <row>
                 <entry>LimitAS=</entry>
                 <entry>ulimit -v</entry>
                 <entry>Bytes</entry>
+                <entry>Don't use. This limits the allowed address range, not memory use! Defaults to unlimited and should not be lowered. To limit memory use, see <varname>MemoryMax=</varname> in <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
               </row>
               <row>
                 <entry>LimitNPROC=</entry>
                 <entry>ulimit -u</entry>
                 <entry>Number of Processes</entry>
+                <entry>This limit is enforced based on the number of processes belonging to the user. Typically it's better to track processes per service, i.e. use <varname>TasksMax=</varname>, see <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
               </row>
               <row>
                 <entry>LimitMEMLOCK=</entry>
                 <entry>ulimit -l</entry>
                 <entry>Bytes</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitLOCKS=</entry>
                 <entry>ulimit -x</entry>
                 <entry>Number of Locks</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitSIGPENDING=</entry>
                 <entry>ulimit -i</entry>
                 <entry>Number of Queued Signals</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitMSGQUEUE=</entry>
                 <entry>ulimit -q</entry>
                 <entry>Bytes</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitNICE=</entry>
                 <entry>ulimit -e</entry>
                 <entry>Nice Level</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitRTPRIO=</entry>
                 <entry>ulimit -r</entry>
                 <entry>Realtime Priority</entry>
+                <entry>-</entry>
               </row>
               <row>
                 <entry>LimitRTTIME=</entry>
-                <entry>No equivalent</entry>
+                <entry>ulimit -R</entry>
                 <entry>Microseconds</entry>
+                <entry>-</entry>
               </row>
             </tbody>
           </tgroup>
@@ -2774,7 +2792,11 @@ SystemCallErrorNumber=EPERM</programlisting>
         writing text to stderr will not work. To mitigate this use the construct <command>echo "hello"
         >&amp;2</command> instead, which is mostly equivalent and avoids this pitfall.</para>
 
-        <para>This setting defaults to the value set with <varname>DefaultStandardOutput=</varname> in
+        <para>If <varname>StandardInput=</varname> is set to one of <option>tty</option>, <option>tty-force</option>,
+        <option>tty-fail</option>, <option>socket</option>, or <option>fd:<replaceable>name</replaceable></option>, this
+        setting defaults to <option>inherit</option>.</para>
+
+        <para>In other cases, this setting defaults to the value set with <varname>DefaultStandardOutput=</varname> in
         <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, which
         defaults to <option>journal</option>. Note that setting this parameter might result in additional dependencies
         to be added to the unit (see above).</para></listitem>
@@ -3107,12 +3129,20 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
         <para>The service manager itself may receive system credentials that can be propagated to services
         from a hosting container manager or VM hypervisor. See the <ulink
         url="https://systemd.io/CONTAINER_INTERFACE">Container Interface</ulink> documentation for details
-        about the former. For the latter, use the <command>qemu</command> <literal>fw_cfg</literal> node
+        about the former. For the latter, pass <ulink
+        url="https://www.dmtf.org/standards/smbios">DMI/SMBIOS</ulink> OEM string table entries (field type
+        11) with a prefix of <literal>io.systemd.credential:</literal> or
+        <literal>io.systemd.credential.binary:</literal>. In both cases a key/value pair separated by
+        <literal>=</literal> is expected, in the latter case the right-hand side is Base64 decoded when
+        parsed (thus permitting binary data to be passed in). Example qemu switch: <literal>-smbios
+        type=11,value=io.systemd.credential:xx=yy</literal>, or <literal>-smbios
+        type=11,value=io.systemd.credential.binary:rick=TmV2ZXIgR29ubmEgR2l2ZSBZb3UgVXA=</literal>. Alternatively,
+        use the <command>qemu</command> <literal>fw_cfg</literal> node
         <literal>opt/io.systemd.credentials/</literal>. Example qemu switch: <literal>-fw_cfg
         name=opt/io.systemd.credentials/mycred,string=supersecret</literal>. They may also be specified on
         the kernel command line using the <literal>systemd.set_credential=</literal> switch (see
-        <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>)
-        and from the UEFI firmware environment via
+        <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>) and from
+        the UEFI firmware environment via
         <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
 
         <para>If referencing an <constant>AF_UNIX</constant> stream socket to connect to, the connection will
@@ -3635,7 +3665,7 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
           <term><varname>$MONITOR_INVOCATION_ID</varname></term>
           <term><varname>$MONITOR_UNIT</varname></term>
 
-          <listitem><para>Only defined for the service unit type. Those environment variable are passed to
+          <listitem><para>Only defined for the service unit type. Those environment variables are passed to
           all <varname>ExecStart=</varname> and <varname>ExecStartPre=</varname> processes which run in
           services triggered by <varname>OnFailure=</varname> or <varname>OnSuccess=</varname> dependencies.
           </para>
@@ -3644,7 +3674,7 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
           and <varname>$MONITOR_EXIT_STATUS</varname> take the same values as for
           <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes. Variables
           <varname>$MONITOR_INVOCATION_ID</varname> and <varname>$MONITOR_UNIT</varname> are set to the
-          invocaton id and unit name of the service which triggered the dependency.</para>
+          invocation id and unit name of the service which triggered the dependency.</para>
 
           <para>Note that when multiple services trigger the same unit, those variables will be
           <emphasis>not</emphasis> be passed. Consider using a template handler unit for that case instead:
index de23b941ad2dcf40527e15eb6c2f638e3e386b50..bb4cd227e6b6b727df0f7355cb4326fbb7a3989f 100644 (file)
           <para>Matches against the hostname or machine ID of the host. See <varname>ConditionHost=</varname> in
           <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
-          If an empty string is assigned, then previously assigned value is cleared.
+          If an empty string is assigned, the previously assigned value is cleared.
           </para>
         </listitem>
       </varlistentry>
           whether it is a specific implementation. See <varname>ConditionVirtualization=</varname> in
           <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
-          If an empty string is assigned, then previously assigned value is cleared.
+          If an empty string is assigned, the previously assigned value is cleared.
           </para>
         </listitem>
       </varlistentry>
           <varname>ConditionKernelCommandLine=</varname> in
           <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
-          If an empty string is assigned, then previously assigned value is cleared.
+          If an empty string is assigned, the previously assigned value is cleared.
           </para>
         </listitem>
       </varlistentry>
           expression. See <varname>ConditionKernelVersion=</varname> in
           <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
           details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
-          If an empty string is assigned, then previously assigned value is cleared.
+          If an empty string is assigned, the previously assigned value is cleared.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry id='credential'>
+        <term><varname>Credential=</varname></term>
+        <listitem>
+          <para>Checks whether the specified credential was passed to the
+          <filename>systemd-networkd.service</filename> service. See <ulink
+          url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for details. When
+          prefixed with an exclamation mark (<literal>!</literal>), the result is negated.  If an empty
+          string is assigned, the previously assigned value is cleared.
           </para>
         </listitem>
       </varlistentry>
           <varname>ConditionArchitecture=</varname> in
           <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
-          If an empty string is assigned, then previously assigned value is cleared.
+          If an empty string is assigned, the previously assigned value is cleared.
           </para>
         </listitem>
       </varlistentry>
           <varname>ConditionFirmware=</varname> in
           <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
-          If an empty string is assigned, then previously assigned value is cleared.
+          If an empty string is assigned, the previously assigned value is cleared.
           </para>
         </listitem>
       </varlistentry>
index 8f4ebfc788ef834106692d9568566fbfc376dd37..e4e35f2dd86465b29b9b0b9b47f80d257167be4d 100644 (file)
   <refsect1>
     <title><filename>fstab</filename></title>
 
-    <para>Mount units may either be configured via unit files, or via
-    <filename>/etc/fstab</filename> (see
+    <para>Mount units may either be configured via unit files, or via <filename>/etc/fstab</filename> (see
     <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-    for details). Mounts listed in <filename>/etc/fstab</filename>
-    will be converted into native units dynamically at boot and when
-    the configuration of the system manager is reloaded. In general,
-    configuring mount points through <filename>/etc/fstab</filename>
-    is the preferred approach. See
-    <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-    for details about the conversion.</para>
+    for details). Mounts listed in <filename>/etc/fstab</filename> will be converted into native units
+    dynamically at boot and when the configuration of the system manager is reloaded. In general, configuring
+    mount points through <filename>/etc/fstab</filename> is the preferred approach to manage mounts for
+    humans. For tooling, writing mount units should be preferred over editing <filename>/etc/fstab</filename>.
+    See <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    for details about the conversion from <filename>/etc/fstab</filename> to mount units.</para>
 
     <para>The NFS mount option <option>bg</option> for NFS background mounts
     as documented in <citerefentry project='man-pages'><refentrytitle>nfs</refentrytitle><manvolnum>5</manvolnum></citerefentry>
index e5a3d7a3139dad6bf7bb888f2435ee820d2fadff..8aac42b49de13a1abf4421cfc5c2d7bbd675b349 100644 (file)
     <variablelist>
         <varlistentry>
           <term><varname>ID_NET_NAME_ONBOARD=<replaceable>prefix</replaceable><constant>o</constant><replaceable>number</replaceable></varname></term>
+          <term><varname>ID_NET_NAME_ONBOARD=<replaceable>prefix</replaceable><constant>d</constant><replaceable>number</replaceable></varname></term>
 
           <listitem><para>This name is set based on the numeric ordering information given by the firmware
-          for on-board devices. The name consists of the prefix, letter <constant>o</constant>, and a number
-          specified by the firmware. This is only available for PCI devices.</para>
+          for on-board devices. Different schemes are used depending on the firmware type, as described in the table below.</para>
+
+            <table>
+              <title>Onboard naming schemes</title>
+
+              <tgroup cols='2'>
+                <thead>
+                  <row>
+                    <entry>Format</entry>
+                    <entry>Description</entry>
+                  </row>
+                </thead>
+
+                <tbody>
+                  <row>
+                    <entry><replaceable>prefix</replaceable><constant>o</constant><replaceable>number</replaceable></entry>
+                    <entry>PCI onboard index</entry>
+                  </row>
+
+                  <row>
+                    <entry><replaceable>prefix</replaceable><constant>d</constant><replaceable>number</replaceable></entry>
+                    <entry>Devicetree alias index</entry>
+                  </row>
+
+                </tbody>
+              </tgroup>
+            </table>
           </listitem>
         </varlistentry>
 
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><constant>v252</constant></term>
+
+          <listitem><para>Added naming scheme for platform devices with devicetree aliases.</para>
+          </listitem>
+        </varlistentry>
+
       </variablelist>
 
     <para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
index 7d3a4f95c858eee6120f8af0796009ca502d9c11..266ad52df28d1de4261caf6dbe6db7519a8d3d47 100644 (file)
       <xi:include href="systemd.link.xml" xpointer="virtualization" />
       <xi:include href="systemd.link.xml" xpointer="kernel-command-line" />
       <xi:include href="systemd.link.xml" xpointer="kernel-version" />
+      <xi:include href="systemd.link.xml" xpointer="credential" />
       <xi:include href="systemd.link.xml" xpointer="architecture" />
       <xi:include href="systemd.link.xml" xpointer="firmware" />
     </variablelist>
         <filename>/dev/net/tun</filename> device.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>KeepCarrier=</varname></term>
+        <listitem>
+          <para>Takes a boolean. If enabled, to make the interface maintain its carrier status, the file
+          descriptor of the interface is kept open. This may be useful to keep the interface in running
+          state, for example while the backing process is temporarily shutdown. Defaults to
+          <literal>no</literal>.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
         <term><varname>PhysicalDevice=</varname></term>
         <listitem>
           <para>Specifies the name or index of the physical WLAN device (e.g. <literal>0</literal> or
-          <literal>phy0</literal>). The list of the physical WLAN devices that exist os the host can be
+          <literal>phy0</literal>). The list of the physical WLAN devices that exist on the host can be
           obtained by <command>iw phy</command> command. This option is mandatory.</para>
         </listitem>
       </varlistentry>
index eae27389ede858bab36f9a5e11389cc7b0713fd4..76a924cec73d2dc1facf55c037179d9955f29a5f 100644 (file)
       <xi:include href="systemd.link.xml" xpointer="virtualization" />
       <xi:include href="systemd.link.xml" xpointer="kernel-command-line" />
       <xi:include href="systemd.link.xml" xpointer="kernel-version" />
+      <xi:include href="systemd.link.xml" xpointer="credential" />
       <xi:include href="systemd.link.xml" xpointer="architecture" />
       <xi:include href="systemd.link.xml" xpointer="firmware" />
     </variablelist>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>IPv4LLStartAddress=</varname></term>
+        <listitem>
+          <para>Specifies the first IPv4 link-local address to try. Takes an IPv4 address for example
+          169.254.1.2, from the link-local address range: 169.254.0.0/16 except for 169.254.0.0/24 and
+          169.254.255.0/24. This setting may be useful if the device should always have the same address
+          as long as there is no address conflict. When unset, a random address will be automatically
+          selected. Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>IPv4LLRoute=</varname></term>
         <listitem>
@@ -1103,9 +1115,9 @@ Table=1234</programlisting></para>
           IGMP snooping since the switch would not replicate multicast packets on  ports that did not
           have IGMP reports for the multicast addresses. Linux vxlan interfaces created via
           <command>ip link add vxlan</command> or networkd's netdev kind vxlan have the group option
-          that enables then to do the required join. By extending ip address command with option
-          <literal>autojoin</literal> we can get similar functionality for openvswitch (OVS) vxlan
-          interfaces as well as other tunneling mechanisms that need to receive multicast traffic.
+          that enables them to do the required join. By extending <command>ip address</command> command
+          with option <literal>autojoin</literal> we can get similar functionality for openvswitch (OVS)
+          vxlan interfaces as well as other tunneling mechanisms that need to receive multicast traffic.
           Defaults to <literal>no</literal>.</para>
         </listitem>
       </varlistentry>
@@ -1487,7 +1499,7 @@ Table=1234</programlisting></para>
           <para>For IPv4 route, defaults to <literal>host</literal> if <varname>Type=</varname> is
           <literal>local</literal> or <literal>nat</literal>, and <literal>link</literal> if
           <varname>Type=</varname> is <literal>broadcast</literal>, <literal>multicast</literal>,
-          <literal>anycast</literal>, or direct <literal>unicast</literal> routes. In other cases,
+          <literal>anycast</literal>, or <literal>unicast</literal>. In other cases,
           defaults to <literal>global</literal>. The value is not used for IPv6.</para>
         </listitem>
       </varlistentry>
@@ -2135,6 +2147,19 @@ Table=1234</programlisting></para>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RapidCommit=</varname></term>
+        <listitem>
+          <para>Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server
+          through a rapid two-message exchange (solicit and reply). When the rapid commit option is set by
+          both the DHCPv6 client and the DHCPv6 server, the two-message exchange is used. Otherwise, the
+          four-message exchange (solicit, advertise, request, and reply) is used. The two-message exchange
+          provides faster client configuration. See
+          <ulink url="https://tools.ietf.org/html/rfc3315#section-17.2.1">RFC 3315</ulink> for details.
+          Defaults to true, and the two-message exchange will be used if the server support it.</para>
+        </listitem>
+      </varlistentry>
+
       <!-- How to use the DHCP lease -->
 
       <varlistentry>
@@ -2534,7 +2559,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
       <varlistentry>
         <term><varname>ServerAddress=</varname></term>
         <listitem><para>Specifies server address for the DHCP server. Takes an IPv4 address with prefix
-        length, for example <literal>192.168.0.1/24</literal>. This setting may be useful when the link on
+        length, for example 192.168.0.1/24. This setting may be useful when the link on
         which the DHCP server is running has multiple static addresses. When unset, one of static addresses
         in the link will be automatically selected. Defaults to unset.</para></listitem>
       </varlistentry>
@@ -2956,7 +2981,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
 
         <listitem><para>The IPv6 route that is to be distributed to hosts.  Similarly to configuring static
         IPv6 routes, the setting is configured as an IPv6 prefix routes and its prefix route length,
-        separated by a <literal>/</literal> character. Use multiple [IPv6PrefixRoutes] sections to configure
+        separated by a <literal>/</literal> character. Use multiple [IPv6RoutePrefix] sections to configure
         multiple IPv6 prefix routes.</para></listitem>
       </varlistentry>
 
index 2b545e4d9316f97d07936b6293d27a637b223647..c16eb3951a18cf806562415397c492d29af21443 100644 (file)
         <term><varname>StartupCPUWeight=<replaceable>weight</replaceable></varname></term>
 
         <listitem>
-          <para>Assign the specified CPU time weight to the processes executed, if the unified control group
-          hierarchy is used on the system. These options take an integer value and control the
-          <literal>cpu.weight</literal> control group attribute. The allowed range is 1 to 10000. Defaults to
-          100. For details about this control group attribute, see <ulink
-          url="https://docs.kernel.org/admin-guide/cgroup-v2.html">Control Groups v2</ulink>
-          and <ulink url="https://docs.kernel.org/scheduler/sched-design-CFS.html">CFS
-          Scheduler</ulink>.  The available CPU time is split up among all units within one slice relative to
-          their CPU time weight. A higher weight means more CPU time, a lower weight means less.</para>
+          <para>These options accept an integer value or a the special string "idle":</para>
+            <itemizedlist>
+              <listitem>
+                <para>If set to an integer value, assign the specified CPU time weight to the processes executed,
+                if the unified control group hierarchy is used on the system. These options control the
+                <literal>cpu.weight</literal> control group attribute. The allowed range is 1 to 10000. Defaults to
+                100. For details about this control group attribute, see <ulink
+                url="https://docs.kernel.org/admin-guide/cgroup-v2.html">Control Groups v2</ulink>
+                and <ulink url="https://docs.kernel.org/scheduler/sched-design-CFS.html">CFS
+                Scheduler</ulink>.  The available CPU time is split up among all units within one slice relative to
+                their CPU time weight. A higher weight means more CPU time, a lower weight means less.</para>
+              </listitem>
+              <listitem>
+                <para>If set to the special string "idle", mark the cgroup for "idle scheduling", which means
+                that it will get CPU resources only when there are no processes not marked in this way to execute in this
+                cgroup or its siblings. This setting corresponds to the <literal>cpu.idle</literal> cgroup attribute.</para>
+
+                <para>Note that this value only has an effect on cgroup-v2, for cgroup-v1 it is equivalent to the minimum weight.</para>
+              </listitem>
+            </itemizedlist>
 
           <para>While <varname>StartupCPUWeight=</varname> applies to the startup and shutdown phases of the system,
           <varname>CPUWeight=</varname> applies to normal runtime of the system, and if the former is not set also to
index 55f32f32728f58df7d4afdf2cfaf01803418f76b..ea95ba886927c7b558f5ae56ab94d9377d01b5df 100644 (file)
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>ConditionCredential=</varname></term>
+
+          <listitem><para><varname>ConditionCredential=</varname> may be used to check whether a credential
+          by the specified name was passed into the service manager. See <ulink
+          url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for details about
+          credentials. If used in services for the system service manager this may be used to conditionalize
+          services based on system credentials passed in. If used in services for the per-user service
+          manager this may be used to conditionalize services based on credentials passed into the
+          <filename>unit@.service</filename> service instance belonging to the user. The argument must be a
+          valid credential name.</para></listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>ConditionEnvironment=</varname></term>
 
index e526a1caea06d4742cc090622b96a6e86607bd30..30484e09a9b01b81d1c850aba6587840370f340b 100644 (file)
         <term><varname>systemd.import_credentials=</varname></term>
 
         <listitem><para>Takes a boolean argument. If false disables importing credentials from the kernel
-        command line, qemu_fw_cfg subsystem or the kernel command line.</para></listitem>
+        command line, the DMI/SMBIOS OEM string table, the qemu_fw_cfg subsystem or the EFI kernel
+        stub.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 03d27b9fbc45eddb0e9a020e74120bd82efecb9a..d57fbf0442b6a362759628467aa0fb9c60587870 100644 (file)
@@ -76,7 +76,7 @@
 
       <listitem><para>Similarly, a file <literal>https://download.example.com/foobarOS_47.verity.xz</literal>
       should be downloaded, decompressed and written to a previously empty partition with GPT partition type
-      UUID of 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 (i.e the partition type for Verity integrity information
+      UUID of 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 (i.e. the partition type for Verity integrity information
       for x86-64 root file systems).</para></listitem>
 
       <listitem><para>Finally, a file <literal>https://download.example.com/foobarOS_47.efi.xz</literal> (a
     <itemizedlist>
       <listitem><para>For partitions: the surrounding GPT partition table contains a list of defined
       partitions, including a partition type UUID and a partition label (in this scheme the partition label
-      plays a role for the partition similar to the filename for a regular file)</para></listitem>
+      plays a role for the partition similar to the filename for a regular file).</para></listitem>
 
       <listitem><para>For regular files: the directory listing of the directory the files are contained in
       provides a list of existing files in a straightforward way.</para></listitem>
             <entry><literal>@r</literal></entry>
             <entry>Read-only flag</entry>
             <entry>Either <literal>0</literal> or <literal>1</literal></entry>
-            <entry>Controls ReadOnly bit of the GPT partition flags, as per <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink> and other output read-only flags, see <varname>ReadOnly=</varname> below.</entry>
+            <entry>Controls ReadOnly bit of the GPT partition flags, as per <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink> and other output read-only flags, see <varname>ReadOnly=</varname> below</entry>
           </row>
 
           <row>
             <entry><literal>@l</literal></entry>
             <entry>Tries left</entry>
             <entry>Formatted decimal integer</entry>
-            <entry>Useful when operating with kernel images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot Assessment</ulink></entry>
+            <entry>Useful when operating with kernel image files, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot Assessment</ulink></entry>
           </row>
 
           <row>
             <entry><literal>@h</literal></entry>
             <entry>SHA256 hash of compressed file</entry>
             <entry>64 hexadecimal characters</entry>
-            <entry>The SHA256 hash of the compressed file; not useful for <constant>url-file</constant> or <constant>url-tar</constant> where the SHA256 hash is already included in the manifest file anyway.</entry>
+            <entry>The SHA256 hash of the compressed file; not useful for <constant>url-file</constant> or <constant>url-tar</constant> where the SHA256 hash is already included in the manifest file anyway</entry>
           </row>
         </tbody>
       </tgroup>
   <refsect1>
     <title>[Transfer] Section Options</title>
 
-    <para>This section defines general properties of this transfer.</para>
+    <para>This section defines general properties of this transfer:</para>
 
     <variablelist>
       <varlistentry>
         <listitem><para>Specifies a file system path where to look for already installed versions or place
         newly downloaded versions of this configured resource. If <varname>Type=</varname> is set to
         <constant>partition</constant>, expects a path to a (whole) block device node, or the special string
-        <literal>auto</literal> in which case the block device the root file system of the currently booted
-        system is automatically determined and used. If <varname>Type=</varname> is set to
+        <literal>auto</literal> in which case the block device which contains the root file system of the
+        currently booted system is automatically determined and used. If <varname>Type=</varname> is set to
         <constant>regular-file</constant>, <constant>directory</constant> or <constant>subvolume</constant>,
         must refer to a path in the local file system referencing the directory to find or place the version
         files or directories under.</para>
@@ -818,7 +818,7 @@ Path=https://download.example.com/
 MatchPattern=foobarOS_@v.efi.xz
 
 [Target]
-Type=file
+Type=regular-file
 Path=/efi/EFI/Linux
 MatchPattern=foobarOS_@v+@l-@d.efi \
              foobarOS_@v+@l.efi \
@@ -831,7 +831,7 @@ InstancesMax=2</programlisting></para>
         <para>The above installs a unified kernel image into the ESP (which is mounted to
         <filename>/efi/</filename>), as per <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
         Loader Specification</ulink> Type #2. This defines three possible patterns for the names of the
-        kernel images images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot
+        kernel images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot
         Assessment</ulink>, and ensures when installing new kernels, they are set up with 3 tries left. No
         more than two parallel kernels are kept.</para>
 
index 3267454f3b23e81ff7d67a8e3934ff0e0ca03785..04617bc532671d60377db0b62ea443027f68ed2c 100644 (file)
@@ -160,8 +160,10 @@ L     /tmp/foobar -    -    -     -   /dev/null</programlisting>
     <refsect2>
       <title>Type</title>
 
-      <para>The type consists of a single letter and optionally an exclamation mark (<literal>!</literal>)
-      minus sign (<literal>-</literal>), and/or equals sign (<literal>=</literal>).</para>
+      <para>The type consists of a single letter and optionally one or emore modifier characters: a plus sign
+      (<literal>+</literal>), exclamation mark (<literal>!</literal>), minus sign (<literal>-</literal>),
+      equals sign (<literal>=</literal>), tilde character (<literal>~</literal>) and/or caret
+      (<literal>^</literal>).</para>
 
       <para>The following line types are understood:</para>
 
@@ -330,7 +332,7 @@ L     /tmp/foobar -    -    -     -   /dev/null</programlisting>
           exists and is not empty. Instead, the entire copy operation is
           skipped. If the argument is omitted, files from the source directory
           <filename>/usr/share/factory/</filename> with the same name
-          are copied. Does not follow symlinks. Contents of the directories 
+          are copied. Does not follow symlinks. Contents of the directories
           are subject to time based cleanup if the age argument is specified.
           </para></listitem>
         </varlistentry>
@@ -460,6 +462,10 @@ L     /tmp/foobar -    -    -     -   /dev/null</programlisting>
           symlinks.</para></listitem>
         </varlistentry>
       </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Type Modifiers</title>
 
       <para>If the exclamation mark (<literal>!</literal>) is used, this line is only safe to execute during
       boot, and can break a running system. Lines without the exclamation mark are presumed to be safe to
@@ -489,6 +495,23 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
       be either directories or directory symlinks). For example, if there is a FIFO in place of one of the parent path
       components it will be replaced with a directory.</para>
 
+      <para>If the tilde character (<literal>~</literal>) is used, the argument (i.e. 6th) column is <ulink
+      url="https://www.rfc-editor.org/rfc/rfc4648.html">Base64 decoded</ulink> before use. This modifier is
+      only supported on line types that can write file contents, i.e. <varname>f</varname>,
+      <varname>f+</varname>, <varname>w</varname>, <varname>+</varname>. This is useful for writing arbitrary
+      binary data (including newlines and NUL bytes) to files. Note that if this switch is used, the argument
+      is not subject to specifier expansion, neither before nor after Base64 decoding.</para>
+
+      <para>If the caret character (<literal>^</literal>) is used, the argument (i.e. 6th) column takes a
+      service credential name to read the argument data from. See <ulink
+      url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for details about the
+      credentials concept. This modifier is only supported on line types that can write file contents,
+      i.e. <varname>f</varname>, <varname>f+</varname>, <varname>w</varname>, <varname>w+</varname>. This is
+      useful for writing arbitrary files with contents sourced from elsewhere, including from VM or container
+      managers further up. If the specified credential is not set for the <command>systemd-tmpfiles</command>
+      service, the line is silently skipped. If <literal>^</literal> and <literal>~</literal> are combined
+      Base64 decoding is applied to the credential contents.</para>
+
       <para>Note that for all line types that result in creation of any kind of file node
       (i.e. <varname>f</varname>/<varname>F</varname>,
       <varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
index ba80e623829c8d4bec7e205b744d23559e3e62d5..47556ee9b365a69623eca43895bcb5742de5802d 100644 (file)
       <para><command>udevadm lock</command> takes an (advisory) exclusive lock(s) on a block device (or
       multiple thereof), as per <ulink url="https://systemd.io/BLOCK_DEVICE_LOCKING">Locking Block Device
       Access</ulink> and invokes a program with the lock(s) taken. When the invoked program exits the lock(s)
-      are automatically released.</para>
+      are automatically released and its return value is propagated as exit code of <command>udevadm
+      lock</command>.</para>
 
       <para>This tool is in particular useful to ensure that
       <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
index e6e32050902ea7482aca47ea049a59fd26c359ca..aa2ae27a93d96dbcc319bcb642d569e86a6c8d99 100644 (file)
@@ -310,6 +310,8 @@ conf.set_quoted('STATUS_UNIT_FORMAT_DEFAULT_STR',             status_unit_format
 
 conf.set10('FIRST_BOOT_FULL_PRESET',                          get_option('first-boot-full-preset'))
 
+conf.set10('EFI_TPM_PCR_COMPAT',                              get_option('efi-tpm-pcr-compat'))
+
 #####################################################################
 
 cc = meson.get_compiler('c')
@@ -390,6 +392,11 @@ possible_common_cc_flags = [
 
 c_args = get_option('c_args')
 
+# Our json library does not support -ffinite-math-only, which is enabled by -Ofast or -ffast-math.
+if (('-Ofast' in c_args or '-ffast-math' in c_args or '-ffinite-math-only' in c_args) and '-fno-finite-math-only' not in c_args)
+        error('-Ofast, -ffast-math, or -ffinite-math-only is specified in c_args.')
+endif
+
 # Disable -Wmaybe-uninitialized when compiling with -Os/-O1/-O3/etc. There are
 # too many false positives with gcc >= 8. Effectively, we only test with -O0
 # and -O2; this should be enough to catch most important cases without too much
@@ -506,7 +513,6 @@ decl_headers = '''
 #include <uchar.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
-#include <linux/fs.h>
 '''
 
 foreach decl : ['char16_t',
@@ -518,6 +524,17 @@ foreach decl : ['char16_t',
         # We get -1 if the size cannot be determined
         have = cc.sizeof(decl, prefix : decl_headers, args : '-D_GNU_SOURCE') > 0
 
+        if decl == 'struct mount_attr'
+                if have
+                        want_linux_fs_h = false
+                else
+                        have = cc.sizeof(decl,
+                                         prefix : decl_headers + '#include <linux/fs.h>',
+                                         args : '-D_GNU_SOURCE') > 0
+                        want_linux_fs_h = have
+                endif
+        endif
+
         if decl == 'struct statx'
                 if have
                         want_linux_stat_h = false
@@ -533,6 +550,7 @@ foreach decl : ['char16_t',
 endforeach
 
 conf.set10('WANT_LINUX_STAT_H', want_linux_stat_h)
+conf.set10('WANT_LINUX_FS_H', want_linux_fs_h)
 
 foreach ident : ['secure_getenv', '__secure_getenv']
         conf.set10('HAVE_' + ident.to_upper(), cc.has_function(ident))
@@ -779,7 +797,7 @@ if default_net_naming_scheme != 'latest'
 endif
 
 time_epoch = get_option('time-epoch')
-if time_epoch == -1
+if time_epoch <= 0
         time_epoch = run_command(sh, '-c', 'echo "$SOURCE_DATE_EPOCH"', check : true).stdout().strip()
         if time_epoch == '' and git.found() and fs.exists('.git')
                 # If we're in a git repository, use the creation time of the latest git tag.
@@ -797,7 +815,7 @@ if time_epoch == -1
                 time_epoch = run_command(stat, '-c', '%Y', NEWS,
                                          check : true).stdout()
         endif
-        time_epoch = time_epoch.to_int()
+        time_epoch = time_epoch.strip().to_int()
 endif
 conf.set('TIME_EPOCH', time_epoch)
 
@@ -812,7 +830,7 @@ foreach tuple : [['system-alloc-uid-min', 'SYS_UID_MIN', 1],  # Also see login.d
                  ['system-alloc-gid-min', 'SYS_GID_MIN', 1],
                  ['system-gid-max',       'SYS_GID_MAX', 999]]
         v = get_option(tuple[0])
-        if v == -1
+        if v <= 0
                 v = run_command(
                         awk,
                         '/^\s*@0@\s+/ { uid=$2 } END { print uid }'.format(tuple[1]),
@@ -858,7 +876,7 @@ if not meson.is_cross_build()
         endif
         id_result = run_command('id', '-u', nobody_user, check : false)
         if id_result.returncode() == 0
-                id = id_result.stdout().to_int()
+                id = id_result.stdout().strip().to_int()
                 if id != 65534
                         warning('\n' +
                                 'The local user with the configured user name "@0@" of the nobody user does not have UID 65534 (it has @1@).\n'.format(nobody_user, id) +
@@ -877,7 +895,7 @@ if not meson.is_cross_build()
         endif
         id_result = run_command('id', '-g', nobody_group, check : false)
         if id_result.returncode() == 0
-                id = id_result.stdout().to_int()
+                id = id_result.stdout().strip().to_int()
                 if id != 65534
                         warning('\n' +
                                 'The local group with the configured group name "@0@" of the nobody group does not have GID 65534 (it has @1@).\n'.format(nobody_group, id) +
@@ -920,8 +938,8 @@ foreach option : ['adm-gid',
         val = get_option(option)
 
         # Ensure provided GID argument is numeric, otherwise fall back to default assignment
-        conf.set(name, val >= 0 ? val : '-')
-        if val >= 0
+        conf.set(name, val > 0 ? val : '-')
+        if val > 0
                 static_ugids += '@0@:@1@'.format(option, val)
         endif
 endforeach
@@ -2213,7 +2231,7 @@ exe = executable(
         dependencies : [versiondep,
                         libseccomp],
         install_rpath : rootpkglibdir,
-        install : conf.get('ENABLE_ANALYZE'))
+        install : conf.get('ENABLE_ANALYZE') == 1)
 public_programs += exe
 
 if want_tests != 'false'
@@ -2247,11 +2265,19 @@ public_programs += executable(
         install_rpath : rootpkglibdir,
         install : true)
 
+if get_option('link-journalctl-shared')
+        journalctl_link_with = [libshared]
+else
+        journalctl_link_with = [libsystemd_static,
+                                libshared_static,
+                                libbasic_gcrypt]
+endif
+
 public_programs += executable(
         'journalctl',
         journalctl_sources,
         include_directories : includes,
-        link_with : [libshared],
+        link_with : [journalctl_link_with],
         dependencies : [threads,
                         libdl,
                         libxz,
@@ -2519,6 +2545,18 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
                 install_rpath : rootpkglibdir,
                 install : true,
                 install_dir : systemgeneratordir)
+
+        if conf.get('HAVE_OPENSSL') == 1
+                executable(
+                        'systemd-measure',
+                        'src/boot/measure.c',
+                        include_directories : includes,
+                        link_with : [libshared],
+                        dependencies : [libopenssl],
+                        install_rpath : rootpkglibdir,
+                        install : true,
+                        install_dir : rootlibexecdir)
+        endif
 endif
 
 executable(
@@ -4276,7 +4314,7 @@ foreach tuple : [
         # components
         ['backlight'],
         ['binfmt'],
-        ['bpf-framework',         conf.get('BPF_FRAMEWORK') == 1],
+        ['bpf-framework',          conf.get('BPF_FRAMEWORK') == 1],
         ['coredump'],
         ['environment.d'],
         ['efi'],
@@ -4306,7 +4344,7 @@ foreach tuple : [
         ['resolve'],
         ['rfkill'],
         ['sysext'],
-        ['systemd-analyze',       conf.get('ENABLE_ANALYZE') == 1],
+        ['systemd-analyze',        conf.get('ENABLE_ANALYZE') == 1],
         ['sysupdate'],
         ['sysusers'],
         ['timedated'],
@@ -4320,36 +4358,37 @@ foreach tuple : [
         ['idn'],
         ['polkit'],
         ['nscd'],
-        ['legacy-pkla',           install_polkit_pkla],
+        ['legacy-pkla',            install_polkit_pkla],
         ['kmod'],
         ['dbus'],
         ['glib'],
         ['tpm'],
-        ['man pages',             want_man],
-        ['html pages',            want_html],
-        ['man page indices',      want_man and have_lxml],
+        ['man pages',              want_man],
+        ['html pages',             want_html],
+        ['man page indices',       want_man and have_lxml],
         ['SysV compat'],
         ['compat-mutable-uid-boundaries'],
         ['utmp'],
         ['ldconfig'],
-        ['adm group',             get_option('adm-group')],
-        ['wheel group',           get_option('wheel-group')],
+        ['adm group',              get_option('adm-group')],
+        ['wheel group',            get_option('wheel-group')],
         ['gshadow'],
         ['debug hashmap'],
         ['debug mmap cache'],
         ['debug siphash'],
-        ['valgrind',              conf.get('VALGRIND') == 1],
-        ['trace logging',         conf.get('LOG_TRACE') == 1],
-        ['install tests',         install_tests],
-        ['link-udev-shared',      get_option('link-udev-shared')],
-        ['link-systemctl-shared', get_option('link-systemctl-shared')],
-        ['link-networkd-shared',  get_option('link-networkd-shared')],
-        ['link-timesyncd-shared', get_option('link-timesyncd-shared')],
-        ['link-boot-shared',      get_option('link-boot-shared')],
+        ['valgrind',               conf.get('VALGRIND') == 1],
+        ['trace logging',          conf.get('LOG_TRACE') == 1],
+        ['install tests',          install_tests],
+        ['link-udev-shared',       get_option('link-udev-shared')],
+        ['link-systemctl-shared',  get_option('link-systemctl-shared')],
+        ['link-networkd-shared',   get_option('link-networkd-shared')],
+        ['link-timesyncd-shared',  get_option('link-timesyncd-shared')],
+        ['link-journalctl-shared', get_option('link-journalctl-shared')],
+        ['link-boot-shared',       get_option('link-boot-shared')],
         ['first-boot-full-preset'],
         ['fexecve'],
-        ['standalone-binaries',   get_option('standalone-binaries')],
-        ['coverage',              get_option('b_coverage')],
+        ['standalone-binaries',    get_option('standalone-binaries')],
+        ['coverage',               get_option('b_coverage')],
 ]
 
         if tuple.length() >= 2
index 628ca1d797c2b695b01c52feee86824458043677..d5237f02ae3c878ce934c237f500158efb0aa325 100644 (file)
@@ -25,6 +25,8 @@ option('link-networkd-shared', type: 'boolean',
        description : 'link systemd-networkd and its helpers to libsystemd-shared.so')
 option('link-timesyncd-shared', type: 'boolean',
        description : 'link systemd-timesyncd and its helpers to libsystemd-shared.so')
+option('link-journalctl-shared', type: 'boolean',
+       description : 'link journalctl against libsystemd-shared.so')
 option('link-boot-shared', type: 'boolean',
        description : 'link bootctl and systemd-bless-boot against libsystemd-shared.so')
 option('first-boot-full-preset', type: 'boolean', value: false,
@@ -36,7 +38,7 @@ option('static-libsystemd', type : 'combo',
 option('static-libudev', type : 'combo',
        choices : ['false', 'true', 'pic', 'no-pic'],
        description : 'install a static library for libudev')
-option('standalone-binaries', type : 'boolean', value : 'false',
+option('standalone-binaries', type : 'boolean', value : false,
        description : 'also build standalone versions of supported binaries')
 
 option('sysvinit-path', type : 'string', value : '/etc/init.d',
@@ -49,7 +51,7 @@ option('rc-local', type : 'string',
        value : '/etc/rc.local')
 option('initrd', type : 'boolean',
        description : 'install services for use when running systemd in initrd')
-option('compat-mutable-uid-boundaries', type : 'boolean', value : 'false',
+option('compat-mutable-uid-boundaries', type : 'boolean', value : false,
        description : 'look at uid boundaries in /etc/login.defs for compatibility')
 option('nscd', type : 'boolean',
        description : 'build support for flushing of the nscd caches')
@@ -218,20 +220,20 @@ option('default-net-naming-scheme', type : 'string', value : 'latest',
 option('status-unit-format-default', type : 'combo',
        choices : ['auto', 'description', 'name', 'combined'],
        description : 'use unit name or description in messages by default')
-option('time-epoch', type : 'integer', value : '-1',
+option('time-epoch', type : 'integer', value : 0,
        description : 'time epoch for time clients')
-option('clock-valid-range-usec-max', type : 'integer', value : '473364000000000', # 15 years
+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('default-user-shell', type : 'string', value : '/bin/bash',
        description : 'default interactive shell')
 
-option('system-alloc-uid-min', type : 'integer', value : '-1',
+option('system-alloc-uid-min', type : 'integer', value : 0,
        description : 'minimum system UID used when allocating')
-option('system-alloc-gid-min', type : 'integer', value : '-1',
+option('system-alloc-gid-min', type : 'integer', value : 0,
        description : 'minimum system GID used when allocating')
-option('system-uid-max', type : 'integer', value : '-1',
+option('system-uid-max', type : 'integer', value : 0,
        description : 'maximum system UID')
-option('system-gid-max', type : 'integer', value : '-1',
+option('system-gid-max', type : 'integer', value : 0,
        description : 'maximum system GID')
 option('dynamic-uid-min', type : 'integer', value : 0x0000EF00,
        description : 'minimum dynamic UID')
@@ -251,47 +253,47 @@ option('nobody-user', type : 'string',
 option('nobody-group', type : 'string',
        description : 'The name of the nobody group (the one with GID 65534)',
        value : 'nobody')
-option('adm-gid', type : 'integer', value : '-1',
+option('adm-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "adm" group')
-option('audio-gid', type : 'integer', value : '-1',
+option('audio-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "audio" group')
-option('cdrom-gid', type : 'integer', value : '-1',
+option('cdrom-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "cdrom" group')
-option('dialout-gid', type : 'integer', value : '-1',
+option('dialout-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "dialout" group')
-option('disk-gid', type : 'integer', value : '-1',
+option('disk-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "disk" group')
-option('input-gid', type : 'integer', value : '-1',
+option('input-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "input" group')
-option('kmem-gid', type : 'integer', value : '-1',
+option('kmem-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "kmem" group')
-option('kvm-gid', type : 'integer', value : '-1',
+option('kvm-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "kvm" group')
-option('lp-gid', type : 'integer', value : '-1',
+option('lp-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "lp" group')
-option('render-gid', type : 'integer', value : '-1',
+option('render-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "render" group')
-option('sgx-gid', type : 'integer', value : '-1',
+option('sgx-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "sgx" group')
-option('tape-gid', type : 'integer', value : '-1',
+option('tape-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "tape" group')
 option('tty-gid', type : 'integer', value : 5,
        description : 'the numeric GID of the "tty" group')
-option('users-gid', type : 'integer', value : '-1',
+option('users-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "users" group')
-option('utmp-gid', type : 'integer', value : '-1',
+option('utmp-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "utmp" group')
-option('video-gid', type : 'integer', value : '-1',
+option('video-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "video" group')
-option('wheel-gid', type : 'integer', value : '-1',
+option('wheel-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the "wheel" group')
-option('systemd-journal-gid', type : 'integer', value : '-1',
+option('systemd-journal-gid', type : 'integer', value : 0,
        description : 'soft-static allocation for the systemd-journal group')
-option('systemd-network-uid', type : 'integer', value : '-1',
+option('systemd-network-uid', type : 'integer', value : 0,
        description : 'soft-static allocation for the systemd-network user')
-option('systemd-resolve-uid', type : 'integer', value : '-1',
+option('systemd-resolve-uid', type : 'integer', value : 0,
        description : 'soft-static allocation for the systemd-resolve user')
-option('systemd-timesync-uid', type : 'integer', value : '-1',
+option('systemd-timesync-uid', type : 'integer', value : 0,
        description : 'soft-static allocation for the systemd-timesync user')
 
 option('dev-kvm-mode', type : 'string', value : '0666',
@@ -440,7 +442,7 @@ option('efi-libdir', type : 'string',
        description : 'path to the EFI lib directory')
 option('efi-includedir', type : 'string', value : '/usr/include/efi',
        description : 'path to the EFI header directory')
-option('efi-tpm-pcr-compat', type : 'boolean', value : 'false',
+option('efi-tpm-pcr-compat', type : 'boolean', value : false,
        description : 'Measure kernel command line also into TPM PCR 8 (in addition to 12)')
 option('sbat-distro', type : 'string', value : 'auto',
        description : 'SBAT distribution ID, e.g. fedora, or auto for autodetection')
@@ -470,11 +472,11 @@ option('zshcompletiondir', type : 'string',
 
 option('tests', type : 'combo', choices : ['true', 'unsafe', 'false'],
        description : 'enable extra tests with =unsafe')
-option('slow-tests', type : 'boolean', value : 'false',
+option('slow-tests', type : 'boolean', value : false,
        description : 'run the slow tests by default')
-option('fuzz-tests', type : 'boolean', value : 'false',
+option('fuzz-tests', type : 'boolean', value : false,
        description : 'run the fuzzer regression tests by default (with sanitizers)')
-option('install-tests', type : 'boolean', value : 'false',
+option('install-tests', type : 'boolean', value : false,
        description : 'install test executables')
 option('log-message-verification', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'do fake printf() calls to verify format strings')
@@ -486,18 +488,18 @@ option('ok-color', type : 'combo',
                   'highlight-cyan', 'highlight-white'],
        value : 'green',
        description: 'color of the "OK" status message')
-option('urlify', type : 'boolean', value : 'true',
+option('urlify', type : 'boolean', value : true,
        description : 'enable pager Hyperlink ANSI sequence support')
-option('fexecve', type : 'boolean', value : 'false',
+option('fexecve', type : 'boolean', value : false,
        description : 'use fexecve() to spawn children')
 
-option('oss-fuzz', type : 'boolean', value : 'false',
+option('oss-fuzz', type : 'boolean', value : false,
        description : 'build against oss-fuzz')
-option('llvm-fuzz', type : 'boolean', value : 'false',
+option('llvm-fuzz', type : 'boolean', value : false,
        description : 'build against LLVM libFuzzer')
-option('kernel-install', type: 'boolean', value: 'true',
+option('kernel-install', type: 'boolean', value: true,
        description : 'install kernel-install and associated files')
-option('analyze', type: 'boolean', value: 'true',
+option('analyze', type: 'boolean', value: true,
        description : 'install systemd-analyze')
 
 option('bpf-compiler', type : 'combo', choices : ['clang', 'gcc'],
@@ -505,5 +507,5 @@ option('bpf-compiler', type : 'combo', choices : ['clang', 'gcc'],
 option('bpf-framework', type : 'combo', choices : ['auto', 'true', 'false'],
     description: 'build BPF programs from source code in restricted C')
 
-option('skip-deps', type : 'boolean', value : 'false',
+option('skip-deps', type : 'boolean', value : false,
        description : 'skip optional dependencies')
index 5855868acc0abc48c7ef218ae47175c4b9d6e56e..27e5b1c65c575b515646f5d3390a85bfe6c8df56 100755 (executable)
@@ -5,6 +5,9 @@ set -e
 # This is a build script for OS image generation using mkosi (https://github.com/systemd/mkosi).
 # Simply invoke "mkosi" in the project directory to build an OS image.
 
+ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:disable_coredump=0:use_madv_dontdump=1
+UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
+
 # On Fedora "ld" is (unfortunately — if you ask me) managed via
 # "alternatives". Since we'd like to support building images in environments
 # with only /usr/ around (e.g. mkosi's UsrOnly=1 option), we have the problem
@@ -49,10 +52,10 @@ if [ ! -f "$BUILDDIR"/build.ninja ] ; then
 
         init_path=$(realpath /sbin/init 2>/dev/null)
         if [ -z "$init_path" ] ; then
-            rootprefix=""
+                rootprefix=""
         else
-            rootprefix=${init_path%/lib/systemd/systemd}
-            rootprefix=/${rootprefix#/}
+                rootprefix=${init_path%/lib/systemd/systemd}
+                rootprefix=/${rootprefix#/}
         fi
 
         meson "$BUILDDIR" \
@@ -60,7 +63,9 @@ if [ ! -f "$BUILDDIR"/build.ninja ] ; then
                 -D "rootprefix=$rootprefix" \
                 -D man=false \
                 -D translations=false \
-                -D version-tag="${VERSION_TAG}"
+                -D version-tag="${VERSION_TAG}" \
+                -D mode=developer \
+                -D b_sanitize="${SANITIZERS:-none}"
 fi
 
 cd "$BUILDDIR"
@@ -70,7 +75,15 @@ if [ "$WITH_TESTS" = 1 ] ; then
                 getent group $id >/dev/null || groupadd -g $id testgroup$id
         done
 
-        ninja test
+        if [ -n "$SANITIZERS" ]; then
+                export ASAN_OPTIONS="$ASAN_OPTIONS"
+                export UBSAN_OPTIONS="$UBSAN_OPTIONS"
+                TIMEOUT_MULTIPLIER=3
+        else
+                TIMEOUT_MULTIPLIER=1
+        fi
+
+        meson test --timeout-multiplier=$TIMEOUT_MULTIPLIER
 fi
 cd "$SRCDIR"
 
@@ -90,32 +103,71 @@ Kernel \r on an \m (\l)
 EOF
 
 if [ -n "$IMAGE_ID" ] ; then
-    mkdir -p "$DESTDIR"/usr/lib
-    sed -n \
-        -e '/^IMAGE_ID=/!p' \
-        -e "\$aIMAGE_ID=$IMAGE_ID" <"/usr/lib/os-release" >"${DESTDIR}/usr/lib/os-release"
+        mkdir -p "$DESTDIR"/usr/lib
+        sed -n \
+                -e '/^IMAGE_ID=/!p' \
+                -e "\$aIMAGE_ID=$IMAGE_ID" <"/usr/lib/os-release" >"${DESTDIR}/usr/lib/os-release"
 
-    OSRELEASEFILE="$DESTDIR"/usr/lib/os-release
+        OSRELEASEFILE="$DESTDIR"/usr/lib/os-release
 else
-    OSRELEASEFILE=/usr/lib/os-release
+        OSRELEASEFILE=/usr/lib/os-release
 fi
 
 
 if [ -n "$IMAGE_VERSION" ] ; then
-    mkdir -p "$DESTDIR"/usr/lib
-    sed -n \
-        -e '/^IMAGE_VERSION=/!p' \
-        -e "\$aIMAGE_VERSION=$IMAGE_VERSION" <$OSRELEASEFILE >"/tmp/os-release.tmp"
+        mkdir -p "$DESTDIR"/usr/lib
+        sed -n \
+                -e '/^IMAGE_VERSION=/!p' \
+                -e "\$aIMAGE_VERSION=$IMAGE_VERSION" <$OSRELEASEFILE >"/tmp/os-release.tmp"
 
-    cat /tmp/os-release.tmp > "$DESTDIR"/usr/lib/os-release
-    rm /tmp/os-release.tmp
+        cat /tmp/os-release.tmp > "$DESTDIR"/usr/lib/os-release
+        rm /tmp/os-release.tmp
 fi
 
 # If $CI_BUILD is set, copy over the CI service which executes a service check
 # after boot and then shuts down the machine
 if [ -n "$CI_BUILD" ]; then
-    mkdir -p "$DESTDIR/usr/lib/systemd/system"
-    cp -v "$SRCDIR/test/mkosi-check-and-shutdown.service" "$DESTDIR/usr/lib/systemd/system/mkosi-check-and-shutdown.service"
-    cp -v "$SRCDIR/test/mkosi-check-and-shutdown.sh" "$DESTDIR/usr/lib/systemd/mkosi-check-and-shutdown.sh"
-    chmod +x "$DESTDIR/usr/lib/systemd/mkosi-check-and-shutdown.sh"
+        mkdir -p "$DESTDIR/usr/lib/systemd/system"
+        cp -v "$SRCDIR/test/mkosi-check-and-shutdown.service" "$DESTDIR/usr/lib/systemd/system/mkosi-check-and-shutdown.service"
+        cp -v "$SRCDIR/test/mkosi-check-and-shutdown.sh" "$DESTDIR/usr/lib/systemd/mkosi-check-and-shutdown.sh"
+        chmod +x "$DESTDIR/usr/lib/systemd/mkosi-check-and-shutdown.sh"
+fi
+
+if [ -n "$SANITIZERS" ]; then
+        LD_PRELOAD=$(ldd $BUILDDIR/systemd | grep libasan.so | awk '{print $3}')
+
+        mkdir -p "$DESTDIR/etc/systemd/system.conf.d"
+
+        cat > "$DESTDIR/etc/systemd/system.conf.d/10-asan.conf" <<EOF
+[Manager]
+ManagerEnvironment=ASAN_OPTIONS=$ASAN_OPTIONS\\
+                   UBSAN_OPTIONS=$UBSAN_OPTIONS\\
+                   LD_PRELOAD=$LD_PRELOAD
+DefaultEnvironment=ASAN_OPTIONS=$ASAN_OPTIONS\\
+                   UBSAN_OPTIONS=$UBSAN_OPTIONS\\
+                   LD_PRELOAD=$LD_PRELOAD
+EOF
+
+        # ASAN logs to stderr by default. However, journald's stderr is connected to /dev/null, so we lose
+        # all the ASAN logs. To rectify that, let's connect journald's stdout to the console so that any
+        # sanitizer failures appear directly on the user's console.
+        mkdir -p "$DESTDIR/etc/systemd/system/systemd-journald.service.d"
+
+        cat > "$DESTDIR/etc/systemd/system/systemd-journald.service.d/10-stdout-tty.conf" <<EOF
+[Service]
+StandardOutput=tty
+EOF
+
+        # Both systemd and util-linux's login call vhangup() on /dev/console which disconnects all users.
+        # This means systemd-journald can't log to /dev/console even if we configure `StandardOutput=tty`. As
+        # a workaround, we modify console-getty.service to disable systemd's vhangup() and disallow login
+        # from calling vhangup() so that journald's ASAN logs correctly end up in the console.
+
+        mkdir -p "$DESTDIR/etc/systemd/system/console-getty.service.d"
+
+        cat > "$DESTDIR/etc/systemd/system/console-getty.service.d/10-no-vhangup.conf" <<EOF
+[Service]
+TTYVHangup=no
+CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG
+EOF
 fi
index 08a03f489b02279c4d0b9b290552cf072bfe32f5..8efd1e97fa3265c1599fb2b5936e531671089c75 100644 (file)
@@ -6,16 +6,19 @@
 Format=gpt_btrfs
 Bootable=yes
 HostonlyInitrd=yes
+# Prevent ASAN warnings when building the image
+Environment=ASAN_OPTIONS=verify_asan_link_order=false
+OutputDirectory=mkosi.output
 
-[Packages]
+[Content]
 BuildDirectory=mkosi.builddir
 Cache=mkosi.cache
-InstallDirectory=mkosi.installdir
 SourceFileTransferFinal=copy-git-others
 
 [Host]
 QemuHeadless=yes
-NetworkVeth=yes
+Netdev=yes
+QemuMem=2G
 
 [Validation]
 Password=
index 55e8a206c8104e01e16b5843fbff8fdad4ad0e45..12f46c71c70650cea49970328baeac6f8aec4e1e 100644 (file)
@@ -8,7 +8,7 @@
 [Distribution]
 Distribution=arch
 
-[Packages]
+[Content]
 BuildPackages=
         acl
         bzip2
@@ -63,6 +63,8 @@ Packages=
         # For testing systemd's zsh completion scripts
         # Run `autoload -Uz compinit; compinit` from a zsh shell in the booted image to enable completions.
         zsh
+        # xxd is provided by the vim package
+        vim
         # Required to run systemd-networkd-tests.py
         python
         iproute
index cf6c74f01ce07653daf9e1bba1e5634eb97c638e..5e726d4aef551458f902718288fbac83b38ebb9d 100644 (file)
@@ -10,7 +10,7 @@ Distribution=centos_epel
 Format=gpt_xfs
 HostonlyInitrd=no
 
-[Packages]
+[Content]
 BuildPackages=
         diffutils
         docbook-style-xsl
@@ -76,6 +76,10 @@ Packages=
         less
         netcat
         e2fsprogs
+        # xxd is provided by the vim-common package
+        vim-common
+        libasan
+        libubsan
         # Required to run systemd-networkd-tests.py
         python3
         iproute
index 7aa0194a605353bda1d24d2c5196af1f2eecebd7..2488eeb557ecbf54b7e216f07441d85e98d2a94b 100644 (file)
@@ -7,7 +7,7 @@
 Distribution=debian
 Release=testing
 
-[Packages]
+[Content]
 BuildPackages=
         acl
         clang
@@ -73,6 +73,9 @@ Packages=
         locales
         nano
         strace
+        xxd
+        # Provides libasan/libubsan
+        gcc
         # Required to run systemd-networkd-tests.py
         python3
         iproute2
index 885981648304000728fefb1d7a35646ec69baef1..c1d8a5755757cda4767354aa6ccb0f975ea374be 100644 (file)
@@ -7,7 +7,7 @@
 Distribution=fedora
 Release=36
 
-[Packages]
+[Content]
 BuildPackages=
         diffutils
         docbook-style-xsl
@@ -75,6 +75,11 @@ Packages=
         netcat
         e2fsprogs
         compsize
+        # xxd is provided by the vim-common package
+        vim-common
+        # Sanitizers
+        libasan
+        libubsan
         # Required to run systemd-networkd-tests.py
         python
         iproute
index a2d35378f7982ec529dfe511103999d713908a96..16fdecdedef2eac933cdacf5550815f7eabb109e 100644 (file)
@@ -7,7 +7,7 @@
 Distribution=opensuse
 Release=tumbleweed
 
-[Packages]
+[Content]
 BuildPackages=
         docbook-xsl-stylesheets
         fdupes
@@ -73,3 +73,7 @@ Packages=
         nano
         strace
         util-linux
+        # xxd is provided by the vim package
+        vim
+        # Provides libasan/libubsan
+        gcc
index 109bd468dc2f9915d665ca707388e64b12e7a661..2d73746f3f87adb4433b81aced824ade452f3d1e 100644 (file)
@@ -5,10 +5,10 @@
 
 [Distribution]
 Distribution=ubuntu
-Release=focal
+Release=jammy
 Repositories=main,universe
 
-[Packages]
+[Content]
 BuildPackages=
         acl
         docbook-xml
@@ -68,9 +68,13 @@ Packages=
         libqrencode4
         # We pull in the -dev package here, since the binary ones appear to change names too often, and the -dev package pulls the right deps in automatically
         libtss2-dev
+        libfdisk1
         locales
         nano
         strace
+        xxd
+        # Provides libasan/libubsan
+        gcc
         # Required to run systemd-networkd-tests.py
         python3
         iproute2
index 1f43eec2cc7c2ebfa0b804e95e2fd3fb05cf7807..1c24b4f51a57aa7cc7b7bd2cebd4e2e331d885ff 100755 (executable)
@@ -1,8 +1,23 @@
 #!/bin/sh
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-if [ "$1" = "final" ] && command -v bootctl > /dev/null; then
-    bootctl install
+if [ "$1" = "final" ]; then
+    if command -v bootctl > /dev/null && [ -d "/efi" ]; then
+        bootctl install
+    fi
+
+    cat >> /root/.gdbinit <<EOF
+set debuginfod enabled off
+set build-id-verbose 0
+EOF
+
+    if [ -n "$SANITIZERS" ]; then
+        # ASAN and syscall filters aren't compatible with each other.
+        find / -name '*.service' -type f -exec sed -i 's/^\(MemoryDeny\|SystemCall\)/# \1/' {} +
+
+        # `systemd-hwdb update` takes > 50s when built with sanitizers so let's not run it by default.
+        systemctl mask systemd-hwdb-update.service
+    fi
 fi
 
 # Temporary workaround until https://github.com/openSUSE/suse-module-tools/commit/158643414ddb8d8208016a5f03a4484d58944d7a
index 11e777004cd87acd814b7895f61cece731c04692..b317e23d1dc01a4faa2b2076411ce4b358c41fa7 100644 (file)
@@ -7,6 +7,7 @@ da
 de
 el
 es
+et
 fi
 fr
 gl
@@ -15,6 +16,7 @@ hu
 id
 it
 ja
+ka
 kab
 ko
 lt
@@ -33,5 +35,3 @@ tr
 uk
 zh_CN
 zh_TW
-ka
-et
index 5044f4ded1ec809cc9d9c4626824819ef6a1e0e1..c6b4acd7ac682aae9568333fbece274ad2335326 100644 (file)
--- a/po/hu.po
+++ b/po/hu.po
@@ -5,19 +5,21 @@
 #
 # Gabor Kelemen <kelemeng at gnome dot hu>, 2015, 2016.
 # Balázs Úr <urbalazs at gmail dot com>, 2016.
+# Balázs Meskó <meskobalazs@mailbox.org>, 2022.
 msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2016-08-23 18:03+0100\n"
-"Last-Translator: Balázs Úr <urbalazs@gmail.com>\n"
-"Language-Team: Hungarian <openscope at googlegroups dot com>\n"
+"PO-Revision-Date: 2022-08-09 20:19+0000\n"
+"Last-Translator: Balázs Meskó <meskobalazs@mailbox.org>\n"
+"Language-Team: Hungarian <https://translate.fedoraproject.org/projects/"
+"systemd/master/hu/>\n"
 "Language: hu\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: Lokalize 2.0\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.13\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -71,73 +73,57 @@ msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
 
 #: src/home/org.freedesktop.home1.policy:13
 msgid "Create a home area"
-msgstr ""
+msgstr "Saját terület létrehozása"
 
 #: src/home/org.freedesktop.home1.policy:14
-#, fuzzy
-#| msgid "Authentication is required to reload the systemd state."
 msgid "Authentication is required to create a user's home area."
-msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
+msgstr "Hitelesítés szükséges a felhasználó saját területének létrehozásához."
 
 #: src/home/org.freedesktop.home1.policy:23
 msgid "Remove a home area"
-msgstr ""
+msgstr "Saját terület eltávolítása"
 
 #: src/home/org.freedesktop.home1.policy:24
-#, fuzzy
-#| msgid "Authentication is required to reload the systemd state."
 msgid "Authentication is required to remove a user's home area."
-msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
+msgstr "Hitelesítés szükséges a felhasználó saját területének eltávolításához."
 
 #: src/home/org.freedesktop.home1.policy:33
 msgid "Check credentials of a home area"
-msgstr ""
+msgstr "Saját terület hitelesítő adatainak ellenőrzése"
 
 #: src/home/org.freedesktop.home1.policy:34
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to manage active sessions, users and seats."
 msgid ""
 "Authentication is required to check credentials against a user's home area."
 msgstr ""
-"Hitelesítés szükséges az aktív munkamenetek, felhasználók és munkaállomások "
-"kezeléséhez."
+"Hitelesítés szükséges a hitelesítő adatok a felhasználó saját területével "
+"való összevetéséhez."
 
 #: src/home/org.freedesktop.home1.policy:43
 msgid "Update a home area"
-msgstr ""
+msgstr "Saját terület frissítése"
 
 #: 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 ""
-"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
-"munkaállomáshoz"
+msgstr "Hitelesítés szükséges a felhasználó saját területének frissítéséhez."
 
 #: src/home/org.freedesktop.home1.policy:53
 msgid "Resize a home area"
-msgstr ""
+msgstr "Saját terület átméretezése"
 
 #: 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 "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a felhasználó saját területének átméretezéséhez."
 
 #: src/home/org.freedesktop.home1.policy:63
 msgid "Change password of a home area"
-msgstr ""
+msgstr "Saját terület hitelesítő adatainak módosítása"
 
 #: 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 ""
-"Hitelesítés szükséges az aktív munkamenetek, felhasználók és munkaállomások "
-"kezeléséhez."
+"Hitelesítés szükséges a felhasználó saját területének jelszavának "
+"módosításához."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set hostname"
@@ -165,45 +151,43 @@ msgstr "Gépinformációk beállítása"
 
 #: src/hostname/org.freedesktop.hostname1.policy:42
 msgid "Authentication is required to set local machine information."
-msgstr "Hitelesítés szükséges a helyi gép információinak beállításához."
+msgstr "Hitelesítés szükséges a helyi gépinformációk beállításához."
 
 #: src/hostname/org.freedesktop.hostname1.policy:51
 msgid "Get product UUID"
-msgstr ""
+msgstr "Termék UUID-jának lekérése"
 
 #: src/hostname/org.freedesktop.hostname1.policy:52
-#, fuzzy
-#| msgid "Authentication is required to reload '$(unit)'."
 msgid "Authentication is required to get product UUID."
-msgstr "Hitelesítés szükséges a következő újratöltéséhez: „$(unit)”."
+msgstr "Hitelesítés szükséges a termék UUID-jának lekéréséhez."
 
 #: src/import/org.freedesktop.import1.policy:22
 msgid "Import a VM or container image"
-msgstr "VM vagy konténer lemezkép importálása"
+msgstr "VM vagy konténer lemezképének importálása"
 
 #: src/import/org.freedesktop.import1.policy:23
 msgid "Authentication is required to import a VM or container image"
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép importálásához."
+msgstr "Hitelesítés szükséges a VM vagy konténer lemezképének importálásához"
 
 #: src/import/org.freedesktop.import1.policy:32
 msgid "Export a VM or container image"
-msgstr "VM vagy konténer lemezkép exportálása"
+msgstr "VM vagy konténer lemezképének exportálása"
 
 #: src/import/org.freedesktop.import1.policy:33
 msgid "Authentication is required to export a VM or container image"
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép exportálásához."
+msgstr "Hitelesítés szükséges a VM vagy konténer lemezképének exportálásához"
 
 #: src/import/org.freedesktop.import1.policy:42
 msgid "Download a VM or container image"
-msgstr "VM vagy konténer lemezkép letöltése"
+msgstr "VM vagy konténer lemezképének letöltése"
 
 #: src/import/org.freedesktop.import1.policy:43
 msgid "Authentication is required to download a VM or container image"
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép letöltéséhez."
+msgstr "Hitelesítés szükséges a VM vagy konténer lemezképének letöltéséhez"
 
 #: src/locale/org.freedesktop.locale1.policy:22
 msgid "Set system locale"
-msgstr "Területi beállítás megadása"
+msgstr "Rendszer területi beállításának megadása"
 
 #: src/locale/org.freedesktop.locale1.policy:23
 msgid "Authentication is required to set the system locale."
@@ -211,7 +195,7 @@ msgstr "Hitelesítés szükséges a rendszer területi beállításainak megadá
 
 #: src/locale/org.freedesktop.locale1.policy:33
 msgid "Set system keyboard settings"
-msgstr "Rendszer billentyűzetbeállítások megadása"
+msgstr "Rendszer billentyűzetbeállításainak megadása"
 
 #: src/locale/org.freedesktop.locale1.policy:34
 msgid "Authentication is required to set the system keyboard settings."
@@ -220,7 +204,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:22
 msgid "Allow applications to inhibit system shutdown"
-msgstr "Alkalmazások meggátolhatják a rendszer leállítását"
+msgstr "Az alkalmazások meggátolhatják a rendszer leállítását"
 
 #: src/login/org.freedesktop.login1.policy:23
 msgid ""
@@ -231,7 +215,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:33
 msgid "Allow applications to delay system shutdown"
-msgstr "Alkalmazások késleltethetik a rendszer leállítását"
+msgstr "Az alkalmazások késleltethetik a rendszer leállítását"
 
 #: src/login/org.freedesktop.login1.policy:34
 msgid "Authentication is required for an application to delay system shutdown."
@@ -241,7 +225,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:44
 msgid "Allow applications to inhibit system sleep"
-msgstr "Alkalmazások meggátolhatják a rendszer altatását"
+msgstr "Az alkalmazások meggátolhatják a rendszer altatását"
 
 #: src/login/org.freedesktop.login1.policy:45
 msgid "Authentication is required for an application to inhibit system sleep."
@@ -250,7 +234,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:55
 msgid "Allow applications to delay system sleep"
-msgstr "Alkalmazások késleltethetik a rendszer altatását"
+msgstr "Az alkalmazások késleltethetik a rendszer altatását"
 
 #: src/login/org.freedesktop.login1.policy:56
 msgid "Authentication is required for an application to delay system sleep."
@@ -260,7 +244,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:65
 msgid "Allow applications to inhibit automatic system suspend"
-msgstr "Alkalmazások meggátolhatják a rendszer automatikus felfüggesztését"
+msgstr "Az alkalmazások meggátolhatják a rendszer automatikus felfüggesztését"
 
 #: src/login/org.freedesktop.login1.policy:66
 msgid ""
@@ -273,7 +257,7 @@ msgstr ""
 #: src/login/org.freedesktop.login1.policy:75
 msgid "Allow applications to inhibit system handling of the power key"
 msgstr ""
-"Alkalmazások meggátolhatják a bekapcsoló gomb rendszer általi kezelését"
+"Az alkalmazások meggátolhatják a bekapcsoló gomb rendszer általi kezelését"
 
 #: src/login/org.freedesktop.login1.policy:76
 msgid ""
@@ -286,7 +270,7 @@ msgstr ""
 #: src/login/org.freedesktop.login1.policy:86
 msgid "Allow applications to inhibit system handling of the suspend key"
 msgstr ""
-"Alkalmazások meggátolhatják a felfüggesztés gomb rendszer általi kezelését"
+"Az alkalmazások meggátolhatják a felfüggesztés gomb rendszer általi kezelését"
 
 #: src/login/org.freedesktop.login1.policy:87
 msgid ""
@@ -299,7 +283,7 @@ msgstr ""
 #: src/login/org.freedesktop.login1.policy:97
 msgid "Allow applications to inhibit system handling of the hibernate key"
 msgstr ""
-"Alkalmazások meggátolhatják a hibernálás gomb rendszer általi kezelését"
+"Az alkalmazások meggátolhatják a hibernálás gomb rendszer általi kezelését"
 
 #: src/login/org.freedesktop.login1.policy:98
 msgid ""
@@ -311,7 +295,8 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:107
 msgid "Allow applications to inhibit system handling of the lid switch"
-msgstr "Alkalmazások meggátolhatják a fedélkapcsoló rendszer általi kezelését"
+msgstr ""
+"Az alkalmazások meggátolhatják a fedélkapcsoló rendszer általi kezelését"
 
 #: src/login/org.freedesktop.login1.policy:108
 msgid ""
@@ -322,22 +307,16 @@ msgstr ""
 "kezelésének meggátlásához."
 
 #: 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 ""
-"Alkalmazások meggátolhatják a bekapcsoló gomb rendszer általi kezelését"
+"Az alkalmazások meggátolhatják az újraindítás gomb rendszer általi kezelését"
 
 #: 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 ""
-"Hitelesítés szükséges egy alkalmazás számára a bekapcsoló gomb rendszer "
+"Hitelesítés szükséges egy alkalmazás számára az újraindítás gomb rendszer "
 "általi kezelésének meggátlásához."
 
 #: src/login/org.freedesktop.login1.policy:128
@@ -347,8 +326,8 @@ msgstr "Programfuttatás engedélyezése be nem jelentkezett felhasználó szám
 #: src/login/org.freedesktop.login1.policy:129
 msgid "Explicit request is required to run programs as a non-logged-in user."
 msgstr ""
-"Határozott kérés szükséges a programfuttatáshoz be nem jelentkezett "
-"felhasználóként."
+"Határozott kérés szükséges a be nem jelentkezett felhasználókénti "
+"programfuttatáshoz."
 
 #: src/login/org.freedesktop.login1.policy:138
 msgid "Allow non-logged-in users to run programs"
@@ -357,18 +336,18 @@ msgstr "Programfuttatás engedélyezése be nem jelentkezett felhasználók szá
 #: src/login/org.freedesktop.login1.policy:139
 msgid "Authentication is required to run programs as a non-logged-in user."
 msgstr ""
-"Hitelesítés szükséges a programfuttatáshoz be nem jelentkezett "
-"felhasználóként."
+"Hitelesítés szükséges a be nem jelentkezett felhasználókénti "
+"programfuttatáshoz."
 
 #: src/login/org.freedesktop.login1.policy:148
 msgid "Allow attaching devices to seats"
-msgstr "Eszközök csatolásának engedélyezése munkaállomásokhoz"
+msgstr "Eszközök munkaállomásokhoz csatolásának engedélyezése"
 
 #: src/login/org.freedesktop.login1.policy:149
 msgid "Authentication is required to attach a device to a seat."
 msgstr ""
-"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
-"munkaállomáshoz"
+"Hitelesítés szükséges az eszköz munkaállomáshoz csatolásának "
+"engedélyezéséhez."
 
 #: src/login/org.freedesktop.login1.policy:159
 msgid "Flush device to seat attachments"
@@ -424,7 +403,7 @@ msgstr "Hitelesítés szükséges a rendszer újraindításához."
 
 #: src/login/org.freedesktop.login1.policy:213
 msgid "Reboot the system while other users are logged in"
-msgstr "A rendszer újraindítása mialatt be vannak jelentkezve más felhasználók"
+msgstr "A rendszer újraindítása miközben be vannak jelentkezve más felhasználók"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
@@ -448,52 +427,36 @@ msgstr ""
 "ennek meggátlását kérte."
 
 #: src/login/org.freedesktop.login1.policy:235
-#, fuzzy
-#| msgid "Hibernate the system"
 msgid "Halt the system"
-msgstr "A rendszer hibernálása"
+msgstr "A rendszer leállítása"
 
 #: src/login/org.freedesktop.login1.policy:236
-#, fuzzy
-#| msgid "Authentication is required to hibernate the system."
 msgid "Authentication is required to halt the system."
-msgstr "Hitelesítés szükséges a rendszer hibernálásához."
+msgstr "Hitelesítés szükséges a rendszer leállításához."
 
 #: src/login/org.freedesktop.login1.policy:246
-#, fuzzy
-#| msgid "Hibernate the system while other users are logged in"
 msgid "Halt the system while other users are logged in"
-msgstr "A rendszer hibernálása mialatt be vannak jelentkezve más felhasználók"
+msgstr "A rendszer leállítása miközben más felhasználók be vannak jelentkezve"
 
 #: src/login/org.freedesktop.login1.policy:247
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to hibernate the system while other users are "
-#| "logged in."
 msgid ""
 "Authentication is required to halt the system while other users are logged "
 "in."
 msgstr ""
-"Hitelesítés szükséges a rendszer hibernálásához miközben be vannak "
-"jelentkezve más felhasználók."
+"Hitelesítés szükséges a rendszer leállításához miközben más felhasználók be "
+"vannak jelentkezve."
 
 #: src/login/org.freedesktop.login1.policy:257
-#, fuzzy
-#| msgid "Hibernate the system while an application is inhibiting this"
 msgid "Halt the system while an application is inhibiting this"
-msgstr "A rendszer hibernálása miközben egy alkalmazás ennek meggátlását kérte"
+msgstr "A rendszer leállítása miközben egy alkalmazás ennek meggátlását kérte"
 
 #: 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 ""
-"Hitelesítés szükséges a rendszer hibernálásához miközben egy alkalmazás "
-"ennek meggátlását kérte."
+"Hitelesítés szükséges a rendszer leállításához miközben egy alkalmazás ennek "
+"meggátlását kérte."
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid "Suspend the system"
@@ -539,7 +502,7 @@ msgstr "Hitelesítés szükséges a rendszer hibernálásához."
 
 #: src/login/org.freedesktop.login1.policy:310
 msgid "Hibernate the system while other users are logged in"
-msgstr "A rendszer hibernálása mialatt be vannak jelentkezve más felhasználók"
+msgstr "A rendszer hibernálása miközben be vannak jelentkezve más felhasználók"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
@@ -582,59 +545,55 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:352
 msgid "Set the reboot \"reason\" in the kernel"
-msgstr ""
+msgstr "Az újraindítás „okának” beállítása a kernelben"
 
 #: 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 "Hitelesítés szükséges a rendszer időzónájának beállításához."
+msgstr ""
+"Hitelesítés szükséges a rendszer újraindítási „okának” beállításához a "
+"kernelben."
 
 #: src/login/org.freedesktop.login1.policy:363
-#, fuzzy
-#| msgid "Allow indication to the firmware to boot to setup interface"
 msgid "Indicate to the firmware to boot to setup interface"
-msgstr "A firmware-nek jelezhető, hogy a beállítófelületet bootolja"
+msgstr ""
+"Jelzés a firmware számára, hogy a beállítófelületet indítsa el "
+"rendszerindításkor"
 
 #: src/login/org.freedesktop.login1.policy:364
 msgid ""
 "Authentication is required to indicate to the firmware to boot to setup "
 "interface."
 msgstr ""
-"Hitelesítés szükséges a firmware-nek jelzéshez, hogy a beállítófelületet "
-"bootolja"
+"Hitelesítés szükséges, hogy a firmware a beállítófelületet indítsa el "
+"rendszerindításkor."
 
 #: src/login/org.freedesktop.login1.policy:374
 msgid "Indicate to the boot loader to boot to the boot loader menu"
 msgstr ""
+"Jelzés a rendszerbetöltő számára, hogy a rendszerbetöltő menüt indítsa el "
+"rendszerindításkor"
 
 #: 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 ""
-"Hitelesítés szükséges a firmware-nek jelzéshez, hogy a beállítófelületet "
-"bootolja"
+"Hitelesítés szükséges, hogy a firmware a rendszerbetöltő menüt indítsa el "
+"rendszerindításkor."
 
 #: src/login/org.freedesktop.login1.policy:385
 msgid "Indicate to the boot loader to boot a specific entry"
 msgstr ""
+"Jelzés a rendszerbetöltő számára, hogy egy adott bejegyzést indítson el "
+"rendszerindításkor"
 
 #: 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 ""
-"Hitelesítés szükséges a firmware-nek jelzéshez, hogy a beállítófelületet "
-"bootolja"
+"Hitelesítés szükséges, hogy a rendszerbetöltő egy adott bejegyzést indítson "
+"el rendszerindításkor."
 
 #: src/login/org.freedesktop.login1.policy:396
 msgid "Set a wall message"
@@ -646,13 +605,11 @@ msgstr "Hitelesítés szükséges a falüzenet beállításához"
 
 #: src/login/org.freedesktop.login1.policy:406
 msgid "Change Session"
-msgstr ""
+msgstr "Munkamenet módosítása"
 
 #: 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 "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr "Hitelesítés szükséges a virtuális terminál módosításához."
 
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
@@ -660,7 +617,7 @@ msgstr "Bejelentkezés helyi konténerbe"
 
 #: src/machine/org.freedesktop.machine1.policy:23
 msgid "Authentication is required to log into a local container."
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr "Hitelesítés szükséges a helyi konténerbe történő bejelentkezéshez."
 
 #: src/machine/org.freedesktop.machine1.policy:32
 msgid "Log into the local host"
@@ -668,7 +625,7 @@ msgstr "Bejelentkezés a helyi gépre"
 
 #: src/machine/org.freedesktop.machine1.policy:33
 msgid "Authentication is required to log into the local host."
-msgstr "Hitelesítés szükséges a bejelentkezéshez a helyi gépre."
+msgstr "Hitelesítés szükséges a helyi gépre történtő bejelentkezéshez."
 
 #: src/machine/org.freedesktop.machine1.policy:42
 msgid "Acquire a shell in a local container"
@@ -714,246 +671,210 @@ msgstr "Hitelesítés szükséges helyi virtuális gépek és konténerek kezel
 
 #: src/machine/org.freedesktop.machine1.policy:95
 msgid "Manage local virtual machine and container images"
-msgstr "Helyi virtuális gép és konténer lemezképek kezelése"
+msgstr "Helyi virtuális gépek és konténerek lemezképeinek kezelése"
 
 #: src/machine/org.freedesktop.machine1.policy:96
 msgid ""
 "Authentication is required to manage local virtual machine and container "
 "images."
 msgstr ""
-"Hitelesítés szükséges a helyi virtuális gép és konténer lemezképek "
+"Hitelesítés szükséges a helyi virtuális gépek és konténerek lemezképeinek "
 "kezeléséhez."
 
 #: src/network/org.freedesktop.network1.policy:22
 msgid "Set NTP servers"
-msgstr ""
+msgstr "NTP-kiszolgálók beállítása"
 
 #: 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 "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges az NTP-kiszolgálók beállításához."
 
 #: src/network/org.freedesktop.network1.policy:33
 #: src/resolve/org.freedesktop.resolve1.policy:44
 msgid "Set DNS servers"
-msgstr ""
+msgstr "DNS-kiszolgálók beállítása"
 
 #: 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 "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges a DNS-kiszolgálók beállításához."
 
 #: src/network/org.freedesktop.network1.policy:44
 #: src/resolve/org.freedesktop.resolve1.policy:55
 msgid "Set domains"
-msgstr ""
+msgstr "Domainek beállítása"
 
 #: 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 "Hitelesítés szükséges a következő leállításához: „$(unit)”."
+msgstr "Hitelesítés szükséges a domainek beállításához."
 
 #: src/network/org.freedesktop.network1.policy:55
 #: src/resolve/org.freedesktop.resolve1.policy:66
 msgid "Set default route"
-msgstr ""
+msgstr "Alapértelmezett hálózati útvonal beállítása"
 
 #: 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 "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr ""
+"Hitelesítés szükséges az alapértelmezett hálózati útvonal beállításához."
 
 #: src/network/org.freedesktop.network1.policy:66
 #: src/resolve/org.freedesktop.resolve1.policy:77
 msgid "Enable/disable LLMNR"
-msgstr ""
+msgstr "LLMNR engedélyezése/letiltása"
 
 #: 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 "Hitelesítés szükséges a rendszer hibernálásához."
+msgstr "Hitelesítés szükséges az LLMNR engedélyezéséhez vagy kikapcsolásához."
 
 #: src/network/org.freedesktop.network1.policy:77
 #: src/resolve/org.freedesktop.resolve1.policy:88
 msgid "Enable/disable multicast DNS"
-msgstr ""
+msgstr "Multicast DNS engedélyezése/letiltása"
 
 #: 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 "Hitelesítés szükséges a bejelentkezéshez a helyi gépre."
+msgstr ""
+"Hitelesítés szükséges a multicast DNS engedélyezéséhez vagy kikapcsolásához."
 
 #: src/network/org.freedesktop.network1.policy:88
 #: src/resolve/org.freedesktop.resolve1.policy:99
 msgid "Enable/disable DNS over TLS"
-msgstr ""
+msgstr "TLS feletti DNS engedélyezése/letiltása"
 
 #: 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 "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr ""
+"Hitelesítés szükséges a TLS feletti DNS engedélyezéséhez vagy "
+"kikapcsolásához."
 
 #: src/network/org.freedesktop.network1.policy:99
 #: src/resolve/org.freedesktop.resolve1.policy:110
 msgid "Enable/disable DNSSEC"
-msgstr ""
+msgstr "DNSSEC engedélyezése/letiltása"
 
 #: 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 "Hitelesítés szükséges a rendszer hibernálásához."
+msgstr "Hitelesítés szükséges a DNSSEC engedélyezéséhez vagy kikapcsolásához."
 
 #: src/network/org.freedesktop.network1.policy:110
 #: src/resolve/org.freedesktop.resolve1.policy:121
 msgid "Set DNSSEC Negative Trust Anchors"
-msgstr ""
+msgstr "DNSSEC Negative Trust Anchor beállítása"
 
 #: 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 "Hitelesítés szükséges a rendszer területi beállításainak megadásához."
+msgstr "Hitelestés szükséges a DNSSEC Negative Trust Anchor beállításához."
 
 #: src/network/org.freedesktop.network1.policy:121
 msgid "Revert NTP settings"
-msgstr ""
+msgstr "NTP-beállítások visszaállítása"
 
 #: 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 "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges az NTP-beállítások visszaállításához."
 
 #: src/network/org.freedesktop.network1.policy:132
 msgid "Revert DNS settings"
-msgstr ""
+msgstr "DNS-beállítások visszaállítása"
 
 #: 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 "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges a DNS-beállítások visszaállításához."
 
 #: src/network/org.freedesktop.network1.policy:143
 msgid "DHCP server sends force renew message"
-msgstr ""
+msgstr "A DHCP-kiszolgáló kényszerített megújítási üzenetet küld"
 
 #: 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 "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a kényszerített megújítási üzenetet küldéséhez."
 
 #: src/network/org.freedesktop.network1.policy:154
 msgid "Renew dynamic addresses"
-msgstr ""
+msgstr "Dinamikus címke megújítása"
 
 #: 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 "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a dinamikus címke megújításához."
 
 #: src/network/org.freedesktop.network1.policy:165
 msgid "Reload network settings"
-msgstr ""
+msgstr "Hálózati beállítások újratöltése"
 
 #: 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 "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
+msgstr "Hitelesítés szükséges a hálózati beállítások újratöltéséhez."
 
 #: src/network/org.freedesktop.network1.policy:176
 msgid "Reconfigure network interface"
-msgstr ""
+msgstr "Hálózati csatoló újrakonfigurálása"
 
 #: 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 "Hitelesítés szükséges a rendszer újraindításához."
+msgstr "Hitelesítés szükséges a hálózati csatoló újrakonfigurálásához."
 
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
-msgstr ""
+msgstr "Hordozható szolgáltatás lemezképének vizsgálata"
 
 #: src/portable/org.freedesktop.portable1.policy:14
-#, fuzzy
-#| msgid "Authentication is required to import a VM or container image"
 msgid "Authentication is required to inspect a portable service image."
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép importálásához."
+msgstr ""
+"Hitelesítés szükséges a hordozható szolgáltatás lemezképének vizsgálatához."
 
 #: src/portable/org.freedesktop.portable1.policy:23
 msgid "Attach or detach a portable service image"
-msgstr ""
+msgstr "Hordozható szolgáltatás lemezképének csatolása vagy leválasztása"
 
 #: src/portable/org.freedesktop.portable1.policy:24
-#, fuzzy
-#| msgid "Authentication is required to attach a device to a seat."
 msgid ""
 "Authentication is required to attach or detach a portable service image."
 msgstr ""
-"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
-"munkaállomáshoz"
+"Hitelesítés szükséges a hordozható szolgáltatás lemezképének csatolásához "
+"vagy leválasztásához."
 
 #: src/portable/org.freedesktop.portable1.policy:34
 msgid "Delete or modify portable service image"
-msgstr ""
+msgstr "Hordozható szolgáltatás lemezképének törlése vagy módosítása"
 
 #: src/portable/org.freedesktop.portable1.policy:35
-#, fuzzy
-#| msgid "Authentication is required to download a VM or container image"
 msgid ""
 "Authentication is required to delete or modify a portable service image."
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép letöltéséhez."
+msgstr ""
+"Hitelesítés szükséges a hordozható szolgáltatás lemezképének törléséhez vagy "
+"módosításához."
 
 #: src/resolve/org.freedesktop.resolve1.policy:22
 msgid "Register a DNS-SD service"
-msgstr ""
+msgstr "DNS-SD szolgáltatás regisztrálása"
 
 #: src/resolve/org.freedesktop.resolve1.policy:23
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
 msgid "Authentication is required to register a DNS-SD service"
-msgstr "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a DNS-SD szolgáltatás regisztrálásához"
 
 #: src/resolve/org.freedesktop.resolve1.policy:33
 msgid "Unregister a DNS-SD service"
-msgstr ""
+msgstr "DNS-SD szolgáltatás kiregisztrálása"
 
 #: src/resolve/org.freedesktop.resolve1.policy:34
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
 msgid "Authentication is required to unregister a DNS-SD service"
-msgstr "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a DNS-SD szolgáltatás kiregisztrálásához"
 
 #: src/resolve/org.freedesktop.resolve1.policy:132
 msgid "Revert name resolution settings"
-msgstr ""
+msgstr "Névfeloldási beállítások visszaállítása"
 
 #: src/resolve/org.freedesktop.resolve1.policy:133
-#, fuzzy
-#| msgid "Authentication is required to set the system keyboard settings."
 msgid "Authentication is required to reset name resolution settings."
-msgstr ""
-"Hitelesítés szükséges a rendszer billentyűzetbeállításainak megadásához."
+msgstr "Hitelesítés szükséges a névfeloldási beállítások visszaállításához."
 
 #: src/timedate/org.freedesktop.timedate1.policy:22
 msgid "Set system time"
@@ -1010,13 +931,12 @@ msgid "Authentication is required to restart '$(unit)'."
 msgstr "Hitelesítés szükséges a következő újraindításához: „$(unit)”."
 
 #: src/core/dbus-unit.c:535
-#, fuzzy
-#| msgid "Authentication is required to set properties on '$(unit)'."
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
 msgstr ""
-"Hitelesítés szükséges a következő tulajdonságainak beállításához: „$(unit)”."
+"Hitelesítés szükséges a UNIX szignál elküldéséhez a következő folyamatai "
+"számára: „$(unit)”."
 
 #: src/core/dbus-unit.c:566
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
@@ -1030,24 +950,18 @@ msgstr ""
 "Hitelesítés szükséges a következő tulajdonságainak beállításához: „$(unit)”."
 
 #: src/core/dbus-unit.c:708
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
 msgstr ""
-"Hitelesítés szükséges a következő „sikertelen” állapotának törléséhez: "
-"„$(unit)”."
+"Hitelesítés szükséges a következővel kapcsolatos fájlok és könyvtárak "
+"törléséhez: „$(unit)”."
 
 #: src/core/dbus-unit.c:757
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgid ""
 "Authentication is required to freeze or thaw the processes of '$(unit)' unit."
 msgstr ""
-"Hitelesítés szükséges a következő „sikertelen” állapotának törléséhez: "
+"Hitelesítés szükséges a következő egység folyamatainak befagyasztásához: "
 "„$(unit)”."
 
 #~ msgid "Authentication is required to kill '$(unit)'."
index 48f9d3d17345ec5140332914dbc9d74d46449720..f69f46079555cb47c3c519520c1696379bf16131 100644 (file)
--- a/po/ko.po
+++ b/po/ko.po
@@ -4,12 +4,13 @@
 # Seong-ho Cho <shcho@gnome.org>, 2015, 2021.
 # Dongsu Park <dongsu@endocode.com>, 2015.
 # simmon <simmon@nplob.com>, 2021.
+# 김인수 <simmon@nplob.com>, 2022.
 msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2021-06-22 10:04+0000\n"
-"Last-Translator: simmon <simmon@nplob.com>\n"
+"PO-Revision-Date: 2022-07-18 15:19+0000\n"
+"Last-Translator: 김인수 <simmon@nplob.com>\n"
 "Language-Team: Korean <https://translate.fedoraproject.org/projects/systemd/"
 "master/ko/>\n"
 "Language: ko\n"
@@ -17,7 +18,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.7\n"
+"X-Generator: Weblate 4.13\n"
 "X-Poedit-SourceCharset: UTF-8\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
@@ -876,8 +877,7 @@ msgstr "'$(unit)' 서비스 유닛 속성을 설정하려면 인증이 필요합
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
-msgstr ""
-"'$(unit)'에 해당하는 파일 또는 디렉터리를 삭제하려면 인증이 필요합니다."
+msgstr "인증은 '$(unit)'과 관련된 파일과 디렉토리를 삭제하는데 필요합니다."
 
 #: src/core/dbus-unit.c:757
 msgid ""
index 139f393e331648fc54864a12716cd29a29e9c105..0a140b40b50cba43fee87974b62c95ebf7c40813 100644 (file)
@@ -1,9 +1,9 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-i18n = import('i18n')
 want_translations = get_option('translations')
 
 if want_translations
+        i18n = import('i18n')
         i18n.gettext(meson.project_name(),
                      preset : 'glib',
                      data_dirs : '.')
index de0842820720ae10f54f15c66ab8c9f3367f1633..03f0a619dc4568e6bc234fb1048bf1cffbda7da9 100644 (file)
@@ -99,6 +99,9 @@ ENV{DEVTYPE}=="partition", ENV{ID_PATH_ATA_COMPAT}=="?*", SYMLINK+="disk/by-path
 KERNEL=="vd*[!0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}"
 KERNEL=="vd*[0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}-part%n"
 
+# allow admin to disable probing the filesystem for slow devices like floppy disk drives
+ENV{UDEV_DISABLE_PERSISTENT_STORAGE_BLKID_FLAG}=="1", GOTO="persistent_storage_blkid_probe_end"
+
 # probe filesystem metadata of optical drives which have a media inserted
 KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \
   IMPORT{builtin}="blkid --hint=session_offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}"
@@ -109,6 +112,8 @@ KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DAT
 # probe filesystem metadata of disks
 KERNEL!="sr*|mmcblk[0-9]boot[0-9]", IMPORT{builtin}="blkid"
 
+LABEL="persistent_storage_blkid_probe_end"
+
 # by-label/by-uuid links (filesystem metadata)
 ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
 ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
index a3301be1bc8ffa0f43cb92ea38ca8128196d0302..b82ce04a39d384706a0662b50cc42b77adceb62f 100644 (file)
@@ -83,6 +83,7 @@ ENV{ID_MAKER_TOOL}=="?*", TAG+="uaccess"
 
 # Protocol analyzers
 ENV{ID_SIGNAL_ANALYZER}=="?*", ENV{DEVTYPE}=="usb_device", TAG+="uaccess"
+ENV{ID_SIGNAL_ANALYZER}=="?*", KERNEL=="ttyACM[0-9]*", TAG+="uaccess"
 
 # rfkill / radio killswitches
 KERNEL=="rfkill", SUBSYSTEM=="misc", TAG+="uaccess"
index b61f88b012c1ade6db29ba84cf349fdde01134d9..c8ca2882c09552f5dc62847a4b3dfcc0aee38e78 100644 (file)
@@ -215,7 +215,7 @@ _systemctl () {
                              suspend-then-hibernate kexec list-jobs list-sockets
                              list-timers list-units list-unit-files poweroff
                              reboot rescue show-environment suspend get-default
-                             is-system-running preset-all'
+                             is-system-running preset-all list-automounts'
         [FILE]='link switch-root bind mount-image'
         [TARGETS]='set-default'
         [MACHINES]='list-machines'
index c909260098066afaeb110fa9f7780c34a6a74f41..ca0a51e94901adbf7d525dbc8227b1b93fb45f7b 100644 (file)
@@ -56,6 +56,11 @@ _systemd_cgtop() {
     fi
 
     COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
+    if [ -d /sys/fs/cgroup/systemd/ ]; then
+        COMPREPLY+=( $(cd /sys/fs/cgroup/systemd/ && compgen -o nospace -o dirnames "$cur") )
+    elif [ -d /sys/fs/cgroup/ ]; then
+        COMPREPLY+=( $(cd /sys/fs/cgroup/ && compgen -o nospace -o dirnames "$cur") )
+    fi
 }
 
 complete -F _systemd_cgtop systemd-cgtop
index 6fbe8737c238a4fd390301a236739480abdb41e2..8b8c59e0d7758fbb8c207dc5fddb107c2e8a5e8b 100644 (file)
@@ -7,6 +7,7 @@
 
     local -a unit_commands=(
         # Unit Commands
+        "list-automounts:List automounts"
         "list-sockets:List sockets"
         "list-timers:List timers"
         "list-units:List units"
index 9255f4cb896f8a8178229179553f8c8663c76987..d4858f77c553e12658ec8b4673a34bd644e51417 100644 (file)
@@ -7,6 +7,7 @@
 #include "analyze-security.h"
 #include "analyze-verify.h"
 #include "bus-error.h"
+#include "bus-locator.h"
 #include "bus-map-properties.h"
 #include "bus-unit-util.h"
 #include "bus-util.h"
@@ -2815,11 +2816,9 @@ static int analyze_security(sd_bus *bus,
                 _cleanup_strv_free_ char **list = NULL;
                 size_t n = 0;
 
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
+                                bus_systemd_mgr,
                                 "ListUnits",
                                 &error,
                                 &reply,
index a4e5d77f6c8074c98136d506d440c6dd80d04d01..b6474d31a72378d85a989ee39c7bc3f47e09f89c 100644 (file)
@@ -21,6 +21,8 @@
 #include "terminal-util.h"
 #include "util.h"
 
+#define PCI_CLASS_GRAPHICS_CARD 0x30000
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -44,6 +46,47 @@ static int help(void) {
         return 0;
 }
 
+static int has_multiple_graphics_cards(void) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *dev;
+        bool found = false;
+        int r;
+
+        r = sd_device_enumerator_new(&e);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_subsystem(e, "pci", /* match = */ true);
+        if (r < 0)
+                return r;
+
+        /* class is an unsigned number, let's validate the value later. */
+        r = sd_device_enumerator_add_match_sysattr(e, "class", NULL, /* match = */ true);
+        if (r < 0)
+                return r;
+
+        FOREACH_DEVICE(e, dev) {
+                const char *s;
+                unsigned long c;
+
+                if (sd_device_get_sysattr_value(dev, "class", &s) < 0)
+                        continue;
+
+                if (safe_atolu(s, &c) < 0)
+                        continue;
+
+                if (c != PCI_CLASS_GRAPHICS_CARD)
+                        continue;
+
+                if (found)
+                        return true; /* This is the second device. */
+
+                found = true; /* Found the first device. */
+        }
+
+        return false;
+}
+
 static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
         const char *subsystem, *sysname, *value;
         sd_device *parent;
@@ -72,7 +115,7 @@ static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
                         return -ENODATA;
 
                 c += strspn(c, DIGITS);
-                if (*c == '-' && !STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-"))
+                if (*c == '-' && !STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-", "-eDP-"))
                         /* A connector DRM device, let's ignore all but LVDS and eDP! */
                         return -EOPNOTSUPP;
 
@@ -86,7 +129,7 @@ static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
                                                  value, subsystem, sysname);
 
                 /* Graphics card */
-                if (class == 0x30000) {
+                if (class == PCI_CLASS_GRAPHICS_CARD) {
                         *ret = parent;
                         return 0;
                 }
@@ -130,7 +173,7 @@ static int same_device(sd_device *a, sd_device *b) {
 
 static int validate_device(sd_device *device) {
         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerate = NULL;
-        const char *v, *subsystem;
+        const char *v, *sysname, *subsystem;
         sd_device *parent, *other;
         int r;
 
@@ -145,80 +188,121 @@ static int validate_device(sd_device *device) {
          * device to userspace. However, we still need to make sure that we use "raw" only if no
          * "firmware" or "platform" device for the same device exists. */
 
+        r = sd_device_get_sysname(device, &sysname);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to get sysname: %m");
+
         r = sd_device_get_subsystem(device, &subsystem);
         if (r < 0)
-                return r;
+                return log_device_debug_errno(device, r, "Failed to get subsystem: %m");
         if (!streq(subsystem, "backlight"))
                 return true;
 
         r = sd_device_get_sysattr_value(device, "type", &v);
         if (r < 0)
-                return r;
+                return log_device_debug_errno(device, r, "Failed to read 'type' sysattr: %m");
         if (!streq(v, "raw"))
                 return true;
 
         r = find_pci_or_platform_parent(device, &parent);
         if (r < 0)
-                return r;
+                return log_device_debug_errno(device, r, "Failed to find PCI or platform parent: %m");
 
         r = sd_device_get_subsystem(parent, &subsystem);
         if (r < 0)
-                return r;
+                return log_device_debug_errno(parent, r, "Failed to get subsystem: %m");
+
+        if (DEBUG_LOGGING) {
+                const char *s = NULL;
+
+                (void) sd_device_get_syspath(parent, &s);
+                log_device_debug(device, "Found %s parent device: %s", subsystem, strna(s));
+        }
 
         r = sd_device_enumerator_new(&enumerate);
         if (r < 0)
-                return r;
+                return log_oom_debug();
 
         r = sd_device_enumerator_allow_uninitialized(enumerate);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Failed to allow uninitialized devices: %m");
 
-        r = sd_device_enumerator_add_match_subsystem(enumerate, "backlight", true);
+        r = sd_device_enumerator_add_match_subsystem(enumerate, "backlight", /* match = */ true);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Failed to add subsystem match: %m");
+
+        r = sd_device_enumerator_add_nomatch_sysname(enumerate, sysname);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to add sysname unmatch: %m");
+
+        r = sd_device_enumerator_add_match_sysattr(enumerate, "type", "platform", /* match = */ true);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to add sysattr match: %m");
+
+        r = sd_device_enumerator_add_match_sysattr(enumerate, "type", "firmware", /* match = */ true);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to add sysattr match: %m");
+
+        if (streq(subsystem, "pci")) {
+                r = has_multiple_graphics_cards();
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to check if the system has multiple graphics cards: %m");
+                if (r > 0) {
+                        /* If the system has multiple graphics cards, then we cannot associate platform
+                         * devices on non-PCI bus (especially WMI bus) with PCI devices. Let's ignore all
+                         * backlight devices that do not have the same parent PCI device. */
+                        log_debug("Found multiple graphics cards on PCI bus. "
+                                  "Skipping to associate platform backlight devices on non-PCI bus.");
+
+                        r = sd_device_enumerator_add_match_parent(enumerate, parent);
+                        if (r < 0)
+                                return log_debug_errno(r, "Failed to add parent match: %m");
+                }
+        }
 
         FOREACH_DEVICE(enumerate, other) {
                 const char *other_subsystem;
                 sd_device *other_parent;
 
-                if (same_device(device, other) > 0)
-                        continue;
-
-                if (sd_device_get_sysattr_value(other, "type", &v) < 0 ||
-                    !STR_IN_SET(v, "platform", "firmware"))
-                        continue;
-
                 /* OK, so there's another backlight device, and it's a platform or firmware device.
                  * Let's see if we can verify it belongs to the same device as ours. */
-                if (find_pci_or_platform_parent(other, &other_parent) < 0)
+                r = find_pci_or_platform_parent(other, &other_parent);
+                if (r < 0) {
+                        log_device_debug_errno(other, r, "Failed to get PCI or platform parent, ignoring: %m");
                         continue;
+                }
 
                 if (same_device(parent, other_parent) > 0) {
-                        const char *device_sysname = NULL, *other_sysname = NULL;
-
                         /* Both have the same PCI parent, that means we are out. */
-
-                        (void) sd_device_get_sysname(device, &device_sysname);
-                        (void) sd_device_get_sysname(other, &other_sysname);
-
-                        log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.",
-                                  device_sysname, other_sysname);
+                        if (DEBUG_LOGGING) {
+                                const char *other_sysname = NULL, *other_type = NULL;
+
+                                (void) sd_device_get_sysname(other, &other_sysname);
+                                (void) sd_device_get_sysattr_value(other, "type", &other_type);
+                                log_device_debug(device,
+                                                 "Found another %s backlight device %s on the same PCI, skipping.",
+                                                 strna(other_type), strna(other_sysname));
+                        }
                         return false;
                 }
 
-                if (sd_device_get_subsystem(other_parent, &other_subsystem) < 0)
+                r = sd_device_get_subsystem(other_parent, &other_subsystem);
+                if (r < 0) {
+                        log_device_debug_errno(other_parent, r, "Failed to get subsystem, ignoring: %m");
                         continue;
+                }
 
                 if (streq(other_subsystem, "platform") && streq(subsystem, "pci")) {
-                        const char *device_sysname = NULL, *other_sysname = NULL;
-
                         /* The other is connected to the platform bus and we are a PCI device, that also means we are out. */
-
-                        (void) sd_device_get_sysname(device, &device_sysname);
-                        (void) sd_device_get_sysname(other, &other_sysname);
-
-                        log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.",
-                                  device_sysname, other_sysname);
+                        if (DEBUG_LOGGING) {
+                                const char *other_sysname = NULL, *other_type = NULL;
+
+                                (void) sd_device_get_sysname(other, &other_sysname);
+                                (void) sd_device_get_sysattr_value(other, "type", &other_type);
+                                log_device_debug(device,
+                                                 "Found another %s backlight device %s, which has higher precedence, skipping.",
+                                                 strna(other_type), strna(other_sysname));
+                        }
                         return false;
                 }
         }
index 13dab0304fe557fc50a6c34d55794a43fc5ba85a..b38db7d4737d1fb7aa93bbcd3747501218bfa884 100644 (file)
@@ -50,16 +50,30 @@ typedef void* (*mfree_func_t)(void *p);
 
 #define malloc0(n) (calloc(1, (n) ?: 1))
 
-#define free_and_replace(a, b)                  \
+#define free_and_replace_full(a, b, free_func)  \
         ({                                      \
                 typeof(a)* _a = &(a);           \
                 typeof(b)* _b = &(b);           \
-                free(*_a);                      \
+                free_func(*_a);                 \
                 *_a = *_b;                      \
                 *_b = NULL;                     \
                 0;                              \
         })
 
+#define free_and_replace(a, b)                  \
+        free_and_replace_full(a, b, free)
+
+/* This is similar to free_and_replace_full(), but NULL is not assigned to 'b', and its reference counter is
+ * increased. */
+#define unref_and_replace_full(a, b, ref_func, unref_func)      \
+        ({                                       \
+                typeof(a)* _a = &(a);            \
+                typeof(b) _b = ref_func(b);      \
+                unref_func(*_a);                 \
+                *_a = _b;                        \
+                0;                               \
+        })
+
 void* memdup(const void *p, size_t l) _alloc_(2);
 void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, since we return a buffer one byte larger than the specified size */
 
index 4c413a8d174cc50ed96bb5a04c9c9ff8ec8793d3..df6d5b7bbb423ee9612dc19ab5df0666ea6e1d66 100644 (file)
@@ -86,6 +86,7 @@ bool cpu_accounting_is_cheap(void);
 
 /* Special values for all weight knobs on unified hierarchy */
 #define CGROUP_WEIGHT_INVALID UINT64_MAX
+#define CGROUP_WEIGHT_IDLE UINT64_C(0)
 #define CGROUP_WEIGHT_MIN UINT64_C(1)
 #define CGROUP_WEIGHT_MAX UINT64_C(10000)
 #define CGROUP_WEIGHT_DEFAULT UINT64_C(100)
index c609ea6ce017a52e662df5169c87dc42315cc76c..0a6a54f8f139dded22de9ac299e9d81320a24e3d 100644 (file)
@@ -33,6 +33,15 @@ char *hw_addr_to_string_full(
         return buffer;
 }
 
+struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length) {
+        assert(addr);
+        assert(length <= HW_ADDR_MAX_SIZE);
+
+        addr->length = length;
+        memcpy_safe(addr->bytes, bytes, length);
+        return addr;
+}
+
 int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) {
         int r;
 
index 32f45fe8135ea6236c4b678d19ca8cb4f2161801..83ed77d634513d189c30ac84542013833298fd6d 100644 (file)
@@ -52,6 +52,8 @@ static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buff
 
 #define HW_ADDR_NULL ((const struct hw_addr_data){})
 
+struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length);
+
 void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state);
 int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b);
 static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) {
index 6c1de92a26caa786211cdda45b209d5ed59eb07f..00591d6c2d31fe67805f6c6970100d6698273009 100644 (file)
@@ -3,7 +3,9 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/btrfs.h>
+#if WANT_LINUX_FS_H
 #include <linux/fs.h>
+#endif
 #include <linux/magic.h>
 #include <sys/ioctl.h>
 #include <sys/resource.h>
index cc25222c32df14403ea2657ea71bc52cde6d0e23..d1272fb04b1c12a9824aaad077de2f9c203c3a51 100644 (file)
@@ -398,10 +398,6 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
         return ret;
 }
 
-int touch(const char *path) {
-        return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
-}
-
 int symlink_idempotent(const char *from, const char *to, bool make_relative) {
         _cleanup_free_ char *relpath = NULL;
         int r;
index e48cf6800fac1796ba7d403a592adb2c1fe4d603..080959d3b99b9e221803276ba49bf0cea9518bcb 100644 (file)
@@ -13,6 +13,7 @@
 #include "alloc-util.h"
 #include "errno-util.h"
 #include "time-util.h"
+#include "user-util.h"
 
 #define MODE_INVALID ((mode_t) -1)
 
@@ -50,7 +51,10 @@ int stat_warn_permissions(const char *path, const struct stat *st);
         RET_NERRNO(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW))
 
 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
-int touch(const char *path);
+
+static inline int touch(const char *path) {
+        return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
+}
 
 int symlink_idempotent(const char *from, const char *to, bool make_relative);
 
index 64c63cdab1fe07533fedbdcf3a8ffd3fa2b119b3..41c9362be18e007d67dc450f739f8de9f17de23c 100644 (file)
@@ -9,12 +9,14 @@ void initialize_libgcrypt(bool secmem) {
         if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
                 return;
 
+        gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
         assert_se(gcry_check_version("1.4.5"));
 
         /* Turn off "secmem". Clients which wish to make use of this
          * feature should initialize the library manually */
         if (!secmem)
                 gcry_control(GCRYCTL_DISABLE_SECMEM);
+
         gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
 }
 
index 6addb76f1b2d52cd18df19794bcbef922db99b77..5fac467185710bc8f73038d4c33bb84cfd08293f 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "hash-funcs.h"
 #include "path-util.h"
+#include "strv.h"
 
 void string_hash_func(const char *p, struct siphash *state) {
         siphash24_compress(p, strlen(p) + 1, state);
@@ -15,6 +16,9 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free,
 DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
                      char, string_hash_func, string_compare_func, free,
                      void, free);
+DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free,
+                     char, string_hash_func, string_compare_func, free,
+                     char*, strv_free);
 
 void path_hash_func(const char *q, struct siphash *state) {
         bool add_slash = false;
index c537c6af7e8cc2a9f8761cf15e53aeb72524ee15..c14302ec72285c5fab5feb6b7b3edac368a13571 100644 (file)
@@ -78,6 +78,7 @@ void string_hash_func(const char *p, struct siphash *state);
 extern const struct hash_ops string_hash_ops;
 extern const struct hash_ops string_hash_ops_free;
 extern const struct hash_ops string_hash_ops_free_free;
+extern const struct hash_ops string_hash_ops_free_strv_free;
 
 void path_hash_func(const char *p, struct siphash *state);
 extern const struct hash_ops path_hash_ops;
index eafc08f658624d7ea1a2a548b00284987e5b81b6..91b3fe862b02325808edf615fe77a6689a054095 100644 (file)
@@ -90,12 +90,7 @@ OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DE
 #define ordered_hashmap_new(ops) _ordered_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
 
 #define hashmap_free_and_replace(a, b)          \
-        ({                                      \
-                hashmap_free(a);                \
-                (a) = (b);                      \
-                (b) = NULL;                     \
-                0;                              \
-        })
+        free_and_replace_full(a, b, hashmap_free)
 
 HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
 static inline Hashmap* hashmap_free(Hashmap *h) {
index 6f8ffaf25969f18c31c8e7d3ba6f15cf4ee55592..fe356c6d15c169875d085767a55330480bdb1f47 100644 (file)
@@ -49,6 +49,20 @@ bool in4_addr_is_link_local(const struct in_addr *a) {
         return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
 }
 
+bool in4_addr_is_link_local_dynamic(const struct in_addr *a) {
+        assert(a);
+
+        if (!in4_addr_is_link_local(a))
+                return false;
+
+        /* 169.254.0.0/24 and 169.254.255.0/24 must not be used for the dynamic IPv4LL assignment.
+         * See RFC 3927 Section 2.1:
+         * The IPv4 prefix 169.254/16 is registered with the IANA for this purpose. The first 256 and last
+         * 256 addresses in the 169.254/16 prefix are reserved for future use and MUST NOT be selected by a
+         * host using this dynamic configuration mechanism. */
+        return !IN_SET(be32toh(a->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
+}
+
 bool in6_addr_is_link_local(const struct in6_addr *a) {
         assert(a);
 
index c1e7ef965da0117ab98cb0397d6da726a93f8767..fbc60436c799162a098bb5a5ca93ccfb3ddf7d6c 100644 (file)
@@ -44,6 +44,7 @@ static inline bool in_addr_data_is_set(const struct in_addr_data *a) {
 int in_addr_is_multicast(int family, const union in_addr_union *u);
 
 bool in4_addr_is_link_local(const struct in_addr *a);
+bool in4_addr_is_link_local_dynamic(const struct in_addr *a);
 bool in6_addr_is_link_local(const struct in6_addr *a);
 int in_addr_is_link_local(int family, const union in_addr_union *u);
 bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a);
index bcac55620e2cf99acdfb67feb5d6f076afa2e0e4..237117db12a76f146dedde2a80fe00746f33ae2e 100644 (file)
         _Pragma("GCC diagnostic push")
 #endif
 
-#define DISABLE_WARNING_FLOAT_EQUAL \
-        _Pragma("GCC diagnostic push");                                 \
-        _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"")
-
 #define DISABLE_WARNING_TYPE_LIMITS \
         _Pragma("GCC diagnostic push");                                 \
         _Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
diff --git a/src/basic/math-util.h b/src/basic/math-util.h
new file mode 100644 (file)
index 0000000..24023cd
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <math.h>
+
+#include "macro.h"
+
+/* On some optimization level, iszero(x) is converted to (x == 0.0), and emits warning -Wfloat-equal.
+ * The argument must be a floating point, i.e. one of float, double, or long double. */
+#define iszero_safe(x) (fpclassify(x) == FP_ZERO)
+
+/* To avoid x == y and triggering compile warning -Wfloat-equal. This returns false if one of the argument is
+ * NaN or infinity. One of the argument must be a floating point. */
+#define fp_equal(x, y) iszero_safe((x) - (y))
index cc4d948abd1f117ceb86ca76d380976377ffb035..19d3395a49fbaa1c4d51e1228b3fb39a94c74070 100644 (file)
@@ -129,6 +129,7 @@ basic_sources = files(
         'login-util.c',
         'login-util.h',
         'macro.h',
+        'math-util.h',
         'memfd-util.c',
         'memfd-util.h',
         'memory-util.c',
@@ -384,7 +385,7 @@ filesystem_includes = ['linux/magic.h',
                        'linux/gfs2_ondisk.h']
 
 check_filesystems = find_program('check-filesystems.sh')
-r = run_command([check_filesystems, cpp, 'filesystems-gperf.gperf'] + filesystem_includes, check: false)
+r = run_command([check_filesystems, cpp, files('filesystems-gperf.gperf')] + filesystem_includes, check: false)
 if r.returncode() != 0
         error('found unknown filesystem(s) defined in kernel headers:\n\n' + r.stdout())
         r.stdout()
index 0cacd49bcff7eb500e7ed388091e51ebada3a0e3..6638d7696221d17ada03e392fad61c89c3bc50a4 100644 (file)
@@ -64,3 +64,8 @@
 #ifndef FS_PROJINHERIT_FL
 #define FS_PROJINHERIT_FL 0x20000000
 #endif
+
+/* linux/fscrypt.h */
+#ifndef FS_KEY_DESCRIPTOR_SIZE
+#define FS_KEY_DESCRIPTOR_SIZE 8
+#endif
index 2e451085beccf48e83f48bdad6e56ea751fca980..c7468e2a4526901a2d11603be70f6dbcd94fabdc 100644 (file)
@@ -182,12 +182,18 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
         int r;
 
         assert(fd >= 0);
-        assert(filename);
-        assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
+        assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
 
-        /* Insist that the specified filename is actually a filename, and not a path, i.e. some inode further
-         * up or down the tree then immediately below the specified directory fd. */
-        if (!filename_possibly_with_slash_suffix(filename))
+        if (!filename) {
+                /* If the file name is specified as NULL we'll see if the specified 'fd' is a mount
+                 * point. That's only supported if the kernel supports statx(), or if the inode specified via
+                 * 'fd' refers to a directory. Otherwise, we'll have to fail (ENOTDIR), because we have no
+                 * kernel API to query the information we need. */
+                flags |= AT_EMPTY_PATH;
+                filename = "";
+        } else if (!filename_possibly_with_slash_suffix(filename))
+                /* Insist that the specified filename is actually a filename, and not a path, i.e. some inode further
+                 * up or down the tree then immediately below the specified directory fd. */
                 return -EINVAL;
 
         /* First we will try statx()' STATX_ATTR_MOUNT_ROOT attribute, which is our ideal API, available
@@ -234,7 +240,10 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
                 nosupp = true;
         }
 
-        r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
+        if (isempty(filename))
+                r = name_to_handle_at_loop(fd, "..", &h_parent, &mount_id_parent, 0); /* can't work for non-directories 😢 */
+        else
+                r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
         if (r < 0) {
                 if (is_name_to_handle_at_fatal_error(r))
                         return r;
@@ -271,7 +280,10 @@ fallback_fdinfo:
         if (r < 0)
                 return r;
 
-        r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
+        if (isempty(filename))
+                r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent); /* can't work for non-directories 😢 */
+        else
+                r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
         if (r < 0)
                 return r;
 
@@ -295,7 +307,11 @@ fallback_fstat:
         if (S_ISLNK(a.st_mode)) /* Symlinks are never mount points */
                 return false;
 
-        if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
+        if (isempty(filename))
+                r = fstatat(fd, "..", &b, 0);
+        else
+                r = fstatat(fd, "", &b, AT_EMPTY_PATH);
+        if (r < 0)
                 return -errno;
 
         /* A directory with same device and inode as its parent? Must be the root directory */
index e40ab3f5b6f3f836e2f1ab01015b72db9f6f3565..88657d5775059f1781d76b5b1063039e046452a5 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <errno.h>
+#include <fnmatch.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -17,6 +18,7 @@
 #include "extract-word.h"
 #include "fd-util.h"
 #include "fs-util.h"
+#include "glob-util.h"
 #include "log.h"
 #include "macro.h"
 #include "path-util.h"
@@ -1310,3 +1312,63 @@ bool prefixed_path_strv_contains(char **l, const char *path) {
 
         return false;
 }
+
+int path_glob_can_match(const char *pattern, const char *prefix, char **ret) {
+        assert(pattern);
+        assert(prefix);
+
+        for (const char *a = pattern, *b = prefix;;) {
+                _cleanup_free_ char *g = NULL, *h = NULL;
+                const char *p, *q;
+                int r, s;
+
+                r = path_find_first_component(&a, /* accept_dot_dot = */ false, &p);
+                if (r < 0)
+                        return r;
+
+                s = path_find_first_component(&b, /* accept_dot_dot = */ false, &q);
+                if (s < 0)
+                        return s;
+
+                if (s == 0) {
+                        /* The pattern matches the prefix. */
+                        if (ret) {
+                                char *t;
+
+                                t = path_join(prefix, p);
+                                if (!t)
+                                        return -ENOMEM;
+
+                                *ret = t;
+                        }
+                        return true;
+                }
+
+                if (r == 0)
+                        break;
+
+                if (r == s && strneq(p, q, r))
+                        continue; /* common component. Check next. */
+
+                g = strndup(p, r);
+                if (!g)
+                        return -ENOMEM;
+
+                if (!string_is_glob(g))
+                        break;
+
+                /* We found a glob component. Check if the glob pattern matches the prefix component. */
+
+                h = strndup(q, s);
+                if (!h)
+                        return -ENOMEM;
+
+                if (fnmatch(g, h, 0) != 0)
+                        break;
+        }
+
+        /* The pattern does not match the prefix. */
+        if (ret)
+                *ret = NULL;
+        return false;
+}
index bb200872216068875c7d1fc98b80a1cbe2a17dc8..757ed722d51c0f34814ad81ac3fcbaa6e55f4f01 100644 (file)
@@ -196,3 +196,5 @@ static inline const char *empty_to_root(const char *path) {
 
 bool path_strv_contains(char **l, const char *path);
 bool prefixed_path_strv_contains(char **l, const char *path);
+
+int path_glob_can_match(const char *pattern, const char *prefix, char **ret);
index d885d92dfb2638901afaace9d8d48b34734d6a90..3dd6f4df597bf411d55b2e48a08a25add83feb8c 100644 (file)
@@ -1147,7 +1147,7 @@ void reset_cached_pid(void) {
 
 pid_t getpid_cached(void) {
         static bool installed = false;
-        pid_t current_value;
+        pid_t current_value = CACHED_PID_UNSET;
 
         /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a
          * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally
@@ -1158,7 +1158,13 @@ pid_t getpid_cached(void) {
          * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
          */
 
-        current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY);
+        __atomic_compare_exchange_n(
+                        &cached_pid,
+                        &current_value,
+                        CACHED_PID_BUSY,
+                        false,
+                        __ATOMIC_SEQ_CST,
+                        __ATOMIC_SEQ_CST);
 
         switch (current_value) {
 
index 9423a0805dd132b708fbb8e5671550e5c2320e76..d8734cc7d0faf78edcfc3f21044228df73c45b0e 100644 (file)
@@ -1,9 +1,5 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#if defined(__i386__) || defined(__x86_64__)
-#include <cpuid.h>
-#endif
-
 #include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
 #include "sha256.h"
 #include "time-util.h"
 
-/* This is a "best effort" kind of thing, but has no real security value.
- * So, this should only be used by random_bytes(), which is not meant for
- * crypto. This could be made better, but we're *not* trying to roll a
- * userspace prng here, or even have forward secrecy, but rather just do
- * the shortest thing that is at least better than libc rand(). */
+/* This is a "best effort" kind of thing, but has no real security value.  So, this should only be used by
+ * random_bytes(), which is not meant for crypto. This could be made better, but we're *not* trying to roll a
+ * userspace prng here, or even have forward secrecy, but rather just do the shortest thing that is at least
+ * better than libc rand(). */
 static void fallback_random_bytes(void *p, size_t n) {
         static thread_local uint64_t fallback_counter = 0;
         struct {
@@ -53,7 +48,7 @@ static void fallback_random_bytes(void *p, size_t n) {
                 .stamp_mono = now(CLOCK_MONOTONIC),
                 .stamp_real = now(CLOCK_REALTIME),
                 .pid = getpid(),
-                .tid = gettid()
+                .tid = gettid(),
         };
 
 #if HAVE_SYS_AUXV_H
index 52cf63e2ddaefa289f6e88665a36a5811b14352b..618e729744662b29938db978573cd49da8b6cfc0 100644 (file)
@@ -6,12 +6,7 @@
 #include "macro.h"
 
 #define set_free_and_replace(a, b)              \
-        ({                                      \
-                set_free(a);                    \
-                (a) = (b);                      \
-                (b) = NULL;                     \
-                0;                              \
-        })
+        free_and_replace_full(a, b, set_free)
 
 Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
 #define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
index 95ecb60fe225703139739e3dc53c6d17720f3fc7..d570b1df4783583bfce01464b61b3858c4a9dba7 100644 (file)
@@ -28,24 +28,31 @@ static void sigbus_push(void *addr) {
         assert(addr);
 
         /* Find a free place, increase the number of entries and leave, if we can */
-        for (size_t u = 0; u < SIGBUS_QUEUE_MAX; u++)
-                if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) {
-                        __sync_fetch_and_add(&n_sigbus_queue, 1);
+        for (size_t u = 0; u < SIGBUS_QUEUE_MAX; u++) {
+                /* OK to initialize this here since we haven't started the atomic ops yet */
+                void *tmp = NULL;
+                if (__atomic_compare_exchange_n(&sigbus_queue[u], &tmp, addr, false,
+                                                __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
+                        __atomic_fetch_add(&n_sigbus_queue, 1, __ATOMIC_SEQ_CST);
                         return;
                 }
+        }
 
         /* If we can't, make sure the queue size is out of bounds, to
          * mark it as overflow */
         for (;;) {
-                unsigned c;
+                sig_atomic_t c;
 
-                __sync_synchronize();
+                __atomic_thread_fence(__ATOMIC_SEQ_CST);
                 c = n_sigbus_queue;
 
                 if (c > SIGBUS_QUEUE_MAX) /* already overflow */
                         return;
 
-                if (__sync_bool_compare_and_swap(&n_sigbus_queue, c, c + SIGBUS_QUEUE_MAX))
+                /* OK if we clobber c here, since we either immediately return
+                 * or it will be immediately reinitialized on next loop */
+                if (__atomic_compare_exchange_n(&n_sigbus_queue, &c, c + SIGBUS_QUEUE_MAX, false,
+                                                __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
                         return;
         }
 }
@@ -56,7 +63,7 @@ int sigbus_pop(void **ret) {
         for (;;) {
                 unsigned u, c;
 
-                __sync_synchronize();
+                __atomic_thread_fence(__ATOMIC_SEQ_CST);
                 c = n_sigbus_queue;
 
                 if (_likely_(c == 0))
@@ -72,8 +79,13 @@ int sigbus_pop(void **ret) {
                         if (!addr)
                                 continue;
 
-                        if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) {
-                                __sync_fetch_and_sub(&n_sigbus_queue, 1);
+                        /* OK if we clobber addr here, since we either immediately return
+                         * or it will be immediately reinitialized on next loop */
+                        if (__atomic_compare_exchange_n(&sigbus_queue[u], &addr, NULL, false,
+                                                        __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
+                                __atomic_fetch_sub(&n_sigbus_queue, 1, __ATOMIC_SEQ_CST);
+                                /* If we successfully entered this if condition, addr won't
+                                 * have been modified since its assignment, so safe to use it */
                                 *ret = addr;
                                 return 1;
                         }
index 072739df355518abff28da8bb391ea1ea409e845..87ec6337bde12083f63f439aa7f16345ebe561cb 100644 (file)
@@ -258,14 +258,7 @@ int strv_extend_n(char ***l, const char *value, size_t n);
 int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
 
 #define strv_free_and_replace(a, b)             \
-        ({                                      \
-                char ***_a = &(a);              \
-                char ***_b = &(b);              \
-                strv_free(*_a);                 \
-                (*_a) = (*_b);                  \
-                (*_b) = NULL;                   \
-                0;                              \
-        })
+        free_and_replace_full(a, b, strv_free)
 
 extern const struct hash_ops string_strv_hash_ops;
 int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS);
index 58b367be06b93d379c35f5c29b240cdc88c6d637..94cd603e32c020b3a8e425898d4d52bfcf43756a 100644 (file)
@@ -169,6 +169,7 @@ DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
 
 static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
         [SCOPE_DEAD]         = "dead",
+        [SCOPE_START_CHOWN]  = "start-chown",
         [SCOPE_RUNNING]      = "running",
         [SCOPE_ABANDONED]    = "abandoned",
         [SCOPE_STOP_SIGTERM] = "stop-sigterm",
index f80e554d2b9a262d0479cb80dcf73c118f048283..5fcd51c095df67311e18beaa2cf9a1c5c6013736 100644 (file)
@@ -114,6 +114,7 @@ typedef enum PathState {
 
 typedef enum ScopeState {
         SCOPE_DEAD,
+        SCOPE_START_CHOWN,
         SCOPE_RUNNING,
         SCOPE_ABANDONED,
         SCOPE_STOP_SIGTERM,
index 43d35a231818e6fd2d64ebf20f6c9d0f6efc8637..c81c69db305b5840b526fa1d73bec2ab63cb83fa 100644 (file)
@@ -697,12 +697,9 @@ static int add_names(
                                 continue;
                         }
 
-                        r = set_consume(*names, TAKE_PTR(inst));
-                        if (r > 0)
-                                log_debug("Unit %s has alias %s.", unit_name, inst);
+                        r = add_name(unit_name, names, inst);
                 } else
                         r = add_name(unit_name, names, *alias);
-
                 if (r < 0)
                         return r;
         }
index 2666f62b6cac4ecfb9d634e5a0c443a26088446a..e5277b4a4bfc7e537d7770dbec60f0f75c039d6f 100644 (file)
@@ -158,6 +158,7 @@ static Virtualization detect_vm_dmi_vendor(void) {
         } dmi_vendor_table[] = {
                 { "KVM",                 VIRTUALIZATION_KVM       },
                 { "OpenStack",           VIRTUALIZATION_KVM       }, /* Detect OpenStack instance as KVM in non x86 architecture */
+                { "KubeVirt",            VIRTUALIZATION_KVM       }, /* Detect KubeVirt instance as KVM in non x86 architecture */
                 { "Amazon EC2",          VIRTUALIZATION_AMAZON    },
                 { "QEMU",                VIRTUALIZATION_QEMU      },
                 { "VMware",              VIRTUALIZATION_VMWARE    }, /* https://kb.vmware.com/s/article/1009458 */
index 4907b3ce3deba1bf378087aa77b84524c36f0a77..10da1c8d7f2973bae0de00fd60d4f7157c4e31e5 100644 (file)
@@ -456,13 +456,21 @@ static const char *get_efi_arch(void) {
         return EFI_MACHINE_TYPE_NAME;
 }
 
-static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
+static int enumerate_binaries(
+                const char *esp_path,
+                const char *path,
+                const char *prefix,
+                char **previous,
+                bool *is_first) {
+
         _cleanup_closedir_ DIR *d = NULL;
         const char *p;
         int c = 0, r;
 
         assert(esp_path);
         assert(path);
+        assert(previous);
+        assert(is_first);
 
         p = prefix_roota(esp_path, path);
         d = opendir(p);
@@ -490,10 +498,24 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
                 r = get_file_version(fd, &v);
                 if (r < 0)
                         return r;
+
+                if (*previous) { /* let's output the previous entry now, since now we know that there will be one more, and can draw the tree glyph properly */
+                        printf("         %s %s%s\n",
+                               *is_first ? "File:" : "     ",
+                               special_glyph(SPECIAL_GLYPH_TREE_BRANCH), *previous);
+                        *is_first = false;
+                        *previous = mfree(*previous);
+                }
+
+                /* Do not output this entry immediately, but store what should be printed in a state
+                 * variable, because we only will know the tree glyph to print (branch or final edge) once we
+                 * read one more entry */
                 if (r > 0)
-                        printf("         File: %s/%s/%s (%s%s%s)\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal());
+                        r = asprintf(previous, "/%s/%s (%s%s%s)", path, de->d_name, ansi_highlight(), v, ansi_normal());
                 else
-                        printf("         File: %s/%s/%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name);
+                        r = asprintf(previous, "/%s/%s", path, de->d_name);
+                if (r < 0)
+                        return log_oom();
 
                 c++;
         }
@@ -502,9 +524,11 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
 }
 
 static int status_binaries(const char *esp_path, sd_id128_t partition) {
-        int r;
+        _cleanup_free_ char *last = NULL;
+        bool is_first = true;
+        int r, k;
 
-        printf("Available Boot Loaders on ESP:\n");
+        printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
 
         if (!esp_path) {
                 printf("          ESP: Cannot find or access mount point of ESP.\n\n");
@@ -516,42 +540,56 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
                 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition));
         printf("\n");
 
-        r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
-        if (r < 0)
-                goto finish;
-        if (r == 0 && !arg_quiet)
-                log_info("systemd-boot not installed in ESP.");
+        r = enumerate_binaries(esp_path, "EFI/systemd", NULL, &last, &is_first);
+        if (r < 0) {
+                printf("\n");
+                return r;
+        }
+
+        k = enumerate_binaries(esp_path, "EFI/BOOT", "boot", &last, &is_first);
+        if (k < 0) {
+                printf("\n");
+                return k;
+        }
+
+        if (last) /* let's output the last entry now, since now we know that there will be no more, and can draw the tree glyph properly */
+                printf("         %s %s%s\n",
+                       is_first ? "File:" : "     ",
+                       special_glyph(SPECIAL_GLYPH_TREE_RIGHT), last);
 
-        r = enumerate_binaries(esp_path, "EFI/BOOT", "boot");
-        if (r < 0)
-                goto finish;
         if (r == 0 && !arg_quiet)
+                log_info("systemd-boot not installed in ESP.");
+        if (k == 0 && !arg_quiet)
                 log_info("No default/fallback boot loader installed in ESP.");
 
-        r = 0;
-
-finish:
         printf("\n");
-        return r;
+        return 0;
 }
 
-static int print_efi_option(uint16_t id, bool in_order) {
+static int print_efi_option(uint16_t id, int *n_printed, bool in_order) {
         _cleanup_free_ char *title = NULL;
         _cleanup_free_ char *path = NULL;
         sd_id128_t partition;
         bool active;
         int r;
 
+        assert(n_printed);
+
         r = efi_get_boot_option(id, &title, &partition, &path, &active);
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to read boot option %u: %m", id);
 
         /* print only configured entries with partition information */
-        if (!path || sd_id128_is_null(partition))
+        if (!path || sd_id128_is_null(partition)) {
+                log_debug("Ignoring boot entry %u without partition information.", id);
                 return 0;
+        }
 
         efi_tilt_backslashes(path);
 
+        if (*n_printed == 0) /* Print section title before first entry */
+                printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
+
         printf("        Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
         printf("           ID: 0x%04X\n", id);
         printf("       Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
@@ -560,12 +598,13 @@ static int print_efi_option(uint16_t id, bool in_order) {
         printf("         File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path);
         printf("\n");
 
-        return 0;
+        (*n_printed)++;
+        return 1;
 }
 
 static int status_variables(void) {
         _cleanup_free_ uint16_t *options = NULL, *order = NULL;
-        int n_options, n_order;
+        int n_options, n_order, n_printed = 0;
 
         n_options = efi_get_boot_options(&options);
         if (n_options == -ENOENT)
@@ -582,9 +621,8 @@ static int status_variables(void) {
                 return log_error_errno(n_order, "Failed to read EFI boot order: %m");
 
         /* print entries in BootOrder first */
-        printf("Boot Loaders Listed in EFI Variables:\n");
         for (int i = 0; i < n_order; i++)
-                print_efi_option(order[i], true);
+                (void) print_efi_option(order[i], &n_printed, /* in_order= */ true);
 
         /* print remaining entries */
         for (int i = 0; i < n_options; i++) {
@@ -592,12 +630,15 @@ static int status_variables(void) {
                         if (options[i] == order[j])
                                 goto next_option;
 
-                print_efi_option(options[i], false);
+                (void) print_efi_option(options[i], &n_printed, /* in_order= */ false);
 
         next_option:
                 continue;
         }
 
+        if (n_printed == 0)
+                printf("No boot loaders listed in EFI Variables.\n\n");
+
         return 0;
 }
 
@@ -655,8 +696,8 @@ static int status_entries(
                 dollar_boot_partition_uuid = esp_partition_uuid;
         }
 
-        printf("Boot Loader Entries:\n"
-               "        $BOOT: %s", dollar_boot_path);
+        printf("%sBoot Loader Entries:%s\n"
+               "        $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path);
         if (!sd_id128_is_null(dollar_boot_partition_uuid))
                 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")",
                        SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
@@ -665,7 +706,7 @@ static int status_entries(
         if (config->default_entry < 0)
                 printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
         else {
-                printf("Default Boot Loader Entry:\n");
+                printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
 
                 r = show_boot_entry(
                                 boot_config_default_entry(config),
@@ -872,7 +913,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
                                        "Failed to resolve path %s%s%s: %m",
                                        p,
                                        root ? " under directory " : "",
-                                       root ?: "");
+                                       strempty(root));
 
         q = path_join("/EFI/systemd/", dest_name);
         if (!q)
@@ -918,7 +959,7 @@ static int install_binaries(const char *esp_path, const char *arch, bool force)
         if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
                 r = chase_symlinks_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT, &path, &d);
         if (r < 0)
-                return log_error_errno(r, "Failed to open boot loader directory %s%s: %m", root ?: "", BOOTLIBDIR);
+                return log_error_errno(r, "Failed to open boot loader directory %s%s: %m", strempty(root), BOOTLIBDIR);
 
         const char *suffix = strjoina(arch, ".efi");
         const char *suffix_signed = strjoina(arch, ".efi.signed");
@@ -1714,7 +1755,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                 static const struct {
                         uint64_t flag;
                         const char *name;
-                } flags[] = {
+                } loader_flags[] = {
                         { EFI_LOADER_FEATURE_BOOT_COUNTING,           "Boot counting"                         },
                         { EFI_LOADER_FEATURE_CONFIG_TIMEOUT,          "Menu timeout control"                  },
                         { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control"         },
@@ -1723,10 +1764,22 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                         { EFI_LOADER_FEATURE_XBOOTLDR,                "Support for XBOOTLDR partition"        },
                         { EFI_LOADER_FEATURE_RANDOM_SEED,             "Support for passing random seed to OS" },
                         { EFI_LOADER_FEATURE_LOAD_DRIVER,             "Load drop-in drivers"                  },
+                        { EFI_LOADER_FEATURE_SORT_KEY,                "Support Type #1 sort-key field"        },
+                        { EFI_LOADER_FEATURE_SAVED_ENTRY,             "Support @saved pseudo-entry"           },
+                        { EFI_LOADER_FEATURE_DEVICETREE,              "Support Type #1 devicetree field"      },
+                };
+                static const struct {
+                        uint64_t flag;
+                        const char *name;
+                } stub_flags[] = {
+                        { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION,     "Stub sets ESP information"                            },
+                        { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS,       "Picks up credentials from boot partition"             },
+                        { EFI_STUB_FEATURE_PICK_UP_SYSEXTS,           "Picks up system extension images from boot partition" },
+                        { EFI_STUB_FEATURE_THREE_PCRS,                "Measures kernel+command line+sysexts"                 },
                 };
                 _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
                 sd_id128_t loader_part_uuid = SD_ID128_NULL;
-                uint64_t loader_features = 0;
+                uint64_t loader_features = 0, stub_features = 0;
                 Tpm2Support s;
                 int have;
 
@@ -1736,6 +1789,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                 read_efi_var(EFI_LOADER_VARIABLE(StubInfo), &stub);
                 read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
                 (void) efi_loader_get_features(&loader_features);
+                (void) efi_stub_get_features(&stub_features);
 
                 if (loader_path)
                         efi_tilt_backslashes(loader_path);
@@ -1745,7 +1799,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                         r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
 
                 SecureBootMode secure = efi_get_secure_boot_mode();
-                printf("System:\n");
+                printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
                 printf("      Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
                 printf(" Firmware Arch: %s\n", get_efi_arch());
                 printf("   Secure Boot: %sd (%s)\n",
@@ -1774,11 +1828,11 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                 }
                 printf("\n");
 
-                printf("Current Boot Loader:\n");
+                printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
                 printf("      Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
 
-                for (size_t i = 0; i < ELEMENTSOF(flags); i++)
-                        print_yes_no_line(i == 0, FLAGS_SET(loader_features, flags[i].flag), flags[i].name);
+                for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
+                        print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
 
                 sd_id128_t bootloader_esp_uuid;
                 bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
@@ -1790,8 +1844,11 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                                SD_ID128_FORMAT_VAL(bootloader_esp_uuid),
                                SD_ID128_FORMAT_VAL(esp_uuid));
 
-                if (stub)
+                if (stub) {
                         printf("         Stub: %s\n", stub);
+                        for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
+                                print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
+                }
                 if (!sd_id128_is_null(loader_part_uuid))
                         printf("          ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
                                SD_ID128_FORMAT_VAL(loader_part_uuid));
@@ -1800,7 +1857,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                 printf("         File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
                 printf("\n");
 
-                printf("Random Seed:\n");
+                printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
                 have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)), F_OK) >= 0;
                 printf(" Passed to OS: %s\n", yes_no(have));
                 have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
@@ -1819,7 +1876,9 @@ static int verb_status(int argc, char *argv[], void *userdata) {
 
                 printf("\n");
         } else
-                printf("System:\n    Not booted with EFI\n\n");
+                printf("%sSystem:%s\n"
+                       "Not booted with EFI\n\n",
+                       ansi_underline(), ansi_normal());
 
         if (arg_esp_path) {
                 k = status_binaries(arg_esp_path, esp_uuid);
index ece1c11cd5ed078590f02512d0cec8903cf0e7a2..9c1d95e67256d43f1dcfb8e536dd54e32863e993 100644 (file)
@@ -48,6 +48,7 @@ enum loader_type {
         LOADER_EFI,
         LOADER_LINUX,         /* Boot loader spec type #1 entries */
         LOADER_UNIFIED_LINUX, /* Boot loader spec type #2 entries */
+        LOADER_SECURE_BOOT_KEYS,
 };
 
 typedef struct {
@@ -88,6 +89,7 @@ typedef struct {
         bool auto_entries;
         bool auto_firmware;
         bool reboot_for_bitlocker;
+        secure_boot_enroll secure_boot_enroll;
         bool force_menu;
         bool use_saved_entry;
         bool use_saved_entry_efivar;
@@ -528,6 +530,17 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
           ps_bool(L"  reboot-for-bitlocker: %s\n", config->reboot_for_bitlocker);
         ps_string(L"      random-seed-mode: %s\n", random_seed_modes_table[config->random_seed_mode]);
 
+        switch (config->secure_boot_enroll) {
+        case ENROLL_OFF:
+                Print(L"    secure-boot-enroll: off\n"); break;
+        case ENROLL_MANUAL:
+                Print(L"    secure-boot-enroll: manual\n"); break;
+        case ENROLL_FORCE:
+                Print(L"    secure-boot-enroll: force\n"); break;
+        default:
+                assert_not_reached();
+        }
+
         switch (config->console_mode) {
         case CONSOLE_MODE_AUTO:
             Print(L" console-mode (config): %s\n", L"auto"); break;
@@ -1217,6 +1230,17 @@ static void config_defaults_load_from_file(Config *config, char *content) {
                         err = parse_boolean(value, &config->reboot_for_bitlocker);
                         if (err != EFI_SUCCESS)
                                 log_error_stall(L"Error parsing 'reboot-for-bitlocker' config option: %a", value);
+                }
+
+                if (streq8(key, "secure-boot-enroll")) {
+                        if (streq8(value, "manual"))
+                                config->secure_boot_enroll = ENROLL_MANUAL;
+                        else if (streq8(value, "force"))
+                                config->secure_boot_enroll = ENROLL_FORCE;
+                        else if (streq8(value, "off"))
+                                config->secure_boot_enroll = ENROLL_OFF;
+                        else
+                                log_error_stall(L"Error parsing 'secure-boot-enroll' config option: %a", value);
                         continue;
                 }
 
@@ -1519,6 +1543,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
                 .auto_entries = true,
                 .auto_firmware = true,
                 .reboot_for_bitlocker = false,
+                .secure_boot_enroll = ENROLL_MANUAL,
                 .random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN,
                 .idx_default_efivar = IDX_INVALID,
                 .console_mode = CONSOLE_MODE_KEEP,
@@ -2339,7 +2364,7 @@ static EFI_STATUS image_start(
                 loaded_image->LoadOptionsSize = strsize16(options);
 
                 /* Try to log any options to the TPM, especially to catch manually edited options */
-                (void) tpm_log_load_options(options);
+                (void) tpm_log_load_options(options, NULL);
         }
 
         efivar_set_time_usec(LOADER_GUID, L"LoaderTimeExecUSec", 0);
@@ -2429,6 +2454,55 @@ static void save_selected_entry(const Config *config, const ConfigEntry *entry)
                 (void) efivar_set(LOADER_GUID, L"LoaderEntryLastBooted", NULL, EFI_VARIABLE_NON_VOLATILE);
 }
 
+static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir) {
+        EFI_STATUS err;
+        _cleanup_(file_closep) EFI_FILE *keys_basedir = NULL;
+
+        if (secure_boot_mode() != SECURE_BOOT_SETUP)
+                return EFI_SUCCESS;
+
+        /* the lack of a 'keys' directory is not fatal and is silently ignored */
+        err = open_directory(root_dir, u"\\loader\\keys", &keys_basedir);
+        if (err == EFI_NOT_FOUND)
+                return EFI_SUCCESS;
+        if (err != EFI_SUCCESS)
+                return err;
+
+        for (;;) {
+                _cleanup_free_ EFI_FILE_INFO *dirent = NULL;
+                size_t dirent_size = 0;
+                ConfigEntry *entry = NULL;
+
+                err = readdir_harder(keys_basedir, &dirent, &dirent_size);
+                if (err != EFI_SUCCESS || !dirent)
+                        return err;
+
+                if (dirent->FileName[0] == '.')
+                        continue;
+
+                if (!FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY))
+                        continue;
+
+                entry = xnew(ConfigEntry, 1);
+                *entry = (ConfigEntry) {
+                        .id = xpool_print(L"secure-boot-keys-%s", dirent->FileName),
+                        .title = xpool_print(L"Enroll Secure Boot keys: %s", dirent->FileName),
+                        .path = xpool_print(L"\\loader\\keys\\%s", dirent->FileName),
+                        .type = LOADER_SECURE_BOOT_KEYS,
+                        .tries_done = -1,
+                        .tries_left = -1,
+                };
+                config_add_entry(config, entry);
+
+                if (config->secure_boot_enroll == ENROLL_FORCE && strcaseeq16(dirent->FileName, u"auto"))
+                        /* if we auto enroll successfully this call does not return, if it fails we still
+                         * want to add other potential entries to the menu */
+                        secure_boot_enroll_at(root_dir, entry->path);
+        }
+
+        return EFI_SUCCESS;
+}
+
 static void export_variables(
                 EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
                 const char16_t *loaded_image_path,
@@ -2443,6 +2517,9 @@ static void export_variables(
                 EFI_LOADER_FEATURE_XBOOTLDR |
                 EFI_LOADER_FEATURE_RANDOM_SEED |
                 EFI_LOADER_FEATURE_LOAD_DRIVER |
+                EFI_LOADER_FEATURE_SORT_KEY |
+                EFI_LOADER_FEATURE_SAVED_ENTRY |
+                EFI_LOADER_FEATURE_DEVICETREE |
                 0;
 
         _cleanup_free_ char16_t *infostr = NULL, *typestr = NULL;
@@ -2515,6 +2592,13 @@ static void config_load_all_entries(
                 config_add_entry(config, entry);
         }
 
+        /* find if secure boot signing keys exist and autoload them if necessary
+        otherwise creates menu entries so that the user can load them manually
+        if the secure-boot-enroll variable is set to no (the default), we do not
+        even search for keys on the ESP */
+        if (config->secure_boot_enroll != ENROLL_OFF)
+                secure_boot_discover_keys(config, root_dir);
+
         if (config->entry_count == 0)
                 return;
 
@@ -2603,6 +2687,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                                 break;
                 }
 
+                /* if auto enrollment is activated, we try to load keys for the given entry. */
+                if (entry->type == LOADER_SECURE_BOOT_KEYS && config.secure_boot_enroll != ENROLL_OFF) {
+                        err = secure_boot_enroll_at(root_dir, entry->path);
+                        if (err != EFI_SUCCESS)
+                                return err;
+                        continue;
+                }
+
                 /* Run special entry like "reboot" now. Those that have a loader
                  * will be handled by image_start() instead. */
                 if (entry->call && !entry->loader) {
index ad469af034ddd2b1d70c3cb131f9f36b6142ab00..0d41102d2d8c9e9ec493725a5b26d483015a2543 100644 (file)
@@ -315,7 +315,8 @@ EFI_STATUS pack_cpio(
                 UINTN n_tpm_pcr,
                 const char16_t *tpm_description,
                 void **ret_buffer,
-                UINTN *ret_buffer_size) {
+                UINTN *ret_buffer_size,
+                bool *ret_measured) {
 
         _cleanup_(file_closep) EFI_FILE *root = NULL, *extra_dir = NULL;
         UINTN dirent_size = 0, buffer_size = 0, n_items = 0, n_allocated = 0;
@@ -324,6 +325,7 @@ EFI_STATUS pack_cpio(
         _cleanup_(strv_freep) char16_t **items = NULL;
         _cleanup_free_ void *buffer = NULL;
         uint32_t inode = 1; /* inode counter, so that each item gets a new inode */
+        int measured = -1;
         EFI_STATUS err;
 
         assert(loaded_image);
@@ -332,20 +334,14 @@ EFI_STATUS pack_cpio(
         assert(ret_buffer);
         assert(ret_buffer_size);
 
-        if (!loaded_image->DeviceHandle) {
-                *ret_buffer = NULL;
-                *ret_buffer_size = 0;
-                return EFI_SUCCESS;
-        }
+        if (!loaded_image->DeviceHandle)
+                goto nothing;
 
         err = open_volume(loaded_image->DeviceHandle, &root);
-        if (err == EFI_UNSUPPORTED) {
+        if (err == EFI_UNSUPPORTED)
                 /* Error will be unsupported if the bootloader doesn't implement the file system protocol on
                  * its file handles. */
-                *ret_buffer = NULL;
-                *ret_buffer_size = 0;
-                return EFI_SUCCESS;
-        }
+                goto nothing;
         if (err != EFI_SUCCESS)
                 return log_error_status_stall(
                                 err, L"Unable to open root directory: %r", err);
@@ -354,12 +350,9 @@ EFI_STATUS pack_cpio(
                 dropin_dir = rel_dropin_dir = xpool_print(L"%D.extra.d", loaded_image->FilePath);
 
         err = open_directory(root, dropin_dir, &extra_dir);
-        if (err == EFI_NOT_FOUND) {
+        if (err == EFI_NOT_FOUND)
                 /* No extra subdir, that's totally OK */
-                *ret_buffer = NULL;
-                *ret_buffer_size = 0;
-                return EFI_SUCCESS;
-        }
+                goto nothing;
         if (err != EFI_SUCCESS)
                 return log_error_status_stall(err, L"Failed to open extra directory of loaded image: %r", err);
 
@@ -401,12 +394,9 @@ EFI_STATUS pack_cpio(
                 items[n_items] = NULL; /* Let's always NUL terminate, to make freeing via strv_free() easy */
         }
 
-        if (n_items == 0) {
+        if (n_items == 0)
                 /* Empty directory */
-                *ret_buffer = NULL;
-                *ret_buffer_size = 0;
-                return EFI_SUCCESS;
-        }
+                goto nothing;
 
         /* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements
          * are not dependent on read order) */
@@ -444,17 +434,40 @@ EFI_STATUS pack_cpio(
                 return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
 
         for (UINTN i = 0; i < n_tpm_pcr; i++) {
+                bool m;
+
+                if (tpm_pcr[i] == UINT32_MAX) /* Disabled */
+                        continue;
+
                 err = tpm_log_event(
                                 tpm_pcr[i],
                                 POINTER_TO_PHYSICAL_ADDRESS(buffer),
                                 buffer_size,
-                                tpm_description);
-                if (err != EFI_SUCCESS)
+                                tpm_description,
+                                &m);
+                if (err != EFI_SUCCESS) {
                         log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr[i], tpm_description, err);
+                        measured = false;
+                        continue;
+                }
+
+                measured = measured < 0 ? m : (measured && m);
         }
 
         *ret_buffer = TAKE_PTR(buffer);
         *ret_buffer_size = buffer_size;
 
+        if (ret_measured)
+                *ret_measured = measured;
+
+        return EFI_SUCCESS;
+
+nothing:
+        *ret_buffer = NULL;
+        *ret_buffer_size = 0;
+
+        if (ret_measured)
+                *ret_measured = true;
+
         return EFI_SUCCESS;
 }
index e3d206d7e89c74254207488eaa5a65c4ab40e5cf..672a751825f903b9015f02f09684f50eee7b95d5 100644 (file)
@@ -2,6 +2,7 @@
 #pragma once
 
 #include <efi.h>
+#include <stdbool.h>
 #include <uchar.h>
 
 EFI_STATUS pack_cpio(
@@ -15,4 +16,5 @@ EFI_STATUS pack_cpio(
                 UINTN n_tpm_pcr,
                 const char16_t *tpm_description,
                 void **ret_buffer,
-                UINTN *ret_buffer_size);
+                UINTN *ret_buffer_size,
+                bool *ret_measured);
index 0e04a6ee0666ae351de71ae8abe6df212df9210e..3cbffdbbeb59489e9fbea477415527e8b6b90f76 100644 (file)
@@ -141,7 +141,7 @@ EFI_STATUS linux_exec(
         */
         /* allocate SizeOfImage + SectionAlignment because the new_buffer can move up to Alignment-1 bytes */
         kernel.num = EFI_SIZE_TO_PAGES(ALIGN_TO(kernel_size_of_image, kernel_alignment) + kernel_alignment);
-        err = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, kernel.num, &kernel.addr);
+        err = BS->AllocatePages(AllocateAnyPages, EfiLoaderCode, kernel.num, &kernel.addr);
         if (err != EFI_SUCCESS)
                 return EFI_OUT_OF_RESOURCES;
         new_buffer = PHYSICAL_ADDRESS_TO_POINTER(ALIGN_TO(kernel.addr, kernel_alignment));
index 7108554681814820a116f7871f0acfb522d94808..9a16920787bf3f41d11a616368fae23d6f2f2c39 100644 (file)
@@ -5,6 +5,7 @@
 #include <efi.h>
 #include <efilib.h>
 
+#include "tpm-pcr.h"
 #include "macro-fundamental.h"
 #include "measure.h"
 #include "missing_efi.h"
@@ -141,43 +142,79 @@ bool tpm_present(void) {
         return tcg2_interface_check() || tcg1_interface_check();
 }
 
-EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description) {
-        EFI_TCG *tpm1;
+EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description, bool *ret_measured) {
         EFI_TCG2 *tpm2;
+        EFI_STATUS err;
 
         assert(description);
 
-        /* PCR disabled */
-        if (pcrindex == UINT32_MAX)
+        /* If EFI_SUCCESS is returned, will initialize ret_measured to true if we actually measured
+         * something, or false if measurement was turned off. */
+
+        if (pcrindex == UINT32_MAX) { /* PCR disabled? */
+                if (ret_measured)
+                        *ret_measured = false;
+
                 return EFI_SUCCESS;
+        }
 
         tpm2 = tcg2_interface_check();
         if (tpm2)
-                return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
+                err = tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
+        else {
+                EFI_TCG *tpm1;
 
-        tpm1 = tcg1_interface_check();
-        if (tpm1)
-                return tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description);
+                tpm1 = tcg1_interface_check();
+                if (tpm1)
+                        err = tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description);
+                else {
+                        /* No active TPM found, so don't return an error */
 
-        /* No active TPM found, so don't return an error */
-        return EFI_SUCCESS;
+                        if (ret_measured)
+                                *ret_measured = false;
+
+                        return EFI_SUCCESS;
+                }
+        }
+
+        if (err == EFI_SUCCESS && ret_measured)
+                *ret_measured = true;
+
+        return err;
+}
+
+EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char *description, bool *ret_measured) {
+        _cleanup_free_ char16_t *c = NULL;
+
+        if (description)
+                c = xstra_to_str(description);
+
+        return tpm_log_event(pcrindex, buffer, buffer_size, c, ret_measured);
 }
 
-EFI_STATUS tpm_log_load_options(const char16_t *load_options) {
+EFI_STATUS tpm_log_load_options(const char16_t *load_options, bool *ret_measured) {
+        int measured = -1;
         EFI_STATUS err;
 
         /* Measures a load options string into the TPM2, i.e. the kernel command line */
 
         for (UINTN i = 0; i < 2; i++) {
                 uint32_t pcr = i == 0 ? TPM_PCR_INDEX_KERNEL_PARAMETERS : TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT;
+                bool m;
+
+                if (pcr == UINT32_MAX) /* Skip this one, if it's invalid, so that our 'measured' return value is not corrupted by it */
+                        continue;
 
-                err = tpm_log_event(pcr,
-                                    POINTER_TO_PHYSICAL_ADDRESS(load_options),
-                                    strsize16(load_options), load_options);
+                err = tpm_log_event(pcr, POINTER_TO_PHYSICAL_ADDRESS(load_options), strsize16(load_options), load_options, &m);
                 if (err != EFI_SUCCESS)
                         return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement to PCR %u: %r", pcr, err);
+
+                measured = measured < 0 ? m : (measured && m);
         }
 
+        if (ret_measured)
+                *ret_measured = measured < 0 ? false : measured;
+
         return EFI_SUCCESS;
 }
 
index 057f4a44d6c71248c40c323c491bacdff834e6a8..19a50f47e7d37bbb6c090edbfe82638101e370b8 100644 (file)
@@ -5,25 +5,12 @@
 #include <stdbool.h>
 #include <uchar.h>
 
-/* This TPM PCR is where we extend the kernel command line and any passed credentials here. */
-#define TPM_PCR_INDEX_KERNEL_PARAMETERS 12U
-
-/* We used to write the kernel command line/credentials into PCR 8, in systemd <= 250. Let's provide for
- * some compatibility. (Remove in 2023!) */
-#if EFI_TPM_PCR_COMPAT
-#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT 8U
-#else
-#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT UINT32_MAX
-#endif
-
-/* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */
-#define TPM_PCR_INDEX_INITRD 4U
-
 #if ENABLE_TPM
 
 bool tpm_present(void);
-EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description);
-EFI_STATUS tpm_log_load_options(const char16_t *cmdline);
+EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description, bool *ret_measured);
+EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char *description, bool *ret_measured);
+EFI_STATUS tpm_log_load_options(const char16_t *cmdline, bool *ret_measured);
 
 #else
 
@@ -31,11 +18,21 @@ static inline bool tpm_present(void) {
         return false;
 }
 
-static inline EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description) {
+static inline EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description, bool *ret_measured) {
+        if (ret_measured)
+                *ret_measured = false;
+        return EFI_SUCCESS;
+}
+
+static inline EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char *description, bool *ret_measured) {
+        if (ret_measured)
+                *ret_measured = false;
         return EFI_SUCCESS;
 }
 
-static inline EFI_STATUS tpm_log_load_options(const char16_t *cmdline) {
+static inline EFI_STATUS tpm_log_load_options(const char16_t *cmdline, bool *ret_measured) {
+        if (ret_measured)
+                *ret_measured = false;
         return EFI_SUCCESS;
 }
 
index 0129fcd070264b0faa038336dccbce29b01e1a3e..95785dfd5d83d41895079a7f38b24b1a7877533f 100644 (file)
@@ -199,6 +199,12 @@ efi_cflags = [
         ]
 )
 
+# On some distros, sd-boot/-stub may trigger some bug somewhere that will cause
+# kernel execution to fail. The cause seems to be purely based on code size and
+# always compiling with at least -O1 will work around that.
+# https://github.com/systemd/systemd/issues/24202
+efi_cflags += '-O1'
+
 efi_cflags += cc.get_supported_arguments({
         'ia32':   ['-mno-sse', '-mno-mmx'],
         'x86_64': ['-mno-red-zone', '-mno-sse', '-mno-mmx'],
@@ -260,11 +266,19 @@ efi_ldflags = [
         efi_crt0,
 ]
 
-possible_link_flags = [
-        '-Wl,--no-warn-execstack',
-        '-Wl,--no-warn-rwx-segments',
-]
-efi_ldflags += cc.get_supported_link_arguments(possible_link_flags)
+foreach arg : ['-Wl,--no-warn-execstack',
+               '-Wl,--no-warn-rwx-segments']
+        # We need to check the correct linker for supported args. This is what
+        # cc.has_multi_link_arguments() is for, but it helpfully overrides our
+        # choice of linker by putting its own -fuse-ld= arg after ours.
+        if run_command('bash', '-c',
+                       'exec "$@" -x c -o/dev/null <(echo "int main(void){return 0;}")' +
+                       ' -fuse-ld=' + efi_ld + ' -Wl,--fatal-warnings ' + arg,
+                       'bash', cc.cmd_array(),
+                       check : false).returncode() == 0
+                efi_ldflags += arg
+        endif
+endforeach
 
 if efi_arch[1] in ['aarch64', 'arm', 'riscv64']
         efi_ldflags += ['-shared']
@@ -355,6 +369,7 @@ efi_headers = files(
 
 common_sources = files(
         'assert.c',
+        'console.c',
         'devicetree.c',
         'disk.c',
         'efi-string.c',
@@ -369,7 +384,6 @@ common_sources = files(
 
 systemd_boot_sources = files(
         'boot.c',
-        'console.c',
         'drivers.c',
         'random-seed.c',
         'shim.c',
index f9700e342294143e7142fa5cd8990309d3f63e4b..4e80acca5632a220763d3efcc7166cd977cf713c 100644 (file)
@@ -385,3 +385,10 @@ typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
 } EFI_CONSOLE_CONTROL_PROTOCOL;
 
 #endif
+
+#ifndef EFI_IMAGE_SECURITY_DATABASE_VARIABLE
+
+#define EFI_IMAGE_SECURITY_DATABASE_VARIABLE \
+        { 0xd719b2cb, 0x3d3a, 0x4596, {0xa3, 0xbc, 0xda, 0xd0,  0xe, 0x67, 0x65, 0x6f }}
+
+#endif
index 31f634a4d7ab71d79a60967cf05d6477d5b58325..cf7a464d0a4826340cee7db409e4aa3909841a81 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "sbat.h"
 #include "secure-boot.h"
+#include "console.h"
 #include "util.h"
 
 bool secure_boot_enabled(void) {
@@ -33,3 +34,95 @@ SecureBootMode secure_boot_mode(void) {
 #ifdef SBAT_DISTRO
 static const char sbat[] _used_ _section_(".sbat") = SBAT_SECTION_TEXT;
 #endif
+
+EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
+        assert(root_dir);
+        assert(path);
+
+        EFI_STATUS err;
+
+        clear_screen(COLOR_NORMAL);
+
+        Print(L"Enrolling secure boot keys from directory: %s\n"
+              L"Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n",
+              path);
+
+        unsigned timeout_sec = 15;
+        for(;;) {
+                /* Enrolling secure boot keys is safe to do in virtualized environments as there is nothing
+                 * we can brick there. */
+                if (in_hypervisor())
+                        break;
+
+                PrintAt(0, ST->ConOut->Mode->CursorRow, L"Enrolling in %2u s, press any key to abort.", timeout_sec);
+
+                uint64_t key;
+                err = console_key_read(&key, 1000 * 1000);
+                if (err == EFI_NOT_READY)
+                        continue;
+                if (err == EFI_TIMEOUT) {
+                        if (timeout_sec == 0) /* continue enrolling keys */
+                                break;
+                        timeout_sec--;
+                        continue;
+                }
+                if (err != EFI_SUCCESS)
+                        return log_error_status_stall(err, L"Error waiting for user input to enroll Secure Boot keys: %r", err);
+
+                /* user aborted, returning EFI_SUCCESS here allows the user to go back to the menu */
+                return EFI_SUCCESS;
+        }
+
+        _cleanup_(file_closep) EFI_FILE *dir = NULL;
+
+        err = open_directory(root_dir, path, &dir);
+        if (err != EFI_SUCCESS)
+                return log_error_status_stall(err, L"Failed opening keys directory %s: %r", path, err);
+
+        struct {
+                const char16_t *name;
+                const char16_t *filename;
+                const EFI_GUID vendor;
+                char *buffer;
+                size_t size;
+        } sb_vars[] = {
+                { u"db",  u"db.auth",  EFI_IMAGE_SECURITY_DATABASE_VARIABLE, NULL, 0 },
+                { u"KEK", u"KEK.auth", EFI_GLOBAL_VARIABLE, NULL, 0 },
+                { u"PK",  u"PK.auth",  EFI_GLOBAL_VARIABLE, NULL, 0 },
+        };
+
+        /* Make sure all keys files exist before we start enrolling them by loading them from the disk first. */
+        for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++) {
+                err = file_read(dir, sb_vars[i].filename, 0, 0, &sb_vars[i].buffer, &sb_vars[i].size);
+                if (err != EFI_SUCCESS) {
+                        log_error_stall(L"Failed reading file %s\\%s: %r", path, sb_vars[i].filename, err);
+                        goto out_deallocate;
+                }
+        }
+
+        for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++) {
+                uint32_t sb_vars_opts =
+                        EFI_VARIABLE_NON_VOLATILE |
+                        EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                        EFI_VARIABLE_RUNTIME_ACCESS |
+                        EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+
+                err = efivar_set_raw(&sb_vars[i].vendor, sb_vars[i].name, sb_vars[i].buffer, sb_vars[i].size, sb_vars_opts);
+                if (err != EFI_SUCCESS) {
+                        log_error_stall(L"Failed to write %s secure boot variable: %r", sb_vars[i].name, err);
+                        goto out_deallocate;
+                }
+        }
+
+        /* The system should be in secure boot mode now and we could continue a regular boot. But at least
+         * TPM PCR7 measurements should change on next boot. Reboot now so that any OS we load does not end
+         * up relying on the old PCR state. */
+        RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
+        assert_not_reached();
+
+out_deallocate:
+        for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++)
+                FreePool(sb_vars[i].buffer);
+
+        return err;
+}
index ce43423fcebafd696dc0ebd980ba83ad397868be..ff434ed1adbca1606d03b61340bd790b21c4bc99 100644 (file)
@@ -4,5 +4,13 @@
 #include <efi.h>
 #include "efivars-fundamental.h"
 
+typedef enum {
+        ENROLL_OFF,         /* no Secure Boot key enrollment whatsoever, even manual entries are not generated */
+        ENROLL_MANUAL,      /* Secure Boot key enrollment is strictly manual: manual entries are generated and need to be selected by the user */
+        ENROLL_FORCE,       /* Secure Boot key enrollment may be automatic if it is available but might not be safe */
+} secure_boot_enroll;
+
 bool secure_boot_enabled(void);
 SecureBootMode secure_boot_mode(void);
+
+EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path);
index 2acd5e573856d7ee7df9aa62a15d586a18fff95c..003ae8c99a0bc192bac180a50282093e4f98d7b0 100644 (file)
@@ -12,6 +12,7 @@
 #include "pe.h"
 #include "secure-boot.h"
 #include "splash.h"
+#include "tpm-pcr.h"
 #include "util.h"
 
 /* magic string to find in the binary image */
@@ -102,6 +103,13 @@ static EFI_STATUS combine_initrd(
 }
 
 static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
+        static const uint64_t stub_features =
+                EFI_STUB_FEATURE_REPORT_BOOT_PARTITION |    /* We set LoaderDevicePartUUID */
+                EFI_STUB_FEATURE_PICK_UP_CREDENTIALS |      /* We pick up credentials from the boot partition */
+                EFI_STUB_FEATURE_PICK_UP_SYSEXTS |          /* We pick up system extensions from the boot partition */
+                EFI_STUB_FEATURE_THREE_PCRS |               /* We can measure kernel image, parameters and sysext */
+                0;
+
         char16_t uuid[37];
 
         assert(loaded_image);
@@ -142,31 +150,15 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
                 efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
         }
 
-        /* add StubInfo */
-        if (efivar_get_raw(LOADER_GUID, L"StubInfo", NULL, NULL) != EFI_SUCCESS)
-                efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0);
-}
 
-EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+        /* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our
+         * own data) */
+        (void) efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0);
 
-        enum {
-                SECTION_CMDLINE,
-                SECTION_LINUX,
-                SECTION_INITRD,
-                SECTION_SPLASH,
-                SECTION_DTB,
-                _SECTION_MAX,
-        };
-
-        static const char * const sections[_SECTION_MAX + 1] = {
-                [SECTION_CMDLINE] = ".cmdline",
-                [SECTION_LINUX]   = ".linux",
-                [SECTION_INITRD]  = ".initrd",
-                [SECTION_SPLASH]  = ".splash",
-                [SECTION_DTB]     = ".dtb",
-                NULL,
-        };
+        (void) efivar_set_uint64_le(LOADER_GUID, L"StubFeatures", stub_features, 0);
+}
 
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         UINTN cmdline_len = 0, linux_size, initrd_size, dt_size;
         UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0;
         _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL;
@@ -174,10 +166,11 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
         _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
         EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
-        UINTN addrs[_SECTION_MAX] = {};
-        UINTN szs[_SECTION_MAX] = {};
+        UINTN addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
         char *cmdline = NULL;
         _cleanup_free_ char *cmdline_owned = NULL;
+        int sections_measured = -1, parameters_measured = -1;
+        bool sysext_measured = false, m;
         EFI_STATUS err;
 
         InitializeLib(image, sys_table);
@@ -195,83 +188,136 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
         if (err != EFI_SUCCESS)
                 return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
 
-        err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, szs);
-        if (err != EFI_SUCCESS || szs[SECTION_LINUX] == 0) {
+        err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, addrs, szs);
+        if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) {
                 if (err == EFI_SUCCESS)
                         err = EFI_NOT_FOUND;
                 return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
         }
 
+        /* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written
+         * into so far), so that we have one PCR that we can nicely write policies against because it
+         * contains all static data of this image, and thus can be easily be pre-calculated. */
+        for (UnifiedSection section = 0; section < _UNIFIED_SECTION_MAX; section++) {
+                m = false;
+
+                if (szs[section] == 0) /* not found */
+                        continue;
+
+                /* First measure the name of the section */
+                (void) tpm_log_event_ascii(
+                                TPM_PCR_INDEX_KERNEL_IMAGE,
+                                POINTER_TO_PHYSICAL_ADDRESS(unified_sections[section]),
+                                strsize8(unified_sections[section]), /* including NUL byte */
+                                unified_sections[section],
+                                &m);
+
+                sections_measured = sections_measured < 0 ? m : (sections_measured && m);
+
+                /* Then measure the data of the section */
+                (void) tpm_log_event_ascii(
+                                TPM_PCR_INDEX_KERNEL_IMAGE,
+                                POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[section],
+                                szs[section],
+                                unified_sections[section],
+                                &m);
+
+                sections_measured = sections_measured < 0 ? m : (sections_measured && m);
+        }
+
+        /* After we are done, set an EFI variable that tells userspace this was done successfully, and encode
+         * in it which PCR was used. */
+        if (sections_measured > 0)
+                (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrKernelImage", TPM_PCR_INDEX_KERNEL_IMAGE, 0);
+
         /* Show splash screen as early as possible */
-        graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL);
+        graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH], NULL);
 
-        if (szs[SECTION_CMDLINE] > 0) {
-                cmdline = (char *) loaded_image->ImageBase + addrs[SECTION_CMDLINE];
-                cmdline_len = szs[SECTION_CMDLINE];
+        if (szs[UNIFIED_SECTION_CMDLINE] > 0) {
+                cmdline = (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE];
+                cmdline_len = szs[UNIFIED_SECTION_CMDLINE];
         }
 
-        /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace the built-in one */
-        if ((!secure_boot_enabled() || cmdline_len == 0) && loaded_image->LoadOptionsSize > 0 &&
-            *(char16_t *) loaded_image->LoadOptions > 0x1F) {
+        /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace
+         * the built-in one. We also do a superficial check whether first character of passed command line
+         * is printable character (for compat with some Dell systems which fill in garbage?). */
+        if ((!secure_boot_enabled() || cmdline_len == 0) &&
+            loaded_image->LoadOptionsSize > 0 &&
+            ((char16_t *) loaded_image->LoadOptions)[0] > 0x1F) {
                 cmdline_len = (loaded_image->LoadOptionsSize / sizeof(char16_t)) * sizeof(char);
-                cmdline = cmdline_owned = xmalloc(cmdline_len);
+                cmdline = cmdline_owned = xnew(char, cmdline_len);
 
-                for (UINTN i = 0; i < cmdline_len; i++)
-                        cmdline[i] = ((char16_t *) loaded_image->LoadOptions)[i];
+                for (UINTN i = 0; i < cmdline_len; i++) {
+                        char16_t c = ((char16_t *) loaded_image->LoadOptions)[i];
+                        cmdline[i] = c > 0x1F && c < 0x7F ? c : ' '; /* convert non-printable and non_ASCII characters to spaces. */
+                }
 
                 /* Let's measure the passed kernel command line into the TPM. Note that this possibly
                  * duplicates what we already did in the boot menu, if that was already used. However, since
                  * we want the boot menu to support an EFI binary, and want to this stub to be usable from
                  * any boot menu, let's measure things anyway. */
-                (void) tpm_log_load_options(loaded_image->LoadOptions);
+                m = false;
+                (void) tpm_log_load_options(loaded_image->LoadOptions, &m);
+                parameters_measured = m;
         }
 
         export_variables(loaded_image);
 
-        (void) pack_cpio(loaded_image,
-                         NULL,
-                         L".cred",
-                         ".extra/credentials",
-                         /* dir_mode= */ 0500,
-                         /* access_mode= */ 0400,
-                         /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
-                         /* n_tpm_pcr= */ 2,
-                         L"Credentials initrd",
-                         &credential_initrd,
-                         &credential_initrd_size);
-
-        (void) pack_cpio(loaded_image,
-                         L"\\loader\\credentials",
-                         L".cred",
-                         ".extra/global_credentials",
-                         /* dir_mode= */ 0500,
-                         /* access_mode= */ 0400,
-                         /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
-                         /* n_tpm_pcr= */ 2,
-                         L"Global credentials initrd",
-                         &global_credential_initrd,
-                         &global_credential_initrd_size);
-
-        (void) pack_cpio(loaded_image,
-                         NULL,
-                         L".raw",
-                         ".extra/sysext",
-                         /* dir_mode= */ 0555,
-                         /* access_mode= */ 0444,
-                         /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD },
-                         /* n_tpm_pcr= */ 1,
-                         L"System extension initrd",
-                         &sysext_initrd,
-                         &sysext_initrd_size);
-
-        linux_size = szs[SECTION_LINUX];
-        linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_LINUX];
-
-        initrd_size = szs[SECTION_INITRD];
-        initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_INITRD] : 0;
-
-        dt_size = szs[SECTION_DTB];
-        dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_DTB] : 0;
+        if (pack_cpio(loaded_image,
+                      NULL,
+                      L".cred",
+                      ".extra/credentials",
+                      /* dir_mode= */ 0500,
+                      /* access_mode= */ 0400,
+                      /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
+                      /* n_tpm_pcr= */ 2,
+                      L"Credentials initrd",
+                      &credential_initrd,
+                      &credential_initrd_size,
+                      &m) == EFI_SUCCESS)
+                parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
+
+        if (pack_cpio(loaded_image,
+                      L"\\loader\\credentials",
+                      L".cred",
+                      ".extra/global_credentials",
+                      /* dir_mode= */ 0500,
+                      /* access_mode= */ 0400,
+                      /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
+                      /* n_tpm_pcr= */ 2,
+                      L"Global credentials initrd",
+                      &global_credential_initrd,
+                      &global_credential_initrd_size,
+                      &m) == EFI_SUCCESS)
+                parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
+
+        if (pack_cpio(loaded_image,
+                      NULL,
+                      L".raw",
+                      ".extra/sysext",
+                      /* dir_mode= */ 0555,
+                      /* access_mode= */ 0444,
+                      /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD_SYSEXTS },
+                      /* n_tpm_pcr= */ 1,
+                      L"System extension initrd",
+                      &sysext_initrd,
+                      &sysext_initrd_size,
+                      &m) == EFI_SUCCESS)
+                sysext_measured = m;
+
+        if (parameters_measured > 0)
+                (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrKernelParameters", TPM_PCR_INDEX_KERNEL_PARAMETERS, 0);
+        if (sysext_measured)
+                (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrInitRDSysExts", TPM_PCR_INDEX_INITRD_SYSEXTS, 0);
+
+        linux_size = szs[UNIFIED_SECTION_LINUX];
+        linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_LINUX];
+
+        initrd_size = szs[UNIFIED_SECTION_INITRD];
+        initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
+
+        dt_size = szs[UNIFIED_SECTION_DTB];
+        dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0;
 
         if (credential_initrd || global_credential_initrd || sysext_initrd) {
                 /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
index 16e488c9586cb2af87b8b9a29cda357971646b32..1b74ba15d07915390ce38ee9ba7eb4c7b9fd4571 100644 (file)
@@ -2,35 +2,17 @@
 
 #include <efi.h>
 #include <efilib.h>
-#if defined(__i386__) || defined(__x86_64__)
-#include <cpuid.h>
-#endif
-#include <stdbool.h>
 
 #include "ticks.h"
-
-#if defined(__i386__) || defined(__x86_64__)
-static bool in_hypervisor(void) {
-        uint32_t eax, ebx, ecx, edx;
-
-        /* The TSC might or might not be virtualized in VMs (and thus might not be accurate or start at zero
-         * at boot), depending on hypervisor and CPU functionality. If it's not virtualized it's not useful
-         * for keeping time, hence don't attempt to use it.
-         *
-         * This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
-         * environment. */
-
-        if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
-                return false;
-
-        return !!(ecx & 0x80000000U);
-}
-#endif
+#include "util.h"
 
 #ifdef __x86_64__
 static uint64_t ticks_read(void) {
         uint64_t a, d;
 
+        /* The TSC might or might not be virtualized in VMs (and thus might not be accurate or start at zero
+         * at boot), depending on hypervisor and CPU functionality. If it's not virtualized it's not useful
+         * for keeping time, hence don't attempt to use it. */
         if (in_hypervisor())
                 return 0;
 
index 7603eb8f534a9fd2c31ad24cde9942aaf1a5fa14..a41dbaa43e1f6278c3d082875f75cf85b7c33592 100644 (file)
@@ -2,6 +2,9 @@
 
 #include <efi.h>
 #include <efilib.h>
+#if defined(__i386__) || defined(__x86_64__)
+#  include <cpuid.h>
+#endif
 
 #include "ticks.h"
 #include "util.h"
@@ -637,6 +640,31 @@ __attribute__((noinline)) void debug_break(void) {
 }
 #endif
 
+
+#ifdef EFI_DEBUG
+void hexdump(const char16_t *prefix, const void *data, UINTN size) {
+        static const char hex[16] = "0123456789abcdef";
+        _cleanup_free_ char16_t *buf = NULL;
+        const uint8_t *d = data;
+
+        assert(prefix);
+        assert(data || size == 0);
+
+        /* Debugging helper — please keep this around, even if not used */
+
+        buf = xnew(char16_t, size*2+1);
+
+        for (UINTN i = 0; i < size; i++) {
+                buf[i*2] = hex[d[i] >> 4];
+                buf[i*2+1] = hex[d[i] & 0x0F];
+        }
+
+        buf[size*2] = 0;
+
+        log_error_stall(L"%s[%" PRIuN "]: %s", prefix, size, buf);
+}
+#endif
+
 #if defined(__i386__) || defined(__x86_64__)
 static inline uint8_t inb(uint16_t port) {
         uint8_t value;
@@ -743,3 +771,17 @@ EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DE
         SetDevicePathEndNode(dp);
         return EFI_SUCCESS;
 }
+
+#if defined(__i386__) || defined(__x86_64__)
+bool in_hypervisor(void) {
+        uint32_t eax, ebx, ecx, edx;
+
+        /* This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
+         * environment. */
+
+        if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
+                return false;
+
+        return !!(ecx & 0x80000000U);
+}
+#endif
index 4de799ea0e4e937a99e4fdf024bb882ebc22aeeb..afbc217d5357d3b9e64725fee1c7a7bdd2ee4de6 100644 (file)
@@ -167,6 +167,10 @@ extern uint8_t _text, _data;
 #  define debug_hook(identity)
 #endif
 
+#ifdef EFI_DEBUG
+void hexdump(const char16_t *prefix, const void *data, UINTN size);
+#endif
+
 #if defined(__i386__) || defined(__x86_64__)
 void beep(UINTN beep_count);
 #else
@@ -175,3 +179,11 @@ static inline void beep(UINTN beep_count) {}
 
 EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file);
 EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp);
+
+#if defined(__i386__) || defined(__x86_64__)
+bool in_hypervisor(void);
+#else
+static inline bool in_hypervisor(void) {
+        return false;
+}
+#endif
diff --git a/src/boot/measure.c b/src/boot/measure.c
new file mode 100644 (file)
index 0000000..29568c5
--- /dev/null
@@ -0,0 +1,533 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "efi-loader.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hexdecoct.h"
+#include "main-func.h"
+#include "openssl-util.h"
+#include "parse-argument.h"
+#include "parse-util.h"
+#include "pretty-print.h"
+#include "terminal-util.h"
+#include "tpm-pcr.h"
+#include "tpm2-util.h"
+#include "verbs.h"
+
+/* Tool for pre-calculating expected TPM PCR values based on measured resources. This is intended to be used
+ * to pre-calculate suitable values for PCR 11, the way sd-stub measures into it. */
+
+static char *arg_sections[_UNIFIED_SECTION_MAX] = {};
+static char **arg_banks = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
+
+static inline void free_sections(char*(*sections)[_UNIFIED_SECTION_MAX]) {
+        for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++)
+                free((*sections)[c]);
+}
+
+STATIC_DESTRUCTOR_REGISTER(arg_sections, free_sections);
+
+static int help(int argc, char *argv[], void *userdata) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("systemd-measure", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s  [OPTIONS...] COMMAND ...\n"
+               "\n%5$sPre-calculate PCR hash for kernel image.%6$s\n"
+               "\n%3$sCommands:%4$s\n"
+               "  status             Show current PCR values\n"
+               "  calculate          Calculate expected PCR values\n"
+               "\n%3$sOptions:%4$s\n"
+               "  -h --help          Show this help\n"
+               "     --version       Print version\n"
+               "     --linux=PATH    Path Linux kernel ELF image\n"
+               "     --osrel=PATH    Path to os-release file\n"
+               "     --cmdline=PATH  Path to file with kernel command line\n"
+               "     --initrd=PATH   Path to initrd image\n"
+               "     --splash=PATH   Path to splash bitmap\n"
+               "     --dtb=PATH      Path to Devicetree file\n"
+               "     --bank=DIGEST   Select TPM bank (SHA1, SHA256)\n"
+               "\nSee the %2$s for details.\n",
+               program_invocation_short_name,
+               link,
+               ansi_underline(),
+               ansi_normal(),
+               ansi_highlight(),
+               ansi_normal());
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+        enum {
+                ARG_VERSION = 0x100,
+                _ARG_SECTION_FIRST,
+                ARG_LINUX = _ARG_SECTION_FIRST,
+                ARG_OSREL,
+                ARG_CMDLINE,
+                ARG_INITRD,
+                ARG_SPLASH,
+                _ARG_SECTION_LAST,
+                ARG_DTB = _ARG_SECTION_LAST,
+                ARG_BANK,
+        };
+
+        static const struct option options[] = {
+                { "help",    no_argument,       NULL, 'h'         },
+                { "version", no_argument,       NULL, ARG_VERSION },
+                { "linux",   required_argument, NULL, ARG_LINUX   },
+                { "osrel",   required_argument, NULL, ARG_OSREL   },
+                { "cmdline", required_argument, NULL, ARG_CMDLINE },
+                { "initrd",  required_argument, NULL, ARG_INITRD  },
+                { "splash",  required_argument, NULL, ARG_SPLASH  },
+                { "dtb",     required_argument, NULL, ARG_DTB     },
+                { "bank",    required_argument, NULL, ARG_BANK    },
+                {}
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        /* Make sure the arguments list and the section list, stays in sync */
+        assert_cc(_ARG_SECTION_FIRST + _UNIFIED_SECTION_MAX == _ARG_SECTION_LAST + 1);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+                switch (c) {
+
+                case 'h':
+                        help(0, NULL, NULL);
+                        return 0;
+
+                case ARG_VERSION:
+                        return version();
+
+                case _ARG_SECTION_FIRST..._ARG_SECTION_LAST: {
+                        UnifiedSection section = c - _ARG_SECTION_FIRST;
+
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, arg_sections + section);
+                        if (r < 0)
+                                return r;
+                        break;
+                }
+
+                case ARG_BANK: {
+                        const EVP_MD *implementation;
+
+                        implementation = EVP_get_digestbyname(optarg);
+                        if (!implementation)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown bank '%s', refusing.", optarg);
+
+                        if (strv_extend(&arg_banks, EVP_MD_name(implementation)) < 0)
+                                return log_oom();
+
+                        break;
+                }
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+
+        if (strv_isempty(arg_banks)) {
+                /* If no banks are specifically selected, pick all known banks */
+                arg_banks = strv_new("SHA1", "SHA256", "SHA384", "SHA512");
+                if (!arg_banks)
+                        return log_oom();
+        }
+
+        strv_sort(arg_banks);
+        strv_uniq(arg_banks);
+
+        return 1;
+}
+
+typedef struct PcrState {
+        const EVP_MD *md;
+        void *value;
+        size_t value_size;
+} PcrState;
+
+static void pcr_state_free_all(PcrState **pcr_state) {
+        assert(pcr_state);
+
+        if (!*pcr_state)
+                return;
+
+        for (size_t i = 0; (*pcr_state)[i].value; i++)
+                free((*pcr_state)[i].value);
+
+        *pcr_state = mfree(*pcr_state);
+}
+
+static void evp_md_ctx_free_all(EVP_MD_CTX **md[]) {
+        assert(md);
+
+        if (!*md)
+                return;
+
+        for (size_t i = 0; (*md)[i]; i++)
+                EVP_MD_CTX_free((*md)[i]);
+
+        *md = mfree(*md);
+}
+
+static int pcr_state_extend(PcrState *pcr_state, const void *data, size_t sz) {
+        _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mc = NULL;
+        unsigned value_size;
+
+        assert(pcr_state);
+        assert(data || sz == 0);
+        assert(pcr_state->md);
+        assert(pcr_state->value);
+        assert(pcr_state->value_size > 0);
+
+        /* Extends a (virtual) PCR by the given data */
+
+        mc = EVP_MD_CTX_new();
+        if (!mc)
+                return log_oom();
+
+        if (EVP_DigestInit_ex(mc, pcr_state->md, NULL) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize %s context.", EVP_MD_name(pcr_state->md));
+
+        /* First thing we do, is hash the old PCR value */
+        if (EVP_DigestUpdate(mc, pcr_state->value, pcr_state->value_size) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
+
+        /* Then, we hash the new data */
+        if (EVP_DigestUpdate(mc, data, sz) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
+
+        if (EVP_DigestFinal_ex(mc, pcr_state->value, &value_size) != 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finalize hash context.");
+
+        assert(value_size == pcr_state->value_size);
+        return 0;
+}
+
+#define BUFFER_SIZE (16U * 1024U)
+
+static int measure_pcr(PcrState *pcr_states, size_t n) {
+        _cleanup_free_ void *buffer = NULL;
+        int r;
+
+        assert(n > 0);
+        assert(pcr_states);
+
+        buffer = malloc(BUFFER_SIZE);
+        if (!buffer)
+                return log_oom();
+
+        for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++) {
+                _cleanup_(evp_md_ctx_free_all) EVP_MD_CTX **mdctx = NULL;
+                _cleanup_close_ int fd = -1;
+                uint64_t m = 0;
+
+                if (!arg_sections[c])
+                        continue;
+
+                fd = open(arg_sections[c], O_RDONLY|O_CLOEXEC);
+                if (fd < 0)
+                        return log_error_errno(errno, "Failed to open '%s': %m", arg_sections[c]);
+
+                /* Allocate one message digest context per bank (NULL terminated) */
+                mdctx = new0(EVP_MD_CTX*, n + 1);
+                if (!mdctx)
+                        return log_oom();
+
+                for (size_t i = 0; i < n; i++) {
+                        mdctx[i] = EVP_MD_CTX_new();
+                        if (!mdctx[i])
+                                return log_oom();
+
+                        if (EVP_DigestInit_ex(mdctx[i], pcr_states[i].md, NULL) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize data %s context.", EVP_MD_name(pcr_states[i].md));
+                }
+
+                for (;;) {
+                        ssize_t sz;
+
+                        sz = read(fd, buffer, BUFFER_SIZE);
+                        if (sz < 0)
+                                return log_error_errno(errno, "Failed to read '%s': %m", arg_sections[c]);
+                        if (sz == 0) /* EOF */
+                                break;
+
+                        for (size_t i = 0; i < n; i++)
+                                if (EVP_DigestUpdate(mdctx[i], buffer, sz) != 1)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
+
+                        m += sz;
+                }
+
+                fd = safe_close(fd);
+
+                if (m == 0) /* We skip over empty files, the stub does so too */
+                        continue;
+
+                for (size_t i = 0; i < n; i++) {
+                        _cleanup_free_ void *data_hash = NULL;
+                        unsigned data_hash_size;
+
+                        data_hash = malloc(pcr_states[i].value_size);
+                        if (!data_hash)
+                                return log_oom();
+
+                        /* Measure name of section */
+                        if (EVP_Digest(unified_sections[c], strlen(unified_sections[c]) + 1, data_hash, &data_hash_size, pcr_states[i].md, NULL) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to hash section name with %s.", EVP_MD_name(pcr_states[i].md));
+
+                        assert(data_hash_size == (unsigned) pcr_states[i].value_size);
+
+                        r = pcr_state_extend(pcr_states + i, data_hash, data_hash_size);
+                        if (r < 0)
+                                return r;
+
+                        /* Retrieve hash of data an measure it*/
+                        if (EVP_DigestFinal_ex(mdctx[i], data_hash, &data_hash_size) != 1)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finalize hash context.");
+
+                        assert(data_hash_size == (unsigned) pcr_states[i].value_size);
+
+                        r = pcr_state_extend(pcr_states + i, data_hash, data_hash_size);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
+static int verb_calculate(int argc, char *argv[], void *userdata) {
+        _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
+        size_t n = 0;
+        int r;
+
+        if (!arg_sections[UNIFIED_SECTION_LINUX])
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--linux= switch must be specified, refusing.");
+
+        pcr_states = new0(PcrState, strv_length(arg_banks) + 1);
+        if (!pcr_states)
+                return log_oom();
+
+        /* Allocate a PCR state structure, one for each bank */
+        STRV_FOREACH(d, arg_banks) {
+                const EVP_MD *implementation;
+                _cleanup_free_ void *v = NULL;
+                int sz;
+
+                assert_se(implementation = EVP_get_digestbyname(*d)); /* Must work, we already checked while parsing  command line */
+
+                sz = EVP_MD_size(implementation);
+                if (sz <= 0 || sz >= INT_MAX)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected digest size: %i", sz);
+
+                v = malloc0(sz); /* initial PCR state is all zeroes */
+                if (!v)
+                        return log_oom();
+
+                pcr_states[n++] = (struct PcrState) {
+                        .md = implementation,
+                        .value = TAKE_PTR(v),
+                        .value_size = sz,
+                };
+        }
+
+        r = measure_pcr(pcr_states, n);
+        if (r < 0)
+                return r;
+
+        for (size_t i = 0; i < n; i++) {
+                _cleanup_free_ char *hd = NULL, *b = NULL;
+
+                hd = hexmem(pcr_states[i].value, pcr_states[i].value_size);
+                if (!hd)
+                        return log_oom();
+
+                b = strdup(EVP_MD_name(pcr_states[i].md));
+                if (!b)
+                        return log_oom();
+
+                printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, ascii_strlower(b), hd);
+        }
+
+        return 0;
+}
+
+static int compare_reported_pcr_nr(uint32_t pcr, const char *varname, const char *description) {
+        _cleanup_free_ char *s = NULL;
+        uint32_t v;
+        int r;
+
+        r = efi_get_variable_string(varname, &s);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to read EFI variable '%s': %m", varname);
+
+        r = safe_atou32(s, &v);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse EFI variable '%s': %s", varname, s);
+
+        if (pcr != v)
+                log_warning("PCR number reported by stub for %s (%" PRIu32 ") different from our expectation (%" PRIu32 ").\n"
+                            "The measurements are likely inconsistent.", description, v, pcr);
+
+        return 0;
+}
+
+static int validate_stub(void) {
+        uint64_t features;
+        bool found = false;
+        int r;
+
+        if (tpm2_support() != TPM2_SUPPORT_FULL)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Sorry, system lacks full TPM2 support.");
+
+        r = efi_stub_get_features(&features);
+        if (r < 0)
+                return log_error_errno(r, "Unable to get stub features: %m");
+
+        if (!FLAGS_SET(features, EFI_STUB_FEATURE_THREE_PCRS))
+                log_warning("Warning: current kernel image does not support measuring itself, the command line or initrd system extension images.\n"
+                            "The PCR measurements seen are unlikely to be valid.");
+
+        r = compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_IMAGE, EFI_LOADER_VARIABLE("StubPcrKernelImage"), "kernel image");
+        if (r < 0)
+                return r;
+
+        r = compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_PARAMETERS, EFI_LOADER_VARIABLE("StubPcrKernelParameters"), "kernel parameters");
+        if (r < 0)
+                return r;
+
+        r = compare_reported_pcr_nr(TPM_PCR_INDEX_INITRD_SYSEXTS, EFI_LOADER_VARIABLE("StubPcrInitRDSysExts"), "initrd system extension images");
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(bank, arg_banks) {
+                _cleanup_free_ char *b = NULL, *p = NULL;
+
+                b = strdup(*bank);
+                if (!b)
+                        return log_oom();
+
+                if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/", ascii_strlower(b)) < 0)
+                        return log_oom();
+
+                if (access(p, F_OK) < 0) {
+                        if (errno != ENOENT)
+                                return log_error_errno(errno, "Failed to detect if '%s' exists: %m", b);
+                } else
+                        found = true;
+        }
+
+        if (!found)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "None of the select PCR banks appear to exist.");
+
+        return 0;
+}
+
+static int verb_status(int argc, char *argv[], void *userdata) {
+
+        static const struct {
+                uint32_t nr;
+                const char *description;
+        } relevant_pcrs[] = {
+                { TPM_PCR_INDEX_KERNEL_IMAGE,      "Unified Kernel Image"     },
+                { TPM_PCR_INDEX_KERNEL_PARAMETERS, "Kernel Parameters"        },
+                { TPM_PCR_INDEX_INITRD_SYSEXTS,    "initrd System Extensions" },
+        };
+
+        int r;
+
+        r = validate_stub();
+        if (r < 0)
+                return r;
+
+        for (size_t i = 0; i < ELEMENTSOF(relevant_pcrs); i++) {
+
+                STRV_FOREACH(bank, arg_banks) {
+                        _cleanup_free_ char *b = NULL, *p = NULL, *s = NULL, *f = NULL;
+                        _cleanup_free_ void *h = NULL;
+                        size_t l;
+
+                        b = strdup(*bank);
+                        if (!b)
+                                return log_oom();
+
+                        if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32, ascii_strlower(b), relevant_pcrs[i].nr) < 0)
+                                return log_oom();
+
+                        r = read_virtual_file(p, 4096, &s, NULL);
+                        if (r == -ENOENT)
+                                continue;
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to read '%s': %m", p);
+
+                        r = unhexmem(strstrip(s), SIZE_MAX, &h, &l);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
+
+                        f = hexmem(h, l);
+                        if (!h)
+                                return log_oom();
+
+                        if (bank == arg_banks) {
+                                /* before the first line for each PCR, write a short descriptive text to
+                                 * stderr, and leave the primary content on stdout */
+                                fflush(stdout);
+                                fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n",
+                                        ansi_grey(),
+                                        relevant_pcrs[i].nr,
+                                        relevant_pcrs[i].description,
+                                        memeqzero(h, l) ? " (NOT SET!)" : "",
+                                        ansi_normal());
+                                fflush(stderr);
+                        }
+
+                        printf("%" PRIu32 ":%s=%s\n", relevant_pcrs[i].nr, b, f);
+                }
+        }
+
+        return 0;
+}
+
+static int measure_main(int argc, char *argv[]) {
+        static const Verb verbs[] = {
+                { "help",      VERB_ANY, VERB_ANY, 0,            help           },
+                { "status",    VERB_ANY, 1,        VERB_DEFAULT, verb_status    },
+                { "calculate", VERB_ANY, 1,        0,            verb_calculate },
+                {}
+        };
+
+        return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+static int run(int argc, char *argv[]) {
+        int r;
+
+        log_show_color(true);
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r;
+
+        return measure_main(argc, argv);
+}
+
+DEFINE_MAIN_FUNCTION(run);
index 071cba30996e855d83bdad59ca8917234341dc4a..91267362351805b2e2c818347f009fad2986f381 100644 (file)
@@ -16,6 +16,13 @@ int main(int argc, char *argv[]) {
         _cleanup_close_ int fd = -1;
         ssize_t n;
         size_t l;
+        int r;
+
+        r = rearrange_stdio(-1, -1, -1);
+        if (r < 0) {
+                log_error_errno(r, "Failed to connect stdin/stdout/stderr with /dev/null: %m");
+                return EXIT_FAILURE;
+        }
 
         if (argc != 2) {
                 log_error("Incorrect number of arguments.");
index c2f9b5ac4286a8f79486e91b53e16f10354e62af..f65401617a93e0f9895f5d67580fe1d6004b3a7a 100644 (file)
@@ -5,6 +5,7 @@ if conf.get('BPF_FRAMEWORK') != 1
 endif
 
 bpf_clang_flags = [
+        '-std=gnu11',
         '-Wno-compare-distinct-pointer-types',
         '-O2',
         '-target',
@@ -14,11 +15,13 @@ bpf_clang_flags = [
 ]
 
 bpf_gcc_flags = [
+        '-std=gnu11',
         '-O2',
         '-mkernel=5.2',
         '-mcpu=v3',
         '-mco-re',
         '-gbtf',
+        '-c',
 ]
 
 # Generate defines that are appropriate to tell the compiler what architecture
index 32cde5c8e020fc8f4e20470dcaa395a1c4f5bb5f..3e33728f4008dc4b75d0125ebec9c75fc147bc85 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/bpf.h>
 #include <bpf/bpf_helpers.h>
 
-const volatile __u8 is_allow_list = 0;
+const volatile __u8 is_allow_list SEC(".rodata") = 0;
 
 /* Map containing the network interfaces indexes.
  * The interpretation of the map depends on the value of is_allow_list.
index 871d79e0d2532dcccbf1976fbefa94e1d9d388f1..8ecbd69031b80eed3877830162f79973382bdae0 100644 (file)
@@ -13,6 +13,7 @@
 #include "bpf-socket-bind.h"
 #include "btrfs-util.h"
 #include "bus-error.h"
+#include "bus-locator.h"
 #include "cgroup-setup.h"
 #include "cgroup-util.h"
 #include "cgroup.h"
@@ -948,10 +949,25 @@ static usec_t cgroup_cpu_adjust_period_and_log(Unit *u, usec_t period, usec_t qu
 static void cgroup_apply_unified_cpu_weight(Unit *u, uint64_t weight) {
         char buf[DECIMAL_STR_MAX(uint64_t) + 2];
 
+        if (weight == CGROUP_WEIGHT_IDLE)
+                return;
         xsprintf(buf, "%" PRIu64 "\n", weight);
         (void) set_attribute_and_warn(u, "cpu", "cpu.weight", buf);
 }
 
+static void cgroup_apply_unified_cpu_idle(Unit *u, uint64_t weight) {
+        int r;
+        bool is_idle;
+        const char *idle_val;
+
+        is_idle = weight == CGROUP_WEIGHT_IDLE;
+        idle_val = one_zero(is_idle);
+        r = cg_set_attribute("cpu", u->cgroup_path, "cpu.idle", idle_val);
+        if (r < 0 && (r != -ENOENT || is_idle))
+                log_unit_full_errno(u, LOG_LEVEL_CGROUP_WRITE(r), r, "Failed to set '%s' attribute on '%s' to '%s': %m",
+                                    "cpu.idle", empty_to_root(u->cgroup_path), idle_val);
+}
+
 static void cgroup_apply_unified_cpu_quota(Unit *u, usec_t quota, usec_t period) {
         char buf[(DECIMAL_STR_MAX(usec_t) + 1) * 2 + 1];
 
@@ -992,6 +1008,10 @@ static uint64_t cgroup_cpu_shares_to_weight(uint64_t shares) {
 }
 
 static uint64_t cgroup_cpu_weight_to_shares(uint64_t weight) {
+        /* we don't support idle in cgroupv1 */
+        if (weight == CGROUP_WEIGHT_IDLE)
+                return CGROUP_CPU_SHARES_MIN;
+
         return CLAMP(weight * CGROUP_CPU_SHARES_DEFAULT / CGROUP_WEIGHT_DEFAULT,
                      CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX);
 }
@@ -1397,6 +1417,7 @@ static void cgroup_context_apply(
                         } else
                                 weight = CGROUP_WEIGHT_DEFAULT;
 
+                        cgroup_apply_unified_cpu_idle(u, weight);
                         cgroup_apply_unified_cpu_weight(u, weight);
                         cgroup_apply_unified_cpu_quota(u, c->cpu_quota_per_sec_usec, c->cpu_quota_period_usec);
 
@@ -2261,14 +2282,12 @@ static int unit_attach_pid_to_cgroup_via_bus(Unit *u, pid_t pid, const char *suf
         pp = strjoina("/", pp, suffix_path);
         path_simplify(pp);
 
-        r = sd_bus_call_method(u->manager->system_bus,
-                               "org.freedesktop.systemd1",
-                               "/org/freedesktop/systemd1",
-                               "org.freedesktop.systemd1.Manager",
-                               "AttachProcessesToUnit",
-                               &error, NULL,
-                               "ssau",
-                               NULL /* empty unit name means client's unit, i.e. us */, pp, 1, (uint32_t) pid);
+        r = bus_call_method(u->manager->system_bus,
+                            bus_systemd_mgr,
+                            "AttachProcessesToUnit",
+                            &error, NULL,
+                            "ssau",
+                            NULL /* empty unit name means client's unit, i.e. us */, pp, 1, (uint32_t) pid);
         if (r < 0)
                 return log_unit_debug_errno(u, r, "Failed to attach unit process " PID_FMT " via the bus: %s", pid, bus_error_message(&error, r));
 
index 4fc4fb0021612b1604e1059902a25a8d747bc6a2..015dc238d682f16b33af5b060b6184484cb24565 100644 (file)
@@ -894,7 +894,6 @@ static int bus_cgroup_set_boolean(
         }
 
 DISABLE_WARNING_TYPE_LIMITS;
-BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_weight, CGROUP_MASK_CPU, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID);
 BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_OK, CGROUP_CPU_SHARES_INVALID);
 BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID);
 BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEIGHT_IS_OK, CGROUP_BLKIO_WEIGHT_INVALID);
@@ -903,6 +902,35 @@ BUS_DEFINE_SET_CGROUP_LIMIT(memory_protection, CGROUP_MASK_MEMORY, physical_memo
 BUS_DEFINE_SET_CGROUP_LIMIT(swap, CGROUP_MASK_MEMORY, physical_memory_scale, 0);
 REENABLE_WARNING;
 
+static int bus_cgroup_set_cpu_weight(
+                Unit *u,
+                const char *name,
+                uint64_t *p,
+                sd_bus_message *message,
+                UnitWriteFlags flags,
+                sd_bus_error *error) {
+        uint64_t v;
+        int r;
+        assert(p);
+        r = sd_bus_message_read(message, "t", &v);
+        if (r < 0)
+                return r;
+        if (!CGROUP_WEIGHT_IS_OK(v) && v != CGROUP_WEIGHT_IDLE)
+                return sd_bus_error_setf(
+                                error, SD_BUS_ERROR_INVALID_ARGS, "Value specified in %s is out of range", name);
+        if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                *p = v;
+                unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+                if (v == CGROUP_WEIGHT_INVALID)
+                        unit_write_settingf(u, flags, name, "%s=", name);
+                else if (v == CGROUP_WEIGHT_IDLE)
+                        unit_write_settingf(u, flags, name, "%s=idle", name);
+                else
+                        unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, v);
+        }
+        return 1;
+}
+
 static int bus_cgroup_set_tasks_max(
                 Unit *u,
                 const char *name,
index 52521c414642062f4a45db752aa79c97854a5af2..95defd36a3781db264ab29bec56d89895c152272 100644 (file)
@@ -2791,6 +2791,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultTimeoutAbortUSec", "t", property_get_default_timeout_abort_usec, 0, 0),
+        SD_BUS_PROPERTY("DefaultDeviceTimeoutUSec", "t", bus_property_get_usec, offsetof(Manager, default_device_timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultStartLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
         /* The following two items are obsolete alias */
index 109ad6f2ef59a8e29fecef4390ed3597c78b8a65..0f596221669bd817ad184b3f3e871ef023de0e93 100644 (file)
@@ -186,6 +186,12 @@ int bus_scope_set_property(
                 r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, flags, error);
                 if (r != 0)
                         return r;
+
+                if (streq(name, "User"))
+                        return bus_set_transient_user_relaxed(u, name, &s->user, message, flags, error);
+
+                if (streq(name, "Group"))
+                        return bus_set_transient_user_relaxed(u, name, &s->group, message, flags, error);
         }
 
         return 0;
index aaa72cec80f9343dfe803695a2362d6b40ce5b22..ee013e1bc5af37d43d9e68473da4bebf00bd7dde 100644 (file)
@@ -894,6 +894,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("RequiresMountsFor", "as", property_get_requires_mounts_for, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("AccessSELinuxContext", "s", NULL, offsetof(Unit, access_selinux_context), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("FreezerState", "s", property_get_freezer_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -1771,9 +1772,6 @@ int bus_unit_queue_job_one(
         if (r < 0)
                 return r;
 
-        if (FLAGS_SET(flags, BUS_UNIT_QUEUE_RETURN_SKIP_ON_CONDITION_FAIL))
-                j->return_skip_on_cond_failure = true;
-
         r = bus_job_track_sender(j, message);
         if (r < 0)
                 return r;
@@ -2439,10 +2437,6 @@ int bus_unit_set_properties(
                 if (r < 0)
                         return r;
 
-                if (!UNIT_VTABLE(u)->bus_set_property)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY,
-                                                 "Objects of this type do not support setting properties.");
-
                 r = sd_bus_message_enter_container(message, 'v', NULL);
                 if (r < 0)
                         return r;
@@ -2450,7 +2444,10 @@ int bus_unit_set_properties(
                 /* If not for real, then mask out the two target flags */
                 f = for_real ? flags : (flags & ~(UNIT_RUNTIME|UNIT_PERSISTENT));
 
-                r = UNIT_VTABLE(u)->bus_set_property(u, name, message, f, error);
+                if (UNIT_VTABLE(u)->bus_set_property)
+                        r = UNIT_VTABLE(u)->bus_set_property(u, name, message, f, error);
+                else
+                        r = 0;
                 if (r == 0 && u->transient && u->load_state == UNIT_STUB)
                         r = bus_unit_set_transient_property(u, name, message, f, error);
                 if (r == 0)
index ab7787ce46618861ed14634386956db159e14879..643edcd87e972a44a9efac907708e11a2111fc57 100644 (file)
@@ -31,7 +31,6 @@ int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *
 typedef enum BusUnitQueueFlags {
         BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE            = 1 << 0,
         BUS_UNIT_QUEUE_VERBOSE_REPLY                 = 1 << 1,
-        BUS_UNIT_QUEUE_RETURN_SKIP_ON_CONDITION_FAIL = 1 << 2, // FIXME: currently not used, will be changed soon
 } BusUnitQueueFlags;
 
 int bus_unit_queue_job_one(
index 073675ceefa446f074cc78a9554445f3d6474215..ad2230d1b5e9f95421296374a5b4f41c27a52298 100644 (file)
@@ -42,6 +42,7 @@
 #include "string-util.h"
 #include "strv.h"
 #include "strxcpyx.h"
+#include "umask-util.h"
 #include "user-util.h"
 
 #define CONNECTIONS_MAX 4096
@@ -950,7 +951,8 @@ int bus_init_private(Manager *m) {
         if (fd < 0)
                 return log_error_errno(errno, "Failed to allocate private socket: %m");
 
-        r = bind(fd, &sa.sa, sa_len);
+        RUN_WITH_UMASK(0077)
+                r = bind(fd, &sa.sa, sa_len);
         if (r < 0)
                 return log_error_errno(errno, "Failed to bind private socket: %m");
 
index fcde8a420e6de05e34199f42a1733e104c23c7de..d6710262a99360f2ec31ebbaefdffe6eb1e4268b 100644 (file)
@@ -31,7 +31,6 @@ static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
 };
 
 static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata);
-static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask);
 
 static void device_unset_sysfs(Device *d) {
         Hashmap *devices;
@@ -102,7 +101,7 @@ static void device_init(Unit *u) {
          * indefinitely for plugged in devices, something which cannot
          * happen for the other units since their operations time out
          * anyway. */
-        u->job_running_timeout = u->manager->default_timeout_start_usec;
+        u->job_running_timeout = u->manager->default_device_timeout_usec;
 
         u->ignore_on_isolate = true;
 
@@ -116,6 +115,7 @@ static void device_done(Unit *u) {
 
         device_unset_sysfs(d);
         d->wants_property = strv_free(d->wants_property);
+        d->path = mfree(d->path);
 }
 
 static int device_load(Unit *u) {
@@ -156,6 +156,85 @@ static void device_set_state(Device *d, DeviceState state) {
         unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], 0);
 }
 
+static void device_found_changed(Device *d, DeviceFound previous, DeviceFound now) {
+        assert(d);
+
+        /* Didn't exist before, but does now? if so, generate a new invocation ID for it */
+        if (previous == DEVICE_NOT_FOUND && now != DEVICE_NOT_FOUND)
+                (void) unit_acquire_invocation_id(UNIT(d));
+
+        if (FLAGS_SET(now, DEVICE_FOUND_UDEV))
+                /* When the device is known to udev we consider it plugged. */
+                device_set_state(d, DEVICE_PLUGGED);
+        else if (now != DEVICE_NOT_FOUND && !FLAGS_SET(previous, DEVICE_FOUND_UDEV))
+                /* If the device has not been seen by udev yet, but is now referenced by the kernel, then we assume the
+                 * kernel knows it now, and udev might soon too. */
+                device_set_state(d, DEVICE_TENTATIVE);
+        else
+                /* If nobody sees the device, or if the device was previously seen by udev and now is only referenced
+                 * from the kernel, then we consider the device is gone, the kernel just hasn't noticed it yet. */
+                device_set_state(d, DEVICE_DEAD);
+}
+
+static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) {
+        assert(d);
+
+        if (MANAGER_IS_RUNNING(UNIT(d)->manager)) {
+                DeviceFound n, previous;
+
+                /* When we are already running, then apply the new mask right-away, and trigger state changes
+                 * right-away */
+
+                n = (d->found & ~mask) | (found & mask);
+                if (n == d->found)
+                        return;
+
+                previous = d->found;
+                d->found = n;
+
+                device_found_changed(d, previous, n);
+        } else
+                /* We aren't running yet, let's apply the new mask to the shadow variable instead, which we'll apply as
+                 * soon as we catch-up with the state. */
+                d->enumerated_found = (d->enumerated_found & ~mask) | (found & mask);
+}
+
+static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFound found, DeviceFound mask) {
+        Device *l;
+
+        assert(m);
+        assert(sysfs);
+
+        if (mask == 0)
+                return;
+
+        l = hashmap_get(m->devices_by_sysfs, sysfs);
+        LIST_FOREACH(same_sysfs, d, l)
+                device_update_found_one(d, found, mask);
+}
+
+static void device_update_found_by_name(Manager *m, const char *path, DeviceFound found, DeviceFound mask) {
+        _cleanup_free_ char *e = NULL;
+        Unit *u;
+        int r;
+
+        assert(m);
+        assert(path);
+
+        if (mask == 0)
+                return;
+
+        r = unit_name_from_path(path, ".device", &e);
+        if (r < 0)
+                return (void) log_debug_errno(r, "Failed to generate unit name from device path, ignoring: %m");
+
+        u = manager_get_unit(m, e);
+        if (!u)
+                return;
+
+        device_update_found_one(DEVICE(u), found, mask);
+}
+
 static int device_coldplug(Unit *u) {
         Device *d = DEVICE(u);
 
@@ -191,9 +270,8 @@ static int device_coldplug(Unit *u) {
          *   OPTIONS="db_persist". Hence, almost no devices are enumerated in the step 2. However, in general,
          *   we have several serialized devices. So, DEVICE_FOUND_UDEV bit in the deserialized_found must be
          *   ignored, as udev rules in initramfs and the main system are often different. If the deserialized
-         *   state is DEVICE_PLUGGED, we need to downgrade it to DEVICE_TENTATIVE (or DEVICE_DEAD if nobody
-         *   sees the device). Unlike the other starting mode, Manager.honor_device_enumeration == false
-         *   (maybe, it is better to rename the flag) when device_coldplug() and device_catchup() are called.
+         *   state is DEVICE_PLUGGED, we need to downgrade it to DEVICE_TENTATIVE. Unlike the other starting
+         *   mode, MANAGER_IS_SWITCHING_ROOT() is true when device_coldplug() and device_catchup() are called.
          *   Hence, let's conditionalize the operations by using the flag. After switch-root, systemd-udevd
          *   will (re-)process all devices, and the Device.found and Device.state will be adjusted.
          *
@@ -201,7 +279,7 @@ static int device_coldplug(Unit *u) {
          *   Of course, deserialized parameters may be outdated, but the unit state can be adjusted later by
          *   device_catchup() or uevents. */
 
-        if (!m->honor_device_enumeration && !MANAGER_IS_USER(m) &&
+        if (MANAGER_IS_SWITCHING_ROOT(m) &&
             !FLAGS_SET(d->enumerated_found, DEVICE_FOUND_UDEV)) {
                 found &= ~DEVICE_FOUND_UDEV; /* ignore DEVICE_FOUND_UDEV bit */
                 if (state == DEVICE_PLUGGED)
@@ -294,6 +372,9 @@ static int device_serialize(Unit *u, FILE *f, FDSet *fds) {
         assert(f);
         assert(fds);
 
+        if (d->path)
+                (void) serialize_item(f, "path", d->path);
+
         (void) serialize_item(f, "state", device_state_to_string(d->state));
 
         if (device_found_to_string_many(d->found, &s) >= 0)
@@ -312,7 +393,14 @@ static int device_deserialize_item(Unit *u, const char *key, const char *value,
         assert(value);
         assert(fds);
 
-        if (streq(key, "state")) {
+        if (streq(key, "path")) {
+                if (!d->path) {
+                        d->path = strdup(value);
+                        if (!d->path)
+                                log_oom_debug();
+                }
+
+        } else if (streq(key, "state")) {
                 DeviceState state;
 
                 state = device_state_from_string(value);
@@ -342,9 +430,11 @@ static void device_dump(Unit *u, FILE *f, const char *prefix) {
 
         fprintf(f,
                 "%sDevice State: %s\n"
+                "%sDevice Path: %s\n"
                 "%sSysfs Path: %s\n"
                 "%sFound: %s\n",
                 prefix, device_state_to_string(d->state),
+                prefix, strna(d->path),
                 prefix, strna(d->sysfs),
                 prefix, strna(s));
 
@@ -457,18 +547,24 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) {
         if (d->state != DEVICE_DEAD)
                 /* So here's a special hack, to compensate for the fact that the udev database's reload cycles are not
                  * synchronized with our own reload cycles: when we detect that the SYSTEMD_WANTS property of a device
-                 * changes while the device unit is already up, let's manually trigger any new units listed in it not
-                 * seen before. This typically happens during the boot-time switch root transition, as udev devices
-                 * will generally already be up in the initrd, but SYSTEMD_WANTS properties get then added through udev
-                 * rules only available on the host system, and thus only when the initial udev coldplug trigger runs.
+                 * changes while the device unit is already up, let's skip to trigger units that were already listed
+                 * and are active, and start units otherwise. This typically happens during the boot-time switch root
+                 * transition, as udev devices will generally already be up in the initrd, but SYSTEMD_WANTS properties
+                 * get then added through udev rules only available on the host system, and thus only when the initial
+                 * udev coldplug trigger runs.
                  *
                  * We do this only if the device has been up already when we parse this, as otherwise the usual
                  * dependency logic that is run from the dead → plugged transition will trigger these deps. */
                 STRV_FOREACH(i, added) {
                         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
 
-                        if (strv_contains(d->wants_property, *i)) /* Was this unit already listed before? */
-                                continue;
+                        if (strv_contains(d->wants_property, *i)) {
+                                Unit *v;
+
+                                v = manager_get_unit(u->manager, *i);
+                                if (v && UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(v)))
+                                        continue; /* The unit was already listed and is running. */
+                        }
 
                         r = manager_add_job_by_name(u->manager, JOB_START, *i, JOB_FAIL, NULL, &error, NULL);
                         if (r < 0)
@@ -542,16 +638,9 @@ static int device_setup_unit(Manager *m, sd_device *dev, const char *path, bool
                  * dependency on the mount unit which was added during the loading of the later. When the device is
                  * plugged the sysfs might not be initialized yet, as we serialize the device's state but do not
                  * serialize the sysfs path across reloads/reexecs. Hence, when coming back from a reload/restart we
-                 * might have the state valid, but not the sysfs path. Hence, let's filter out conflicting devices, but
-                 * let's accept devices in any state with no sysfs path set. */
-
-                if (DEVICE(u)->state == DEVICE_PLUGGED &&
-                    DEVICE(u)->sysfs &&
-                    sysfs &&
-                    !path_equal(DEVICE(u)->sysfs, sysfs))
-                        return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EEXIST),
-                                                    "Device %s appeared twice with different sysfs paths %s and %s, ignoring the latter.",
-                                                    e, DEVICE(u)->sysfs, sysfs);
+                 * might have the state valid, but not the sysfs path. Also, there is another possibility; when multiple
+                 * devices have the same devlink (e.g. /dev/disk/by-uuid/xxxx), adding/updating/removing one of the
+                 * device causes syspath change. Hence, let's always update sysfs path.*/
 
                 /* Let's remove all dependencies generated due to udev properties. We'll re-add whatever is configured
                  * now below. */
@@ -567,6 +656,12 @@ static int device_setup_unit(Manager *m, sd_device *dev, const char *path, bool
                 unit_add_to_load_queue(u);
         }
 
+        if (!DEVICE(u)->path) {
+                DEVICE(u)->path = strdup(path);
+                if (!DEVICE(u)->path)
+                        return log_oom();
+        }
+
         /* If this was created via some dependency and has not actually been seen yet ->sysfs will not be
          * initialized. Hence initialize it if necessary. */
         if (sysfs) {
@@ -592,187 +687,184 @@ static int device_setup_unit(Manager *m, sd_device *dev, const char *path, bool
         return 0;
 }
 
-static void device_process_new(Manager *m, sd_device *dev, const char *sysfs) {
-        const char *dn, *alias;
-        dev_t devnum;
+static bool device_is_ready(sd_device *dev) {
         int r;
 
-        assert(m);
         assert(dev);
-        assert(sysfs);
 
-        /* Add the main unit named after the sysfs path. If this one fails, don't bother with the rest, as
-         * this one shall be the main device unit the others just follow. (Compare with how
-         * device_following() is implemented, see below, which looks for the sysfs device.) */
-        if (device_setup_unit(m, dev, sysfs, true) < 0)
-                return;
+        r = device_is_renaming(dev);
+        if (r < 0)
+                log_device_warning_errno(dev, r, "Failed to check if device is renaming, assuming device is not renaming: %m");
+        if (r > 0) {
+                log_device_debug(dev, "Device busy: device is renaming");
+                return false;
+        }
 
-        /* Add an additional unit for the device node */
-        if (sd_device_get_devname(dev, &dn) >= 0)
-                (void) device_setup_unit(m, dev, dn, false);
-
-        /* Add additional units for all symlinks */
-        if (sd_device_get_devnum(dev, &devnum) >= 0) {
-                const char *p;
-
-                FOREACH_DEVICE_DEVLINK(dev, p) {
-                        struct stat st;
-
-                        if (PATH_STARTSWITH_SET(p, "/dev/block/", "/dev/char/"))
-                                continue;
-
-                        /* Verify that the symlink in the FS actually belongs to this device. This is useful
-                         * to deal with conflicting devices, e.g. when two disks want the same
-                         * /dev/disk/by-label/xxx link because they have the same label. We want to make sure
-                         * that the same device that won the symlink wins in systemd, so we check the device
-                         * node major/minor */
-                        if (stat(p, &st) >= 0 &&
-                            ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
-                             st.st_rdev != devnum)) {
-                                log_device_debug(dev, "Skipping device unit creation for symlink %s not owned by device", p);
-                                continue;
-                        }
+        /* Is it really tagged as 'systemd' right now? */
+        r = sd_device_has_current_tag(dev, "systemd");
+        if (r < 0)
+                log_device_warning_errno(dev, r, "Failed to check if device has \"systemd\" tag, assuming device is not tagged with \"systemd\": %m");
+        if (r == 0)
+                log_device_debug(dev, "Device busy: device is not tagged with \"systemd\"");
+        if (r <= 0)
+                return false;
 
-                        (void) device_setup_unit(m, dev, p, false);
-                }
-        }
+        r = device_get_property_bool(dev, "SYSTEMD_READY");
+        if (r < 0 && r != -ENOENT)
+                log_device_warning_errno(dev, r, "Failed to get device SYSTEMD_READY property, assuming device does not have \"SYSTEMD_READY\" property: %m");
+        if (r == 0)
+                log_device_debug(dev, "Device busy: SYSTEMD_READY property from device is false");
 
-        /* Add additional units for all explicitly configured aliases */
-        r = sd_device_get_property_value(dev, "SYSTEMD_ALIAS", &alias);
-        if (r < 0) {
-                if (r != -ENOENT)
-                        log_device_error_errno(dev, r, "Failed to get SYSTEMD_ALIAS property, ignoring: %m");
-                return;
-        }
+        return r != 0;
+}
 
-        for (;;) {
-                _cleanup_free_ char *word = NULL;
+static int device_setup_devlink_unit_one(Manager *m, const char *devlink, sd_device **ret) {
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+        int r;
 
-                r = extract_first_word(&alias, &word, NULL, EXTRACT_UNQUOTE);
-                if (r == 0)
-                        break;
-                if (r == -ENOMEM)
-                        return (void) log_oom();
-                if (r < 0)
-                        return (void) log_device_warning_errno(dev, r, "Failed to parse SYSTEMD_ALIAS property, ignoring: %m");
+        assert(m);
+        assert(devlink);
 
-                if (!path_is_absolute(word))
-                        log_device_warning(dev, "SYSTEMD_ALIAS is not an absolute path, ignoring: %s", word);
-                else if (!path_is_normalized(word))
-                        log_device_warning(dev, "SYSTEMD_ALIAS is not a normalized path, ignoring: %s", word);
-                else
-                        (void) device_setup_unit(m, dev, word, false);
+        if (sd_device_new_from_devname(&dev, devlink) < 0 || !device_is_ready(dev)) {
+                /* the devlink is already removed or not ready */
+                device_update_found_by_name(m, devlink, 0, DEVICE_FOUND_UDEV);
+                *ret = NULL;
+                return 0; /* not ready */
         }
+
+        r = device_setup_unit(m, dev, devlink, /* main = */ false);
+        if (r < 0)
+                return log_device_warning_errno(dev, r, "Failed to setup unit for '%s': %m", devlink);
+
+        *ret = TAKE_PTR(dev);
+        return 1; /* ready */
 }
 
-static void device_found_changed(Device *d, DeviceFound previous, DeviceFound now) {
-        assert(d);
+static int device_setup_devlink_units(Manager *m, sd_device *dev, char ***ret_ready_devlinks) {
+        _cleanup_strv_free_ char **ready_devlinks = NULL;
+        const char *devlink, *syspath;
+        int r;
 
-        /* Didn't exist before, but does now? if so, generate a new invocation ID for it */
-        if (previous == DEVICE_NOT_FOUND && now != DEVICE_NOT_FOUND)
-                (void) unit_acquire_invocation_id(UNIT(d));
+        assert(m);
+        assert(dev);
+        assert(ret_ready_devlinks);
 
-        if (FLAGS_SET(now, DEVICE_FOUND_UDEV))
-                /* When the device is known to udev we consider it plugged. */
-                device_set_state(d, DEVICE_PLUGGED);
-        else if (now != DEVICE_NOT_FOUND && !FLAGS_SET(previous, DEVICE_FOUND_UDEV))
-                /* If the device has not been seen by udev yet, but is now referenced by the kernel, then we assume the
-                 * kernel knows it now, and udev might soon too. */
-                device_set_state(d, DEVICE_TENTATIVE);
-        else
-                /* If nobody sees the device, or if the device was previously seen by udev and now is only referenced
-                 * from the kernel, then we consider the device is gone, the kernel just hasn't noticed it yet. */
-                device_set_state(d, DEVICE_DEAD);
-}
+        r = sd_device_get_syspath(dev, &syspath);
+        if (r < 0)
+                return r;
 
-static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) {
-        assert(d);
+        FOREACH_DEVICE_DEVLINK(dev, devlink) {
+                _cleanup_(sd_device_unrefp) sd_device *assigned = NULL;
+                const char *s;
 
-        if (MANAGER_IS_RUNNING(UNIT(d)->manager)) {
-                DeviceFound n, previous;
+                if (PATH_STARTSWITH_SET(devlink, "/dev/block/", "/dev/char/"))
+                        continue;
 
-                /* When we are already running, then apply the new mask right-away, and trigger state changes
-                 * right-away */
+                if (device_setup_devlink_unit_one(m, devlink, &assigned) <= 0)
+                        continue;
 
-                n = (d->found & ~mask) | (found & mask);
-                if (n == d->found)
-                        return;
+                if (sd_device_get_syspath(assigned, &s) < 0)
+                        continue;
 
-                previous = d->found;
-                d->found = n;
+                if (path_equal(s, syspath))
+                        continue;
 
-                device_found_changed(d, previous, n);
-        } else
-                /* We aren't running yet, let's apply the new mask to the shadow variable instead, which we'll apply as
-                 * soon as we catch-up with the state. */
-                d->enumerated_found = (d->enumerated_found & ~mask) | (found & mask);
+                r = strv_extend(&ready_devlinks, devlink);
+                if (r < 0)
+                        return -ENOMEM;
+        }
+
+        *ret_ready_devlinks = TAKE_PTR(ready_devlinks);
+        return 0;
 }
 
-static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFound found, DeviceFound mask) {
+static int device_setup_devlink_units_on_remove(Manager *m, sd_device *dev, char ***ret_ready_devlinks) {
+        _cleanup_strv_free_ char **ready_devlinks = NULL;
+        const char *syspath;
         Device *l;
+        int r;
 
         assert(m);
-        assert(sysfs);
+        assert(dev);
+        assert(ret_ready_devlinks);
 
-        if (mask == 0)
-                return;
+        r = sd_device_get_syspath(dev, &syspath);
+        if (r < 0)
+                return r;
 
-        l = hashmap_get(m->devices_by_sysfs, sysfs);
-        LIST_FOREACH(same_sysfs, d, l)
-                device_update_found_one(d, found, mask);
-}
+        l = hashmap_get(m->devices_by_sysfs, syspath);
+        LIST_FOREACH(same_sysfs, d, l) {
+                _cleanup_(sd_device_unrefp) sd_device *assigned = NULL;
+                const char *s;
 
-static void device_update_found_by_name(Manager *m, const char *path, DeviceFound found, DeviceFound mask) {
-        _cleanup_free_ char *e = NULL;
-        Unit *u;
-        int r;
+                if (!d->path)
+                        continue;
 
-        assert(m);
-        assert(path);
+                if (!path_startswith(d->path, "/dev/"))
+                        continue;
 
-        if (mask == 0)
-                return;
+                if (device_setup_devlink_unit_one(m, d->path, &assigned) <= 0)
+                        continue;
 
-        r = unit_name_from_path(path, ".device", &e);
-        if (r < 0)
-                return (void) log_debug_errno(r, "Failed to generate unit name from device path, ignoring: %m");
+                if (sd_device_get_syspath(assigned, &s) < 0)
+                        continue;
 
-        u = manager_get_unit(m, e);
-        if (!u)
-                return;
+                if (path_equal(s, syspath))
+                        continue;
 
-        device_update_found_one(DEVICE(u), found, mask);
+                r = strv_extend(&ready_devlinks, d->path);
+                if (r < 0)
+                        return -ENOMEM;
+        }
+
+        *ret_ready_devlinks = TAKE_PTR(ready_devlinks);
+        return 0;
 }
 
-static bool device_is_ready(sd_device *dev) {
+static void device_process_new(Manager *m, sd_device *dev, const char *sysfs) {
+        const char *dn, *alias;
         int r;
 
+        assert(m);
         assert(dev);
+        assert(sysfs);
 
-        r = device_is_renaming(dev);
-        if (r < 0)
-                log_device_warning_errno(dev, r, "Failed to check if device is renaming, assuming device is not renaming: %m");
-        if (r > 0) {
-                log_device_debug(dev, "Device busy: device is renaming");
-                return false;
+        /* Add the main unit named after the sysfs path. If this one fails, don't bother with the rest, as
+         * this one shall be the main device unit the others just follow. (Compare with how
+         * device_following() is implemented, see below, which looks for the sysfs device.) */
+        if (device_setup_unit(m, dev, sysfs, /* main = */ true) < 0)
+                return;
+
+        /* Add an additional unit for the device node */
+        if (sd_device_get_devname(dev, &dn) >= 0)
+                (void) device_setup_unit(m, dev, dn, /* main = */ false);
+
+        /* Add additional units for all explicitly configured aliases */
+        r = sd_device_get_property_value(dev, "SYSTEMD_ALIAS", &alias);
+        if (r < 0) {
+                if (r != -ENOENT)
+                        log_device_error_errno(dev, r, "Failed to get SYSTEMD_ALIAS property, ignoring: %m");
+                return;
         }
 
-        /* Is it really tagged as 'systemd' right now? */
-        r = sd_device_has_current_tag(dev, "systemd");
-        if (r < 0)
-                log_device_warning_errno(dev, r, "Failed to check if device has \"systemd\" tag, assuming device is not tagged with \"systemd\": %m");
-        if (r == 0)
-                log_device_debug(dev, "Device busy: device is not tagged with \"systemd\"");
-        if (r <= 0)
-                return false;
+        for (;;) {
+                _cleanup_free_ char *word = NULL;
 
-        r = device_get_property_bool(dev, "SYSTEMD_READY");
-        if (r < 0 && r != -ENOENT)
-                log_device_warning_errno(dev, r, "Failed to get device SYSTEMD_READY property, assuming device does not have \"SYSTEMD_READY\" property: %m");
-        if (r == 0)
-                log_device_debug(dev, "Device busy: SYSTEMD_READY property from device is false");
+                r = extract_first_word(&alias, &word, NULL, EXTRACT_UNQUOTE);
+                if (r == 0)
+                        break;
+                if (r == -ENOMEM)
+                        return (void) log_oom();
+                if (r < 0)
+                        return (void) log_device_warning_errno(dev, r, "Failed to parse SYSTEMD_ALIAS property, ignoring: %m");
 
-        return r != 0;
+                if (!path_is_absolute(word))
+                        log_device_warning(dev, "SYSTEMD_ALIAS is not an absolute path, ignoring: %s", word);
+                else if (!path_is_normalized(word))
+                        log_device_warning(dev, "SYSTEMD_ALIAS is not a normalized path, ignoring: %s", word);
+                else
+                        (void) device_setup_unit(m, dev, word, /* main = */ false);
+        }
 }
 
 static Unit *device_following(Unit *u) {
@@ -890,10 +982,9 @@ static void device_enumerate(Manager *m) {
         }
 
         FOREACH_DEVICE(e, dev) {
+                _cleanup_strv_free_ char **ready_devlinks = NULL;
                 const char *sysfs;
-
-                if (!device_is_ready(dev))
-                        continue;
+                bool ready;
 
                 r = sd_device_get_syspath(dev, &sysfs);
                 if (r < 0) {
@@ -901,8 +992,18 @@ static void device_enumerate(Manager *m) {
                         continue;
                 }
 
-                device_process_new(m, dev, sysfs);
-                device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
+                ready = device_is_ready(dev);
+                if (ready)
+                        device_process_new(m, dev, sysfs);
+
+                /* Add additional units for all symlinks */
+                (void) device_setup_devlink_units(m, dev, &ready_devlinks);
+
+                if (ready)
+                        device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
+
+                STRV_FOREACH(devlink, ready_devlinks)
+                        device_update_found_by_name(m, *devlink, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
         }
 
         return;
@@ -925,10 +1026,34 @@ static void device_propagate_reload_by_sysfs(Manager *m, const char *sysfs) {
 
                 r = manager_propagate_reload(m, UNIT(d), JOB_REPLACE, NULL);
                 if (r < 0)
-                        log_warning_errno(r, "Failed to propagate reload, ignoring: %m");
+                        log_unit_warning_errno(UNIT(d), r, "Failed to propagate reload, ignoring: %m");
         }
 }
 
+static void device_propagate_reload_by_name(Manager *m, const char *path) {
+        _cleanup_free_ char *e = NULL;
+        Unit *u;
+        int r;
+
+        assert(m);
+        assert(path);
+
+        r = unit_name_from_path(path, ".device", &e);
+        if (r < 0)
+                return (void) log_debug_errno(r, "Failed to generate unit name from device path, ignoring: %m");
+
+        u = manager_get_unit(m, e);
+        if (!u)
+                return;
+
+        if (DEVICE(u)->state == DEVICE_DEAD)
+                return;
+
+        r = manager_propagate_reload(m, u, JOB_REPLACE, NULL);
+        if (r < 0)
+                log_unit_warning_errno(u, r, "Failed to propagate reload, ignoring: %m");
+}
+
 static void device_remove_old_on_move(Manager *m, sd_device *dev) {
         _cleanup_free_ char *syspath_old = NULL;
         const char *devpath_old;
@@ -949,9 +1074,11 @@ static void device_remove_old_on_move(Manager *m, sd_device *dev) {
 }
 
 static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
+        _cleanup_strv_free_ char **ready_devlinks = NULL;
         Manager *m = ASSERT_PTR(userdata);
         sd_device_action_t action;
         const char *sysfs;
+        bool ready;
         int r;
 
         assert(dev);
@@ -970,9 +1097,6 @@ static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *
                 return 0;
         }
 
-        if (!IN_SET(action, SD_DEVICE_ADD, SD_DEVICE_REMOVE, SD_DEVICE_MOVE))
-                device_propagate_reload_by_sysfs(m, sysfs);
-
         if (action == SD_DEVICE_MOVE)
                 device_remove_old_on_move(m, dev);
 
@@ -984,27 +1108,50 @@ static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *
                 if (r < 0)
                         log_device_warning_errno(dev, r, "Failed to process swap device remove event, ignoring: %m");
 
-                /* If we get notified that a device was removed by udev, then it's completely gone, hence
-                 * unset all found bits */
-                device_update_found_by_sysfs(m, sysfs, DEVICE_NOT_FOUND, DEVICE_FOUND_MASK);
+                ready = false;
+
+                (void) device_setup_devlink_units_on_remove(m, dev, &ready_devlinks);
 
-        } else if (device_is_ready(dev)) {
+        } else {
+                ready = device_is_ready(dev);
 
-                device_process_new(m, dev, sysfs);
+                if (ready) {
+                        device_process_new(m, dev, sysfs);
 
-                r = swap_process_device_new(m, dev);
-                if (r < 0)
-                        log_device_warning_errno(dev, r, "Failed to process swap device new event, ignoring: %m");
+                        r = swap_process_device_new(m, dev);
+                        if (r < 0)
+                                log_device_warning_errno(dev, r, "Failed to process swap device new event, ignoring: %m");
+                }
+
+                /* Add additional units for all symlinks */
+                (void) device_setup_devlink_units(m, dev, &ready_devlinks);
+        }
+
+        if (!IN_SET(action, SD_DEVICE_ADD, SD_DEVICE_REMOVE, SD_DEVICE_MOVE)) {
+                device_propagate_reload_by_sysfs(m, sysfs);
+
+                STRV_FOREACH(devlink, ready_devlinks)
+                        device_propagate_reload_by_name(m, *devlink);
+        }
 
+        if (ready || !strv_isempty(ready_devlinks))
                 manager_dispatch_load_queue(m);
 
+        if (action == SD_DEVICE_REMOVE)
+                /* If we get notified that a device was removed by udev, then it's completely gone, hence
+                 * unset all found bits */
+                device_update_found_by_sysfs(m, sysfs, 0, DEVICE_FOUND_MASK);
+        else if (ready)
                 /* The device is found now, set the udev found bit */
                 device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
-        else
+        else
                 /* The device is nominally around, but not ready for us. Hence unset the udev bit, but leave
                  * the rest around. */
                 device_update_found_by_sysfs(m, sysfs, DEVICE_NOT_FOUND, DEVICE_FOUND_UDEV);
 
+        STRV_FOREACH(devlink, ready_devlinks)
+                device_update_found_by_name(m, *devlink, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
+
         return 0;
 }
 
@@ -1047,7 +1194,7 @@ void device_found_node(Manager *m, const char *node, DeviceFound found, DeviceFo
                         return;
                 }
 
-                (void) device_setup_unit(m, dev, node, false); /* 'dev' may be NULL. */
+                (void) device_setup_unit(m, dev, node, /* main = */ false); /* 'dev' may be NULL. */
         }
 
         /* Update the device unit's state, should it exist */
index dfe8a13aff93b8a8afee292d71cc63b209df3f6a..7584bc70c4f609b9d7ca331404fad635dd594c2f 100644 (file)
@@ -21,6 +21,7 @@ struct Device {
         Unit meta;
 
         char *sysfs;
+        char *path; /* syspath, device node, alias, or devlink */
 
         /* In order to be able to distinguish dependencies on different device nodes we might end up creating multiple
          * devices for the same sysfs path. We chain them up here. */
index 64b290d3e79d7c65337571cad91a823b5a4e6917..e68d23173931fe9b118b5df15648baa5b22146fa 100644 (file)
@@ -1213,15 +1213,13 @@ static int setup_pam(
 
                 if (getttyname_malloc(STDIN_FILENO, &q) >= 0)
                         tty = strjoina("/dev/", q);
-                else
-                        /* If everything else failed then let's just use value "systemd". This will cause that session
-                         * isn't going to be marked as "background" and user manager will be started. */
-                        tty = "systemd";
         }
 
-        pam_code = pam_set_item(handle, PAM_TTY, tty);
-        if (pam_code != PAM_SUCCESS)
-               goto fail;
+        if (tty) {
+                pam_code = pam_set_item(handle, PAM_TTY, tty);
+                if (pam_code != PAM_SUCCESS)
+                        goto fail;
+        }
 
         STRV_FOREACH(nv, *env) {
                 pam_code = pam_putenv(handle, *nv);
index 53796484ee3f6457b836940ebe60a9c6e53afece..4685e43f475b964db00622cb286fdd62026ae92b 100644 (file)
@@ -4,9 +4,11 @@
 
 #include "copy.h"
 #include "creds-util.h"
+#include "escape.h"
 #include "fileio.h"
 #include "format-util.h"
 #include "fs-util.h"
+#include "hexdecoct.h"
 #include "import-creds.h"
 #include "io-util.h"
 #include "mkdir-label.h"
@@ -24,7 +26,7 @@
  * generators invoked by it) can acquire credentials from outside, to mimic how we support it for containers,
  * but on VM/physical environments.
  *
- * This does three things:
+ * This does four things:
  *
  * 1. It imports credentials picked up by sd-boot (and placed in the /.extra/credentials/ dir in the initrd)
  *    and puts them in /run/credentials/@encrypted/. Note that during the initrd→host transition the initrd root
  *    /sys/firmware/qemu_fw_cfg/by_name/opt/io.systemd.credentials/ is picked up and also placed in
  *    /run/credentials/@system/.
  *
+ * 4. It imports credentials passed in via the DMI/SMBIOS OEM string tables, quite similar to fw_cfg. It
+ *    looks for strings starting with "io.systemd.credential:" and "io.systemd.credential.binary:". Both
+ *    expect a key=value assignment, but in the latter case the value is Base64 decoded, allowing binary
+ *    credentials to be passed in.
+ *
  * If it picked up any credentials it will set the $CREDENTIALS_DIRECTORY and
  * $ENCRYPTED_CREDENTIALS_DIRECTORY environment variables to point to these directories, so that processes
  * can find them there later on. If "ramfs" is available $CREDENTIALS_DIRECTORY will be backed by it (but
@@ -446,26 +453,177 @@ static int import_credentials_qemu(ImportCredentialContext *c) {
         return 0;
 }
 
+static int parse_smbios_strings(ImportCredentialContext *c, const char *data, size_t size) {
+        size_t left, skip;
+        const char *p;
+        int r;
+
+        assert(c);
+        assert(data || size == 0);
+
+        /* Unpacks a packed series of SMBIOS OEM vendor strings. These are a series of NUL terminated
+         * strings, one after the other. */
+
+        for (p = data, left = size; left > 0; p += skip, left -= skip) {
+                _cleanup_free_ void *buf = NULL;
+                _cleanup_free_ char *cn = NULL;
+                _cleanup_close_ int nfd = -1;
+                const char *nul, *n, *eq;
+                const void *cdata;
+                size_t buflen, cdata_len;
+                bool unbase64;
+
+                nul = memchr(p, 0, left);
+                if (nul)
+                        skip = (nul - p) + 1;
+                else {
+                        nul = p + left;
+                        skip = left;
+                }
+
+                if (nul - p == 0) /* Skip empty strings */
+                        continue;
+
+                /* Only care about strings starting with either of these two prefixes */
+                if ((n = memory_startswith(p, nul - p, "io.systemd.credential:")))
+                        unbase64 = false;
+                else if ((n = memory_startswith(p, nul - p, "io.systemd.credential.binary:")))
+                        unbase64 = true;
+                else {
+                        _cleanup_free_ char *escaped = NULL;
+
+                        escaped = cescape_length(p, nul - p);
+                        log_debug("Ignoring OEM string: %s", strnull(escaped));
+                        continue;
+                }
+
+                eq = memchr(n, '=', nul - n);
+                if (!eq) {
+                        log_warning("SMBIOS OEM string lacks '=' character, ignoring.");
+                        continue;
+                }
+
+                cn = memdup_suffix0(n, eq - n);
+                if (!cn)
+                        return log_oom();
+
+                if (!credential_name_valid(cn)) {
+                        log_warning("SMBIOS credential name '%s' is not valid, ignoring: %m", cn);
+                        continue;
+                }
+
+                /* Optionally base64 decode the data, if requested, to allow binary credentials */
+                if (unbase64) {
+                        r = unbase64mem(eq + 1, nul - (eq + 1), &buf, &buflen);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to base64 decode credential '%s', ignoring: %m", cn);
+                                continue;
+                        }
+
+                        cdata = buf;
+                        cdata_len = buflen;
+                } else {
+                        cdata = eq + 1;
+                        cdata_len = nul - (eq + 1);
+                }
+
+                if (!credential_size_ok(c, cn, cdata_len))
+                        continue;
+
+                r = acquire_credential_directory(c);
+                if (r < 0)
+                        return r;
+
+                nfd = open_credential_file_for_write(c->target_dir_fd, SYSTEM_CREDENTIALS_DIRECTORY, cn);
+                if (nfd == -EEXIST)
+                        continue;
+                if (nfd < 0)
+                        return nfd;
+
+                r = loop_write(nfd, cdata, cdata_len, /* do_poll= */ false);
+                if (r < 0) {
+                        (void) unlinkat(c->target_dir_fd, cn, 0);
+                        return log_error_errno(r, "Failed to write credential: %m");
+                }
+
+                c->size_sum += cdata_len;
+                c->n_credentials++;
+
+                log_debug("Successfully processed SMBIOS credential '%s'.", cn);
+        }
+
+        return 0;
+}
+
+static int import_credentials_smbios(ImportCredentialContext *c) {
+        int r;
+
+        /* Parses DMI OEM strings fields (SMBIOS type 11), as settable with qemu's -smbios type=11,value=… switch. */
+
+        for (unsigned i = 0;; i++) {
+                struct dmi_field_header {
+                        uint8_t type;
+                        uint8_t length;
+                        uint16_t handle;
+                        uint8_t count;
+                        char contents[];
+                } _packed_ *dmi_field_header;
+                _cleanup_free_ char *p = NULL;
+                _cleanup_free_ void *data = NULL;
+                size_t size;
+
+                assert_cc(offsetof(struct dmi_field_header, contents) == 5);
+
+                if (asprintf(&p, "/sys/firmware/dmi/entries/11-%u/raw", i) < 0)
+                        return log_oom();
+
+                r = read_virtual_file(p, sizeof(dmi_field_header) + CREDENTIALS_TOTAL_SIZE_MAX, (char**) &data, &size);
+                if (r < 0) {
+                        /* Once we reach ENOENT there are no more DMI Type 11 fields around. */
+                        log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, "Failed to open '%s', ignoring: %m", p);
+                        break;
+                }
+
+                if (size < offsetof(struct dmi_field_header, contents))
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DMI field header of '%s' too short.", p);
+
+                dmi_field_header = data;
+                if (dmi_field_header->type != 11 ||
+                    dmi_field_header->length != offsetof(struct dmi_field_header, contents))
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid DMI field header.");
+
+                r = parse_smbios_strings(c, dmi_field_header->contents, size - offsetof(struct dmi_field_header, contents));
+                if (r < 0)
+                        return r;
+
+                if (i == UINT_MAX) /* Prevent overflow */
+                        break;
+        }
+
+        return 0;
+}
+
 static int import_credentials_trusted(void) {
         _cleanup_(import_credentials_context_free) ImportCredentialContext c = {
                 .target_dir_fd = -1,
         };
-        int q, r;
+        int q, w, r;
 
         r = import_credentials_qemu(&c);
+        w = import_credentials_smbios(&c);
         q = import_credentials_proc_cmdline(&c);
 
         if (c.n_credentials > 0) {
                 int z;
 
-                log_debug("Imported %u credentials from kernel command line/fw_cfg.", c.n_credentials);
+                log_debug("Imported %u credentials from kernel command line/smbios/fw_cfg.", c.n_credentials);
 
                 z = finalize_credentials_dir(SYSTEM_CREDENTIALS_DIRECTORY, "CREDENTIALS_DIRECTORY");
                 if (z < 0)
                         return z;
         }
 
-        return r < 0 ? r : q;
+        return r < 0 ? r : w < 0 ? w : q;
 }
 
 static int symlink_credential_dir(const char *envvar, const char *path, const char *where) {
index 36d6f0a4565c407cca5086c83ffba07a98756796..6653dbde84b79acbf12036b0d78ff926eb6e371f 100644 (file)
@@ -889,8 +889,8 @@ int job_run_and_invalidate(Job *j) {
                         job_set_state(j, JOB_WAITING); /* Hmm, not ready after all, let's return to JOB_WAITING state */
                 else if (r == -EALREADY) /* already being executed */
                         r = job_finish_and_invalidate(j, JOB_DONE, true, true);
-                else if (r == -ECOMM)    /* condition failed, but all is good. Return 'skip' if caller requested it. */
-                        r = job_finish_and_invalidate(j, j->return_skip_on_cond_failure ? JOB_SKIPPED : JOB_DONE, true, false);
+                else if (r == -ECOMM)
+                        r = job_finish_and_invalidate(j, JOB_DONE, true, false);
                 else if (r == -EBADR)
                         r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false);
                 else if (r == -ENOEXEC)
index a66e5985b8520d3cdea0cec1332c965cd7d4c713..c033c8a4faaa0e2d21615fcdac9e287c0364f672 100644 (file)
@@ -160,7 +160,6 @@ struct Job {
         bool irreversible:1;
         bool in_gc_queue:1;
         bool ref_by_private_bus:1;
-        bool return_skip_on_cond_failure:1;
 };
 
 Job* job_new(Unit *unit, JobType type);
index f4488dd6924151e2ae8bfc420b6a0f36b89a2f71..966631d44eca23207cb89d98a24c50dcc880c085 100644 (file)
@@ -117,6 +117,9 @@ int kmod_setup(void) {
 
                 /* qemu_fw_cfg would be loaded by udev later, but we want to import credentials from it super early */
                 { "qemu_fw_cfg", "/sys/firmware/qemu_fw_cfg", false, false,  in_qemu   },
+
+                /* dmi-sysfs is needed to import credentials from it super early */
+                { "dmi-sysfs", "/sys/firmware/dmi/entries", false, false,  NULL   },
         };
         _cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
         unsigned i;
index 7817c20c0ba9d5a42bbd556ca2cdc4dde75e2075..7675b7bb2e74619f59f0db8b52beadc1a436d097 100644 (file)
 {{type}}.AllowedMemoryNodes,               config_parse_allowed_cpuset,                 0,                                  offsetof({{type}}, cgroup_context.cpuset_mems)
 {{type}}.StartupAllowedMemoryNodes,        config_parse_allowed_cpuset,                 0,                                  offsetof({{type}}, cgroup_context.startup_cpuset_mems)
 {{type}}.CPUAccounting,                    config_parse_bool,                           0,                                  offsetof({{type}}, cgroup_context.cpu_accounting)
-{{type}}.CPUWeight,                        config_parse_cg_weight,                      0,                                  offsetof({{type}}, cgroup_context.cpu_weight)
-{{type}}.StartupCPUWeight,                 config_parse_cg_weight,                      0,                                  offsetof({{type}}, cgroup_context.startup_cpu_weight)
+{{type}}.CPUWeight,                        config_parse_cg_cpu_weight,                  0,                                  offsetof({{type}}, cgroup_context.cpu_weight)
+{{type}}.StartupCPUWeight,                 config_parse_cg_cpu_weight,                  0,                                  offsetof({{type}}, cgroup_context.startup_cpu_weight)
 {{type}}.CPUShares,                        config_parse_cpu_shares,                     0,                                  offsetof({{type}}, cgroup_context.cpu_shares)
 {{type}}.StartupCPUShares,                 config_parse_cpu_shares,                     0,                                  offsetof({{type}}, cgroup_context.startup_cpu_shares)
 {{type}}.CPUQuota,                         config_parse_cpu_quota,                      0,                                  offsetof({{type}}, cgroup_context)
@@ -332,6 +332,7 @@ Unit.ConditionVirtualization,            config_parse_unit_condition_string,
 Unit.ConditionHost,                      config_parse_unit_condition_string,          CONDITION_HOST,                     offsetof(Unit, conditions)
 Unit.ConditionKernelCommandLine,         config_parse_unit_condition_string,          CONDITION_KERNEL_COMMAND_LINE,      offsetof(Unit, conditions)
 Unit.ConditionKernelVersion,             config_parse_unit_condition_string,          CONDITION_KERNEL_VERSION,           offsetof(Unit, conditions)
+Unit.ConditionCredential,                config_parse_unit_condition_string,          CONDITION_CREDENTIAL,               offsetof(Unit, conditions)
 Unit.ConditionSecurity,                  config_parse_unit_condition_string,          CONDITION_SECURITY,                 offsetof(Unit, conditions)
 Unit.ConditionCapability,                config_parse_unit_condition_string,          CONDITION_CAPABILITY,               offsetof(Unit, conditions)
 Unit.ConditionACPower,                   config_parse_unit_condition_string,          CONDITION_AC_POWER,                 offsetof(Unit, conditions)
@@ -363,6 +364,7 @@ Unit.AssertVirtualization,               config_parse_unit_condition_string,
 Unit.AssertHost,                         config_parse_unit_condition_string,          CONDITION_HOST,                     offsetof(Unit, asserts)
 Unit.AssertKernelCommandLine,            config_parse_unit_condition_string,          CONDITION_KERNEL_COMMAND_LINE,      offsetof(Unit, asserts)
 Unit.AssertKernelVersion,                config_parse_unit_condition_string,          CONDITION_KERNEL_VERSION,           offsetof(Unit, asserts)
+Unit.AssertCredential,                   config_parse_unit_condition_string,          CONDITION_CREDENTIAL,               offsetof(Unit, asserts)
 Unit.AssertSecurity,                     config_parse_unit_condition_string,          CONDITION_SECURITY,                 offsetof(Unit, asserts)
 Unit.AssertCapability,                   config_parse_unit_condition_string,          CONDITION_CAPABILITY,               offsetof(Unit, asserts)
 Unit.AssertACPower,                      config_parse_unit_condition_string,          CONDITION_AC_POWER,                 offsetof(Unit, asserts)
index 92e99e7d536d88e6af3a3a7a3c5f28563e563d4f..0a2d4d4035b21b7edc86252fd7bb8f3b5182a06d 100644 (file)
@@ -58,6 +58,7 @@
 #include "seccomp-util.h"
 #endif
 #include "securebits-util.h"
+#include "selinux-util.h"
 #include "signal-util.h"
 #include "socket-netlink.h"
 #include "specifier.h"
@@ -146,6 +147,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_managed_oom_preference, managed_oom_prefer
 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value");
 DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint64_t, "Invalid block IO weight");
 DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight");
+DEFINE_CONFIG_PARSE_PTR(config_parse_cg_cpu_weight, cg_cpu_weight_parse, uint64_t, "Invalid CPU weight");
 DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
 DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
@@ -6096,6 +6098,7 @@ int unit_load_fragment(Unit *u) {
         assert(u->id);
 
         if (u->transient) {
+                u->access_selinux_context = mfree(u->access_selinux_context);
                 u->load_state = UNIT_LOADED;
                 return 0;
         }
@@ -6141,7 +6144,24 @@ int unit_load_fragment(Unit *u) {
 
                         u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
                         u->fragment_mtime = 0;
+                        u->access_selinux_context = mfree(u->access_selinux_context);
                 } else {
+#if HAVE_SELINUX
+                        if (mac_selinux_use()) {
+                                _cleanup_freecon_ char *selcon = NULL;
+
+                                /* Cache the SELinux context of the unit file here. We'll make use of when checking access permissions to loaded units */
+                                r = fgetfilecon_raw(fileno(f), &selcon);
+                                if (r < 0)
+                                        log_unit_warning_errno(u, r, "Failed to read SELinux context of '%s', ignoring: %m", fragment);
+
+                                r = free_and_strdup(&u->access_selinux_context, selcon);
+                                if (r < 0)
+                                        return r;
+                        } else
+#endif
+                                u->access_selinux_context = mfree(u->access_selinux_context);
+
                         u->load_state = UNIT_LOADED;
                         u->fragment_mtime = timespec_load(&st.st_mtim);
 
@@ -6267,6 +6287,7 @@ void unit_dump_config_items(FILE *f) {
                 { config_parse_restrict_filesystems,  "FILESYSTEMS"  },
                 { config_parse_cpu_shares,            "SHARES" },
                 { config_parse_cg_weight,             "WEIGHT" },
+                { config_parse_cg_cpu_weight,         "CPUWEIGHT" },
                 { config_parse_memory_limit,          "LIMIT" },
                 { config_parse_device_allow,          "DEVICE" },
                 { config_parse_device_policy,         "POLICY" },
index 26b8de28f7a09d6a18c4e2f43daa817393978832..8842d7ddc8acdad3576d0085d256b26aa06d8d12 100644 (file)
@@ -78,6 +78,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_pass_environ);
 CONFIG_PARSER_PROTOTYPE(config_parse_unset_environ);
 CONFIG_PARSER_PROTOTYPE(config_parse_unit_slice);
 CONFIG_PARSER_PROTOTYPE(config_parse_cg_weight);
+CONFIG_PARSER_PROTOTYPE(config_parse_cg_cpu_weight);
 CONFIG_PARSER_PROTOTYPE(config_parse_cpu_shares);
 CONFIG_PARSER_PROTOTYPE(config_parse_memory_limit);
 CONFIG_PARSER_PROTOTYPE(config_parse_tasks_max);
index 72d86d3efd06cbbe507fc7bbac06562030dc4a7a..9ad208fdfca83e2e7cd1158b5a84e03ce4f87df6 100644 (file)
@@ -134,6 +134,7 @@ static usec_t arg_default_restart_usec;
 static usec_t arg_default_timeout_start_usec;
 static usec_t arg_default_timeout_stop_usec;
 static usec_t arg_default_timeout_abort_usec;
+static usec_t arg_default_device_timeout_usec;
 static bool arg_default_timeout_abort_set;
 static usec_t arg_default_start_limit_interval;
 static unsigned arg_default_start_limit_burst;
@@ -627,6 +628,7 @@ static int parse_config_file(void) {
                 { "Manager", "DefaultTimeoutStartSec",       config_parse_sec,                   0,                        &arg_default_timeout_start_usec   },
                 { "Manager", "DefaultTimeoutStopSec",        config_parse_sec,                   0,                        &arg_default_timeout_stop_usec    },
                 { "Manager", "DefaultTimeoutAbortSec",       config_parse_default_timeout_abort, 0,                        NULL                              },
+                { "Manager", "DefaultDeviceTimeoutSec",      config_parse_sec,                   0,                        &arg_default_device_timeout_usec  },
                 { "Manager", "DefaultRestartSec",            config_parse_sec,                   0,                        &arg_default_restart_usec         },
                 { "Manager", "DefaultStartLimitInterval",    config_parse_sec,                   0,                        &arg_default_start_limit_interval }, /* obsolete alias */
                 { "Manager", "DefaultStartLimitIntervalSec", config_parse_sec,                   0,                        &arg_default_start_limit_interval },
@@ -689,6 +691,7 @@ static int parse_config_file(void) {
                         config_item_table_lookup, items,
                         CONFIG_PARSE_WARN,
                         NULL,
+                        NULL,
                         NULL);
 
         /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we use
@@ -716,6 +719,7 @@ static void set_manager_defaults(Manager *m) {
         m->default_timeout_stop_usec = arg_default_timeout_stop_usec;
         m->default_timeout_abort_usec = arg_default_timeout_abort_usec;
         m->default_timeout_abort_set = arg_default_timeout_abort_set;
+        m->default_device_timeout_usec = arg_default_device_timeout_usec;
         m->default_restart_usec = arg_default_restart_usec;
         m->default_start_limit_interval = arg_default_start_limit_interval;
         m->default_start_limit_burst = arg_default_start_limit_burst;
@@ -1966,6 +1970,8 @@ static int invoke_main_loop(
                         return objective;
 
                 case MANAGER_SWITCH_ROOT:
+                        manager_set_switching_root(m, true);
+
                         if (!m->switch_root_init) {
                                 r = prepare_reexecute(m, &arg_serialization, ret_fds, true);
                                 if (r < 0) {
@@ -2387,6 +2393,7 @@ static void reset_arguments(void) {
         arg_default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
         arg_default_timeout_abort_usec = DEFAULT_TIMEOUT_USEC;
         arg_default_timeout_abort_set = false;
+        arg_default_device_timeout_usec = DEFAULT_TIMEOUT_USEC;
         arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL;
         arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST;
         arg_runtime_watchdog = 0;
@@ -2819,12 +2826,18 @@ int main(int argc, char *argv[]) {
         } else {
                 /* Running as user instance */
                 arg_system = false;
+                log_set_always_reopen_console(true);
                 log_set_target(LOG_TARGET_AUTO);
                 log_open();
 
                 /* clear the kernel timestamp, because we are not PID 1 */
                 kernel_timestamp = DUAL_TIMESTAMP_NULL;
 
+                /* Clear ambient capabilities, so services do not inherit them implicitly. Dropping them does
+                 * not affect the permitted and effective sets which are important for the manager itself to
+                 * operate. */
+                capability_ambient_set_apply(0, /* also_inherit= */ false);
+
                 if (mac_selinux_init() < 0) {
                         error_message = "Failed to initialize SELinux support";
                         goto finish;
@@ -2937,6 +2950,7 @@ int main(int argc, char *argv[]) {
         set_manager_defaults(m);
         set_manager_settings(m);
         manager_set_first_boot(m, first_boot);
+        manager_set_switching_root(m, arg_switched_root);
 
         /* Remember whether we should queue the default job */
         queue_default_job = !arg_serialization || arg_switched_root;
index a91c8268d5024cf6068ccef9f1ef2fccaaf4952e..914bd92e36bab564cb794dd4a6a63fbba81b78a2 100644 (file)
@@ -103,9 +103,6 @@ int manager_serialize(
         (void) serialize_bool(f, "taint-logged", m->taint_logged);
         (void) serialize_bool(f, "service-watchdogs", m->service_watchdogs);
 
-        /* After switching root, udevd has not been started yet. So, enumeration results should not be emitted. */
-        (void) serialize_bool(f, "honor-device-enumeration", !switching_root);
-
         if (m->show_status_overridden != _SHOW_STATUS_INVALID)
                 (void) serialize_item(f, "show-status-overridden",
                                       show_status_to_string(m->show_status_overridden));
@@ -399,15 +396,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         else
                                 m->service_watchdogs = b;
 
-                } else if ((val = startswith(l, "honor-device-enumeration="))) {
-                        int b;
-
-                        b = parse_boolean(val);
-                        if (b < 0)
-                                log_notice("Failed to parse honor-device-enumeration flag '%s', ignoring.", val);
-                        else
-                                m->honor_device_enumeration = b;
-
                 } else if ((val = startswith(l, "show-status-overridden="))) {
                         ShowStatus s;
 
@@ -544,7 +532,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
 
                         if (q < _MANAGER_TIMESTAMP_MAX) /* found it */
                                 (void) deserialize_dual_timestamp(val, m->timestamps + q);
-                        else if (!startswith(l, "kdbus-fd=")) /* ignore kdbus */
+                        else if (!STARTSWITH_SET(l, "kdbus-fd=", "honor-device-enumeration=")) /* ignore deprecated values */
                                 log_notice("Unknown serialization item '%s', ignoring.", l);
                 }
         }
index 45f6dccf4a26201b0aa8938107f86976fdb8701a..d21698a99bfbafd7de10ecd3b8d70d37c3f41c18 100644 (file)
@@ -807,6 +807,10 @@ static int manager_find_credentials_dirs(Manager *m) {
         return 0;
 }
 
+void manager_set_switching_root(Manager *m, bool switching_root) {
+        m->switching_root = MANAGER_IS_SYSTEM(m) && switching_root;
+}
+
 int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **_m) {
         _cleanup_(manager_freep) Manager *m = NULL;
         int r;
@@ -831,6 +835,7 @@ int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager *
                 .default_timeout_start_usec = DEFAULT_TIMEOUT_USEC,
                 .default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC,
                 .default_restart_usec = DEFAULT_RESTART_USEC,
+                .default_device_timeout_usec = DEFAULT_TIMEOUT_USEC,
 
                 .original_log_level = -1,
                 .original_log_target = _LOG_TARGET_INVALID,
@@ -1760,8 +1765,6 @@ static void manager_ready(Manager *m) {
                 (void) touch_file("/run/systemd/systemd-units-load", false,
                         m->timestamps[MANAGER_TIMESTAMP_UNITS_LOAD].realtime ?: now(CLOCK_REALTIME),
                         UID_INVALID, GID_INVALID, 0444);
-
-        m->honor_device_enumeration = true;
 }
 
 Manager* manager_reloading_start(Manager *m) {
@@ -1872,6 +1875,8 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *roo
 
         manager_ready(m);
 
+        manager_set_switching_root(m, false);
+
         return 0;
 }
 
@@ -3418,11 +3423,6 @@ int manager_reload(Manager *m) {
         assert(m->n_reloading > 0);
         m->n_reloading--;
 
-        /* On manager reloading, device tag data should exists, thus, we should honor the results of device
-         * enumeration. The flag should be always set correctly by the serialized data, but it may fail. So,
-         * let's always set the flag here for safety. */
-        m->honor_device_enumeration = true;
-
         manager_ready(m);
 
         m->send_reloading_done = true;
index 63cff7989d4380d4570e82d88aefb6b0395159ae..ab4b30cfe7edc3f930f6110e91ae56a6ba6494dd 100644 (file)
@@ -356,6 +356,7 @@ struct Manager {
         ExecOutput default_std_output, default_std_error;
 
         usec_t default_restart_usec, default_timeout_start_usec, default_timeout_stop_usec;
+        usec_t default_device_timeout_usec;
         usec_t default_timeout_abort_usec;
         bool default_timeout_abort_set;
 
@@ -406,6 +407,9 @@ struct Manager {
         char *switch_root;
         char *switch_root_init;
 
+        /* This is true before and after switching root. */
+        bool switching_root;
+
         /* This maps all possible path prefixes to the units needing
          * them. It's a hashmap with a path string as key and a Set as
          * value where Unit objects are contained. */
@@ -446,8 +450,6 @@ struct Manager {
         unsigned sigchldgen;
         unsigned notifygen;
 
-        bool honor_device_enumeration;
-
         VarlinkServer *varlink_server;
         /* When we're a system manager, this object manages the subscription from systemd-oomd to PID1 that's
          * used to report changes in ManagedOOM settings (systemd server - oomd client). When
@@ -476,6 +478,8 @@ static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
 /* The objective is set to OK as soon as we enter the main loop, and set otherwise as soon as we are done with it */
 #define MANAGER_IS_RUNNING(m) ((m)->objective == MANAGER_OK)
 
+#define MANAGER_IS_SWITCHING_ROOT(m) ((m)->switching_root)
+
 #define MANAGER_IS_TEST_RUN(m) ((m)->test_run_flags != 0)
 
 int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **m);
@@ -541,6 +545,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason);
 void manager_override_show_status(Manager *m, ShowStatus mode, const char *reason);
 
 void manager_set_first_boot(Manager *m, bool b);
+void manager_set_switching_root(Manager *m, bool switching_root);
 
 void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5);
 
index 836ba132cb912f1a23fc08c52ec43fe81ca86bd0..9dc04cd07e97228eb7aec877534b787225706cb0 100644 (file)
@@ -347,7 +347,6 @@ static int mount_add_mount_dependencies(Mount *m) {
 }
 
 static int mount_add_device_dependencies(Mount *m) {
-        UnitDependencyMask mask;
         MountParameters *p;
         UnitDependency dep;
         int r;
@@ -396,17 +395,14 @@ static int mount_add_device_dependencies(Mount *m) {
          * suddenly. */
         dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
 
-        /* We always use 'what' from /proc/self/mountinfo if mounted */
-        mask = m->from_proc_self_mountinfo ? UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT : UNIT_DEPENDENCY_FILE;
-
-        r = unit_add_node_dependency(UNIT(m), p->what, dep, mask);
+        r = unit_add_node_dependency(UNIT(m), p->what, dep, UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
         if (r < 0)
                 return r;
         if (r > 0)
                 log_unit_trace(UNIT(m), "Added %s dependency on %s", unit_dependency_to_string(dep), p->what);
 
         if (mount_propagate_stop(m)) {
-                r = unit_add_node_dependency(UNIT(m), p->what, UNIT_STOP_PROPAGATED_FROM, mask);
+                r = unit_add_node_dependency(UNIT(m), p->what, UNIT_STOP_PROPAGATED_FROM, UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
                 if (r < 0)
                         return r;
                 if (r > 0)
@@ -414,11 +410,11 @@ static int mount_add_device_dependencies(Mount *m) {
                                        unit_dependency_to_string(UNIT_STOP_PROPAGATED_FROM), p->what);
         }
 
-        r = unit_add_blockdev_dependency(UNIT(m), p->what, mask);
+        r = unit_add_blockdev_dependency(UNIT(m), p->what, UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
         if (r > 0)
                 log_unit_trace(UNIT(m), "Added %s dependency on %s", unit_dependency_to_string(UNIT_AFTER), p->what);
 
-        return r;
+        return 0;
 }
 
 static int mount_add_quota_dependencies(Mount *m) {
@@ -474,8 +470,7 @@ static bool mount_is_extrinsic(Unit *u) {
 
 static int mount_add_default_ordering_dependencies(
                 Mount *m,
-                MountParameters *p,
-                UnitDependencyMask mask) {
+                MountParameters *p) {
 
         const char *after, *before, *e;
         int r;
@@ -501,23 +496,25 @@ static int mount_add_default_ordering_dependencies(
         }
 
         if (!mount_is_nofail(m)) {
-                r = unit_add_dependency_by_name(UNIT(m), UNIT_BEFORE, before, true, mask);
+                r = unit_add_dependency_by_name(UNIT(m), UNIT_BEFORE, before, true,
+                                                UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
                 if (r < 0)
                         return r;
         }
 
         if (after) {
-                r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, true, mask);
+                r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, true,
+                                                UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
                 if (r < 0)
                         return r;
         }
 
         return unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS,
-                                                 SPECIAL_UMOUNT_TARGET, true, mask);
+                                                 SPECIAL_UMOUNT_TARGET, true,
+                                                 UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
 }
 
 static int mount_add_default_dependencies(Mount *m) {
-        UnitDependencyMask mask;
         MountParameters *p;
         int r;
 
@@ -537,9 +534,7 @@ static int mount_add_default_dependencies(Mount *m) {
         if (!p)
                 return 0;
 
-        mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_DEFAULT;
-
-        r = mount_add_default_ordering_dependencies(m, p, mask);
+        r = mount_add_default_ordering_dependencies(m, p);
         if (r < 0)
                 return r;
 
@@ -549,7 +544,8 @@ static int mount_add_default_dependencies(Mount *m) {
                  * network.target, so that they are shut down only after this mount unit is
                  * stopped. */
 
-                r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, true, mask);
+                r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, true,
+                                                UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
                 if (r < 0)
                         return r;
 
@@ -558,14 +554,17 @@ static int mount_add_default_dependencies(Mount *m) {
                  * mounting network file systems, and whose purpose it is to delay this until the
                  * network is "up". */
 
-                r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, true, mask);
+                r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER,
+                                                      SPECIAL_NETWORK_ONLINE_TARGET, true,
+                                                      UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
                 if (r < 0)
                         return r;
         }
 
         /* If this is a tmpfs mount then we have to unmount it before we try to deactivate swaps */
         if (streq_ptr(p->fstype, "tmpfs")) {
-                r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, true, mask);
+                r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, true,
+                                                UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
                 if (r < 0)
                         return r;
         }
@@ -607,8 +606,18 @@ static int mount_verify(Mount *m) {
 
 static int mount_add_non_exec_dependencies(Mount *m) {
         int r;
+
         assert(m);
 
+        /* Any dependencies based on /proc/self/mountinfo may be now stale. */
+        unit_remove_dependencies(UNIT(m),
+                                 UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT |
+                                 UNIT_DEPENDENCY_MOUNTINFO_DEFAULT |
+                                 UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
+
+        if (!m->where)
+                return 0;
+
         /* Adds in all dependencies directly responsible for ordering the mount, as opposed to dependencies
          * resulting from the ExecContext and such. */
 
@@ -908,8 +917,9 @@ static void mount_enter_dead(Mount *m, MountResult f) {
 
         dynamic_creds_destroy(&m->dynamic_creds);
 
-        /* Any dependencies based on /proc/self/mountinfo are now stale */
-        unit_remove_dependencies(UNIT(m), UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
+        /* Any dependencies based on /proc/self/mountinfo are now stale. Let's re-generate dependencies from
+         * .mount unit. */
+        (void) mount_add_non_exec_dependencies(m);
 }
 
 static void mount_enter_mounted(Mount *m, MountResult f) {
@@ -1053,11 +1063,13 @@ static void mount_enter_mounting(Mount *m) {
         if (p && mount_is_bind(p)) {
                 r = mkdir_p_label(p->what, m->directory_mode);
                 /* mkdir_p_label() can return -EEXIST if the target path exists and is not a directory - which is
-                 * totally OK, in case the user wants us to overmount a non-directory inode. */
-                if (r < 0 && r != -EEXIST) {
-                        log_unit_error_errno(UNIT(m), r, "Failed to make bind mount source '%s': %m", p->what);
-                        goto fail;
-                }
+                 * totally OK, in case the user wants us to overmount a non-directory inode. Also -EROFS can be
+                 * returned on read-only filesystem. Moreover, -EACCES (and also maybe -EPERM?) may be returned
+                 * when the path is on NFS. See issue #24120. All such errors will be logged in the debug level. */
+                if (r < 0 && r != -EEXIST)
+                        log_unit_full_errno(UNIT(m),
+                                            (r == -EROFS || ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_WARNING,
+                                            r, "Failed to make bind mount source '%s', ignoring: %m", p->what);
         }
 
         if (p) {
@@ -1598,15 +1610,15 @@ static int mount_setup_new_unit(
         if (r < 0)
                 return r;
 
-        r = mount_add_non_exec_dependencies(MOUNT(u));
-        if (r < 0)
-                return r;
-
         /* This unit was generated because /proc/self/mountinfo reported it. Remember this, so that by the
          * time we load the unit file for it (and thus add in extra deps right after) we know what source to
          * attributes the deps to. */
         MOUNT(u)->from_proc_self_mountinfo = true;
 
+        r = mount_add_non_exec_dependencies(MOUNT(u));
+        if (r < 0)
+                return r;
+
         /* We have only allocated the stub now, let's enqueue this unit for loading now, so that everything
          * else is loaded in now. */
         unit_add_to_load_queue(u);
@@ -1672,9 +1684,6 @@ static int mount_setup_existing_unit(
         if (FLAGS_SET(flags, MOUNT_PROC_JUST_CHANGED)) {
                 /* If things changed, then make sure that all deps are regenerated. Let's
                  * first remove all automatic deps, and then add in the new ones. */
-
-                unit_remove_dependencies(u, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
-
                 r = mount_add_non_exec_dependencies(MOUNT(u));
                 if (r < 0)
                         return r;
@@ -1715,37 +1724,14 @@ static int mount_setup_unit(
         if (!is_path(where))
                 return 0;
 
-        /* Mount unit names have to be (like all other unit names) short enough to fit into file names. This
-         * means there's a good chance that overly long mount point paths after mangling them to look like a
-         * unit name would result in unit names we don't actually consider valid. This should be OK however
-         * as such long mount point paths should not happen on regular systems — and if they appear
-         * nonetheless they are generally synthesized by software, and thus managed by that other
-         * software. Having such long names just means you cannot use systemd to manage those specific mount
-         * points, which should be an OK restriction to make. After all we don't have to be able to manage
-         * all mount points in the world — as long as we don't choke on them when we encounter them. */
         r = unit_name_from_path(where, ".mount", &e);
-        if (r < 0) {
-                static RateLimit rate_limit = { /* Let's log about this at warning level at most once every
-                                                 * 5s. Given that we generate this whenever we read the file
-                                                 * otherwise we probably shouldn't flood the logs with
-                                                 * this */
-                        .interval = 5 * USEC_PER_SEC,
-                        .burst = 1,
-                };
-
-                if (r == -ENAMETOOLONG)
-                        return log_struct_errno(
-                                        ratelimit_below(&rate_limit) ? LOG_WARNING : LOG_DEBUG, r,
-                                        "MESSAGE_ID=" SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR,
-                                        "MOUNT_POINT=%s", where,
-                                        LOG_MESSAGE("Mount point path '%s' too long to fit into unit name, ignoring mount point.", where));
-
+        if (r < 0)
                 return log_struct_errno(
-                                ratelimit_below(&rate_limit) ? LOG_WARNING : LOG_DEBUG, r,
+                                LOG_WARNING, r,
                                 "MESSAGE_ID=" SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR,
                                 "MOUNT_POINT=%s", where,
-                                LOG_MESSAGE("Failed to generate valid unit name from mount point path '%s', ignoring mount point: %m", where));
-        }
+                                LOG_MESSAGE("Failed to generate valid unit name from mount point path '%s', ignoring mount point: %m",
+                                            where));
 
         u = manager_get_unit(m, e);
         if (u)
@@ -1982,7 +1968,7 @@ static int drain_libmount(Manager *m) {
 }
 
 static int mount_process_proc_self_mountinfo(Manager *m) {
-        _cleanup_set_free_free_ Set *around = NULL, *gone = NULL;
+        _cleanup_set_free_ Set *around = NULL, *gone = NULL;
         const char *what;
         int r;
 
@@ -2013,13 +1999,10 @@ static int mount_process_proc_self_mountinfo(Manager *m) {
                          * existed. */
 
                         if (mount->from_proc_self_mountinfo &&
-                            mount->parameters_proc_self_mountinfo.what) {
-
+                            mount->parameters_proc_self_mountinfo.what)
                                 /* Remember that this device might just have disappeared */
-                                if (set_ensure_allocated(&gone, &path_hash_ops) < 0 ||
-                                    set_put_strdup(&gone, mount->parameters_proc_self_mountinfo.what) < 0)
+                                if (set_put_strdup_full(&gone, &path_hash_ops_free, mount->parameters_proc_self_mountinfo.what) < 0)
                                         log_oom(); /* we don't care too much about OOM here... */
-                        }
 
                         mount->from_proc_self_mountinfo = false;
                         assert_se(update_parameters_proc_self_mountinfo(mount, NULL, NULL, NULL) >= 0);
@@ -2077,13 +2060,10 @@ static int mount_process_proc_self_mountinfo(Manager *m) {
 
                 if (mount_is_mounted(mount) &&
                     mount->from_proc_self_mountinfo &&
-                    mount->parameters_proc_self_mountinfo.what) {
+                    mount->parameters_proc_self_mountinfo.what)
                         /* Track devices currently used */
-
-                        if (set_ensure_allocated(&around, &path_hash_ops) < 0 ||
-                            set_put_strdup(&around, mount->parameters_proc_self_mountinfo.what) < 0)
+                        if (set_put_strdup_full(&around, &path_hash_ops_free, mount->parameters_proc_self_mountinfo.what) < 0)
                                 log_oom();
-                }
 
                 /* Reset the flags for later calls */
                 mount->proc_flags = 0;
index 41457ea816474a58f5ff2afee7638cdbaeffd2b9..016afe4d9e90be208935354aa63f0f6b3008967d 100644 (file)
@@ -7,7 +7,9 @@
 #include <sys/file.h>
 #include <sys/mount.h>
 #include <unistd.h>
+#if WANT_LINUX_FS_H
 #include <linux/fs.h>
+#endif
 
 #include "alloc-util.h"
 #include "base-filesystem.h"
index 63d3288caf16fc8bb3960b8d94d9498f84a6b480..54a6cc63e4b4a9d55c4fde55ed20264311a1ab3f 100644 (file)
@@ -6,6 +6,7 @@
 #include "alloc-util.h"
 #include "dbus-scope.h"
 #include "dbus-unit.h"
+#include "exit-status.h"
 #include "load-dropin.h"
 #include "log.h"
 #include "process-util.h"
 #include "strv.h"
 #include "unit-name.h"
 #include "unit.h"
+#include "user-util.h"
 
 static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
         [SCOPE_DEAD] = UNIT_INACTIVE,
+        [SCOPE_START_CHOWN] = UNIT_ACTIVATING,
         [SCOPE_RUNNING] = UNIT_ACTIVE,
         [SCOPE_ABANDONED] = UNIT_ACTIVE,
         [SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING,
@@ -39,6 +42,7 @@ static void scope_init(Unit *u) {
         s->runtime_max_usec = USEC_INFINITY;
         s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
         u->ignore_on_isolate = true;
+        s->user = s->group = NULL;
 }
 
 static void scope_done(Unit *u) {
@@ -50,6 +54,9 @@ static void scope_done(Unit *u) {
         s->controller_track = sd_bus_track_unref(s->controller_track);
 
         s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
+
+        s->user = mfree(s->user);
+        s->group = mfree(s->group);
 }
 
 static usec_t scope_running_timeout(Scope *s) {
@@ -107,7 +114,7 @@ static void scope_set_state(Scope *s, ScopeState state) {
         old_state = s->state;
         s->state = state;
 
-        if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
+        if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL, SCOPE_START_CHOWN))
                 s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
 
         if (IN_SET(state, SCOPE_DEAD, SCOPE_FAILED)) {
@@ -353,26 +360,72 @@ fail:
         scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
 }
 
-static int scope_start(Unit *u) {
-        Scope *s = SCOPE(u);
+static int scope_enter_start_chown(Scope *s) {
+        Unit *u = UNIT(s);
+        pid_t pid;
         int r;
 
         assert(s);
+        assert(s->user);
 
-        if (unit_has_name(u, SPECIAL_INIT_SCOPE))
-                return -EPERM;
+        r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), u->manager->default_timeout_start_usec));
+        if (r < 0)
+                return r;
 
-        if (s->state == SCOPE_FAILED)
-                return -EPERM;
+        r = unit_fork_helper_process(u, "(sd-chown-cgroup)", &pid);
+        if (r < 0)
+                goto fail;
 
-        /* We can't fulfill this right now, please try again later */
-        if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
-                return -EAGAIN;
+        if (r == 0) {
+                uid_t uid = UID_INVALID;
+                gid_t gid = GID_INVALID;
 
-        assert(s->state == SCOPE_DEAD);
+                if (!isempty(s->user)) {
+                        const char *user = s->user;
 
-        if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
-                return -ENOENT;
+                        r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
+                        if (r < 0) {
+                                log_unit_error_errno(UNIT(s), r, "Failed to resolve user \"%s\": %m", user);
+                                _exit(EXIT_USER);
+                        }
+                }
+
+                if (!isempty(s->group)) {
+                        const char *group = s->group;
+
+                        r = get_group_creds(&group, &gid, 0);
+                        if (r < 0) {
+                                log_unit_error_errno(UNIT(s), r, "Failed to resolve group \"%s\": %m", group);
+                                _exit(EXIT_GROUP);
+                        }
+                }
+
+                r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, uid, gid);
+                if (r < 0) {
+                        log_unit_error_errno(UNIT(s), r, "Failed to adjust control group access: %m");
+                        _exit(EXIT_CGROUP);
+                }
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        r = unit_watch_pid(UNIT(s), pid, true);
+        if (r < 0)
+                goto fail;
+
+        scope_set_state(s, SCOPE_START_CHOWN);
+
+        return 1;
+fail:
+        s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
+        return r;
+}
+
+static int scope_enter_running(Scope *s) {
+        Unit *u = UNIT(s);
+        int r;
+
+        assert(s);
 
         (void) bus_scope_track_controller(s);
 
@@ -380,9 +433,6 @@ static int scope_start(Unit *u) {
         if (r < 0)
                 return r;
 
-        (void) unit_realize_cgroup(u);
-        (void) unit_reset_accounting(u);
-
         unit_export_state_files(u);
 
         r = unit_attach_pids_to_cgroup(u, u->pids, NULL);
@@ -392,7 +442,7 @@ static int scope_start(Unit *u) {
                 return r;
         }
         if (r == 0) {
-                log_unit_warning(u, "No PIDs left to attach to the scope's control group, refusing: %m");
+                log_unit_warning(u, "No PIDs left to attach to the scope's control group, refusing.");
                 scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
                 return -ECHILD;
         }
@@ -416,6 +466,37 @@ static int scope_start(Unit *u) {
         return 1;
 }
 
+static int scope_start(Unit *u) {
+        Scope *s = SCOPE(u);
+
+        assert(s);
+
+        if (unit_has_name(u, SPECIAL_INIT_SCOPE))
+                return -EPERM;
+
+        if (s->state == SCOPE_FAILED)
+                return -EPERM;
+
+        /* We can't fulfill this right now, please try again later */
+        if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
+                return -EAGAIN;
+
+        assert(s->state == SCOPE_DEAD);
+
+        if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
+                return -ENOENT;
+
+        (void) unit_realize_cgroup(u);
+        (void) unit_reset_accounting(u);
+
+        /* We check only for User= option to keep behavior consistent with logic for service units,
+         * i.e. having 'Delegate=true Group=foo' w/o specifying User= has no effect. */
+        if (s->user && unit_cgroup_delegate(u))
+                return scope_enter_start_chown(s);
+
+        return scope_enter_running(s);
+}
+
 static int scope_stop(Unit *u) {
         Scope *s = SCOPE(u);
 
@@ -546,8 +627,45 @@ static void scope_notify_cgroup_empty_event(Unit *u) {
                 unit_prune_cgroup(u);
 }
 
+static void scope_notify_cgroup_oom_event(Unit *u, bool managed_oom) {
+        Scope *s = SCOPE(u);
+
+        if (managed_oom)
+                log_unit_debug(u, "Process(es) of control group were killed by systemd-oomd.");
+        else
+                log_unit_debug(u, "Process of control group was killed by the OOM killer.");
+
+        /* This will probably need to be modified when scope units get an oom-policy */
+        switch (s->state) {
+
+        case SCOPE_START_CHOWN:
+        case SCOPE_RUNNING:
+        case SCOPE_STOP_SIGTERM:
+                scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_OOM_KILL);
+                break;
+
+        case SCOPE_STOP_SIGKILL:
+                if (s->result == SCOPE_SUCCESS)
+                        s->result = SCOPE_FAILURE_OOM_KILL;
+                break;
+        /* SCOPE_DEAD, SCOPE_ABANDONED, and SCOPE_FAILED end up in default */
+        default:
+                ;
+        }
+}
+
 static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
-        assert(u);
+        Scope *s = SCOPE(u);
+
+        assert(s);
+
+        if (s->state == SCOPE_START_CHOWN) {
+                if (!is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
+                        scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
+                else
+                        scope_enter_running(s);
+                return;
+        }
 
         /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to
          * watch, under the assumption that we'll sooner or later get a SIGCHLD for them, as the original
@@ -585,6 +703,11 @@ static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *user
                 scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
                 break;
 
+        case SCOPE_START_CHOWN:
+                log_unit_warning(UNIT(s), "User lookup timed out. Entering failed state.");
+                scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
+                break;
+
         default:
                 assert_not_reached();
         }
@@ -659,6 +782,7 @@ static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
         [SCOPE_SUCCESS]           = "success",
         [SCOPE_FAILURE_RESOURCES] = "resources",
         [SCOPE_FAILURE_TIMEOUT]   = "timeout",
+        [SCOPE_FAILURE_OOM_KILL]  = "oom-kill",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult);
@@ -709,6 +833,7 @@ const UnitVTable scope_vtable = {
         .reset_failed = scope_reset_failed,
 
         .notify_cgroup_empty = scope_notify_cgroup_empty_event,
+        .notify_cgroup_oom = scope_notify_cgroup_oom_event,
 
         .bus_set_property = bus_scope_set_property,
         .bus_commit_properties = bus_scope_commit_properties,
index 03a9ba432438daac75d6cc7166b81eaf79a55f32..6a228f1177d027d96370eb5a0faf03505275b9b2 100644 (file)
@@ -11,6 +11,7 @@ typedef enum ScopeResult {
         SCOPE_SUCCESS,
         SCOPE_FAILURE_RESOURCES,
         SCOPE_FAILURE_TIMEOUT,
+        SCOPE_FAILURE_OOM_KILL,
         _SCOPE_RESULT_MAX,
         _SCOPE_RESULT_INVALID = -EINVAL,
 } ScopeResult;
@@ -34,6 +35,9 @@ struct Scope {
         bool was_abandoned;
 
         sd_event_source *timer_event_source;
+
+        char *user;
+        char *group;
 };
 
 extern const UnitVTable scope_vtable;
index d77901caa5f2c3d992162f5b1ad62f225e9d61ec..848ae246a7f7b075e78ec9d858e9e5278647d557 100644 (file)
@@ -179,13 +179,14 @@ static int access_init(sd_bus_error *error) {
 */
 int mac_selinux_access_check_internal(
                 sd_bus_message *message,
-                const char *path,
+                const char *unit_path,
+                const char *unit_context,
                 const char *permission,
                 const char *function,
                 sd_bus_error *error) {
 
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
-        const char *tclass, *scon;
+        const char *tclass, *scon, *acon;
         _cleanup_free_ char *cl = NULL;
         _cleanup_freecon_ char *fcon = NULL;
         char **cmdline = NULL;
@@ -214,37 +215,22 @@ int mac_selinux_access_check_internal(
         if (r < 0)
                 return r;
 
-        /* The SELinux context is something we really should have
-         * gotten directly from the message or sender, and not be an
-         * augmented field. If it was augmented we cannot use it for
-         * authorization, since this is racy and vulnerable. Let's add
-         * an extra check, just in case, even though this really
-         * shouldn't be possible. */
+        /* The SELinux context is something we really should have gotten directly from the message or sender,
+         * and not be an augmented field. If it was augmented we cannot use it for authorization, since this
+         * is racy and vulnerable. Let's add an extra check, just in case, even though this really shouldn't
+         * be possible. */
         assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
 
         r = sd_bus_creds_get_selinux_context(creds, &scon);
         if (r < 0)
                 return r;
 
-        if (path) {
-                /* Get the file context of the unit file */
-
-                if (getfilecon_raw(path, &fcon) < 0) {
-                        r = -errno;
-
-                        log_warning_errno(r, "SELinux getfilecon_raw() on '%s' failed%s (perm=%s): %m",
-                                          path,
-                                          enforce ? "" : ", ignoring",
-                                          permission);
-                        if (!enforce)
-                                return 0;
-
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
-                }
-
+        if (unit_context) {
+                /* Nice! The unit comes with a SELinux context read from the unit file */
+                acon = unit_context;
                 tclass = "service";
-
         } else {
+                /* If no unit context is known, use our own */
                 if (getcon_raw(&fcon) < 0) {
                         r = -errno;
 
@@ -254,9 +240,10 @@ int mac_selinux_access_check_internal(
                         if (!enforce)
                                 return 0;
 
-                        return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context: %m");
                 }
 
+                acon = fcon;
                 tclass = "system";
         }
 
@@ -265,22 +252,22 @@ int mac_selinux_access_check_internal(
 
         struct audit_info audit_info = {
                 .creds = creds,
-                .path = path,
+                .path = unit_path,
                 .cmdline = cl,
                 .function = function,
         };
 
-        r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
+        r = selinux_check_access(scon, acon, tclass, permission, &audit_info);
         if (r < 0) {
-                r = errno_or_else(EPERM);
+                errno = -(r = errno_or_else(EPERM));
 
                 if (enforce)
-                        sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
+                        sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access: %m");
         }
 
         log_full_errno_zerook(LOG_DEBUG, r,
                               "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s function=%s path=%s cmdline=%s: %m",
-                              scon, fcon, tclass, permission, enforce ? "enforcing" : "permissive", function, strna(path), isempty(cl) ? "n/a" : cl);
+                              scon, acon, tclass, permission, enforce ? "enforcing" : "permissive", function, strna(unit_path), strna(empty_to_null(cl)));
         return enforce ? r : 0;
 }
 
@@ -288,7 +275,8 @@ int mac_selinux_access_check_internal(
 
 int mac_selinux_access_check_internal(
                 sd_bus_message *message,
-                const char *path,
+                const char *unit_path,
+                const char *unit_label,
                 const char *permission,
                 const char *function,
                 sd_bus_error *error) {
index e5ebcb13a20cfdb70961823f918dc09e8d29980b..dc8da9e97bde56af842f40820d8f84d1cd22e620 100644 (file)
@@ -5,14 +5,10 @@
 
 #include "manager.h"
 
-int mac_selinux_access_check_internal(sd_bus_message *message,
-                                      const char *path,
-                                      const char *permission,
-                                      const char *function,
-                                      sd_bus_error *error);
+int mac_selinux_access_check_internal(sd_bus_message *message, const char *unit_path, const char *unit_label, const char *permission, const char *function, sd_bus_error *error);
 
 #define mac_selinux_access_check(message, permission, error) \
-        mac_selinux_access_check_internal((message), NULL, (permission), __func__, (error))
+        mac_selinux_access_check_internal((message), NULL, NULL, (permission), __func__, (error))
 
 #define mac_selinux_unit_access_check(unit, message, permission, error) \
-        mac_selinux_access_check_internal((message), unit_label_path(unit), (permission), __func__, (error))
+        mac_selinux_access_check_internal((message), (unit)->fragment_path, (unit)->access_selinux_context, (permission), __func__, (error))
index 5f1a218bb5beba1a679f0d2340b7343ee0cfb6b1..a715a1d1dc82424be0041750aaaccfb5ee6585fd 100644 (file)
@@ -600,7 +600,7 @@ static int service_verify(Service *s) {
                 return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has Restart= set to either always or on-success, which isn't allowed for Type=oneshot services. Refusing.");
 
         if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status))
-                return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing.");
+                return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has RestartForceExitStatus= set, which isn't allowed for Type=oneshot services. Refusing.");
 
         if (s->type == SERVICE_ONESHOT && s->exit_type == SERVICE_EXIT_CGROUP)
                 return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has ExitType=cgroup set, which isn't allowed for Type=oneshot services. Refusing.");
index 222c76cf79c28438be431d42b58456c97c02f4cb..2d1190d0182c21942904349011867a3dbbb1354b 100644 (file)
@@ -1437,8 +1437,7 @@ int swap_process_device_new(Manager *m, sd_device *dev) {
                 int q;
 
                 q = unit_name_from_path(devlink, ".swap", &n);
-                if (IN_SET(q, -EINVAL, -ENAMETOOLONG)) /* If name too long or otherwise not convertible to
-                                                        * unit name, we can't manage it */
+                if (q == -EINVAL) /* If the name is not convertible to unit name, we can't manage it */
                         continue;
                 if (q < 0)
                         return q;
index ae1b47b2ba3d3437ce288ce3c1efc95fb4b2d92f..318c0348264f553bec0387681b2bab15588ceada 100644 (file)
@@ -46,6 +46,7 @@
 #DefaultTimeoutStartSec=90s
 #DefaultTimeoutStopSec=90s
 #DefaultTimeoutAbortSec=
+#DefaultDeviceTimeoutSec=90s
 #DefaultRestartSec=100ms
 #DefaultStartLimitIntervalSec=10s
 #DefaultStartLimitBurst=5
index 63256d541acfe0ef7b8bce69e3012e9d472fd8ac..9de325ba66240af365e0d9ca1b00d1187ed79373 100644 (file)
@@ -394,19 +394,18 @@ static void timer_enter_waiting(Timer *t, bool time_change) {
                 if (v->base == TIMER_CALENDAR) {
                         usec_t b, rebased;
 
-                        /* If we know the last time this was
-                         * triggered, schedule the job based relative
-                         * to that. If we don't, just start from
-                         * the activation time. */
-
-                        if (t->last_trigger.realtime > 0)
+                        /* Update last_trigger to 'now' in case the system time changes, so that
+                         * next_elapse is not stuck with a future date. */
+                        if (time_change)
+                                b = ts.realtime;
+                        /* If we know the last time this was triggered, schedule the job based relative
+                         * to that. If we don't, just start from the activation time. */
+                        else if (t->last_trigger.realtime > 0)
                                 b = t->last_trigger.realtime;
-                        else {
-                                if (state_translation_table[t->state] == UNIT_ACTIVE)
-                                        b = UNIT(t)->inactive_exit_timestamp.realtime;
-                                else
-                                        b = ts.realtime;
-                        }
+                        else if (state_translation_table[t->state] == UNIT_ACTIVE)
+                                b = UNIT(t)->inactive_exit_timestamp.realtime;
+                        else
+                                b = ts.realtime;
 
                         r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
                         if (r < 0)
index 65ed110ce63a6dcf5c804dd083b9ca2c75262434..797bf6c5ce92248e2c66b850ee89605431a197fa 100644 (file)
@@ -592,6 +592,7 @@ static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependency
                 { UNIT_DEPENDENCY_PATH,               "path"               },
                 { UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT, "mountinfo-implicit" },
                 { UNIT_DEPENDENCY_MOUNTINFO_DEFAULT,  "mountinfo-default"  },
+                { UNIT_DEPENDENCY_MOUNTINFO_OR_FILE,  "mountinfo-or-file"  },
                 { UNIT_DEPENDENCY_PROC_SWAP,          "proc-swap"          },
                 { UNIT_DEPENDENCY_SLICE_PROPERTY,     "slice-property"     },
         };
@@ -732,6 +733,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
         STRV_FOREACH(j, u->documentation)
                 fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
 
+        if (u->access_selinux_context)
+                fprintf(f, "%s\tAccess SELinux Context: %s\n", prefix, u->access_selinux_context);
+
         following = unit_following(u);
         if (following)
                 fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
index 40fc79995adc63910c4bc9763a0cc0cd006a388f..0798c29c9d469f3a1f5038866a38154c64db74af 100644 (file)
@@ -804,6 +804,8 @@ Unit* unit_free(Unit *u) {
         free(u->job_timeout_reboot_arg);
         free(u->reboot_arg);
 
+        free(u->access_selinux_context);
+
         set_free_free(u->aliases);
         free(u->id);
 
@@ -1517,7 +1519,7 @@ static int unit_add_mount_dependencies(Unit *u) {
                         Unit *m;
 
                         r = unit_name_from_path(prefix, ".mount", &p);
-                        if (IN_SET(r, -EINVAL, -ENAMETOOLONG))
+                        if (r == -EINVAL)
                                 continue; /* If the path cannot be converted to a mount unit name, then it's
                                            * not manageable as a unit by systemd, and hence we don't need a
                                            * dependency on it. Let's thus silently ignore the issue. */
@@ -5165,6 +5167,9 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
 
                                 unit_add_to_gc_queue(other);
 
+                                /* The unit 'other' may not be wanted by the unit 'u'. */
+                                unit_submit_to_stop_when_unneeded_queue(other);
+
                                 done = false;
                                 break;
                         }
@@ -5550,34 +5555,6 @@ bool unit_needs_console(Unit *u) {
         return exec_context_may_touch_console(ec);
 }
 
-const char *unit_label_path(const Unit *u) {
-        const char *p;
-
-        assert(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;
-
-        return p;
-}
-
 int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) {
         int r;
 
index c6a2fadf528024c31c8a7a90010f65ef4e4f0ce6..4dabbb9a3c93fcd94243759b88bc1d60d8acaa8a 100644 (file)
@@ -86,13 +86,17 @@ typedef enum UnitDependencyMask {
          * DefaultDependencies= and thus also involving configuration from UNIT_DEPENDENCY_FILE sources */
         UNIT_DEPENDENCY_MOUNTINFO_DEFAULT  = 1 << 6,
 
+        /* A dependency created because of data read from /proc/self/mountinfo, but fallback to unit configuration
+         * sources */
+        UNIT_DEPENDENCY_MOUNTINFO_OR_FILE  = 1 << 7,
+
         /* A dependency created because of data read from /proc/swaps and no other configuration source */
-        UNIT_DEPENDENCY_PROC_SWAP          = 1 << 7,
+        UNIT_DEPENDENCY_PROC_SWAP          = 1 << 8,
 
         /* A dependency for units in slices assigned by directly setting Slice= */
-        UNIT_DEPENDENCY_SLICE_PROPERTY     = 1 << 8,
+        UNIT_DEPENDENCY_SLICE_PROPERTY     = 1 << 9,
 
-        _UNIT_DEPENDENCY_MASK_FULL         = (1 << 9) - 1,
+        _UNIT_DEPENDENCY_MASK_FULL         = (1 << 10) - 1,
 } UnitDependencyMask;
 
 /* The Unit's dependencies[] hashmaps use this structure as value. It has the same size as a void pointer, and thus can
@@ -151,6 +155,11 @@ typedef struct Unit {
         char *description;
         char **documentation;
 
+        /* The SELinux context used for checking access to this unit read off the unit file at load time (do
+         * not confuse with the selinux_context field in ExecContext which is the SELinux context we'll set
+         * for processes) */
+        char *access_selinux_context;
+
         char *fragment_path; /* if loaded from a config file this is the primary path to it */
         char *source_path; /* if converted, the source file */
         char **dropin_paths;
@@ -944,8 +953,6 @@ int unit_warn_leftover_processes(Unit *u, cg_kill_log_func_t log_func);
 
 bool unit_needs_console(Unit *u);
 
-const char *unit_label_path(const Unit *u);
-
 int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error);
 
 static inline bool unit_has_job_type(Unit *u, JobType type) {
index c29068828cbd501678b1e3305896258fc63dc036..b69974978eeff73ef50d9826bb09b4ab25e40b02 100644 (file)
@@ -14,7 +14,7 @@
 
 [Manager]
 #LogLevel=info
-#LogTarget=console
+#LogTarget=auto
 #LogColor=yes
 #LogLocation=no
 #LogTime=no
index 994d968d871b4946029947cfceb096a2f9fcdd9a..3ec41a32c37cc033f7ea910e08bf4eceafbbf47e 100644 (file)
@@ -29,6 +29,7 @@
 #include "fs-util.h"
 #include "io-util.h"
 #include "journal-importer.h"
+#include "journal-send.h"
 #include "log.h"
 #include "macro.h"
 #include "main-func.h"
@@ -842,12 +843,10 @@ log:
 
         core_message = strjoina(core_message, stacktrace ? "\n\n" : NULL, stacktrace);
 
-        if (context->is_journald) {
-                /* We cannot log to the journal, so just print the message.
-                 * The target was set previously to something safe. */
+        if (context->is_journald)
+                /* We might not be able to log to the journal, so let's always print the message to another
+                 * log target. The target was set previously to something safe. */
                 log_dispatch(LOG_ERR, 0, core_message);
-                return 0;
-        }
 
         (void) iovw_put_string_field(iovw, "MESSAGE=", core_message);
 
@@ -903,8 +902,29 @@ log:
                                  coredump_size, arg_journal_size_max);
         }
 
+        /* If journald is coredumping, we have to be careful that we don't deadlock when trying to write the
+         * coredump to the journal, so we put the journal socket in nonblocking mode before trying to write
+         * the coredump to the socket. */
+
+        if (context->is_journald) {
+                r = journal_fd_nonblock(true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make journal socket non-blocking: %m");
+        }
+
         r = sd_journal_sendv(iovw->iovec, iovw->count);
-        if (r < 0)
+
+        if (context->is_journald) {
+                int k;
+
+                k = journal_fd_nonblock(false);
+                if (k < 0)
+                        return log_error_errno(k, "Failed to make journal socket blocking: %m");
+        }
+
+        if (r == -EAGAIN && context->is_journald)
+                log_warning_errno(r, "Failed to log journal coredump, ignoring: %m");
+        else if (r < 0)
                 return log_error_errno(r, "Failed to log coredump: %m");
 
         return 0;
@@ -1271,6 +1291,13 @@ static int process_kernel(int argc, char* argv[]) {
         struct iovec_wrapper *iovw;
         int r;
 
+        /* When we're invoked by the kernel, stdout/stderr are closed which is dangerous because the fds
+         * could get reallocated. To avoid hard to debug issues, let's instead bind stdout/stderr to
+         * /dev/null. */
+        r = rearrange_stdio(STDIN_FILENO, -1, -1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect stdout/stderr to /dev/null: %m");
+
         log_debug("Processing coredump received from the kernel...");
 
         iovw = iovw_new();
index 01e922a9624fa0b8fc7e52088314e82e6a93c1fa..43babb9803301dc08b484d8be27b1330d027cd3d 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-locator.h"
 #include "bus-util.h"
 #include "compress.h"
 #include "def.h"
@@ -1196,13 +1197,7 @@ static int check_units_active(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire bus: %m");
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "ListUnitsByPatterns");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsByPatterns");
         if (r < 0)
                 return bus_log_create_error(r);
 
index 59bb072115fe7a867275488bde53c3a7ca47804d..e9c7c96fc561065e58093d16afb02a64394a5b92 100644 (file)
@@ -832,13 +832,14 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_tpm2_device = streq(optarg, "auto") ? NULL : optarg;
                         break;
 
-                case ARG_TPM2_PCRS:
+                case ARG_TPM2_PCRS: {
+                        uint32_t mask;
+
                         if (isempty(optarg)) {
                                 arg_tpm2_pcr_mask = 0;
                                 break;
                         }
 
-                        uint32_t mask;
                         r = tpm2_parse_pcrs(optarg, &mask);
                         if (r < 0)
                                 return r;
@@ -849,6 +850,7 @@ static int parse_argv(int argc, char *argv[]) {
                                 arg_tpm2_pcr_mask |= mask;
 
                         break;
+                }
 
                 case ARG_NAME:
                         if (isempty(optarg)) {
index 045adf871a1666d5ce76f1b4855bcd75ee81e80d..f7fc4963a83341ecf798fa6c4bf8a46287dc7077 100644 (file)
@@ -14,6 +14,7 @@
 #include "cryptsetup-util.h"
 #include "env-util.h"
 #include "escape.h"
+#include "fileio.h"
 #include "libfido2-util.h"
 #include "main-func.h"
 #include "memory-util.h"
@@ -28,6 +29,7 @@
 #include "tpm2-util.h"
 
 static EnrollType arg_enroll_type = _ENROLL_TYPE_INVALID;
+static char *arg_unlock_keyfile = NULL;
 static char *arg_pkcs11_token_uri = NULL;
 static char *arg_fido2_device = NULL;
 static char *arg_tpm2_device = NULL;
@@ -47,10 +49,12 @@ static int arg_fido2_cred_alg = 0;
 
 assert_cc(sizeof(arg_wipe_slots_mask) * 8 >= _ENROLL_TYPE_MAX);
 
+STATIC_DESTRUCTOR_REGISTER(arg_unlock_keyfile, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_wipe_slots, freep);
 
 static bool wipe_requested(void) {
         return arg_n_wipe_slots > 0 ||
@@ -92,6 +96,8 @@ static int help(void) {
                "     --version         Show package version\n"
                "     --password        Enroll a user-supplied password\n"
                "     --recovery-key    Enroll a recovery key\n"
+               "     --unlock-key-file=PATH\n"
+               "                       Use a file to unlock the volume\n"
                "     --pkcs11-token-uri=URI\n"
                "                       Specify PKCS#11 security token URI\n"
                "     --fido2-credential-algorithm=STRING\n"
@@ -127,6 +133,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VERSION = 0x100,
                 ARG_PASSWORD,
                 ARG_RECOVERY_KEY,
+                ARG_UNLOCK_KEYFILE,
                 ARG_PKCS11_TOKEN_URI,
                 ARG_FIDO2_DEVICE,
                 ARG_TPM2_DEVICE,
@@ -144,6 +151,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "version",                      no_argument,       NULL, ARG_VERSION          },
                 { "password",                     no_argument,       NULL, ARG_PASSWORD         },
                 { "recovery-key",                 no_argument,       NULL, ARG_RECOVERY_KEY     },
+                { "unlock-key-file",              required_argument, NULL, ARG_UNLOCK_KEYFILE   },
                 { "pkcs11-token-uri",             required_argument, NULL, ARG_PKCS11_TOKEN_URI },
                 { "fido2-credential-algorithm",   required_argument, NULL, ARG_FIDO2_CRED_ALG   },
                 { "fido2-device",                 required_argument, NULL, ARG_FIDO2_DEVICE     },
@@ -221,6 +229,12 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_enroll_type = ENROLL_RECOVERY;
                         break;
 
+                case ARG_UNLOCK_KEYFILE:
+                        r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_unlock_keyfile);
+                        if (r < 0)
+                                return r;
+                        break;
+
                 case ARG_PKCS11_TOKEN_URI: {
                         _cleanup_free_ char *uri = NULL;
 
@@ -321,13 +335,12 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
-                case ARG_TPM2_PIN: {
+                case ARG_TPM2_PIN:
                         r = parse_boolean_argument("--tpm2-with-pin=", optarg, &arg_tpm2_pin);
                         if (r < 0)
                                 return r;
 
                         break;
-                }
 
                 case ARG_WIPE_SLOT: {
                         const char *p = optarg;
@@ -473,6 +486,35 @@ static int prepare_luks(
         if (!vk)
                 return log_oom();
 
+        if (arg_unlock_keyfile) {
+                _cleanup_(erase_and_freep) char *password = NULL;
+                size_t password_len;
+
+                r = read_full_file_full(
+                                AT_FDCWD,
+                                arg_unlock_keyfile,
+                                0,
+                                SIZE_MAX,
+                                READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
+                                NULL,
+                                &password,
+                                &password_len);
+                if (r < 0)
+                        return log_error_errno(r, "Reading keyfile %s failed: %m", arg_unlock_keyfile);
+
+                r = crypt_volume_key_get(
+                                cd,
+                                CRYPT_ANY_SLOT,
+                                vk,
+                                &vks,
+                                password,
+                                password_len);
+                if (r < 0)
+                        return log_error_errno(r, "Unlocking via keyfile failed: %m");
+
+                goto out;
+        }
+
         r = getenv_steal_erase("PASSWORD", &envpw);
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire password from environment: %m");
@@ -536,6 +578,7 @@ static int prepare_luks(
                 }
         }
 
+out:
         *ret_cd = TAKE_PTR(cd);
         *ret_volume_key = TAKE_PTR(vk);
         *ret_volume_key_size = vks;
index 8f5ad67f48ee4b14ba4546ae84d112d6c91c5d18..07903f1044e7cb0524368c570ba639fa7f1f2c34 100644 (file)
@@ -227,9 +227,11 @@ static int generate_device_umount(const char *name,
         return 0;
 }
 
-static int print_dependencies(FILE *f, const char* device_path) {
+static int print_dependencies(FILE *f, const char* device_path, const char* timeout_value, bool canfail) {
         int r;
 
+        assert(!canfail || timeout_value);
+
         if (STR_IN_SET(device_path, "-", "none"))
                 /* None, nothing to do */
                 return 0;
@@ -259,9 +261,16 @@ static int print_dependencies(FILE *f, const char* device_path) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to generate unit name: %m");
 
-                fprintf(f,
-                        "After=%1$s\n"
-                        "Requires=%1$s\n", unit);
+                fprintf(f, "After=%1$s\n", unit);
+                if (canfail) {
+                        fprintf(f, "Wants=%1$s\n", unit);
+                        r = write_drop_in_format(arg_dest, unit, 90, "device-timeout",
+                                "# Automatically generated by systemd-cryptsetup-generator \n\n"
+                                "[Unit]\nJobRunningTimeoutSec=%s", timeout_value);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to write device drop-in: %m");
+                } else
+                        fprintf(f, "Requires=%1$s\n", unit);
         } else {
                 /* Regular file, add mount dependency */
                 _cleanup_free_ char *escaped_path = specifier_escape(device_path);
@@ -463,14 +472,18 @@ static int create_disk(
                         netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
 
         if (key_file && !keydev) {
-                r = print_dependencies(f, key_file);
+                r = print_dependencies(f, key_file,
+                        keyfile_timeout_value,
+                        /* canfail= */ keyfile_can_timeout > 0);
                 if (r < 0)
                         return r;
         }
 
         /* Check if a header option was specified */
         if (detached_header > 0 && !headerdev) {
-                r = print_dependencies(f, header_path);
+                r = print_dependencies(f, header_path,
+                        NULL,
+                        /* canfail= */ false); /* header is always necessary */
                 if (r < 0)
                         return r;
         }
index 0db0f562e518093425eebe7c25bbaa589967b88c..30278040650066f8b8e01b2e7d3f4bc01c00f6b5 100644 (file)
@@ -43,18 +43,11 @@ _public_ int cryptsetup_token_open_pin(
         assert_se(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);
-        }
+        r = crypt_normalize_pin(pin, pin_size, &pin_string);
+        if (r < 0)
+                return crypt_log_debug_errno(cd, r, "Can not normalize PIN: %m");
 
-        return acquire_luks2_key(cd, json, (const char *)usrptr, pin_string ?: pin, password, password_len);
+        return acquire_luks2_key(cd, json, (const char *)usrptr, pin_string, password, password_len);
 }
 
 /*
index 23df9749994783cb9e576c48a7106580a3ab22d1..bbc8a39c98b7d37e0caebbaf48ba9ab0744d0a6c 100644 (file)
@@ -35,22 +35,11 @@ static int log_debug_open_error(struct crypt_device *cd, int r) {
         return crypt_log_debug_errno(cd, r, TOKEN_NAME " open failed: %m.");
 }
 
-/*
- * 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-tpm2'):
- *
- * - 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(
+_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 */) {
@@ -66,8 +55,9 @@ _public_ int cryptsetup_token_open(
         _cleanup_free_ void *blob = NULL, *policy_hash = NULL;
         _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL;
         _cleanup_(erase_and_freep) void *decrypted_key = NULL;
-        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
 
+        assert(!pin || pin_size);
         assert(password);
         assert(password_len);
         assert(token >= 0);
@@ -77,6 +67,10 @@ _public_ int cryptsetup_token_open(
         assert(token == r);
         assert(json);
 
+        r = crypt_normalize_pin(pin, pin_size, &pin_string);
+        if (r < 0)
+                return crypt_log_debug_errno(cd, r, "Can not normalize PIN: %m");
+
         if (usrptr)
                 params = *(systemd_tpm2_plugin_params *)usrptr;
 
@@ -105,6 +99,7 @@ _public_ int cryptsetup_token_open(
                         policy_hash,
                         policy_hash_size,
                         flags,
+                        pin_string,
                         &decrypted_key,
                         &decrypted_key_size);
         if (r < 0)
@@ -122,6 +117,29 @@ _public_ int cryptsetup_token_open(
         return 0;
 }
 
+/*
+ * 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-tpm2'):
+ *
+ * - 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
index f4086ae367b415ec9f58d7c63ee95813b2d9fc68..e305d8ba7958f8676adfafa3a085b8a426dca9b2 100644 (file)
@@ -56,3 +56,29 @@ int crypt_dump_hex_string(const char *hex_str, char **ret_dump_str) {
 
         return 0;
 }
+
+int crypt_normalize_pin(const void *pin, size_t pin_size, char **ret_pin_string) {
+
+        _cleanup_free_ char *pin_string = NULL;
+
+        assert(pin || !pin_size);
+        assert(ret_pin_string);
+
+        if (!pin) {
+                *ret_pin_string = NULL;
+                return 0;
+        }
+
+        /* Refuse embedded NULL bytes, but allow trailing NULL */
+        if (memchr(pin, 0, pin_size - 1))
+                return -EINVAL;
+
+        /* Enforce trailing NULL byte if missing */
+        pin_string = memdup_suffix0(pin, pin_size);
+        if (!pin_string)
+                return -ENOMEM;
+
+        *ret_pin_string = TAKE_PTR(pin_string);
+
+        return 0;
+}
index 57ffca136f4740aa3528b4332b2d137c9c557b35..146beffb3054ce64bc7701af11ab914531591126 100644 (file)
@@ -36,3 +36,5 @@ int crypt_dump_buffer_to_hex_string(
                 char **ret_dump_str);
 
 int crypt_dump_hex_string(const char *hex_str, char **ret_dump_str);
+
+int crypt_normalize_pin(const void *pin, size_t pin_size, char **ret_pin_string);
index 0d6e4bc0f8c676a3ddf7a9a1f859701be27eb326..f0286ec1bf4f76864962a93e6fed17859c9b4c5b 100644 (file)
@@ -22,11 +22,11 @@ int acquire_luks2_key(
                 const void *policy_hash,
                 size_t policy_hash_size,
                 TPM2Flags flags,
+                const char *pin,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size) {
 
         _cleanup_free_ char *auto_device = NULL;
-        _cleanup_(erase_and_freep) char *pin_str = NULL;
         int r;
 
         assert(ret_decrypted_key);
@@ -42,22 +42,15 @@ int acquire_luks2_key(
                 device = auto_device;
         }
 
-        r = getenv_steal_erase("PIN", &pin_str);
-        if (r < 0)
-                return log_error_errno(r, "Failed to acquire PIN from environment: %m");
-        if (!r) {
-                /* PIN entry is not supported by plugin, let it fallback, possibly to sd-cryptsetup's
-                 * internal handling. */
-                if (flags & TPM2_FLAGS_USE_PIN)
-                        return -EOPNOTSUPP;
-        }
+        if ((flags & TPM2_FLAGS_USE_PIN) && !pin)
+                return -ENOANO;
 
         return tpm2_unseal(
                         device,
                         pcr_mask, pcr_bank,
                         primary_alg,
                         key_data, key_data_size,
-                        policy_hash, policy_hash_size, pin_str,
+                        policy_hash, policy_hash_size, pin,
                         ret_decrypted_key, ret_decrypted_key_size);
 }
 
index 34c93216eee18d14bf58a6244d4b8f9419fab406..5e3341802520354f227fb27484d1cba15655de36 100644 (file)
@@ -16,6 +16,7 @@ int acquire_luks2_key(
                 const void *policy_hash,
                 size_t policy_hash_size,
                 TPM2Flags flags,
+                const char *pin,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size);
 
index bd666230be32ca30d22971ad9df1c59657773d10..c8e71c43de5c9cca0c851e45e2021d593341f8d5 100644 (file)
@@ -851,20 +851,24 @@ static int acquire_pins_from_env_variable(char ***ret_pins) {
 }
 #endif
 
-static int attach_luks2_by_fido2_via_plugin(
+static int crypt_activate_by_token_pin_ask_password(
                 struct crypt_device *cd,
                 const char *name,
+                const char *type,
                 usec_t until,
                 bool headless,
                 void *usrptr,
-                uint32_t activation_flags) {
+                uint32_t activation_flags,
+                const char *message,
+                const char *key_name,
+                const char *credential_name) {
 
 #if HAVE_LIBCRYPTSETUP_PLUGINS
         AskPasswordFlags flags = ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
         _cleanup_strv_free_erase_ char **pins = NULL;
         int r;
 
-        r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags);
+        r = crypt_activate_by_token_pin(cd, name, type, 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 */
@@ -875,7 +879,7 @@ static int attach_luks2_by_fido2_via_plugin(
                 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);
+                r = crypt_activate_by_token_pin(cd, name, type, 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 */
@@ -887,12 +891,12 @@ static int attach_luks2_by_fido2_via_plugin(
 
         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);
+                r = ask_password_auto(message, "drive-harddisk", NULL, key_name, credential_name, 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);
+                        r = crypt_activate_by_token_pin(cd, name, type, 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 */
@@ -907,6 +911,27 @@ static int attach_luks2_by_fido2_via_plugin(
 #endif
 }
 
+static int attach_luks2_by_fido2_via_plugin(
+                struct crypt_device *cd,
+                const char *name,
+                usec_t until,
+                bool headless,
+                void *usrptr,
+                uint32_t activation_flags) {
+
+        return crypt_activate_by_token_pin_ask_password(
+                        cd,
+                        name,
+                        "systemd-fido2",
+                        until,
+                        headless,
+                        usrptr,
+                        activation_flags,
+                        "Please enter security token PIN:",
+                        "fido2-pin",
+                        "cryptsetup.fido2-pin");
+}
+
 static int attach_luks_or_plain_or_bitlk_by_fido2(
                 struct crypt_device *cd,
                 const char *name,
@@ -1250,11 +1275,11 @@ static int make_tpm2_device_monitor(
 static int attach_luks2_by_tpm2_via_plugin(
                 struct crypt_device *cd,
                 const char *name,
+                usec_t until,
+                bool headless,
                 uint32_t flags) {
 
 #if HAVE_LIBCRYPTSETUP_PLUGINS
-        int r;
-
         systemd_tpm2_plugin_params params = {
                 .search_pcr_mask = arg_tpm2_pcr_mask,
                 .device = arg_tpm2_device
@@ -1264,11 +1289,17 @@ static int attach_luks2_by_tpm2_via_plugin(
                 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                        "Libcryptsetup has external plugins support disabled.");
 
-        r = crypt_activate_by_token_pin(cd, name, "systemd-tpm2", CRYPT_ANY_TOKEN, NULL, 0, &params, flags);
-        if (r > 0) /* returns unlocked keyslot id on success */
-                r = 0;
-
-        return r;
+        return crypt_activate_by_token_pin_ask_password(
+                        cd,
+                        name,
+                        "systemd-tpm2",
+                        until,
+                        headless,
+                        &params,
+                        flags,
+                        "Please enter TPM2 PIN:",
+                        "tpm2-pin",
+                        "cryptsetup.tpm2-pin");
 #else
         return -EOPNOTSUPP;
 #endif
@@ -1329,7 +1360,9 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
                                 return -EAGAIN; /* Mangle error code: let's make any form of TPM2 failure non-fatal. */
                         }
                 } else {
-                        r = attach_luks2_by_tpm2_via_plugin(cd, name, flags);
+                        r = attach_luks2_by_tpm2_via_plugin(cd, name, until, arg_headless, flags);
+                        if (r >= 0)
+                                return 0;
                         /* EAGAIN     means: no tpm2 chip found
                          * EOPNOTSUPP means: no libcryptsetup plugins support */
                         if (r == -ENXIO)
@@ -1853,7 +1886,17 @@ static int run(int argc, char *argv[]) {
 
                         /* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */
                         if (!key_file && !key_data) {
-                                r = crypt_activate_by_token(cd, volume, CRYPT_ANY_TOKEN, NULL, flags);
+                                r = crypt_activate_by_token_pin_ask_password(
+                                                cd,
+                                                volume,
+                                                NULL,
+                                                until,
+                                                arg_headless,
+                                                NULL,
+                                                flags,
+                                                "Please enter LUKS2 token PIN:",
+                                                "luks2-pin",
+                                                "cryptsetup.luks2-pin");
                                 if (r >= 0) {
                                         log_debug("Volume %s activated with LUKS token id %i.", volume, r);
                                         return 0;
index 4e39f959cfe8eb0cf9ca561b4c785fee1f4e9660..d9f3fab835be32e1b4dfa696390ed1dad8b368c0 100644 (file)
@@ -8,7 +8,10 @@
 #include <sys/ioctl.h>
 #include <sys/mount.h>
 
+#include "sd-device.h"
+
 #include "architecture.h"
+#include "blockdev-util.h"
 #include "chase-symlinks.h"
 #include "copy.h"
 #include "dissect-image.h"
@@ -24,6 +27,7 @@
 #include "main-func.h"
 #include "mkdir.h"
 #include "mount-util.h"
+#include "mountpoint-util.h"
 #include "namespace-util.h"
 #include "parse-argument.h"
 #include "parse-util.h"
@@ -40,6 +44,7 @@
 static enum {
         ACTION_DISSECT,
         ACTION_MOUNT,
+        ACTION_UMOUNT,
         ACTION_COPY_FROM,
         ACTION_COPY_TO,
 } arg_action = ACTION_DISSECT;
@@ -58,6 +63,7 @@ static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
 static PagerFlags arg_pager_flags = 0;
 static bool arg_legend = true;
+static bool arg_rmdir = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
 
@@ -81,6 +87,7 @@ static int help(void) {
                "     --fsck=BOOL          Run fsck before mounting\n"
                "     --growfs=BOOL        Grow file system to partition size, if marked\n"
                "     --mkdir              Make mount directory before mounting, if missing\n"
+               "     --rmdir              Remove mount directory after unmounting\n"
                "     --discard=MODE       Choose 'discard' mode (disabled, loop, all, crypto)\n"
                "     --root-hash=HASH     Specify root hash for verity\n"
                "     --root-hash-sig=SIG  Specify pkcs7 signature of root hash for verity\n"
@@ -96,6 +103,8 @@ static int help(void) {
                "     --version            Show package version\n"
                "  -m --mount              Mount the image to the specified directory\n"
                "  -M                      Shortcut for --mount --mkdir\n"
+               "  -u --umount             Unmount the image from the specified directory\n"
+               "  -U                      Shortcut for --umount --rmdir\n"
                "  -x --copy-from          Copy files from image to host\n"
                "  -a --copy-to            Copy files from host to image\n"
                "\nSee the %2$s for details.\n",
@@ -122,6 +131,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_ROOT_HASH_SIG,
                 ARG_VERITY_DATA,
                 ARG_MKDIR,
+                ARG_RMDIR,
                 ARG_JSON,
         };
 
@@ -131,6 +141,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "no-pager",      no_argument,       NULL, ARG_NO_PAGER      },
                 { "no-legend",     no_argument,       NULL, ARG_NO_LEGEND     },
                 { "mount",         no_argument,       NULL, 'm'               },
+                { "umount",        no_argument,       NULL, 'u'               },
                 { "read-only",     no_argument,       NULL, 'r'               },
                 { "discard",       required_argument, NULL, ARG_DISCARD       },
                 { "fsck",          required_argument, NULL, ARG_FSCK          },
@@ -139,6 +150,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
                 { "verity-data",   required_argument, NULL, ARG_VERITY_DATA   },
                 { "mkdir",         no_argument,       NULL, ARG_MKDIR         },
+                { "rmdir",         no_argument,       NULL, ARG_RMDIR         },
                 { "copy-from",     no_argument,       NULL, 'x'               },
                 { "copy-to",       no_argument,       NULL, 'a'               },
                 { "json",          required_argument, NULL, ARG_JSON          },
@@ -150,7 +162,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hmrMxa", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "hmurMUxa", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -182,6 +194,20 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_flags |= DISSECT_IMAGE_MKDIR;
                         break;
 
+                case 'u':
+                        arg_action = ACTION_UMOUNT;
+                        break;
+
+                case ARG_RMDIR:
+                        arg_rmdir = true;
+                        break;
+
+                case 'U':
+                        /* Shortcut combination of the above two */
+                        arg_action = ACTION_UMOUNT;
+                        arg_rmdir = true;
+                        break;
+
                 case 'x':
                         arg_action = ACTION_COPY_FROM;
                         arg_flags |= DISSECT_IMAGE_READ_ONLY;
@@ -316,6 +342,14 @@ static int parse_argv(int argc, char *argv[]) {
                 arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
                 break;
 
+        case ACTION_UMOUNT:
+                if (optind + 1 != argc)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Expected a mount point path as only argument.");
+
+                arg_path = argv[optind];
+                break;
+
         case ACTION_COPY_FROM:
                 if (argc < optind + 2 || argc > optind + 3)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -823,6 +857,82 @@ static int action_copy(DissectedImage *m, LoopDevice *d) {
         return 0;
 }
 
+static int action_umount(const char *path) {
+        _cleanup_close_ int fd = -1;
+        _cleanup_free_ char *canonical = NULL;
+        dev_t devno;
+        const char *devname;
+        _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+        int r, k;
+
+        fd = chase_symlinks_and_open(path, NULL, 0, O_DIRECTORY, &canonical);
+        if (fd == -ENOTDIR)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "'%s' is not a directory", path);
+        if (fd < 0)
+                return log_error_errno(fd, "Failed to resolve path '%s': %m", path);
+
+        r = fd_is_mount_point(fd, NULL, 0);
+        if (r == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'%s' is not a mount point", canonical);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine whether '%s' is a mount point: %m", canonical);
+
+        r = fd_get_whole_disk(fd, /*backing=*/ true, &devno);
+        if (r < 0)
+                return log_error_errno(r, "Failed to find backing block device for '%s': %m", canonical);
+
+        r = sd_device_new_from_devnum(&device, 'b', devno);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create sd-device object for block device %u:%u: %m",
+                                       major(devno), minor(devno));
+
+        r = sd_device_get_devname(device, &devname);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get devname of block device %u:%u: %m",
+                                       major(devno), minor(devno));
+
+        r = loop_device_open(devname, 0, &d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to open loop device '%s': %m", devname);
+
+        r = loop_device_flock(d, LOCK_EX);
+        if (r < 0)
+                return log_error_errno(r, "Failed to lock loop device '%s': %m", devname);
+
+        /* We've locked the loop device, now we're ready to unmount. To allow the unmount to succeed, we have
+         * to close the O_PATH fd we opened earlier. */
+        fd = safe_close(fd);
+
+        r = umount_recursive(canonical, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to unmount '%s': %m", canonical);
+
+        /* We managed to lock and unmount successfully? That means we can try to remove the loop device.*/
+        loop_device_unrelinquish(d);
+
+        if (arg_rmdir) {
+                k = RET_NERRNO(rmdir(canonical));
+                if (k < 0)
+                        log_error_errno(k, "Failed to remove mount directory '%s': %m", canonical);
+        } else
+                k = 0;
+
+        /* Before loop_device_unrefp() kicks in, let's explicitly remove all the partition subdevices of the
+         * loop device. We do this to ensure that all traces of the loop device are gone by the time this
+         * command exits. */
+        r = block_device_remove_all_partitions(d->fd);
+        if (r == -EBUSY) {
+                log_error_errno(r, "One or more partitions of '%s' are busy, ignoring", devname);
+                r = 0;
+        }
+        if (r < 0)
+                log_error_errno(r, "Failed to remove one or more partitions of '%s': %m", devname);
+
+
+        return k < 0 ? k : r;
+}
+
 static int run(int argc, char *argv[]) {
         _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
         _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
@@ -835,6 +945,9 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
+        if (arg_action == ACTION_UMOUNT)
+                return action_umount(arg_path);
+
         r = verity_settings_load(
                         &arg_verity_settings,
                         arg_image, NULL, NULL);
index 0716914b26f38bc9ddca8fa43748330f0a6fc1e5..9169129a6880776464d3e9dbcce3782c1102d4f5 100644 (file)
@@ -479,8 +479,9 @@ static int process_timezone(void) {
                                 return log_error_errno(r, "Failed to read host timezone: %m");
 
                         (void) mkdir_parents(etc_localtime, 0755);
-                        if (symlink(p, etc_localtime) < 0)
-                                return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
+                        r = symlink_atomic(p, etc_localtime);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to create %s symlink: %m", etc_localtime);
 
                         log_info("%s copied.", etc_localtime);
                         return 0;
@@ -497,8 +498,9 @@ static int process_timezone(void) {
         e = strjoina("../usr/share/zoneinfo/", arg_timezone);
 
         (void) mkdir_parents(etc_localtime, 0755);
-        if (symlink(e, etc_localtime) < 0)
-                return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
+        r = symlink_atomic(e, etc_localtime);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create %s symlink: %m", etc_localtime);
 
         log_info("%s written", etc_localtime);
         return 0;
@@ -595,21 +597,8 @@ static int prompt_root_password(void) {
         if (arg_root_password)
                 return 0;
 
-        r = read_credential("passwd.hashed-password.root", (void**) &arg_root_password, NULL);
-        if (r == -ENOENT) {
-                r = read_credential("passwd.plaintext-password.root", (void**) &arg_root_password, NULL);
-                if (r < 0)
-                        log_debug_errno(r, "Couldn't read credential 'passwd.{hashed|plaintext}-password.root', ignoring: %m");
-                else {
-                        arg_root_password_is_hashed = false;
-                        return 0;
-                }
-        } else if (r < 0)
-                log_debug_errno(r, "Couldn't read credential 'passwd.hashed-password.root', ignoring: %m");
-        else {
-                arg_root_password_is_hashed = true;
+        if (get_credential_user_password("root", &arg_root_password, &arg_root_password_is_hashed) >= 0)
                 return 0;
-        }
 
         if (!arg_prompt_root_password)
                 return 0;
index d1b70eafdce3a814c449de44af2fc6a3e30c1e92..fe34e6c714a10dd7ba8addcf8e71befaba9ae2a6 100644 (file)
@@ -4,6 +4,7 @@
 #include <errno.h>
 #include "string-util-fundamental.h"
 
+/* Features of the loader, i.e. systemd-boot */
 #define EFI_LOADER_FEATURE_CONFIG_TIMEOUT          (UINT64_C(1) << 0)
 #define EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (UINT64_C(1) << 1)
 #define EFI_LOADER_FEATURE_ENTRY_DEFAULT           (UINT64_C(1) << 2)
 #define EFI_LOADER_FEATURE_XBOOTLDR                (UINT64_C(1) << 5)
 #define EFI_LOADER_FEATURE_RANDOM_SEED             (UINT64_C(1) << 6)
 #define EFI_LOADER_FEATURE_LOAD_DRIVER             (UINT64_C(1) << 7)
+#define EFI_LOADER_FEATURE_SORT_KEY                (UINT64_C(1) << 8)
+#define EFI_LOADER_FEATURE_SAVED_ENTRY             (UINT64_C(1) << 9)
+#define EFI_LOADER_FEATURE_DEVICETREE              (UINT64_C(1) << 10)
+
+/* Features of the stub, i.e. systemd-stub */
+#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION     (UINT64_C(1) << 0)
+#define EFI_STUB_FEATURE_PICK_UP_CREDENTIALS       (UINT64_C(1) << 1)
+#define EFI_STUB_FEATURE_PICK_UP_SYSEXTS           (UINT64_C(1) << 2)
+#define EFI_STUB_FEATURE_THREE_PCRS                (UINT64_C(1) << 3)
 
 typedef enum SecureBootMode {
         SECURE_BOOT_UNSUPPORTED,
index a738b1f50e8151302493270c1f2f1f8faf52a33a..8b483f0b50d21176049bd405f727ed0fbf94cee9 100644 (file)
                 _expr_;                         \
         })
 
+#define ASSERT_NONNEG(expr)                              \
+        ({                                               \
+                typeof(expr) _expr_ = (expr), _zero = 0; \
+                assert(_expr_ >= _zero);                 \
+                _expr_;                                  \
+        })
+
+#define ASSERT_SE_NONNEG(expr)                           \
+        ({                                               \
+                typeof(expr) _expr_ = (expr), _zero = 0; \
+                assert_se(_expr_ >= _zero);              \
+                _expr_;                                  \
+        })
+
 #define assert_cc(expr) static_assert(expr, #expr)
 
 
  * on this macro will run concurrently to all other code conditionalized
  * the same way, there's no ordering or completion enforced. */
 #define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
-#define __ONCE(o)                                                \
-        ({                                                       \
-                static bool (o) = false;                         \
-                __sync_bool_compare_and_swap(&(o), false, true); \
+#define __ONCE(o)                                                  \
+        ({                                                         \
+                static bool (o) = false;                           \
+                __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
         })
 
 #undef MAX
                 MIN(_c, z);                             \
         })
 
+/* Returns true if the passed integer is a positive power of two */
+#define CONST_ISPOWEROF2(x)                     \
+        ((x) > 0 && ((x) & ((x) - 1)) == 0)
+
+#define ISPOWEROF2(x)                                                  \
+        __builtin_choose_expr(                                         \
+                __builtin_constant_p(x),                               \
+                CONST_ISPOWEROF2(x),                                   \
+                ({                                                     \
+                        const typeof(x) _x = (x);                      \
+                        CONST_ISPOWEROF2(_x);                          \
+                }))
+
 #define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
 #define __LESS_BY(aq, a, bq, b)                         \
         ({                                              \
         })
 
 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 == ULLONG_MAX
-        assert(__builtin_popcountll(ali) == 1);
-#else
-        #error "Unexpected size_t"
-#endif
+        assert(ISPOWEROF2(ali));
 
         if (l > SIZE_MAX - (ali - 1))
                 return SIZE_MAX; /* indicate overflow */
@@ -330,7 +348,7 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
         __builtin_choose_expr(                                         \
                 __builtin_constant_p(l) &&                             \
                 __builtin_constant_p(ali) &&                           \
-                __builtin_popcountll(ali) == 1 && /* is power of 2? */ \
+                CONST_ISPOWEROF2(ali) &&                               \
                 (l <= SIZE_MAX - (ali - 1)),      /* overflow? */      \
                 ((l) + (ali) - 1) & ~((ali) - 1),                      \
                 VOID_0)
index 2ec4a28433d477d65854f2242c9cfc59f24c08f8..3810d6b456de6146ac112f168f7d5927b2265dce 100644 (file)
@@ -8,6 +8,7 @@ fundamental_headers = files(
         'macro-fundamental.h',
         'sha256.h',
         'string-util-fundamental.h',
+        'tpm-pcr.h',
 )
 
 # for sd-boot
@@ -16,6 +17,7 @@ fundamental_source_paths = files(
         'efivars-fundamental.c',
         'sha256.c',
         'string-util-fundamental.c',
+        'tpm-pcr.c',
 )
 
 # for libbasic
diff --git a/src/fundamental/tpm-pcr.c b/src/fundamental/tpm-pcr.c
new file mode 100644 (file)
index 0000000..97b3c7b
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stddef.h>
+
+#include "tpm-pcr.h"
+
+const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = {
+        [UNIFIED_SECTION_LINUX]   = ".linux",
+        [UNIFIED_SECTION_OSREL]   = ".osrel",
+        [UNIFIED_SECTION_CMDLINE] = ".cmdline",
+        [UNIFIED_SECTION_INITRD]  = ".initrd",
+        [UNIFIED_SECTION_SPLASH]  = ".splash",
+        [UNIFIED_SECTION_DTB]     = ".dtb",
+        NULL,
+};
diff --git a/src/fundamental/tpm-pcr.h b/src/fundamental/tpm-pcr.h
new file mode 100644 (file)
index 0000000..fb0774f
--- /dev/null
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* The various TPM PCRs we measure into from sd-stub and sd-boot. */
+
+/* This TPM PCR is where we extend the sd-stub "payloads" into, before using them. i.e. the kernel ELF image,
+ * embedded initrd, and so on. In contrast to PCR 4 (which also contains this data, given the whole
+ * surrounding PE image is measured into it) this should be reasonably pre-calculatable, because it *only*
+ * consists of static data from the kernel PE image. */
+#define TPM_PCR_INDEX_KERNEL_IMAGE 11U
+
+/* This TPM PCR is where sd-stub extends the kernel command line and any passed credentials into. */
+#define TPM_PCR_INDEX_KERNEL_PARAMETERS 12U
+
+/* sd-stub used to write the kernel command line/credentials into PCR 8, in systemd <= 250. Let's provide for
+ * some compatibility. (Remove in 2023!) */
+#if EFI_TPM_PCR_COMPAT
+#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT 8U
+#else
+#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT UINT32_MAX
+#endif
+
+/* This TPM PCR is where we extend the initrd sysext images into which we pass to the booted kernel */
+#define TPM_PCR_INDEX_INITRD_SYSEXTS 13U
+
+/* List of PE sections that have special meaning for us in unified kernels. This is the canonical order in
+ * which we measure the sections into TPM PCR 11 (see above). PLEASE DO NOT REORDER! */
+typedef enum UnifiedSection {
+        UNIFIED_SECTION_LINUX,
+        UNIFIED_SECTION_OSREL,
+        UNIFIED_SECTION_CMDLINE,
+        UNIFIED_SECTION_INITRD,
+        UNIFIED_SECTION_SPLASH,
+        UNIFIED_SECTION_DTB,
+        _UNIFIED_SECTION_MAX,
+} UnifiedSection;
+
+extern const char* const unified_sections[_UNIFIED_SECTION_MAX + 1];
index b39ca3262c7695a97d5e0699bf1280829cf7fcaa..4663792e5c34dde026b922c666b5c414e9883cf2 100644 (file)
@@ -771,6 +771,8 @@ static int bus_home_node_enumerator(
                 r = bus_home_path(h, l + k);
                 if (r < 0)
                         return r;
+
+                k++;
         }
 
         *nodes = TAKE_PTR(l);
index 728a92260cfdb9253eca1fdd37a5b76679255bc8..e79def3daec75a3c5215eb9c9f45b3b815fa813d 100644 (file)
@@ -1,5 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <sys/mount.h>
+#if WANT_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+
 #include "dirent-util.h"
 #include "fd-util.h"
 #include "fileio.h"
index 0108ab54075f3482bc691357d53f9e0a1210c5fd..5f3e79a67aad8aca168f774c5229a48522b0592b 100644 (file)
@@ -4,7 +4,6 @@
 #include <poll.h>
 #include <sys/file.h>
 #include <sys/ioctl.h>
-#include <sys/mount.h>
 #include <sys/xattr.h>
 
 #if HAVE_VALGRIND_MEMCHECK_H
index e71e8cd8538179497e05cb5fd51780aea854e3a7..a9c793c42943914882d60caab43b5cdd97b8e9f3 100644 (file)
@@ -2,7 +2,9 @@
 
 #include <sched.h>
 #include <sys/mount.h>
+#if WANT_LINUX_FS_H
 #include <linux/fs.h>
+#endif
 
 #include "alloc-util.h"
 #include "fd-util.h"
index 882a3f500b3bfcf0151e7a8f510f6004a600dce1..b27c31d56bab562e54050440e7cf2c7b8213fae4 100644 (file)
@@ -1,13 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include <linux/fs.h>
 #include <sys/vfs.h>
 
 #include "sd-id128.h"
 
+#include "cryptsetup-util.h"
 #include "homework-password-cache.h"
 #include "loop-util.h"
+#include "missing_fs.h" /* for FS_KEY_DESCRIPTOR_SIZE, do not include linux/fs.h */
 #include "missing_keyctl.h"
 #include "missing_syscall.h"
 #include "user-record.h"
index 2347db3bacf33faf82168bf1c3b3bf956f4a0f40..475613a9076f2c0c99806be2c0c41643ae177c6e 100644 (file)
@@ -9,15 +9,15 @@
 typedef enum PullFlags {
         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 */
+        PULL_SETTINGS           = 1 << 2, /* download .nspawn settings file */
+        PULL_ROOTHASH           = 1 << 3, /* only for raw: download .roothash file for verity */
+        PULL_ROOTHASH_SIGNATURE = 1 << 4, /* only for raw: download .roothash.p7s file for verity */
+        PULL_VERITY             = 1 << 5, /* only for raw: download .verity file for verity */
+        PULL_BTRFS_SUBVOL       = 1 << 6, /* tar: preferably create images as btrfs subvols */
+        PULL_BTRFS_QUOTA        = 1 << 7, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
+        PULL_CONVERT_QCOW2      = 1 << 8, /* raw: if we detect a qcow2 image, unpack it */
+        PULL_DIRECT             = 1 << 9, /* download without rename games */
+        PULL_SYNC               = 1 << 10, /* fsync() right before we are done */
 
         /* The supported flags for the tar and the raw pulling */
         PULL_FLAGS_MASK_TAR     = PULL_FORCE|PULL_READ_ONLY|PULL_SETTINGS|PULL_BTRFS_SUBVOL|PULL_BTRFS_QUOTA|PULL_DIRECT|PULL_SYNC,
index 924c15b6eb1caeed3feab4fee7a3f2f4b636fdb3..a602886cb3e9fe6a610efdb5f0883cbdc25c1871 100644 (file)
@@ -152,12 +152,6 @@ static int run(int argc, char *argv[]) {
                         return 0;
                 }
 
-                if (!isempty(arg_existing_data_device)) {
-                        r = crypt_init_data_device(&cd, device, arg_existing_data_device);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to add separate data device: %m");
-                }
-
                 r = crypt_load(cd,
                         CRYPT_INTEGRITY,
                         &(struct crypt_params_integrity) {
@@ -168,6 +162,12 @@ static int run(int argc, char *argv[]) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to load integrity superblock: %m");
 
+                if (!isempty(arg_existing_data_device)) {
+                        r = crypt_set_data_device(cd, arg_existing_data_device);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to add separate data device: %m");
+                }
+
                 r = crypt_activate_by_volume_key(cd, volume, key_buf, key_buf_size, arg_activate_flags);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set up integrity device: %m");
index 127c3d491b86825411492bff763c8f563e61a006..5e25ed9bfdf3d87be149a7eab62c212dbda01795 100644 (file)
 #include <sys/stat.h>
 #include <unistd.h>
 
-#if HAVE_PCRE2
-#  define PCRE2_CODE_UNIT_WIDTH 8
-#  include <pcre2.h>
-#endif
-
 #include "sd-bus.h"
 #include "sd-device.h"
 #include "sd-journal.h"
@@ -133,11 +128,9 @@ static uint64_t arg_vacuum_size = 0;
 static uint64_t arg_vacuum_n_files = 0;
 static usec_t arg_vacuum_time = 0;
 static char **arg_output_fields = NULL;
-#if HAVE_PCRE2
 static const char *arg_pattern = NULL;
 static pcre2_code *arg_compiled_pattern = NULL;
-static int arg_case_sensitive = -1; /* -1 means be smart */
-#endif
+static PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO;
 
 STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_facilities, set_freep);
@@ -148,9 +141,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_user_units, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_output_fields, strv_freep);
-#if HAVE_PCRE2
-STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, sym_pcre2_code_freep);
-#endif
+STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, pattern_freep);
 
 static enum {
         ACTION_SHOW,
@@ -180,29 +171,6 @@ typedef struct BootId {
         LIST_FIELDS(struct BootId, boot_list);
 } BootId;
 
-#if HAVE_PCRE2
-static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
-        int errorcode, r;
-        PCRE2_SIZE erroroffset;
-        pcre2_code *p;
-
-        p = sym_pcre2_compile((PCRE2_SPTR8) pattern,
-                              PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
-        if (!p) {
-                unsigned char buf[LINE_MAX];
-
-                r = sym_pcre2_get_error_message(errorcode, buf, sizeof buf);
-
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Bad pattern \"%s\": %s", pattern,
-                                       r < 0 ? "unknown error" : (char *)buf);
-        }
-
-        *out = p;
-        return 0;
-}
-#endif
-
 static int add_matches_for_device(sd_journal *j, const char *devpath) {
         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
         sd_device *d = NULL;
@@ -348,19 +316,23 @@ static int help(void) {
 
         printf("%1$s [OPTIONS...] [MATCHES...]\n\n"
                "%5$sQuery the journal.%6$s\n\n"
-               "%3$sOptions:%4$s\n"
+               "%3$sSource Options:%4$s\n"
                "     --system                Show the system journal\n"
                "     --user                  Show the user journal for the current user\n"
                "  -M --machine=CONTAINER     Operate on local container\n"
+               "  -m --merge                 Show entries from all available journals\n"
+               "  -D --directory=PATH        Show journal files from directory\n"
+               "     --file=PATH             Show journal file\n"
+               "     --root=ROOT             Operate on files below a root directory\n"
+               "     --image=IMAGE           Operate on files in filesystem image\n"
+               "     --namespace=NAMESPACE   Show journal data from specified journal namespace\n"
+               "\n%3$sFiltering Options:%4$s\n"
                "  -S --since=DATE            Show entries not older than the specified date\n"
                "  -U --until=DATE            Show entries not newer than the specified date\n"
                "  -c --cursor=CURSOR         Show entries starting at the specified cursor\n"
                "     --after-cursor=CURSOR   Show entries after the specified cursor\n"
-               "     --show-cursor           Print the cursor after all the entries\n"
                "     --cursor-file=FILE      Show entries after cursor in FILE and update FILE\n"
                "  -b --boot[=ID]             Show current boot or the specified boot\n"
-               "     --list-boots            Show terse information about recorded boots\n"
-               "  -k --dmesg                 Show kernel message log from the current boot\n"
                "  -u --unit=UNIT             Show logs from the specified unit\n"
                "     --user-unit=UNIT        Show logs from the specified user unit\n"
                "  -t --identifier=STRING     Show entries with the specified syslog identifier\n"
@@ -368,30 +340,29 @@ static int help(void) {
                "     --facility=FACILITY...  Show entries with the specified facilities\n"
                "  -g --grep=PATTERN          Show entries with MESSAGE matching PATTERN\n"
                "     --case-sensitive[=BOOL] Force case sensitive or insensitive matching\n"
-               "  -e --pager-end             Immediately jump to the end in the pager\n"
-               "  -f --follow                Follow the journal\n"
-               "  -n --lines[=INTEGER]       Number of journal entries to show\n"
-               "     --no-tail               Show all lines, even in follow mode\n"
-               "  -r --reverse               Show the newest entries first\n"
+               "  -k --dmesg                 Show kernel message log from the current boot\n"
+               "\n%3$sOutput Control Options:%4$s\n"
                "  -o --output=STRING         Change journal output mode (short, short-precise,\n"
                "                               short-iso, short-iso-precise, short-full,\n"
                "                               short-monotonic, short-unix, verbose, export,\n"
                "                               json, json-pretty, json-sse, json-seq, cat,\n"
                "                               with-unit)\n"
                "     --output-fields=LIST    Select fields to print in verbose/export/json modes\n"
+               "  -n --lines[=INTEGER]       Number of journal entries to show\n"
+               "  -r --reverse               Show the newest entries first\n"
+               "     --show-cursor           Print the cursor after all the entries\n"
                "     --utc                   Express time in Coordinated Universal Time (UTC)\n"
                "  -x --catalog               Add message explanations where available\n"
+               "     --no-hostname           Suppress output of hostname field\n"
                "     --no-full               Ellipsize fields\n"
                "  -a --all                   Show all fields, including long and unprintable\n"
+               "  -f --follow                Follow the journal\n"
+               "     --no-tail               Show all lines, even in follow mode\n"
                "  -q --quiet                 Do not show info messages and privilege warning\n"
+               "\n%3$sPager Control Options:%4$s\n"
                "     --no-pager              Do not pipe output into a pager\n"
-               "     --no-hostname           Suppress output of hostname field\n"
-               "  -m --merge                 Show entries from all available journals\n"
-               "  -D --directory=PATH        Show journal files from directory\n"
-               "     --file=PATH             Show journal file\n"
-               "     --root=ROOT             Operate on files below a root directory\n"
-               "     --image=IMAGE           Operate on files in filesystem image\n"
-               "     --namespace=NAMESPACE   Show journal data from specified namespace\n"
+               "  -e --pager-end             Immediately jump to the end in the pager\n"
+               "\n%3$sForward Secure Sealing (FSS) Options:%4$s\n"
                "     --interval=TIME         Time interval for changing the FSS sealing key\n"
                "     --verify-key=KEY        Specify FSS verification key\n"
                "     --force                 Override of the FSS key pair with --setup-keys\n"
@@ -400,6 +371,7 @@ static int help(void) {
                "     --version               Show package version\n"
                "  -N --fields                List all field names currently used\n"
                "  -F --field=FIELD           List all values that a specified field takes\n"
+               "     --list-boots            Show terse information about recorded boots\n"
                "     --disk-usage            Show total disk usage of all journal files\n"
                "     --vacuum-size=BYTES     Reduce disk usage below specified size\n"
                "     --vacuum-files=INT      Leave only the specified number of journal files\n"
@@ -918,7 +890,6 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
-#if HAVE_PCRE2
                 case 'g':
                         arg_pattern = optarg;
                         break;
@@ -928,16 +899,11 @@ static int parse_argv(int argc, char *argv[]) {
                                 r = parse_boolean(optarg);
                                 if (r < 0)
                                         return log_error_errno(r, "Bad --case-sensitive= argument \"%s\": %m", optarg);
-                                arg_case_sensitive = r;
+                                arg_case = r ? PATTERN_COMPILE_CASE_SENSITIVE : PATTERN_COMPILE_CASE_INSENSITIVE;
                         } else
-                                arg_case_sensitive = true;
+                                arg_case = PATTERN_COMPILE_CASE_SENSITIVE;
 
                         break;
-#else
-                case 'g':
-                case ARG_CASE_SENSITIVE:
-                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Compiled without pattern matching support");
-#endif
 
                 case 'S':
                         r = parse_timestamp(optarg, &arg_since);
@@ -1088,7 +1054,7 @@ static int parse_argv(int argc, char *argv[]) {
 
         if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Please specify only one of --since=, --cursor=, and --after-cursor.");
+                                       "Please specify only one of --since=, --cursor=, and --after-cursor=.");
 
         if (arg_follow && arg_reverse)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -1114,44 +1080,11 @@ static int parse_argv(int argc, char *argv[]) {
                 arg_system_units = strv_free(arg_system_units);
         }
 
-#if HAVE_PCRE2
         if (arg_pattern) {
-                unsigned flags;
-
-                r = dlopen_pcre2();
-                if (r < 0)
-                        return r;
-
-                if (arg_case_sensitive >= 0)
-                        flags = !arg_case_sensitive * PCRE2_CASELESS;
-                else {
-                        _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
-                        bool has_case;
-                        _cleanup_(sym_pcre2_code_freep) pcre2_code *cs = NULL;
-
-                        md = sym_pcre2_match_data_create(1, NULL);
-                        if (!md)
-                                return log_oom();
-
-                        r = pattern_compile("[[:upper:]]", 0, &cs);
-                        if (r < 0)
-                                return r;
-
-                        r = sym_pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
-                        has_case = r >= 0;
-
-                        flags = !has_case * PCRE2_CASELESS;
-                }
-
-                log_debug("Doing case %s matching based on %s",
-                          flags & PCRE2_CASELESS ? "insensitive" : "sensitive",
-                          arg_case_sensitive >= 0 ? "request" : "pattern casing");
-
-                r = pattern_compile(arg_pattern, flags, &arg_compiled_pattern);
+                r = pattern_compile_and_log(arg_pattern, arg_case, &arg_compiled_pattern);
                 if (r < 0)
                         return r;
         }
-#endif
 
         return 1;
 }
@@ -2703,16 +2636,9 @@ int main(int argc, char *argv[]) {
                                 }
                         }
 
-#if HAVE_PCRE2
                         if (arg_compiled_pattern) {
-                                _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
                                 const void *message;
                                 size_t len;
-                                PCRE2_SIZE *ovec;
-
-                                md = sym_pcre2_match_data_create(1, NULL);
-                                if (!md)
-                                        return log_oom();
 
                                 r = sd_journal_get_data(j, "MESSAGE", &message, &len);
                                 if (r < 0) {
@@ -2727,33 +2653,15 @@ int main(int argc, char *argv[]) {
 
                                 assert_se(message = startswith(message, "MESSAGE="));
 
-                                r = sym_pcre2_match(arg_compiled_pattern,
-                                                    message,
-                                                    len - strlen("MESSAGE="),
-                                                    0,      /* start at offset 0 in the subject */
-                                                    0,      /* default options */
-                                                    md,
-                                                    NULL);
-                                if (r == PCRE2_ERROR_NOMATCH) {
+                                r = pattern_matches_and_log(arg_compiled_pattern, message,
+                                                            len - strlen("MESSAGE="), highlight);
+                                if (r < 0)
+                                        goto finish;
+                                if (r == 0) {
                                         need_seek = true;
                                         continue;
                                 }
-                                if (r < 0) {
-                                        unsigned char buf[LINE_MAX];
-                                        int r2;
-
-                                        r2 = sym_pcre2_get_error_message(r, buf, sizeof buf);
-                                        log_error("Pattern matching failed: %s",
-                                                  r2 < 0 ? "unknown error" : (char*) buf);
-                                        r = -EINVAL;
-                                        goto finish;
-                                }
-
-                                ovec = sym_pcre2_get_ovector_pointer(md);
-                                highlight[0] = ovec[0];
-                                highlight[1] = ovec[1];
                         }
-#endif
 
                         flags =
                                 arg_all * OUTPUT_SHOW_ALL |
@@ -2828,14 +2736,12 @@ int main(int argc, char *argv[]) {
 finish:
         pager_close();
 
-#if HAVE_PCRE2
         if (arg_compiled_pattern && r == 0 && n_shown == 0)
                 /* --grep was used, no error was thrown, but the pattern didn't
                  * match anything. Let's mimic grep's behavior here and return
                  * a non-zero exit code, so journalctl --grep can be used
                  * in scripts and such */
                 r = -ENOENT;
-#endif
 
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }
index 954e154f574ab9bf6c89be8fff9db74fd4544eb4..e7afbd3eb3aa23a1e8ad05280bf5f58e4ac04401 100644 (file)
@@ -1746,7 +1746,7 @@ static int server_parse_config_file(Server *s) {
                                 dropin_dirname,
                                 "Journal\0",
                                 config_item_perf_lookup, journald_gperf_lookup,
-                                CONFIG_PARSE_WARN, s, NULL);
+                                CONFIG_PARSE_WARN, s, NULL, NULL);
                 if (r < 0)
                         return r;
 
index 2672679458f2b88c2e2b35f2b82000393f46ad40..c22aac32715e38036ed7d9d0e84ada026e8a406e 100644 (file)
@@ -166,19 +166,28 @@ static void managed_journal_file_set_offline_internal(ManagedJournalFile *f) {
 
         for (;;) {
                 switch (f->file->offline_state) {
-                case OFFLINE_CANCEL:
-                        if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_CANCEL, OFFLINE_DONE))
+                case OFFLINE_CANCEL: {
+                        OfflineState tmp_state = OFFLINE_CANCEL;
+                        if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_DONE,
+                                                         false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
                                 continue;
+                        }
                         return;
 
-                case OFFLINE_AGAIN_FROM_SYNCING:
-                        if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_SYNCING))
+                case OFFLINE_AGAIN_FROM_SYNCING: {
+                        OfflineState tmp_state = OFFLINE_AGAIN_FROM_SYNCING;
+                        if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_SYNCING,
+                                                         false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
                                 continue;
+                        }
                         break;
 
-                case OFFLINE_AGAIN_FROM_OFFLINING:
-                        if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_SYNCING))
+                case OFFLINE_AGAIN_FROM_OFFLINING: {
+                        OfflineState tmp_state = OFFLINE_AGAIN_FROM_OFFLINING;
+                        if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_SYNCING,
+                                                         false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
                                 continue;
+                        }
                         break;
 
                 case OFFLINE_SYNCING:
@@ -189,8 +198,12 @@ static void managed_journal_file_set_offline_internal(ManagedJournalFile *f) {
 
                         (void) fsync(f->file->fd);
 
-                        if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING))
-                                continue;
+                        {
+                                OfflineState tmp_state = OFFLINE_SYNCING;
+                                if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_OFFLINING,
+                                                                 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+                                        continue;
+                        }
 
                         f->file->header->state = f->file->archive ? STATE_ARCHIVED : STATE_OFFLINE;
                         (void) fsync(f->file->fd);
@@ -221,9 +234,12 @@ static void managed_journal_file_set_offline_internal(ManagedJournalFile *f) {
 
                         break;
 
-                case OFFLINE_OFFLINING:
-                        if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_OFFLINING, OFFLINE_DONE))
+                case OFFLINE_OFFLINING: {
+                        OfflineState tmp_state = OFFLINE_OFFLINING;
+                        if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_DONE,
+                                                         false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
                                 continue;
+                        }
                         _fallthrough_;
                 case OFFLINE_DONE:
                         return;
@@ -253,19 +269,28 @@ static bool managed_journal_file_set_offline_try_restart(ManagedJournalFile *f)
                 case OFFLINE_AGAIN_FROM_OFFLINING:
                         return true;
 
-                case OFFLINE_CANCEL:
-                        if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_CANCEL, OFFLINE_AGAIN_FROM_SYNCING))
+                case OFFLINE_CANCEL: {
+                        OfflineState tmp_state = OFFLINE_CANCEL;
+                        if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_AGAIN_FROM_SYNCING,
+                                                         false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
                                 continue;
+                        }
                         return true;
 
-                case OFFLINE_SYNCING:
-                        if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_SYNCING, OFFLINE_AGAIN_FROM_SYNCING))
+                case OFFLINE_SYNCING: {
+                        OfflineState tmp_state = OFFLINE_SYNCING;
+                        if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_AGAIN_FROM_SYNCING,
+                                                         false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
                                 continue;
+                        }
                         return true;
 
-                case OFFLINE_OFFLINING:
-                        if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_OFFLINING, OFFLINE_AGAIN_FROM_OFFLINING))
+                case OFFLINE_OFFLINING: {
+                        OfflineState tmp_state = OFFLINE_OFFLINING;
+                        if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_AGAIN_FROM_OFFLINING,
+                                                         false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
                                 continue;
+                        }
                         return true;
 
                 default:
@@ -352,7 +377,7 @@ int managed_journal_file_set_offline(ManagedJournalFile *f, bool wait) {
 bool managed_journal_file_is_offlining(ManagedJournalFile *f) {
         assert(f);
 
-        __sync_synchronize();
+        __atomic_thread_fence(__ATOMIC_SEQ_CST);
 
         if (IN_SET(f->file->offline_state, OFFLINE_DONE, OFFLINE_JOINED))
                 return false;
index eda3eaaebf7b038f4234f55f1fdad5f2c33da406..2e440636680161319af264380d3e9adf471a3349 100755 (executable)
@@ -23,7 +23,7 @@ echo 'opt1 opt2' >"$D/sources/cmdline"
 cat >"$D/sources/install.conf" <<EOF
 layout=bls
 initrd_generator=none
-# those are overriden by envvars
+# those are overridden by envvars
 BOOT_ROOT="$D/badboot"
 MACHINE_ID=badbadbadbadbadbad6abadbadbadbad
 EOF
index 326b09ac5e20cc42a9ff6c11a6e9c6ce06388513..68f6a7cb3c040845c28668374b5913c2b80026e3 100644 (file)
@@ -63,20 +63,26 @@ int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) {
         return 0;
 }
 
-static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t t, struct duid *ret_duid, size_t *ret_len) {
+static int dhcp_identifier_set_duid_llt(
+                const struct hw_addr_data *hw_addr,
+                uint16_t arp_type,
+                usec_t t,
+                struct duid *ret_duid,
+                size_t *ret_len) {
+
         uint16_t time_from_2000y;
 
-        assert(addr);
+        assert(hw_addr);
         assert(ret_duid);
         assert(ret_len);
 
-        if (addr_len == 0)
+        if (hw_addr->length == 0)
                 return -EOPNOTSUPP;
 
         if (arp_type == ARPHRD_ETHER)
-                assert_return(addr_len == ETH_ALEN, -EINVAL);
+                assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
         else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+                assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
         else
                 return -EOPNOTSUPP;
 
@@ -88,33 +94,38 @@ static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, ui
         unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
         unaligned_write_be16(&ret_duid->llt.htype, arp_type);
         unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
-        memcpy(ret_duid->llt.haddr, addr, addr_len);
+        memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length);
 
-        *ret_len = offsetof(struct duid, llt.haddr) + addr_len;
+        *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
 
         return 0;
 }
 
-static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uint16_t arp_type, struct duid *ret_duid, size_t *ret_len) {
-        assert(addr);
+static int dhcp_identifier_set_duid_ll(
+                const struct hw_addr_data *hw_addr,
+                uint16_t arp_type,
+                struct duid *ret_duid,
+                size_t *ret_len) {
+
+        assert(hw_addr);
         assert(ret_duid);
         assert(ret_len);
 
-        if (addr_len == 0)
+        if (hw_addr->length == 0)
                 return -EOPNOTSUPP;
 
         if (arp_type == ARPHRD_ETHER)
-                assert_return(addr_len == ETH_ALEN, -EINVAL);
+                assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
         else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+                assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
         else
                 return -EOPNOTSUPP;
 
         unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
         unaligned_write_be16(&ret_duid->ll.htype, arp_type);
-        memcpy(ret_duid->ll.haddr, addr, addr_len);
+        memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length);
 
-        *ret_len = offsetof(struct duid, ll.haddr) + addr_len;
+        *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
 
         return 0;
 }
@@ -174,8 +185,7 @@ static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len)
 
 int dhcp_identifier_set_duid(
                 DUIDType duid_type,
-                const uint8_t *addr,
-                size_t addr_len,
+                const struct hw_addr_data *hw_addr,
                 uint16_t arp_type,
                 usec_t llt_time,
                 bool test_mode,
@@ -184,11 +194,11 @@ int dhcp_identifier_set_duid(
 
         switch (duid_type) {
         case DUID_TYPE_LLT:
-                return dhcp_identifier_set_duid_llt(addr, addr_len, arp_type, llt_time, ret_duid, ret_len);
+                return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len);
         case DUID_TYPE_EN:
                 return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
         case DUID_TYPE_LL:
-                return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len);
+                return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len);
         case DUID_TYPE_UUID:
                 return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
         default:
@@ -198,8 +208,7 @@ int dhcp_identifier_set_duid(
 
 int dhcp_identifier_set_iaid(
                 int ifindex,
-                const uint8_t *mac,
-                size_t mac_len,
+                const struct hw_addr_data *hw_addr,
                 bool legacy_unstable_byteorder,
                 bool use_mac,
                 void *ret) {
@@ -212,6 +221,10 @@ int dhcp_identifier_set_iaid(
         uint64_t id;
         int r;
 
+        assert(ifindex > 0);
+        assert(hw_addr);
+        assert(ret);
+
         if (udev_available() && !use_mac) {
                 /* udev should be around */
 
@@ -240,7 +253,7 @@ int dhcp_identifier_set_iaid(
                 id = siphash24(name, strlen(name), HASH_KEY.bytes);
         else
                 /* fall back to MAC address if no predictable name available */
-                id = siphash24(mac, mac_len, HASH_KEY.bytes);
+                id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
 
         id32 = (id & 0xffffffff) ^ (id >> 32);
 
index 697ba3bfbb0d28d77a4841ca546cfca6333e43d2..8acb8c3210a94a7a18706791029344bdc7b26ce8 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "sd-id128.h"
 
+#include "ether-addr-util.h"
 #include "macro.h"
 #include "sparse-endian.h"
 #include "time-util.h"
@@ -58,8 +59,7 @@ int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict);
 int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
 int dhcp_identifier_set_duid(
                 DUIDType duid_type,
-                const uint8_t *addr,
-                size_t addr_len,
+                const struct hw_addr_data *hw_addr,
                 uint16_t arp_type,
                 usec_t llt_time,
                 bool test_mode,
@@ -67,8 +67,7 @@ int dhcp_identifier_set_duid(
                 size_t *ret_len);
 int dhcp_identifier_set_iaid(
                 int ifindex,
-                const uint8_t *mac,
-                size_t mac_len,
+                const struct hw_addr_data *hw_addr,
                 bool legacy_unstable_byteorder,
                 bool use_mac,
                 void *ret);
index 466d8e4b3f1403f4889ef1f3a5a7d77802d9ab65..a311d1d5b9129d3d150ee4338e7b66292d33d26f 100644 (file)
@@ -12,6 +12,7 @@
 #include "sd-dhcp-client.h"
 
 #include "dhcp-protocol.h"
+#include "ether-addr-util.h"
 #include "network-common.h"
 #include "socket-util.h"
 
@@ -32,10 +33,14 @@ extern const struct hash_ops dhcp_option_hash_ops;
 
 typedef struct sd_dhcp_client sd_dhcp_client;
 
-int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid,
-                                 const uint8_t *mac_addr, size_t mac_addr_len,
-                                 const uint8_t *bcast_addr, size_t bcast_addr_len,
-                                 uint16_t arp_type, uint16_t port);
+int dhcp_network_bind_raw_socket(
+                int ifindex,
+                union sockaddr_union *link,
+                uint32_t xid,
+                const struct hw_addr_data *hw_addr,
+                const struct hw_addr_data *bcast_addr,
+                uint16_t arp_type,
+                uint16_t port);
 int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type);
 int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
                                  const void *packet, size_t len);
index 7abdaa515bcf6d9d18ab5f90a043fb332bdf5e60..0ee977a8c82c77a4795c29f6db617cdd0d74b0c1 100644 (file)
 #include "socket-util.h"
 #include "unaligned.h"
 
-static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
-                            uint32_t xid,
-                            const uint8_t *bcast_addr,
-                            size_t bcast_addr_len,
-                            const struct ether_addr *eth_mac,
-                            uint16_t arp_type, uint8_t dhcp_hlen,
-                            uint16_t port) {
+static int _bind_raw_socket(
+                int ifindex,
+                union sockaddr_union *link,
+                uint32_t xid,
+                const struct hw_addr_data *hw_addr,
+                const struct hw_addr_data *bcast_addr,
+                uint16_t arp_type,
+                uint16_t port) {
+
+        assert(ifindex > 0);
+        assert(link);
+        assert(hw_addr);
+        assert(bcast_addr);
+        assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
+
+        switch (arp_type) {
+        case ARPHRD_ETHER:
+                assert(hw_addr->length == ETH_ALEN);
+                assert(bcast_addr->length == ETH_ALEN);
+                break;
+        case ARPHRD_INFINIBAND:
+                assert(hw_addr->length == 0);
+                assert(bcast_addr->length == INFINIBAND_ALEN);
+                break;
+        default:
+                assert_not_reached();
+        }
+
         struct sock_filter filter[] = {
                 BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),                                 /* A <- packet length */
                 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0),         /* packet >= DHCPPacket ? */
@@ -53,20 +74,20 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0),                        /* client identifier == xid ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
                 BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)),   /* A <- MAC address length */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0),                  /* address length == dhcp_hlen ? */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (uint8_t) hw_addr->length, 1, 0),  /* address length == hw_addr->length ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
 
                 /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
                  * compare chaddr for ETH_ALEN bytes. */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 8),                                   /* A (the MAC address length) == ETH_ALEN ? */
-                BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),       /* X <- 4 bytes of client's MAC */
-                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)),                 /* A <- 4 bytes of MAC from dhcp.chaddr */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),                                          /* A == X ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                          /* ignore */
-                BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),       /* X <- remainder of client's MAC */
-                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4),             /* A <- remainder of MAC from dhcp.chaddr */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),                                          /* A == X ? */
-                BPF_STMT(BPF_RET + BPF_K, 0),                                                          /* ignore */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 8),                        /* A (the MAC address length) == ETH_ALEN ? */
+                BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(hw_addr->bytes)),           /* X <- 4 bytes of client's MAC */
+                BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)),      /* A <- 4 bytes of MAC from dhcp.chaddr */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),                               /* A == X ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                               /* ignore */
+                BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(hw_addr->bytes + 4)),       /* X <- remainder of client's MAC */
+                BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4),  /* A <- remainder of MAC from dhcp.chaddr */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),                               /* A == X ? */
+                BPF_STMT(BPF_RET + BPF_K, 0),                                               /* ignore */
 
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)),  /* A <- DHCP magic cookie */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0),          /* cookie == DHCP magic cookie ? */
@@ -80,9 +101,6 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
         _cleanup_close_ int s = -1;
         int r;
 
-        assert(ifindex > 0);
-        assert(link);
-
         s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
         if (s < 0)
                 return -errno;
@@ -100,9 +118,10 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
                 .sll_protocol = htobe16(ETH_P_IP),
                 .sll_ifindex = ifindex,
                 .sll_hatype = htobe16(arp_type),
-                .sll_halen = bcast_addr_len,
+                .sll_halen = bcast_addr->length,
         };
-        memcpy(link->ll.sll_addr, bcast_addr, bcast_addr_len); /* We may overflow link->ll. link->ll_buffer ensures we have enough space. */
+        /* We may overflow link->ll. link->ll_buffer ensures we have enough space. */
+        memcpy(link->ll.sll_addr, bcast_addr->bytes, bcast_addr->length);
 
         r = bind(s, &link->sa, SOCKADDR_LL_LEN(link->ll));
         if (r < 0)
@@ -115,47 +134,42 @@ int dhcp_network_bind_raw_socket(
                 int ifindex,
                 union sockaddr_union *link,
                 uint32_t xid,
-                const uint8_t *mac_addr,
-                size_t mac_addr_len,
-                const uint8_t *bcast_addr,
-                size_t bcast_addr_len,
+                const struct hw_addr_data *hw_addr,
+                const struct hw_addr_data *bcast_addr,
                 uint16_t arp_type,
                 uint16_t port) {
 
-        static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-        /* Default broadcast address for IPoIB */
-        static const uint8_t ib_bcast[] = {
-                0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
-                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                0xff, 0xff, 0xff, 0xff
+        static struct hw_addr_data default_eth_bcast = {
+                .length = ETH_ALEN,
+                .ether = {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }},
+        }, default_ib_bcast = {
+                .length = INFINIBAND_ALEN,
+                .infiniband = {
+                        0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
+                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                        0xff, 0xff, 0xff, 0xff
+                },
         };
-        struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } };
-        const uint8_t *default_bcast_addr;
-        size_t expected_bcast_addr_len;
-        uint8_t dhcp_hlen = 0;
-
-        if (arp_type == ARPHRD_ETHER) {
-                assert_return(mac_addr_len == ETH_ALEN, -EINVAL);
-                memcpy(&eth_mac, mac_addr, ETH_ALEN);
-                dhcp_hlen = ETH_ALEN;
-
-                default_bcast_addr = eth_bcast;
-                expected_bcast_addr_len = ETH_ALEN;
-        } else if (arp_type == ARPHRD_INFINIBAND) {
-                default_bcast_addr = ib_bcast;
-                expected_bcast_addr_len = INFINIBAND_ALEN;
-        } else
-                return -EINVAL;
 
-        if (bcast_addr && bcast_addr_len > 0)
-                assert_return(bcast_addr_len == expected_bcast_addr_len, -EINVAL);
-        else {
-                bcast_addr = default_bcast_addr;
-                bcast_addr_len = expected_bcast_addr_len;
+        assert(ifindex > 0);
+        assert(link);
+        assert(hw_addr);
+
+        switch (arp_type) {
+        case ARPHRD_ETHER:
+                return _bind_raw_socket(ifindex, link, xid,
+                                        hw_addr,
+                                        (bcast_addr && !hw_addr_is_null(bcast_addr)) ? bcast_addr : &default_eth_bcast,
+                                        arp_type, port);
+
+        case ARPHRD_INFINIBAND:
+                return _bind_raw_socket(ifindex, link, xid,
+                                        &HW_ADDR_NULL,
+                                        (bcast_addr && !hw_addr_is_null(bcast_addr)) ? bcast_addr : &default_ib_bcast,
+                                        arp_type, port);
+        default:
+                return -EINVAL;
         }
-
-        return _bind_raw_socket(ifindex, link, xid, bcast_addr, bcast_addr_len,
-                                &eth_mac, arp_type, dhcp_hlen, port);
 }
 
 int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
index dd54bcf6ee933842049b15047a43de1583b5f370..c4972d144c238c2cff5d708a8846278d50f483c0 100644 (file)
@@ -43,9 +43,10 @@ typedef struct DHCPPacket DHCPPacket;
 
 #define DHCP_IP_SIZE            (int32_t)(sizeof(struct iphdr))
 #define DHCP_IP_UDP_SIZE        (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
-#define DHCP_MESSAGE_SIZE       (int32_t)(sizeof(DHCPMessage))
-#define DHCP_DEFAULT_MIN_SIZE   576 /* the minimum internet hosts must be able to receive */
-#define DHCP_MIN_OPTIONS_SIZE   (DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE)
+#define DHCP_HEADER_SIZE        (int32_t)(sizeof(DHCPMessage))
+#define DHCP_MIN_MESSAGE_SIZE   576 /* the minimum internet hosts must be able to receive, see RFC 2132 Section 9.10 */
+#define DHCP_MIN_OPTIONS_SIZE   (DHCP_MIN_MESSAGE_SIZE - DHCP_HEADER_SIZE)
+#define DHCP_MIN_PACKET_SIZE    (DHCP_MIN_MESSAGE_SIZE + DHCP_IP_UDP_SIZE)
 #define DHCP_MAGIC_COOKIE       (uint32_t)(0x63825363)
 
 enum {
index 176391ebecdd0926522828d958cd87073da3e68b..65f6cb057f4ad497832aae3ae4af0a42313b414c 100644 (file)
@@ -71,6 +71,7 @@ struct sd_dhcp6_client {
         char **vendor_class;
         OrderedHashmap *extra_options;
         OrderedSet *vendor_options;
+        bool rapid_commit;
 
         struct sd_dhcp6_lease *lease;
 
index c399a7ac50e2b1cb25b22230942541242ca1cbc3..f965ea40fea9c882ccc6f0c68f441d154ede7a23 100644 (file)
@@ -82,3 +82,14 @@ static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status);
+
+int dhcp6_message_status_to_errno(DHCP6Status s) {
+        switch (s) {
+        case DHCP6_STATUS_SUCCESS:
+                return 0;
+        case DHCP6_STATUS_NO_BINDING:
+                return -EADDRNOTAVAIL;
+        default:
+                return -EINVAL;
+        }
+}
index f4e47857e3e97137254f421bc380ad6ddd40a7e7..18217691b7511afd8ccce48dd420bb1c4e42c89b 100644 (file)
@@ -154,3 +154,4 @@ const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_;
 DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_;
 const char *dhcp6_message_status_to_string(DHCP6Status s) _const_;
 DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_;
+int dhcp6_message_status_to_errno(DHCP6Status s);
index 1812a61950ec9cbf52a8ad09943eb9a37a7c35f6..a9cfba90e3d769f9242b44c6523ab96769dab192 100644 (file)
@@ -14,8 +14,8 @@ int dhcp_network_bind_raw_socket(
                 int ifindex,
                 union sockaddr_union *link,
                 uint32_t id,
-                const uint8_t *addr, size_t addr_len,
-                const uint8_t *bcaddr, size_t bcaddr_len,
+                const struct hw_addr_data *hw_addr,
+                const struct hw_addr_data *bcast_addr,
                 uint16_t arp_type, uint16_t port) {
 
         int fd;
index 3b53c5c6a80d98320ed9c12dca69879b75c5f4cd..a1c34365c0301dbcd80e038da770da091b5932ab 100644 (file)
@@ -48,7 +48,7 @@ static void fuzz_client(sd_dhcp6_client *client, const uint8_t *data, size_t siz
                         assert_se(IN_SET(client->state, DHCP6_STATE_REQUEST, DHCP6_STATE_BOUND));
                         break;
                 case DHCP6_STATE_REQUEST:
-                        assert_se(client->state == DHCP6_STATE_BOUND);
+                        assert_se(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_SOLICITATION));
                         break;
                 default:
                         assert_not_reached();
index d2c6361cb85510e4c7b26f4cfe0e4a1700cdcc4f..5b7e3640e87f7a181ee8043f9668ccaf00f893b3 100644 (file)
@@ -19,6 +19,7 @@
 #include "dhcp-lease-internal.h"
 #include "dhcp-protocol.h"
 #include "dns-domain.h"
+#include "ether-addr-util.h"
 #include "event-util.h"
 #include "fd-util.h"
 #include "hostname-util.h"
@@ -86,10 +87,8 @@ struct sd_dhcp_client {
         Set *req_opts;
         bool anonymize;
         be32_t last_addr;
-        uint8_t mac_addr[MAX_MAC_ADDR_LEN];
-        size_t mac_addr_len;
-        uint8_t bcast_addr[MAX_MAC_ADDR_LEN];
-        size_t bcast_addr_len;
+        struct hw_addr_data hw_addr;
+        struct hw_addr_data bcast_addr;
         uint16_t arp_type;
         sd_dhcp_client_id client_id;
         size_t client_id_len;
@@ -237,6 +236,7 @@ int sd_dhcp_client_set_callback(
 
 int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         client->request_broadcast = broadcast;
 
@@ -245,7 +245,7 @@ int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast)
 
 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
         assert_return(client, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         switch (option) {
 
@@ -268,7 +268,7 @@ int sd_dhcp_client_set_request_address(
                 const struct in_addr *last_addr) {
 
         assert_return(client, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         if (last_addr)
                 client->last_addr = last_addr->s_addr;
@@ -280,7 +280,7 @@ int sd_dhcp_client_set_request_address(
 
 int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
         assert_return(client, -EINVAL);
-        assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
         assert_return(ifindex > 0, -EINVAL);
 
         client->ifindex = ifindex;
@@ -314,54 +314,20 @@ int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret) {
 
 int sd_dhcp_client_set_mac(
                 sd_dhcp_client *client,
-                const uint8_t *addr,
+                const uint8_t *hw_addr,
                 const uint8_t *bcast_addr,
                 size_t addr_len,
                 uint16_t arp_type) {
 
-        DHCP_CLIENT_DONT_DESTROY(client);
-        bool need_restart = false;
-        int r;
-
         assert_return(client, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
-        assert_return(arp_type > 0, -EINVAL);
-
-        if (arp_type == ARPHRD_ETHER)
-                assert_return(addr_len == ETH_ALEN, -EINVAL);
-        else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
-        else
-                return -EINVAL;
-
-        if (client->mac_addr_len == addr_len &&
-            memcmp(&client->mac_addr, addr, addr_len) == 0 &&
-            (client->bcast_addr_len > 0) == !!bcast_addr &&
-            (!bcast_addr || memcmp(&client->bcast_addr, bcast_addr, addr_len) == 0))
-                return 0;
-
-        if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
-                log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting");
-                need_restart = true;
-                client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-        }
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+        assert_return(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND), -EINVAL);
+        assert_return(hw_addr, -EINVAL);
+        assert_return(addr_len == (arp_type == ARPHRD_ETHER ? ETH_ALEN : INFINIBAND_ALEN), -EINVAL);
 
-        memcpy(&client->mac_addr, addr, addr_len);
-        client->mac_addr_len = addr_len;
         client->arp_type = arp_type;
-        client->bcast_addr_len = 0;
-
-        if (bcast_addr) {
-                memcpy(&client->bcast_addr, bcast_addr, addr_len);
-                client->bcast_addr_len = addr_len;
-        }
-
-        if (need_restart && client->state != DHCP_STATE_STOPPED) {
-                r = sd_dhcp_client_start(client);
-                if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
-        }
+        hw_addr_set(&client->hw_addr, hw_addr, addr_len);
+        hw_addr_set(&client->bcast_addr, bcast_addr, bcast_addr ? addr_len : 0);
 
         return 0;
 }
@@ -396,19 +362,11 @@ int sd_dhcp_client_set_client_id(
                 const uint8_t *data,
                 size_t data_len) {
 
-        DHCP_CLIENT_DONT_DESTROY(client);
-        bool need_restart = false;
-        int r;
-
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
         assert_return(data, -EINVAL);
         assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
 
-        if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
-            client->client_id.type == type &&
-            memcmp(&client->client_id.raw.data, data, data_len) == 0)
-                return 0;
-
         /* For hardware types, log debug message about unexpected data length.
          *
          * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only
@@ -420,23 +378,10 @@ int sd_dhcp_client_set_client_id(
                                 "unexpected address length %zu",
                                 type, data_len);
 
-        if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
-                log_dhcp_client(client, "Changing client ID on running DHCP "
-                                "client, restarting");
-                need_restart = true;
-                client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-        }
-
         client->client_id.type = type;
         memcpy(&client->client_id.raw.data, data, data_len);
         client->client_id_len = data_len + sizeof (client->client_id.type);
 
-        if (need_restart && client->state != DHCP_STATE_STOPPED) {
-                r = sd_dhcp_client_start(client);
-                if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
-        }
-
         return 0;
 }
 
@@ -455,11 +400,11 @@ static int dhcp_client_set_iaid_duid_internal(
                 size_t duid_len,
                 usec_t llt_time) {
 
-        DHCP_CLIENT_DONT_DESTROY(client);
-        int r;
         size_t len;
+        int r;
 
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
         assert_return(duid_len == 0 || duid, -EINVAL);
 
         if (duid) {
@@ -475,8 +420,7 @@ static int dhcp_client_set_iaid_duid_internal(
                 if (iaid_set)
                         client->client_id.ns.iaid = htobe32(iaid);
                 else {
-                        r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
-                                                     client->mac_addr_len,
+                        r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr,
                                                      /* legacy_unstable_byteorder = */ true,
                                                      /* use_mac = */ client->test_mode,
                                                      &client->client_id.ns.iaid);
@@ -491,7 +435,7 @@ static int dhcp_client_set_iaid_duid_internal(
                 len = sizeof(client->client_id.ns.duid.type) + duid_len;
 
         } else {
-                r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len,
+                r = dhcp_identifier_set_duid(duid_type, &client->hw_addr,
                                              client->arp_type, llt_time, client->test_mode,
                                              &client->client_id.ns.duid, &len);
                 if (r == -EOPNOTSUPP)
@@ -507,14 +451,6 @@ static int dhcp_client_set_iaid_duid_internal(
         client->client_id_len = sizeof(client->client_id.type) + len +
                                 (iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
 
-        if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
-                log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
-                client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-                r = sd_dhcp_client_start(client);
-                if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
-        }
-
         return 0;
 }
 
@@ -561,6 +497,7 @@ int sd_dhcp_client_set_hostname(
                 const char *hostname) {
 
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         /* Make sure hostnames qualify as DNS and as Linux hostnames */
         if (hostname &&
@@ -575,6 +512,7 @@ int sd_dhcp_client_set_vendor_class_identifier(
                 const char *vci) {
 
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         return free_and_strdup(&client->vendor_class_identifier, vci);
 }
@@ -584,6 +522,7 @@ int sd_dhcp_client_set_mud_url(
                 const char *mudurl) {
 
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
         assert_return(mudurl, -EINVAL);
         assert_return(strlen(mudurl) <= 255, -EINVAL);
         assert_return(http_url_is_valid(mudurl), -EINVAL);
@@ -598,6 +537,7 @@ int sd_dhcp_client_set_user_class(
         char **s = NULL;
 
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
         assert_return(!strv_isempty(user_class), -EINVAL);
 
         STRV_FOREACH(p, user_class) {
@@ -619,6 +559,7 @@ int sd_dhcp_client_set_client_port(
                 uint16_t port) {
 
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         client->port = port;
 
@@ -627,7 +568,10 @@ int sd_dhcp_client_set_client_port(
 
 int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
         assert_return(client, -EINVAL);
-        assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
+        assert_return(mtu >= DHCP_MIN_PACKET_SIZE, -ERANGE);
+
+        /* MTU may be changed by the acquired lease. Hence, we cannot require that the client is stopped here.
+         * Please do not add assertion for !sd_dhcp_client_is_running(client) here. */
 
         client->mtu = mtu;
 
@@ -636,6 +580,7 @@ int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
 
 int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) {
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         client->max_attempts = max_attempts;
 
@@ -646,6 +591,7 @@ int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) {
         int r;
 
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
         assert_return(v, -EINVAL);
 
         r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp_option_hash_ops, UINT_TO_PTR(v->option), v);
@@ -660,6 +606,7 @@ int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v)
         int r;
 
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
         assert_return(v, -EINVAL);
 
         r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops);
@@ -689,6 +636,7 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
 
 int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         client->ip_service_type = type;
 
@@ -697,6 +645,7 @@ int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
 
 int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
         assert_return(fallback_lease_lifetime > 0, -EINVAL);
 
         client->fallback_lease_lifetime = fallback_lease_lifetime;
@@ -794,7 +743,6 @@ static int client_message_init(
 
         _cleanup_free_ DHCPPacket *packet = NULL;
         size_t optlen, optoffset, size;
-        be16_t max_size;
         usec_t time_now;
         uint16_t secs;
         int r;
@@ -814,7 +762,7 @@ static int client_message_init(
                 return -ENOMEM;
 
         r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
-                              client->arp_type, client->mac_addr_len, client->mac_addr,
+                              client->arp_type, client->hw_addr.length, client->hw_addr.bytes,
                               optlen, &optoffset);
         if (r < 0)
                 return r;
@@ -851,7 +799,7 @@ static int client_message_init(
 
                 client->client_id.type = 255;
 
-                r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
+                r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr,
                                              /* legacy_unstable_byteorder = */ true,
                                              /* use_mac = */ client->test_mode,
                                              &client->client_id.ns.iaid);
@@ -937,9 +885,9 @@ static int client_message_init(
          */
         /* RFC7844 section 3:
            SHOULD NOT contain any other option. */
-        if (!client->anonymize && type != DHCP_RELEASE) {
-                max_size = htobe16(size);
-                r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
+        if (!client->anonymize && IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)) {
+                be16_t max_size = htobe16(MIN(client->mtu - DHCP_IP_UDP_SIZE, (uint32_t) UINT16_MAX));
+                r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
                                        SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
                                        2, &max_size);
                 if (r < 0)
@@ -1430,8 +1378,7 @@ static int client_start_delayed(sd_dhcp_client *client) {
         client->xid = random_u32();
 
         r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
-                                         client->mac_addr, client->mac_addr_len,
-                                         client->bcast_addr, client->bcast_addr_len,
+                                         &client->hw_addr, &client->bcast_addr,
                                          client->arp_type, client->port);
         if (r < 0) {
                 client_stop(client, r);
@@ -1481,8 +1428,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
         client->attempt = 0;
 
         r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
-                                         client->mac_addr, client->mac_addr_len,
-                                         client->bcast_addr, client->bcast_addr_len,
+                                         &client->hw_addr, &client->bcast_addr,
                                          client->arp_type, client->port);
         if (r < 0) {
                 client_stop(client, r);
@@ -1946,7 +1892,7 @@ static int client_receive_message_udp(
 
         if (client->arp_type == ARPHRD_ETHER) {
                 expected_hlen = ETH_ALEN;
-                expected_chaddr = &client->mac_addr[0];
+                expected_chaddr = client->hw_addr.bytes;
         }
 
         if (message->hlen != expected_hlen) {
@@ -2042,6 +1988,7 @@ static int client_receive_message_raw(
 
 int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
         assert_return(client, -EINVAL);
+        assert_return(sd_dhcp_client_is_running(client), -ESTALE);
         assert_return(client->fd >= 0, -EINVAL);
 
         if (!client->lease)
@@ -2089,7 +2036,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
 
 int sd_dhcp_client_send_release(sd_dhcp_client *client) {
         assert_return(client, -EINVAL);
-        assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
+        assert_return(sd_dhcp_client_is_running(client), -ESTALE);
         assert_return(client->lease, -EUNATCH);
 
         _cleanup_free_ DHCPPacket *release = NULL;
@@ -2102,7 +2049,7 @@ int sd_dhcp_client_send_release(sd_dhcp_client *client) {
 
         /* Fill up release IP and MAC */
         release->dhcp.ciaddr = client->lease->address;
-        memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
+        memcpy(&release->dhcp.chaddr, client->hw_addr.bytes, client->hw_addr.length);
 
         r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
@@ -2124,7 +2071,7 @@ int sd_dhcp_client_send_release(sd_dhcp_client *client) {
 
 int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
         assert_return(client, -EINVAL);
-        assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
+        assert_return(sd_dhcp_client_is_running(client), -ESTALE);
         assert_return(client->lease, -EUNATCH);
 
         _cleanup_free_ DHCPPacket *release = NULL;
@@ -2136,7 +2083,7 @@ int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
                 return r;
 
         release->dhcp.ciaddr = client->lease->address;
-        memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
+        memcpy(&release->dhcp.chaddr, client->hw_addr.bytes, client->hw_addr.length);
 
         r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
@@ -2181,6 +2128,7 @@ int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t
 
         assert_return(client, -EINVAL);
         assert_return(!client->event, -EBUSY);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         if (event)
                 client->event = sd_event_ref(event);
@@ -2197,6 +2145,7 @@ int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t
 
 int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
         assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
         client->event = sd_event_unref(client->event);
 
@@ -2253,7 +2202,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
                 .state = DHCP_STATE_INIT,
                 .ifindex = -1,
                 .fd = -1,
-                .mtu = DHCP_DEFAULT_MIN_SIZE,
+                .mtu = DHCP_MIN_PACKET_SIZE,
                 .port = DHCP_PORT_CLIENT,
                 .anonymize = !!anonymize,
                 .max_attempts = UINT64_MAX,
index 388c5cd2a4b48ed10d2b6bcc6d4b32faad67eb3c..734a4fa27df088e052cdbc0d08efb78053e1de5d 100644 (file)
@@ -713,9 +713,9 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                 r = lease_parse_u16(option, len, &lease->mtu, 68);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
-                if (lease->mtu < DHCP_DEFAULT_MIN_SIZE) {
-                        log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_DEFAULT_MIN_SIZE);
-                        lease->mtu = DHCP_DEFAULT_MIN_SIZE;
+                if (lease->mtu < DHCP_MIN_PACKET_SIZE) {
+                        log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_MIN_PACKET_SIZE);
+                        lease->mtu = DHCP_MIN_PACKET_SIZE;
                 }
 
                 break;
index 4ca51591065bec6f458b6e79572d138bebfb89d9..26114900677cd04f0a88ab2a8f8bed4c40d00711 100644 (file)
@@ -116,9 +116,8 @@ int sd_dhcp6_client_set_mac(
                 return 0;
         }
 
-        memcpy(client->hw_addr.bytes, addr, addr_len);
-        client->hw_addr.length = addr_len;
         client->arp_type = arp_type;
+        hw_addr_set(&client->hw_addr, addr, addr_len);
 
         return 0;
 }
@@ -216,8 +215,8 @@ static int dhcp6_client_set_duid_internal(
                 client->duid_len = sizeof(client->duid.type) + duid_len;
 
         } else {
-                r = dhcp_identifier_set_duid(duid_type, client->hw_addr.bytes, client->hw_addr.length,
-                                             client->arp_type, llt_time, client->test_mode, &client->duid, &client->duid_len);
+                r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
+                                             client->test_mode, &client->duid, &client->duid_len);
                 if (r == -EOPNOTSUPP)
                         return log_dhcp6_client_errno(client, r,
                                                       "Failed to set %s. MAC address is not set or "
@@ -300,7 +299,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
         if (client->iaid_set)
                 return 0;
 
-        r = dhcp_identifier_set_iaid(client->ifindex, client->hw_addr.bytes, client->hw_addr.length,
+        r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr,
                                      /* legacy_unstable_byteorder = */ true,
                                      /* use_mac = */ client->test_mode,
                                      &iaid);
@@ -491,6 +490,14 @@ int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transactio
         return 0;
 }
 
+int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+        client->rapid_commit = enable;
+        return 0;
+}
+
 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
         assert_return(client, -EINVAL);
 
@@ -536,13 +543,9 @@ static void client_notify(sd_dhcp6_client *client, int event) {
                 client->callback(client, event, client->userdata);
 }
 
-static void client_stop(sd_dhcp6_client *client, int error) {
-        DHCP6_CLIENT_DONT_DESTROY(client);
-
+static void client_cleanup(sd_dhcp6_client *client) {
         assert(client);
 
-        client_notify(client, error);
-
         client->lease = sd_dhcp6_lease_unref(client->lease);
 
         /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
@@ -559,6 +562,16 @@ static void client_stop(sd_dhcp6_client *client, int error) {
         client_set_state(client, DHCP6_STATE_STOPPED);
 }
 
+static void client_stop(sd_dhcp6_client *client, int error) {
+        DHCP6_CLIENT_DONT_DESTROY(client);
+
+        assert(client);
+
+        client_notify(client, error);
+
+        client_cleanup(client);
+}
+
 static int client_append_common_options_in_managed_mode(
                 sd_dhcp6_client *client,
                 uint8_t **opt,
@@ -677,6 +690,9 @@ static int client_append_oro(sd_dhcp6_client *client, uint8_t **opt, size_t *opt
                 req_opts = client->req_opts;
         }
 
+        if (n == 0)
+                return 0;
+
         return dhcp6_option_append(opt, optlen, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
 }
 
@@ -714,9 +730,11 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
                 break;
 
         case DHCP6_STATE_SOLICITATION:
-                r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
-                if (r < 0)
-                        return r;
+                if (client->rapid_commit) {
+                        r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+                        if (r < 0)
+                                return r;
+                }
 
                 r = client_append_common_options_in_managed_mode(client, &opt, &optlen,
                                                                  &client->ia_na, &client->ia_pd);
@@ -1124,6 +1142,20 @@ static int client_process_reply(
                 return log_invalid_message_type(client, message);
 
         r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
+        if (r == -EADDRNOTAVAIL) {
+
+                /* If NoBinding status code is received, we cannot request the address anymore.
+                 * Let's restart transaction from the beginning. */
+
+                if (client->state == DHCP6_STATE_REQUEST)
+                        /* The lease is not acquired yet, hence it is not necessary to notify the restart. */
+                        client_cleanup(client);
+                else
+                        /* We need to notify the previous lease was expired. */
+                        client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
+
+                return client_start_transaction(client, DHCP6_STATE_SOLICITATION);
+        }
         if (r < 0)
                 return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
 
@@ -1160,6 +1192,10 @@ static int client_process_advertise_or_rapid_commit_reply(
         if (message->type == DHCP6_MESSAGE_REPLY) {
                 bool rapid_commit;
 
+                if (!client->rapid_commit)
+                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+                                                      "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring.");
+
                 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
                 if (r < 0)
                         return r;
@@ -1467,6 +1503,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
                 .ifindex = -1,
                 .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
                 .fd = -1,
+                .rapid_commit = true,
         };
 
         *ret = TAKE_PTR(client);
index f588514cb646ba537e177ca47b6c423766e694c3..57c23965eda3ab3b73b0bfdf61856213fa879756 100644 (file)
@@ -512,7 +512,7 @@ static int dhcp6_lease_parse_message(
                                 return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
 
                         if (r > 0)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+                                return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r),
                                                               "Received %s message with non-zero status: %s%s%s",
                                                               dhcp6_message_type_to_string(message->type),
                                                               strempty(msg), isempty(msg) ? "" : ": ",
index fe0d8361655063880aa26314902c1a0ddc1e85e1..155c1a5de6ce5fa8a2726db8a7cc9bf6c5fb2e71 100644 (file)
@@ -212,21 +212,12 @@ int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
         return sd_ipv4acd_is_running(ll->acd);
 }
 
-static bool ipv4ll_address_is_valid(const struct in_addr *address) {
-        assert(address);
-
-        if (!in4_addr_is_link_local(address))
-                return false;
-
-        return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
-}
-
 int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
         int r;
 
         assert_return(ll, -EINVAL);
         assert_return(address, -EINVAL);
-        assert_return(ipv4ll_address_is_valid(address), -EINVAL);
+        assert_return(in4_addr_is_link_local_dynamic(address), -EINVAL);
 
         r = sd_ipv4acd_set_address(ll->acd, address);
         if (r < 0)
index e369f5a5921c89c206e5b25e935e6667cc360901..73105f5a04104fa84ac5613ee7c0ec9fdf629881 100644 (file)
@@ -12,7 +12,6 @@
 #include "sd-netlink.h"
 
 #include "in-addr-util.h"
-#include "netlink-util.h"
 #include "tests.h"
 #include "util.h"
 
index fb86e6c994c7f666fbe1670596bb56091cbb1d9d..8344dc4f4a806505ae1cdbaa9cfe306fc9f9a471 100644 (file)
 #include "dhcp-identifier.h"
 #include "dhcp-internal.h"
 #include "dhcp-protocol.h"
+#include "ether-addr-util.h"
 #include "fd-util.h"
 #include "random-util.h"
 #include "tests.h"
 #include "util.h"
 
-static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'};
-static uint8_t bcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-
+static struct hw_addr_data hw_addr = {
+        .length = ETH_ALEN,
+        .ether = {{ 'A', 'B', 'C', '1', '2', '3' }},
+}, bcast_addr = {
+        .length = ETH_ALEN,
+        .ether = {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }},
+};
 typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp);
 
 static bool verbose = true;
@@ -139,9 +144,9 @@ static void test_dhcp_identifier_set_iaid(void) {
         uint32_t iaid_legacy;
         be32_t iaid;
 
-        assert_se(dhcp_identifier_set_iaid(42, mac_addr, sizeof(mac_addr), /* legacy = */ true,
+        assert_se(dhcp_identifier_set_iaid(42, &hw_addr, /* legacy = */ true,
                                            /* use_mac = */ true, &iaid_legacy) >= 0);
-        assert_se(dhcp_identifier_set_iaid(42, mac_addr, sizeof(mac_addr), /* legacy = */ false,
+        assert_se(dhcp_identifier_set_iaid(42, &hw_addr, /* legacy = */ false,
                                            /* use_mac = */ true, &iaid) >= 0);
 
         /* we expect, that the MAC address was hashed. The legacy value is in native
@@ -164,7 +169,7 @@ static int check_options(uint8_t code, uint8_t len, const void *option, void *us
                 size_t duid_len;
 
                 assert_se(dhcp_identifier_set_duid_en(/* test_mode = */ true, &duid, &duid_len) >= 0);
-                assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, true, /* use_mac = */ true, &iaid) >= 0);
+                assert_se(dhcp_identifier_set_iaid(42, &hw_addr, /* legacy = */ true, /* use_mac = */ true, &iaid) >= 0);
 
                 assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
                 assert_se(len == 19);
@@ -217,7 +222,7 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const
         assert_se(ip_check == 0xffff);
 
         assert_se(discover->dhcp.xid);
-        assert_se(memcmp(discover->dhcp.chaddr, &mac_addr, ETH_ALEN) == 0);
+        assert_se(memcmp(discover->dhcp.chaddr, hw_addr.bytes, hw_addr.length) == 0);
 
         size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
 
@@ -231,8 +236,8 @@ int dhcp_network_bind_raw_socket(
                 int ifindex,
                 union sockaddr_union *link,
                 uint32_t id,
-                const uint8_t *addr, size_t addr_len,
-                const uint8_t *bcaddr, size_t bcaddr_len,
+                const struct hw_addr_data *_hw_addr,
+                const struct hw_addr_data *_bcast_addr,
                 uint16_t arp_type, uint16_t port) {
 
         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
@@ -282,7 +287,7 @@ static void test_discover_message(sd_event *e) {
         assert_se(r >= 0);
 
         assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
-        assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0);
+        assert_se(sd_dhcp_client_set_mac(client, hw_addr.bytes, bcast_addr.bytes, hw_addr.length, ARPHRD_ETHER) >= 0);
         dhcp_client_set_test_mode(client, true);
 
         assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0);
@@ -440,7 +445,7 @@ static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) {
 
         memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check));
         memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid));
-        memcpy(&test_addr_acq_ack[56], &mac_addr, ETHER_ADDR_LEN);
+        memcpy(&test_addr_acq_ack[56], hw_addr.bytes, hw_addr.length);
 
         callback_recv = NULL;
 
@@ -471,7 +476,7 @@ static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) {
 
         memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check));
         memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid));
-        memcpy(&test_addr_acq_offer[56], &mac_addr, ETHER_ADDR_LEN);
+        memcpy(&test_addr_acq_offer[56], hw_addr.bytes, hw_addr.length);
 
         callback_recv = test_addr_acq_recv_request;
 
@@ -500,7 +505,7 @@ static void test_addr_acq(sd_event *e) {
         assert_se(r >= 0);
 
         assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
-        assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0);
+        assert_se(sd_dhcp_client_set_mac(client, hw_addr.bytes, bcast_addr.bytes, hw_addr.length, ARPHRD_ETHER) >= 0);
         dhcp_client_set_test_mode(client, true);
 
         assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e) >= 0);
index e79758c5f5c56cce075c20c87032596b37c626a5..5453ef93a4a30fb50fe509a1070d7697d537568e 100644 (file)
@@ -12,7 +12,6 @@
 
 #include "alloc-util.h"
 #include "in-addr-util.h"
-#include "netlink-util.h"
 #include "parse-util.h"
 #include "string-util.h"
 #include "tests.h"
@@ -39,7 +38,7 @@ static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
         }
 }
 
-static int client_run(int ifindex, const char *seed_str, const struct ether_addr *ha, sd_event *e) {
+static int client_run(int ifindex, const char *seed_str, const struct in_addr *start_address, const struct ether_addr *ha, sd_event *e) {
         sd_ipv4ll *ll;
 
         assert_se(sd_ipv4ll_new(&ll) >= 0);
@@ -57,6 +56,9 @@ static int client_run(int ifindex, const char *seed_str, const struct ether_addr
                 assert_se(sd_ipv4ll_set_address_seed(ll, seed) >= 0);
         }
 
+        if (start_address && in4_addr_is_set(start_address))
+                assert_se(sd_ipv4ll_set_address(ll, start_address) >= 0);
+
         log_info("starting IPv4LL client");
 
         assert_se(sd_ipv4ll_start(ll) >= 0);
@@ -68,7 +70,7 @@ static int client_run(int ifindex, const char *seed_str, const struct ether_addr
         return EXIT_SUCCESS;
 }
 
-static int test_ll(const char *ifname, const char *seed) {
+static int test_ll(const char *ifname, const char *seed, const struct in_addr *start_address) {
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
@@ -87,7 +89,7 @@ static int test_ll(const char *ifname, const char *seed) {
         assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0);
         assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0);
 
-        client_run(ifindex, seed, &ha, e);
+        client_run(ifindex, seed, start_address, &ha, e);
 
         return EXIT_SUCCESS;
 }
@@ -96,12 +98,19 @@ int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
         if (argc == 2)
-                return test_ll(argv[1], NULL);
-        else if (argc == 3)
-                return test_ll(argv[1], argv[2]);
-        else {
+                return test_ll(argv[1], NULL, NULL);
+        else if (argc == 3) {
+                int r;
+                union in_addr_union a;
+
+                r = in_addr_from_string(AF_INET, argv[2], &a);
+                if (r < 0)
+                        return test_ll(argv[1], argv[2], NULL);
+                else
+                        return test_ll(argv[1], NULL, &a.in);
+        } else {
                 log_error("This program takes one or two arguments.\n"
-                          "\t %s <ifname> [<seed>]", program_invocation_short_name);
+                          "\t %s <ifname> [<seed>|<start_address>]", program_invocation_short_name);
                 return EXIT_FAILURE;
         }
 }
index 44d0bfe17c3038f0571614ac3834af64ff091c7c..2a078a502e70e1acd3462ee4ed6cdea898ef5d51 100644 (file)
@@ -122,7 +122,7 @@ static void test_public_api_setters(sd_event *e) {
         assert_se(sd_ipv4ll_unref(ll) == NULL);
 }
 
-static void test_basic_request(sd_event *e) {
+static void test_basic_request(sd_event *e, const struct in_addr *start_address) {
 
         sd_ipv4ll *ll;
         struct ether_arp arp;
@@ -133,6 +133,8 @@ static void test_basic_request(sd_event *e) {
                 printf("* %s\n", __func__);
 
         assert_se(sd_ipv4ll_new(&ll) == 0);
+        if (in4_addr_is_set(start_address))
+                assert_se(sd_ipv4ll_set_address(ll, start_address) >= 0);
         assert_se(sd_ipv4ll_start(ll) == -EINVAL);
 
         assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0);
@@ -168,6 +170,13 @@ static void test_basic_request(sd_event *e) {
 
                 sd_event_run(e, UINT64_MAX);
                 assert_se(basic_request_handler_bind == 1);
+
+                if (in4_addr_is_set(start_address)) {
+                        struct in_addr address;
+
+                        assert_se(sd_ipv4ll_get_address(ll, &address) >= 0);
+                        assert_se(start_address->s_addr == address.s_addr);
+                }
         }
 
         sd_ipv4ll_stop(ll);
@@ -179,6 +188,7 @@ static void test_basic_request(sd_event *e) {
 }
 
 int main(int argc, char *argv[]) {
+        struct in_addr start_address = {};
         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
 
         test_setup_logging(LOG_DEBUG);
@@ -186,7 +196,12 @@ int main(int argc, char *argv[]) {
         assert_se(sd_event_new(&e) >= 0);
 
         test_public_api_setters(e);
-        test_basic_request(e);
+        test_basic_request(e, &start_address);
+
+        basic_request_handler_bind = 0;
+        basic_request_handler_stop = 0;
+        start_address.s_addr = htobe32(169U << 24 | 254U << 16 | 1U << 8 | 2U);
+        test_basic_request(e, &start_address);
 
         return 0;
 }
index 24dfee709530904ef1ca2f1c6d202da8b06d1309..215b6cf87fefb1100b5fa177eb0ef71cf8289680 100644 (file)
@@ -784,6 +784,10 @@ global:
         sd_bus_message_read_strv_extend;
         sd_bus_error_setfv;
 
+        sd_device_new_child;
+        sd_device_monitor_set_description;
+        sd_device_monitor_get_description;
+
         sd_id128_string_equal;
 
         sd_hwdb_new_from_path;
@@ -818,6 +822,7 @@ global:
         sd_netlink_message_append_s32;
         sd_netlink_message_append_s64;
         sd_netlink_message_append_data;
+        sd_netlink_message_append_container_data;
         sd_netlink_message_append_in_addr;
         sd_netlink_message_append_in6_addr;
         sd_netlink_message_append_sockaddr_in;
index 909dfe4d3a3ff7538b562acf81e931591b648a85..2ad7a9993d949ee09ca9bbc48482e7b2db3eacaf 100644 (file)
@@ -1044,6 +1044,7 @@ static int object_manager_serialize_path(
                 const char *prefix,
                 const char *path,
                 bool require_fallback,
+                bool *found_object_manager,
                 sd_bus_error *error) {
 
         const char *previous_interface = NULL;
@@ -1055,12 +1056,16 @@ static int object_manager_serialize_path(
         assert(reply);
         assert(prefix);
         assert(path);
+        assert(found_object_manager);
         assert(error);
 
         n = hashmap_get(bus->nodes, prefix);
         if (!n)
                 return 0;
 
+        if (!require_fallback && n->object_managers)
+                *found_object_manager = true;
+
         LIST_FOREACH(vtables, i, n->vtables) {
                 void *u;
 
@@ -1103,9 +1108,12 @@ static int object_manager_serialize_path(
                         if (r < 0)
                                 return r;
 
-                        r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
-                        if (r < 0)
-                                return r;
+                        if (*found_object_manager) {
+                                r = sd_bus_message_append(
+                                                reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
+                                if (r < 0)
+                                        return r;
+                        }
 
                         found_something = true;
                 }
@@ -1180,6 +1188,7 @@ static int object_manager_serialize_path_and_fallbacks(
         _cleanup_free_ char *prefix = NULL;
         size_t pl;
         int r;
+        bool found_object_manager = false;
 
         assert(bus);
         assert(reply);
@@ -1187,7 +1196,7 @@ static int object_manager_serialize_path_and_fallbacks(
         assert(error);
 
         /* First, add all vtables registered for this path */
-        r = object_manager_serialize_path(bus, reply, path, path, false, error);
+        r = object_manager_serialize_path(bus, reply, path, path, false, &found_object_manager, error);
         if (r < 0)
                 return r;
         if (bus->nodes_modified)
@@ -1201,7 +1210,7 @@ static int object_manager_serialize_path_and_fallbacks(
                 return -ENOMEM;
 
         OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
-                r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
+                r = object_manager_serialize_path(bus, reply, prefix, path, true, &found_object_manager, error);
                 if (r < 0)
                         return r;
                 if (bus->nodes_modified)
@@ -1548,13 +1557,18 @@ void bus_node_gc(sd_bus *b, struct node *n) {
         free(n);
 }
 
-static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
+static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path, bool* path_has_object_manager) {
         struct node *n;
 
         assert(bus);
         assert(path);
+        assert(path_has_object_manager);
 
         n = hashmap_get(bus->nodes, path);
+
+        if (n)
+                *path_has_object_manager = n->object_managers;
+
         if (!n) {
                 _cleanup_free_ char *prefix = NULL;
                 size_t pl;
@@ -2423,7 +2437,7 @@ static int object_added_append_all_prefix(
         return 0;
 }
 
-static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
+static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path, bool path_has_object_manager) {
         _cleanup_ordered_set_free_ OrderedSet *s = NULL;
         _cleanup_free_ char *prefix = NULL;
         size_t pl;
@@ -2461,9 +2475,11 @@ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *p
         r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
         if (r < 0)
                 return r;
-        r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
-        if (r < 0)
-                return r;
+        if (path_has_object_manager){
+                r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
+                if (r < 0)
+                        return r;
+        }
 
         r = object_added_append_all_prefix(bus, m, s, path, path, false);
         if (r < 0)
@@ -2512,7 +2528,8 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
         if (!BUS_IS_OPEN(bus->state))
                 return -ENOTCONN;
 
-        r = bus_find_parent_object_manager(bus, &object_manager, path);
+        bool path_has_object_manager = false;
+        r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -2536,7 +2553,7 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
                 if (r < 0)
                         return r;
 
-                r = object_added_append_all(bus, m, path);
+                r = object_added_append_all(bus, m, path, path_has_object_manager);
                 if (r < 0)
                         return r;
 
@@ -2612,7 +2629,7 @@ static int object_removed_append_all_prefix(
         return 0;
 }
 
-static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
+static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path, bool path_has_object_manager) {
         _cleanup_ordered_set_free_ OrderedSet *s = NULL;
         _cleanup_free_ char *prefix = NULL;
         size_t pl;
@@ -2637,9 +2654,12 @@ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char
         r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
         if (r < 0)
                 return r;
-        r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
-        if (r < 0)
-                return r;
+
+        if (path_has_object_manager){
+                r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
+                if (r < 0)
+                        return r;
+        }
 
         r = object_removed_append_all_prefix(bus, m, s, path, path, false);
         if (r < 0)
@@ -2688,7 +2708,8 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
         if (!BUS_IS_OPEN(bus->state))
                 return -ENOTCONN;
 
-        r = bus_find_parent_object_manager(bus, &object_manager, path);
+        bool path_has_object_manager = false;
+        r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -2712,7 +2733,7 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
                 if (r < 0)
                         return r;
 
-                r = object_removed_append_all(bus, m, path);
+                r = object_removed_append_all(bus, m, path, path_has_object_manager);
                 if (r < 0)
                         return r;
 
@@ -2849,7 +2870,8 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch
         if (strv_isempty(interfaces))
                 return 0;
 
-        r = bus_find_parent_object_manager(bus, &object_manager, path);
+        bool path_has_object_manager = false;
+        r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -2936,7 +2958,8 @@ _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path,
         if (strv_isempty(interfaces))
                 return 0;
 
-        r = bus_find_parent_object_manager(bus, &object_manager, path);
+        bool path_has_object_manager = false;
+        r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
         if (r < 0)
                 return r;
         if (r == 0)
index 2148351952eecf3ac679014b05d38b1c042ccd80..cf87dac321bf2dbe4483e05affbdb3ee4a00bf86 100644 (file)
@@ -156,75 +156,86 @@ static int bus_socket_write_auth(sd_bus *b) {
 }
 
 static int bus_socket_auth_verify_client(sd_bus *b) {
-        char *d, *e, *f, *start;
+        char *l, *lines[4] = {};
         sd_id128_t peer;
+        size_t i, n;
         int r;
 
         assert(b);
 
         /*
-         * We expect three response lines:
-         *   "DATA\r\n"
+         * We expect up to three response lines:
+         *   "DATA\r\n"                 (optional)
          *   "OK <server-id>\r\n"
          *   "AGREE_UNIX_FD\r\n"        (optional)
          */
 
-        d = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2);
-        if (!d)
-                return 0;
-
-        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_safe(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
-                if (!f)
-                        return 0;
-
-                start = f + 2;
-        } else {
-                f = NULL;
-                start = e + 2;
+        n = 0;
+        lines[n] = b->rbuffer;
+        for (i = 0; i < 3; ++i) {
+                l = memmem_safe(lines[n], b->rbuffer_size - (lines[n] - (char*) b->rbuffer), "\r\n", 2);
+                if (l)
+                        lines[++n] = l + 2;
+                else
+                        break;
         }
 
-        /* Nice! We got all the lines we need. First check the DATA line. */
-
-        if (d - (char*) b->rbuffer == 4) {
-                if (memcmp(b->rbuffer, "DATA", 4))
-                        return -EPERM;
-        } else if (d - (char*) b->rbuffer == 3 + 32) {
-                /*
-                 * Old versions of the server-side implementation of `sd-bus` replied with "OK <id>" to
-                 * "AUTH" requests from a client, even if the "AUTH" line did not contain inlined
-                 * arguments. Therefore, we also accept "OK <id>" here, even though it is technically the
-                 * wrong reply. We ignore the "<id>" parameter, though, since it has no real value.
-                 */
-                if (memcmp(b->rbuffer, "OK ", 3))
+        /*
+         * If we sent a non-empty initial response, then we just expect an OK
+         * reply. We currently do this if, and only if, we picked ANONYMOUS.
+         * If we did not send an initial response, then we expect a DATA
+         * challenge, reply with our own DATA, and expect an OK reply. We do
+         * this for EXTERNAL.
+         * If FD negotiation was requested, we additionally expect
+         * an AGREE_UNIX_FD response in all cases.
+         */
+        if (n < (b->anonymous_auth ? 1U : 2U) + !!b->accept_fd)
+                return 0; /* wait for more data */
+
+        i = 0;
+
+        /* In case of EXTERNAL, verify the first response was DATA. */
+        if (!b->anonymous_auth) {
+                l = lines[i++];
+                if (lines[i] - l == 4 + 2) {
+                        if (memcmp(l, "DATA", 4))
+                                return -EPERM;
+                } else if (lines[i] - l == 3 + 32 + 2) {
+                        /*
+                         * Old versions of the server-side implementation of
+                         * `sd-bus` replied with "OK <id>" to "AUTH" requests
+                         * from a client, even if the "AUTH" line did not
+                         * contain inlined arguments. Therefore, we also accept
+                         * "OK <id>" here, even though it is technically the
+                         * wrong reply. We ignore the "<id>" parameter, though,
+                         * since it has no real value.
+                         */
+                        if (memcmp(l, "OK ", 3))
+                                return -EPERM;
+                } else
                         return -EPERM;
-        } else
-                return -EPERM;
+        }
 
         /* Now check the OK line. */
+        l = lines[i++];
 
-        if (e - d != 2 + 3 + 32)
+        if (lines[i] - l != 3 + 32 + 2)
                 return -EPERM;
-
-        if (memcmp(d + 2, "OK ", 3))
+        if (memcmp(l, "OK ", 3))
                 return -EPERM;
 
         b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL;
 
-        for (unsigned i = 0; i < 32; i += 2) {
+        for (unsigned j = 0; j < 32; j += 2) {
                 int x, y;
 
-                x = unhexchar(d[2 + 3 + i]);
-                y = unhexchar(d[2 + 3 + i + 1]);
+                x = unhexchar(l[3 + j]);
+                y = unhexchar(l[3 + j + 1]);
 
                 if (x < 0 || y < 0)
                         return -EINVAL;
 
-                peer.bytes[i/2] = ((uint8_t) x << 4 | (uint8_t) y);
+                peer.bytes[j/2] = ((uint8_t) x << 4 | (uint8_t) y);
         }
 
         if (!sd_id128_is_null(b->server_id) &&
@@ -234,15 +245,15 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
         b->server_id = peer;
 
         /* And possibly check the third line, too */
+        if (b->accept_fd) {
+                l = lines[i++];
+                b->can_fds = !!memory_startswith(l, lines[i] - l, "AGREE_UNIX_FD");
+        }
 
-        if (f)
-                b->can_fds =
-                        (f - e == STRLEN("\r\nAGREE_UNIX_FD")) &&
-                        memcmp(e + 2, "AGREE_UNIX_FD",
-                               STRLEN("AGREE_UNIX_FD")) == 0;
+        assert(i == n);
 
-        b->rbuffer_size -= (start - (char*) b->rbuffer);
-        memmove(b->rbuffer, start, b->rbuffer_size);
+        b->rbuffer_size -= (lines[i] - (char*) b->rbuffer);
+        memmove(b->rbuffer, lines[i], b->rbuffer_size);
 
         r = bus_start_running(b);
         if (r < 0)
@@ -646,9 +657,8 @@ static int bus_socket_start_auth_client(sd_bus *b) {
                  * message broker to aid debugging of clients. We fully anonymize the connection and use a
                  * static default.
                  */
-                "\0AUTH ANONYMOUS\r\n"
-                /* HEX a n o n y m o u s */
-                "DATA 616e6f6e796d6f7573\r\n"
+                /*            HEX a n o n y m o u s */
+                "\0AUTH ANONYMOUS 616e6f6e796d6f7573\r\n"
         };
         static const char sasl_auth_external[] = {
                 "\0AUTH EXTERNAL\r\n"
index 6c8d5db6933bb5b3ce063f3198a9be72e27a3ec8..949ef07c341dd71005ad7fff0fd6669da1f1335d 100644 (file)
@@ -165,6 +165,12 @@ static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *er
         return 1;
 }
 
+static int emit_object_with_manager_added(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a") >= 0);
+
+        return ASSERT_SE_NONNEG(sd_bus_reply_method_return(m, NULL));
+}
+
 static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         int r;
 
@@ -187,6 +193,7 @@ static const sd_bus_vtable vtable[] = {
         SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0),
         SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0),
         SD_BUS_METHOD("EmitObjectAdded", NULL, NULL, emit_object_added, 0),
+        SD_BUS_METHOD("EmitObjectWithManagerAdded", NULL, NULL, emit_object_with_manager_added, 0),
         SD_BUS_METHOD("EmitObjectRemoved", NULL, NULL, emit_object_removed, 0),
         SD_BUS_VTABLE_END
 };
@@ -463,6 +470,41 @@ static int client(struct context *c) {
 
         sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
+        /* Check that /value/b does not have ObjectManager interface but /value/a does */
+        assert_se(sd_bus_message_rewind(reply, 1) > 0);
+        assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{oa{sa{sv}}}") > 0);
+        while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "oa{sa{sv}}")) > 0) {
+                const char *path = NULL;
+                assert_se(sd_bus_message_read_basic(reply, 'o', &path) > 0);
+                if (STR_IN_SET(path, "/value/b", "/value/a")) {
+                        /* Check that there is no object manager interface here */
+                        bool found_object_manager_interface = false;
+                        assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sa{sv}}") > 0);
+                        while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}")) > 0) {
+                                const char *interface_name = NULL;
+                                assert_se(sd_bus_message_read_basic(reply, 's', &interface_name) > 0);
+
+                                if (streq(interface_name, "org.freedesktop.DBus.ObjectManager")) {
+                                        assert_se(!streq(path, "/value/b"));
+                                        found_object_manager_interface = true;
+                                }
+
+                                assert_se(sd_bus_message_skip(reply, "a{sv}") >= 0);
+                                assert_se(sd_bus_message_exit_container(reply) >= 0);
+                        }
+                        assert_se(sd_bus_message_exit_container(reply) >= 0);
+
+                        if (streq(path, "/value/a")) {
+                                /* ObjectManager must be here */
+                                assert_se(found_object_manager_interface);
+                        }
+
+                } else
+                        assert_se(sd_bus_message_skip(reply, "a{sa{sv}}") >= 0);
+
+                assert_se(sd_bus_message_exit_container(reply) >= 0);
+        }
+
         reply = sd_bus_message_unref(reply);
 
         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, NULL);
@@ -518,6 +560,54 @@ static int client(struct context *c) {
         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
         sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
+        /* Check if /value/a/x does not have org.freedesktop.DBus.ObjectManager */
+        assert_se(sd_bus_message_rewind(reply, 1) >= 0);
+        const char* should_be_value_a_x = NULL;
+        assert_se(sd_bus_message_read_basic(reply, 'o', &should_be_value_a_x) > 0);
+        assert_se(streq(should_be_value_a_x, "/value/a/x"));
+        assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sa{sv}}") > 0);
+        while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}")) > 0) {
+                const char* interface_name = NULL;
+                assert_se(sd_bus_message_read_basic(reply, 's', &interface_name) > 0);
+
+                assert(!streq(interface_name, "org.freedesktop.DBus.ObjectManager"));
+
+                assert_se(sd_bus_message_skip(reply, "a{sv}") >= 0);
+
+                assert_se(sd_bus_message_exit_container(reply) >= 0);
+        }
+
+        reply = sd_bus_message_unref(reply);
+
+        assert_se(sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectWithManagerAdded", &error, NULL, NULL) >= 0);
+
+        assert_se(sd_bus_process(bus, &reply) > 0);
+
+        assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
+        sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
+
+        /* Check if /value/a has org.freedesktop.DBus.ObjectManager */
+        assert_se(sd_bus_message_rewind(reply, 1) >= 0);
+        const char* should_be_value_a = NULL;
+        bool found_object_manager = false;
+        assert_se(sd_bus_message_read_basic(reply, 'o', &should_be_value_a) > 0);
+        assert_se(streq(should_be_value_a, "/value/a"));
+        assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sa{sv}}") > 0);
+        while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}")) > 0) {
+                const char* interface_name = NULL;
+                assert_se(sd_bus_message_read_basic(reply, 's', &interface_name));
+
+                if (streq(interface_name, "org.freedesktop.DBus.ObjectManager")) {
+                        found_object_manager = true;
+                        break;
+                }
+
+                assert_se(sd_bus_message_skip(reply, "a{sv}") >= 0);
+
+                assert_se(sd_bus_message_exit_container(reply) >= 0);
+        }
+        assert_se(found_object_manager);
+
         reply = sd_bus_message_unref(reply);
 
         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &error, NULL, NULL);
@@ -529,6 +619,18 @@ static int client(struct context *c) {
         assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
         sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
 
+        /* Check if /value/a/x does not have org.freedesktop.DBus.ObjectManager */
+        assert_se(sd_bus_message_rewind(reply, 1) >= 0);
+        should_be_value_a_x = NULL;
+        assert_se(sd_bus_message_read_basic(reply, 'o', &should_be_value_a_x) > 0);
+        assert_se(streq(should_be_value_a_x, "/value/a/x"));
+        assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s") > 0);
+        const char* deleted_interface_name = NULL;
+        while (ASSERT_SE_NONNEG(sd_bus_message_read_basic(reply, 's', &deleted_interface_name)) > 0) {
+                assert(!streq(deleted_interface_name, "org.freedesktop.DBus.ObjectManager"));
+        }
+        assert_se(sd_bus_message_exit_container(reply) >= 0);
+
         reply = sd_bus_message_unref(reply);
 
         r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, NULL);
index 39f769c35cf65bd240363219a1f069b9916dd8fa..30f2520b7245eed5febba2f0d6bc809ae279553f 100644 (file)
@@ -157,9 +157,7 @@ _public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumer
         else
                 hashmap = &enumerator->nomatch_sysattr;
 
-        /* Do not use string_has_ops_free_free or hashmap_put_strdup() here, as this may be called
-         * multiple times with the same sysattr but different value. */
-        r = hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value);
+        r = update_match_strv(hashmap, sysattr, value, /* clear_on_null = */ true);
         if (r <= 0)
                 return r;
 
@@ -174,9 +172,7 @@ _public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enume
         assert_return(enumerator, -EINVAL);
         assert_return(property, -EINVAL);
 
-        /* Do not use string_has_ops_free_free or hashmap_put_strdup() here, as this may be called
-         * multiple times with the same property but different value. */
-        r = hashmap_put_strdup_full(&enumerator->match_property, &trivial_hash_ops_free_free, property, value);
+        r = update_match_strv(&enumerator->match_property, property, value, /* clear_on_null = */ false);
         if (r <= 0)
                 return r;
 
@@ -466,29 +462,25 @@ int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *de
 }
 
 static bool match_property(sd_device_enumerator *enumerator, sd_device *device) {
-        const char *property;
-        const char *value;
+        const char *property_pattern;
+        char * const *value_patterns;
 
         assert(enumerator);
         assert(device);
 
+        /* Unlike device_match_sysattr(), this accepts device that has at least one matching property. */
+
         if (hashmap_isempty(enumerator->match_property))
                 return true;
 
-        HASHMAP_FOREACH_KEY(value, property, enumerator->match_property) {
-                const char *property_dev, *value_dev;
+        HASHMAP_FOREACH_KEY(value_patterns, property_pattern, enumerator->match_property) {
+                const char *property, *value;
 
-                FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) {
-                        if (fnmatch(property, property_dev, 0) != 0)
+                FOREACH_DEVICE_PROPERTY(device, property, value) {
+                        if (fnmatch(property_pattern, property, 0) != 0)
                                 continue;
 
-                        if (!value && !value_dev)
-                                return true;
-
-                        if (!value || !value_dev)
-                                continue;
-
-                        if (fnmatch(value, value_dev, 0) == 0)
+                        if (strv_fnmatch(value_patterns, value))
                                 return true;
                 }
         }
@@ -750,8 +742,6 @@ static int enumerator_scan_dir(
         if (!dir)
                 return -errno;
 
-        log_debug("sd-device-enumerator: Scanning %s", path);
-
         FOREACH_DIRENT_ALL(de, dir, return -errno) {
                 int k;
 
@@ -957,8 +947,6 @@ static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) {
 static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
         int k, r = 0;
 
-        log_debug("sd-device-enumerator: Scan all dirs");
-
         k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
         if (k < 0)
                 r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
index e8913c3d1f8a3c3433d8bfdf384b78f76949f891..566280d66f3f03da729a64fa2dfaf7d2c6c4918b 100644 (file)
 #include "string-util.h"
 #include "strv.h"
 
+#define log_monitor(m, format, ...)                                     \
+        log_debug("sd-device-monitor(%s): " format, strna(m ? m->description : NULL), ##__VA_ARGS__)
+#define log_monitor_errno(m, r, format, ...)                            \
+        log_debug_errno(r, "sd-device-monitor(%s): " format, strna(m ? m->description : NULL), ##__VA_ARGS__)
+#define log_device_monitor(d, m, format, ...)                           \
+        log_device_debug(d, "sd-device-monitor(%s): " format, strna(m ? m->description : NULL), ##__VA_ARGS__)
+#define log_device_monitor_errno(d, m, r, format, ...)                  \
+        log_device_debug_errno(d, r, "sd-device-monitor(%s): " format, strna(m ? m->description : NULL), ##__VA_ARGS__)
+
 struct sd_device_monitor {
         unsigned n_ref;
 
@@ -46,6 +55,7 @@ struct sd_device_monitor {
 
         sd_event *event;
         sd_event_source *event_source;
+        char *description;
         sd_device_monitor_handler_t callback;
         void *userdata;
 };
@@ -139,14 +149,14 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group,
                  * will not receive any messages.
                  */
 
-                log_debug("sd-device-monitor: The udev service seems not to be active, disabling the monitor");
+                log_monitor(m, "The udev service seems not to be active, disabling the monitor.");
                 group = MONITOR_GROUP_NONE;
         }
 
         if (fd < 0) {
                 sock = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
                 if (sock < 0)
-                        return log_debug_errno(errno, "sd-device-monitor: Failed to create socket: %m");
+                        return log_monitor_errno(m, errno, "Failed to create socket: %m");
         }
 
         m = new(sd_device_monitor, 1);
@@ -164,7 +174,7 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group,
         if (fd >= 0) {
                 r = monitor_set_nl_address(m);
                 if (r < 0) {
-                        log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m");
+                        log_monitor_errno(m, r, "Failed to set netlink address: %m");
                         goto fail;
                 }
         }
@@ -179,12 +189,12 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group,
 
                 netns = ioctl(m->sock, SIOCGSKNS);
                 if (netns < 0)
-                        log_debug_errno(errno, "sd-device-monitor: Unable to get network namespace of udev netlink socket, unable to determine if we are in host netns, ignoring: %m");
+                        log_monitor_errno(m, errno, "Unable to get network namespace of udev netlink socket, unable to determine if we are in host netns, ignoring: %m");
                 else {
                         struct stat a, b;
 
                         if (fstat(netns, &a) < 0) {
-                                r = log_debug_errno(errno, "sd-device-monitor: Failed to stat netns of udev netlink socket: %m");
+                                r = log_monitor_errno(m, errno, "Failed to stat netns of udev netlink socket: %m");
                                 goto fail;
                         }
 
@@ -192,12 +202,12 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group,
                                 if (ERRNO_IS_PRIVILEGE(errno))
                                         /* If we can't access PID1's netns info due to permissions, it's fine, this is a
                                          * safety check only after all. */
-                                        log_debug_errno(errno, "sd-device-monitor: No permission to stat PID1's netns, unable to determine if we are in host netns, ignoring: %m");
+                                        log_monitor_errno(m, errno, "No permission to stat PID1's netns, unable to determine if we are in host netns, ignoring: %m");
                                 else
-                                        log_debug_errno(errno, "sd-device-monitor: Failed to stat PID1's netns, ignoring: %m");
+                                        log_monitor_errno(m, errno, "Failed to stat PID1's netns, ignoring: %m");
 
                         } else if (!stat_inode_same(&a, &b))
-                                log_debug("sd-device-monitor: Netlink socket we listen on is not from host netns, we won't see device events.");
+                                log_monitor(m, "Netlink socket we listen on is not from host netns, we won't see device events.");
                 }
         }
 
@@ -263,7 +273,7 @@ _public_ int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_han
         if (r < 0)
                 return r;
 
-        (void) sd_event_source_set_description(m->event_source, "sd-device-monitor");
+        (void) sd_event_source_set_description(m->event_source, m->description ?: "sd-device-monitor");
 
         return 0;
 }
@@ -306,6 +316,29 @@ _public_ sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *
         return m->event_source;
 }
 
+_public_ int sd_device_monitor_set_description(sd_device_monitor *m, const char *description) {
+        int r;
+
+        assert_return(m, -EINVAL);
+
+        r = free_and_strdup(&m->description, description);
+        if (r <= 0)
+                return r;
+
+        if (m->event_source)
+                (void) sd_event_source_set_description(m->event_source, description);
+
+        return r;
+}
+
+_public_ int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret) {
+        assert_return(m, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        *ret = m->description;
+        return 0;
+}
+
 int device_monitor_enable_receiving(sd_device_monitor *m) {
         int r;
 
@@ -313,22 +346,22 @@ int device_monitor_enable_receiving(sd_device_monitor *m) {
 
         r = sd_device_monitor_filter_update(m);
         if (r < 0)
-                return log_debug_errno(r, "sd-device-monitor: Failed to update filter: %m");
+                return log_monitor_errno(m, r, "Failed to update filter: %m");
 
         if (!m->bound) {
                 /* enable receiving of sender credentials */
                 r = setsockopt_int(m->sock, SOL_SOCKET, SO_PASSCRED, true);
                 if (r < 0)
-                        return log_debug_errno(r, "sd-device-monitor: Failed to set socket option SO_PASSCRED: %m");
+                        return log_monitor_errno(m, r, "Failed to set socket option SO_PASSCRED: %m");
 
                 if (bind(m->sock, &m->snl.sa, sizeof(struct sockaddr_nl)) < 0)
-                        return log_debug_errno(errno, "sd-device-monitor: Failed to bind monitoring socket: %m");
+                        return log_monitor_errno(m, errno, "Failed to bind monitoring socket: %m");
 
                 m->bound = true;
 
                 r = monitor_set_nl_address(m);
                 if (r < 0)
-                        return log_debug_errno(r, "sd-device-monitor: Failed to set address: %m");
+                        return log_monitor_errno(m, r, "Failed to set address: %m");
         }
 
         return 0;
@@ -339,6 +372,7 @@ static sd_device_monitor *device_monitor_free(sd_device_monitor *m) {
 
         (void) sd_device_monitor_detach_event(m);
 
+        free(m->description);
         hashmap_free(m->subsystem_filter);
         set_free(m->tag_filter);
         hashmap_free(m->match_sysattr_filter);
@@ -446,49 +480,49 @@ int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) {
 
         buflen = recvmsg(m->sock, &smsg, 0);
         if (buflen < 0) {
-                if (ERRNO_IS_TRANSIENT(errno))
-                        log_debug_errno(errno, "sd-device-monitor: Failed to receive message: %m");
+                if (!ERRNO_IS_TRANSIENT(errno))
+                        log_monitor_errno(m, errno, "Failed to receive message: %m");
                 return -errno;
         }
 
         if (buflen < 32 || (smsg.msg_flags & MSG_TRUNC))
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "sd-device-monitor: Invalid message length.");
+                return log_monitor_errno(m, SYNTHETIC_ERRNO(EINVAL), "Invalid message length.");
 
         if (snl.nl.nl_groups == MONITOR_GROUP_NONE) {
                 /* unicast message, check if we trust the sender */
                 if (m->snl_trusted_sender.nl.nl_pid == 0 ||
                     snl.nl.nl_pid != m->snl_trusted_sender.nl.nl_pid)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                               "sd-device-monitor: Unicast netlink message ignored.");
+                        return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+                                                 "Unicast netlink message ignored.");
 
         } else if (snl.nl.nl_groups == MONITOR_GROUP_KERNEL) {
                 if (snl.nl.nl_pid > 0)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                               "sd-device-monitor: Multicast kernel netlink message from PID %"PRIu32" ignored.", snl.nl.nl_pid);
+                        return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+                                                 "Multicast kernel netlink message from PID %"PRIu32" ignored.",
+                                                 snl.nl.nl_pid);
         }
 
         cmsg = CMSG_FIRSTHDR(&smsg);
         if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS)
-                return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                       "sd-device-monitor: No sender credentials received, message ignored.");
+                return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+                                         "No sender credentials received, ignoring message.");
 
         cred = (struct ucred*) CMSG_DATA(cmsg);
         if (cred->uid != 0)
-                return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                       "sd-device-monitor: Sender uid="UID_FMT", message ignored.", cred->uid);
+                return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+                                         "Sender uid="UID_FMT", message ignored.", cred->uid);
 
         if (streq(buf.raw, "libudev")) {
                 /* udev message needs proper version magic */
                 if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                               "sd-device-monitor: Invalid message signature (%x != %x)",
-                                               buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC));
+                        return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+                                                 "Invalid message signature (%x != %x).",
+                                                 buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC));
 
                 if (buf.nlh.properties_off+32 > (size_t) buflen)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                               "sd-device-monitor: Invalid message length (%u > %zd)",
-                                               buf.nlh.properties_off+32, buflen);
+                        return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+                                                 "Invalid message length (%u > %zd).",
+                                                 buf.nlh.properties_off+32, buflen);
 
                 bufpos = buf.nlh.properties_off;
 
@@ -499,18 +533,18 @@ int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) {
                 /* kernel message with header */
                 bufpos = strlen(buf.raw) + 1;
                 if ((size_t) bufpos < sizeof("a@/d") || bufpos >= buflen)
-                        return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                               "sd-device-monitor: Invalid message length");
+                        return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+                                                 "Invalid message length.");
 
                 /* check message header */
                 if (!strstr(buf.raw, "@/"))
-                        return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
-                                               "sd-device-monitor: Invalid message header");
+                        return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+                                                 "Invalid message header.");
         }
 
         r = device_new_from_nulstr(&device, &buf.raw[bufpos], buflen - bufpos);
         if (r < 0)
-                return log_debug_errno(r, "sd-device-monitor: Failed to create device from received message: %m");
+                return log_monitor_errno(m, r, "Failed to create device from received message: %m");
 
         if (is_initialized)
                 device_set_is_initialized(device);
@@ -518,9 +552,9 @@ int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) {
         /* Skip device, if it does not pass the current filter */
         r = passes_filter(m, device);
         if (r < 0)
-                return log_device_debug_errno(device, r, "sd-device-monitor: Failed to check received device passing filter: %m");
+                return log_device_monitor_errno(device, m, r, "Failed to check received device passing filter: %m");
         if (r == 0)
-                log_device_debug(device, "sd-device-monitor: Received device does not pass filter, ignoring");
+                log_device_monitor(device, m, "Received device does not pass filter, ignoring.");
         else
                 *ret = TAKE_PTR(device);
 
@@ -576,15 +610,15 @@ int device_monitor_send_device(
 
         r = device_get_properties_nulstr(device, &buf, &blen);
         if (r < 0)
-                return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device properties: %m");
+                return log_device_monitor_errno(device, m, r, "Failed to get device properties: %m");
         if (blen < 32)
-                log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
-                                       "sd-device-monitor: Length of device property nulstr is too small to contain valid device information");
+                return log_device_monitor_errno(device, m, SYNTHETIC_ERRNO(EINVAL),
+                                                "Length of device property nulstr is too small to contain valid device information.");
 
         /* fill in versioned header */
         r = sd_device_get_subsystem(device, &val);
         if (r < 0)
-                return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device subsystem: %m");
+                return log_device_monitor_errno(device, m, r, "Failed to get device subsystem: %m");
         nlh.filter_subsystem_hash = htobe32(string_hash32(val));
 
         if (sd_device_get_devtype(device, &val) >= 0)
@@ -616,13 +650,13 @@ int device_monitor_send_device(
         count = sendmsg(m->sock, &smsg, 0);
         if (count < 0) {
                 if (!destination && errno == ECONNREFUSED) {
-                        log_device_debug(device, "sd-device-monitor: Passed to netlink monitor");
+                        log_device_monitor(device, m, "Passed to netlink monitor.");
                         return 0;
                 } else
-                        return log_device_debug_errno(device, errno, "sd-device-monitor: Failed to send device to netlink monitor: %m");
+                        return log_device_monitor_errno(device, m, errno, "Failed to send device to netlink monitor: %m");
         }
 
-        log_device_debug(device, "sd-device-monitor: Passed %zi byte to netlink monitor", count);
+        log_device_monitor(device, m, "Passed %zi byte to netlink monitor.", count);
         return count;
 }
 
@@ -787,7 +821,7 @@ _public_ int sd_device_monitor_filter_add_match_sysattr(sd_device_monitor *m, co
                 hashmap = &m->nomatch_sysattr_filter;
 
         /* TODO: unset m->filter_uptodate on success when we support this filter on BPF. */
-        return hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value);
+        return update_match_strv(hashmap, sysattr, value, /* clear_on_null = */ true);
 }
 
 _public_ int sd_device_monitor_filter_add_match_parent(sd_device_monitor *m, sd_device *device, int match) {
index 9602f9eda3e5f4e83d556d5ffeddbb8a9834c271..3d670a3afccf1769730b6f13b84359e857192b79 100644 (file)
@@ -18,6 +18,7 @@ static inline int device_new_from_watch_handle(sd_device **ret, int wd) {
 }
 
 int device_get_property_bool(sd_device *device, const char *key);
+int device_get_sysattr_bool(sd_device *device, const char *sysattr);
 int device_get_device_id(sd_device *device, const char **ret);
 int device_get_devlink_priority(sd_device *device, int *ret);
 int device_get_watch_handle(sd_device *device);
@@ -25,6 +26,7 @@ int device_get_devnode_mode(sd_device *device, mode_t *ret);
 int device_get_devnode_uid(sd_device *device, uid_t *ret);
 int device_get_devnode_gid(sd_device *device, gid_t *ret);
 
+void device_clear_sysattr_cache(sd_device *device);
 int device_cache_sysattr_value(sd_device *device, const char *key, char *value);
 int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
 
index 616c16c1fc4f47e87aada43e151200b28639ed4c..3b8689e0d656225679948bd72785e29ebb38bfcc 100644 (file)
@@ -5,7 +5,65 @@
 #include "device-util.h"
 #include "path-util.h"
 
-static bool device_match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) {
+int update_match_strv(Hashmap **match_strv, const char *key, const char *value, bool clear_on_null) {
+        char **strv;
+        int r;
+
+        assert(match_strv);
+        assert(key);
+
+        strv = hashmap_get(*match_strv, key);
+        if (strv) {
+                if (!value) {
+                        char **v;
+
+                        if (strv_isempty(strv) || !clear_on_null)
+                                return 0;
+
+                        /* Accept all value. Clear previous assignment. */
+
+                        v = new0(char*, 1);
+                        if (!v)
+                                return -ENOMEM;
+
+                        strv_free_and_replace(strv, v);
+                } else {
+                        if (strv_contains(strv, value))
+                                return 0;
+
+                        r = strv_extend(&strv, value);
+                        if (r < 0)
+                                return r;
+                }
+
+                r = hashmap_update(*match_strv, key, strv);
+                if (r < 0)
+                        return r;
+
+        } else {
+                _cleanup_strv_free_ char **strv_alloc = NULL;
+                _cleanup_free_ char *key_alloc = NULL;
+
+                key_alloc = strdup(key);
+                if (!key_alloc)
+                        return -ENOMEM;
+
+                strv_alloc = strv_new(value);
+                if (!strv_alloc)
+                        return -ENOMEM;
+
+                r = hashmap_ensure_put(match_strv, &string_hash_ops_free_strv_free, key_alloc, strv_alloc);
+                if (r < 0)
+                        return r;
+
+                TAKE_PTR(key_alloc);
+                TAKE_PTR(strv_alloc);
+        }
+
+        return 1;
+}
+
+static bool device_match_sysattr_value(sd_device *device, const char *sysattr, char * const *patterns) {
         const char *value;
 
         assert(device);
@@ -14,27 +72,21 @@ static bool device_match_sysattr_value(sd_device *device, const char *sysattr, c
         if (sd_device_get_sysattr_value(device, sysattr, &value) < 0)
                 return false;
 
-        if (!match_value)
-                return true;
-
-        if (fnmatch(match_value, value, 0) == 0)
-                return true;
-
-        return false;
+        return strv_fnmatch_or_empty(patterns, value, 0);
 }
 
 bool device_match_sysattr(sd_device *device, Hashmap *match_sysattr, Hashmap *nomatch_sysattr) {
+        char * const *patterns;
         const char *sysattr;
-        const char *value;
 
         assert(device);
 
-        HASHMAP_FOREACH_KEY(value, sysattr, match_sysattr)
-                if (!device_match_sysattr_value(device, sysattr, value))
+        HASHMAP_FOREACH_KEY(patterns, sysattr, match_sysattr)
+                if (!device_match_sysattr_value(device, sysattr, patterns))
                         return false;
 
-        HASHMAP_FOREACH_KEY(value, sysattr, nomatch_sysattr)
-                if (device_match_sysattr_value(device, sysattr, value))
+        HASHMAP_FOREACH_KEY(patterns, sysattr, nomatch_sysattr)
+                if (device_match_sysattr_value(device, sysattr, patterns))
                         return false;
 
         return true;
index e2adfe8132f2aeaf9caf7b85bb046b45a11fb73c..d9e9f1e8c4a0a8581b8144ed08a2e97d8522f52f 100644 (file)
@@ -10,6 +10,9 @@
 #include "macro.h"
 #include "set.h"
 
+#define device_unref_and_replace(a, b)                                  \
+        unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
+
 #define FOREACH_DEVICE_PROPERTY(device, key, value)                \
         for (key = sd_device_get_property_first(device, &(value)); \
              key;                                                  \
@@ -79,5 +82,6 @@
 #define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__)
 #define log_device_error_errno(device, error, ...)   log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
 
+int update_match_strv(Hashmap **match_strv, const char *key, const char *value, bool clear_on_null);
 bool device_match_sysattr(sd_device *device, Hashmap *match_sysattr, Hashmap *nomatch_sysattr);
 bool device_match_parent(sd_device *device, Set *match_parent, Set *nomatch_parent);
index a53f31e5ff73d9c15ccb947978e4ca1449e14d5e..8574337bda9670a04fa7bcd7ea1ba47ce1a8c7b0 100644 (file)
@@ -145,15 +145,12 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
         assert(device);
         assert(_syspath);
 
-        /* must be a subdirectory of /sys */
-        if (!path_startswith(_syspath, "/sys/"))
-                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "sd-device: Syspath '%s' is not a subdirectory of /sys",
-                                       _syspath);
-
         if (verify) {
                 _cleanup_close_ int fd = -1;
 
+                /* The input path maybe a symlink located outside of /sys. Let's try to chase the symlink at first.
+                 * The primary usecase is that e.g. /proc/device-tree is a symlink to /sys/firmware/devicetree/base.
+                 * By chasing symlinks in the path at first, we can call sd_device_new_from_path() with such path. */
                 r = chase_symlinks(_syspath, NULL, 0, &syspath, &fd);
                 if (r == -ENOENT)
                          /* the device does not exist (any more?) */
@@ -230,6 +227,12 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
                                                        "sd-device: the syspath \"%s\" is outside of sysfs, refusing.", syspath);
                 }
         } else {
+                /* must be a subdirectory of /sys */
+                if (!path_startswith(_syspath, "/sys/"))
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "sd-device: Syspath '%s' is not a subdirectory of /sys",
+                                               _syspath);
+
                 syspath = strdup(_syspath);
                 if (!syspath)
                         return log_oom_debug();
@@ -250,13 +253,16 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
         return 0;
 }
 
-_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
+static int device_new_from_syspath(sd_device **ret, const char *syspath, bool strict) {
         _cleanup_(sd_device_unrefp) sd_device *device = NULL;
         int r;
 
         assert_return(ret, -EINVAL);
         assert_return(syspath, -EINVAL);
 
+        if (strict && !path_startswith(syspath, "/sys/"))
+                return -EINVAL;
+
         r = device_new_aux(&device);
         if (r < 0)
                 return r;
@@ -269,6 +275,10 @@ _public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
         return 0;
 }
 
+_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
+        return device_new_from_syspath(ret, syspath, /* strict = */ true);
+}
+
 static int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum) {
         _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
         _cleanup_free_ char *syspath = NULL;
@@ -516,7 +526,7 @@ _public_ int sd_device_new_from_path(sd_device **ret, const char *path) {
         if (path_startswith(path, "/dev"))
                 return sd_device_new_from_devname(ret, path);
 
-        return sd_device_new_from_syspath(ret, path);
+        return device_new_from_syspath(ret, path, /* strict = */ false);
 }
 
 int device_set_devtype(sd_device *device, const char *devtype) {
@@ -861,6 +871,29 @@ _public_ int sd_device_get_syspath(sd_device *device, const char **ret) {
         return 0;
 }
 
+_public_ int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix) {
+        _cleanup_free_ char *path = NULL;
+        const char *s;
+        int r;
+
+        assert_return(ret, -EINVAL);
+        assert_return(device, -EINVAL);
+        assert_return(suffix, -EINVAL);
+
+        if (!path_is_normalized(suffix))
+                return -EINVAL;
+
+        r = sd_device_get_syspath(device, &s);
+        if (r < 0)
+                return r;
+
+        path = path_join(s, suffix);
+        if (!path)
+                return -ENOMEM;
+
+        return sd_device_new_from_syspath(ret, path);
+}
+
 static int device_new_from_child(sd_device **ret, sd_device *child) {
         _cleanup_free_ char *path = NULL;
         const char *syspath;
@@ -2044,6 +2077,10 @@ _public_ int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret) {
         return 0;
 }
 
+void device_clear_sysattr_cache(sd_device *device) {
+        device->sysattr_values = hashmap_free(device->sysattr_values);
+}
+
 int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
         _unused_ _cleanup_free_ char *old_value = NULL;
         _cleanup_free_ char *new_key = NULL;
@@ -2166,6 +2203,20 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
         return 0;
 }
 
+int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
+        const char *value;
+        int r;
+
+        assert(device);
+        assert(sysattr);
+
+        r = sd_device_get_sysattr_value(device, sysattr, &value);
+        if (r < 0)
+                return r;
+
+        return parse_boolean(value);
+}
+
 static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) {
         _cleanup_free_ char *key = NULL;
 
index 67f9bf4ae11283b795e04b2138522751c741d729..66ca63600d7b3ebd834118824af8312bfaba8dc9 100644 (file)
@@ -10,6 +10,7 @@
 #include "device-private.h"
 #include "device-util.h"
 #include "macro.h"
+#include "path-util.h"
 #include "stat-util.h"
 #include "string-util.h"
 #include "tests.h"
@@ -39,13 +40,13 @@ static void test_receive_device_fail(void) {
         assert_se(sd_device_get_syspath(loopback, &syspath) >= 0);
 
         assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
         assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
 
         assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
         assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
         assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
 
         assert_se(device_monitor_send_device(monitor_server, monitor_client, loopback) >= 0);
         assert_se(sd_event_run(sd_device_monitor_get_event(monitor_client), 0) >= 0);
@@ -61,13 +62,13 @@ static void test_send_receive_one(sd_device *device, bool subsystem_filter, bool
         assert_se(sd_device_get_syspath(device, &syspath) >= 0);
 
         assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
         assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
 
         assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
         assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
         assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
 
         if (subsystem_filter) {
                 assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
@@ -98,14 +99,14 @@ static void test_subsystem_filter(sd_device *device) {
         assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
 
         assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
         assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
 
         assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
         assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
         assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0);
         assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
 
         assert_se(sd_device_enumerator_new(&e) >= 0);
         assert_se(sd_device_enumerator_add_match_subsystem(e, subsystem, false) >= 0);
@@ -115,6 +116,9 @@ static void test_subsystem_filter(sd_device *device) {
                 assert_se(sd_device_get_syspath(d, &p) >= 0);
                 assert_se(sd_device_get_subsystem(d, &s) >= 0);
 
+                assert_se(device_add_property(d, "ACTION", "add") >= 0);
+                assert_se(device_add_property(d, "SEQNUM", "10") >= 0);
+
                 log_device_debug(d, "Sending device subsystem:%s syspath:%s", s, p);
                 assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
         }
@@ -135,14 +139,14 @@ static void test_tag_filter(sd_device *device) {
         assert_se(sd_device_get_syspath(device, &syspath) >= 0);
 
         assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
         assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
 
         assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
         assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
         assert_se(sd_device_monitor_filter_add_match_tag(monitor_client, "TEST_SD_DEVICE_MONITOR") >= 0);
         assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
 
         assert_se(sd_device_enumerator_new(&e) >= 0);
         FOREACH_DEVICE(e, d) {
@@ -150,6 +154,9 @@ static void test_tag_filter(sd_device *device) {
 
                 assert_se(sd_device_get_syspath(d, &p) >= 0);
 
+                assert_se(device_add_property(d, "ACTION", "add") >= 0);
+                assert_se(device_add_property(d, "SEQNUM", "10") >= 0);
+
                 log_device_debug(d, "Sending device syspath:%s", p);
                 assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
         }
@@ -163,27 +170,23 @@ static void test_tag_filter(sd_device *device) {
 static void test_sysattr_filter(sd_device *device, const char *sysattr) {
         _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
-        const char *syspath, *subsystem, *sysattr_value;
+        const char *syspath, *sysattr_value;
         sd_device *d;
 
         log_device_info(device, "/* %s(%s) */", __func__, sysattr);
 
         assert_se(sd_device_get_syspath(device, &syspath) >= 0);
-        assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
         assert_se(sd_device_get_sysattr_value(device, sysattr, &sysattr_value) >= 0);
 
         assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
         assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
 
         assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
         assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
-        /* The sysattr filter is not implemented in BPF yet, so the below device_monito_send_device()
-         * may cause EAGAIN. So, let's also filter devices with subsystem. */
-        assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0);
         assert_se(sd_device_monitor_filter_add_match_sysattr(monitor_client, sysattr, sysattr_value, true) >= 0);
         assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
 
         assert_se(sd_device_enumerator_new(&e) >= 0);
         assert_se(sd_device_enumerator_add_match_sysattr(e, sysattr, sysattr_value, false) >= 0);
@@ -192,8 +195,16 @@ static void test_sysattr_filter(sd_device *device, const char *sysattr) {
 
                 assert_se(sd_device_get_syspath(d, &p) >= 0);
 
+                assert_se(device_add_property(d, "ACTION", "add") >= 0);
+                assert_se(device_add_property(d, "SEQNUM", "10") >= 0);
+
                 log_device_debug(d, "Sending device syspath:%s", p);
                 assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
+
+                /* The sysattr filter is not implemented in BPF yet. So, sending multiple devices may fills up
+                 * buffer and device_monitor_send_device() may return EAGAIN. Let's send one device here,
+                 * which should be filtered out by the receiver. */
+                break;
         }
 
         log_device_info(device, "Sending device syspath:%s", syspath);
@@ -205,39 +216,46 @@ static void test_sysattr_filter(sd_device *device, const char *sysattr) {
 static void test_parent_filter(sd_device *device) {
         _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
-        const char *syspath, *subsystem;
+        const char *syspath, *parent_syspath;
         sd_device *parent, *d;
         int r;
 
         log_device_info(device, "/* %s */", __func__);
 
         assert_se(sd_device_get_syspath(device, &syspath) >= 0);
-        assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
         r = sd_device_get_parent(device, &parent);
         if (r < 0)
                 return (void) log_device_info(device, "Device does not have parent, skipping.");
+        assert_se(sd_device_get_syspath(parent, &parent_syspath) >= 0);
 
         assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
         assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
 
         assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
         assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
-        /* The parent filter is not implemented in BPF yet, so the below device_monito_send_device()
-         * may cause EAGAIN. So, let's also filter devices with subsystem. */
-        assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0);
         assert_se(sd_device_monitor_filter_add_match_parent(monitor_client, parent, true) >= 0);
         assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
 
         assert_se(sd_device_enumerator_new(&e) >= 0);
         FOREACH_DEVICE(e, d) {
                 const char *p;
 
                 assert_se(sd_device_get_syspath(d, &p) >= 0);
+                if (path_startswith(p, parent_syspath))
+                        continue;
+
+                assert_se(device_add_property(d, "ACTION", "add") >= 0);
+                assert_se(device_add_property(d, "SEQNUM", "10") >= 0);
 
                 log_device_debug(d, "Sending device syspath:%s", p);
                 assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
+
+                /* The parent filter is not implemented in BPF yet. So, sending multiple devices may fills up
+                 * buffer and device_monitor_send_device() may return EAGAIN. Let's send one device here,
+                 * which should be filtered out by the receiver. */
+                break;
         }
 
         log_device_info(device, "Sending device syspath:%s", syspath);
@@ -255,13 +273,13 @@ static void test_sd_device_monitor_filter_remove(sd_device *device) {
         assert_se(sd_device_get_syspath(device, &syspath) >= 0);
 
         assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
         assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
 
         assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+        assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
         assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
         assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
-        assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
 
         assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, "hoge", NULL) >= 0);
         assert_se(sd_device_monitor_filter_update(monitor_client) >= 0);
index 91e358e4645240f888e3c0c3b306072d524e922f..8172c64e456d31d60dc40b583b2099048c54d9e4 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <ctype.h>
 #include <fcntl.h>
+#include <unistd.h>
 
 #include "device-enumerator-private.h"
 #include "device-internal.h"
 #include "hashmap.h"
 #include "nulstr-util.h"
 #include "path-util.h"
+#include "rm-rf.h"
 #include "string-util.h"
 #include "tests.h"
 #include "time-util.h"
+#include "tmpfile-util.h"
 
 static void test_sd_device_one(sd_device *d) {
         _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
@@ -310,6 +313,49 @@ TEST(sd_device_enumerator_filter_subsystem) {
         assert_se(n_new_dev + n_removed_dev <= 10);
 }
 
+TEST(sd_device_enumerator_add_match_sysattr) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *dev;
+        int ifindex;
+
+        assert_se(sd_device_enumerator_new(&e) >= 0);
+        assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
+        assert_se(sd_device_enumerator_add_match_subsystem(e, "net", true) >= 0);
+        assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true) >= 0);
+        assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "hoge", true) >= 0);
+        assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "foo", true) >= 0);
+        assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "bar", false) >= 0);
+        assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "baz", false) >= 0);
+
+        dev = sd_device_enumerator_get_device_first(e);
+        assert_se(dev);
+        assert_se(sd_device_get_ifindex(dev, &ifindex) >= 0);
+        assert_se(ifindex == 1);
+
+        assert_se(!sd_device_enumerator_get_device_next(e));
+}
+
+TEST(sd_device_enumerator_add_match_property) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *dev;
+        int ifindex;
+
+        assert_se(sd_device_enumerator_new(&e) >= 0);
+        assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
+        assert_se(sd_device_enumerator_add_match_subsystem(e, "net", true) >= 0);
+        assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true) >= 0);
+        assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", "1*") >= 0);
+        assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", "hoge") >= 0);
+        assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", NULL) >= 0);
+        assert_se(sd_device_enumerator_add_match_property(e, "AAAAA", "BBBB") >= 0);
+        assert_se(sd_device_enumerator_add_match_property(e, "FOOOO", NULL) >= 0);
+
+        dev = sd_device_enumerator_get_device_first(e);
+        assert_se(dev);
+        assert_se(sd_device_get_ifindex(dev, &ifindex) >= 0);
+        assert_se(ifindex == 1);
+}
+
 TEST(sd_device_new_from_nulstr) {
         const char *devlinks =
                 "/dev/disk/by-partuuid/1290d63a-42cc-4c71-b87c-xxxxxxxxxxxx\0"
@@ -363,4 +409,50 @@ TEST(sd_device_new_from_nulstr) {
         }
 }
 
+TEST(sd_device_new_from_path) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
+        sd_device *dev;
+        int r;
+
+        assert_se(mkdtemp_malloc("/tmp/test-sd-device.XXXXXXX", &tmpdir) >= 0);
+
+        assert_se(sd_device_enumerator_new(&e) >= 0);
+        assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
+        assert_se(sd_device_enumerator_add_match_subsystem(e, "block", true) >= 0);
+        assert_se(sd_device_enumerator_add_nomatch_sysname(e, "loop*") >= 0);
+        assert_se(sd_device_enumerator_add_match_property(e, "DEVNAME", "*") >= 0);
+
+        FOREACH_DEVICE(e, dev) {
+                _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+                const char *syspath, *devpath, *sysname, *s;
+                _cleanup_free_ char *path = NULL;
+
+                assert_se(sd_device_get_sysname(dev, &sysname) >= 0);
+
+                log_debug("%s(%s)", __func__, sysname);
+
+                assert_se(sd_device_get_syspath(dev, &syspath) >= 0);
+                assert_se(sd_device_new_from_path(&d, syspath) >= 0);
+                assert_se(sd_device_get_syspath(d, &s) >= 0);
+                assert_se(streq(s, syspath));
+                d = sd_device_unref(d);
+
+                assert_se(sd_device_get_devname(dev, &devpath) >= 0);
+                r = sd_device_new_from_path(&d, devpath);
+                if (r >= 0) {
+                        assert_se(sd_device_get_syspath(d, &s) >= 0);
+                        assert_se(streq(s, syspath));
+                        d = sd_device_unref(d);
+                } else
+                        assert_se(r == -ENODEV || ERRNO_IS_PRIVILEGE(r));
+
+                assert_se(path = path_join(tmpdir, sysname));
+                assert_se(symlink(syspath, path) >= 0);
+                assert_se(sd_device_new_from_path(&d, path) >= 0);
+                assert_se(sd_device_get_syspath(d, &s) >= 0);
+                assert_se(streq(s, syspath));
+        }
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index df48b3ce0466d9a80d0d9ae05c08f690801ddb38..027a8dffa1ef76b4b4ec5f7c56495adf3c9e7109 100644 (file)
@@ -420,6 +420,8 @@ fail:
 }
 
 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free);
+#define PROTECT_EVENT(e)                                                \
+        _unused_ _cleanup_(sd_event_unrefp) sd_event *_ref = sd_event_ref(e);
 
 _public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) {
         if (s)
@@ -2139,12 +2141,9 @@ static sd_event_source* event_source_free(sd_event_source *s) {
          * we still retain a valid event source object after
          * the callback. */
 
-        if (s->dispatching) {
-                if (s->type == SOURCE_IO)
-                        source_io_unregister(s);
-
+        if (s->dispatching)
                 source_disconnect(s);
-        else
+        else
                 source_free(s);
 
         return NULL;
@@ -3542,8 +3541,8 @@ static int process_inotify(sd_event *e) {
 }
 
 static int source_dispatch(sd_event_source *s) {
-        _cleanup_(sd_event_unrefp) sd_event *saved_event = NULL;
         EventSourceType saved_type;
+        sd_event *saved_event;
         int r = 0;
 
         assert(s);
@@ -3555,7 +3554,8 @@ static int source_dispatch(sd_event_source *s) {
 
         /* Similarly, store a reference to the event loop object, so that we can still access it after the
          * callback might have invalidated/disconnected the event source. */
-        saved_event = sd_event_ref(s->event);
+        saved_event = s->event;
+        PROTECT_EVENT(saved_event);
 
         /* Check if we hit the ratelimit for this event source, and if so, let's disable it. */
         assert(!s->ratelimited);
@@ -3756,7 +3756,7 @@ static int dispatch_exit(sd_event *e) {
                 return 0;
         }
 
-        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+        PROTECT_EVENT(e);
         e->iteration++;
         e->state = SD_EVENT_EXITING;
         r = source_dispatch(p);
@@ -3850,7 +3850,7 @@ _public_ int sd_event_prepare(sd_event *e) {
         assert_return(!e->default_event_ptr || e->tid == gettid(), -EREMOTEIO);
 
         /* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
-        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+        PROTECT_EVENT(e);
 
         if (e->exit_requested)
                 goto pending;
@@ -4180,7 +4180,7 @@ _public_ int sd_event_dispatch(sd_event *e) {
 
         p = event_next_pending(e);
         if (p) {
-                _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+                PROTECT_EVENT(e);
 
                 e->state = SD_EVENT_RUNNING;
                 r = source_dispatch(p);
@@ -4232,7 +4232,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
         }
 
         /* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
-        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+        PROTECT_EVENT(e);
 
         r = sd_event_prepare(e);
         if (r == 0)
@@ -4262,7 +4262,7 @@ _public_ int sd_event_loop(sd_event *e) {
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
 
-        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+        PROTECT_EVENT(e);
 
         while (e->state != SD_EVENT_FINISHED) {
                 r = sd_event_run(e, UINT64_MAX);
index bbe7c78306f683bb771c440406a4847fe532cd53..1fad2a8146a06c9a66adb390ab98c9c655f0396c 100644 (file)
@@ -204,23 +204,32 @@ static int journal_file_set_online(JournalFile *f) {
                         wait = false;
                         break;
 
-                case OFFLINE_SYNCING:
-                        if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_CANCEL))
-                                continue;
+                case OFFLINE_SYNCING: {
+                                OfflineState tmp_state = OFFLINE_SYNCING;
+                                if (!__atomic_compare_exchange_n(&f->offline_state, &tmp_state, OFFLINE_CANCEL,
+                                    false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+                                        continue;
+                        }
                         /* Canceled syncing prior to offlining, no need to wait. */
                         wait = false;
                         break;
 
-                case OFFLINE_AGAIN_FROM_SYNCING:
-                        if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_CANCEL))
-                                continue;
+                case OFFLINE_AGAIN_FROM_SYNCING: {
+                                OfflineState tmp_state = OFFLINE_AGAIN_FROM_SYNCING;
+                                if (!__atomic_compare_exchange_n(&f->offline_state, &tmp_state, OFFLINE_CANCEL,
+                                    false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+                                        continue;
+                        }
                         /* Canceled restart from syncing, no need to wait. */
                         wait = false;
                         break;
 
-                case OFFLINE_AGAIN_FROM_OFFLINING:
-                        if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_CANCEL))
-                                continue;
+                case OFFLINE_AGAIN_FROM_OFFLINING: {
+                                OfflineState tmp_state = OFFLINE_AGAIN_FROM_OFFLINING;
+                                if (!__atomic_compare_exchange_n(&f->offline_state, &tmp_state, OFFLINE_CANCEL,
+                                    false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+                                        continue;
+                        }
                         /* Canceled restart from offlining, must wait for offlining to complete however. */
                         _fallthrough_;
                 default: {
@@ -1810,7 +1819,7 @@ static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
         if (o->object.type != OBJECT_ENTRY)
                 return -EINVAL;
 
-        __sync_synchronize();
+        __atomic_thread_fence(__ATOMIC_SEQ_CST);
 
         /* Link up the entry itself */
         r = link_entry_into_array(f,
@@ -1910,7 +1919,7 @@ void journal_file_post_change(JournalFile *f) {
          * trigger IN_MODIFY by truncating the journal file to its
          * current size which triggers IN_MODIFY. */
 
-        __sync_synchronize();
+        __atomic_thread_fence(__ATOMIC_SEQ_CST);
 
         if (ftruncate(f->fd, f->last_stat.st_size) < 0)
                 log_debug_errno(errno, "Failed to truncate file to its own size: %m");
index e1c40702d38748a6b80cebf4533f0fdf838b259e..db53dd7debb9205ac968f194557cf659c21ee93c 100644 (file)
@@ -58,7 +58,8 @@ retry:
 
         fd_inc_sndbuf(fd, SNDBUF_SIZE);
 
-        if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
+        if (!__atomic_compare_exchange_n(&fd_plus_one, &(int){0}, fd+1,
+                false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
                 safe_close(fd);
                 goto retry;
         }
@@ -66,6 +67,16 @@ retry:
         return fd;
 }
 
+int journal_fd_nonblock(bool nonblock) {
+        int r;
+
+        r = journal_fd();
+        if (r < 0)
+                return r;
+
+        return fd_nonblock(r, nonblock);
+}
+
 #if VALGRIND
 void close_journal_fd(void) {
         /* Be nice to valgrind. This is not atomic. This must be used only in tests. */
@@ -318,7 +329,7 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
         if (errno == ENOENT)
                 return 0;
 
-        if (!IN_SET(errno, EMSGSIZE, ENOBUFS))
+        if (!IN_SET(errno, EMSGSIZE, ENOBUFS, EAGAIN))
                 return -errno;
 
         /* Message doesn't fit... Let's dump the data in a memfd or
index cf8b199297cf6a24f16a1a7d359dbbf8bc348350..558d39a8c08236a46a8c95b3aaa98090a3571614 100644 (file)
@@ -1,6 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <stdbool.h>
+
+int journal_fd_nonblock(bool nonblock);
+
 #if VALGRIND
 void close_journal_fd(void);
 #else
index 74be75d4067f3eae2295d226b62ecb30cffe83f5..aeb4b8a68680dd52dbea8af9e8dc688099947dd8 100644 (file)
@@ -12,7 +12,7 @@
 typedef struct GenericNetlinkFamily {
         sd_netlink *genl;
 
-        const NLTypeSystem *type_system;
+        const NLAPolicySet *policy_set;
 
         uint16_t id; /* a.k.a nlmsg_type */
         char *name;
@@ -56,14 +56,14 @@ void genl_clear_family(sd_netlink *nl) {
 static int genl_family_new_unsupported(
                 sd_netlink *nl,
                 const char *family_name,
-                const NLTypeSystem *type_system) {
+                const NLAPolicySet *policy_set) {
 
         _cleanup_(genl_family_freep) GenericNetlinkFamily *f = NULL;
         int r;
 
         assert(nl);
         assert(family_name);
-        assert(type_system);
+        assert(policy_set);
 
         /* 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. */
@@ -73,7 +73,7 @@ static int genl_family_new_unsupported(
                 return -ENOMEM;
 
         *f = (GenericNetlinkFamily) {
-                .type_system = type_system,
+                .policy_set = policy_set,
         };
 
         f->name = strdup(family_name);
@@ -92,7 +92,7 @@ static int genl_family_new_unsupported(
 static int genl_family_new(
                 sd_netlink *nl,
                 const char *expected_family_name,
-                const NLTypeSystem *type_system,
+                const NLAPolicySet *policy_set,
                 sd_netlink_message *message,
                 const GenericNetlinkFamily **ret) {
 
@@ -103,7 +103,7 @@ static int genl_family_new(
 
         assert(nl);
         assert(expected_family_name);
-        assert(type_system);
+        assert(policy_set);
         assert(message);
         assert(ret);
 
@@ -112,7 +112,7 @@ static int genl_family_new(
                 return -ENOMEM;
 
         *f = (GenericNetlinkFamily) {
-                .type_system = type_system,
+                .policy_set = policy_set,
         };
 
         r = sd_genl_message_get_family_name(nl, message, &family_name);
@@ -205,13 +205,13 @@ static int genl_family_new(
         return 0;
 }
 
-static const NLTypeSystem *genl_family_get_type_system(const GenericNetlinkFamily *family) {
+static const NLAPolicySet *genl_family_get_policy_set(const GenericNetlinkFamily *family) {
         assert(family);
 
-        if (family->type_system)
-                return family->type_system;
+        if (family->policy_set)
+                return family->policy_set;
 
-        return genl_get_type_system_by_name(family->name);
+        return genl_get_policy_set_by_name(family->name);
 }
 
 static int genl_message_new(
@@ -221,7 +221,7 @@ static int genl_message_new(
                 sd_netlink_message **ret) {
 
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        const NLTypeSystem *type_system;
+        const NLAPolicySet *policy_set;
         int r;
 
         assert(nl);
@@ -229,11 +229,11 @@ static int genl_message_new(
         assert(family);
         assert(ret);
 
-        type_system = genl_family_get_type_system(family);
-        if (!type_system)
+        policy_set = genl_family_get_policy_set(family);
+        if (!policy_set)
                 return -EOPNOTSUPP;
 
-        r = message_new_full(nl, family->id, type_system,
+        r = message_new_full(nl, family->id, policy_set,
                              sizeof(struct genlmsghdr) + family->additional_header_size, &m);
         if (r < 0)
                 return r;
@@ -254,7 +254,7 @@ static int genl_family_get_by_name_internal(
                 const GenericNetlinkFamily **ret) {
 
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        const NLTypeSystem *type_system;
+        const NLAPolicySet *policy_set;
         int r;
 
         assert(nl);
@@ -263,8 +263,8 @@ static int genl_family_get_by_name_internal(
         assert(name);
         assert(ret);
 
-        type_system = genl_get_type_system_by_name(name);
-        if (!type_system)
+        policy_set = genl_get_policy_set_by_name(name);
+        if (!policy_set)
                 return -EOPNOTSUPP;
 
         r = genl_message_new(nl, ctrl, CTRL_CMD_GETFAMILY, &req);
@@ -276,11 +276,11 @@ static int genl_family_get_by_name_internal(
                 return r;
 
         if (sd_netlink_call(nl, req, 0, &reply) < 0) {
-                (void) genl_family_new_unsupported(nl, name, type_system);
+                (void) genl_family_new_unsupported(nl, name, policy_set);
                 return -EOPNOTSUPP;
         }
 
-        return genl_family_new(nl, name, type_system, reply, ret);
+        return genl_family_new(nl, name, policy_set, reply, ret);
 }
 
 static int genl_family_get_by_name(sd_netlink *nl, const char *name, const GenericNetlinkFamily **ret) {
@@ -335,10 +335,10 @@ static int genl_family_get_by_id(sd_netlink *nl, uint16_t id, const GenericNetli
         return -ENOENT;
 }
 
-int genl_get_type_system_and_header_size(
+int genl_get_policy_set_and_header_size(
                 sd_netlink *nl,
                 uint16_t id,
-                const NLTypeSystem **ret_type_system,
+                const NLAPolicySet **ret_policy_set,
                 size_t *ret_header_size) {
 
         const GenericNetlinkFamily *f;
@@ -351,14 +351,14 @@ int genl_get_type_system_and_header_size(
         if (r < 0)
                 return r;
 
-        if (ret_type_system) {
-                const NLTypeSystem *t;
+        if (ret_policy_set) {
+                const NLAPolicySet *p;
 
-                t = genl_family_get_type_system(f);
-                if (!t)
+                p = genl_family_get_policy_set(f);
+                if (!p)
                         return -EOPNOTSUPP;
 
-                *ret_type_system = t;
+                *ret_policy_set = p;
         }
         if (ret_header_size)
                 *ret_header_size = sizeof(struct genlmsghdr) + f->additional_header_size;
@@ -420,7 +420,7 @@ _public_ int sd_genl_message_get_command(sd_netlink *nl, sd_netlink_message *m,
         if (r < 0)
                 return r;
 
-        r = genl_get_type_system_and_header_size(nl, nlmsg_type, NULL, &size);
+        r = genl_get_policy_set_and_header_size(nl, nlmsg_type, NULL, &size);
         if (r < 0)
                 return r;
 
index 497ffe9112ce856172d48670f7a111ff25fa5f29..514f22511c342dab8109cd7627736d5b0ca04a8b 100644 (file)
@@ -109,7 +109,7 @@ struct netlink_attribute {
 };
 
 struct netlink_container {
-        const struct NLTypeSystem *type_system; /* the type system of the container */
+        const struct NLAPolicySet *policy_set; /* the policy set of the container */
         size_t offset; /* offset from hdr to the start of the container */
         struct netlink_attribute *attributes;
         uint16_t max_attribute; /* the maximum attribute in container */
@@ -133,7 +133,7 @@ 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,
+                const NLAPolicySet *policy_set,
                 size_t header_size,
                 sd_netlink_message **ret);
 int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type);
@@ -176,26 +176,38 @@ int netlink_add_match_internal(
 /* nfnl */
 /* TODO: to be exported later */
 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_send_batch(
+                sd_netlink *nfnl,
+                sd_netlink_message **messages,
+                size_t msgcount,
+                uint32_t **ret_serials);
+int sd_nfnl_call_batch(
+                sd_netlink *nfnl,
+                sd_netlink_message **messages,
+                size_t n_messages,
+                uint64_t usec,
+                sd_netlink_message ***ret_messages);
+int sd_nfnl_message_new(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int nfproto,
+                uint16_t subsys,
+                uint16_t msg_type,
+                uint16_t flags);
 int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret,
-                                  int family, const char *table);
+                                  int nfproto, 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,
+                                      int nfproto, const char *table, const char *chain,
                                       const char *type, uint8_t hook, int prio);
 int sd_nfnl_nft_message_new_rule(sd_netlink *nfnl, sd_netlink_message **ret,
-                                 int family, const char *table, const char *chain);
+                                 int nfproto, const char *table, const char *chain);
 int sd_nfnl_nft_message_new_set(sd_netlink *nfnl, sd_netlink_message **ret,
-                                int family, const char *table, const char *set_name,
+                                int nfproto, const char *table, const char *set_name,
                                 uint32_t setid, uint32_t klen);
-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_del_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
-                                           int family, const char *table, const char *set_name);
-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_end(sd_netlink_message *m);
+int sd_nfnl_nft_message_new_setelems(sd_netlink *nfnl, sd_netlink_message **ret,
+                                     int add, int nfproto, const char *table, const char *set_name);
+int sd_nfnl_nft_message_append_setelem(sd_netlink_message *m,
+                                       uint32_t index,
+                                       const void *key, size_t key_len,
+                                       const void *data, size_t data_len,
+                                       uint32_t flags);
index 6f32167772346a37f90f142e6c59ce6f673b6fdc..582f623efe70fe66d8659801fac064177f8df3e5 100644 (file)
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <netinet/in.h>
-#include <linux/if_addrlabel.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nf_tables.h>
-#include <linux/nexthop.h>
-#include <stdbool.h>
-#include <unistd.h>
+#include <linux/netfilter.h>
 
 #include "sd-netlink.h"
 
-#include "format-util.h"
+#include "io-util.h"
 #include "netlink-internal.h"
 #include "netlink-types.h"
-#include "socket-util.h"
+#include "netlink-util.h"
+
+static bool nfproto_is_valid(int nfproto) {
+        return IN_SET(nfproto,
+                      NFPROTO_UNSPEC,
+                      NFPROTO_INET,
+                      NFPROTO_IPV4,
+                      NFPROTO_ARP,
+                      NFPROTO_NETDEV,
+                      NFPROTO_BRIDGE,
+                      NFPROTO_IPV6,
+                      NFPROTO_DECNET);
+}
 
-static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t msg_type, uint16_t flags) {
+int sd_nfnl_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int nfproto, uint16_t subsys, uint16_t msg_type, uint16_t flags) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
         assert_return(nfnl, -EINVAL);
         assert_return(ret, -EINVAL);
+        assert_return(nfproto_is_valid(nfproto), -EINVAL);
+        assert_return(NFNL_MSG_TYPE(msg_type) == msg_type, -EINVAL);
 
-        r = message_new(nfnl, &m, NFNL_SUBSYS_NFTABLES << 8 | msg_type);
+        r = message_new(nfnl, &m, subsys << 8 | msg_type);
         if (r < 0)
                 return r;
 
         m->hdr->nlmsg_flags |= flags;
 
         *(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
-                .nfgen_family = family,
+                .nfgen_family = nfproto,
                 .version = NFNETLINK_V0,
-                .res_id = nfnl->serial,
         };
 
         *ret = TAKE_PTR(m);
         return 0;
 }
 
-static int nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, uint16_t msg_type) {
+static int nfnl_message_set_res_id(sd_netlink_message *m, uint16_t res_id) {
+        struct nfgenmsg *nfgen;
+
+        assert(m);
+        assert(m->hdr);
+
+        nfgen = NLMSG_DATA(m->hdr);
+        nfgen->res_id = htobe16(res_id);
+
+        return 0;
+}
+
+static int nfnl_message_get_subsys(sd_netlink_message *m, uint16_t *ret) {
+        uint16_t t;
+        int r;
+
+        assert(m);
+        assert(ret);
+
+        r = sd_netlink_message_get_type(m, &t);
+        if (r < 0)
+                return r;
+
+        *ret = NFNL_SUBSYS_ID(t);
+        return 0;
+}
+
+static int nfnl_message_new_batch(sd_netlink *nfnl, sd_netlink_message **ret, uint16_t subsys, uint16_t msg_type) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = message_new(nfnl, &m, NFNL_SUBSYS_NONE << 8 | msg_type);
+        assert_return(nfnl, -EINVAL);
+        assert_return(ret, -EINVAL);
+        assert_return(NFNL_MSG_TYPE(msg_type) == msg_type, -EINVAL);
+
+        r = sd_nfnl_message_new(nfnl, &m, NFPROTO_UNSPEC, NFNL_SUBSYS_NONE, msg_type, 0);
         if (r < 0)
                 return r;
 
-        *(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
-                .nfgen_family = AF_UNSPEC,
-                .version = NFNETLINK_V0,
-                .res_id = NFNL_SUBSYS_NFTABLES,
-        };
+        r = nfnl_message_set_res_id(m, subsys);
+        if (r < 0)
+                return r;
 
         *ret = TAKE_PTR(m);
         return 0;
 }
 
-int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret) {
-        return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_BEGIN);
+int sd_nfnl_send_batch(
+                sd_netlink *nfnl,
+                sd_netlink_message **messages,
+                size_t n_messages,
+                uint32_t **ret_serials) {
+
+        /* iovs refs batch_begin and batch_end, hence, free iovs first, then free batch_begin and batch_end. */
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *batch_begin = NULL, *batch_end = NULL;
+        _cleanup_free_ struct iovec *iovs = NULL;
+        _cleanup_free_ uint32_t *serials = NULL;
+        uint16_t subsys;
+        ssize_t k;
+        size_t c = 0;
+        int r;
+
+        assert_return(nfnl, -EINVAL);
+        assert_return(!netlink_pid_changed(nfnl), -ECHILD);
+        assert_return(messages, -EINVAL);
+        assert_return(n_messages > 0, -EINVAL);
+
+        iovs = new(struct iovec, n_messages + 2);
+        if (!iovs)
+                return -ENOMEM;
+
+        if (ret_serials) {
+                serials = new(uint32_t, n_messages);
+                if (!serials)
+                        return -ENOMEM;
+        }
+
+        r = nfnl_message_get_subsys(messages[0], &subsys);
+        if (r < 0)
+                return r;
+
+        r = nfnl_message_new_batch(nfnl, &batch_begin, subsys, NFNL_MSG_BATCH_BEGIN);
+        if (r < 0)
+                return r;
+
+        netlink_seal_message(nfnl, batch_begin);
+        iovs[c++] = IOVEC_MAKE(batch_begin->hdr, batch_begin->hdr->nlmsg_len);
+
+        for (size_t i = 0; i < n_messages; i++) {
+                uint16_t s;
+
+                r = nfnl_message_get_subsys(messages[i], &s);
+                if (r < 0)
+                        return r;
+
+                if (s != subsys)
+                        return -EINVAL;
+
+                netlink_seal_message(nfnl, messages[i]);
+                if (serials)
+                        serials[i] = message_get_serial(messages[i]);
+
+                /* It seems that the kernel accepts an arbitrary number. Let's set the serial of the
+                 * first message. */
+                nfnl_message_set_res_id(messages[i], message_get_serial(batch_begin));
+
+                iovs[c++] = IOVEC_MAKE(messages[i]->hdr, messages[i]->hdr->nlmsg_len);
+        }
+
+        r = nfnl_message_new_batch(nfnl, &batch_end, subsys, NFNL_MSG_BATCH_END);
+        if (r < 0)
+                return r;
+
+        netlink_seal_message(nfnl, batch_end);
+        iovs[c++] = IOVEC_MAKE(batch_end->hdr, batch_end->hdr->nlmsg_len);
+
+        assert(c == n_messages + 2);
+        k = writev(nfnl->fd, iovs, n_messages + 2);
+        if (k < 0)
+                return -errno;
+
+        if (ret_serials)
+                *ret_serials = TAKE_PTR(serials);
+
+        return 0;
 }
 
-int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret) {
-        return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_END);
+int sd_nfnl_call_batch(
+                sd_netlink *nfnl,
+                sd_netlink_message **messages,
+                size_t n_messages,
+                uint64_t usec,
+                sd_netlink_message ***ret_messages) {
+
+        _cleanup_free_ sd_netlink_message **replies = NULL;
+        _cleanup_free_ uint32_t *serials = NULL;
+        int k, r;
+
+        assert_return(nfnl, -EINVAL);
+        assert_return(!netlink_pid_changed(nfnl), -ECHILD);
+        assert_return(messages, -EINVAL);
+        assert_return(n_messages > 0, -EINVAL);
+
+        if (ret_messages) {
+                replies = new0(sd_netlink_message*, n_messages);
+                if (!replies)
+                        return -ENOMEM;
+        }
+
+        r = sd_nfnl_send_batch(nfnl, messages, n_messages, &serials);
+        if (r < 0)
+                return r;
+
+        for (size_t i = 0; i < n_messages; i++) {
+                k = sd_netlink_read(nfnl, serials[i], usec, ret_messages ? replies + i : NULL);
+                if (k < 0 && r >= 0)
+                        r = k;
+        }
+        if (r < 0)
+                return r;
+
+        if (ret_messages)
+                *ret_messages = TAKE_PTR(replies);
+
+        return 0;
 }
 
 int sd_nfnl_nft_message_new_basechain(
                 sd_netlink *nfnl,
                 sd_netlink_message **ret,
-                int family,
+                int nfproto,
                 const char *table,
                 const char *chain,
                 const char *type,
@@ -77,7 +228,7 @@ int sd_nfnl_nft_message_new_basechain(
         _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);
+        r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWCHAIN, NLM_F_CREATE);
         if (r < 0)
                 return r;
 
@@ -113,37 +264,16 @@ int sd_nfnl_nft_message_new_basechain(
         return 0;
 }
 
-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);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_string(m, NFTA_TABLE_NAME, table);
-        if (r < 0)
-                return r;
-
-        *ret = TAKE_PTR(m);
-        return r;
-}
-
 int sd_nfnl_nft_message_new_table(
                 sd_netlink *nfnl,
                 sd_netlink_message **ret,
-                int family,
+                int nfproto,
                 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 | NLM_F_EXCL);
+        r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL);
         if (r < 0)
                 return r;
 
@@ -158,14 +288,14 @@ int sd_nfnl_nft_message_new_table(
 int sd_nfnl_nft_message_new_rule(
                 sd_netlink *nfnl,
                 sd_netlink_message **ret,
-                int family,
+                int nfproto,
                 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);
+        r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWRULE, NLM_F_CREATE);
         if (r < 0)
                 return r;
 
@@ -184,7 +314,7 @@ int sd_nfnl_nft_message_new_rule(
 int sd_nfnl_nft_message_new_set(
                 sd_netlink *nfnl,
                 sd_netlink_message **ret,
-                int family,
+                int nfproto,
                 const char *table,
                 const char *set_name,
                 uint32_t set_id,
@@ -193,7 +323,7 @@ int sd_nfnl_nft_message_new_set(
         _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);
+        r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSET, NLM_F_CREATE);
         if (r < 0)
                 return r;
 
@@ -217,17 +347,21 @@ int sd_nfnl_nft_message_new_set(
         return r;
 }
 
-int sd_nfnl_nft_message_new_setelems_begin(
+int sd_nfnl_nft_message_new_setelems(
                 sd_netlink *nfnl,
                 sd_netlink_message **ret,
-                int family,
+                int add, /* boolean */
+                int nfproto,
                 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);
+        if (add)
+                r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSETELEM, NLM_F_CREATE);
+        else
+                r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_DELSETELEM, 0);
         if (r < 0)
                 return r;
 
@@ -239,93 +373,48 @@ int sd_nfnl_nft_message_new_setelems_begin(
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
-        if (r < 0)
-                return r;
-
         *ret = TAKE_PTR(m);
         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) {
-
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        int r;
-
-        r = nft_message_new(nfnl, &m, family, NFT_MSG_DELSETELEM, 0);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_TABLE, table);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_SET, set_name);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
-        if (r < 0)
-                return r;
-
-        *ret = TAKE_PTR(m);
-        return r;
-}
-
-static int add_data(sd_netlink_message *m, uint16_t attr, const void *data, uint32_t dlen) {
-        int r;
-
-        r = sd_netlink_message_open_container(m, attr);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_data(m, NFTA_DATA_VALUE, data, dlen);
-        if (r < 0)
-                return r;
-
-        return sd_netlink_message_close_container(m); /* attr */
-}
-
-int sd_nfnl_nft_message_add_setelem(
+int sd_nfnl_nft_message_append_setelem(
                 sd_netlink_message *m,
-                uint32_t num,
+                uint32_t index,
                 const void *key,
-                uint32_t klen,
+                size_t key_len,
                 const void *data,
-                uint32_t dlen) {
+                size_t data_len,
+                uint32_t flags) {
 
         int r;
 
-        r = sd_netlink_message_open_array(m, num);
+        r = sd_netlink_message_open_array(m, index);
         if (r < 0)
                 return r;
 
-        r = add_data(m, NFTA_SET_ELEM_KEY, key, klen);
+        r = sd_netlink_message_append_container_data(m, NFTA_SET_ELEM_KEY, NFTA_DATA_VALUE, key, key_len);
         if (r < 0)
                 goto cancel;
 
         if (data) {
-                r = add_data(m, NFTA_SET_ELEM_DATA, data, dlen);
+                r = sd_netlink_message_append_container_data(m, NFTA_SET_ELEM_DATA, NFTA_DATA_VALUE, data, data_len);
                 if (r < 0)
                         goto cancel;
         }
 
-        return 0;
+        if (flags != 0) {
+                r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(flags));
+                if (r < 0)
+                        goto cancel;
+        }
+
+        return sd_netlink_message_close_container(m); /* array */
 
 cancel:
-        sd_netlink_message_cancel_array(m);
+        (void) sd_netlink_message_cancel_array(m);
         return r;
 }
 
-int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m) {
-        return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
-}
-
 int sd_nfnl_socket_open(sd_netlink **ret) {
         return netlink_open_family(ret, NETLINK_NETFILTER);
 }
index 289eb41ca94423707dc76fc1406b363b6e8f67fb..0ff25f8e6c78f20de5abd2d9718632a786316052 100644 (file)
 #include "socket-util.h"
 #include "util.h"
 
+static bool rtnl_message_type_is_neigh(uint16_t type) {
+        return IN_SET(type, RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH);
+}
+
+static bool rtnl_message_type_is_route(uint16_t type) {
+        return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE);
+}
+
+static bool rtnl_message_type_is_nexthop(uint16_t type) {
+        return IN_SET(type, RTM_NEWNEXTHOP, RTM_GETNEXTHOP, RTM_DELNEXTHOP);
+}
+
+static bool rtnl_message_type_is_link(uint16_t type) {
+        return IN_SET(type,
+                      RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK,
+                      RTM_NEWLINKPROP, RTM_DELLINKPROP, RTM_GETLINKPROP);
+}
+
+static bool rtnl_message_type_is_addr(uint16_t type) {
+        return IN_SET(type, RTM_NEWADDR, RTM_GETADDR, RTM_DELADDR);
+}
+
+static bool rtnl_message_type_is_addrlabel(uint16_t type) {
+        return IN_SET(type, RTM_NEWADDRLABEL, RTM_DELADDRLABEL, RTM_GETADDRLABEL);
+}
+
+static bool rtnl_message_type_is_routing_policy_rule(uint16_t type) {
+        return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE);
+}
+
+static bool rtnl_message_type_is_traffic_control(uint16_t type) {
+        return IN_SET(type,
+                      RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC,
+                      RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS);
+}
+
+static bool rtnl_message_type_is_mdb(uint16_t type) {
+        return IN_SET(type, RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB);
+}
+
 _public_ int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) {
         struct rtmsg *rtm;
 
index 34b4c23bd546732877637222c3840180508a9a83..093527a20def330467d23bc93ef620c7dbea5277 100644 (file)
@@ -46,7 +46,7 @@ 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,
+                const NLAPolicySet *policy_set,
                 size_t header_size,
                 sd_netlink_message **ret) {
 
@@ -55,7 +55,7 @@ int message_new_full(
         int r;
 
         assert(nl);
-        assert(type_system);
+        assert(policy_set);
         assert(ret);
 
         size = NLMSG_SPACE(header_size);
@@ -65,7 +65,7 @@ int message_new_full(
         if (r < 0)
                 return r;
 
-        m->containers[0].type_system = type_system;
+        m->containers[0].policy_set = policy_set;
 
         m->hdr = malloc0(size);
         if (!m->hdr)
@@ -79,19 +79,19 @@ int message_new_full(
         return 0;
 }
 
-int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type) {
-        const NLTypeSystem *type_system;
+int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type) {
+        const NLAPolicySet *policy_set;
         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);
+        r = netlink_get_policy_set_and_header_size(nl, nlmsg_type, &policy_set, &size);
         if (r < 0)
                 return r;
 
-        return message_new_full(nl, type, type_system, size, ret);
+        return message_new_full(nl, nlmsg_type, policy_set, size, ret);
 }
 
 int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret) {
@@ -146,11 +146,11 @@ _public_ sd_netlink_message* sd_netlink_message_unref(sd_netlink_message *m) {
         return NULL;
 }
 
-_public_ int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) {
+_public_ int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *ret) {
         assert_return(m, -EINVAL);
-        assert_return(type != 0, -EINVAL);
+        assert_return(ret, -EINVAL);
 
-        *type = m->hdr->nlmsg_type;
+        *ret = m->hdr->nlmsg_type;
 
         return 0;
 }
@@ -170,9 +170,8 @@ _public_ int sd_netlink_message_is_broadcast(sd_netlink_message *m) {
         return m->multicast_group != 0;
 }
 
-/* If successful the updated message will be correctly aligned, if
-   unsuccessful the old message is untouched. */
-static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) {
+/* If successful the updated message will be correctly aligned, if unsuccessful the old message is untouched. */
+static int add_rtattr(sd_netlink_message *m, uint16_t attr_type, const void *data, size_t data_length) {
         size_t message_length;
         struct nlmsghdr *new_hdr;
         struct rtattr *rta;
@@ -200,7 +199,7 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
         /* get pointer to the attribute we are about to add */
         rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
 
-        rtattr_append_attribute_internal(rta, type, data, data_length);
+        rtattr_append_attribute_internal(rta, attr_type, data, data_length);
 
         /* if we are inside containers, extend them */
         for (unsigned i = 0; i < m->n_containers; i++)
@@ -214,24 +213,24 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
         return offset;
 }
 
-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;
+static int message_attribute_has_type(sd_netlink_message *m, size_t *ret_size, uint16_t attr_type, NLAType type) {
+        const NLAPolicy *policy;
 
         assert(m);
 
-        type = type_system_get_type(m->containers[m->n_containers].type_system, attribute_type);
-        if (!type)
+        policy = policy_set_get_policy(m->containers[m->n_containers].policy_set, attr_type);
+        if (!policy)
                 return -EOPNOTSUPP;
 
-        if (type_get_type(type) != data_type)
+        if (policy_get_type(policy) != type)
                 return -EINVAL;
 
-        if (out_size)
-                *out_size = type_get_size(type);
+        if (ret_size)
+                *ret_size = policy_get_size(policy);
         return 0;
 }
 
-_public_ int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) {
+_public_ int sd_netlink_message_append_string(sd_netlink_message *m, uint16_t attr_type, const char *data) {
         size_t length, size;
         int r;
 
@@ -239,7 +238,7 @@ _public_ int sd_netlink_message_append_string(sd_netlink_message *m, unsigned sh
         assert_return(!m->sealed, -EPERM);
         assert_return(data, -EINVAL);
 
-        r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING);
+        r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_STRING);
         if (r < 0)
                 return r;
 
@@ -250,14 +249,14 @@ _public_ int sd_netlink_message_append_string(sd_netlink_message *m, unsigned sh
         } else
                 length = strlen(data);
 
-        r = add_rtattr(m, type, data, length + 1);
+        r = add_rtattr(m, attr_type, data, length + 1);
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned short type, char * const *data) {
+_public_ int sd_netlink_message_append_strv(sd_netlink_message *m, uint16_t attr_type, char * const *data) {
         size_t length, size;
         int r;
 
@@ -265,7 +264,7 @@ _public_ int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned shor
         assert_return(!m->sealed, -EPERM);
         assert_return(data, -EINVAL);
 
-        r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING);
+        r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_STRING);
         if (r < 0)
                 return r;
 
@@ -277,7 +276,7 @@ _public_ int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned shor
                 } else
                         length = strlen(*p);
 
-                r = add_rtattr(m, type, *p, length + 1);
+                r = add_rtattr(m, attr_type, *p, length + 1);
                 if (r < 0)
                         return r;
         }
@@ -285,174 +284,197 @@ _public_ int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned shor
         return 0;
 }
 
-_public_ int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) {
+_public_ int sd_netlink_message_append_flag(sd_netlink_message *m, uint16_t attr_type) {
         size_t size;
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG);
+        r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_FLAG);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, NULL, 0);
+        r = add_rtattr(m, attr_type, NULL, 0);
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) {
+_public_ int sd_netlink_message_append_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t data) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U8);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, &data, sizeof(uint8_t));
+        r = add_rtattr(m, attr_type, &data, sizeof(uint8_t));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data) {
+_public_ int sd_netlink_message_append_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t data) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U16);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, &data, sizeof(uint16_t));
+        r = add_rtattr(m, attr_type, &data, sizeof(uint16_t));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) {
+_public_ int sd_netlink_message_append_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t data) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U32);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, &data, sizeof(uint32_t));
+        r = add_rtattr(m, attr_type, &data, sizeof(uint32_t));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data) {
+_public_ int sd_netlink_message_append_u64(sd_netlink_message *m, uint16_t attr_type, uint64_t data) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U64);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U64);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, &data, sizeof(uint64_t));
+        r = add_rtattr(m, attr_type, &data, sizeof(uint64_t));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data) {
+_public_ int sd_netlink_message_append_s8(sd_netlink_message *m, uint16_t attr_type, int8_t data) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S8);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S8);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, &data, sizeof(int8_t));
+        r = add_rtattr(m, attr_type, &data, sizeof(int8_t));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data) {
+_public_ int sd_netlink_message_append_s16(sd_netlink_message *m, uint16_t attr_type, int16_t data) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S16);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S16);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, &data, sizeof(int16_t));
+        r = add_rtattr(m, attr_type, &data, sizeof(int16_t));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data) {
+_public_ int sd_netlink_message_append_s32(sd_netlink_message *m, uint16_t attr_type, int32_t data) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S32);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S32);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, &data, sizeof(int32_t));
+        r = add_rtattr(m, attr_type, &data, sizeof(int32_t));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data) {
+_public_ int sd_netlink_message_append_s64(sd_netlink_message *m, uint16_t attr_type, int64_t data) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S64);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S64);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, &data, sizeof(int64_t));
+        r = add_rtattr(m, attr_type, &data, sizeof(int64_t));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) {
+_public_ int sd_netlink_message_append_data(sd_netlink_message *m, uint16_t attr_type, const void *data, size_t len) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = add_rtattr(m, type, data, len);
+        r = add_rtattr(m, attr_type, data, len);
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data) {
+_public_ int sd_netlink_message_append_container_data(
+                sd_netlink_message *m,
+                uint16_t container_type,
+                uint16_t attr_type,
+                const void *data,
+                size_t len) {
+
+        int r;
+
+        assert_return(m, -EINVAL);
+        assert_return(!m->sealed, -EPERM);
+
+        r = sd_netlink_message_open_container(m, container_type);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_append_data(m, attr_type, data, len);
+        if (r < 0)
+                return r;
+
+        return sd_netlink_message_close_container(m);
+}
+
+int netlink_message_append_in_addr_union(sd_netlink_message *m, uint16_t attr_type, int family, const union in_addr_union *data) {
         int r;
 
         assert_return(m, -EINVAL);
@@ -460,26 +482,26 @@ int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short t
         assert_return(data, -EINVAL);
         assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_IN_ADDR);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, data, FAMILY_ADDRESS_SIZE(family));
+        r = add_rtattr(m, attr_type, data, FAMILY_ADDRESS_SIZE(family));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) {
-        return netlink_message_append_in_addr_union(m, type, AF_INET, (const union in_addr_union *) data);
+_public_ int sd_netlink_message_append_in_addr(sd_netlink_message *m, uint16_t attr_type, const struct in_addr *data) {
+        return netlink_message_append_in_addr_union(m, attr_type, AF_INET, (const union in_addr_union *) data);
 }
 
-_public_ int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) {
-        return netlink_message_append_in_addr_union(m, type, AF_INET6, (const union in_addr_union *) data);
+_public_ int sd_netlink_message_append_in6_addr(sd_netlink_message *m, uint16_t attr_type, const struct in6_addr *data) {
+        return netlink_message_append_in_addr_union(m, attr_type, AF_INET6, (const union in_addr_union *) data);
 }
 
-int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data) {
+int netlink_message_append_sockaddr_union(sd_netlink_message *m, uint16_t attr_type, const union sockaddr_union *data) {
         int r;
 
         assert_return(m, -EINVAL);
@@ -487,44 +509,44 @@ int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short
         assert_return(data, -EINVAL);
         assert_return(IN_SET(data->sa.sa_family, AF_INET, AF_INET6), -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_SOCKADDR);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_SOCKADDR);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, data, data->sa.sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
+        r = add_rtattr(m, attr_type, data, data->sa.sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, unsigned short type, const struct sockaddr_in *data) {
-        return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data);
+_public_ int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in *data) {
+        return netlink_message_append_sockaddr_union(m, attr_type, (const union sockaddr_union *) data);
 }
 
-_public_ int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, unsigned short type, const struct sockaddr_in6 *data) {
-        return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data);
+_public_ int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in6 *data) {
+        return netlink_message_append_sockaddr_union(m, attr_type, (const union sockaddr_union *) data);
 }
 
-_public_ int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) {
+_public_ int sd_netlink_message_append_ether_addr(sd_netlink_message *m, uint16_t attr_type, const struct ether_addr *data) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
         assert_return(data, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, data, ETH_ALEN);
+        r = add_rtattr(m, attr_type, data, ETH_ALEN);
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, const struct hw_addr_data *data) {
+int netlink_message_append_hw_addr(sd_netlink_message *m, uint16_t attr_type, const struct hw_addr_data *data) {
         int r;
 
         assert_return(m, -EINVAL);
@@ -532,36 +554,36 @@ int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, c
         assert_return(data, -EINVAL);
         assert_return(data->length > 0, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, data->bytes, data->length);
+        r = add_rtattr(m, attr_type, data->bytes, data->length);
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) {
+_public_ int sd_netlink_message_append_cache_info(sd_netlink_message *m, uint16_t attr_type, const struct ifa_cacheinfo *info) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
         assert_return(info, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_CACHE_INFO);
         if (r < 0)
                 return r;
 
-        r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo));
+        r = add_rtattr(m, attr_type, info, sizeof(struct ifa_cacheinfo));
         if (r < 0)
                 return r;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) {
+_public_ int sd_netlink_message_open_container(sd_netlink_message *m, uint16_t attr_type) {
         size_t size;
         int r;
 
@@ -570,12 +592,12 @@ _public_ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned s
         /* m->containers[m->n_containers + 1] is accessed both in read and write. Prevent access out of bound */
         assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
 
-        r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED);
+        r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_NESTED);
         if (r < 0) {
-                const NLTypeSystemUnion *type_system_union;
+                const NLAPolicySetUnion *policy_set_union;
                 int family;
 
-                r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION);
+                r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_NESTED_UNION_BY_FAMILY);
                 if (r < 0)
                         return r;
 
@@ -583,25 +605,25 @@ _public_ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned s
                 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)
+                policy_set_union = policy_set_get_policy_set_union(
+                                m->containers[m->n_containers].policy_set,
+                                attr_type);
+                if (!policy_set_union)
                         return -EOPNOTSUPP;
 
-                m->containers[m->n_containers + 1].type_system =
-                        type_system_union_get_type_system_by_protocol(
-                                type_system_union,
+                m->containers[m->n_containers + 1].policy_set =
+                        policy_set_union_get_policy_set_by_family(
+                                policy_set_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)
+                m->containers[m->n_containers + 1].policy_set =
+                        policy_set_get_policy_set(
+                                m->containers[m->n_containers].policy_set,
+                                attr_type);
+        if (!m->containers[m->n_containers + 1].policy_set)
                 return -EOPNOTSUPP;
 
-        r = add_rtattr(m, type | NLA_F_NESTED, NULL, size);
+        r = add_rtattr(m, attr_type | NLA_F_NESTED, NULL, size);
         if (r < 0)
                 return r;
 
@@ -610,33 +632,37 @@ _public_ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned s
         return 0;
 }
 
-_public_ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) {
-        const NLTypeSystemUnion *type_system_union;
+_public_ int sd_netlink_message_open_container_union(sd_netlink_message *m, uint16_t attr_type, const char *key) {
+        const NLAPolicySetUnion *policy_set_union;
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
         assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
 
-        type_system_union = type_system_get_type_system_union(
-                        m->containers[m->n_containers].type_system,
-                        type);
-        if (!type_system_union)
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_NESTED_UNION_BY_STRING);
+        if (r < 0)
+                return r;
+
+        policy_set_union = policy_set_get_policy_set_union(
+                        m->containers[m->n_containers].policy_set,
+                        attr_type);
+        if (!policy_set_union)
                 return -EOPNOTSUPP;
 
-        m->containers[m->n_containers + 1].type_system =
-                type_system_union_get_type_system_by_string(
-                        type_system_union,
+        m->containers[m->n_containers + 1].policy_set =
+                policy_set_union_get_policy_set_by_string(
+                        policy_set_union,
                         key);
-        if (!m->containers[m->n_containers + 1].type_system)
+        if (!m->containers[m->n_containers + 1].policy_set)
                 return -EOPNOTSUPP;
 
-        r = sd_netlink_message_append_string(m, type_system_union_get_match_attribute(type_system_union), key);
+        r = sd_netlink_message_append_string(m, policy_set_union_get_match_attribute(policy_set_union), key);
         if (r < 0)
                 return r;
 
         /* do we ever need non-null size */
-        r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
+        r = add_rtattr(m, attr_type | NLA_F_NESTED, NULL, 0);
         if (r < 0)
                 return r;
 
@@ -650,27 +676,27 @@ _public_ int sd_netlink_message_close_container(sd_netlink_message *m) {
         assert_return(!m->sealed, -EPERM);
         assert_return(m->n_containers > 0, -EINVAL);
 
-        m->containers[m->n_containers].type_system = NULL;
+        m->containers[m->n_containers].policy_set = NULL;
         m->containers[m->n_containers].offset = 0;
         m->n_containers--;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
+_public_ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t attr_type) {
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
         assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
 
-        r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
+        r = add_rtattr(m, attr_type | NLA_F_NESTED, NULL, 0);
         if (r < 0)
                 return r;
 
         m->containers[m->n_containers].offset = r;
         m->n_containers++;
-        m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
+        m->containers[m->n_containers].policy_set = m->containers[m->n_containers - 1].policy_set;
 
         return 0;
 }
@@ -690,14 +716,14 @@ _public_ int sd_netlink_message_cancel_array(sd_netlink_message *m) {
         m->hdr->nlmsg_len -= rta_len;
 
         m->n_containers--;
-        m->containers[m->n_containers].type_system = NULL;
+        m->containers[m->n_containers].policy_set = NULL;
 
         return 0;
 }
 
 static int netlink_message_read_internal(
                 sd_netlink_message *m,
-                unsigned short type,
+                uint16_t attr_type,
                 void **ret_data,
                 bool *ret_net_byteorder) {
 
@@ -712,10 +738,10 @@ static int netlink_message_read_internal(
         if (!m->containers[m->n_containers].attributes)
                 return -ENODATA;
 
-        if (type > m->containers[m->n_containers].max_attribute)
+        if (attr_type > m->containers[m->n_containers].max_attribute)
                 return -ENODATA;
 
-        attribute = &m->containers[m->n_containers].attributes[type];
+        attribute = &m->containers[m->n_containers].attributes[attr_type];
 
         if (attribute->offset == 0)
                 return -ENODATA;
@@ -731,13 +757,13 @@ static int netlink_message_read_internal(
         return RTA_PAYLOAD(rta);
 }
 
-_public_ int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data) {
+_public_ int sd_netlink_message_read(sd_netlink_message *m, uint16_t attr_type, size_t size, void *data) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -750,13 +776,13 @@ _public_ int sd_netlink_message_read(sd_netlink_message *m, unsigned short type,
         return r;
 }
 
-_public_ int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
+_public_ int sd_netlink_message_read_data(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -776,13 +802,13 @@ _public_ int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short
         return r;
 }
 
-_public_ int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
+_public_ int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -802,17 +828,17 @@ _public_ int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigne
         return r;
 }
 
-_public_ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) {
+_public_ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, uint16_t attr_type, char **data) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_STRING);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -829,17 +855,17 @@ _public_ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsign
         return 0;
 }
 
-_public_ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
+_public_ int sd_netlink_message_read_string(sd_netlink_message *m, uint16_t attr_type, const char **data) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_STRING);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -852,17 +878,17 @@ _public_ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned shor
         return 0;
 }
 
-_public_ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) {
+_public_ int sd_netlink_message_read_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t *data) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U8);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -875,18 +901,18 @@ _public_ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short ty
         return 0;
 }
 
-_public_ int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) {
+_public_ int sd_netlink_message_read_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t *data) {
         void *attr_data;
         bool net_byteorder;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U16);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, &net_byteorder);
         if (r < 0)
                 return r;
 
@@ -903,18 +929,18 @@ _public_ int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short t
         return 0;
 }
 
-_public_ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) {
+_public_ int sd_netlink_message_read_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t *data) {
         void *attr_data;
         bool net_byteorder;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U32);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, &net_byteorder);
         if (r < 0)
                 return r;
 
@@ -931,17 +957,17 @@ _public_ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short t
         return 0;
 }
 
-_public_ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) {
+_public_ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, uint16_t attr_type, struct ether_addr *data) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -954,17 +980,17 @@ _public_ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned
         return 0;
 }
 
-int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, struct hw_addr_data *data) {
+int netlink_message_read_hw_addr(sd_netlink_message *m, uint16_t attr_type, struct hw_addr_data *data) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -979,17 +1005,17 @@ int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, str
         return 0;
 }
 
-_public_ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) {
+_public_ int sd_netlink_message_read_cache_info(sd_netlink_message *m, uint16_t attr_type, struct ifa_cacheinfo *info) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_CACHE_INFO);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -1002,18 +1028,18 @@ _public_ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned
         return 0;
 }
 
-int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data) {
+int netlink_message_read_in_addr_union(sd_netlink_message *m, uint16_t attr_type, int family, union in_addr_union *data) {
         void *attr_data;
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL);
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_IN_ADDR);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r < 0)
                 return r;
 
@@ -1026,29 +1052,29 @@ int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short typ
         return 0;
 }
 
-_public_ int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) {
+_public_ int sd_netlink_message_read_in_addr(sd_netlink_message *m, uint16_t attr_type, struct in_addr *data) {
         union in_addr_union u;
         int r;
 
-        r = netlink_message_read_in_addr_union(m, type, AF_INET, &u);
+        r = netlink_message_read_in_addr_union(m, attr_type, AF_INET, &u);
         if (r >= 0 && data)
                 *data = u.in;
 
         return r;
 }
 
-_public_ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) {
+_public_ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, uint16_t attr_type, struct in6_addr *data) {
         union in_addr_union u;
         int r;
 
-        r = netlink_message_read_in_addr_union(m, type, AF_INET6, &u);
+        r = netlink_message_read_in_addr_union(m, attr_type, AF_INET6, &u);
         if (r >= 0 && data)
                 *data = u.in6;
 
         return r;
 }
 
-_public_ int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short type) {
+_public_ int sd_netlink_message_has_flag(sd_netlink_message *m, uint16_t attr_type) {
         void *attr_data;
         int r;
 
@@ -1056,11 +1082,11 @@ _public_ int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short t
 
         /* This returns 1 when the flag is set, 0 when not set, negative errno on error. */
 
-        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_FLAG);
+        r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_FLAG);
         if (r < 0)
                 return r;
 
-        r = netlink_message_read_internal(m, type, &attr_data, NULL);
+        r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
         if (r == -ENODATA)
                 return 0;
         if (r < 0)
@@ -1069,10 +1095,10 @@ _public_ int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short t
         return 1;
 }
 
-_public_ int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret) {
+_public_ int sd_netlink_message_read_strv(sd_netlink_message *m, uint16_t container_type, uint16_t attr_type, char ***ret) {
         _cleanup_strv_free_ char **s = NULL;
-        const NLTypeSystem *type_system;
-        const NLType *nl_type;
+        const NLAPolicySet *policy_set;
+        const NLAPolicy *policy;
         struct rtattr *rta;
         void *container;
         size_t rt_len;
@@ -1081,26 +1107,26 @@ _public_ int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short
         assert_return(m, -EINVAL);
         assert_return(m->n_containers < NETLINK_CONTAINER_DEPTH, -EINVAL);
 
-        nl_type = type_system_get_type(
-                        m->containers[m->n_containers].type_system,
+        policy = policy_set_get_policy(
+                        m->containers[m->n_containers].policy_set,
                         container_type);
-        if (!nl_type)
+        if (!policy)
                 return -EOPNOTSUPP;
 
-        if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
+        if (policy_get_type(policy) != NETLINK_TYPE_NESTED)
                 return -EINVAL;
 
-        type_system = type_system_get_type_system(
-                        m->containers[m->n_containers].type_system,
+        policy_set = policy_set_get_policy_set(
+                        m->containers[m->n_containers].policy_set,
                         container_type);
-        if (!type_system)
+        if (!policy_set)
                 return -EOPNOTSUPP;
 
-        nl_type = type_system_get_type(type_system, type_id);
-        if (!nl_type)
+        policy = policy_set_get_policy(policy_set, attr_type);
+        if (!policy)
                 return -EOPNOTSUPP;
 
-        if (type_get_type(nl_type) != NETLINK_TYPE_STRING)
+        if (policy_get_type(policy) != NETLINK_TYPE_STRING)
                 return -EINVAL;
 
         r = netlink_message_read_internal(m, container_type, &container, NULL);
@@ -1115,10 +1141,10 @@ _public_ int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short
          * introduce an unsigned short variable as a workaround. */
         unsigned short len = rt_len;
         for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
-                unsigned short type;
+                uint16_t type;
 
                 type = RTA_TYPE(rta);
-                if (type != type_id)
+                if (type != attr_type)
                         continue;
 
                 r = strv_extend(&s, RTA_DATA(rta));
@@ -1166,81 +1192,73 @@ static int netlink_container_parse(
         return 0;
 }
 
-_public_ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) {
-        const NLType *nl_type;
-        const NLTypeSystem *type_system;
+_public_ int sd_netlink_message_enter_container(sd_netlink_message *m, uint16_t attr_type) {
+        const NLAPolicy *policy;
+        const NLAPolicySet *policy_set;
         void *container;
-        uint16_t type;
         size_t size;
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
 
-        nl_type = type_system_get_type(
-                        m->containers[m->n_containers].type_system,
-                        type_id);
-        if (!nl_type)
+        policy = policy_set_get_policy(
+                        m->containers[m->n_containers].policy_set,
+                        attr_type);
+        if (!policy)
                 return -EOPNOTSUPP;
 
-        type = type_get_type(nl_type);
+        switch (policy_get_type(policy)) {
+        case NETLINK_TYPE_NESTED:
+                policy_set = policy_set_get_policy_set(
+                                m->containers[m->n_containers].policy_set,
+                                attr_type);
+                break;
+
+        case NETLINK_TYPE_NESTED_UNION_BY_STRING: {
+                const NLAPolicySetUnion *policy_set_union;
+                const char *key;
 
-        if (type == NETLINK_TYPE_NESTED) {
-                type_system = type_system_get_type_system(
-                                m->containers[m->n_containers].type_system,
-                                type_id);
-                if (!type_system)
+                policy_set_union = policy_get_policy_set_union(policy);
+                if (!policy_set_union)
                         return -EOPNOTSUPP;
-        } else if (type == NETLINK_TYPE_UNION) {
-                const NLTypeSystemUnion *type_system_union;
 
-                type_system_union = type_system_get_type_system_union(
-                                 m->containers[m->n_containers].type_system,
-                                 type_id);
-                if (!type_system_union)
+                r = sd_netlink_message_read_string(
+                                m,
+                                policy_set_union_get_match_attribute(policy_set_union),
+                                &key);
+                if (r < 0)
+                        return r;
+
+                policy_set = policy_set_union_get_policy_set_by_string(
+                                policy_set_union,
+                                key);
+                break;
+        }
+        case NETLINK_TYPE_NESTED_UNION_BY_FAMILY: {
+                const NLAPolicySetUnion *policy_set_union;
+                int family;
+
+                policy_set_union = policy_get_policy_set_union(policy);
+                if (!policy_set_union)
                         return -EOPNOTSUPP;
 
-                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_get_match_attribute(type_system_union),
-                                        &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: {
-                        int family;
-
-                        r = sd_rtnl_message_get_family(m, &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();
-                }
-        } else
-                return -EINVAL;
+                r = sd_rtnl_message_get_family(m, &family);
+                if (r < 0)
+                        return r;
+
+                policy_set = policy_set_union_get_policy_set_by_family(
+                                policy_set_union,
+                                family);
+                break;
+        }
+        default:
+                assert_not_reached();
+        }
+        if (!policy_set)
+                return -EOPNOTSUPP;
 
-        r = netlink_message_read_internal(m, type_id, &container, NULL);
+        r = netlink_message_read_internal(m, attr_type, &container, NULL);
         if (r < 0)
                 return r;
 
@@ -1256,12 +1274,12 @@ _public_ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned
                 return r;
         }
 
-        m->containers[m->n_containers].type_system = type_system;
+        m->containers[m->n_containers].policy_set = policy_set;
 
         return 0;
 }
 
-_public_ int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type_id) {
+_public_ int sd_netlink_message_enter_array(sd_netlink_message *m, uint16_t attr_type) {
         void *container;
         size_t size;
         int r;
@@ -1269,7 +1287,7 @@ _public_ int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned shor
         assert_return(m, -EINVAL);
         assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
 
-        r = netlink_message_read_internal(m, type_id, &container, NULL);
+        r = netlink_message_read_internal(m, attr_type, &container, NULL);
         if (r < 0)
                 return r;
 
@@ -1285,7 +1303,7 @@ _public_ int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned shor
                 return r;
         }
 
-        m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
+        m->containers[m->n_containers].policy_set = m->containers[m->n_containers - 1].policy_set;
 
         return 0;
 }
@@ -1297,7 +1315,7 @@ _public_ int sd_netlink_message_exit_container(sd_netlink_message *m) {
 
         m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes);
         m->containers[m->n_containers].max_attribute = 0;
-        m->containers[m->n_containers].type_system = NULL;
+        m->containers[m->n_containers].policy_set = NULL;
 
         m->n_containers--;
 
@@ -1376,8 +1394,8 @@ _public_ int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *nl) {
 
         assert(m->hdr);
 
-        r = type_system_root_get_type_system_and_header_size(nl, m->hdr->nlmsg_type,
-                                                             &m->containers[0].type_system, &size);
+        r = netlink_get_policy_set_and_header_size(nl, m->hdr->nlmsg_type,
+                                                   &m->containers[0].policy_set, &size);
         if (r < 0)
                 return r;
 
index 70f5b85a54ddfeaf7b385bf1fc31647f531500f4..605e80916c7b4c41d05240d481b6ba2b7a9bfb7b 100644 (file)
@@ -13,7 +13,6 @@
 #include "io-util.h"
 #include "netlink-internal.h"
 #include "netlink-types.h"
-#include "netlink-util.h"
 #include "socket-util.h"
 #include "util.h"
 
@@ -309,7 +308,7 @@ int socket_read_message(sd_netlink *nl) {
                 }
 
                 /* check that we support this message type */
-                r = type_system_root_get_type_system_and_header_size(nl, new_msg->nlmsg_type, NULL, &size);
+                r = netlink_get_policy_set_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",
index 15becad30df96596401429b74548ca2da5ff45ad..9d6d978b45995783c7e81cfa6832782e6b36b78b 100644 (file)
 #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 },
+static const NLAPolicy genl_ctrl_mcast_group_policies[] = {
+        [CTRL_ATTR_MCAST_GRP_NAME]  = BUILD_POLICY(STRING),
+        [CTRL_ATTR_MCAST_GRP_ID]    = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(genl_ctrl_mcast_group);
+DEFINE_POLICY_SET(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 },
+static const NLAPolicy genl_ctrl_ops_policies[] = {
+        [CTRL_ATTR_OP_ID]           = BUILD_POLICY(U32),
+        [CTRL_ATTR_OP_FLAGS]        = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(genl_ctrl_ops);
+DEFINE_POLICY_SET(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 },
+static const NLAPolicy genl_ctrl_policies[] = {
+        [CTRL_ATTR_FAMILY_ID]    = BUILD_POLICY(U16),
+        [CTRL_ATTR_FAMILY_NAME]  = BUILD_POLICY(STRING),
+        [CTRL_ATTR_VERSION]      = BUILD_POLICY(U32),
+        [CTRL_ATTR_HDRSIZE]      = BUILD_POLICY(U32),
+        [CTRL_ATTR_MAXATTR]      = BUILD_POLICY(U32),
+        [CTRL_ATTR_OPS]          = BUILD_POLICY_NESTED(genl_ctrl_ops),
+        [CTRL_ATTR_MCAST_GROUPS] = BUILD_POLICY_NESTED(genl_ctrl_mcast_group),
         /*
         [CTRL_ATTR_POLICY]       = { .type = NETLINK_TYPE_NESTED, },
         [CTRL_ATTR_OP_POLICY]    = { .type = NETLINK_TYPE_NESTED, }
         */
-        [CTRL_ATTR_OP]           = { .type = NETLINK_TYPE_U32 },
+        [CTRL_ATTR_OP]           = BUILD_POLICY(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]                  = { .type = NETLINK_TYPE_ETHER_ADDR, .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]                  = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-        [BATADV_ATTR_ORIG_ADDRESS]                  = { .type = NETLINK_TYPE_ETHER_ADDR, .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]                    = { .type = NETLINK_TYPE_ETHER_ADDR, .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]                 = { .type = NETLINK_TYPE_ETHER_ADDR, .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]                        = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-        [BATADV_ATTR_BLA_OWN]                       = { .type = NETLINK_TYPE_FLAG },
-        [BATADV_ATTR_BLA_ADDRESS]                   = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-        [BATADV_ATTR_BLA_VID]                       = { .type = NETLINK_TYPE_U16 },
-        [BATADV_ATTR_BLA_BACKBONE]                  = { .type = NETLINK_TYPE_ETHER_ADDR, .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]           = { .type = NETLINK_TYPE_ETHER_ADDR, .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 NLAPolicy genl_batadv_policies[] = {
+        [BATADV_ATTR_VERSION]                       = BUILD_POLICY(STRING),
+        [BATADV_ATTR_ALGO_NAME]                     = BUILD_POLICY(STRING),
+        [BATADV_ATTR_MESH_IFINDEX]                  = BUILD_POLICY(U32),
+        [BATADV_ATTR_MESH_IFNAME]                   = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ),
+        [BATADV_ATTR_MESH_ADDRESS]                  = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [BATADV_ATTR_HARD_IFINDEX]                  = BUILD_POLICY(U32),
+        [BATADV_ATTR_HARD_IFNAME]                   = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ),
+        [BATADV_ATTR_HARD_ADDRESS]                  = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [BATADV_ATTR_ORIG_ADDRESS]                  = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [BATADV_ATTR_TPMETER_RESULT]                = BUILD_POLICY(U8),
+        [BATADV_ATTR_TPMETER_TEST_TIME]             = BUILD_POLICY(U32),
+        [BATADV_ATTR_TPMETER_BYTES]                 = BUILD_POLICY(U64),
+        [BATADV_ATTR_TPMETER_COOKIE]                = BUILD_POLICY(U32),
+        [BATADV_ATTR_PAD]                           = BUILD_POLICY(UNSPEC),
+        [BATADV_ATTR_ACTIVE]                        = BUILD_POLICY(FLAG),
+        [BATADV_ATTR_TT_ADDRESS]                    = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [BATADV_ATTR_TT_TTVN]                       = BUILD_POLICY(U8),
+        [BATADV_ATTR_TT_LAST_TTVN]                  = BUILD_POLICY(U8),
+        [BATADV_ATTR_TT_CRC32]                      = BUILD_POLICY(U32),
+        [BATADV_ATTR_TT_VID]                        = BUILD_POLICY(U16),
+        [BATADV_ATTR_TT_FLAGS]                      = BUILD_POLICY(U32),
+        [BATADV_ATTR_FLAG_BEST]                     = BUILD_POLICY(FLAG),
+        [BATADV_ATTR_LAST_SEEN_MSECS]               = BUILD_POLICY(U32),
+        [BATADV_ATTR_NEIGH_ADDRESS]                 = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [BATADV_ATTR_TQ]                            = BUILD_POLICY(U8),
+        [BATADV_ATTR_THROUGHPUT]                    = BUILD_POLICY(U32),
+        [BATADV_ATTR_BANDWIDTH_UP]                  = BUILD_POLICY(U32),
+        [BATADV_ATTR_BANDWIDTH_DOWN]                = BUILD_POLICY(U32),
+        [BATADV_ATTR_ROUTER]                        = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [BATADV_ATTR_BLA_OWN]                       = BUILD_POLICY(FLAG),
+        [BATADV_ATTR_BLA_ADDRESS]                   = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [BATADV_ATTR_BLA_VID]                       = BUILD_POLICY(U16),
+        [BATADV_ATTR_BLA_BACKBONE]                  = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [BATADV_ATTR_BLA_CRC]                       = BUILD_POLICY(U16),
+        [BATADV_ATTR_DAT_CACHE_IP4ADDRESS]          = BUILD_POLICY(U32),
+        [BATADV_ATTR_DAT_CACHE_HWADDRESS]           = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [BATADV_ATTR_DAT_CACHE_VID]                 = BUILD_POLICY(U16),
+        [BATADV_ATTR_MCAST_FLAGS]                   = BUILD_POLICY(U32),
+        [BATADV_ATTR_MCAST_FLAGS_PRIV]              = BUILD_POLICY(U32),
+        [BATADV_ATTR_VLANID]                        = BUILD_POLICY(U16),
+        [BATADV_ATTR_AGGREGATED_OGMS_ENABLED]       = BUILD_POLICY(U8),
+        [BATADV_ATTR_AP_ISOLATION_ENABLED]          = BUILD_POLICY(U8),
+        [BATADV_ATTR_ISOLATION_MARK]                = BUILD_POLICY(U32),
+        [BATADV_ATTR_ISOLATION_MASK]                = BUILD_POLICY(U32),
+        [BATADV_ATTR_BONDING_ENABLED]               = BUILD_POLICY(U8),
+        [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = BUILD_POLICY(U8),
+        [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = BUILD_POLICY(U8),
+        [BATADV_ATTR_FRAGMENTATION_ENABLED]         = BUILD_POLICY(U8),
+        [BATADV_ATTR_GW_BANDWIDTH_DOWN]             = BUILD_POLICY(U32),
+        [BATADV_ATTR_GW_BANDWIDTH_UP]               = BUILD_POLICY(U32),
+        [BATADV_ATTR_GW_MODE]                       = BUILD_POLICY(U8),
+        [BATADV_ATTR_GW_SEL_CLASS]                  = BUILD_POLICY(U32),
+        [BATADV_ATTR_HOP_PENALTY]                   = BUILD_POLICY(U8),
+        [BATADV_ATTR_LOG_LEVEL]                     = BUILD_POLICY(U32),
+        [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED]  = BUILD_POLICY(U8),
+        [BATADV_ATTR_MULTICAST_FANOUT]              = BUILD_POLICY(U32),
+        [BATADV_ATTR_NETWORK_CODING_ENABLED]        = BUILD_POLICY(U8),
+        [BATADV_ATTR_ORIG_INTERVAL]                 = BUILD_POLICY(U32),
+        [BATADV_ATTR_ELP_INTERVAL]                  = BUILD_POLICY(U32),
+        [BATADV_ATTR_THROUGHPUT_OVERRIDE]           = BUILD_POLICY(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, .size = sizeof(struct in_addr) },
-        [FOU_ATTR_PEER_V4]           = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
-        [FOU_ATTR_LOCAL_V6]          = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
-        [FOU_ATTR_PEER_V6]           = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
-        [FOU_ATTR_PEER_PORT]         = { .type = NETLINK_TYPE_U16 },
-        [FOU_ATTR_IFINDEX]           = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy genl_fou_policies[] = {
+        [FOU_ATTR_PORT]              = BUILD_POLICY(U16),
+        [FOU_ATTR_AF]                = BUILD_POLICY(U8),
+        [FOU_ATTR_IPPROTO]           = BUILD_POLICY(U8),
+        [FOU_ATTR_TYPE]              = BUILD_POLICY(U8),
+        [FOU_ATTR_REMCSUM_NOPARTIAL] = BUILD_POLICY(FLAG),
+        [FOU_ATTR_LOCAL_V4]          = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+        [FOU_ATTR_PEER_V4]           = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+        [FOU_ATTR_LOCAL_V6]          = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [FOU_ATTR_PEER_V6]           = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [FOU_ATTR_PEER_PORT]         = BUILD_POLICY(U16),
+        [FOU_ATTR_IFINDEX]           = BUILD_POLICY(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, .size = sizeof(struct in_addr) },
-        [L2TP_ATTR_IP_DADDR]          = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct 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, .size = sizeof(struct in6_addr) },
-        [L2TP_ATTR_IP6_DADDR]         = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
-        [L2TP_ATTR_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_FLAG },
-        [L2TP_ATTR_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_FLAG },
+static const NLAPolicy genl_l2tp_policies[] = {
+        [L2TP_ATTR_PW_TYPE]           = BUILD_POLICY(U16),
+        [L2TP_ATTR_ENCAP_TYPE]        = BUILD_POLICY(U16),
+        [L2TP_ATTR_OFFSET]            = BUILD_POLICY(U16),
+        [L2TP_ATTR_DATA_SEQ]          = BUILD_POLICY(U16),
+        [L2TP_ATTR_L2SPEC_TYPE]       = BUILD_POLICY(U8),
+        [L2TP_ATTR_L2SPEC_LEN]        = BUILD_POLICY(U8),
+        [L2TP_ATTR_PROTO_VERSION]     = BUILD_POLICY(U8),
+        [L2TP_ATTR_IFNAME]            = BUILD_POLICY(STRING),
+        [L2TP_ATTR_CONN_ID]           = BUILD_POLICY(U32),
+        [L2TP_ATTR_PEER_CONN_ID]      = BUILD_POLICY(U32),
+        [L2TP_ATTR_SESSION_ID]        = BUILD_POLICY(U32),
+        [L2TP_ATTR_PEER_SESSION_ID]   = BUILD_POLICY(U32),
+        [L2TP_ATTR_UDP_CSUM]          = BUILD_POLICY(U8),
+        [L2TP_ATTR_VLAN_ID]           = BUILD_POLICY(U16),
+        [L2TP_ATTR_RECV_SEQ]          = BUILD_POLICY(U8),
+        [L2TP_ATTR_SEND_SEQ]          = BUILD_POLICY(U8),
+        [L2TP_ATTR_LNS_MODE]          = BUILD_POLICY(U8),
+        [L2TP_ATTR_USING_IPSEC]       = BUILD_POLICY(U8),
+        [L2TP_ATTR_FD]                = BUILD_POLICY(U32),
+        [L2TP_ATTR_IP_SADDR]          = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+        [L2TP_ATTR_IP_DADDR]          = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+        [L2TP_ATTR_UDP_SPORT]         = BUILD_POLICY(U16),
+        [L2TP_ATTR_UDP_DPORT]         = BUILD_POLICY(U16),
+        [L2TP_ATTR_IP6_SADDR]         = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [L2TP_ATTR_IP6_DADDR]         = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [L2TP_ATTR_UDP_ZERO_CSUM6_TX] = BUILD_POLICY(FLAG),
+        [L2TP_ATTR_UDP_ZERO_CSUM6_RX] = BUILD_POLICY(FLAG),
 };
 
 /***************** genl macsec type systems *****************/
-static const NLType genl_macsec_rxsc_types[] = {
-        [MACSEC_RXSC_ATTR_SCI] = { .type = NETLINK_TYPE_U64 },
+static const NLAPolicy genl_macsec_rxsc_policies[] = {
+        [MACSEC_RXSC_ATTR_SCI] = BUILD_POLICY(U64),
 };
 
-DEFINE_TYPE_SYSTEM(genl_macsec_rxsc);
+DEFINE_POLICY_SET(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]  = { .type = NETLINK_TYPE_BINARY, .size = MACSEC_KEYID_LEN },
-        [MACSEC_SA_ATTR_KEY]    = { .type = NETLINK_TYPE_BINARY, .size = MACSEC_MAX_KEY_LEN },
+static const NLAPolicy genl_macsec_sa_policies[] = {
+        [MACSEC_SA_ATTR_AN]     = BUILD_POLICY(U8),
+        [MACSEC_SA_ATTR_ACTIVE] = BUILD_POLICY(U8),
+        [MACSEC_SA_ATTR_PN]     = BUILD_POLICY(U32),
+        [MACSEC_SA_ATTR_KEYID]  = BUILD_POLICY_WITH_SIZE(BINARY, MACSEC_KEYID_LEN),
+        [MACSEC_SA_ATTR_KEY]    = BUILD_POLICY_WITH_SIZE(BINARY, MACSEC_MAX_KEY_LEN),
 };
 
-DEFINE_TYPE_SYSTEM(genl_macsec_sa);
+DEFINE_POLICY_SET(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 },
+static const NLAPolicy genl_macsec_policies[] = {
+        [MACSEC_ATTR_IFINDEX]     = BUILD_POLICY(U32),
+        [MACSEC_ATTR_RXSC_CONFIG] = BUILD_POLICY_NESTED(genl_macsec_rxsc),
+        [MACSEC_ATTR_SA_CONFIG]   = BUILD_POLICY_NESTED(genl_macsec_sa),
 };
 
 /***************** genl nl80211 type systems *****************/
-static const NLType genl_nl80211_types[] = {
-        [NL80211_ATTR_WIPHY]       = { .type = NETLINK_TYPE_U32 },
-        [NL80211_ATTR_WIPHY_NAME]  = { .type = NETLINK_TYPE_STRING },
-        [NL80211_ATTR_IFINDEX]     = { .type = NETLINK_TYPE_U32 },
-        [NL80211_ATTR_IFNAME]      = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
-        [NL80211_ATTR_IFTYPE]      = { .type = NETLINK_TYPE_U32 },
-        [NL80211_ATTR_MAC]         = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-        [NL80211_ATTR_SSID]        = { .type = NETLINK_TYPE_BINARY, .size = IEEE80211_MAX_SSID_LEN },
-        [NL80211_ATTR_STATUS_CODE] = { .type = NETLINK_TYPE_U16 },
-        [NL80211_ATTR_4ADDR]       = { .type = NETLINK_TYPE_U8 },
+static const NLAPolicy genl_nl80211_policies[] = {
+        [NL80211_ATTR_WIPHY]       = BUILD_POLICY(U32),
+        [NL80211_ATTR_WIPHY_NAME]  = BUILD_POLICY(STRING),
+        [NL80211_ATTR_IFINDEX]     = BUILD_POLICY(U32),
+        [NL80211_ATTR_IFNAME]      = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ-1),
+        [NL80211_ATTR_IFTYPE]      = BUILD_POLICY(U32),
+        [NL80211_ATTR_MAC]         = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [NL80211_ATTR_SSID]        = BUILD_POLICY_WITH_SIZE(BINARY, IEEE80211_MAX_SSID_LEN),
+        [NL80211_ATTR_STATUS_CODE] = BUILD_POLICY(U16),
+        [NL80211_ATTR_4ADDR]       = BUILD_POLICY(U8),
 };
 
 /***************** 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 },
+static const NLAPolicy genl_wireguard_allowedip_policies[] = {
+        [WGALLOWEDIP_A_FAMILY]    = BUILD_POLICY(U16),
+        [WGALLOWEDIP_A_IPADDR]    = BUILD_POLICY(IN_ADDR),
+        [WGALLOWEDIP_A_CIDR_MASK] = BUILD_POLICY(U8),
 };
 
-DEFINE_TYPE_SYSTEM(genl_wireguard_allowedip);
+DEFINE_POLICY_SET(genl_wireguard_allowedip);
 
-static const NLType genl_wireguard_peer_types[] = {
-        [WGPEER_A_PUBLIC_KEY]                    = { .type = NETLINK_TYPE_BINARY, .size = WG_KEY_LEN },
-        [WGPEER_A_FLAGS]                         = { .type = NETLINK_TYPE_U32 },
-        [WGPEER_A_PRESHARED_KEY]                 = { .type = NETLINK_TYPE_BINARY, .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 NLAPolicy genl_wireguard_peer_policies[] = {
+        [WGPEER_A_PUBLIC_KEY]                    = BUILD_POLICY_WITH_SIZE(BINARY, WG_KEY_LEN),
+        [WGPEER_A_FLAGS]                         = BUILD_POLICY(U32),
+        [WGPEER_A_PRESHARED_KEY]                 = BUILD_POLICY_WITH_SIZE(BINARY, WG_KEY_LEN),
+        [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = BUILD_POLICY(U16),
+        [WGPEER_A_ENDPOINT]                      = BUILD_POLICY(SOCKADDR),
+        [WGPEER_A_ALLOWEDIPS]                    = BUILD_POLICY_NESTED(genl_wireguard_allowedip),
 };
 
-DEFINE_TYPE_SYSTEM(genl_wireguard_peer);
+DEFINE_POLICY_SET(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] = { .type = NETLINK_TYPE_BINARY, .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 NLAPolicy genl_wireguard_policies[] = {
+        [WGDEVICE_A_IFINDEX]     = BUILD_POLICY(U32),
+        [WGDEVICE_A_IFNAME]      = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ-1),
+        [WGDEVICE_A_FLAGS]       = BUILD_POLICY(U32),
+        [WGDEVICE_A_PRIVATE_KEY] = BUILD_POLICY_WITH_SIZE(BINARY, WG_KEY_LEN),
+        [WGDEVICE_A_LISTEN_PORT] = BUILD_POLICY(U16),
+        [WGDEVICE_A_FWMARK]      = BUILD_POLICY(U32),
+        [WGDEVICE_A_PEERS]       = BUILD_POLICY_NESTED(genl_wireguard_peer),
 };
 
 /***************** 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), },
+static const NLAPolicySetUnionElement genl_policy_set_union_elements[] = {
+        BUILD_UNION_ELEMENT_BY_STRING(CTRL_GENL_NAME,    genl_ctrl),
+        BUILD_UNION_ELEMENT_BY_STRING(BATADV_NL_NAME,    genl_batadv),
+        BUILD_UNION_ELEMENT_BY_STRING(FOU_GENL_NAME,     genl_fou),
+        BUILD_UNION_ELEMENT_BY_STRING(L2TP_GENL_NAME,    genl_l2tp),
+        BUILD_UNION_ELEMENT_BY_STRING(MACSEC_GENL_NAME,  genl_macsec),
+        BUILD_UNION_ELEMENT_BY_STRING(NL80211_GENL_NAME, genl_nl80211),
+        BUILD_UNION_ELEMENT_BY_STRING(WG_GENL_NAME,      genl_wireguard),
 };
 
 /* This is the root type system union, so match_attribute is not necessary. */
-DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(genl, 0);
+DEFINE_POLICY_SET_UNION(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);
+const NLAPolicySet *genl_get_policy_set_by_name(const char *name) {
+        return policy_set_union_get_policy_set_by_string(&genl_policy_set_union, name);
 }
index 16da6aad0e6776d50f7548598c4ddc212db7302d..1412514f9ba77362e17d021dd417ef72ec929e7a 100644 (file)
@@ -4,46 +4,63 @@
 #include "macro.h"
 #include "netlink-types.h"
 
-struct NLType {
-        uint16_t type;
+/* C.f. see 'struct nla_policy' at include/net/netlink.h. */
+struct NLAPolicy {
+        NLAType type;
         size_t size;
-        const NLTypeSystem *type_system;
-        const NLTypeSystemUnion *type_system_union;
+        union {
+                const NLAPolicySet *policy_set;
+                const NLAPolicySetUnion *policy_set_union;
+        };
 };
 
-struct NLTypeSystem {
+struct NLAPolicySet {
         uint16_t count;
-        const NLType *types;
+        const NLAPolicy *policies;
 };
 
-typedef struct NLTypeSystemUnionElement {
+typedef struct NLAPolicySetUnionElement {
         union {
-                int protocol;
-                const char *name;
+                int family;          /* used by NETLINK_TYPE_NESTED_UNION_BY_FAMILY */
+                const char *string;  /* used by NETLINK_TYPE_NESTED_UNION_BY_STRING */
         };
-        NLTypeSystem type_system;
-} NLTypeSystemUnionElement;
+        NLAPolicySet policy_set;
+} NLAPolicySetUnionElement;
 
-struct NLTypeSystemUnion {
+struct NLAPolicySetUnion {
         size_t count;
-        const NLTypeSystemUnionElement *elements;
-        NLMatchType match_type;
-        uint16_t match_attribute;
+        const NLAPolicySetUnionElement *elements;
+        uint16_t match_attribute; /* used by NETLINK_TYPE_NESTED_UNION_BY_STRING */
 };
 
-#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 BUILD_POLICY_WITH_SIZE(t, n)            \
+        { .type = NETLINK_TYPE_##t, .size = n }
+#define BUILD_POLICY(t)                         \
+        BUILD_POLICY_WITH_SIZE(t, 0)
+#define BUILD_POLICY_NESTED_WITH_SIZE(name, n)                          \
+        { .type = NETLINK_TYPE_NESTED, .size = n, .policy_set = &name##_policy_set }
+#define BUILD_POLICY_NESTED(name)               \
+        BUILD_POLICY_NESTED_WITH_SIZE(name, 0)
+#define _BUILD_POLICY_NESTED_UNION(name, by)                            \
+        { .type = NETLINK_TYPE_NESTED_UNION_BY_##by, .policy_set_union = &name##_policy_set_union }
+#define BUILD_POLICY_NESTED_UNION_BY_STRING(name)       \
+        _BUILD_POLICY_NESTED_UNION(name, STRING)
+#define BUILD_POLICY_NESTED_UNION_BY_FAMILY(name)       \
+        _BUILD_POLICY_NESTED_UNION(name, FAMILY)
+
+#define _BUILD_POLICY_SET(name)                                         \
+        { .count = ELEMENTSOF(name##_policies), .policies = name##_policies }
+#define DEFINE_POLICY_SET(name)                                         \
+        static const NLAPolicySet name##_policy_set = _BUILD_POLICY_SET(name)
+
+# define BUILD_UNION_ELEMENT_BY_STRING(s, name)                 \
+        { .string = s, .policy_set = _BUILD_POLICY_SET(name) }
+# define BUILD_UNION_ELEMENT_BY_FAMILY(f, name)                 \
+        { .family = f, .policy_set = _BUILD_POLICY_SET(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,                                     \
+#define DEFINE_POLICY_SET_UNION(name, attr)                             \
+        static const NLAPolicySetUnion name##_policy_set_union = {      \
+                .count = ELEMENTSOF(name##_policy_set_union_elements),  \
+                .elements = name##_policy_set_union_elements,           \
                 .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)
index 1ba134a976a6dbd156feae8f22ac290cbfd9b283..8ef4d45d42680859dadc816682307a3d8bc602c9 100644 (file)
 /* 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 },
+static const NLAPolicy nfnl_nft_table_policies[] = {
+        [NFTA_TABLE_NAME]  = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+        [NFTA_TABLE_FLAGS] = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_nft_table);
+DEFINE_POLICY_SET(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 },
+static const NLAPolicy nfnl_nft_chain_hook_policies[] = {
+        [NFTA_HOOK_HOOKNUM]  = BUILD_POLICY(U32),
+        [NFTA_HOOK_PRIORITY] = BUILD_POLICY(U32),
+        [NFTA_HOOK_DEV]      = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ - 1),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_nft_chain_hook);
+DEFINE_POLICY_SET(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 },
+static const NLAPolicy nfnl_nft_chain_policies[] = {
+        [NFTA_CHAIN_TABLE] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+        [NFTA_CHAIN_NAME]  = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+        [NFTA_CHAIN_HOOK]  = BUILD_POLICY_NESTED(nfnl_nft_chain_hook),
+        [NFTA_CHAIN_TYPE]  = BUILD_POLICY_WITH_SIZE(STRING, 16),
+        [NFTA_CHAIN_FLAGS] = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_nft_chain);
+DEFINE_POLICY_SET(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 NLAPolicy nfnl_nft_expr_meta_policies[] = {
+        [NFTA_META_DREG] = BUILD_POLICY(U32),
+        [NFTA_META_KEY]  = BUILD_POLICY(U32),
+        [NFTA_META_SREG] = BUILD_POLICY(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 NLAPolicy nfnl_nft_expr_payload_policies[] = {
+        [NFTA_PAYLOAD_DREG]   = BUILD_POLICY(U32),
+        [NFTA_PAYLOAD_BASE]   = BUILD_POLICY(U32),
+        [NFTA_PAYLOAD_OFFSET] = BUILD_POLICY(U32),
+        [NFTA_PAYLOAD_LEN]    = BUILD_POLICY(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 NLAPolicy nfnl_nft_expr_nat_policies[] = {
+        [NFTA_NAT_TYPE]          = BUILD_POLICY(U32),
+        [NFTA_NAT_FAMILY]        = BUILD_POLICY(U32),
+        [NFTA_NAT_REG_ADDR_MIN]  = BUILD_POLICY(U32),
+        [NFTA_NAT_REG_ADDR_MAX]  = BUILD_POLICY(U32),
+        [NFTA_NAT_REG_PROTO_MIN] = BUILD_POLICY(U32),
+        [NFTA_NAT_REG_PROTO_MAX] = BUILD_POLICY(U32),
+        [NFTA_NAT_FLAGS]         = BUILD_POLICY(U32),
 };
 
-static const NLType nfnl_nft_data_types[] = {
+static const NLAPolicy nfnl_nft_data_policies[] = {
         [NFTA_DATA_VALUE] = { .type = NETLINK_TYPE_BINARY },
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_nft_data);
+DEFINE_POLICY_SET(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 NLAPolicy nfnl_nft_expr_bitwise_policies[] = {
+        [NFTA_BITWISE_SREG] = BUILD_POLICY(U32),
+        [NFTA_BITWISE_DREG] = BUILD_POLICY(U32),
+        [NFTA_BITWISE_LEN]  = BUILD_POLICY(U32),
+        [NFTA_BITWISE_MASK] = BUILD_POLICY_NESTED(nfnl_nft_data),
+        [NFTA_BITWISE_XOR]  = BUILD_POLICY_NESTED(nfnl_nft_data),
 };
 
-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 NLAPolicy nfnl_nft_expr_cmp_policies[] = {
+        [NFTA_CMP_SREG] = BUILD_POLICY(U32),
+        [NFTA_CMP_OP]   = BUILD_POLICY(U32),
+        [NFTA_CMP_DATA] = BUILD_POLICY_NESTED(nfnl_nft_data),
 };
 
-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 NLAPolicy nfnl_nft_expr_fib_policies[] = {
+        [NFTA_FIB_DREG]   = BUILD_POLICY(U32),
+        [NFTA_FIB_RESULT] = BUILD_POLICY(U32),
+        [NFTA_FIB_FLAGS]  = BUILD_POLICY(U32),
 };
 
-static const NLType nfnl_nft_expr_lookup_types[] = {
+static const NLAPolicy nfnl_nft_expr_lookup_policies[] = {
         [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 },
+        [NFTA_LOOKUP_SREG]  = BUILD_POLICY(U32),
+        [NFTA_LOOKUP_DREG]  = BUILD_POLICY(U32),
+        [NFTA_LOOKUP_FLAGS] = BUILD_POLICY(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 NLAPolicy nfnl_nft_expr_masq_policies[] = {
+        [NFTA_MASQ_FLAGS]         = BUILD_POLICY(U32),
+        [NFTA_MASQ_REG_PROTO_MIN] = BUILD_POLICY(U32),
+        [NFTA_MASQ_REG_PROTO_MAX] = BUILD_POLICY(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), },
+static const NLAPolicySetUnionElement nfnl_expr_data_policy_set_union_elements[] = {
+        BUILD_UNION_ELEMENT_BY_STRING("bitwise", nfnl_nft_expr_bitwise),
+        BUILD_UNION_ELEMENT_BY_STRING("cmp",     nfnl_nft_expr_cmp),
+        BUILD_UNION_ELEMENT_BY_STRING("fib",     nfnl_nft_expr_fib),
+        BUILD_UNION_ELEMENT_BY_STRING("lookup",  nfnl_nft_expr_lookup),
+        BUILD_UNION_ELEMENT_BY_STRING("masq",    nfnl_nft_expr_masq),
+        BUILD_UNION_ELEMENT_BY_STRING("meta",    nfnl_nft_expr_meta),
+        BUILD_UNION_ELEMENT_BY_STRING("nat",     nfnl_nft_expr_nat),
+        BUILD_UNION_ELEMENT_BY_STRING("payload", nfnl_nft_expr_payload),
 };
 
-DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(nfnl_expr_data, NFTA_EXPR_NAME);
+DEFINE_POLICY_SET_UNION(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 },
+static const NLAPolicy nfnl_nft_rule_expr_policies[] = {
+        [NFTA_EXPR_NAME] = BUILD_POLICY_WITH_SIZE(STRING, 16),
+        [NFTA_EXPR_DATA] = BUILD_POLICY_NESTED_UNION_BY_STRING(nfnl_expr_data),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_nft_rule_expr);
+DEFINE_POLICY_SET(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 }
+static const NLAPolicy nfnl_nft_rule_policies[] = {
+        [NFTA_RULE_TABLE]       = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+        [NFTA_RULE_CHAIN]       = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+        [NFTA_RULE_EXPRESSIONS] = BUILD_POLICY_NESTED(nfnl_nft_rule_expr),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_nft_rule);
+DEFINE_POLICY_SET(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 },
+static const NLAPolicy nfnl_nft_set_policies[] = {
+        [NFTA_SET_TABLE]      = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+        [NFTA_SET_NAME]       = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+        [NFTA_SET_FLAGS]      = BUILD_POLICY(U32),
+        [NFTA_SET_KEY_TYPE]   = BUILD_POLICY(U32),
+        [NFTA_SET_KEY_LEN]    = BUILD_POLICY(U32),
+        [NFTA_SET_DATA_TYPE]  = BUILD_POLICY(U32),
+        [NFTA_SET_DATA_LEN]   = BUILD_POLICY(U32),
+        [NFTA_SET_POLICY]     = BUILD_POLICY(U32),
+        [NFTA_SET_ID]         = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_nft_set);
+DEFINE_POLICY_SET(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 },
+static const NLAPolicy nfnl_nft_setelem_policies[] = {
+        [NFTA_SET_ELEM_KEY]   = BUILD_POLICY_NESTED(nfnl_nft_data),
+        [NFTA_SET_ELEM_DATA]  = BUILD_POLICY_NESTED(nfnl_nft_data),
+        [NFTA_SET_ELEM_FLAGS] = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_nft_setelem);
+DEFINE_POLICY_SET(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 },
+static const NLAPolicy nfnl_nft_setelem_list_policies[] = {
+        [NFTA_SET_ELEM_LIST_TABLE]    = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+        [NFTA_SET_ELEM_LIST_SET]      = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+        [NFTA_SET_ELEM_LIST_ELEMENTS] = BUILD_POLICY_NESTED(nfnl_nft_setelem),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_nft_setelem_list);
+DEFINE_POLICY_SET(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) },
+static const NLAPolicy nfnl_subsys_nft_policies[] = {
+        [NFT_MSG_DELTABLE]   = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_table,        sizeof(struct nfgenmsg)),
+        [NFT_MSG_NEWTABLE]   = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_table,        sizeof(struct nfgenmsg)),
+        [NFT_MSG_NEWCHAIN]   = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_chain,        sizeof(struct nfgenmsg)),
+        [NFT_MSG_NEWRULE]    = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_rule,         sizeof(struct nfgenmsg)),
+        [NFT_MSG_NEWSET]     = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_set,          sizeof(struct nfgenmsg)),
+        [NFT_MSG_NEWSETELEM] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_setelem_list, sizeof(struct nfgenmsg)),
+        [NFT_MSG_DELSETELEM] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_setelem_list, sizeof(struct nfgenmsg)),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_subsys_nft);
+DEFINE_POLICY_SET(nfnl_subsys_nft);
 
-static const NLType nfnl_msg_batch_types [] = {
-        [NFNL_BATCH_GENID] = { .type = NETLINK_TYPE_U32 }
+static const NLAPolicy nfnl_msg_batch_policies[] = {
+        [NFNL_BATCH_GENID] = BUILD_POLICY(U32)
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_msg_batch);
+DEFINE_POLICY_SET(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) },
+static const NLAPolicy nfnl_subsys_none_policies[] = {
+        [NFNL_MSG_BATCH_BEGIN] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_msg_batch, sizeof(struct nfgenmsg)),
+        [NFNL_MSG_BATCH_END]   = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_msg_batch, sizeof(struct nfgenmsg)),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl_subsys_none);
+DEFINE_POLICY_SET(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 },
+static const NLAPolicy nfnl_policies[] = {
+        [NFNL_SUBSYS_NONE]     = BUILD_POLICY_NESTED(nfnl_subsys_none),
+        [NFNL_SUBSYS_NFTABLES] = BUILD_POLICY_NESTED(nfnl_subsys_nft),
 };
 
-DEFINE_TYPE_SYSTEM(nfnl);
+DEFINE_POLICY_SET(nfnl);
 
-const NLType *nfnl_get_type(uint16_t nlmsg_type) {
-        const NLTypeSystem *subsys;
+const NLAPolicy *nfnl_get_policy(uint16_t nlmsg_type) {
+        const NLAPolicySet *subsys;
 
-        subsys = type_system_get_type_system(&nfnl_type_system, nlmsg_type >> 8);
+        subsys = policy_set_get_policy_set(&nfnl_policy_set, NFNL_SUBSYS_ID(nlmsg_type));
         if (!subsys)
                 return NULL;
 
-        return type_system_get_type(subsys, nlmsg_type & ((1U << 8) - 1));
+        return policy_set_get_policy(subsys, NFNL_MSG_TYPE(nlmsg_type));
 }
index c98be6da54bc5f2aad691b36c4c121bdcd65a248..be6f7cf791766e2474144964fbea0c65861aa656 100644 (file)
 #include <linux/veth.h>
 #include <linux/wireguard.h>
 
-#include "sd-netlink.h"
-
 #include "missing_network.h"
 #include "netlink-types-internal.h"
-#include "string-table.h"
 
 enum {
         BOND_ARP_TARGETS_0,
@@ -53,1180 +50,1179 @@ enum {
 
 assert_cc(_BOND_ARP_TARGETS_MAX == BOND_MAX_ARP_TARGETS);
 
-static const NLTypeSystem rtnl_link_type_system;
-
-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 NLType rtnl_link_info_data_batadv_types[] = {
-        [IFLA_BATADV_ALGO_NAME] = { .type = NETLINK_TYPE_STRING, .size = 20 },
-};
-
-static const NLType rtnl_bond_arp_ip_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_15]       = { .type = NETLINK_TYPE_U32 },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_bond_arp_ip_target);
-
-static const NLType rtnl_bond_ad_info_types[] = {
-        [IFLA_BOND_AD_INFO_AGGREGATOR]  = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BOND_AD_INFO_NUM_PORTS]   = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BOND_AD_INFO_ACTOR_KEY]   = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BOND_AD_INFO_PARTNER_KEY] = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BOND_AD_INFO_PARTNER_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_bond_ad_info);
-
-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_ip_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, .type_system = &rtnl_bond_ad_info_type_system },
-        [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, .size = ETH_ALEN },
-        [IFLA_BOND_TLB_DYNAMIC_LB]      = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BOND_PEER_NOTIF_DELAY]    = { .type = NETLINK_TYPE_U32 },
-};
-
-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_ID]                    = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_bridge_id) },
-        [IFLA_BR_BRIDGE_ID]                  = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_bridge_id) },
-        [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_U64 },
-        [IFLA_BR_TCN_TIMER]                  = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_TOPOLOGY_CHANGE_TIMER]      = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_GC_TIMER]                   = { .type = NETLINK_TYPE_U64 },
-        [IFLA_BR_GROUP_ADDR]                 = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-        [IFLA_BR_FDB_FLUSH]                  = { .type = NETLINK_TYPE_FLAG },
-        [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_U32 },
-        [IFLA_BR_MCAST_LAST_MEMBER_CNT]      = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BR_MCAST_STARTUP_QUERY_CNT]    = { .type = NETLINK_TYPE_U32 },
-        [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_VLAN_STATS_ENABLED]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_MCAST_STATS_ENABLED]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_MCAST_IGMP_VERSION]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_MCAST_MLD_VERSION]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_VLAN_STATS_PER_PORT]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BR_MULTI_BOOLOPT]              = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct br_boolopt_multi) },
-};
-
-static const NLType rtnl_link_info_data_can_types[] = {
-        [IFLA_CAN_BITTIMING]            = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_bittiming) },
-        [IFLA_CAN_BITTIMING_CONST]      = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_bittiming_const) },
-        [IFLA_CAN_CLOCK]                = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_clock) },
-        [IFLA_CAN_STATE]                = { .type = NETLINK_TYPE_U32 },
-        [IFLA_CAN_CTRLMODE]             = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_ctrlmode) },
-        [IFLA_CAN_RESTART_MS]           = { .type = NETLINK_TYPE_U32 },
-        [IFLA_CAN_RESTART]              = { .type = NETLINK_TYPE_U32 },
-        [IFLA_CAN_BERR_COUNTER]         = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_berr_counter) },
-        [IFLA_CAN_DATA_BITTIMING]       = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_bittiming) },
-        [IFLA_CAN_DATA_BITTIMING_CONST] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_bittiming_const) },
-        [IFLA_CAN_TERMINATION]          = { .type = NETLINK_TYPE_U16 },
-        [IFLA_CAN_TERMINATION_CONST]    = { .type = NETLINK_TYPE_BINARY }, /* size = termination_const_cnt * sizeof(u16) */
-        [IFLA_CAN_BITRATE_CONST]        = { .type = NETLINK_TYPE_BINARY }, /* size = bitrate_const_cnt * sizeof(u32) */
-        [IFLA_CAN_DATA_BITRATE_CONST]   = { .type = NETLINK_TYPE_BINARY }, /* size = data_bitrate_const_cnt * sizeof(u32) */
-        [IFLA_CAN_BITRATE_MAX]          = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_geneve_types[] = {
-        [IFLA_GENEVE_ID]                = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GENEVE_REMOTE]            = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
-        [IFLA_GENEVE_TTL]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GENEVE_TOS]               = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GENEVE_PORT]              = { .type = NETLINK_TYPE_U16 },
-        [IFLA_GENEVE_COLLECT_METADATA]  = { .type = NETLINK_TYPE_FLAG },
-        [IFLA_GENEVE_REMOTE6]           = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_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_gre_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_ENCAP_LIMIT]      = { .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_COLLECT_METADATA] = { .type = NETLINK_TYPE_FLAG },
-        [IFLA_GRE_IGNORE_DF]        = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GRE_FWMARK]           = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_ERSPAN_INDEX]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GRE_ERSPAN_VER]       = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GRE_ERSPAN_DIR]       = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GRE_ERSPAN_HWID]      = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_link_info_data_ipoib_types[] = {
-        [IFLA_IPOIB_PKEY]           = { .type = NETLINK_TYPE_U16 },
-        [IFLA_IPOIB_MODE]           = { .type = NETLINK_TYPE_U16 },
-        [IFLA_IPOIB_UMCAST]         = { .type = NETLINK_TYPE_U16 },
+static const NLAPolicySet rtnl_link_policy_set;
+
+static const NLAPolicy rtnl_link_info_data_bareudp_policies[] = {
+        [IFLA_BAREUDP_PORT]            = BUILD_POLICY(U16),
+        [IFLA_BAREUDP_ETHERTYPE]       = BUILD_POLICY(U16),
+        [IFLA_BAREUDP_SRCPORT_MIN]     = BUILD_POLICY(U16),
+        [IFLA_BAREUDP_MULTIPROTO_MODE] = BUILD_POLICY(FLAG),
+};
+
+static const NLAPolicy rtnl_link_info_data_batadv_policies[] = {
+        [IFLA_BATADV_ALGO_NAME] = BUILD_POLICY_WITH_SIZE(STRING, 20),
+};
+
+static const NLAPolicy rtnl_bond_arp_ip_target_policies[] = {
+        [BOND_ARP_TARGETS_0]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_1]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_2]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_3]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_4]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_5]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_6]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_7]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_8]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_9]        = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_10]       = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_11]       = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_12]       = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_13]       = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_14]       = BUILD_POLICY(U32),
+        [BOND_ARP_TARGETS_15]       = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_bond_arp_ip_target);
+
+static const NLAPolicy rtnl_bond_ad_info_policies[] = {
+        [IFLA_BOND_AD_INFO_AGGREGATOR]  = BUILD_POLICY(U16),
+        [IFLA_BOND_AD_INFO_NUM_PORTS]   = BUILD_POLICY(U16),
+        [IFLA_BOND_AD_INFO_ACTOR_KEY]   = BUILD_POLICY(U16),
+        [IFLA_BOND_AD_INFO_PARTNER_KEY] = BUILD_POLICY(U16),
+        [IFLA_BOND_AD_INFO_PARTNER_MAC] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+};
+
+DEFINE_POLICY_SET(rtnl_bond_ad_info);
+
+static const NLAPolicy rtnl_link_info_data_bond_policies[] = {
+        [IFLA_BOND_MODE]                = BUILD_POLICY(U8),
+        [IFLA_BOND_ACTIVE_SLAVE]        = BUILD_POLICY(U32),
+        [IFLA_BOND_MIIMON]              = BUILD_POLICY(U32),
+        [IFLA_BOND_UPDELAY]             = BUILD_POLICY(U32),
+        [IFLA_BOND_DOWNDELAY]           = BUILD_POLICY(U32),
+        [IFLA_BOND_USE_CARRIER]         = BUILD_POLICY(U8),
+        [IFLA_BOND_ARP_INTERVAL]        = BUILD_POLICY(U32),
+        [IFLA_BOND_ARP_IP_TARGET]       = BUILD_POLICY_NESTED(rtnl_bond_arp_ip_target),
+        [IFLA_BOND_ARP_VALIDATE]        = BUILD_POLICY(U32),
+        [IFLA_BOND_ARP_ALL_TARGETS]     = BUILD_POLICY(U32),
+        [IFLA_BOND_PRIMARY]             = BUILD_POLICY(U32),
+        [IFLA_BOND_PRIMARY_RESELECT]    = BUILD_POLICY(U8),
+        [IFLA_BOND_FAIL_OVER_MAC]       = BUILD_POLICY(U8),
+        [IFLA_BOND_XMIT_HASH_POLICY]    = BUILD_POLICY(U8),
+        [IFLA_BOND_RESEND_IGMP]         = BUILD_POLICY(U32),
+        [IFLA_BOND_NUM_PEER_NOTIF]      = BUILD_POLICY(U8),
+        [IFLA_BOND_ALL_SLAVES_ACTIVE]   = BUILD_POLICY(U8),
+        [IFLA_BOND_MIN_LINKS]           = BUILD_POLICY(U32),
+        [IFLA_BOND_LP_INTERVAL]         = BUILD_POLICY(U32),
+        [IFLA_BOND_PACKETS_PER_SLAVE]   = BUILD_POLICY(U32),
+        [IFLA_BOND_AD_LACP_RATE]        = BUILD_POLICY(U8),
+        [IFLA_BOND_AD_SELECT]           = BUILD_POLICY(U8),
+        [IFLA_BOND_AD_INFO]             = BUILD_POLICY_NESTED(rtnl_bond_ad_info),
+        [IFLA_BOND_AD_ACTOR_SYS_PRIO]   = BUILD_POLICY(U16),
+        [IFLA_BOND_AD_USER_PORT_KEY]    = BUILD_POLICY(U16),
+        [IFLA_BOND_AD_ACTOR_SYSTEM]     = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [IFLA_BOND_TLB_DYNAMIC_LB]      = BUILD_POLICY(U8),
+        [IFLA_BOND_PEER_NOTIF_DELAY]    = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_bridge_policies[] = {
+        [IFLA_BR_FORWARD_DELAY]              = BUILD_POLICY(U32),
+        [IFLA_BR_HELLO_TIME]                 = BUILD_POLICY(U32),
+        [IFLA_BR_MAX_AGE]                    = BUILD_POLICY(U32),
+        [IFLA_BR_AGEING_TIME]                = BUILD_POLICY(U32),
+        [IFLA_BR_STP_STATE]                  = BUILD_POLICY(U32),
+        [IFLA_BR_PRIORITY]                   = BUILD_POLICY(U16),
+        [IFLA_BR_VLAN_FILTERING]             = BUILD_POLICY(U8),
+        [IFLA_BR_VLAN_PROTOCOL]              = BUILD_POLICY(U16),
+        [IFLA_BR_GROUP_FWD_MASK]             = BUILD_POLICY(U16),
+        [IFLA_BR_ROOT_ID]                    = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_bridge_id)),
+        [IFLA_BR_BRIDGE_ID]                  = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_bridge_id)),
+        [IFLA_BR_ROOT_PORT]                  = BUILD_POLICY(U16),
+        [IFLA_BR_ROOT_PATH_COST]             = BUILD_POLICY(U32),
+        [IFLA_BR_TOPOLOGY_CHANGE]            = BUILD_POLICY(U8),
+        [IFLA_BR_TOPOLOGY_CHANGE_DETECTED]   = BUILD_POLICY(U8),
+        [IFLA_BR_HELLO_TIMER]                = BUILD_POLICY(U64),
+        [IFLA_BR_TCN_TIMER]                  = BUILD_POLICY(U64),
+        [IFLA_BR_TOPOLOGY_CHANGE_TIMER]      = BUILD_POLICY(U64),
+        [IFLA_BR_GC_TIMER]                   = BUILD_POLICY(U64),
+        [IFLA_BR_GROUP_ADDR]                 = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [IFLA_BR_FDB_FLUSH]                  = BUILD_POLICY(FLAG),
+        [IFLA_BR_MCAST_ROUTER]               = BUILD_POLICY(U8),
+        [IFLA_BR_MCAST_SNOOPING]             = BUILD_POLICY(U8),
+        [IFLA_BR_MCAST_QUERY_USE_IFADDR]     = BUILD_POLICY(U8),
+        [IFLA_BR_MCAST_QUERIER]              = BUILD_POLICY(U8),
+        [IFLA_BR_MCAST_HASH_ELASTICITY]      = BUILD_POLICY(U32),
+        [IFLA_BR_MCAST_HASH_MAX]             = BUILD_POLICY(U32),
+        [IFLA_BR_MCAST_LAST_MEMBER_CNT]      = BUILD_POLICY(U32),
+        [IFLA_BR_MCAST_STARTUP_QUERY_CNT]    = BUILD_POLICY(U32),
+        [IFLA_BR_MCAST_LAST_MEMBER_INTVL]    = BUILD_POLICY(U64),
+        [IFLA_BR_MCAST_MEMBERSHIP_INTVL]     = BUILD_POLICY(U64),
+        [IFLA_BR_MCAST_QUERIER_INTVL]        = BUILD_POLICY(U64),
+        [IFLA_BR_MCAST_QUERY_INTVL]          = BUILD_POLICY(U64),
+        [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = BUILD_POLICY(U64),
+        [IFLA_BR_MCAST_STARTUP_QUERY_INTVL]  = BUILD_POLICY(U64),
+        [IFLA_BR_NF_CALL_IPTABLES]           = BUILD_POLICY(U8),
+        [IFLA_BR_NF_CALL_IP6TABLES]          = BUILD_POLICY(U8),
+        [IFLA_BR_NF_CALL_ARPTABLES]          = BUILD_POLICY(U8),
+        [IFLA_BR_VLAN_DEFAULT_PVID]          = BUILD_POLICY(U16),
+        [IFLA_BR_VLAN_STATS_ENABLED]         = BUILD_POLICY(U8),
+        [IFLA_BR_MCAST_STATS_ENABLED]        = BUILD_POLICY(U8),
+        [IFLA_BR_MCAST_IGMP_VERSION]         = BUILD_POLICY(U8),
+        [IFLA_BR_MCAST_MLD_VERSION]          = BUILD_POLICY(U8),
+        [IFLA_BR_VLAN_STATS_PER_PORT]        = BUILD_POLICY(U8),
+        [IFLA_BR_MULTI_BOOLOPT]              = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct br_boolopt_multi)),
+};
+
+static const NLAPolicy rtnl_link_info_data_can_policies[] = {
+        [IFLA_CAN_BITTIMING]            = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_bittiming)),
+        [IFLA_CAN_BITTIMING_CONST]      = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_bittiming_const)),
+        [IFLA_CAN_CLOCK]                = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_clock)),
+        [IFLA_CAN_STATE]                = BUILD_POLICY(U32),
+        [IFLA_CAN_CTRLMODE]             = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_ctrlmode)),
+        [IFLA_CAN_RESTART_MS]           = BUILD_POLICY(U32),
+        [IFLA_CAN_RESTART]              = BUILD_POLICY(U32),
+        [IFLA_CAN_BERR_COUNTER]         = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_berr_counter)),
+        [IFLA_CAN_DATA_BITTIMING]       = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_bittiming)),
+        [IFLA_CAN_DATA_BITTIMING_CONST] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_bittiming_const)),
+        [IFLA_CAN_TERMINATION]          = BUILD_POLICY(U16),
+        [IFLA_CAN_TERMINATION_CONST]    = BUILD_POLICY(BINARY), /* size = termination_const_cnt * sizeof(u16) */
+        [IFLA_CAN_BITRATE_CONST]        = BUILD_POLICY(BINARY), /* size = bitrate_const_cnt * sizeof(u32) */
+        [IFLA_CAN_DATA_BITRATE_CONST]   = BUILD_POLICY(BINARY), /* size = data_bitrate_const_cnt * sizeof(u32) */
+        [IFLA_CAN_BITRATE_MAX]          = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_geneve_policies[] = {
+        [IFLA_GENEVE_ID]                = BUILD_POLICY(U32),
+        [IFLA_GENEVE_REMOTE]            = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+        [IFLA_GENEVE_TTL]               = BUILD_POLICY(U8),
+        [IFLA_GENEVE_TOS]               = BUILD_POLICY(U8),
+        [IFLA_GENEVE_PORT]              = BUILD_POLICY(U16),
+        [IFLA_GENEVE_COLLECT_METADATA]  = BUILD_POLICY(FLAG),
+        [IFLA_GENEVE_REMOTE6]           = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [IFLA_GENEVE_UDP_CSUM]          = BUILD_POLICY(U8),
+        [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = BUILD_POLICY(U8),
+        [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = BUILD_POLICY(U8),
+        [IFLA_GENEVE_LABEL]             = BUILD_POLICY(U32),
+        [IFLA_GENEVE_TTL_INHERIT]       = BUILD_POLICY(U8),
+        [IFLA_GENEVE_DF]                = BUILD_POLICY(U8),
+};
+
+static const NLAPolicy rtnl_link_info_data_gre_policies[] = {
+        [IFLA_GRE_LINK]             = BUILD_POLICY(U32),
+        [IFLA_GRE_IFLAGS]           = BUILD_POLICY(U16),
+        [IFLA_GRE_OFLAGS]           = BUILD_POLICY(U16),
+        [IFLA_GRE_IKEY]             = BUILD_POLICY(U32),
+        [IFLA_GRE_OKEY]             = BUILD_POLICY(U32),
+        [IFLA_GRE_LOCAL]            = BUILD_POLICY(IN_ADDR),
+        [IFLA_GRE_REMOTE]           = BUILD_POLICY(IN_ADDR),
+        [IFLA_GRE_TTL]              = BUILD_POLICY(U8),
+        [IFLA_GRE_TOS]              = BUILD_POLICY(U8),
+        [IFLA_GRE_PMTUDISC]         = BUILD_POLICY(U8),
+        [IFLA_GRE_ENCAP_LIMIT]      = BUILD_POLICY(U8),
+        [IFLA_GRE_FLOWINFO]         = BUILD_POLICY(U32),
+        [IFLA_GRE_FLAGS]            = BUILD_POLICY(U32),
+        [IFLA_GRE_ENCAP_TYPE]       = BUILD_POLICY(U16),
+        [IFLA_GRE_ENCAP_FLAGS]      = BUILD_POLICY(U16),
+        [IFLA_GRE_ENCAP_SPORT]      = BUILD_POLICY(U16),
+        [IFLA_GRE_ENCAP_DPORT]      = BUILD_POLICY(U16),
+        [IFLA_GRE_COLLECT_METADATA] = BUILD_POLICY(FLAG),
+        [IFLA_GRE_IGNORE_DF]        = BUILD_POLICY(U8),
+        [IFLA_GRE_FWMARK]           = BUILD_POLICY(U32),
+        [IFLA_GRE_ERSPAN_INDEX]     = BUILD_POLICY(U32),
+        [IFLA_GRE_ERSPAN_VER]       = BUILD_POLICY(U8),
+        [IFLA_GRE_ERSPAN_DIR]       = BUILD_POLICY(U8),
+        [IFLA_GRE_ERSPAN_HWID]      = BUILD_POLICY(U16),
+};
+
+static const NLAPolicy rtnl_link_info_data_ipoib_policies[] = {
+        [IFLA_IPOIB_PKEY]           = BUILD_POLICY(U16),
+        [IFLA_IPOIB_MODE]           = BUILD_POLICY(U16),
+        [IFLA_IPOIB_UMCAST]         = BUILD_POLICY(U16),
 };
 
 /* IFLA_IPTUN_ attributes are used in ipv4/ipip.c, ipv6/ip6_tunnel.c, and ipv6/sit.c. And unfortunately,
  * IFLA_IPTUN_FLAGS is used with different types, ugh... */
-#define DEFINE_IPTUN_TYPES(name, flags_type)                                            \
-        static const NLType rtnl_link_info_data_##name##_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_ENCAP_LIMIT]         = { .type = NETLINK_TYPE_U8 },         \
-                [IFLA_IPTUN_FLOWINFO]            = { .type = NETLINK_TYPE_U32 },        \
-                [IFLA_IPTUN_FLAGS]               = { .type = flags_type },              \
-                [IFLA_IPTUN_PROTO]               = { .type = NETLINK_TYPE_U8 },         \
-                [IFLA_IPTUN_PMTUDISC]            = { .type = NETLINK_TYPE_U8 },         \
-                [IFLA_IPTUN_6RD_PREFIX]          = { .type = NETLINK_TYPE_IN_ADDR,      \
-                                                     .size = sizeof(struct in6_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 },        \
-                [IFLA_IPTUN_COLLECT_METADATA]    = { .type = NETLINK_TYPE_FLAG },       \
-                [IFLA_IPTUN_FWMARK]              = { .type = NETLINK_TYPE_U32 },        \
+#define DEFINE_IPTUN_TYPES(name, flags_type)                            \
+        static const NLAPolicy rtnl_link_info_data_##name##_policies[] = {    \
+                [IFLA_IPTUN_LINK]                = BUILD_POLICY(U32),   \
+                [IFLA_IPTUN_LOCAL]               = BUILD_POLICY(IN_ADDR), \
+                [IFLA_IPTUN_REMOTE]              = BUILD_POLICY(IN_ADDR), \
+                [IFLA_IPTUN_TTL]                 = BUILD_POLICY(U8),    \
+                [IFLA_IPTUN_TOS]                 = BUILD_POLICY(U8),    \
+                [IFLA_IPTUN_ENCAP_LIMIT]         = BUILD_POLICY(U8),    \
+                [IFLA_IPTUN_FLOWINFO]            = BUILD_POLICY(U32),   \
+                [IFLA_IPTUN_FLAGS]               = BUILD_POLICY(flags_type), \
+                [IFLA_IPTUN_PROTO]               = BUILD_POLICY(U8),    \
+                [IFLA_IPTUN_PMTUDISC]            = BUILD_POLICY(U8),    \
+                [IFLA_IPTUN_6RD_PREFIX]          = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)), \
+                [IFLA_IPTUN_6RD_RELAY_PREFIX]    = BUILD_POLICY(U32),   \
+                [IFLA_IPTUN_6RD_PREFIXLEN]       = BUILD_POLICY(U16),   \
+                [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = BUILD_POLICY(U16),   \
+                [IFLA_IPTUN_ENCAP_TYPE]          = BUILD_POLICY(U16),   \
+                [IFLA_IPTUN_ENCAP_FLAGS]         = BUILD_POLICY(U16),   \
+                [IFLA_IPTUN_ENCAP_SPORT]         = BUILD_POLICY(U16),   \
+                [IFLA_IPTUN_ENCAP_DPORT]         = BUILD_POLICY(U16),   \
+                [IFLA_IPTUN_COLLECT_METADATA]    = BUILD_POLICY(FLAG),  \
+                [IFLA_IPTUN_FWMARK]              = BUILD_POLICY(U32),   \
         }
 
-DEFINE_IPTUN_TYPES(iptun, NETLINK_TYPE_U32); /* for ipip and ip6tnl */
-DEFINE_IPTUN_TYPES(sit, NETLINK_TYPE_U16); /* for sit */
+DEFINE_IPTUN_TYPES(iptun, U32); /* for ipip and ip6tnl */
+DEFINE_IPTUN_TYPES(sit, U16); /* for sit */
 
-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_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 },
-        [IFLA_MACSEC_OFFLOAD]        = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_macvlan_macaddr_types[] = {
-        [IFLA_MACVLAN_MACADDR] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-};
-
-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_U32 },
-};
-
-static const NLType rtnl_link_info_data_tun_types[] = {
-        [IFLA_TUN_OWNER]               = { .type = NETLINK_TYPE_U32 },
-        [IFLA_TUN_GROUP]               = { .type = NETLINK_TYPE_U32 },
-        [IFLA_TUN_TYPE]                = { .type = NETLINK_TYPE_U8 },
-        [IFLA_TUN_PI]                  = { .type = NETLINK_TYPE_U8 },
-        [IFLA_TUN_VNET_HDR]            = { .type = NETLINK_TYPE_U8 },
-        [IFLA_TUN_PERSIST]             = { .type = NETLINK_TYPE_U8 },
-        [IFLA_TUN_MULTI_QUEUE]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_TUN_NUM_QUEUES]          = { .type = NETLINK_TYPE_U32 },
-        [IFLA_TUN_NUM_DISABLED_QUEUES] = { .type = NETLINK_TYPE_U32 },
-};
-
-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_vlan_qos_map_types[] = {
-        [IFLA_VLAN_QOS_MAPPING]        = { .type = NETLINK_TYPE_BINARY, .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]       = { .type = NETLINK_TYPE_BINARY, .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_vrf_types[] = {
-        [IFLA_VRF_TABLE]                 = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_vti_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 },
-        [IFLA_VTI_FWMARK]       = { .type = NETLINK_TYPE_U32 },
-};
-
-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_vxlan_types[] = {
-        [IFLA_VXLAN_ID]                = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VXLAN_GROUP]             = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
-        [IFLA_VXLAN_LINK]              = { .type = NETLINK_TYPE_U32 },
-        [IFLA_VXLAN_LOCAL]             = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct 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_BINARY, .size = sizeof(struct ifla_vxlan_port_range) },
-        [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, .size = sizeof(struct in6_addr) },
-        [IFLA_VXLAN_LOCAL6]            = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_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_link_info_data_xfrm_types[] = {
-        [IFLA_XFRM_LINK]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_XFRM_IF_ID]        = { .type = NETLINK_TYPE_U32 }
+static const NLAPolicy rtnl_link_info_data_ipvlan_policies[] = {
+        [IFLA_IPVLAN_MODE]  = BUILD_POLICY(U16),
+        [IFLA_IPVLAN_FLAGS] = BUILD_POLICY(U16),
+};
+
+static const NLAPolicy rtnl_link_info_data_macsec_policies[] = {
+        [IFLA_MACSEC_SCI]            = BUILD_POLICY(U64),
+        [IFLA_MACSEC_PORT]           = BUILD_POLICY(U16),
+        [IFLA_MACSEC_ICV_LEN]        = BUILD_POLICY(U8),
+        [IFLA_MACSEC_CIPHER_SUITE]   = BUILD_POLICY(U64),
+        [IFLA_MACSEC_WINDOW]         = BUILD_POLICY(U32),
+        [IFLA_MACSEC_ENCODING_SA]    = BUILD_POLICY(U8),
+        [IFLA_MACSEC_ENCRYPT]        = BUILD_POLICY(U8),
+        [IFLA_MACSEC_PROTECT]        = BUILD_POLICY(U8),
+        [IFLA_MACSEC_INC_SCI]        = BUILD_POLICY(U8),
+        [IFLA_MACSEC_ES]             = BUILD_POLICY(U8),
+        [IFLA_MACSEC_SCB]            = BUILD_POLICY(U8),
+        [IFLA_MACSEC_REPLAY_PROTECT] = BUILD_POLICY(U8),
+        [IFLA_MACSEC_VALIDATION]     = BUILD_POLICY(U8),
+        [IFLA_MACSEC_OFFLOAD]        = BUILD_POLICY(U8),
+};
+
+static const NLAPolicy rtnl_macvlan_macaddr_policies[] = {
+        [IFLA_MACVLAN_MACADDR] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+};
+
+DEFINE_POLICY_SET(rtnl_macvlan_macaddr);
+
+static const NLAPolicy rtnl_link_info_data_macvlan_policies[] = {
+        [IFLA_MACVLAN_MODE]              = BUILD_POLICY(U32),
+        [IFLA_MACVLAN_FLAGS]             = BUILD_POLICY(U16),
+        [IFLA_MACVLAN_MACADDR_MODE]      = BUILD_POLICY(U32),
+        [IFLA_MACVLAN_MACADDR_DATA]      = BUILD_POLICY_NESTED(rtnl_macvlan_macaddr),
+        [IFLA_MACVLAN_MACADDR_COUNT]     = BUILD_POLICY(U32),
+        [IFLA_MACVLAN_BC_QUEUE_LEN]      = BUILD_POLICY(U32),
+        [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_tun_policies[] = {
+        [IFLA_TUN_OWNER]               = BUILD_POLICY(U32),
+        [IFLA_TUN_GROUP]               = BUILD_POLICY(U32),
+        [IFLA_TUN_TYPE]                = BUILD_POLICY(U8),
+        [IFLA_TUN_PI]                  = BUILD_POLICY(U8),
+        [IFLA_TUN_VNET_HDR]            = BUILD_POLICY(U8),
+        [IFLA_TUN_PERSIST]             = BUILD_POLICY(U8),
+        [IFLA_TUN_MULTI_QUEUE]         = BUILD_POLICY(U8),
+        [IFLA_TUN_NUM_QUEUES]          = BUILD_POLICY(U32),
+        [IFLA_TUN_NUM_DISABLED_QUEUES] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_veth_policies[] = {
+        [VETH_INFO_PEER]  = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+};
+
+static const NLAPolicy rtnl_vlan_qos_map_policies[] = {
+        [IFLA_VLAN_QOS_MAPPING]        = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vlan_qos_mapping)),
+};
+
+DEFINE_POLICY_SET(rtnl_vlan_qos_map);
+
+static const NLAPolicy rtnl_link_info_data_vlan_policies[] = {
+        [IFLA_VLAN_ID]          = BUILD_POLICY(U16),
+        [IFLA_VLAN_FLAGS]       = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vlan_flags)),
+        [IFLA_VLAN_EGRESS_QOS]  = BUILD_POLICY_NESTED(rtnl_vlan_qos_map),
+        [IFLA_VLAN_INGRESS_QOS] = BUILD_POLICY_NESTED(rtnl_vlan_qos_map),
+        [IFLA_VLAN_PROTOCOL]    = BUILD_POLICY(U16),
+};
+
+static const NLAPolicy rtnl_link_info_data_vrf_policies[] = {
+        [IFLA_VRF_TABLE]                 = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_vti_policies[] = {
+        [IFLA_VTI_LINK]         = BUILD_POLICY(U32),
+        [IFLA_VTI_IKEY]         = BUILD_POLICY(U32),
+        [IFLA_VTI_OKEY]         = BUILD_POLICY(U32),
+        [IFLA_VTI_LOCAL]        = BUILD_POLICY(IN_ADDR),
+        [IFLA_VTI_REMOTE]       = BUILD_POLICY(IN_ADDR),
+        [IFLA_VTI_FWMARK]       = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_vxcan_policies[] = {
+        [VXCAN_INFO_PEER]  = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+};
+
+static const NLAPolicy rtnl_link_info_data_vxlan_policies[] = {
+        [IFLA_VXLAN_ID]                = BUILD_POLICY(U32),
+        [IFLA_VXLAN_GROUP]             = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+        [IFLA_VXLAN_LINK]              = BUILD_POLICY(U32),
+        [IFLA_VXLAN_LOCAL]             = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+        [IFLA_VXLAN_TTL]               = BUILD_POLICY(U8),
+        [IFLA_VXLAN_TOS]               = BUILD_POLICY(U8),
+        [IFLA_VXLAN_LEARNING]          = BUILD_POLICY(U8),
+        [IFLA_VXLAN_AGEING]            = BUILD_POLICY(U32),
+        [IFLA_VXLAN_LIMIT]             = BUILD_POLICY(U32),
+        [IFLA_VXLAN_PORT_RANGE]        = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vxlan_port_range)),
+        [IFLA_VXLAN_PROXY]             = BUILD_POLICY(U8),
+        [IFLA_VXLAN_RSC]               = BUILD_POLICY(U8),
+        [IFLA_VXLAN_L2MISS]            = BUILD_POLICY(U8),
+        [IFLA_VXLAN_L3MISS]            = BUILD_POLICY(U8),
+        [IFLA_VXLAN_PORT]              = BUILD_POLICY(U16),
+        [IFLA_VXLAN_GROUP6]            = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [IFLA_VXLAN_LOCAL6]            = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [IFLA_VXLAN_UDP_CSUM]          = BUILD_POLICY(U8),
+        [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = BUILD_POLICY(U8),
+        [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = BUILD_POLICY(U8),
+        [IFLA_VXLAN_REMCSUM_TX]        = BUILD_POLICY(U8),
+        [IFLA_VXLAN_REMCSUM_RX]        = BUILD_POLICY(U8),
+        [IFLA_VXLAN_GBP]               = BUILD_POLICY(FLAG),
+        [IFLA_VXLAN_REMCSUM_NOPARTIAL] = BUILD_POLICY(FLAG),
+        [IFLA_VXLAN_COLLECT_METADATA]  = BUILD_POLICY(U8),
+        [IFLA_VXLAN_LABEL]             = BUILD_POLICY(U32),
+        [IFLA_VXLAN_GPE]               = BUILD_POLICY(FLAG),
+        [IFLA_VXLAN_TTL_INHERIT]       = BUILD_POLICY(FLAG),
+        [IFLA_VXLAN_DF]                = BUILD_POLICY(U8),
+};
+
+static const NLAPolicy rtnl_link_info_data_xfrm_policies[] = {
+        [IFLA_XFRM_LINK]         = BUILD_POLICY(U32),
+        [IFLA_XFRM_IF_ID]        = BUILD_POLICY(U32)
 };
-
-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),  },
+
+static const NLAPolicySetUnionElement rtnl_link_info_data_policy_set_union_elements[] = {
+        BUILD_UNION_ELEMENT_BY_STRING("bareudp",   rtnl_link_info_data_bareudp),
+        BUILD_UNION_ELEMENT_BY_STRING("batadv",    rtnl_link_info_data_batadv),
+        BUILD_UNION_ELEMENT_BY_STRING("bond",      rtnl_link_info_data_bond),
+        BUILD_UNION_ELEMENT_BY_STRING("bridge",    rtnl_link_info_data_bridge),
 /*
-        { .name = "caif",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_caif),    },
+        BUILD_UNION_ELEMENT_BY_STRING("caif",      rtnl_link_info_data_caif),
 */
-        { .name = "can",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_can),     },
-        { .name = "erspan",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre),     },
-        { .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_gre),     },
-        { .name = "gretap",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre),     },
+        BUILD_UNION_ELEMENT_BY_STRING("can",       rtnl_link_info_data_can),
+        BUILD_UNION_ELEMENT_BY_STRING("erspan",    rtnl_link_info_data_gre),
+        BUILD_UNION_ELEMENT_BY_STRING("geneve",    rtnl_link_info_data_geneve),
+        BUILD_UNION_ELEMENT_BY_STRING("gre",       rtnl_link_info_data_gre),
+        BUILD_UNION_ELEMENT_BY_STRING("gretap",    rtnl_link_info_data_gre),
 /*
-        { .name = "gtp",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gtp),     },
-        { .name = "hsr",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_hsr),     },
+        BUILD_UNION_ELEMENT_BY_STRING("gtp",       rtnl_link_info_data_gtp),
+        BUILD_UNION_ELEMENT_BY_STRING("hsr",       rtnl_link_info_data_hsr),
 */
-        { .name = "ip6erspan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre),     },
-        { .name = "ip6gre",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre),     },
-        { .name = "ip6gretap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre),     },
-        { .name = "ip6tnl",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_iptun),   },
-        { .name = "ipoib",     .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipoib),   },
-        { .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), },
+        BUILD_UNION_ELEMENT_BY_STRING("ip6erspan", rtnl_link_info_data_gre),
+        BUILD_UNION_ELEMENT_BY_STRING("ip6gre",    rtnl_link_info_data_gre),
+        BUILD_UNION_ELEMENT_BY_STRING("ip6gretap", rtnl_link_info_data_gre),
+        BUILD_UNION_ELEMENT_BY_STRING("ip6tnl",    rtnl_link_info_data_iptun),
+        BUILD_UNION_ELEMENT_BY_STRING("ipoib",     rtnl_link_info_data_ipoib),
+        BUILD_UNION_ELEMENT_BY_STRING("ipip",      rtnl_link_info_data_iptun),
+        BUILD_UNION_ELEMENT_BY_STRING("ipvlan",    rtnl_link_info_data_ipvlan),
+        BUILD_UNION_ELEMENT_BY_STRING("ipvtap",    rtnl_link_info_data_ipvlan),
+        BUILD_UNION_ELEMENT_BY_STRING("macsec",    rtnl_link_info_data_macsec),
+        BUILD_UNION_ELEMENT_BY_STRING("macvlan",   rtnl_link_info_data_macvlan),
+        BUILD_UNION_ELEMENT_BY_STRING("macvtap",   rtnl_link_info_data_macvlan),
 /*
-        { .name = "ppp",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ppp),     },
-        { .name = "rmnet",     .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_rmnet),   },
+        BUILD_UNION_ELEMENT_BY_STRING("ppp",       rtnl_link_info_data_ppp),
+        BUILD_UNION_ELEMENT_BY_STRING("rmnet",     rtnl_link_info_data_rmnet),
 */
-        { .name = "sit",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_sit),     },
-        { .name = "tun",       .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_tun),     },
-        { .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_vti),     },
-        { .name = "vti6",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vti),     },
-        { .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),   },
+        BUILD_UNION_ELEMENT_BY_STRING("sit",       rtnl_link_info_data_sit),
+        BUILD_UNION_ELEMENT_BY_STRING("tun",       rtnl_link_info_data_tun),
+        BUILD_UNION_ELEMENT_BY_STRING("veth",      rtnl_link_info_data_veth),
+        BUILD_UNION_ELEMENT_BY_STRING("vlan",      rtnl_link_info_data_vlan),
+        BUILD_UNION_ELEMENT_BY_STRING("vrf",       rtnl_link_info_data_vrf),
+        BUILD_UNION_ELEMENT_BY_STRING("vti",       rtnl_link_info_data_vti),
+        BUILD_UNION_ELEMENT_BY_STRING("vti6",      rtnl_link_info_data_vti),
+        BUILD_UNION_ELEMENT_BY_STRING("vxcan",     rtnl_link_info_data_vxcan),
+        BUILD_UNION_ELEMENT_BY_STRING("vxlan",     rtnl_link_info_data_vxlan),
 /*
-        { .name = "wwan",      .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_wwan),    },
+        BUILD_UNION_ELEMENT_BY_STRING("wwan",      rtnl_link_info_data_wwan),
 */
-        { .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 struct NLType rtnl_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 },
-        [IFLA_BRPORT_MRP_RING_OPEN]         = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_MRP_IN_OPEN]           = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRPORT_MCAST_EHT_HOSTS_CNT]   = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystemUnionElement rtnl_link_info_slave_data_type_systems[] = {
-        { .name = "bridge",    .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_bridge_port), },
-};
-
-DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(rtnl_link_info_slave_data, IFLA_INFO_SLAVE_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 },
+        BUILD_UNION_ELEMENT_BY_STRING("xfrm",      rtnl_link_info_data_xfrm),
+};
+
+DEFINE_POLICY_SET_UNION(rtnl_link_info_data, IFLA_INFO_KIND);
+
+static const struct NLAPolicy rtnl_bridge_port_policies[] = {
+        [IFLA_BRPORT_STATE]                 = BUILD_POLICY(U8),
+        [IFLA_BRPORT_COST]                  = BUILD_POLICY(U32),
+        [IFLA_BRPORT_PRIORITY]              = BUILD_POLICY(U16),
+        [IFLA_BRPORT_MODE]                  = BUILD_POLICY(U8),
+        [IFLA_BRPORT_GUARD]                 = BUILD_POLICY(U8),
+        [IFLA_BRPORT_PROTECT]               = BUILD_POLICY(U8),
+        [IFLA_BRPORT_FAST_LEAVE]            = BUILD_POLICY(U8),
+        [IFLA_BRPORT_LEARNING]              = BUILD_POLICY(U8),
+        [IFLA_BRPORT_UNICAST_FLOOD]         = BUILD_POLICY(U8),
+        [IFLA_BRPORT_PROXYARP]              = BUILD_POLICY(U8),
+        [IFLA_BRPORT_LEARNING_SYNC]         = BUILD_POLICY(U8),
+        [IFLA_BRPORT_PROXYARP_WIFI]         = BUILD_POLICY(U8),
+        [IFLA_BRPORT_ROOT_ID]               = BUILD_POLICY(U8),
+        [IFLA_BRPORT_BRIDGE_ID]             = BUILD_POLICY(U8),
+        [IFLA_BRPORT_DESIGNATED_PORT]       = BUILD_POLICY(U16),
+        [IFLA_BRPORT_DESIGNATED_COST]       = BUILD_POLICY(U16),
+        [IFLA_BRPORT_ID]                    = BUILD_POLICY(U16),
+        [IFLA_BRPORT_NO]                    = BUILD_POLICY(U16),
+        [IFLA_BRPORT_TOPOLOGY_CHANGE_ACK]   = BUILD_POLICY(U8),
+        [IFLA_BRPORT_CONFIG_PENDING]        = BUILD_POLICY(U8),
+        [IFLA_BRPORT_MESSAGE_AGE_TIMER]     = BUILD_POLICY(U64),
+        [IFLA_BRPORT_FORWARD_DELAY_TIMER]   = BUILD_POLICY(U64),
+        [IFLA_BRPORT_HOLD_TIMER]            = BUILD_POLICY(U64),
+        [IFLA_BRPORT_FLUSH]                 = BUILD_POLICY(U8),
+        [IFLA_BRPORT_MULTICAST_ROUTER]      = BUILD_POLICY(U8),
+        [IFLA_BRPORT_PAD]                   = BUILD_POLICY(U8),
+        [IFLA_BRPORT_MCAST_FLOOD]           = BUILD_POLICY(U8),
+        [IFLA_BRPORT_MCAST_TO_UCAST]        = BUILD_POLICY(U8),
+        [IFLA_BRPORT_VLAN_TUNNEL]           = BUILD_POLICY(U8),
+        [IFLA_BRPORT_BCAST_FLOOD]           = BUILD_POLICY(U8),
+        [IFLA_BRPORT_GROUP_FWD_MASK]        = BUILD_POLICY(U16),
+        [IFLA_BRPORT_NEIGH_SUPPRESS]        = BUILD_POLICY(U8),
+        [IFLA_BRPORT_ISOLATED]              = BUILD_POLICY(U8),
+        [IFLA_BRPORT_BACKUP_PORT]           = BUILD_POLICY(U32),
+        [IFLA_BRPORT_MRP_RING_OPEN]         = BUILD_POLICY(U8),
+        [IFLA_BRPORT_MRP_IN_OPEN]           = BUILD_POLICY(U8),
+        [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = BUILD_POLICY(U32),
+        [IFLA_BRPORT_MCAST_EHT_HOSTS_CNT]   = BUILD_POLICY(U32),
+};
+
+static const NLAPolicySetUnionElement rtnl_link_info_slave_data_policy_set_union_elements[] = {
+        BUILD_UNION_ELEMENT_BY_STRING("bridge",    rtnl_bridge_port),
+};
+
+DEFINE_POLICY_SET_UNION(rtnl_link_info_slave_data, IFLA_INFO_SLAVE_KIND);
+
+static const NLAPolicy rtnl_link_info_policies[] = {
+        [IFLA_INFO_KIND]        = BUILD_POLICY(STRING),
+        [IFLA_INFO_DATA]        = BUILD_POLICY_NESTED_UNION_BY_STRING(rtnl_link_info_data),
         /* TODO: Currently IFLA_INFO_XSTATS is used only when IFLA_INFO_KIND is "can". In the future,
-         * when multiple kinds of netdevs use this attribute, then convert its type to NETLINK_TYPE_UNION. */
-        [IFLA_INFO_XSTATS]      = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_device_stats) },
-        [IFLA_INFO_SLAVE_KIND]  = { .type = NETLINK_TYPE_STRING },
-        [IFLA_INFO_SLAVE_DATA]  = { .type = NETLINK_TYPE_NESTED, .type_system_union = &rtnl_link_info_slave_data_type_system_union },
+         * when multiple kinds of netdevs use this attribute, convert its type to NETLINK_TYPE_UNION. */
+        [IFLA_INFO_XSTATS]      = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_device_stats)),
+        [IFLA_INFO_SLAVE_KIND]  = BUILD_POLICY(STRING),
+        [IFLA_INFO_SLAVE_DATA]  = BUILD_POLICY_NESTED_UNION_BY_STRING(rtnl_link_info_slave_data),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_link_info);
+DEFINE_POLICY_SET(rtnl_link_info);
 
-static const struct NLType rtnl_inet_types[] = {
-        [IFLA_INET_CONF] = { .type = NETLINK_TYPE_BINARY }, /* size = IPV4_DEVCONF_MAX * 4 */
+static const struct NLAPolicy rtnl_inet_policies[] = {
+        [IFLA_INET_CONF] = BUILD_POLICY(BINARY), /* size = IPV4_DEVCONF_MAX * 4 */
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_inet);
+DEFINE_POLICY_SET(rtnl_inet);
 
-static const struct NLType rtnl_inet6_types[] = {
-        [IFLA_INET6_FLAGS]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_INET6_CONF]          = { .type = NETLINK_TYPE_BINARY }, /* size = DEVCONF_MAX * sizeof(s32) */
-        [IFLA_INET6_STATS]         = { .type = NETLINK_TYPE_BINARY }, /* size = IPSTATS_MIB_MAX * sizeof(u64) */
+static const struct NLAPolicy rtnl_inet6_policies[] = {
+        [IFLA_INET6_FLAGS]         = BUILD_POLICY(U32),
+        [IFLA_INET6_CONF]          = BUILD_POLICY(BINARY), /* size = DEVCONF_MAX * sizeof(s32) */
+        [IFLA_INET6_STATS]         = BUILD_POLICY(BINARY), /* size = IPSTATS_MIB_MAX * sizeof(u64) */
         [IFLA_INET6_MCAST]         = {}, /* unused. */
-        [IFLA_INET6_CACHEINFO]     = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_cacheinfo) },
-        [IFLA_INET6_ICMP6STATS]    = { .type = NETLINK_TYPE_BINARY }, /* size = ICMP6_MIB_MAX * sizeof(u64) */
-        [IFLA_INET6_TOKEN]         = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
-        [IFLA_INET6_ADDR_GEN_MODE] = { .type = NETLINK_TYPE_U8 },
+        [IFLA_INET6_CACHEINFO]     = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_cacheinfo)),
+        [IFLA_INET6_ICMP6STATS]    = BUILD_POLICY(BINARY), /* size = ICMP6_MIB_MAX * sizeof(u64) */
+        [IFLA_INET6_TOKEN]         = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [IFLA_INET6_ADDR_GEN_MODE] = BUILD_POLICY(U8),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_inet6);
+DEFINE_POLICY_SET(rtnl_inet6);
 
-static const NLTypeSystemUnionElement rtnl_prot_info_type_systems[] = {
-        { .protocol = AF_BRIDGE, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_bridge_port), },
-        { .protocol = AF_INET6,  .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_inet6), },
+static const NLAPolicySetUnionElement rtnl_prot_info_policy_set_union_elements[] = {
+        BUILD_UNION_ELEMENT_BY_FAMILY(AF_BRIDGE, rtnl_bridge_port),
+        BUILD_UNION_ELEMENT_BY_FAMILY(AF_INET6,  rtnl_inet6),
 };
 
-DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(rtnl_prot_info);
+DEFINE_POLICY_SET_UNION(rtnl_prot_info, 0);
 
-static const NLType rtnl_af_spec_unspec_types[] = {
-        [AF_INET]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_inet_type_system },
-        [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_inet6_type_system },
+static const NLAPolicy rtnl_af_spec_unspec_policies[] = {
+        [AF_INET]  = BUILD_POLICY_NESTED(rtnl_inet),
+        [AF_INET6] = BUILD_POLICY_NESTED(rtnl_inet6),
 };
 
-static const NLType rtnl_bridge_vlan_tunnel_info_types[] = {
-        [IFLA_BRIDGE_VLAN_TUNNEL_ID]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_VLAN_TUNNEL_VID]   = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NETLINK_TYPE_U16 },
+static const NLAPolicy rtnl_bridge_vlan_tunnel_info_policies[] = {
+        [IFLA_BRIDGE_VLAN_TUNNEL_ID]    = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_VLAN_TUNNEL_VID]   = BUILD_POLICY(U16),
+        [IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = BUILD_POLICY(U16),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_vlan_tunnel_info);
+DEFINE_POLICY_SET(rtnl_bridge_vlan_tunnel_info);
 
-static const NLType rtnl_bridge_mrp_instance_types[] = {
-        [IFLA_BRIDGE_MRP_INSTANCE_RING_ID]      = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INSTANCE_PRIO]         = { .type = NETLINK_TYPE_U16 },
+static const NLAPolicy rtnl_bridge_mrp_instance_policies[] = {
+        [IFLA_BRIDGE_MRP_INSTANCE_RING_ID]      = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX]    = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX]    = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INSTANCE_PRIO]         = BUILD_POLICY(U16),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_instance);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_instance);
 
-static const NLType rtnl_bridge_mrp_port_state_types[] = {
-        [IFLA_BRIDGE_MRP_PORT_STATE_STATE]      = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_port_state_policies[] = {
+        [IFLA_BRIDGE_MRP_PORT_STATE_STATE]      = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_port_state);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_port_state);
 
-static const NLType rtnl_bridge_mrp_port_role_types[] = {
-        [IFLA_BRIDGE_MRP_PORT_ROLE_ROLE]        = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_port_role_policies[] = {
+        [IFLA_BRIDGE_MRP_PORT_ROLE_ROLE]        = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_port_role);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_port_role);
 
-static const NLType rtnl_bridge_mrp_ring_state_types[] = {
-        [IFLA_BRIDGE_MRP_RING_STATE_RING_ID]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_RING_STATE_STATE]      = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_ring_state_policies[] = {
+        [IFLA_BRIDGE_MRP_RING_STATE_RING_ID]    = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_RING_STATE_STATE]      = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_ring_state);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_ring_state);
 
-static const NLType rtnl_bridge_mrp_ring_role_types[] = {
-        [IFLA_BRIDGE_MRP_RING_ROLE_RING_ID]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_RING_ROLE_ROLE]        = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_ring_role_policies[] = {
+        [IFLA_BRIDGE_MRP_RING_ROLE_RING_ID]     = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_RING_ROLE_ROLE]        = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_ring_role);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_ring_role);
 
-static const NLType rtnl_bridge_mrp_start_test_types[] = {
-        [IFLA_BRIDGE_MRP_START_TEST_RING_ID]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_START_TEST_INTERVAL]   = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_START_TEST_MAX_MISS]   = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_START_TEST_PERIOD]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_START_TEST_MONITOR]    = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_start_test_policies[] = {
+        [IFLA_BRIDGE_MRP_START_TEST_RING_ID]    = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_START_TEST_INTERVAL]   = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_START_TEST_MAX_MISS]   = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_START_TEST_PERIOD]     = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_START_TEST_MONITOR]    = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_start_test);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_start_test);
 
-static const NLType rtnl_bridge_mrp_info_types[] = {
-        [IFLA_BRIDGE_MRP_INFO_RING_ID]          = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_P_IFINDEX]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_S_IFINDEX]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_PRIO]             = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRIDGE_MRP_INFO_RING_STATE]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_RING_ROLE]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_TEST_INTERVAL]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_TEST_MAX_MISS]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_TEST_MONITOR]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_I_IFINDEX]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_IN_STATE]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_IN_ROLE]          = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_IN_TEST_INTERVAL] = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_INFO_IN_TEST_MAX_MISS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_info_policies[] = {
+        [IFLA_BRIDGE_MRP_INFO_RING_ID]          = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_P_IFINDEX]        = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_S_IFINDEX]        = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_PRIO]             = BUILD_POLICY(U16),
+        [IFLA_BRIDGE_MRP_INFO_RING_STATE]       = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_RING_ROLE]        = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_TEST_INTERVAL]    = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_TEST_MAX_MISS]    = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_TEST_MONITOR]     = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_I_IFINDEX]        = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_IN_STATE]         = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_IN_ROLE]          = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_IN_TEST_INTERVAL] = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_INFO_IN_TEST_MAX_MISS] = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_info);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_info);
 
-static const NLType rtnl_bridge_mrp_in_role_types[] = {
-        [IFLA_BRIDGE_MRP_IN_ROLE_RING_ID]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_IN_ROLE_IN_ID]         = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRIDGE_MRP_IN_ROLE_ROLE]          = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_IN_ROLE_I_IFINDEX]     = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_in_role_policies[] = {
+        [IFLA_BRIDGE_MRP_IN_ROLE_RING_ID]       = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_IN_ROLE_IN_ID]         = BUILD_POLICY(U16),
+        [IFLA_BRIDGE_MRP_IN_ROLE_ROLE]          = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_IN_ROLE_I_IFINDEX]     = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_in_role);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_in_role);
 
-static const NLType rtnl_bridge_mrp_in_state_types[] = {
-        [IFLA_BRIDGE_MRP_IN_STATE_IN_ID]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_IN_STATE_STATE]        = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_in_state_policies[] = {
+        [IFLA_BRIDGE_MRP_IN_STATE_IN_ID]        = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_IN_STATE_STATE]        = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_in_state);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_in_state);
 
-static const NLType rtnl_bridge_mrp_start_in_test_types[] = {
-        [IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD]   = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_start_in_test_policies[] = {
+        [IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID]    = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD]   = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_start_in_test);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_start_in_test);
 
-static const NLType rtnl_bridge_mrp_types[] = {
-        [IFLA_BRIDGE_MRP_INSTANCE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_instance_type_system },
-        [IFLA_BRIDGE_MRP_PORT_STATE]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_port_state_type_system },
-        [IFLA_BRIDGE_MRP_PORT_ROLE]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_port_role_type_system },
-        [IFLA_BRIDGE_MRP_RING_STATE]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_ring_state_type_system },
-        [IFLA_BRIDGE_MRP_RING_ROLE]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_ring_role_type_system },
-        [IFLA_BRIDGE_MRP_START_TEST]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_start_test_type_system },
-        [IFLA_BRIDGE_MRP_INFO]          = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_info_type_system },
-        [IFLA_BRIDGE_MRP_IN_ROLE]       = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_in_role_type_system },
-        [IFLA_BRIDGE_MRP_IN_STATE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_in_state_type_system },
-        [IFLA_BRIDGE_MRP_START_IN_TEST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_start_in_test_type_system },
+static const NLAPolicy rtnl_bridge_mrp_policies[] = {
+        [IFLA_BRIDGE_MRP_INSTANCE]      = BUILD_POLICY_NESTED(rtnl_bridge_mrp_instance),
+        [IFLA_BRIDGE_MRP_PORT_STATE]    = BUILD_POLICY_NESTED(rtnl_bridge_mrp_port_state),
+        [IFLA_BRIDGE_MRP_PORT_ROLE]     = BUILD_POLICY_NESTED(rtnl_bridge_mrp_port_role),
+        [IFLA_BRIDGE_MRP_RING_STATE]    = BUILD_POLICY_NESTED(rtnl_bridge_mrp_ring_state),
+        [IFLA_BRIDGE_MRP_RING_ROLE]     = BUILD_POLICY_NESTED(rtnl_bridge_mrp_ring_role),
+        [IFLA_BRIDGE_MRP_START_TEST]    = BUILD_POLICY_NESTED(rtnl_bridge_mrp_start_test),
+        [IFLA_BRIDGE_MRP_INFO]          = BUILD_POLICY_NESTED(rtnl_bridge_mrp_info),
+        [IFLA_BRIDGE_MRP_IN_ROLE]       = BUILD_POLICY_NESTED(rtnl_bridge_mrp_in_role),
+        [IFLA_BRIDGE_MRP_IN_STATE]      = BUILD_POLICY_NESTED(rtnl_bridge_mrp_in_state),
+        [IFLA_BRIDGE_MRP_START_IN_TEST] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_start_in_test),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp);
+DEFINE_POLICY_SET(rtnl_bridge_mrp);
 
-static const NLType rtnl_bridge_cfm_mep_create_types[] = {
-        [IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE]   = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION]  = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX]    = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_mep_create_policies[] = {
+        [IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE]   = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN]     = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION]  = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX]    = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_mep_create);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_mep_create);
 
-static const NLType rtnl_bridge_cfm_mep_delete_types[] = {
-        [IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE]   = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_mep_delete_policies[] = {
+        [IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE]   = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_mep_delete);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_mep_delete);
 
-static const NLType rtnl_bridge_cfm_mep_config_types[] = {
-        [IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-        [IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID]       = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_mep_config_policies[] = {
+        [IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE]    = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL]     = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID]       = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_mep_config);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_mep_config);
 
-static const NLType rtnl_bridge_cfm_cc_config_types[] = {
-        [IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID]     = { .type = NETLINK_TYPE_BINARY, .size = CFM_MAID_LENGTH },
+static const NLAPolicy rtnl_bridge_cfm_cc_config_policies[] = {
+        [IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE]     = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE]       = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID]     = BUILD_POLICY_WITH_SIZE(BINARY, CFM_MAID_LENGTH),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_config);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_config);
 
-static const NLType rtnl_bridge_cfm_cc_peer_mep_types[] = {
-        [IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]  = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_PEER_MEPID]         = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_cc_peer_mep_policies[] = {
+        [IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE]  = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_PEER_MEPID]         = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_peer_mep);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_peer_mep);
 
-static const NLType rtnl_bridge_cfm_cc_rdi_types[] = {
-        [IFLA_BRIDGE_CFM_CC_RDI_INSTANCE]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_RDI_RDI]            = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_cc_rdi_policies[] = {
+        [IFLA_BRIDGE_CFM_CC_RDI_INSTANCE]       = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_RDI_RDI]            = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_rdi);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_rdi);
 
-static const NLType rtnl_bridge_cfm_cc_ccm_tx_types[] = {
-        [IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC]           = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-        [IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE]  = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV]         = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE]   = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE] = { .type = NETLINK_TYPE_U8 },
+static const NLAPolicy rtnl_bridge_cfm_cc_ccm_tx_policies[] = {
+        [IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE]       = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC]           = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+        [IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE]  = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD]         = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV]         = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE]   = BUILD_POLICY(U8),
+        [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV]       = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE] = BUILD_POLICY(U8),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_ccm_tx);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_ccm_tx);
 
-static const NLType rtnl_bridge_cfm_mep_status_types[] = {
-        [IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE]           = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN]  = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN] = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN]  = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_mep_status_policies[] = {
+        [IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE]           = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN]  = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN] = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN]  = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_mep_status);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_mep_status);
 
-static const NLType rtnl_bridge_cfm_cc_peer_status_types[] = {
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI]            = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE] = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE]   = { .type = NETLINK_TYPE_U8 },
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN]           = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_cc_peer_status_policies[] = {
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE]       = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID]     = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT]     = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI]            = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE] = BUILD_POLICY(U8),
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE]   = BUILD_POLICY(U8),
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN]           = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN]       = BUILD_POLICY(U32),
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN] = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_peer_status);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_peer_status);
 
-static const NLType rtnl_bridge_cfm_types[] = {
-        [IFLA_BRIDGE_CFM_MEP_CREATE]          = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_create_type_system },
-        [IFLA_BRIDGE_CFM_MEP_DELETE]          = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_delete_type_system },
-        [IFLA_BRIDGE_CFM_MEP_CONFIG]          = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_config_type_system },
-        [IFLA_BRIDGE_CFM_CC_CONFIG]           = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_config_type_system },
-        [IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_peer_mep_type_system },
-        [IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE]  = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_peer_mep_type_system },
-        [IFLA_BRIDGE_CFM_CC_RDI]              = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_rdi_type_system },
-        [IFLA_BRIDGE_CFM_CC_CCM_TX]           = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_ccm_tx_type_system },
-        [IFLA_BRIDGE_CFM_MEP_CREATE_INFO]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_create_type_system },
-        [IFLA_BRIDGE_CFM_MEP_CONFIG_INFO]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_config_type_system },
-        [IFLA_BRIDGE_CFM_CC_CONFIG_INFO]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_config_type_system },
-        [IFLA_BRIDGE_CFM_CC_RDI_INFO]         = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_rdi_type_system },
-        [IFLA_BRIDGE_CFM_CC_CCM_TX_INFO]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_ccm_tx_type_system },
-        [IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_peer_mep_type_system },
-        [IFLA_BRIDGE_CFM_MEP_STATUS_INFO]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_status_type_system },
-        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_peer_status_type_system },
+static const NLAPolicy rtnl_bridge_cfm_policies[] = {
+        [IFLA_BRIDGE_CFM_MEP_CREATE]          = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_create),
+        [IFLA_BRIDGE_CFM_MEP_DELETE]          = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_delete),
+        [IFLA_BRIDGE_CFM_MEP_CONFIG]          = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_config),
+        [IFLA_BRIDGE_CFM_CC_CONFIG]           = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_config),
+        [IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD]     = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_peer_mep),
+        [IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE]  = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_peer_mep),
+        [IFLA_BRIDGE_CFM_CC_RDI]              = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_rdi),
+        [IFLA_BRIDGE_CFM_CC_CCM_TX]           = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_ccm_tx),
+        [IFLA_BRIDGE_CFM_MEP_CREATE_INFO]     = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_create),
+        [IFLA_BRIDGE_CFM_MEP_CONFIG_INFO]     = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_config),
+        [IFLA_BRIDGE_CFM_CC_CONFIG_INFO]      = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_config),
+        [IFLA_BRIDGE_CFM_CC_RDI_INFO]         = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_rdi),
+        [IFLA_BRIDGE_CFM_CC_CCM_TX_INFO]      = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_ccm_tx),
+        [IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO]    = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_peer_mep),
+        [IFLA_BRIDGE_CFM_MEP_STATUS_INFO]     = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_status),
+        [IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_peer_status),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm);
+DEFINE_POLICY_SET(rtnl_bridge_cfm);
 
-static const NLType rtnl_af_spec_bridge_types[] = {
-        [IFLA_BRIDGE_FLAGS]            = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRIDGE_MODE]             = { .type = NETLINK_TYPE_U16 },
-        [IFLA_BRIDGE_VLAN_INFO]        = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct bridge_vlan_info) },
-        [IFLA_BRIDGE_VLAN_TUNNEL_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_vlan_tunnel_info_type_system },
-        [IFLA_BRIDGE_MRP]              = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_type_system },
-        [IFLA_BRIDGE_CFM]              = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_type_system },
+static const NLAPolicy rtnl_af_spec_bridge_policies[] = {
+        [IFLA_BRIDGE_FLAGS]            = BUILD_POLICY(U16),
+        [IFLA_BRIDGE_MODE]             = BUILD_POLICY(U16),
+        [IFLA_BRIDGE_VLAN_INFO]        = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct bridge_vlan_info)),
+        [IFLA_BRIDGE_VLAN_TUNNEL_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_vlan_tunnel_info),
+        [IFLA_BRIDGE_MRP]              = BUILD_POLICY_NESTED(rtnl_bridge_mrp),
+        [IFLA_BRIDGE_CFM]              = BUILD_POLICY_NESTED(rtnl_bridge_cfm),
 };
 
-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), },
+static const NLAPolicySetUnionElement rtnl_af_spec_policy_set_union_elements[] = {
+        BUILD_UNION_ELEMENT_BY_FAMILY(AF_UNSPEC, rtnl_af_spec_unspec),
+        BUILD_UNION_ELEMENT_BY_FAMILY(AF_BRIDGE, rtnl_af_spec_bridge),
 };
 
-DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(rtnl_af_spec);
+DEFINE_POLICY_SET_UNION(rtnl_af_spec, 0);
 
-static const NLType rtnl_prop_list_types[] = {
-        [IFLA_ALT_IFNAME]       = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
+static const NLAPolicy rtnl_prop_list_policies[] = {
+        [IFLA_ALT_IFNAME]       = BUILD_POLICY_WITH_SIZE(STRING, ALTIFNAMSIZ - 1),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_prop_list);
+DEFINE_POLICY_SET(rtnl_prop_list);
 
-static const NLType rtnl_vf_vlan_list_types[] = {
-        [IFLA_VF_VLAN_INFO]  = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_vlan_info) },
+static const NLAPolicy rtnl_vf_vlan_list_policies[] = {
+        [IFLA_VF_VLAN_INFO]  = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_vlan_info)),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_vf_vlan_list);
+DEFINE_POLICY_SET(rtnl_vf_vlan_list);
 
-static const NLType rtnl_vf_info_types[] = {
-        [IFLA_VF_MAC]           = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_mac) },
-        [IFLA_VF_VLAN]          = { .type = NETLINK_TYPE_BINARY, .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]       = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_tx_rate) },
-        [IFLA_VF_SPOOFCHK]      = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_spoofchk) },
-        [IFLA_VF_RATE]          = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_rate) },
-        [IFLA_VF_LINK_STATE]    = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_link_state) },
-        [IFLA_VF_RSS_QUERY_EN]  = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_rss_query_en) },
-        [IFLA_VF_TRUST]         = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_trust) },
-        [IFLA_VF_IB_NODE_GUID]  = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_guid) },
-        [IFLA_VF_IB_PORT_GUID]  = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_guid) },
+static const NLAPolicy rtnl_vf_info_policies[] = {
+        [IFLA_VF_MAC]           = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_mac)),
+        [IFLA_VF_VLAN]          = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_vlan)),
+        [IFLA_VF_VLAN_LIST]     = BUILD_POLICY_NESTED(rtnl_vf_vlan_list),
+        [IFLA_VF_TX_RATE]       = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_tx_rate)),
+        [IFLA_VF_SPOOFCHK]      = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_spoofchk)),
+        [IFLA_VF_RATE]          = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_rate)),
+        [IFLA_VF_LINK_STATE]    = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_link_state)),
+        [IFLA_VF_RSS_QUERY_EN]  = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_rss_query_en)),
+        [IFLA_VF_TRUST]         = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_trust)),
+        [IFLA_VF_IB_NODE_GUID]  = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_guid)),
+        [IFLA_VF_IB_PORT_GUID]  = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_guid)),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_vf_info);
+DEFINE_POLICY_SET(rtnl_vf_info);
 
-static const NLType rtnl_vfinfo_list_types[] = {
-        [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_info_type_system },
+static const NLAPolicy rtnl_vfinfo_list_policies[] = {
+        [IFLA_VF_INFO] = BUILD_POLICY_NESTED(rtnl_vf_info),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_vfinfo_list);
+DEFINE_POLICY_SET(rtnl_vfinfo_list);
 
-static const NLType rtnl_vf_port_types[] = {
-        [IFLA_PORT_VF]            = { .type = NETLINK_TYPE_U32 },
-        [IFLA_PORT_PROFILE]       = { .type = NETLINK_TYPE_STRING },
-        [IFLA_PORT_VSI_TYPE]      = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_port_vsi) },
-        [IFLA_PORT_INSTANCE_UUID] = { .type = NETLINK_TYPE_BINARY, .size = PORT_UUID_MAX },
-        [IFLA_PORT_HOST_UUID]     = { .type = NETLINK_TYPE_BINARY, .size = PORT_UUID_MAX },
-        [IFLA_PORT_REQUEST]       = { .type = NETLINK_TYPE_U8 },
-        [IFLA_PORT_RESPONSE]      = { .type = NETLINK_TYPE_U16 },
+static const NLAPolicy rtnl_vf_port_policies[] = {
+        [IFLA_PORT_VF]            = BUILD_POLICY(U32),
+        [IFLA_PORT_PROFILE]       = BUILD_POLICY(STRING),
+        [IFLA_PORT_VSI_TYPE]      = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_port_vsi)),
+        [IFLA_PORT_INSTANCE_UUID] = BUILD_POLICY_WITH_SIZE(BINARY, PORT_UUID_MAX),
+        [IFLA_PORT_HOST_UUID]     = BUILD_POLICY_WITH_SIZE(BINARY, PORT_UUID_MAX),
+        [IFLA_PORT_REQUEST]       = BUILD_POLICY(U8),
+        [IFLA_PORT_RESPONSE]      = BUILD_POLICY(U16),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_vf_port);
+DEFINE_POLICY_SET(rtnl_vf_port);
 
-static const NLType rtnl_vf_ports_types[] = {
-        [IFLA_VF_PORT] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_port_type_system },
+static const NLAPolicy rtnl_vf_ports_policies[] = {
+        [IFLA_VF_PORT] = BUILD_POLICY_NESTED(rtnl_vf_port),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_vf_ports);
+DEFINE_POLICY_SET(rtnl_vf_ports);
 
-static const NLType rtnl_xdp_types[] = {
-        [IFLA_XDP_FD]          = { .type = NETLINK_TYPE_S32 },
-        [IFLA_XDP_ATTACHED]    = { .type = NETLINK_TYPE_U8 },
-        [IFLA_XDP_FLAGS]       = { .type = NETLINK_TYPE_U32 },
-        [IFLA_XDP_PROG_ID]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_XDP_DRV_PROG_ID] = { .type = NETLINK_TYPE_U32 },
-        [IFLA_XDP_SKB_PROG_ID] = { .type = NETLINK_TYPE_U32 },
-        [IFLA_XDP_HW_PROG_ID]  = { .type = NETLINK_TYPE_U32 },
-        [IFLA_XDP_EXPECTED_FD] = { .type = NETLINK_TYPE_S32 },
+static const NLAPolicy rtnl_xdp_policies[] = {
+        [IFLA_XDP_FD]          = BUILD_POLICY(S32),
+        [IFLA_XDP_ATTACHED]    = BUILD_POLICY(U8),
+        [IFLA_XDP_FLAGS]       = BUILD_POLICY(U32),
+        [IFLA_XDP_PROG_ID]     = BUILD_POLICY(U32),
+        [IFLA_XDP_DRV_PROG_ID] = BUILD_POLICY(U32),
+        [IFLA_XDP_SKB_PROG_ID] = BUILD_POLICY(U32),
+        [IFLA_XDP_HW_PROG_ID]  = BUILD_POLICY(U32),
+        [IFLA_XDP_EXPECTED_FD] = BUILD_POLICY(S32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_xdp);
+DEFINE_POLICY_SET(rtnl_xdp);
 
-static const NLType rtnl_proto_down_reason_types[] = {
-        [IFLA_PROTO_DOWN_REASON_MASK]  = { .type = NETLINK_TYPE_U32 },
-        [IFLA_PROTO_DOWN_REASON_VALUE] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_proto_down_reason_policies[] = {
+        [IFLA_PROTO_DOWN_REASON_MASK]  = BUILD_POLICY(U32),
+        [IFLA_PROTO_DOWN_REASON_VALUE] = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_proto_down_reason);
+DEFINE_POLICY_SET(rtnl_proto_down_reason);
 
-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]               = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct rtnl_link_stats) },
+static const NLAPolicy rtnl_link_policies[] = {
+        [IFLA_ADDRESS]             = BUILD_POLICY(ETHER_ADDR),
+        [IFLA_BROADCAST]           = BUILD_POLICY(ETHER_ADDR),
+        [IFLA_IFNAME]              = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ - 1),
+        [IFLA_MTU]                 = BUILD_POLICY(U32),
+        [IFLA_LINK]                = BUILD_POLICY(U32),
+        [IFLA_QDISC]               = BUILD_POLICY(STRING),
+        [IFLA_STATS]               = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rtnl_link_stats)),
         [IFLA_COST]                = { /* Not used. */ },
         [IFLA_PRIORITY]            = { /* Not used. */ },
-        [IFLA_MASTER]              = { .type = NETLINK_TYPE_U32 },
+        [IFLA_MASTER]              = BUILD_POLICY(U32),
         [IFLA_WIRELESS]            = { /* Used only by wext. */ },
-        [IFLA_PROTINFO]            = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union },
-        [IFLA_TXQLEN]              = { .type = NETLINK_TYPE_U32 },
-        [IFLA_MAP]                 = { .type = NETLINK_TYPE_BINARY, .size = 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]             = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct rtnl_link_stats64) },
-        [IFLA_VF_PORTS]            = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_ports_type_system },
-        [IFLA_PORT_SELF]           = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_port_type_system },
-        [IFLA_AF_SPEC]             = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_af_spec_type_system_union },
-        [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_CARRIER]             = { .type = NETLINK_TYPE_U8 },
-        [IFLA_PHYS_PORT_ID]        = { .type = NETLINK_TYPE_BINARY, .size = MAX_PHYS_ITEM_ID_LEN },
-        [IFLA_CARRIER_CHANGES]     = { .type = NETLINK_TYPE_U32 },
-        [IFLA_PHYS_SWITCH_ID]      = { .type = NETLINK_TYPE_BINARY, .size = MAX_PHYS_ITEM_ID_LEN },
-        [IFLA_LINK_NETNSID]        = { .type = NETLINK_TYPE_S32 },
-        [IFLA_PHYS_PORT_NAME]      = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
-        [IFLA_PROTO_DOWN]          = { .type = NETLINK_TYPE_U8 },
-        [IFLA_GSO_MAX_SEGS]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_GSO_MAX_SIZE]        = { .type = NETLINK_TYPE_U32 },
-        [IFLA_XDP]                 = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_xdp_type_system },
-        [IFLA_EVENT]               = { .type = NETLINK_TYPE_U32 },
-        [IFLA_NEW_NETNSID]         = { .type = NETLINK_TYPE_S32 },
-        [IFLA_TARGET_NETNSID]      = { .type = NETLINK_TYPE_S32 },
-        [IFLA_CARRIER_UP_COUNT]    = { .type = NETLINK_TYPE_U32 },
-        [IFLA_CARRIER_DOWN_COUNT]  = { .type = NETLINK_TYPE_U32 },
-        [IFLA_NEW_IFINDEX]         = { .type = NETLINK_TYPE_S32 },
-        [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 },
-        [IFLA_PERM_ADDRESS]        = { .type = NETLINK_TYPE_ETHER_ADDR },
-        [IFLA_PROTO_DOWN_REASON]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_proto_down_reason_type_system },
-        [IFLA_PARENT_DEV_NAME]     = { .type = NETLINK_TYPE_STRING, },
-        [IFLA_PARENT_DEV_BUS_NAME] = { .type = NETLINK_TYPE_STRING, },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_link);
+        [IFLA_PROTINFO]            = BUILD_POLICY_NESTED_UNION_BY_FAMILY(rtnl_prot_info),
+        [IFLA_TXQLEN]              = BUILD_POLICY(U32),
+        [IFLA_MAP]                 = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rtnl_link_ifmap)),
+        [IFLA_WEIGHT]              = BUILD_POLICY(U32),
+        [IFLA_OPERSTATE]           = BUILD_POLICY(U8),
+        [IFLA_LINKMODE]            = BUILD_POLICY(U8),
+        [IFLA_LINKINFO]            = BUILD_POLICY_NESTED(rtnl_link_info),
+        [IFLA_NET_NS_PID]          = BUILD_POLICY(U32),
+        [IFLA_IFALIAS]             = BUILD_POLICY_WITH_SIZE(STRING, IFALIASZ - 1),
+        [IFLA_NUM_VF]              = BUILD_POLICY(U32),
+        [IFLA_VFINFO_LIST]         = BUILD_POLICY_NESTED(rtnl_vfinfo_list),
+        [IFLA_STATS64]             = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rtnl_link_stats64)),
+        [IFLA_VF_PORTS]            = BUILD_POLICY_NESTED(rtnl_vf_ports),
+        [IFLA_PORT_SELF]           = BUILD_POLICY_NESTED(rtnl_vf_port),
+        [IFLA_AF_SPEC]             = BUILD_POLICY_NESTED_UNION_BY_FAMILY(rtnl_af_spec),
+        [IFLA_GROUP]               = BUILD_POLICY(U32),
+        [IFLA_NET_NS_FD]           = BUILD_POLICY(U32),
+        [IFLA_EXT_MASK]            = BUILD_POLICY(U32),
+        [IFLA_PROMISCUITY]         = BUILD_POLICY(U32),
+        [IFLA_NUM_TX_QUEUES]       = BUILD_POLICY(U32),
+        [IFLA_NUM_RX_QUEUES]       = BUILD_POLICY(U32),
+        [IFLA_CARRIER]             = BUILD_POLICY(U8),
+        [IFLA_PHYS_PORT_ID]        = BUILD_POLICY_WITH_SIZE(BINARY, MAX_PHYS_ITEM_ID_LEN),
+        [IFLA_CARRIER_CHANGES]     = BUILD_POLICY(U32),
+        [IFLA_PHYS_SWITCH_ID]      = BUILD_POLICY_WITH_SIZE(BINARY, MAX_PHYS_ITEM_ID_LEN),
+        [IFLA_LINK_NETNSID]        = BUILD_POLICY(S32),
+        [IFLA_PHYS_PORT_NAME]      = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ - 1),
+        [IFLA_PROTO_DOWN]          = BUILD_POLICY(U8),
+        [IFLA_GSO_MAX_SEGS]        = BUILD_POLICY(U32),
+        [IFLA_GSO_MAX_SIZE]        = BUILD_POLICY(U32),
+        [IFLA_XDP]                 = BUILD_POLICY_NESTED(rtnl_xdp),
+        [IFLA_EVENT]               = BUILD_POLICY(U32),
+        [IFLA_NEW_NETNSID]         = BUILD_POLICY(S32),
+        [IFLA_TARGET_NETNSID]      = BUILD_POLICY(S32),
+        [IFLA_CARRIER_UP_COUNT]    = BUILD_POLICY(U32),
+        [IFLA_CARRIER_DOWN_COUNT]  = BUILD_POLICY(U32),
+        [IFLA_NEW_IFINDEX]         = BUILD_POLICY(S32),
+        [IFLA_MIN_MTU]             = BUILD_POLICY(U32),
+        [IFLA_MAX_MTU]             = BUILD_POLICY(U32),
+        [IFLA_PROP_LIST]           = BUILD_POLICY_NESTED(rtnl_prop_list),
+        [IFLA_ALT_IFNAME]          = BUILD_POLICY_WITH_SIZE(STRING, ALTIFNAMSIZ - 1),
+        [IFLA_PERM_ADDRESS]        = BUILD_POLICY(ETHER_ADDR),
+        [IFLA_PROTO_DOWN_REASON]   = BUILD_POLICY_NESTED(rtnl_proto_down_reason),
+        [IFLA_PARENT_DEV_NAME]     = BUILD_POLICY(STRING),
+        [IFLA_PARENT_DEV_BUS_NAME] = BUILD_POLICY(STRING),
+};
+
+DEFINE_POLICY_SET(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, .size = sizeof(struct in6_addr) },
-        [IFA_CACHEINFO]         = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) },
-        [IFA_MULTICAST]         = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
-        [IFA_FLAGS]             = { .type = NETLINK_TYPE_U32 },
-        [IFA_RT_PRIORITY]       = { .type = NETLINK_TYPE_U32 },
-        [IFA_TARGET_NETNSID]    = { .type = NETLINK_TYPE_S32 },
+static const NLAPolicy rtnl_address_policies[] = {
+        [IFA_ADDRESS]           = BUILD_POLICY(IN_ADDR),
+        [IFA_LOCAL]             = BUILD_POLICY(IN_ADDR),
+        [IFA_LABEL]             = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ - 1),
+        [IFA_BROADCAST]         = BUILD_POLICY(IN_ADDR),
+        [IFA_ANYCAST]           = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [IFA_CACHEINFO]         = BUILD_POLICY_WITH_SIZE(CACHE_INFO, sizeof(struct ifa_cacheinfo)),
+        [IFA_MULTICAST]         = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [IFA_FLAGS]             = BUILD_POLICY(U32),
+        [IFA_RT_PRIORITY]       = BUILD_POLICY(U32),
+        [IFA_TARGET_NETNSID]    = BUILD_POLICY(S32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_address);
+DEFINE_POLICY_SET(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]         = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct rtnexthop) },
-        [RTA_FLOW]              = { .type = NETLINK_TYPE_U32 }, /* 6? */
-        [RTA_CACHEINFO]         = { .type = NETLINK_TYPE_BINARY, .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 },
+static const NLAPolicy rtnl_route_metrics_policies[] = {
+        [RTAX_MTU]                = BUILD_POLICY(U32),
+        [RTAX_WINDOW]             = BUILD_POLICY(U32),
+        [RTAX_RTT]                = BUILD_POLICY(U32),
+        [RTAX_RTTVAR]             = BUILD_POLICY(U32),
+        [RTAX_SSTHRESH]           = BUILD_POLICY(U32),
+        [RTAX_CWND]               = BUILD_POLICY(U32),
+        [RTAX_ADVMSS]             = BUILD_POLICY(U32),
+        [RTAX_REORDERING]         = BUILD_POLICY(U32),
+        [RTAX_HOPLIMIT]           = BUILD_POLICY(U32),
+        [RTAX_INITCWND]           = BUILD_POLICY(U32),
+        [RTAX_FEATURES]           = BUILD_POLICY(U32),
+        [RTAX_RTO_MIN]            = BUILD_POLICY(U32),
+        [RTAX_INITRWND]           = BUILD_POLICY(U32),
+        [RTAX_QUICKACK]           = BUILD_POLICY(U32),
+        [RTAX_CC_ALGO]            = BUILD_POLICY(U32),
+        [RTAX_FASTOPEN_NO_COOKIE] = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_route_metrics);
+
+static const NLAPolicy rtnl_route_policies[] = {
+        [RTA_DST]               = BUILD_POLICY(IN_ADDR),
+        [RTA_SRC]               = BUILD_POLICY(IN_ADDR),
+        [RTA_IIF]               = BUILD_POLICY(U32),
+        [RTA_OIF]               = BUILD_POLICY(U32),
+        [RTA_GATEWAY]           = BUILD_POLICY(IN_ADDR),
+        [RTA_PRIORITY]          = BUILD_POLICY(U32),
+        [RTA_PREFSRC]           = BUILD_POLICY(IN_ADDR),
+        [RTA_METRICS]           = BUILD_POLICY_NESTED(rtnl_route_metrics),
+        [RTA_MULTIPATH]         = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rtnexthop)),
+        [RTA_FLOW]              = BUILD_POLICY(U32),
+        [RTA_CACHEINFO]         = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rta_cacheinfo)),
+        [RTA_TABLE]             = BUILD_POLICY(U32),
+        [RTA_MARK]              = BUILD_POLICY(U32),
+        [RTA_MFC_STATS]         = BUILD_POLICY(U64),
+        [RTA_VIA]               = BUILD_POLICY(BINARY), /* See struct rtvia */
+        [RTA_NEWDST]            = BUILD_POLICY(U32),
+        [RTA_PREF]              = BUILD_POLICY(U8),
+        [RTA_ENCAP_TYPE]        = BUILD_POLICY(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]            = { .type = NETLINK_TYPE_ETHER_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]           = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct fib_rule_uid_range) },
-        [FRA_PROTOCOL]            = { .type = NETLINK_TYPE_U8 },
-        [FRA_IP_PROTO]            = { .type = NETLINK_TYPE_U8 },
-        [FRA_SPORT_RANGE]         = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct fib_rule_port_range) },
-        [FRA_DPORT_RANGE]         = { .type = NETLINK_TYPE_BINARY, .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 },
+        [RTA_EXPIRES]           = BUILD_POLICY(U32),
+        [RTA_UID]               = BUILD_POLICY(U32),
+        [RTA_TTL_PROPAGATE]     = BUILD_POLICY(U8),
+        [RTA_IP_PROTO]          = BUILD_POLICY(U8),
+        [RTA_SPORT]             = BUILD_POLICY(U16),
+        [RTA_DPORT]             = BUILD_POLICY(U16),
+        [RTA_NH_ID]             = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_route);
+
+static const NLAPolicy rtnl_neigh_policies[] = {
+        [NDA_DST]               = BUILD_POLICY(IN_ADDR),
+        [NDA_LLADDR]            = BUILD_POLICY(ETHER_ADDR),
+        [NDA_CACHEINFO]         = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct nda_cacheinfo)),
+        [NDA_PROBES]            = BUILD_POLICY(U32),
+        [NDA_VLAN]              = BUILD_POLICY(U16),
+        [NDA_PORT]              = BUILD_POLICY(U16),
+        [NDA_VNI]               = BUILD_POLICY(U32),
+        [NDA_IFINDEX]           = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_neigh);
+
+static const NLAPolicy rtnl_addrlabel_policies[] = {
+        [IFAL_ADDRESS]         = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+        [IFAL_LABEL]           = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_addrlabel);
+
+static const NLAPolicy rtnl_routing_policy_rule_policies[] = {
+        [FRA_DST]                 = BUILD_POLICY(IN_ADDR),
+        [FRA_SRC]                 = BUILD_POLICY(IN_ADDR),
+        [FRA_IIFNAME]             = BUILD_POLICY(STRING),
+        [FRA_GOTO]                = BUILD_POLICY(U32),
+        [FRA_PRIORITY]            = BUILD_POLICY(U32),
+        [FRA_FWMARK]              = BUILD_POLICY(U32),
+        [FRA_FLOW]                = BUILD_POLICY(U32),
+        [FRA_TUN_ID]              = BUILD_POLICY(U64),
+        [FRA_SUPPRESS_IFGROUP]    = BUILD_POLICY(U32),
+        [FRA_SUPPRESS_PREFIXLEN]  = BUILD_POLICY(U32),
+        [FRA_TABLE]               = BUILD_POLICY(U32),
+        [FRA_FWMASK]              = BUILD_POLICY(U32),
+        [FRA_OIFNAME]             = BUILD_POLICY(STRING),
+        [FRA_PAD]                 = BUILD_POLICY(U32),
+        [FRA_L3MDEV]              = BUILD_POLICY(U8),
+        [FRA_UID_RANGE]           = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct fib_rule_uid_range)),
+        [FRA_PROTOCOL]            = BUILD_POLICY(U8),
+        [FRA_IP_PROTO]            = BUILD_POLICY(U8),
+        [FRA_SPORT_RANGE]         = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct fib_rule_port_range)),
+        [FRA_DPORT_RANGE]         = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct fib_rule_port_range)),
+};
+
+DEFINE_POLICY_SET(rtnl_routing_policy_rule);
+
+static const NLAPolicy rtnl_nexthop_policies[] = {
+        [NHA_ID]                  = BUILD_POLICY(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_GROUP_TYPE]          = BUILD_POLICY(U16),
+        [NHA_BLACKHOLE]           = BUILD_POLICY(FLAG),
+        [NHA_OIF]                 = BUILD_POLICY(U32),
+        [NHA_GATEWAY]             = BUILD_POLICY(IN_ADDR),
+        [NHA_ENCAP_TYPE]          = BUILD_POLICY(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 },
+        [NHA_GROUPS]              = BUILD_POLICY(FLAG),
+        [NHA_MASTER]              = BUILD_POLICY(U32),
+        [NHA_FDB]                 = BUILD_POLICY(FLAG),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_nexthop);
+DEFINE_POLICY_SET(rtnl_nexthop);
 
-static const NLType rtnl_tca_option_data_cake_types[] = {
-        [TCA_CAKE_BASE_RATE64]   = { .type = NETLINK_TYPE_U64 },
-        [TCA_CAKE_DIFFSERV_MODE] = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_ATM]           = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_FLOW_MODE]     = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_OVERHEAD]      = { .type = NETLINK_TYPE_S32 },
-        [TCA_CAKE_RTT]           = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_TARGET]        = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_AUTORATE]      = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_MEMORY]        = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_NAT]           = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_RAW]           = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_WASH]          = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_MPU]           = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_INGRESS]       = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_ACK_FILTER]    = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_SPLIT_GSO]     = { .type = NETLINK_TYPE_U32 },
-        [TCA_CAKE_FWMARK]        = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_tca_option_data_cake_policies[] = {
+        [TCA_CAKE_BASE_RATE64]   = BUILD_POLICY(U64),
+        [TCA_CAKE_DIFFSERV_MODE] = BUILD_POLICY(U32),
+        [TCA_CAKE_ATM]           = BUILD_POLICY(U32),
+        [TCA_CAKE_FLOW_MODE]     = BUILD_POLICY(U32),
+        [TCA_CAKE_OVERHEAD]      = BUILD_POLICY(S32),
+        [TCA_CAKE_RTT]           = BUILD_POLICY(U32),
+        [TCA_CAKE_TARGET]        = BUILD_POLICY(U32),
+        [TCA_CAKE_AUTORATE]      = BUILD_POLICY(U32),
+        [TCA_CAKE_MEMORY]        = BUILD_POLICY(U32),
+        [TCA_CAKE_NAT]           = BUILD_POLICY(U32),
+        [TCA_CAKE_RAW]           = BUILD_POLICY(U32),
+        [TCA_CAKE_WASH]          = BUILD_POLICY(U32),
+        [TCA_CAKE_MPU]           = BUILD_POLICY(U32),
+        [TCA_CAKE_INGRESS]       = BUILD_POLICY(U32),
+        [TCA_CAKE_ACK_FILTER]    = BUILD_POLICY(U32),
+        [TCA_CAKE_SPLIT_GSO]     = BUILD_POLICY(U32),
+        [TCA_CAKE_FWMARK]        = BUILD_POLICY(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 NLAPolicy rtnl_tca_option_data_codel_policies[] = {
+        [TCA_CODEL_TARGET]        = BUILD_POLICY(U32),
+        [TCA_CODEL_LIMIT]         = BUILD_POLICY(U32),
+        [TCA_CODEL_INTERVAL]      = BUILD_POLICY(U32),
+        [TCA_CODEL_ECN]           = BUILD_POLICY(U32),
+        [TCA_CODEL_CE_THRESHOLD]  = BUILD_POLICY(U32),
 };
 
-static const NLType rtnl_tca_option_data_drr_types[] = {
-        [TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_tca_option_data_drr_policies[] = {
+        [TCA_DRR_QUANTUM] = BUILD_POLICY(U32),
 };
 
-static const NLType rtnl_tca_option_data_ets_quanta_types[] = {
-        [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32, },
+static const NLAPolicy rtnl_tca_option_data_ets_quanta_policies[] = {
+        [TCA_ETS_QUANTA_BAND] = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_tca_option_data_ets_quanta);
+DEFINE_POLICY_SET(rtnl_tca_option_data_ets_quanta);
 
-static const NLType rtnl_tca_option_data_ets_prio_types[] = {
-        [TCA_ETS_PRIOMAP_BAND] = { .type = NETLINK_TYPE_U8, },
+static const NLAPolicy rtnl_tca_option_data_ets_prio_policies[] = {
+        [TCA_ETS_PRIOMAP_BAND] = BUILD_POLICY(U8),
 };
 
-DEFINE_TYPE_SYSTEM(rtnl_tca_option_data_ets_prio);
+DEFINE_POLICY_SET(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 NLAPolicy rtnl_tca_option_data_ets_policies[] = {
+        [TCA_ETS_NBANDS]      = BUILD_POLICY(U8),
+        [TCA_ETS_NSTRICT]     = BUILD_POLICY(U8),
+        [TCA_ETS_QUANTA]      = BUILD_POLICY_NESTED(rtnl_tca_option_data_ets_quanta),
+        [TCA_ETS_PRIOMAP]     = BUILD_POLICY_NESTED(rtnl_tca_option_data_ets_prio),
+        [TCA_ETS_QUANTA_BAND] = BUILD_POLICY(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 NLAPolicy rtnl_tca_option_data_fq_policies[] = {
+        [TCA_FQ_PLIMIT]             = BUILD_POLICY(U32),
+        [TCA_FQ_FLOW_PLIMIT]        = BUILD_POLICY(U32),
+        [TCA_FQ_QUANTUM]            = BUILD_POLICY(U32),
+        [TCA_FQ_INITIAL_QUANTUM]    = BUILD_POLICY(U32),
+        [TCA_FQ_RATE_ENABLE]        = BUILD_POLICY(U32),
+        [TCA_FQ_FLOW_DEFAULT_RATE]  = BUILD_POLICY(U32),
+        [TCA_FQ_FLOW_MAX_RATE]      = BUILD_POLICY(U32),
+        [TCA_FQ_BUCKETS_LOG]        = BUILD_POLICY(U32),
+        [TCA_FQ_FLOW_REFILL_DELAY]  = BUILD_POLICY(U32),
+        [TCA_FQ_LOW_RATE_THRESHOLD] = BUILD_POLICY(U32),
+        [TCA_FQ_CE_THRESHOLD]       = BUILD_POLICY(U32),
+        [TCA_FQ_ORPHAN_MASK]        = BUILD_POLICY(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 NLAPolicy rtnl_tca_option_data_fq_codel_policies[] = {
+        [TCA_FQ_CODEL_TARGET]          = BUILD_POLICY(U32),
+        [TCA_FQ_CODEL_LIMIT]           = BUILD_POLICY(U32),
+        [TCA_FQ_CODEL_INTERVAL]        = BUILD_POLICY(U32),
+        [TCA_FQ_CODEL_ECN]             = BUILD_POLICY(U32),
+        [TCA_FQ_CODEL_FLOWS]           = BUILD_POLICY(U32),
+        [TCA_FQ_CODEL_QUANTUM]         = BUILD_POLICY(U32),
+        [TCA_FQ_CODEL_CE_THRESHOLD]    = BUILD_POLICY(U32),
+        [TCA_FQ_CODEL_DROP_BATCH_SIZE] = BUILD_POLICY(U32),
+        [TCA_FQ_CODEL_MEMORY_LIMIT]    = BUILD_POLICY(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] = { .type = NETLINK_TYPE_BINARY, .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]  = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct tc_htb_opt) },
-        [TCA_HTB_INIT]   = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct tc_htb_glob) },
-        [TCA_HTB_CTAB]   = { .type = NETLINK_TYPE_BINARY, .size = TC_RTAB_SIZE },
-        [TCA_HTB_RTAB]   = { .type = NETLINK_TYPE_BINARY, .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] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct tc_sfb_qopt) },
+static const NLAPolicy rtnl_tca_option_data_fq_pie_policies[] = {
+        [TCA_FQ_PIE_LIMIT]   = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_tca_option_data_gred_policies[] = {
+        [TCA_GRED_DPS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_gred_sopt)),
+};
+
+static const NLAPolicy rtnl_tca_option_data_hhf_policies[] = {
+        [TCA_HHF_BACKLOG_LIMIT] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_tca_option_data_htb_policies[] = {
+        [TCA_HTB_PARMS]  = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_htb_opt)),
+        [TCA_HTB_INIT]   = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_htb_glob)),
+        [TCA_HTB_CTAB]   = BUILD_POLICY_WITH_SIZE(BINARY, TC_RTAB_SIZE),
+        [TCA_HTB_RTAB]   = BUILD_POLICY_WITH_SIZE(BINARY, TC_RTAB_SIZE),
+        [TCA_HTB_RATE64] = BUILD_POLICY(U64),
+        [TCA_HTB_CEIL64] = BUILD_POLICY(U64),
+};
+
+static const NLAPolicy rtnl_tca_option_data_pie_policies[] = {
+        [TCA_PIE_LIMIT]   = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_tca_option_data_qfq_policies[] = {
+        [TCA_QFQ_WEIGHT] = BUILD_POLICY(U32),
+        [TCA_QFQ_LMAX]   = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_tca_option_data_sfb_policies[] = {
+        [TCA_SFB_PARMS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_sfb_qopt)),
 };
 
-static const NLType rtnl_tca_option_data_tbf_types[] = {
-        [TCA_TBF_PARMS]   = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct tc_tbf_qopt) },
-        [TCA_TBF_RTAB]    = { .type = NETLINK_TYPE_BINARY, .size = TC_RTAB_SIZE },
-        [TCA_TBF_PTAB]    = { .type = NETLINK_TYPE_BINARY, .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 NLAPolicy rtnl_tca_option_data_tbf_policies[] = {
+        [TCA_TBF_PARMS]   = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_tbf_qopt)),
+        [TCA_TBF_RTAB]    = BUILD_POLICY_WITH_SIZE(BINARY, TC_RTAB_SIZE),
+        [TCA_TBF_PTAB]    = BUILD_POLICY_WITH_SIZE(BINARY, TC_RTAB_SIZE),
+        [TCA_TBF_RATE64]  = BUILD_POLICY(U64),
+        [TCA_TBF_PRATE64] = BUILD_POLICY(U64),
+        [TCA_TBF_BURST]   = BUILD_POLICY(U32),
+        [TCA_TBF_PBURST]  = BUILD_POLICY(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]     = { .type = NETLINK_TYPE_BINARY, .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);
+static const NLAPolicySetUnionElement rtnl_tca_option_data_policy_set_union_elements[] = {
+        BUILD_UNION_ELEMENT_BY_STRING("cake",     rtnl_tca_option_data_cake),
+        BUILD_UNION_ELEMENT_BY_STRING("codel",    rtnl_tca_option_data_codel),
+        BUILD_UNION_ELEMENT_BY_STRING("drr",      rtnl_tca_option_data_drr),
+        BUILD_UNION_ELEMENT_BY_STRING("ets",      rtnl_tca_option_data_ets),
+        BUILD_UNION_ELEMENT_BY_STRING("fq",       rtnl_tca_option_data_fq),
+        BUILD_UNION_ELEMENT_BY_STRING("fq_codel", rtnl_tca_option_data_fq_codel),
+        BUILD_UNION_ELEMENT_BY_STRING("fq_pie",   rtnl_tca_option_data_fq_pie),
+        BUILD_UNION_ELEMENT_BY_STRING("gred",     rtnl_tca_option_data_gred),
+        BUILD_UNION_ELEMENT_BY_STRING("hhf",      rtnl_tca_option_data_hhf),
+        BUILD_UNION_ELEMENT_BY_STRING("htb",      rtnl_tca_option_data_htb),
+        BUILD_UNION_ELEMENT_BY_STRING("pie",      rtnl_tca_option_data_pie),
+        BUILD_UNION_ELEMENT_BY_STRING("qfq",      rtnl_tca_option_data_qfq),
+        BUILD_UNION_ELEMENT_BY_STRING("sfb",      rtnl_tca_option_data_sfb),
+        BUILD_UNION_ELEMENT_BY_STRING("tbf",      rtnl_tca_option_data_tbf),
+};
+
+DEFINE_POLICY_SET_UNION(rtnl_tca_option_data, TCA_KIND);
+
+static const NLAPolicy rtnl_tca_policies[] = {
+        [TCA_KIND]           = BUILD_POLICY(STRING),
+        [TCA_OPTIONS]        = BUILD_POLICY_NESTED_UNION_BY_STRING(rtnl_tca_option_data),
+        [TCA_INGRESS_BLOCK]  = BUILD_POLICY(U32),
+        [TCA_EGRESS_BLOCK]   = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_tca);
+
+static const NLAPolicy rtnl_mdb_policies[] = {
+        [MDBA_SET_ENTRY]     = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct br_port_msg)),
+};
+
+DEFINE_POLICY_SET(rtnl_mdb);
+
+static const NLAPolicy rtnl_policies[] = {
+        [RTM_NEWLINK]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+        [RTM_DELLINK]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+        [RTM_GETLINK]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+        [RTM_SETLINK]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+        [RTM_NEWLINKPROP]  = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+        [RTM_DELLINKPROP]  = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+        [RTM_GETLINKPROP]  = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+        [RTM_NEWADDR]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_address, sizeof(struct ifaddrmsg)),
+        [RTM_DELADDR]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_address, sizeof(struct ifaddrmsg)),
+        [RTM_GETADDR]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_address, sizeof(struct ifaddrmsg)),
+        [RTM_NEWROUTE]     = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_route, sizeof(struct rtmsg)),
+        [RTM_DELROUTE]     = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_route, sizeof(struct rtmsg)),
+        [RTM_GETROUTE]     = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_route, sizeof(struct rtmsg)),
+        [RTM_NEWNEIGH]     = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_neigh, sizeof(struct ndmsg)),
+        [RTM_DELNEIGH]     = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_neigh, sizeof(struct ndmsg)),
+        [RTM_GETNEIGH]     = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_neigh, sizeof(struct ndmsg)),
+        [RTM_NEWADDRLABEL] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_addrlabel, sizeof(struct ifaddrlblmsg)),
+        [RTM_DELADDRLABEL] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_addrlabel, sizeof(struct ifaddrlblmsg)),
+        [RTM_GETADDRLABEL] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_addrlabel, sizeof(struct ifaddrlblmsg)),
+        [RTM_NEWRULE]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_routing_policy_rule, sizeof(struct fib_rule_hdr)),
+        [RTM_DELRULE]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_routing_policy_rule, sizeof(struct fib_rule_hdr)),
+        [RTM_GETRULE]      = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_routing_policy_rule, sizeof(struct fib_rule_hdr)),
+        [RTM_NEWNEXTHOP]   = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nexthop, sizeof(struct nhmsg)),
+        [RTM_DELNEXTHOP]   = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nexthop, sizeof(struct nhmsg)),
+        [RTM_GETNEXTHOP]   = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nexthop, sizeof(struct nhmsg)),
+        [RTM_NEWQDISC]     = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+        [RTM_DELQDISC]     = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+        [RTM_GETQDISC]     = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+        [RTM_NEWTCLASS]    = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+        [RTM_DELTCLASS]    = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+        [RTM_GETTCLASS]    = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+        [RTM_NEWMDB]       = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
+        [RTM_DELMDB]       = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
+        [RTM_GETMDB]       = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
+};
+
+DEFINE_POLICY_SET(rtnl);
+
+const NLAPolicy *rtnl_get_policy(uint16_t nlmsg_type) {
+        return policy_set_get_policy(&rtnl_policy_set, nlmsg_type);
 }
index 0a2a3effb28067cfdaeb91f1754201152ef835e6..21ef80c2ece987dfc96f073458b09b08c64491de 100644 (file)
 #include "netlink-internal.h"
 #include "netlink-types-internal.h"
 
-static const NLType empty_types[1] = {
+static const NLAPolicy empty_policies[1] = {
         /* fake array to avoid .types==NULL, which denotes invalid type-systems */
 };
 
-DEFINE_TYPE_SYSTEM(empty);
+DEFINE_POLICY_SET(empty);
 
-static const NLType error_types[] = {
-        [NLMSGERR_ATTR_MSG]  = { .type = NETLINK_TYPE_STRING },
-        [NLMSGERR_ATTR_OFFS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy error_policies[] = {
+        [NLMSGERR_ATTR_MSG]  = BUILD_POLICY(STRING),
+        [NLMSGERR_ATTR_OFFS] = BUILD_POLICY(U32),
 };
 
-DEFINE_TYPE_SYSTEM(error);
+DEFINE_POLICY_SET(error);
 
-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 NLAPolicy basic_policies[] = {
+        [NLMSG_DONE]  = BUILD_POLICY_NESTED(empty),
+        [NLMSG_ERROR] = BUILD_POLICY_NESTED_WITH_SIZE(error, sizeof(struct nlmsgerr)),
 };
 
-DEFINE_TYPE_SYSTEM(basic);
+DEFINE_POLICY_SET(basic);
 
-uint16_t type_get_type(const NLType *type) {
-        assert(type);
-        return type->type;
+NLAType policy_get_type(const NLAPolicy *policy) {
+        return ASSERT_PTR(policy)->type;
 }
 
-size_t type_get_size(const NLType *type) {
-        assert(type);
-        return type->size;
+size_t policy_get_size(const NLAPolicy *policy) {
+        return ASSERT_PTR(policy)->size;
 }
 
-const NLTypeSystem *type_get_type_system(const NLType *nl_type) {
-        assert(nl_type);
-        assert(nl_type->type == NETLINK_TYPE_NESTED);
-        assert(nl_type->type_system);
-        return nl_type->type_system;
+const NLAPolicySet *policy_get_policy_set(const NLAPolicy *policy) {
+        assert(policy);
+        assert(policy->type == NETLINK_TYPE_NESTED);
+
+        return ASSERT_PTR(policy->policy_set);
 }
 
-const NLTypeSystemUnion *type_get_type_system_union(const NLType *nl_type) {
-        assert(nl_type);
-        assert(nl_type->type == NETLINK_TYPE_UNION);
-        assert(nl_type->type_system_union);
-        return nl_type->type_system_union;
+const NLAPolicySetUnion *policy_get_policy_set_union(const NLAPolicy *policy) {
+        assert(policy);
+        assert(IN_SET(policy->type, NETLINK_TYPE_NESTED_UNION_BY_STRING, NETLINK_TYPE_NESTED_UNION_BY_FAMILY));
+
+        return ASSERT_PTR(policy->policy_set_union);
 }
 
-int type_system_root_get_type_system_and_header_size(
+int netlink_get_policy_set_and_header_size(
                 sd_netlink *nl,
                 uint16_t type,
-                const NLTypeSystem **ret_type_system,
+                const NLAPolicySet **ret_policy_set,
                 size_t *ret_header_size) {
 
-        const NLType *nl_type;
+        const NLAPolicy *policy;
 
         assert(nl);
 
         if (IN_SET(type, NLMSG_DONE, NLMSG_ERROR))
-                nl_type = type_system_get_type(&basic_type_system, type);
+                policy = policy_set_get_policy(&basic_policy_set, type);
         else
                 switch (nl->protocol) {
                 case NETLINK_ROUTE:
-                        nl_type = rtnl_get_type(type);
+                        policy = rtnl_get_policy(type);
                         break;
                 case NETLINK_NETFILTER:
-                        nl_type = nfnl_get_type(type);
+                        policy = nfnl_get_policy(type);
                         break;
                 case NETLINK_GENERIC:
-                        return genl_get_type_system_and_header_size(nl, type, ret_type_system, ret_header_size);
+                        return genl_get_policy_set_and_header_size(nl, type, ret_policy_set, ret_header_size);
                 default:
                         return -EOPNOTSUPP;
                 }
-        if (!nl_type)
+        if (!policy)
                 return -EOPNOTSUPP;
 
-        if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
+        if (policy_get_type(policy) != NETLINK_TYPE_NESTED)
                 return -EOPNOTSUPP;
 
-        if (ret_type_system)
-                *ret_type_system = type_get_type_system(nl_type);
+        if (ret_policy_set)
+                *ret_policy_set = policy_get_policy_set(policy);
         if (ret_header_size)
-                *ret_header_size = type_get_size(nl_type);
+                *ret_header_size = policy_get_size(policy);
         return 0;
 }
 
-const NLType *type_system_get_type(const NLTypeSystem *type_system, uint16_t type) {
-        const NLType *nl_type;
+const NLAPolicy *policy_set_get_policy(const NLAPolicySet *policy_set, uint16_t attr_type) {
+        const NLAPolicy *policy;
 
-        assert(type_system);
-        assert(type_system->types);
+        assert(policy_set);
+        assert(policy_set->policies);
 
-        if (type >= type_system->count)
+        if (attr_type >= policy_set->count)
                 return NULL;
 
-        nl_type = &type_system->types[type];
+        policy = &policy_set->policies[attr_type];
 
-        if (nl_type->type == NETLINK_TYPE_UNSPEC)
+        if (policy->type == NETLINK_TYPE_UNSPEC)
                 return NULL;
 
-        return nl_type;
+        return policy;
 }
 
-const NLTypeSystem *type_system_get_type_system(const NLTypeSystem *type_system, uint16_t type) {
-        const NLType *nl_type;
+const NLAPolicySet *policy_set_get_policy_set(const NLAPolicySet *policy_set, uint16_t attr_type) {
+        const NLAPolicy *policy;
 
-        nl_type = type_system_get_type(type_system, type);
-        if (!nl_type)
+        policy = policy_set_get_policy(policy_set, attr_type);
+        if (!policy)
                 return NULL;
 
-        return type_get_type_system(nl_type);
+        return policy_get_policy_set(policy);
 }
 
-const NLTypeSystemUnion *type_system_get_type_system_union(const NLTypeSystem *type_system, uint16_t type) {
-        const NLType *nl_type;
+const NLAPolicySetUnion *policy_set_get_policy_set_union(const NLAPolicySet *policy_set, uint16_t attr_type) {
+        const NLAPolicy *policy;
 
-        nl_type = type_system_get_type(type_system, type);
-        if (!nl_type)
+        policy = policy_set_get_policy(policy_set, attr_type);
+        if (!policy)
                 return NULL;
 
-        return type_get_type_system_union(nl_type);
+        return policy_get_policy_set_union(policy);
 }
 
-NLMatchType type_system_union_get_match_type(const NLTypeSystemUnion *type_system_union) {
-        assert(type_system_union);
-        return type_system_union->match_type;
-}
+uint16_t policy_set_union_get_match_attribute(const NLAPolicySetUnion *policy_set_union) {
+        assert(policy_set_union->match_attribute != 0);
 
-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;
+        return policy_set_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(key);
+const NLAPolicySet *policy_set_union_get_policy_set_by_string(const NLAPolicySetUnion *policy_set_union, const char *string) {
+        assert(policy_set_union);
+        assert(policy_set_union->elements);
+        assert(string);
 
-        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;
+        for (size_t i = 0; i < policy_set_union->count; i++)
+                if (streq(policy_set_union->elements[i].string, string))
+                        return &policy_set_union->elements[i].policy_set;
 
         return NULL;
 }
 
-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->elements);
-        assert(type_system_union->match_type == NL_MATCH_PROTOCOL);
+const NLAPolicySet *policy_set_union_get_policy_set_by_family(const NLAPolicySetUnion *policy_set_union, int family) {
+        assert(policy_set_union);
+        assert(policy_set_union->elements);
 
-        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;
+        for (size_t i = 0; i < policy_set_union->count; i++)
+                if (policy_set_union->elements[i].family == family)
+                        return &policy_set_union->elements[i].policy_set;
 
         return NULL;
 }
index d481f3072fbec6149ca4b9a429fa1dd4e716b0d3..e034a984e9b0d0e1860d8804fc5f2d77e9f6290e 100644 (file)
@@ -1,10 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <errno.h>
+
 #include "sd-netlink.h"
 
-enum {
-        NETLINK_TYPE_UNSPEC,
+typedef enum NLAType {
+        NETLINK_TYPE_UNSPEC,                    /* NLA_UNSPEC */
+        NETLINK_TYPE_BINARY,                    /* NLA_BINARY */
+        NETLINK_TYPE_FLAG,                      /* NLA_FLAG */
         NETLINK_TYPE_U8,                        /* NLA_U8 */
         NETLINK_TYPE_U16,                       /* NLA_U16 */
         NETLINK_TYPE_U32,                       /* NLA_U32 */
@@ -14,51 +18,46 @@ enum {
         NETLINK_TYPE_S32,                       /* NLA_S32 */
         NETLINK_TYPE_S64,                       /* NLA_S64 */
         NETLINK_TYPE_STRING,                    /* NLA_STRING */
-        NETLINK_TYPE_FLAG,                      /* NLA_FLAG */
+        NETLINK_TYPE_BITFIELD32,                /* NLA_BITFIELD32 */
+        NETLINK_TYPE_REJECT,                    /* NLA_REJECT */
         NETLINK_TYPE_IN_ADDR,
         NETLINK_TYPE_ETHER_ADDR,
         NETLINK_TYPE_CACHE_INFO,
-        NETLINK_TYPE_NESTED,                    /* NLA_NESTED */
-        NETLINK_TYPE_UNION,
         NETLINK_TYPE_SOCKADDR,
-        NETLINK_TYPE_BINARY,
-        NETLINK_TYPE_BITFIELD32,                /* NLA_BITFIELD32 */
-        NETLINK_TYPE_REJECT,                    /* NLA_REJECT */
-};
-
-typedef enum NLMatchType {
-        NL_MATCH_SIBLING,
-        NL_MATCH_PROTOCOL,
-} NLMatchType;
-
-typedef struct NLTypeSystemUnion NLTypeSystemUnion;
-typedef struct NLTypeSystem NLTypeSystem;
-typedef struct NLType NLType;
-
-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(
+        NETLINK_TYPE_NESTED,                    /* NLA_NESTED */
+        NETLINK_TYPE_NESTED_UNION_BY_STRING,
+        NETLINK_TYPE_NESTED_UNION_BY_FAMILY,
+        _NETLINK_TYPE_MAX,
+        _NETLINK_TYPE_INVALID = -EINVAL,
+} NLAType;
+
+typedef struct NLAPolicy NLAPolicy;
+typedef struct NLAPolicySet NLAPolicySet;
+typedef struct NLAPolicySetUnion NLAPolicySetUnion;
+
+const NLAPolicy *rtnl_get_policy(uint16_t nlmsg_type);
+const NLAPolicy *nfnl_get_policy(uint16_t nlmsg_type);
+const NLAPolicySet *genl_get_policy_set_by_name(const char *name);
+int genl_get_policy_set_and_header_size(
                 sd_netlink *nl,
                 uint16_t id,
-                const NLTypeSystem **ret_type_system,
+                const NLAPolicySet **ret_policy_set,
                 size_t *ret_header_size);
 
-uint16_t type_get_type(const NLType *type);
-size_t type_get_size(const NLType *type);
-const NLTypeSystem *type_get_type_system(const NLType *type);
-const NLTypeSystemUnion *type_get_type_system_union(const NLType *type);
+NLAType policy_get_type(const NLAPolicy *policy);
+size_t policy_get_size(const NLAPolicy *policy);
+const NLAPolicySet *policy_get_policy_set(const NLAPolicy *policy);
+const NLAPolicySetUnion *policy_get_policy_set_union(const NLAPolicy *policy);
 
-int type_system_root_get_type_system_and_header_size(
+int netlink_get_policy_set_and_header_size(
                 sd_netlink *nl,
                 uint16_t type,
-                const NLTypeSystem **ret_type_system,
+                const NLAPolicySet **ret_policy_set,
                 size_t *ret_header_size);
 
-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);
+const NLAPolicy *policy_set_get_policy(const NLAPolicySet *policy_set, uint16_t attr_type);
+const NLAPolicySet *policy_set_get_policy_set(const NLAPolicySet *type_system, uint16_t attr_type);
+const NLAPolicySetUnion *policy_set_get_policy_set_union(const NLAPolicySet *type_system, uint16_t attr_type);
+uint16_t policy_set_union_get_match_attribute(const NLAPolicySetUnion *policy_set_union);
+const NLAPolicySet *policy_set_union_get_policy_set_by_string(const NLAPolicySetUnion *type_system_union, const char *string);
+const NLAPolicySet *policy_set_union_get_policy_set_by_family(const NLAPolicySetUnion *type_system_union, int family);
index 606d14eeafec37cca130f25c39fbb5930c054a37..9473e1570056e21b486d00ad4ea757b6b90e16b4 100644 (file)
@@ -29,46 +29,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(MultipathRoute*, multipath_route_free);
 
 int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret);
 
-static inline bool rtnl_message_type_is_neigh(uint16_t type) {
-        return IN_SET(type, RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH);
-}
-
-static inline bool rtnl_message_type_is_route(uint16_t type) {
-        return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE);
-}
-
-static inline bool rtnl_message_type_is_nexthop(uint16_t type) {
-        return IN_SET(type, RTM_NEWNEXTHOP, RTM_GETNEXTHOP, RTM_DELNEXTHOP);
-}
-
-static inline bool rtnl_message_type_is_link(uint16_t type) {
-        return IN_SET(type,
-                      RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK,
-                      RTM_NEWLINKPROP, RTM_DELLINKPROP, RTM_GETLINKPROP);
-}
-
-static inline bool rtnl_message_type_is_addr(uint16_t type) {
-        return IN_SET(type, RTM_NEWADDR, RTM_GETADDR, RTM_DELADDR);
-}
-
-static inline bool rtnl_message_type_is_addrlabel(uint16_t type) {
-        return IN_SET(type, RTM_NEWADDRLABEL, RTM_DELADDRLABEL, RTM_GETADDRLABEL);
-}
-
-static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) {
-        return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE);
-}
-
-static inline bool rtnl_message_type_is_traffic_control(uint16_t type) {
-        return IN_SET(type,
-                      RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC,
-                      RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS);
-}
-
-static inline bool rtnl_message_type_is_mdb(uint16_t type) {
-        return IN_SET(type, RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB);
-}
-
 int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
 int rtnl_set_link_properties(
                 sd_netlink **rtnl,
index fbc3ef06094fb342198d3b0462add1ff2654e364..111781433275b6d095070f01e749c69856acc7c2 100644 (file)
@@ -16,7 +16,6 @@
 #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"
index 598e89591c04d4e6af3877d3b4bc5543b9b6c769..dd2bbf5bfb80a58e1d2256c2bced26854b498fcc 100644 (file)
@@ -746,7 +746,7 @@ int locale_gen_enable_locale(const char *locale) {
                         return r;
                 r = copy_xattr(fileno(fr), fileno(fw), COPY_ALL_XATTRS);
                 if (r < 0)
-                        return r;
+                        log_debug_errno(r, "Failed to copy all xattrs from old to new /etc/locale.gen file, ignoring: %m");
         }
 
         if (!write_new) {
index 83f25135a2e68e1c9ef385ef748001a4ba38f456..d00a280e66380cb8539d8bdbf723771efecec9b5 100644 (file)
@@ -207,25 +207,38 @@ static int list_users(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        table = table_new("uid", "user");
+        table = table_new("uid", "user", "linger");
         if (!table)
                 return log_oom();
 
         (void) table_set_align_percent(table, TABLE_HEADER_CELL(0), 100);
 
         for (;;) {
-                const char *user;
+                const char *user, *object;
                 uint32_t uid;
+                int linger;
 
-                r = sd_bus_message_read(reply, "(uso)", &uid, &user, NULL);
+                r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object);
                 if (r < 0)
                         return bus_log_parse_error(r);
                 if (r == 0)
                         break;
 
+                r = sd_bus_get_property_trivial(bus,
+                                                "org.freedesktop.login1",
+                                                object,
+                                                "org.freedesktop.login1.User",
+                                                "Linger",
+                                                &error,
+                                                'b',
+                                                &linger);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get linger status: %s", bus_error_message(&error, r));
+
                 r = table_add_many(table,
                                    TABLE_UID, (uid_t) uid,
-                                   TABLE_STRING, user);
+                                   TABLE_STRING, user,
+                                   TABLE_BOOLEAN, linger);
                 if (r < 0)
                         return table_log_add_error(r);
         }
index e308e52af3b5f883b9247a300e1720e8928fcb43..65c001215d2f6439e020c98f13db9579a98c391a 100644 (file)
@@ -1305,7 +1305,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
                         return r;
 
                 if (manager_add_user_by_uid(m, uid, &u) >= 0)
-                        user_start(u, /* want_user_instance= */ true);
+                        user_start(u);
 
         } else {
                 User *u;
@@ -2361,7 +2361,7 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
                 log_struct(LOG_INFO,
                            LOG_MESSAGE("System shutdown has been cancelled"),
                            "ACTION=%s", handle_action_to_string(a->handle),
-                           "MESSAGE_ID=" SD_MESSAGE_LOGIND_SHUTDOWN_CANCELED_STR,
+                           "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_CANCELED_STR,
                            username ? "OPERATOR=%s" : NULL, username);
 
                 utmp_wall("System shutdown has been cancelled",
index 003dbc0a959076d2f5c97be0e84047c827c8832c..dc6a0d5407567248acb08e36841d27f369e73820 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "alloc-util.h"
 #include "bus-util.h"
+#include "daemon-util.h"
 #include "fd-util.h"
 #include "logind-session-dbus.h"
 #include "logind-session-device.h"
@@ -376,19 +377,11 @@ error:
 }
 
 void session_device_free(SessionDevice *sd) {
-        int r;
-
         assert(sd);
 
         /* Make sure to remove the pushed fd. */
-        if (sd->pushed_fd) {
-                r = sd_notifyf(false,
-                               "FDSTOREREMOVE=1\n"
-                               "FDNAME=session-%s-device-%u-%u",
-                               sd->session->id, major(sd->dev), minor(sd->dev));
-                if (r < 0)
-                        log_warning_errno(r, "Failed to remove file descriptor from the store, ignoring: %m");
-        }
+        if (sd->pushed_fd)
+                (void) notify_remove_fd_warnf("session-%s-device-%u-%u", sd->session->id, major(sd->dev), minor(sd->dev));
 
         session_device_stop(sd);
         session_device_notify(sd, SESSION_DEVICE_RELEASE);
@@ -469,7 +462,6 @@ unsigned session_device_try_pause_all(Session *s) {
 }
 
 int session_device_save(SessionDevice *sd) {
-        _cleanup_free_ char *m = NULL;
         const char *id;
         int r;
 
@@ -489,13 +481,7 @@ int session_device_save(SessionDevice *sd) {
         id = sd->session->id;
         assert(*(id + strcspn(id, "-\n")) == '\0');
 
-        r = asprintf(&m, "FDSTORE=1\n"
-                         "FDNAME=session-%s-device-%u-%u\n",
-                         id, major(sd->dev), minor(sd->dev));
-        if (r < 0)
-                return r;
-
-        r = sd_pid_notify_with_fds(0, false, m, &sd->fd, 1);
+        r = notify_push_fdf(sd->fd, "session-%s-device-%u-%u", id, major(sd->dev), minor(sd->dev));
         if (r < 0)
                 return r;
 
index 31c37d4e2bee953c0431c8f4321e9af5f2e56af9..267d8af852539293cb22d24e1bcb12e889cde939 100644 (file)
@@ -667,7 +667,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
                 after = strv_new("systemd-logind.service",
                                  s->user->runtime_dir_service,
                                  !uid_is_system(s->user->user_record->uid) ? "systemd-user-sessions.service" : STRV_IGNORE,
-                                 s->class != SESSION_BACKGROUND ? s->user->service : STRV_IGNORE);
+                                 s->user->service);
                 if (!after)
                         return log_oom();
 
@@ -679,7 +679,7 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er
                                 description,
                                 /* These two have StopWhenUnneeded= set, hence add a dep towards them */
                                 STRV_MAKE(s->user->runtime_dir_service,
-                                          s->class != SESSION_BACKGROUND ? s->user->service : NULL),
+                                          s->user->service),
                                 after,
                                 user_record_home_directory(s->user->user_record),
                                 properties,
@@ -711,7 +711,7 @@ int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
         if (s->started)
                 return 0;
 
-        r = user_start(s->user, /* want_user_instance= */ s->class != SESSION_BACKGROUND);
+        r = user_start(s->user);
         if (r < 0)
                 return r;
 
index 5f6cd590ceaafa81ea8d1b8443f582e658675e14..3f1b8f610ba79a3a61217f39ecaaccc480c288f0 100644 (file)
@@ -442,7 +442,7 @@ static int user_update_slice(User *u) {
         return 0;
 }
 
-int user_start(User *u, bool want_user_instance) {
+int user_start(User *u) {
         assert(u);
 
         if (u->started && !u->stopping)
@@ -465,8 +465,7 @@ int user_start(User *u, bool want_user_instance) {
         (void) user_update_slice(u);
 
         /* Start user@UID.service */
-        if (want_user_instance)
-                user_start_service(u);
+        user_start_service(u);
 
         if (!u->started) {
                 if (!dual_timestamp_is_set(&u->timestamp))
index a20775ae7041a9374bc2494a2f8d8aeed48c73d1..21b9f8f348ec5d73b1eecbfd534ef78e783e1a70 100644 (file)
@@ -57,7 +57,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(User *, user_free);
 
 bool user_may_gc(User *u, bool drop_not_started);
 void user_add_to_gc_queue(User *u);
-int user_start(User *u, bool need_user_instance);
+int user_start(User *u);
 int user_stop(User *u, bool force);
 int user_finalize(User *u);
 UserState user_get_state(User *u);
index 06b1bcf9b46944ac359c507a9f56be0e90d35ba9..3f1503ad4842fdf6db35fb98e982f96edd1128fc 100644 (file)
@@ -90,7 +90,7 @@ static int warn_wall(Manager *m, usec_t n) {
         log_struct(level,
                    LOG_MESSAGE("%s", l),
                    "ACTION=%s", handle_action_to_string(m->scheduled_shutdown_action->handle),
-                   "MESSAGE_ID=" SD_MESSAGE_LOGIND_SHUTDOWN_STR,
+                   "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_SCHEDULED_STR,
                    username ? "OPERATOR=%s" : NULL, username);
 
         if (m->enable_wall_messages)
index 2fdcd1d55ed9a5078468de051f69208db6482a64..3a6a6e07488412e5411f3c4c591308d98600a7c1 100644 (file)
@@ -438,7 +438,7 @@ static int deliver_fd(Manager *m, const char *fdname, int fd) {
 
 static int manager_attach_fds(Manager *m) {
         _cleanup_strv_free_ char **fdnames = NULL;
-        int r, n;
+        int n;
 
         /* Upon restart, PID1 will send us back all fds of session devices that we previously opened. Each
          * file descriptor is associated with a given session. The session ids are passed through FDNAMES. */
@@ -455,15 +455,9 @@ static int manager_attach_fds(Manager *m) {
                 if (deliver_fd(m, fdnames[i], fd) >= 0)
                         continue;
 
-                /* Hmm, we couldn't deliver the fd to any session device object? If so, let's close the fd */
-                safe_close(fd);
-
-                /* Remove from fdstore as well */
-                r = sd_notifyf(false,
-                               "FDSTOREREMOVE=1\n"
-                               "FDNAME=%s", fdnames[i]);
-                if (r < 0)
-                        log_warning_errno(r, "Failed to remove file descriptor from the store, ignoring: %m");
+                /* Hmm, we couldn't deliver the fd to any session device object? If so, let's close the fd
+                 * and remove it from fdstore. */
+                close_and_notify_warn(fd, fdnames[i]);
         }
 
         return 0;
@@ -1041,6 +1035,7 @@ static int manager_startup(Manager *m) {
         int r;
         Seat *seat;
         Session *session;
+        User *user;
         Button *button;
         Inhibitor *inhibitor;
 
@@ -1117,7 +1112,9 @@ static int manager_startup(Manager *m) {
         HASHMAP_FOREACH(seat, m->seats)
                 (void) seat_start(seat);
 
-        /* Users are started by respective sessions */
+        HASHMAP_FOREACH(user, m->users)
+                (void) user_start(user);
+
         HASHMAP_FOREACH(session, m->sessions)
                 (void) session_start(session, NULL, NULL);
 
index 677a0140782e0c191d13e571859a7883957ada9b..98736856ee850149abe6b0364ebb2947490a7eaa 100644 (file)
@@ -411,16 +411,18 @@ static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, con
 static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit, const char *field) {
         uint64_t val;
         int r;
+        bool is_cpu_weight;
 
+        is_cpu_weight = streq(field, "CPUWeight");
         if (isempty(limit))
                 return PAM_SUCCESS;
 
-        r = cg_weight_parse(limit, &val);
+        r = is_cpu_weight ? cg_cpu_weight_parse(limit, &val) : cg_weight_parse(limit, &val);
         if (r >= 0) {
                 r = sd_bus_message_append(m, "(sv)", field, "t", val);
                 if (r < 0)
                         return pam_bus_log_create_error(handle, r);
-        } else if (streq(field, "CPUWeight"))
+        } else if (is_cpu_weight)
                 pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit);
         else
                 pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit);
@@ -790,10 +792,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                              * does the PAM session registration early for new connections, and registers a pty only
                              * much later (this is because it doesn't know yet if it needs one at all, as whether to
                              * register a pty or not is negotiated much later in the protocol). */
-        } else if (streq(tty, "systemd")) {
-                if (isempty(class))
-                        class = "user";
-                tty = NULL;
+
         } else
                 /* Chop off leading /dev prefix that some clients specify, but others do not. */
                 tty = skip_dev_prefix(tty);
index 293be0bf8ba892f543175ed95439fb8daf23a097..7a07d20e0b3f3f2c58c65e6e1eb4abde61f4e325 100644 (file)
@@ -928,6 +928,20 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
         if (r < 0)
                 return r;
 
+        if (endswith(sd_bus_message_get_member(message), "WithFlags")) {
+                uint64_t raw_flags;
+
+                r = sd_bus_message_read(message, "t", &raw_flags);
+                if (r < 0)
+                        return r;
+
+                if ((raw_flags & ~_MACHINE_COPY_FLAGS_MASK_PUBLIC) != 0)
+                        return -EINVAL;
+
+                if (raw_flags & MACHINE_COPY_REPLACE)
+                        copy_flags |= COPY_REPLACE;
+        }
+
         if (!path_is_absolute(src))
                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute.");
 
@@ -1328,6 +1342,16 @@ static const sd_bus_vtable machine_vtable[] = {
                                 SD_BUS_NO_RESULT,
                                 bus_machine_method_copy,
                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("CopyFromWithFlags",
+                                SD_BUS_ARGS("s", source, "s", destination, "t", flags),
+                                SD_BUS_NO_RESULT,
+                                bus_machine_method_copy,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("CopyToWithFlags",
+                                SD_BUS_ARGS("s", source, "s", destination, "t", flags),
+                                SD_BUS_NO_RESULT,
+                                bus_machine_method_copy,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD_WITH_ARGS("OpenRootDirectory",
                                 SD_BUS_NO_ARGS,
                                 SD_BUS_RESULT("h", fd),
index 1c114f47c31aca4350d2aeef229669ed66e528d4..a0133451abcd36fb8318fac5dce416337098fcfb 100644 (file)
@@ -6,6 +6,11 @@
 #include "bus-util.h"
 #include "machine.h"
 
+typedef enum {
+        MACHINE_COPY_REPLACE = 1 << 0, /* Public API via DBUS, do not change */
+        _MACHINE_COPY_FLAGS_MASK_PUBLIC = MACHINE_COPY_REPLACE,
+} MachineCopyFlags;
+
 extern const BusObjectImplementation machine_object;
 
 char *machine_bus_path(Machine *s);
index 213879c8edf1011ff3c0ae0acbd342fb3e649bc2..ef600e8ee41d45c17bf642bb7ca7042ea2dae665 100644 (file)
@@ -34,6 +34,7 @@
 #include "locale-util.h"
 #include "log.h"
 #include "logs-show.h"
+#include "machine-dbus.h"
 #include "macro.h"
 #include "main-func.h"
 #include "mkdir.h"
@@ -1093,6 +1094,13 @@ static int terminate_machine(int argc, char *argv[], void *userdata) {
         return 0;
 }
 
+static const char *select_copy_method(bool copy_from, bool force) {
+        if (force)
+                return copy_from ? "CopyFromMachineWithFlags" : "CopyToMachineWithFlags";
+        else
+                return copy_from ? "CopyFromMachine" : "CopyToMachine";
+}
+
 static int copy_files(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
@@ -1123,7 +1131,7 @@ static int copy_files(int argc, char *argv[], void *userdata) {
                         bus,
                         &m,
                         bus_machine_mgr,
-                        copy_from ? "CopyFromMachine" : "CopyToMachine");
+                        select_copy_method(copy_from, arg_force));
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -1136,6 +1144,12 @@ static int copy_files(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_create_error(r);
 
+        if (arg_force) {
+                r = sd_bus_message_append(m, "t", MACHINE_COPY_REPLACE);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
         /* This is a slow operation, hence turn off any method call timeouts */
         r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
         if (r < 0)
index 865d8400e8dd0e4af9cbe4d818a0b4ef045139b0..794e353caa827ffc88864fe5deb2926e7252867b 100644 (file)
@@ -1110,6 +1110,16 @@ const sd_bus_vtable manager_vtable[] = {
                                 SD_BUS_NO_RESULT,
                                 method_copy_machine,
                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("CopyFromMachineWithFlags",
+                                SD_BUS_ARGS("s", name, "s", source, "s", destination, "t", flags),
+                                SD_BUS_NO_RESULT,
+                                method_copy_machine,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("CopyToMachineWithFlags",
+                                SD_BUS_ARGS("s", name, "s", source, "s", destination, "t", flags),
+                                SD_BUS_NO_RESULT,
+                                method_copy_machine,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD_WITH_ARGS("OpenMachineRootDirectory",
                                 SD_BUS_ARGS("s", name),
                                 SD_BUS_RESULT("h", fd),
index c38b9dc31296b7cd2dc2a08cf218f272e69bde17..7e7d1679282d1cc777af07b6750704821a31a4e8 100644 (file)
@@ -292,7 +292,7 @@ static int l2tp_get_local_address(NetDev *netdev, union in_addr_union *ret) {
                 if (r < 0)
                         return r;
 
-                if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                         return -EBUSY;
         }
 
@@ -346,7 +346,7 @@ static int l2tp_get_local_address(NetDev *netdev, union in_addr_union *ret) {
                 return link_get_l2tp_local_address(link, t, ret);
 
         HASHMAP_FOREACH(link, netdev->manager->links_by_index) {
-                if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                         continue;
 
                 if (link_get_l2tp_local_address(link, t, ret) >= 0)
index 55ad60ddc8b8661eb4ba8b23e71e1abb3af79805..3cfcd51e63699b1c600a86b1bae6d1e89b9856ad 100644 (file)
@@ -45,6 +45,7 @@ Match.Host,                               config_parse_net_condition,
 Match.Virtualization,                     config_parse_net_condition,                CONDITION_VIRTUALIZATION,      offsetof(NetDev, conditions)
 Match.KernelCommandLine,                  config_parse_net_condition,                CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, conditions)
 Match.KernelVersion,                      config_parse_net_condition,                CONDITION_KERNEL_VERSION,      offsetof(NetDev, conditions)
+Match.Credential,                         config_parse_net_condition,                CONDITION_CREDENTIAL,          offsetof(NetDev, conditions)
 Match.Architecture,                       config_parse_net_condition,                CONDITION_ARCHITECTURE,        offsetof(NetDev, conditions)
 Match.Firmware,                           config_parse_net_condition,                CONDITION_FIRMWARE,            offsetof(NetDev, conditions)
 NetDev.Description,                       config_parse_string,                       0,                             offsetof(NetDev, description)
@@ -184,12 +185,14 @@ Tun.PacketInfo,                           config_parse_bool,
 Tun.VNetHeader,                           config_parse_bool,                         0,                             offsetof(TunTap, vnet_hdr)
 Tun.User,                                 config_parse_string,                       CONFIG_PARSE_STRING_SAFE,      offsetof(TunTap, user_name)
 Tun.Group,                                config_parse_string,                       CONFIG_PARSE_STRING_SAFE,      offsetof(TunTap, group_name)
+Tun.KeepCarrier,                          config_parse_bool,                         0,                             offsetof(TunTap, keep_fd)
 Tap.OneQueue,                             config_parse_warn_compat,                  DISABLED_LEGACY,               0
 Tap.MultiQueue,                           config_parse_bool,                         0,                             offsetof(TunTap, multi_queue)
 Tap.PacketInfo,                           config_parse_bool,                         0,                             offsetof(TunTap, packet_info)
 Tap.VNetHeader,                           config_parse_bool,                         0,                             offsetof(TunTap, vnet_hdr)
 Tap.User,                                 config_parse_string,                       CONFIG_PARSE_STRING_SAFE,      offsetof(TunTap, user_name)
 Tap.Group,                                config_parse_string,                       CONFIG_PARSE_STRING_SAFE,      offsetof(TunTap, group_name)
+Tap.KeepCarrier,                          config_parse_bool,                         0,                             offsetof(TunTap, keep_fd)
 Bond.Mode,                                config_parse_bond_mode,                    0,                             offsetof(Bond, mode)
 Bond.TransmitHashPolicy,                  config_parse_bond_xmit_hash_policy,        0,                             offsetof(Bond, xmit_hash_policy)
 Bond.LACPTransmitRate,                    config_parse_bond_lacp_rate,               0,                             offsetof(Bond, lacp_rate)
index 06028855a84ac294fe1aee8b3ee6260335c78ac0..23506246ce2f44d9c9454627bf1d0aa545ce0522 100644 (file)
@@ -51,7 +51,7 @@ int link_get_local_address(
                 assert_not_reached();
         }
 
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return -EBUSY;
 
         SET_FOREACH(a, link->addresses) {
index f015168692b318a826f83da4e61b269ab176cf0f..212df3daa02871e53942f0c4a08dad819f10d886 100644 (file)
@@ -31,6 +31,7 @@
 #include "networkd-manager.h"
 #include "networkd-queue.h"
 #include "networkd-setlink.h"
+#include "networkd-sriov.h"
 #include "nlmon.h"
 #include "path-lookup.h"
 #include "siphash24.h"
@@ -236,6 +237,9 @@ void netdev_drop(NetDev *netdev) {
                 return;
         }
 
+        if (NETDEV_VTABLE(netdev) && NETDEV_VTABLE(netdev)->drop)
+                NETDEV_VTABLE(netdev)->drop(netdev);
+
         netdev->state = NETDEV_STATE_LINGER;
 
         log_netdev_debug(netdev, "netdev removed");
@@ -604,24 +608,39 @@ static int stacked_netdev_create(NetDev *netdev, Link *link, Request *req) {
         return 0;
 }
 
+static bool link_is_ready_to_create_stacked_netdev_one(Link *link, bool allow_unmanaged) {
+        assert(link);
+
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED))
+                return false;
+
+        if (!link->network)
+                return allow_unmanaged;
+
+        if (link->set_link_messages > 0)
+                return false;
+
+        /* If stacked netdevs are created before the underlying interface being activated, then
+         * the activation policy for the netdevs are ignored. See issue #22593. */
+        if (!link->activated)
+                return false;
+
+        return true;
+}
+
+static bool link_is_ready_to_create_stacked_netdev(Link *link) {
+        return check_ready_for_all_sr_iov_ports(link, /* allow_unmanaged = */ false,
+                                                link_is_ready_to_create_stacked_netdev_one);
+}
+
 static int netdev_is_ready_to_create(NetDev *netdev, Link *link) {
         assert(netdev);
 
         if (netdev->state != NETDEV_STATE_LOADING)
                 return false;
 
-        if (link) {
-                if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
-                        return false;
-
-                if (link->set_link_messages > 0)
-                        return false;
-
-                /* If stacked netdevs are created before the underlying interface being activated, then
-                 * the activation policy for the netdevs are ignored. See issue #22593. */
-                if (!link->activated)
-                        return false;
-        }
+        if (link && !link_is_ready_to_create_stacked_netdev(link))
+                return false;
 
         if (NETDEV_VTABLE(netdev)->is_ready_to_create)
                 return NETDEV_VTABLE(netdev)->is_ready_to_create(netdev, link);
@@ -773,6 +792,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
                         config_item_perf_lookup, network_netdev_gperf_lookup,
                         CONFIG_PARSE_WARN,
                         netdev_raw,
+                        NULL,
                         NULL);
         if (r < 0)
                 return r; /* config_parse_many() logs internally. */
@@ -807,7 +827,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
                         NETDEV_VTABLE(netdev)->sections,
                         config_item_perf_lookup, network_netdev_gperf_lookup,
                         CONFIG_PARSE_WARN,
-                        netdev, NULL);
+                        netdev, NULL, NULL);
         if (r < 0)
                 return r; /* config_parse_many() logs internally. */
 
index 1c7dc0f7e55d452c2426140ca73b710216a27f34..49eadbb7a442e1c02ff1388d9fd1982abf65ab51 100644 (file)
@@ -142,6 +142,9 @@ typedef struct NetDevVTable {
          * to be set != 0. */
         void (*init)(NetDev *n);
 
+        /* This is called when the interface is removed. */
+        void (*drop)(NetDev *n);
+
         /* This should free all kind-specific variables. It should be
          * idempotent. */
         void (*done)(NetDev *n);
index 6f099a144814e1f060b3e0423802bb85ed0245df..39ea7c1d739e6c1579134caf9980d4cc2b9d9cf1 100644 (file)
 #include <linux/if_tun.h>
 
 #include "alloc-util.h"
+#include "daemon-util.h"
 #include "fd-util.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "socket-util.h"
 #include "tuntap.h"
 #include "user-util.h"
 
 #define TUN_DEV "/dev/net/tun"
 
-static int netdev_fill_tuntap_message(NetDev *netdev, struct ifreq *ifr) {
-        TunTap *t;
-
+static TunTap* TUNTAP(NetDev *netdev) {
         assert(netdev);
-        assert(netdev->ifname);
-        assert(ifr);
-
-        if (netdev->kind == NETDEV_KIND_TAP) {
-                t = TAP(netdev);
-                ifr->ifr_flags |= IFF_TAP;
-        } else {
-                t = TUN(netdev);
-                ifr->ifr_flags |= IFF_TUN;
+
+        switch (netdev->kind) {
+        case NETDEV_KIND_TAP:
+                return TAP(netdev);
+        case NETDEV_KIND_TUN:
+                return TUN(netdev);
+        default:
+                return NULL;
         }
+}
 
-        if (!t->packet_info)
-                ifr->ifr_flags |= IFF_NO_PI;
+static void *close_fd_ptr(void *p) {
+        safe_close(PTR_TO_FD(p));
+        return NULL;
+}
 
-        if (t->multi_queue)
-                ifr->ifr_flags |= IFF_MULTI_QUEUE;
+DEFINE_PRIVATE_HASH_OPS_FULL(named_fd_hash_ops, char, string_hash_func, string_compare_func, free, void, close_fd_ptr);
 
-        if (t->vnet_hdr)
-                ifr->ifr_flags |= IFF_VNET_HDR;
+int manager_add_tuntap_fd(Manager *m, int fd, const char *name) {
+        _cleanup_free_ char *tuntap_name = NULL;
+        const char *p;
+        int r;
+
+        assert(m);
+        assert(fd >= 0);
+        assert(name);
+
+        p = startswith(name, "tuntap-");
+        if (!p)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Received unknown fd (%s).", name);
+
+        if (!ifname_valid(p))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Received tuntap fd with invalid name (%s).", p);
 
-        strncpy(ifr->ifr_name, netdev->ifname, IFNAMSIZ-1);
+        tuntap_name = strdup(p);
+        if (!tuntap_name)
+                return log_oom_debug();
 
+        r = hashmap_ensure_put(&m->tuntap_fds_by_name, &named_fd_hash_ops, tuntap_name, FD_TO_PTR(fd));
+        if (r < 0)
+                return log_debug_errno(r, "Failed to store tuntap fd: %m");
+
+        TAKE_PTR(tuntap_name);
         return 0;
 }
 
-static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) {
+void manager_clear_unmanaged_tuntap_fds(Manager *m) {
+        char *name;
+        void *p;
+
+        assert(m);
+
+        while ((p = hashmap_steal_first_key_and_value(m->tuntap_fds_by_name, (void**) &name))) {
+                close_and_notify_warn(PTR_TO_FD(p), name);
+                name = mfree(name);
+        }
+}
+
+static int tuntap_take_fd(NetDev *netdev) {
+        _cleanup_free_ char *name = NULL;
+        void *p;
+        int r;
+
+        assert(netdev);
+        assert(netdev->manager);
+
+        r = link_get_by_name(netdev->manager, netdev->ifname, NULL);
+        if (r < 0)
+                return r;
+
+        p = hashmap_remove2(netdev->manager->tuntap_fds_by_name, netdev->ifname, (void**) &name);
+        if (!p)
+                return -ENOENT;
+
+        log_netdev_debug(netdev, "Found file descriptor in fd store.");
+        return PTR_TO_FD(p);
+}
+
+static int netdev_create_tuntap(NetDev *netdev) {
         _cleanup_close_ int fd = -1;
-        TunTap *t = NULL;
-        const char *user;
-        const char *group;
-        uid_t uid;
-        gid_t gid;
+        struct ifreq ifr = {};
+        TunTap *t;
         int r;
 
         assert(netdev);
-        assert(ifr);
+        t = TUNTAP(netdev);
+        assert(t);
 
-        fd = open(TUN_DEV, O_RDWR|O_CLOEXEC);
+        fd = TAKE_FD(t->fd);
         if (fd < 0)
-                return log_netdev_error_errno(netdev, errno,  "Failed to open tun dev: %m");
-
-        if (ioctl(fd, TUNSETIFF, ifr) < 0)
-                return log_netdev_error_errno(netdev, errno, "TUNSETIFF failed on tun dev: %m");
+                fd = tuntap_take_fd(netdev);
+        if (fd < 0)
+                fd = open(TUN_DEV, O_RDWR|O_CLOEXEC);
+        if (fd < 0)
+                return log_netdev_error_errno(netdev, errno,  "Failed to open " TUN_DEV ": %m");
 
         if (netdev->kind == NETDEV_KIND_TAP)
-                t = TAP(netdev);
+                ifr.ifr_flags |= IFF_TAP;
         else
-                t = TUN(netdev);
+                ifr.ifr_flags |= IFF_TUN;
 
-        assert(t);
+        if (!t->packet_info)
+                ifr.ifr_flags |= IFF_NO_PI;
+
+        if (t->multi_queue)
+                ifr.ifr_flags |= IFF_MULTI_QUEUE;
+
+        if (t->vnet_hdr)
+                ifr.ifr_flags |= IFF_VNET_HDR;
+
+        strncpy(ifr.ifr_name, netdev->ifname, IFNAMSIZ-1);
+
+        if (ioctl(fd, TUNSETIFF, &ifr) < 0)
+                return log_netdev_error_errno(netdev, errno, "TUNSETIFF failed: %m");
 
         if (t->user_name) {
-                user = t->user_name;
+                const char *user = t->user_name;
+                uid_t uid;
 
                 r = get_user_creds(&user, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
                 if (r < 0)
                         return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name);
 
                 if (ioctl(fd, TUNSETOWNER, uid) < 0)
-                        return log_netdev_error_errno(netdev, errno, "TUNSETOWNER failed on tun dev: %m");
+                        return log_netdev_error_errno(netdev, errno, "TUNSETOWNER failed: %m");
         }
 
         if (t->group_name) {
-                group = t->group_name;
+                const char *group = t->group_name;
+                gid_t gid;
 
                 r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING);
                 if (r < 0)
                         return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name);
 
                 if (ioctl(fd, TUNSETGROUP, gid) < 0)
-                        return log_netdev_error_errno(netdev, errno, "TUNSETGROUP failed on tun dev: %m");
+                        return log_netdev_error_errno(netdev, errno, "TUNSETGROUP failed: %m");
 
         }
 
         if (ioctl(fd, TUNSETPERSIST, 1) < 0)
-                return log_netdev_error_errno(netdev, errno, "TUNSETPERSIST failed on tun dev: %m");
+                return log_netdev_error_errno(netdev, errno, "TUNSETPERSIST failed: %m");
+
+        if (t->keep_fd) {
+                t->fd = TAKE_FD(fd);
+                (void) notify_push_fdf(t->fd, "tuntap-%s", netdev->ifname);
+        }
 
         return 0;
 }
 
-static int netdev_create_tuntap(NetDev *netdev) {
-        struct ifreq ifr = {};
-        int r;
+static void tuntap_init(NetDev *netdev) {
+        TunTap *t;
 
-        r = netdev_fill_tuntap_message(netdev, &ifr);
-        if (r < 0)
-                return r;
+        assert(netdev);
+        t = TUNTAP(netdev);
+        assert(t);
 
-        return netdev_tuntap_add(netdev, &ifr);
+        t->fd = -1;
 }
 
-static void tuntap_done(NetDev *netdev) {
-        TunTap *t = NULL;
+static void tuntap_drop(NetDev *netdev) {
+        TunTap *t;
 
         assert(netdev);
+        t = TUNTAP(netdev);
+        assert(t);
 
-        if (netdev->kind == NETDEV_KIND_TUN)
-                t = TUN(netdev);
-        else
-                t = TAP(netdev);
+        t->fd = close_and_notify_warn(t->fd, netdev->ifname);
+}
 
+static void tuntap_done(NetDev *netdev) {
+        TunTap *t;
+
+        assert(netdev);
+        t = TUNTAP(netdev);
         assert(t);
 
+        t->fd = safe_close(t->fd);
         t->user_name = mfree(t->user_name);
         t->group_name = mfree(t->group_name);
 }
@@ -149,6 +227,8 @@ const NetDevVTable tun_vtable = {
         .object_size = sizeof(TunTap),
         .sections = NETDEV_COMMON_SECTIONS "Tun\0",
         .config_verify = tuntap_verify,
+        .init = tuntap_init,
+        .drop = tuntap_drop,
         .done = tuntap_done,
         .create = netdev_create_tuntap,
         .create_type = NETDEV_CREATE_INDEPENDENT,
@@ -159,6 +239,8 @@ const NetDevVTable tap_vtable = {
         .object_size = sizeof(TunTap),
         .sections = NETDEV_COMMON_SECTIONS "Tap\0",
         .config_verify = tuntap_verify,
+        .init = tuntap_init,
+        .drop = tuntap_drop,
         .done = tuntap_done,
         .create = netdev_create_tuntap,
         .create_type = NETDEV_CREATE_INDEPENDENT,
index 4d1e643f4313f6ee4def870943b831bde02ca9fc..88e0ce5f97047c1f0185d3eda36411f13ea487d6 100644 (file)
@@ -8,14 +8,19 @@ typedef struct TunTap TunTap;
 struct TunTap {
         NetDev meta;
 
+        int fd;
         char *user_name;
         char *group_name;
         bool multi_queue;
         bool packet_info;
         bool vnet_hdr;
+        bool keep_fd;
 };
 
 DEFINE_NETDEV_CAST(TUN, TunTap);
 DEFINE_NETDEV_CAST(TAP, TunTap);
 extern const NetDevVTable tun_vtable;
 extern const NetDevVTable tap_vtable;
+
+int manager_add_tuntap_fd(Manager *m, int fd, const char *name);
+void manager_clear_unmanaged_tuntap_fds(Manager *m);
index 8e8d5f7781693de45c39c4cf7b00edd323d6bbfb..ccb4a426faea04c1d915401532dff8c6810fcd0e 100644 (file)
@@ -212,39 +212,32 @@ void address_set_broadcast(Address *a, Link *link) {
         a->broadcast.s_addr = a->in_addr.in.s_addr | htobe32(UINT32_C(0xffffffff) >> a->prefixlen);
 }
 
-static struct ifa_cacheinfo *address_set_cinfo(const Address *a, struct ifa_cacheinfo *cinfo) {
+static void address_set_cinfo(Manager *m, const Address *a, struct ifa_cacheinfo *cinfo) {
         usec_t now_usec;
 
+        assert(m);
         assert(a);
         assert(cinfo);
 
-        now_usec = now(CLOCK_BOOTTIME);
+        assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &now_usec) >= 0);
 
         *cinfo = (struct ifa_cacheinfo) {
-                .ifa_valid = MIN(usec_sub_unsigned(a->lifetime_valid_usec, now_usec) / USEC_PER_SEC, UINT32_MAX),
-                .ifa_prefered = MIN(usec_sub_unsigned(a->lifetime_preferred_usec, now_usec) / USEC_PER_SEC, UINT32_MAX),
+                .ifa_valid = usec_to_sec(a->lifetime_valid_usec, now_usec),
+                .ifa_prefered = usec_to_sec(a->lifetime_preferred_usec, now_usec),
         };
-
-        return cinfo;
 }
 
-static void address_set_lifetime(Address *a, const struct ifa_cacheinfo *cinfo) {
+static void address_set_lifetime(Manager *m, Address *a, const struct ifa_cacheinfo *cinfo) {
         usec_t now_usec;
 
+        assert(m);
         assert(a);
         assert(cinfo);
 
-        now_usec = now(CLOCK_BOOTTIME);
-
-        if (cinfo->ifa_valid == UINT32_MAX)
-                a->lifetime_valid_usec = USEC_INFINITY;
-        else
-                a->lifetime_valid_usec = usec_add(cinfo->ifa_valid * USEC_PER_SEC, now_usec);
+        assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &now_usec) >= 0);
 
-        if (cinfo->ifa_prefered == UINT32_MAX)
-                a->lifetime_preferred_usec = USEC_INFINITY;
-        else
-                a->lifetime_preferred_usec = usec_add(cinfo->ifa_prefered * USEC_PER_SEC, now_usec);
+        a->lifetime_valid_usec = sec_to_usec(cinfo->ifa_valid, now_usec);
+        a->lifetime_preferred_usec = sec_to_usec(cinfo->ifa_prefered, now_usec);
 }
 
 static uint32_t address_prefix(const Address *a) {
@@ -809,7 +802,7 @@ int link_drop_ipv6ll_addresses(Link *link) {
         /* IPv6LL address may be in the tentative state, and in that case networkd has not received it.
          * So, we need to dump all IPv6 addresses. */
 
-        if (link_may_have_ipv6ll(link))
+        if (link_may_have_ipv6ll(link, /* check_multicast = */ false))
                 return 0;
 
         r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_GETADDR, link->ifindex, AF_INET6);
@@ -1035,12 +1028,13 @@ int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m,
         return 1;
 }
 
-static int address_configure(const Address *address, Link *link, Request *req) {
+static int address_configure(const Address *address, const struct ifa_cacheinfo *c, Link *link, Request *req) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
         assert(address);
         assert(IN_SET(address->family, AF_INET, AF_INET6));
+        assert(c);
         assert(link);
         assert(link->ifindex > 0);
         assert(link->manager);
@@ -1077,8 +1071,7 @@ static int address_configure(const Address *address, Link *link, Request *req) {
                         return r;
         }
 
-        r = sd_netlink_message_append_cache_info(m, IFA_CACHEINFO,
-                                                 address_set_cinfo(address, &(struct ifa_cacheinfo) {}));
+        r = sd_netlink_message_append_cache_info(m, IFA_CACHEINFO, c);
         if (r < 0)
                 return r;
 
@@ -1107,6 +1100,7 @@ static bool address_is_ready_to_configure(Link *link, const Address *address) {
 }
 
 static int address_process_request(Request *req, Link *link, Address *address) {
+        struct ifa_cacheinfo c;
         int r;
 
         assert(req);
@@ -1116,7 +1110,16 @@ static int address_process_request(Request *req, Link *link, Address *address) {
         if (!address_is_ready_to_configure(link, address))
                 return 0;
 
-        r = address_configure(address, link, req);
+        address_set_cinfo(link->manager, address, &c);
+        if (c.ifa_valid == 0) {
+                log_link_debug(link, "Refuse to configure %s address %s, as its valid lifetime is zero.",
+                               network_config_source_to_string(address->source),
+                               IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
+                address_cancel_requesting(address);
+                return 1;
+        }
+
+        r = address_configure(address, &c, link, req);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to configure address: %m");
 
@@ -1455,18 +1458,18 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                         /* update flags and etc. */
                         address->flags = tmp->flags;
                         address->scope = tmp->scope;
-                        address_set_lifetime(address, &cinfo);
+                        address_set_lifetime(m, address, &cinfo);
                         address_enter_configured(address);
                         log_address_debug(address, "Received updated", link);
                 } else {
-                        address_set_lifetime(tmp, &cinfo);
+                        address_set_lifetime(m, tmp, &cinfo);
                         address_enter_configured(tmp);
                         log_address_debug(tmp, "Received new", link);
 
                         r = address_add(link, tmp);
                         if (r < 0) {
                                 log_link_warning_errno(link, r, "Failed to remember foreign address %s, ignoring: %m",
-                                                       IN_ADDR_PREFIX_TO_STRING(tmp->family, &tmp->in_addr, tmp->prefixlen));                
+                                                       IN_ADDR_PREFIX_TO_STRING(tmp->family, &tmp->in_addr, tmp->prefixlen));
                                 return 0;
                         }
 
index ce5262fcd6907c8337c98c1204004f4095bb024a..94771278855ed748a385afe1eb31f69825740e8b 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/if_arp.h>
 
 #include "bus-error.h"
+#include "bus-locator.h"
 #include "dhcp-identifier.h"
 #include "dhcp-internal.h"
 #include "dhcp6-internal.h"
@@ -187,12 +188,10 @@ int manager_request_product_uuid(Manager *m) {
 
         m->product_uuid_requested = false;
 
-        r = sd_bus_call_method_async(
+        r = bus_call_method_async(
                         m->bus,
                         NULL,
-                        "org.freedesktop.hostname1",
-                        "/org/freedesktop/hostname1",
-                        "org.freedesktop.hostname1",
+                        bus_hostname,
                         "GetProductUUID",
                         get_product_uuid_handler,
                         m,
index 5c923c258c2a51db046665c14196e0c55f688273..62f0192bcbb0a0f8aeeded717bb462501201da6c 100644 (file)
@@ -854,11 +854,12 @@ static int dhcp4_pd_assign_subnet_prefix(Link *link, Link *uplink) {
         const struct in_addr *br_addresses;
         struct in_addr ipv4address;
         uint32_t lifetime_sec;
-        usec_t lifetime_usec;
+        usec_t lifetime_usec, now_usec;
         int r;
 
         assert(link);
         assert(uplink);
+        assert(uplink->manager);
         assert(uplink->dhcp_lease);
 
         r = sd_dhcp_lease_get_address(uplink->dhcp_lease, &ipv4address);
@@ -869,7 +870,8 @@ static int dhcp4_pd_assign_subnet_prefix(Link *link, Link *uplink) {
         if (r < 0)
                 return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m");
 
-        lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(CLOCK_BOOTTIME));
+        assert_se(sd_event_now(uplink->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
+        lifetime_usec = sec_to_usec(lifetime_sec, now_usec);
 
         r = sd_dhcp_lease_get_6rd(uplink->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, &br_addresses, NULL);
         if (r < 0)
@@ -926,11 +928,12 @@ int dhcp4_pd_prefix_acquired(Link *uplink) {
         union in_addr_union server_address;
         const struct in_addr *br_addresses;
         uint32_t lifetime_sec;
-        usec_t lifetime_usec;
+        usec_t lifetime_usec, now_usec;
         Link *link;
         int r;
 
         assert(uplink);
+        assert(uplink->manager);
         assert(uplink->dhcp_lease);
 
         r = sd_dhcp_lease_get_address(uplink->dhcp_lease, &ipv4address);
@@ -941,7 +944,8 @@ int dhcp4_pd_prefix_acquired(Link *uplink) {
         if (r < 0)
                 return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m");
 
-        lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(CLOCK_BOOTTIME));
+        assert_se(sd_event_now(uplink->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
+        lifetime_usec = sec_to_usec(lifetime_sec, now_usec);
 
         r = sd_dhcp_lease_get_server_identifier(uplink->dhcp_lease, &server_address.in);
         if (r < 0)
@@ -1032,7 +1036,6 @@ static int dhcp6_pd_assign_subnet_prefixes(Link *link, Link *uplink) {
 
         for (sd_dhcp6_lease_reset_pd_prefix_iter(uplink->dhcp6_lease);;) {
                 uint32_t lifetime_preferred_sec, lifetime_valid_sec;
-                usec_t lifetime_preferred_usec, lifetime_valid_usec;
                 struct in6_addr pd_prefix;
                 uint8_t pd_prefix_len;
 
@@ -1049,11 +1052,9 @@ static int dhcp6_pd_assign_subnet_prefixes(Link *link, Link *uplink) {
                 if (r < 0)
                         return r;
 
-                lifetime_preferred_usec = usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec);
-                lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec);
-
                 r = dhcp_pd_assign_subnet_prefix(link, &pd_prefix, pd_prefix_len,
-                                                 lifetime_preferred_usec, lifetime_valid_usec,
+                                                 sec_to_usec(lifetime_preferred_sec, timestamp_usec),
+                                                 sec_to_usec(lifetime_valid_sec, timestamp_usec),
                                                  /* is_uplink = */ link == uplink);
                 if (r < 0)
                         return r;
@@ -1082,7 +1083,6 @@ int dhcp6_pd_prefix_acquired(Link *uplink) {
         /* First, logs acquired prefixes and request unreachable routes. */
         for (sd_dhcp6_lease_reset_pd_prefix_iter(uplink->dhcp6_lease);;) {
                 uint32_t lifetime_preferred_sec, lifetime_valid_sec;
-                usec_t lifetime_valid_usec;
                 struct in6_addr pd_prefix;
                 uint8_t pd_prefix_len;
 
@@ -1096,13 +1096,13 @@ int dhcp6_pd_prefix_acquired(Link *uplink) {
                 if (r < 0)
                         return log_link_error_errno(uplink, r, "Failed to mask DHCPv6 delegated prefix: %m");
 
-                lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec);
-
                 r = dhcp_pd_prefix_add(uplink, &pd_prefix, pd_prefix_len);
                 if (r < 0)
                         return r;
 
-                r = dhcp6_request_unreachable_route(uplink, &pd_prefix, pd_prefix_len, lifetime_valid_usec, &server_address);
+                r = dhcp6_request_unreachable_route(uplink, &pd_prefix, pd_prefix_len,
+                                                    sec_to_usec(lifetime_valid_sec, timestamp_usec),
+                                                    &server_address);
                 if (r < 0)
                         return r;
         }
index 5861ecb922874fe31926f8295ba183407c81b0c1..076779dc0e5f2c169c8ca9bf2754d3e6a5a430c8 100644 (file)
@@ -539,13 +539,7 @@ static bool dhcp_server_is_ready_to_configure(Link *link) {
 
         assert(link);
 
-        if (!link->network)
-                return false;
-
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
-                return false;
-
-        if (link->set_flags_messages > 0)
+        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return false;
 
         if (!link_has_carrier(link))
index 20db4f5f837c00ed0c8b187fbcbbc92a8787c7bb..4df2848c546718afd9203470f9d346e6db5f815e 100644 (file)
@@ -797,6 +797,7 @@ static int dhcp4_request_address(Link *link, bool announce) {
         int r;
 
         assert(link);
+        assert(link->manager);
         assert(link->network);
         assert(link->dhcp_lease);
 
@@ -814,12 +815,14 @@ static int dhcp4_request_address(Link *link, bool announce) {
 
         if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
                 uint32_t lifetime_sec;
+                usec_t now_usec;
 
                 r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime_sec);
                 if (r < 0)
                         return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
 
-                lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(CLOCK_BOOTTIME));
+                assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
+                lifetime_usec = sec_to_usec(lifetime_sec, now_usec);
         } else
                 lifetime_usec = USEC_INFINITY;
 
@@ -1071,6 +1074,12 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                         if (link->ipv4ll) {
                                 log_link_debug(link, "DHCP client is stopped. Acquiring IPv4 link-local address");
 
+                                if (in4_addr_is_set(&link->network->ipv4ll_start_address)) {
+                                        r = sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
+                                        if (r < 0)
+                                                return log_link_warning_errno(link, r, "Could not set IPv4 link-local start address: %m");
+                                }
+
                                 r = sd_ipv4ll_start(link->ipv4ll);
                                 if (r < 0)
                                         return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
@@ -1154,6 +1163,12 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                         if (link->ipv4ll && !sd_ipv4ll_is_running(link->ipv4ll)) {
                                 log_link_debug(link, "Problems acquiring DHCP lease, acquiring IPv4 link-local address");
 
+                                if (in4_addr_is_set(&link->network->ipv4ll_start_address)) {
+                                        r = sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
+                                        if (r < 0)
+                                                return log_link_warning_errno(link, r, "Could not set IPv4 link-local start address: %m");
+                                }
+
                                 r = sd_ipv4ll_start(link->ipv4ll);
                                 if (r < 0)
                                         return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
@@ -1309,7 +1324,7 @@ static bool link_needs_dhcp_broadcast(Link *link) {
          * If neither is set or a failure occurs, return false, which is the default for this flag.
          */
         r = link->network->dhcp_broadcast;
-        if (r < 0 && link->sd_device && sd_device_get_property_value(link->sd_device, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
+        if (r < 0 && link->dev && sd_device_get_property_value(link->dev, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
                 r = parse_boolean(val);
                 if (r < 0)
                         log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
@@ -1491,6 +1506,7 @@ static int dhcp4_configure(Link *link) {
 }
 
 int dhcp4_update_mac(Link *link) {
+        bool restart;
         int r;
 
         assert(link);
@@ -1498,13 +1514,30 @@ int dhcp4_update_mac(Link *link) {
         if (!link->dhcp_client)
                 return 0;
 
-        r = sd_dhcp_client_set_mac(link->dhcp_client, link->hw_addr.bytes,
+        restart = sd_dhcp_client_is_running(link->dhcp_client);
+
+        r = sd_dhcp_client_stop(link->dhcp_client);
+        if (r < 0)
+                return r;
+
+        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 r;
 
-        return dhcp4_set_client_identifier(link);
+        r = dhcp4_set_client_identifier(link);
+        if (r < 0)
+                return r;
+
+        if (restart) {
+                r = sd_dhcp_client_start(link->dhcp_client);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
 }
 
 int dhcp4_start(Link *link) {
@@ -1542,7 +1575,7 @@ static int dhcp4_process_request(Request *req, Link *link, void *userdata) {
 
         assert(link);
 
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return 0;
 
         r = dhcp4_configure_duid(link);
index 72ec6826bdc5e5ba7f1d6daae7524e3d501e4ac8..4293eb624215de4563de9fc53c31671c6e716522 100644 (file)
@@ -266,8 +266,8 @@ static int dhcp6_address_acquired(Link *link) {
                         break;
 
                 r = dhcp6_request_address(link, &server_address, &ip6_addr,
-                                          usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec),
-                                          usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec));
+                                          sec_to_usec(lifetime_preferred_sec, timestamp_usec),
+                                          sec_to_usec(lifetime_valid_sec, timestamp_usec));
                 if (r < 0)
                         return r;
         }
@@ -695,6 +695,12 @@ static int dhcp6_configure(Link *link) {
                         return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix delegation hint: %m");
         }
 
+        r = sd_dhcp6_client_set_rapid_commit(client, link->network->dhcp6_use_rapid_commit);
+        if (r < 0)
+                return log_link_debug_errno(link, r,
+                                            "DHCPv6 CLIENT: Failed to %s rapid commit: %m",
+                                            enable_disable(link->network->dhcp6_use_rapid_commit));
+
         link->dhcp6_client = TAKE_PTR(client);
 
         return 0;
@@ -735,7 +741,7 @@ static int dhcp6_process_request(Request *req, Link *link, void *userdata) {
 
         assert(link);
 
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return 0;
 
         r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
index 009cde27de19679bfc3b3fe420f4eb18dbd0290e..4f2563ff9cf8e45e55a7ca3dd112f6f04014c839 100644 (file)
@@ -1,14 +1,44 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <linux/if_arp.h>
+
 #include "sd-dhcp-client.h"
 #include "sd-ipv4acd.h"
 
+#include "ipvlan.h"
 #include "networkd-address.h"
 #include "networkd-dhcp4.h"
 #include "networkd-ipv4acd.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 
+bool link_ipv4acd_supported(Link *link) {
+        assert(link);
+
+        if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        /* ARPHRD_INFINIBAND seems to potentially support IPv4ACD.
+         * But currently sd-ipv4acd only supports ARPHRD_ETHER. */
+        if (link->iftype != ARPHRD_ETHER)
+                return false;
+
+        if (link->hw_addr.length != ETH_ALEN)
+                return false;
+
+        if (ether_addr_is_null(&link->hw_addr.ether))
+                return false;
+
+        if (streq_ptr(link->kind, "vrf"))
+                return false;
+
+        /* L3 or L3S mode do not support ARP. */
+        if (IN_SET(link_get_ipvlan_mode(link), NETDEV_IPVLAN_MODE_L3, NETDEV_IPVLAN_MODE_L3S))
+                return false;
+
+        return true;
+}
+
 static int static_ipv4acd_address_remove(Link *link, Address *address, bool on_conflict) {
         int r;
 
@@ -141,7 +171,7 @@ int ipv4acd_configure(Address *address) {
         if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4))
                 return 0;
 
-        if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
+        if (!link_ipv4acd_supported(link))
                 return 0;
 
         /* Currently, only static and DHCP4 addresses are supported. */
index 6ebfa362896d18bdc2f00e010c9c1038862dc06f..7bd6a26b40a67abdd44cb47fbe315efff5926fd3 100644 (file)
@@ -4,6 +4,7 @@
 typedef struct Address Address;
 typedef struct Link Link;
 
+bool link_ipv4acd_supported(Link *link);
 int ipv4acd_configure(Address *address);
 int ipv4acd_update_mac(Link *link);
 int ipv4acd_start(Link *link);
index 67eb9d2aa1cd139fd2b8675ec2dffcd76b18b674..8b0849d2d2e367141b58c835342664c5b228557d 100644 (file)
@@ -5,12 +5,28 @@
 
 #include "netif-util.h"
 #include "networkd-address.h"
+#include "networkd-ipv4acd.h"
 #include "networkd-ipv4ll.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-queue.h"
 #include "parse-util.h"
 
+bool link_ipv4ll_enabled(Link *link) {
+        assert(link);
+
+        if (!link_ipv4acd_supported(link))
+                return false;
+
+        if (!link->network)
+                return false;
+
+        if (link->network->bond)
+                return false;
+
+        return link->network->link_local & ADDRESS_FAMILY_IPV4;
+}
+
 static int address_new_from_ipv4ll(Link *link, Address **ret) {
         _cleanup_(address_freep) Address *address = NULL;
         struct in_addr addr;
@@ -187,8 +203,8 @@ int ipv4ll_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        if (link->sd_device &&
-            net_get_unique_predictable_data(link->sd_device, true, &seed) >= 0) {
+        if (link->dev &&
+            net_get_unique_predictable_data(link->dev, true, &seed) >= 0) {
                 r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
                 if (r < 0)
                         return r;
@@ -263,3 +279,45 @@ int config_parse_ipv4ll(
 
         return 0;
 }
+
+int config_parse_ipv4ll_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) {
+
+        union in_addr_union a;
+        struct in_addr *ipv4ll_address = ASSERT_PTR(data);
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *ipv4ll_address = (struct in_addr) {};
+                return 0;
+        }
+
+        r = in_addr_from_string(AF_INET, rvalue, &a);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+        if (!in4_addr_is_link_local_dynamic(&a.in)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Specified address cannot be used as an IPv4 link local address, ignoring assignment: %s",
+                           rvalue);
+                return 0;
+        }
+
+        *ipv4ll_address = a.in;
+        return 0;
+}
index 82acc2ec70913f8096f7e25cf96ad43c42fceb32..fa53bd2b39ab2f80e4b42afd80a5c87de6a0bef0 100644 (file)
@@ -7,7 +7,10 @@
 
 typedef struct Link Link;
 
+bool link_ipv4ll_enabled(Link *link);
+
 int ipv4ll_configure(Link *link);
 int ipv4ll_update_mac(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll);
+CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll_address);
index 9b859897019e3d9b664b78c05f608fcba3d08f3f..32229a3fc70e84877d960a2e3a57bb5615d5c729 100644 (file)
@@ -38,7 +38,7 @@ bool link_ipv6ll_enabled(Link *link) {
         return link->network->link_local & ADDRESS_FAMILY_IPV6;
 }
 
-bool link_may_have_ipv6ll(Link *link) {
+bool link_may_have_ipv6ll(Link *link, bool check_multicast) {
         assert(link);
 
         /*
@@ -62,6 +62,9 @@ bool link_may_have_ipv6ll(Link *link) {
                 if (!link->network)
                         return false;
 
+                if (check_multicast && !FLAGS_SET(link->flags, IFF_MULTICAST) && link->network->multicast <= 0)
+                        return false;
+
                 ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) {
                         if (a->family != AF_INET6)
                                 continue;
index a9763debb1abf6efcbd9b373df8ae209804ddfeb..2759eed7f70dc11eb8da6988ab581dd4d00a0cd4 100644 (file)
@@ -22,7 +22,7 @@ typedef enum IPv6LinkLocalAddressGenMode {
 } IPv6LinkLocalAddressGenMode;
 
 bool link_ipv6ll_enabled(Link *link);
-bool link_may_have_ipv6ll(Link *link);
+bool link_may_have_ipv6ll(Link *link, bool check_multicast);
 
 IPv6LinkLocalAddressGenMode link_get_ipv6ll_addrgen_mode(Link *link);
 int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalAddressGenMode mode);
index dc9823c375631028742e22f65faae301ccfe6373..9e2ff33d970ce2560f136fc2ebbd556d248778b4 100644 (file)
@@ -1158,7 +1158,7 @@ int link_build_json(Link *link, JsonVariant **ret) {
         assert(link);
         assert(ret);
 
-        r = net_get_type_string(link->sd_device, link->iftype, &type);
+        r = net_get_type_string(link->dev, link->iftype, &type);
         if (r == -ENOMEM)
                 return r;
 
@@ -1215,7 +1215,7 @@ int link_build_json(Link *link, JsonVariant **ret) {
 
         w = json_variant_unref(w);
 
-        r = device_build_json(link->sd_device, &w);
+        r = device_build_json(link->dev, &w);
         if (r < 0)
                 return r;
 
index b314f5db8f2b194ddd0dc62a0f8d9aca30dfc132..06e9822251385d8e9521b808dbc57a9bb6338cee 100644 (file)
@@ -27,7 +27,6 @@
 #include "format-util.h"
 #include "fs-util.h"
 #include "glyph-util.h"
-#include "ipvlan.h"
 #include "missing_network.h"
 #include "netlink-util.h"
 #include "network-internal.h"
 #include "strv.h"
 #include "tc.h"
 #include "tmpfile-util.h"
+#include "tuntap.h"
 #include "udev-util.h"
 #include "util.h"
 #include "vrf.h"
 
-bool link_ipv4ll_enabled(Link *link) {
+bool link_ipv6_enabled(Link *link) {
         assert(link);
 
-        if (link->flags & IFF_LOOPBACK)
-                return false;
-
-        if (!link->network)
+        if (!socket_ipv6_is_supported())
                 return false;
 
         if (link->iftype == ARPHRD_CAN)
                 return false;
 
-        if (link->hw_addr.length != ETH_ALEN)
-                return false;
-
-        if (ether_addr_is_null(&link->hw_addr.ether))
-                return false;
-
-        /* ARPHRD_INFINIBAND seems to potentially support IPv4LL.
-         * But currently sd-ipv4ll and sd-ipv4acd only support ARPHRD_ETHER. */
-        if (link->iftype != ARPHRD_ETHER)
-                return false;
-
-        if (streq_ptr(link->kind, "vrf"))
-                return false;
-
-        /* L3 or L3S mode do not support ARP. */
-        if (IN_SET(link_get_ipvlan_mode(link), NETDEV_IPVLAN_MODE_L3, NETDEV_IPVLAN_MODE_L3S))
-                return false;
-
-        if (link->network->bond)
-                return false;
-
-        return link->network->link_local & ADDRESS_FAMILY_IPV4;
-}
-
-bool link_ipv6_enabled(Link *link) {
-        assert(link);
-
-        if (!socket_ipv6_is_supported())
+        if (!link->network)
                 return false;
 
         if (link->network->bond)
                 return false;
 
-        if (link->iftype == ARPHRD_CAN)
-                return false;
-
-        /* DHCPv6 client will not be started if no IPv6 link-local address is configured. */
-        if (link_ipv6ll_enabled(link))
+        if (link_may_have_ipv6ll(link, /* check_multicast = */ false))
                 return true;
 
         if (network_has_static_ipv6_configurations(link->network))
@@ -127,19 +93,15 @@ bool link_ipv6_enabled(Link *link) {
         return false;
 }
 
-bool link_is_ready_to_configure(Link *link, bool allow_unmanaged) {
+static bool link_is_ready_to_configure_one(Link *link, bool allow_unmanaged) {
         assert(link);
 
-        if (!link->network) {
-                if (!allow_unmanaged)
-                        return false;
-
-                return link_has_carrier(link);
-        }
-
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED))
                 return false;
 
+        if (!link->network)
+                return allow_unmanaged;
+
         if (!link->network->configure_without_carrier) {
                 if (link->set_flags_messages > 0)
                         return false;
@@ -157,6 +119,10 @@ bool link_is_ready_to_configure(Link *link, bool allow_unmanaged) {
         return true;
 }
 
+bool link_is_ready_to_configure(Link *link, bool allow_unmanaged) {
+        return check_ready_for_all_sr_iov_ports(link, allow_unmanaged, link_is_ready_to_configure_one);
+}
+
 void link_ntp_settings_clear(Link *link) {
         link->ntp = strv_free(link->ntp);
 }
@@ -218,6 +184,7 @@ static Link *link_free(Link *link) {
 
         link_free_engines(link);
 
+        set_free(link->sr_iov_virt_port_ifindices);
         free(link->ifname);
         strv_free(link->alternative_names);
         free(link->kind);
@@ -229,7 +196,7 @@ static Link *link_free(Link *link) {
         unlink_and_free(link->lldp_file);
         unlink_and_free(link->state_file);
 
-        sd_device_unref(link->sd_device);
+        sd_device_unref(link->dev);
         netdev_unref(link->netdev);
 
         hashmap_free(link->bound_to_links);
@@ -611,6 +578,12 @@ static int link_acquire_dynamic_ipv4_conf(Link *link) {
                 log_link_debug(link, "Acquiring DHCPv4 lease.");
 
         } else if (link->ipv4ll) {
+                if (in4_addr_is_set(&link->network->ipv4ll_start_address)) {
+                        r = sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not set IPv4 link-local start address: %m");
+                }
+
                 r = sd_ipv4ll_start(link->ipv4ll);
                 if (r < 0)
                         return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
@@ -923,6 +896,8 @@ static Link *link_drop(Link *link) {
         link_free_bound_to_list(link);
         link_free_bound_by_list(link);
 
+        link_clear_sr_iov_ifindices(link);
+
         link_drop_from_master(link);
 
         if (link->state_file)
@@ -1158,7 +1133,7 @@ static int link_get_network(Link *link, Network **ret) {
 
                 r = net_match_config(
                                 &network->match,
-                                link->sd_device,
+                                link->dev,
                                 &link->hw_addr,
                                 &link->permanent_hw_addr,
                                 link->driver,
@@ -1174,11 +1149,11 @@ static int link_get_network(Link *link, Network **ret) {
                 if (r == 0)
                         continue;
 
-                if (network->match.ifname && link->sd_device) {
+                if (network->match.ifname && link->dev) {
                         uint8_t name_assign_type = NET_NAME_UNKNOWN;
                         const char *attr;
 
-                        if (sd_device_get_sysattr_value(link->sd_device, "name_assign_type", &attr) >= 0)
+                        if (sd_device_get_sysattr_value(link->dev, "name_assign_type", &attr) >= 0)
                                 (void) safe_atou8(attr, &name_assign_type);
 
                         warn = name_assign_type == NET_NAME_ENUM;
@@ -1422,14 +1397,18 @@ static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Lin
 }
 
 static int link_initialized(Link *link, sd_device *device) {
+        int r;
+
         assert(link);
         assert(device);
 
         /* Always replace with the new sd_device object. As the sysname (and possibly other properties
          * or sysattrs) may be outdated. */
-        sd_device_ref(device);
-        sd_device_unref(link->sd_device);
-        link->sd_device = device;
+        device_unref_and_replace(link->dev, device);
+
+        r = link_set_sr_iov_ifindices(link);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Failed to manage SR-IOV PF and VF ports, ignoring: %m");
 
         /* Do not ignore unamanaged state case here. If an interface is renamed after being once
          * configured, and the corresponding .network file has Name= in [Match] section, then the
@@ -1484,48 +1463,39 @@ static int link_check_initialized(Link *link) {
         return link_initialized(link, device);
 }
 
-int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata) {
-        sd_device_action_t action;
-        Manager *m = userdata;
-        Link *link = NULL;
+int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t action) {
         int r, ifindex;
+        Link *link;
 
         assert(m);
         assert(device);
 
-        r = sd_device_get_action(device, &action);
+        r = sd_device_get_ifindex(device, &ifindex);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to get ifindex: %m");
+
+        r = link_get_by_index(m, ifindex, &link);
         if (r < 0) {
-                log_device_debug_errno(device, r, "Failed to get udev action, ignoring device: %m");
+                /* This error is not critical, as the corresponding rtnl message may be received later. */
+                log_device_debug_errno(device, r, "Failed to get link from ifindex %i, ignoring: %m", ifindex);
                 return 0;
         }
 
-        /* Ignore the "remove" uevent — let's remove a device only if rtnetlink says so. All other uevents
-         * are "positive" events in some form, i.e. inform us about a changed or new network interface, that
-         * still exists — and we are interested in that. */
-        if (action == SD_DEVICE_REMOVE)
-                return 0;
-
-        r = sd_device_get_ifindex(device, &ifindex);
-        if (r < 0) {
-                log_device_debug_errno(device, r, "Ignoring udev %s event for device without ifindex or with invalid ifindex: %m",
-                                       device_action_to_string(action));
+        /* Let's unref the sd-device object assigned to the corresponding Link object, but keep the Link
+         * object here. It will be removed only when rtnetlink says so. */
+        if (action == SD_DEVICE_REMOVE) {
+                link->dev = sd_device_unref(link->dev);
                 return 0;
         }
 
         r = device_is_renaming(device);
-        if (r < 0) {
-                log_device_debug_errno(device, r, "Failed to determine the device is renamed or not, ignoring '%s' uevent: %m",
-                                       device_action_to_string(action));
-                return 0;
-        }
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to determine if the device is renaming or not: %m");
         if (r > 0) {
-                log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed.");
-                return 0;
-        }
-
-        r = link_get_by_index(m, ifindex, &link);
-        if (r < 0) {
-                log_device_debug_errno(device, r, "Failed to get link from ifindex %i, ignoring: %m", ifindex);
+                log_device_debug(device, "Device is renaming, waiting for the interface to be renamed.");
+                /* TODO:
+                 * What happens when a device is initialized, then soon renamed after that? When we detect
+                 * such, maybe we should cancel or postpone all queued requests for the interface. */
                 return 0;
         }
 
index 5174127413f02ff7353909e3d300c6ce1ea51b59..362f439940a8f7e24f41b62b01d98117614d31c9 100644 (file)
@@ -52,6 +52,9 @@ typedef struct Link {
         int ifindex;
         int master_ifindex;
         int dsa_master_ifindex;
+        int sr_iov_phys_port_ifindex;
+        Set *sr_iov_virt_port_ifindices;
+
         char *ifname;
         char **alternative_names;
         char *kind;
@@ -66,7 +69,7 @@ typedef struct Link {
         uint32_t min_mtu;
         uint32_t max_mtu;
         uint32_t original_mtu;
-        sd_device *sd_device;
+        sd_device *dev;
         char *driver;
 
         /* link-local addressing */
@@ -225,8 +228,6 @@ static inline bool link_has_carrier(Link *link) {
 bool link_ipv6_enabled(Link *link);
 int link_ipv6ll_gained(Link *link);
 
-bool link_ipv4ll_enabled(Link *link);
-
 int link_stop_engines(Link *link, bool may_keep_dhcp);
 
 const char* link_state_to_string(LinkState s) _const_;
@@ -235,7 +236,7 @@ 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_udev_process_link(Manager *m, sd_device *device, sd_device_action_t action);
 int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
 
 int link_flags_to_string_alloc(uint32_t flags, char **ret);
index d8a18006d3db5c8e036e112bb23208bc421e2991..52c62d72978c9cfa99f51cf9af0075245f90abb7 100644 (file)
@@ -8,16 +8,19 @@
 #include <linux/nexthop.h>
 #include <linux/nl80211.h>
 
-#include "sd-daemon.h"
 #include "sd-netlink.h"
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-locator.h"
 #include "bus-log-control-api.h"
 #include "bus-polkit.h"
 #include "bus-util.h"
 #include "conf-parser.h"
+#include "daemon-util.h"
 #include "def.h"
+#include "device-private.h"
+#include "device-util.h"
 #include "dns-domain.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -55,6 +58,7 @@
 #include "sysctl-util.h"
 #include "tclass.h"
 #include "tmpfile-util.h"
+#include "tuntap.h"
 #include "udev-util.h"
 
 /* use 128 MB for receive socket kernel queue. */
@@ -168,6 +172,39 @@ static int manager_connect_bus(Manager *m) {
         return 0;
 }
 
+static int manager_process_uevent(sd_device_monitor *monitor, sd_device *device, void *userdata) {
+        Manager *m = ASSERT_PTR(userdata);
+        sd_device_action_t action;
+        const char *s;
+        int r;
+
+        assert(device);
+
+        r = sd_device_get_action(device, &action);
+        if (r < 0)
+                return log_device_warning_errno(device, r, "Failed to get udev action, ignoring: %m");
+
+        r = sd_device_get_subsystem(device, &s);
+        if (r < 0)
+                return log_device_warning_errno(device, r, "Failed to get subsystem, ignoring: %m");
+
+        if (streq(s, "net"))
+                r = manager_udev_process_link(m, device, action);
+        else if (streq(s, "ieee80211"))
+                r = manager_udev_process_wiphy(m, device, action);
+        else if (streq(s, "rfkill"))
+                r = manager_udev_process_rfkill(m, device, action);
+        else {
+                log_device_debug(device, "Received device with unexpected subsystem \"%s\", ignoring.", s);
+                return 0;
+        }
+        if (r < 0)
+                log_device_warning_errno(device, r, "Failed to process \"%s\" uevent, ignoring: %m",
+                                         device_action_to_string(action));
+
+        return 0;
+}
+
 static int manager_connect_udev(Manager *m) {
         int r;
 
@@ -186,35 +223,66 @@ static int manager_connect_udev(Manager *m) {
 
         r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "net", NULL);
         if (r < 0)
-                return log_error_errno(r, "Could not add device monitor filter: %m");
+                return log_error_errno(r, "Could not add device monitor filter for net subsystem: %m");
+
+        r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "ieee80211", NULL);
+        if (r < 0)
+                return log_error_errno(r, "Could not add device monitor filter for ieee80211 subsystem: %m");
+
+        r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "rfkill", NULL);
+        if (r < 0)
+                return log_error_errno(r, "Could not add device monitor filter for rfkill subsystem: %m");
 
         r = sd_device_monitor_attach_event(m->device_monitor, m->event);
         if (r < 0)
                 return log_error_errno(r, "Failed to attach event to device monitor: %m");
 
-        r = sd_device_monitor_start(m->device_monitor, manager_udev_process_link, m);
+        r = sd_device_monitor_start(m->device_monitor, manager_process_uevent, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to start device monitor: %m");
 
         return 0;
 }
 
-static int systemd_netlink_fd(void) {
-        int n, fd, rtnl_fd = -EINVAL;
+static int manager_listen_fds(Manager *m, int *ret_rtnl_fd) {
+        _cleanup_strv_free_ char **names = NULL;
+        int n, rtnl_fd = -1;
 
-        n = sd_listen_fds(true);
-        if (n <= 0)
+        assert(m);
+        assert(ret_rtnl_fd);
+
+        n = sd_listen_fds_with_names(/* unset_environment = */ true, &names);
+        if (n < 0)
+                return n;
+
+        if (strv_length(names) != (size_t) n)
                 return -EINVAL;
 
-        for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++)
+        for (int i = 0; i < n; i++) {
+                int fd = i + SD_LISTEN_FDS_START;
+
                 if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
-                        if (rtnl_fd >= 0)
-                                return -EINVAL;
+                        if (rtnl_fd >= 0) {
+                                log_debug("Received multiple netlink socket, ignoring.");
+                                safe_close(fd);
+                                continue;
+                        }
 
                         rtnl_fd = fd;
+                        continue;
                 }
 
-        return rtnl_fd;
+                if (manager_add_tuntap_fd(m, fd, names[i]) >= 0)
+                        continue;
+
+                if (m->test_mode)
+                        safe_close(fd);
+                else
+                        close_and_notify_warn(fd, names[i]);
+        }
+
+        *ret_rtnl_fd = rtnl_fd;
+        return 0;
 }
 
 static int manager_connect_genl(Manager *m) {
@@ -281,18 +349,21 @@ static int manager_setup_rtnl_filter(Manager *manager) {
         return sd_netlink_attach_filter(manager->rtnl, ELEMENTSOF(filter), filter);
 }
 
-static int manager_connect_rtnl(Manager *m) {
-        int fd, r;
+static int manager_connect_rtnl(Manager *m, int fd) {
+        _unused_ _cleanup_close_ int fd_close = fd;
+        int r;
 
         assert(m);
 
-        fd = systemd_netlink_fd();
+        /* This takes input fd. */
+
         if (fd < 0)
                 r = sd_netlink_open(&m->rtnl);
         else
                 r = sd_netlink_open_fd(&m->rtnl, fd);
         if (r < 0)
                 return r;
+        TAKE_FD(fd_close);
 
         /* Bump receiver buffer, but only if we are not called via socket activation, as in that
          * case systemd sets the receive buffer size for us, and the value in the .socket unit
@@ -443,6 +514,7 @@ static int manager_set_keep_configuration(Manager *m) {
 }
 
 int manager_setup(Manager *m) {
+        _cleanup_close_ int rtnl_fd = -1;
         int r;
 
         assert(m);
@@ -466,7 +538,11 @@ int manager_setup(Manager *m) {
         if (r < 0)
                 return r;
 
-        r = manager_connect_rtnl(m);
+        r = manager_listen_fds(m, &rtnl_fd);
+        if (r < 0)
+                return r;
+
+        r = manager_connect_rtnl(m, TAKE_FD(rtnl_fd));
         if (r < 0)
                 return r;
 
@@ -556,6 +632,8 @@ Manager* manager_free(Manager *m) {
 
         m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
 
+        m->tuntap_fds_by_name = hashmap_free(m->tuntap_fds_by_name);
+
         m->wiphy_by_name = hashmap_free(m->wiphy_by_name);
         m->wiphy_by_index = hashmap_free_with_destructor(m->wiphy_by_index, wiphy_free);
 
@@ -634,6 +712,8 @@ int manager_load_config(Manager *m) {
         if (r < 0)
                 return r;
 
+        manager_clear_unmanaged_tuntap_fds(m);
+
         r = network_load(m, &m->networks);
         if (r < 0)
                 return r;
@@ -949,12 +1029,10 @@ int manager_set_hostname(Manager *m, const char *hostname) {
                 return 0;
         }
 
-        r = sd_bus_call_method_async(
+        r = bus_call_method_async(
                         m->bus,
                         NULL,
-                        "org.freedesktop.hostname1",
-                        "/org/freedesktop/hostname1",
-                        "org.freedesktop.hostname1",
+                        bus_hostname,
                         "SetHostname",
                         set_hostname_handler,
                         m,
@@ -998,12 +1076,10 @@ int manager_set_timezone(Manager *m, const char *tz) {
                 return 0;
         }
 
-        r = sd_bus_call_method_async(
+        r = bus_call_method_async(
                         m->bus,
                         NULL,
-                        "org.freedesktop.timedate1",
-                        "/org/freedesktop/timedate1",
-                        "org.freedesktop.timedate1",
+                        bus_timedate,
                         "SetTimezone",
                         set_timezone_handler,
                         m,
index 2191d0d9557340a7dbea70309b162d598a270fda..e16f2b854b76ff9829cce2f65dbb145089f15c18 100644 (file)
@@ -100,6 +100,8 @@ struct Manager {
         FirewallContext *fw_ctx;
 
         OrderedSet *request_queue;
+
+        Hashmap *tuntap_fds_by_name;
 };
 
 int manager_new(Manager **ret, bool test_mode);
index 2b02978ab47c8fa9c0f8082e7399d83c658da393..579282136f2c8f1e6dee2afaeb9443d559b514ad 100644 (file)
@@ -41,7 +41,7 @@ bool link_ipv6_accept_ra_enabled(Link *link) {
         if (!link->network)
                 return false;
 
-        if (!link_may_have_ipv6ll(link))
+        if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
                 return false;
 
         assert(link->network->ipv6_accept_ra >= 0);
@@ -314,7 +314,7 @@ 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");
 
-        lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+        lifetime_usec = sec16_to_usec(lifetime_sec, timestamp_usec);
 
         r = sd_ndisc_router_get_address(rt, &gateway);
         if (r < 0)
@@ -436,8 +436,8 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
         if (lifetime_preferred_sec > lifetime_valid_sec)
                 return 0;
 
-        lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec);
-        lifetime_preferred_usec = usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec);
+        lifetime_valid_usec = sec_to_usec(lifetime_valid_sec, timestamp_usec);
+        lifetime_preferred_usec = sec_to_usec(lifetime_preferred_sec, timestamp_usec);
 
         r = ndisc_generate_addresses(link, &prefix, prefixlen, &addresses);
         if (r < 0)
@@ -513,7 +513,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         route->family = AF_INET6;
         route->flags = RTM_F_PREFIX;
         route->dst_prefixlen = prefixlen;
-        route->lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+        route->lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
 
         r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6);
         if (r < 0)
@@ -646,7 +646,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         route->gw_family = AF_INET6;
         route->dst.in6 = dst;
         route->dst_prefixlen = prefixlen;
-        route->lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+        route->lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
 
         r = ndisc_request_route(TAKE_PTR(route), link, rt);
         if (r < 0)
@@ -700,7 +700,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
         if (lifetime_sec == 0)
                 return 0;
 
-        lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+        lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
 
         n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
         if (n < 0)
@@ -794,7 +794,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
         if (lifetime_sec == 0)
                 return 0;
 
-        lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+        lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
 
         r = sd_ndisc_router_dnssl_get_domains(rt, &l);
         if (r < 0)
@@ -1099,7 +1099,7 @@ static int ndisc_process_request(Request *req, Link *link, void *userdata) {
 
         assert(link);
 
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return 0;
 
         r = ndisc_configure(link);
@@ -1137,20 +1137,21 @@ int link_request_ndisc(Link *link) {
 void ndisc_vacuum(Link *link) {
         NDiscRDNSS *r;
         NDiscDNSSL *d;
-        usec_t time_now;
+        usec_t now_usec;
 
         assert(link);
+        assert(link->manager);
 
         /* Removes all RDNSS and DNSSL entries whose validity time has passed */
 
-        time_now = now(CLOCK_BOOTTIME);
+        assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
 
         SET_FOREACH(r, link->ndisc_rdnss)
-                if (r->lifetime_usec < time_now)
+                if (r->lifetime_usec < now_usec)
                         free(set_remove(link->ndisc_rdnss, r));
 
         SET_FOREACH(d, link->ndisc_dnssl)
-                if (d->lifetime_usec < time_now)
+                if (d->lifetime_usec < now_usec)
                         free(set_remove(link->ndisc_dnssl, d));
 }
 
index ceaaa6a0f7cdbabe2fa65ad3191cc2b4c93bf99b..a4f038681a42e04217e05436f3e3281af717eaf1 100644 (file)
@@ -62,6 +62,7 @@ Match.Host,                                  config_parse_net_condition,
 Match.Virtualization,                        config_parse_net_condition,                               CONDITION_VIRTUALIZATION,      offsetof(Network, conditions)
 Match.KernelCommandLine,                     config_parse_net_condition,                               CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions)
 Match.KernelVersion,                         config_parse_net_condition,                               CONDITION_KERNEL_VERSION,      offsetof(Network, conditions)
+Match.Credential,                            config_parse_net_condition,                               CONDITION_CREDENTIAL,          offsetof(Network, conditions)
 Match.Architecture,                          config_parse_net_condition,                               CONDITION_ARCHITECTURE,        offsetof(Network, conditions)
 Match.Firmware,                              config_parse_net_condition,                               CONDITION_FIRMWARE,            offsetof(Network, conditions)
 Link.MACAddress,                             config_parse_hw_addr,                                     0,                             offsetof(Network, hw_addr)
@@ -106,6 +107,7 @@ Network.DHCPServer,                          config_parse_bool,
 Network.LinkLocalAddressing,                 config_parse_link_local_address_family,                   0,                             offsetof(Network, link_local)
 Network.IPv6LinkLocalAddressGenerationMode,  config_parse_ipv6_link_local_address_gen_mode,            0,                             offsetof(Network, ipv6ll_address_gen_mode)
 Network.IPv6StableSecretAddress,             config_parse_in_addr_non_null,                            AF_INET6,                      offsetof(Network, ipv6ll_stable_secret)
+Network.IPv4LLStartAddress,                  config_parse_ipv4ll_address,                              0,                             offsetof(Network, ipv4ll_start_address)
 Network.IPv4LLRoute,                         config_parse_bool,                                        0,                             offsetof(Network, ipv4ll_route)
 Network.DefaultRouteOnDevice,                config_parse_bool,                                        0,                             offsetof(Network, default_route_on_device)
 Network.LLDP,                                config_parse_lldp_mode,                                   0,                             offsetof(Network, lldp_mode)
@@ -260,6 +262,7 @@ DHCPv6.SendOption,                           config_parse_dhcp_send_option,
 DHCPv6.IAID,                                 config_parse_iaid,                                        AF_INET6,                      0
 DHCPv6.DUIDType,                             config_parse_duid_type,                                   0,                             offsetof(Network, dhcp6_duid)
 DHCPv6.DUIDRawData,                          config_parse_duid_rawdata,                                0,                             offsetof(Network, dhcp6_duid)
+DHCPv6.RapidCommit,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_rapid_commit)
 IPv6AcceptRA.UseGateway,                     config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_gateway)
 IPv6AcceptRA.UseRoutePrefix,                 config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_route_prefix)
 IPv6AcceptRA.UseAutonomousPrefix,            config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
@@ -553,12 +556,11 @@ DHCP.RouteMetric,                            config_parse_dhcp_or_ra_route_metri
 DHCP.RouteTable,                             config_parse_dhcp_or_ra_route_table,                      AF_INET,                       0
 DHCP.UseTimezone,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_timezone)
 DHCP.ListenPort,                             config_parse_uint16,                                      0,                             offsetof(Network, dhcp_client_port)
-DHCP.RapidCommit,                            config_parse_warn_compat,                                 DISABLED_LEGACY,               0
+DHCP.RapidCommit,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_rapid_commit)
 DHCP.ForceDHCPv6PDOtherInformation,          config_parse_warn_compat,                                 DISABLED_LEGACY,               0
 DHCPv4.UseDomainName,                        config_parse_dhcp_use_domains,                            AF_INET,                       0
 DHCPv4.CriticalConnection,                   config_parse_tristate,                                    0,                             offsetof(Network, dhcp_critical)
 DHCPv6.RouteMetric,                          config_parse_dhcp_or_ra_route_metric,                     AF_INET6,                      0
-DHCPv6.RapidCommit,                          config_parse_warn_compat,                                 DISABLED_LEGACY,               0
 DHCPv6.ForceDHCPv6PDOtherInformation,        config_parse_warn_compat,                                 DISABLED_LEGACY,               0
 DHCPv6PrefixDelegation.SubnetId,             config_parse_dhcp_pd_subnet_id,                           0,                             offsetof(Network, dhcp_pd_subnet_id)
 DHCPv6PrefixDelegation.Announce,             config_parse_bool,                                        0,                             offsetof(Network, dhcp_pd_announce)
index e090a78023e9f51995fd0f8b8ca99380ce561073..61b900ada2dfcb13df33f8e891c5ee9dca57469e 100644 (file)
@@ -414,6 +414,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .dhcp6_use_dns = true,
                 .dhcp6_use_hostname = true,
                 .dhcp6_use_ntp = true,
+                .dhcp6_use_rapid_commit = true,
                 .dhcp6_duid.type = _DUID_TYPE_INVALID,
                 .dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID,
 
@@ -549,7 +550,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                         config_item_perf_lookup, network_network_gperf_lookup,
                         CONFIG_PARSE_WARN,
                         network,
-                        &network->stats_by_path);
+                        &network->stats_by_path,
+                        NULL);
         if (r < 0)
                 return r; /* config_parse_many() logs internally. */
 
index 98e6159040ee302b8cbfa1064d7fb898a0a7505f..3df7eee555e3b71e210ed38a5d3627f90edde2df 100644 (file)
@@ -164,6 +164,7 @@ struct Network {
         bool dhcp6_use_hostname;
         bool dhcp6_use_ntp;
         bool dhcp6_use_ntp_set;
+        bool dhcp6_use_rapid_commit;
         DHCPUseDomains dhcp6_use_domains;
         bool dhcp6_use_domains_set;
         uint32_t dhcp6_iaid;
@@ -208,6 +209,7 @@ struct Network {
         AddressFamily link_local;
         IPv6LinkLocalAddressGenMode ipv6ll_address_gen_mode;
         struct in6_addr ipv6ll_stable_secret;
+        struct in_addr ipv4ll_start_address;
         bool ipv4ll_route;
 
         /* IPv6 RA support */
index 32ee7aa7abfaceff868360138cad7a719538fbef..a0ce162532c12702c61e1ce3f74c31cc7552cb3f 100644 (file)
@@ -54,7 +54,7 @@ void network_adjust_radv(Network *network) {
 bool link_radv_enabled(Link *link) {
         assert(link);
 
-        if (!link_may_have_ipv6ll(link))
+        if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
                 return false;
 
         if (link->hw_addr.length != ETH_ALEN)
@@ -549,7 +549,7 @@ static int radv_is_ready_to_configure(Link *link) {
         assert(link);
         assert(link->network);
 
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+        if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return false;
 
         if (in6_addr_is_null(&link->ipv6ll_address))
index 2708f9d79dc057608e6db0e1c14023c1feea6e81..71e578d89820f5c40e658c5966b30cb75832b939 100644 (file)
@@ -1153,7 +1153,7 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li
         return 1;
 }
 
-static int route_configure(const Route *route, Link *link, Request *req) {
+static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link, Request *req) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
@@ -1179,9 +1179,8 @@ static int route_configure(const Route *route, Link *link, Request *req) {
         if (r < 0)
                 return r;
 
-        if (route->lifetime_usec != USEC_INFINITY) {
-                r = sd_netlink_message_append_u32(m, RTA_EXPIRES,
-                        MIN(DIV_ROUND_UP(usec_sub_unsigned(route->lifetime_usec, now(CLOCK_BOOTTIME)), USEC_PER_SEC), UINT32_MAX));
+        if (lifetime_sec != UINT32_MAX) {
+                r = sd_netlink_message_append_u32(m, RTA_EXPIRES, lifetime_sec);
                 if (r < 0)
                         return r;
         }
@@ -1299,7 +1298,8 @@ static int route_is_ready_to_configure(const Route *route, Link *link) {
                 if (r < 0)
                         return false;
                 if (r > 0) {
-                        if (!link_is_ready_to_configure(l, true))
+                        if (!link_is_ready_to_configure(l, /* allow_unmanaged = */ true) ||
+                            !link_has_carrier(l))
                                 return false;
 
                         m->ifindex = l->ifindex;
@@ -1318,6 +1318,7 @@ static int route_process_request(Request *req, Link *link, Route *route) {
 
         assert(req);
         assert(link);
+        assert(link->manager);
         assert(route);
 
         r = route_is_ready_to_configure(route, link);
@@ -1356,7 +1357,25 @@ static int route_process_request(Request *req, Link *link, Route *route) {
                 }
         }
 
-        r = route_configure(route, link, req);
+        usec_t now_usec;
+        assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
+        uint32_t sec = usec_to_sec(route->lifetime_usec, now_usec);
+        if (sec == 0) {
+                log_link_debug(link, "Refuse to configure %s route with zero lifetime.",
+                               network_config_source_to_string(route->source));
+
+                if (converted)
+                        for (size_t i = 0; i < converted->n; i++) {
+                                Route *existing;
+
+                                assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0);
+                                route_cancel_requesting(existing);
+                        }
+                else
+                        route_cancel_requesting(route);
+        }
+
+        r = route_configure(route, sec, link, req);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to configure route: %m");
 
index 148277685e0355e693e4e0477ef244fe58b1c2de..f9d774eee9e38331305b1f6e9efd253641c9cc88 100644 (file)
@@ -14,6 +14,8 @@
 #include "networkd-manager.h"
 #include "networkd-queue.h"
 #include "networkd-setlink.h"
+#include "networkd-sriov.h"
+#include "networkd-wiphy.h"
 
 static int get_link_default_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         return link_getlink_handler_internal(rtnl, m, link, "Failed to sync link information");
@@ -230,7 +232,7 @@ static int link_configure_fill_message(
                         return r;
 
                 if (link->network->use_bpdu >= 0) {
-                        r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, link->network->use_bpdu);
+                        r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, !link->network->use_bpdu);
                         if (r < 0)
                                 return r;
                 }
@@ -254,7 +256,7 @@ static int link_configure_fill_message(
                 }
 
                 if (link->network->allow_port_to_be_root >= 0) {
-                        r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, link->network->allow_port_to_be_root);
+                        r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, !link->network->allow_port_to_be_root);
                         if (r < 0)
                                 return r;
                 }
@@ -796,20 +798,28 @@ int link_request_to_set_master(Link *link) {
         assert(link->network);
 
         if (link->network->keep_master) {
+                /* When KeepMaster=yes, BatmanAdvanced=, Bond=, Bridge=, and VRF= are ignored. */
                 link->master_set = true;
                 return 0;
-        }
-
-        link->master_set = false;
 
-        if (link->network->batadv || link->network->bond || link->network->bridge || link->network->vrf)
+        } else if (link->network->batadv || link->network->bond || link->network->bridge || link->network->vrf) {
+                link->master_set = false;
                 return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MASTER,
                                              link_set_master_handler,
                                              NULL);
-        else
+
+        } else if (link->master_ifindex != 0) {
+                /* Unset master only when it is set. */
+                link->master_set = false;
                 return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MASTER,
                                              link_unset_master_handler,
                                              NULL);
+
+        } else {
+                /* Nothing we need to do. */
+                link->master_set = true;
+                return 0;
+        }
 }
 
 int link_request_to_set_mtu(Link *link, uint32_t mtu) {
@@ -951,17 +961,10 @@ static int link_up_or_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Requ
         r = sd_netlink_message_get_errno(m);
         if (r == -ENETDOWN && up && link_up_dsa_slave(link) > 0)
                 log_link_message_debug_errno(link, m, r, "Could not bring up dsa slave, retrying again after dsa master becomes up");
-        else if (r < 0) {
-                const char *error_msg;
-
-                error_msg = up ?
-                        (on_activate ? "Could not bring up interface" : "Could not bring up interface, ignoring") :
-                        (on_activate ? "Could not bring down interface" : "Could not bring down interface, ignoring");
-
-                log_link_message_warning_errno(link, m, r, error_msg);
-                if (on_activate)
-                        return 0;
-        }
+        else if (r < 0)
+                log_link_message_warning_errno(link, m, r, up ?
+                                               "Could not bring up interface, ignoring" :
+                                               "Could not bring down interface, ignoring");
 
         r = link_call_getlink(link, get_link_update_flag_handler);
         if (r < 0) {
@@ -1005,18 +1008,34 @@ static int link_up_or_down(Link *link, bool up, Request *req) {
         return request_call_netlink_async(link->manager->rtnl, m, req);
 }
 
-static bool link_is_ready_to_activate(Link *link) {
+static bool link_is_ready_to_activate_one(Link *link, bool allow_unmanaged) {
         assert(link);
 
-        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED))
                 return false;
 
+        if (!link->network)
+                return allow_unmanaged;
+
         if (link->set_link_messages > 0)
                 return false;
 
         return true;
 }
 
+ static bool link_is_ready_to_activate(Link *link, bool up) {
+        assert(link);
+
+        if (!check_ready_for_all_sr_iov_ports(link, /* allow_unmanaged = */ false,
+                                              link_is_ready_to_activate_one))
+                return false;
+
+        if (up && link_rfkilled(link) > 0)
+                return false;
+
+        return true;
+}
+
 static int link_process_activation(Request *req, Link *link, void *userdata) {
         bool up = PTR_TO_INT(userdata);
         int r;
@@ -1024,7 +1043,7 @@ static int link_process_activation(Request *req, Link *link, void *userdata) {
         assert(req);
         assert(link);
 
-        if (!link_is_ready_to_activate(link))
+        if (!link_is_ready_to_activate(link, up))
                 return 0;
 
         r = link_up_or_down(link, up, req);
@@ -1104,6 +1123,9 @@ static bool link_is_ready_to_bring_up_or_down(Link *link, bool up) {
         if (!link->activated)
                 return false;
 
+        if (up && link_rfkilled(link) > 0)
+                return false;
+
         return true;
 }
 
index 09980b42562fa315ce7b499803219e530585232e..5c270741bbd650d223dfbdbb7c4b6b00ee75c55e 100644 (file)
@@ -1,6 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later
  * Copyright © 2020 VMware, Inc. */
 
+#include "device-enumerator-private.h"
+#include "dirent-util.h"
+#include "fd-util.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-queue.h"
@@ -103,3 +106,262 @@ int link_request_sr_iov_vfs(Link *link) {
 
         return 0;
 }
+
+static int find_ifindex_from_pci_dev_port(sd_device *pci_dev, const char *dev_port) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *dev;
+        int ifindex, r;
+
+        assert(pci_dev);
+        assert(dev_port);
+
+        r = sd_device_enumerator_new(&e);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_allow_uninitialized(e);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_parent(e, pci_dev);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_subsystem(e, "net", true);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_sysattr(e, "dev_port", dev_port, true);
+        if (r < 0)
+                return r;
+
+        dev = sd_device_enumerator_get_device_first(e);
+        if (!dev)
+                return -ENODEV; /* no device found */
+
+        if (sd_device_enumerator_get_device_next(e))
+                return -ENXIO; /* multiple devices found */
+
+        r = sd_device_get_ifindex(dev, &ifindex);
+        if (r < 0)
+                return r;
+
+        assert(ifindex > 0);
+        return ifindex;
+}
+
+static int manager_update_sr_iov_ifindices(Manager *manager, int phys_port_ifindex, int virt_port_ifindex) {
+        Link *phys_link = NULL, *virt_link = NULL;
+        int r;
+
+        assert(manager);
+        assert(phys_port_ifindex > 0);
+        assert(virt_port_ifindex > 0);
+
+        /* This sets ifindices only when both interfaces are already managed by us. */
+
+        r = link_get_by_index(manager, phys_port_ifindex, &phys_link);
+        if (r < 0)
+                return r;
+
+        r = link_get_by_index(manager, virt_port_ifindex, &virt_link);
+        if (r < 0)
+                return r;
+
+        /* update VF ifindex in PF */
+        r = set_ensure_put(&phys_link->sr_iov_virt_port_ifindices, NULL, INT_TO_PTR(virt_port_ifindex));
+        if (r < 0)
+                return r;
+
+        log_link_debug(phys_link,
+                       "Found SR-IOV VF port %s(%i).",
+                       virt_link ? virt_link->ifname : "n/a", virt_port_ifindex);
+
+        /* update PF ifindex in VF */
+        if (virt_link->sr_iov_phys_port_ifindex > 0 && virt_link->sr_iov_phys_port_ifindex != phys_port_ifindex) {
+                Link *old_phys_link;
+
+                if (link_get_by_index(manager, virt_link->sr_iov_phys_port_ifindex, &old_phys_link) >= 0)
+                        set_remove(old_phys_link->sr_iov_virt_port_ifindices, INT_TO_PTR(virt_port_ifindex));
+        }
+
+        virt_link->sr_iov_phys_port_ifindex = phys_port_ifindex;
+
+        log_link_debug(virt_link,
+                       "Found SR-IOV PF port %s(%i).",
+                       phys_link ? phys_link->ifname : "n/a", phys_port_ifindex);
+
+        return 0;
+}
+
+static int link_set_sr_iov_phys_port(Link *link) {
+        _cleanup_(sd_device_unrefp) sd_device *pci_physfn_dev = NULL;
+        const char *dev_port;
+        sd_device *pci_dev;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        if (link->sr_iov_phys_port_ifindex > 0)
+                return 0;
+
+        if (!link->dev)
+                return -ENODEV;
+
+        r = sd_device_get_sysattr_value(link->dev, "dev_port", &dev_port);
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_parent_with_subsystem_devtype(link->dev, "pci", NULL, &pci_dev);
+        if (r < 0)
+                return r;
+
+        r = sd_device_new_child(&pci_physfn_dev, pci_dev, "physfn");
+        if (r < 0)
+                return r;
+
+        r = find_ifindex_from_pci_dev_port(pci_physfn_dev, dev_port);
+        if (r < 0)
+                return r;
+
+        return manager_update_sr_iov_ifindices(link->manager, r, link->ifindex);
+}
+
+static int link_set_sr_iov_virt_ports(Link *link) {
+        const char *dev_port, *pci_syspath;
+        _cleanup_closedir_ DIR *dir = NULL;
+        sd_device *pci_dev;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        set_clear(link->sr_iov_virt_port_ifindices);
+
+        if (!link->dev)
+                return -ENODEV;
+
+        r = sd_device_get_sysattr_value(link->dev, "dev_port", &dev_port);
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_parent_with_subsystem_devtype(link->dev, "pci", NULL, &pci_dev);
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_syspath(pci_dev, &pci_syspath);
+        if (r < 0)
+                return r;
+
+        dir = opendir(pci_syspath);
+        if (!dir)
+                return -errno;
+
+        FOREACH_DIRENT_ALL(de, dir, break) {
+                _cleanup_(sd_device_unrefp) sd_device *pci_virtfn_dev = NULL;
+
+                if (de->d_type != DT_LNK)
+                        continue;
+
+                /* Accept name prefixed with "virtfn", but refuse "virtfn" itself. */
+                if (isempty(startswith(de->d_name, "virtfn")))
+                        continue;
+
+                if (sd_device_new_child(&pci_virtfn_dev, pci_dev, de->d_name) < 0)
+                        continue;
+
+                if (find_ifindex_from_pci_dev_port(pci_virtfn_dev, dev_port) < 0)
+                        continue;
+
+                if (manager_update_sr_iov_ifindices(link->manager, link->ifindex, r) < 0)
+                        continue;
+        }
+
+        return 0;
+}
+
+int link_set_sr_iov_ifindices(Link *link) {
+        int r;
+
+        assert(link);
+
+        r = link_set_sr_iov_phys_port(link);
+        if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
+                return r;
+
+        r = link_set_sr_iov_virt_ports(link);
+        if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
+                return r;
+
+        return 0;
+}
+
+void link_clear_sr_iov_ifindices(Link *link) {
+        void *v;
+
+        assert(link);
+        assert(link->manager);
+
+        if (link->sr_iov_phys_port_ifindex > 0) {
+                Link *phys_link;
+
+                if (link_get_by_index(link->manager, link->sr_iov_phys_port_ifindex, &phys_link) >= 0)
+                        set_remove(phys_link->sr_iov_virt_port_ifindices, INT_TO_PTR(link->ifindex));
+
+                link->sr_iov_phys_port_ifindex = 0;
+        }
+
+        while ((v = set_steal_first(link->sr_iov_virt_port_ifindices))) {
+                Link *virt_link;
+
+                if (link_get_by_index(link->manager, PTR_TO_INT(v), &virt_link) >= 0)
+                        virt_link->sr_iov_phys_port_ifindex = 0;
+        }
+}
+
+bool check_ready_for_all_sr_iov_ports(
+                Link *link,
+                bool allow_unmanaged, /* for the main target */
+                bool (check_one)(Link *link, bool allow_unmanaged)) {
+
+        Link *phys_link;
+        void *v;
+
+        assert(link);
+        assert(link->manager);
+        assert(check_one);
+
+        /* Some drivers make VF ports become down when their PF port becomes down, and may fail to configure
+         * VF ports. Also, when a VF port becomes up/down, its PF port and other VF ports may become down.
+         * See issue #23315. */
+
+        /* First, check the main target. */
+        if (!check_one(link, allow_unmanaged))
+                return false;
+
+        /* If this is a VF port, then also check the PF port. */
+        if (link->sr_iov_phys_port_ifindex > 0) {
+                if (link_get_by_index(link->manager, link->sr_iov_phys_port_ifindex, &phys_link) < 0 ||
+                    !check_one(phys_link, /* allow_unmanaged = */ true))
+                        return false;
+        } else
+                phys_link = link;
+
+        /* Also check all VF ports. */
+        SET_FOREACH(v, phys_link->sr_iov_virt_port_ifindices) {
+                int ifindex = PTR_TO_INT(v);
+                Link *virt_link;
+
+                if (ifindex == link->ifindex)
+                        continue; /* The main target link is a VF port, and its state is already checked. */
+
+                if (link_get_by_index(link->manager, ifindex, &virt_link) < 0)
+                        return false;
+
+                if (!check_one(virt_link, /* allow_unmanaged = */ true))
+                        return false;
+        }
+
+        return true;
+}
index 539fa060992a8b5d9f672763f67fa12944fae3c8..0d4276e099359756aaa81636fe6df80162a41ba7 100644 (file)
@@ -7,3 +7,11 @@
 typedef struct Link Link;
 
 int link_request_sr_iov_vfs(Link *link);
+
+int link_set_sr_iov_ifindices(Link *link);
+void link_clear_sr_iov_ifindices(Link *link);
+
+bool check_ready_for_all_sr_iov_ports(
+                Link *link,
+                bool allow_unmanaged, /* for the main target */
+                bool (check_one)(Link *link, bool allow_unmanaged));
index 0a627588aa2e5c617dcad43f846098ab4fd23c1c..373184a3ed71b903fd78797619faa355db03c7d8 100644 (file)
@@ -37,6 +37,18 @@ typedef enum NetworkConfigState {
         NETWORK_CONFIG_STATE_REMOVING    = 1 << 5, /* e.g. address_remove() is called, but no response is received yet */
 } NetworkConfigState;
 
+static inline usec_t sec16_to_usec(uint16_t sec, usec_t timestamp_usec) {
+        return sec == UINT16_MAX ? USEC_INFINITY : usec_add(timestamp_usec, sec * USEC_PER_SEC);
+}
+
+static inline usec_t sec_to_usec(uint32_t sec, usec_t timestamp_usec) {
+        return sec == UINT32_MAX ? USEC_INFINITY : usec_add(timestamp_usec, sec * USEC_PER_SEC);
+}
+
+static inline uint32_t usec_to_sec(usec_t usec, usec_t now_usec) {
+        return MIN(DIV_ROUND_UP(usec_sub_unsigned(usec, now_usec), USEC_PER_SEC), UINT32_MAX);
+}
+
 CONFIG_PARSER_PROTOTYPE(config_parse_link_local_address_family);
 CONFIG_PARSER_PROTOTYPE(config_parse_address_family_with_kernel);
 CONFIG_PARSER_PROTOTYPE(config_parse_ip_masquerade);
index 861ebe6b699e5144add10ea58dcd433c60cf71be..63874cdf98ec3c38ea1fe7a9cc14280c3e51fbe3 100644 (file)
@@ -1,11 +1,15 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <net/if_arp.h>
 #include <linux/nl80211.h>
 
+#include "device-private.h"
 #include "device-util.h"
 #include "networkd-manager.h"
 #include "networkd-wiphy.h"
 #include "parse-util.h"
+#include "path-util.h"
+#include "udev-util.h"
 #include "wifi-util.h"
 
 Wiphy *wiphy_free(Wiphy *w) {
@@ -18,29 +22,49 @@ Wiphy *wiphy_free(Wiphy *w) {
                         hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
         }
 
+        sd_device_unref(w->dev);
+        sd_device_unref(w->rfkill);
+
         free(w->name);
         return mfree(w);
 }
 
-static int wiphy_new(Manager *manager, uint32_t index, Wiphy **ret) {
+static int wiphy_new(Manager *manager, sd_netlink_message *message, Wiphy **ret) {
         _cleanup_(wiphy_freep) Wiphy *w = NULL;
+        _cleanup_free_ char *name = NULL;
+        uint32_t index;
         int r;
 
         assert(manager);
+        assert(message);
+
+        r = sd_netlink_message_read_u32(message, NL80211_ATTR_WIPHY, &index);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_read_string_strdup(message, NL80211_ATTR_WIPHY_NAME, &name);
+        if (r < 0)
+                return r;
 
         w = new(Wiphy, 1);
         if (!w)
                 return -ENOMEM;
 
         *w = (Wiphy) {
+                .manager = manager,
                 .index = index,
+                .name = TAKE_PTR(name),
         };
 
         r = hashmap_ensure_put(&manager->wiphy_by_index, NULL, UINT32_TO_PTR(w->index), w);
         if (r < 0)
                 return r;
 
-        w->manager = manager;
+        r = hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
+        if (r < 0)
+                return r;
+
+        log_wiphy_debug(w, "Saved new wiphy: index=%"PRIu32, w->index);
 
         if (ret)
                 *ret = w;
@@ -80,6 +104,125 @@ int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret) {
         return 0;
 }
 
+static int link_get_wiphy(Link *link, Wiphy **ret) {
+        _cleanup_(sd_device_unrefp) sd_device *phy = NULL;
+        const char *s;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        if (link->iftype != ARPHRD_ETHER)
+                return -EOPNOTSUPP;
+
+        if (!link->dev)
+                return -ENODEV;
+
+        r = sd_device_get_devtype(link->dev, &s);
+        if (r < 0)
+                return r;
+
+        if (!streq_ptr(s, "wlan"))
+                return -EOPNOTSUPP;
+
+        r = sd_device_new_child(&phy, link->dev, "phy80211");
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_sysname(phy, &s);
+        if (r < 0)
+                return r;
+
+        /* TODO:
+         * Maybe, it is better to cache the found Wiphy object in the Link object.
+         * To support that, we need to investigate what happens when the _phy_ is renamed. */
+
+        return wiphy_get_by_name(link->manager, s, ret);
+}
+
+static int rfkill_get_state(sd_device *dev) {
+        int r;
+
+        assert(dev);
+
+        /* The previous values may be outdated. Let's clear cache and re-read the values. */
+        device_clear_sysattr_cache(dev);
+
+        r = device_get_sysattr_bool(dev, "soft");
+        if (r < 0 && r != -ENOENT)
+                return r;
+        if (r > 0)
+                return RFKILL_SOFT;
+
+        r = device_get_sysattr_bool(dev, "hard");
+        if (r < 0 && r != -ENOENT)
+                return r;
+        if (r > 0)
+                return RFKILL_HARD;
+
+        return RFKILL_UNBLOCKED;
+}
+
+static int wiphy_rfkilled(Wiphy *w) {
+        int r;
+
+        assert(w);
+
+        if (!udev_available()) {
+                if (w->rfkill_state != RFKILL_UNBLOCKED) {
+                        log_wiphy_debug(w, "Running in container, assuming the radio transmitter is unblocked.");
+                        w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
+                }
+                return false;
+        }
+
+        if (!w->rfkill) {
+                if (w->rfkill_state != RFKILL_UNBLOCKED) {
+                        log_wiphy_debug(w, "No rfkill device found, assuming the radio transmitter is unblocked.");
+                        w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
+                }
+                return false;
+        }
+
+        r = rfkill_get_state(w->rfkill);
+        if (r < 0)
+                return log_wiphy_debug_errno(w, r, "Could not get rfkill state: %m");
+
+        if (w->rfkill_state != r)
+                switch (r) {
+                case RFKILL_UNBLOCKED:
+                        log_wiphy_debug(w, "The radio transmitter is unblocked.");
+                        break;
+                case RFKILL_SOFT:
+                        log_wiphy_debug(w, "The radio transmitter is turned off by software.");
+                        break;
+                case RFKILL_HARD:
+                        log_wiphy_debug(w, "The radio transmitter is forced off by something outside of the driver's control.");
+                        break;
+                default:
+                        assert_not_reached();
+                }
+
+        w->rfkill_state = r; /* Cache the state to suppress the above log messages. */
+        return r != RFKILL_UNBLOCKED;
+}
+
+int link_rfkilled(Link *link) {
+        Wiphy *w;
+        int r;
+
+        assert(link);
+
+        r = link_get_wiphy(link, &w);
+        if (r < 0) {
+                if (ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_DEVICE_ABSENT(r))
+                        return false; /* Typically, non-wifi interface or running in container */
+                return log_link_debug_errno(link, r, "Could not get phy: %m");
+        }
+
+        return wiphy_rfkilled(w);
+}
+
 static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) {
         const char *name;
         int r;
@@ -94,28 +237,122 @@ static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) {
         if (r < 0)
                 return r;
 
-        if (streq_ptr(w->name, name))
+        if (streq(w->name, name))
                 return 0;
 
-        if (w->name)
-                hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
+        log_wiphy_debug(w, "Wiphy name change detected, renamed to %s.", name);
+
+        hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
 
         r = free_and_strdup(&w->name, name);
         if (r < 0)
                 return r;
 
-        return hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
+        r = hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
+        if (r < 0)
+                return r;
+
+        return 1; /* updated */
 }
 
-static int wiphy_update(Wiphy *w, sd_netlink_message *message) {
+static int wiphy_update_device(Wiphy *w) {
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
         int r;
 
         assert(w);
-        assert(message);
+        assert(w->name);
+
+        if (!udev_available())
+                return 0;
+
+        w->dev = sd_device_unref(w->dev);
+
+        r = sd_device_new_from_subsystem_sysname(&dev, "ieee80211", w->name);
+        if (r < 0)
+                return r;
+
+        if (DEBUG_LOGGING) {
+                const char *s = NULL;
+
+                (void) sd_device_get_syspath(dev, &s);
+                log_wiphy_debug(w, "Found device: %s", strna(s));
+        }
+
+        w->dev = TAKE_PTR(dev);
+        return 0;
+}
+
+static int wiphy_update_rfkill(Wiphy *w) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *rfkill;
+        int r;
+
+        assert(w);
+
+        if (!udev_available())
+                return 0;
+
+        w->rfkill = sd_device_unref(w->rfkill);
+
+        if (!w->dev)
+                return 0;
+
+        r = sd_device_enumerator_new(&e);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_allow_uninitialized(e);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_subsystem(e, "rfkill", true);
+        if (r < 0)
+                return r;
 
-        r = wiphy_update_name(w, message);
+        r = sd_device_enumerator_add_match_parent(e, w->dev);
         if (r < 0)
-                return log_wiphy_debug_errno(w, r, "Failed to update wiphy name: %m");
+                return r;
+
+        rfkill = sd_device_enumerator_get_device_first(e);
+        if (!rfkill)
+                /* rfkill device may not detected by the kernel yet, and may appear later. */
+                return -ENODEV;
+
+        if (sd_device_enumerator_get_device_next(e))
+                return -ENXIO; /* multiple devices found */
+
+        w->rfkill = sd_device_ref(rfkill);
+
+        if (DEBUG_LOGGING) {
+                const char *s = NULL;
+
+                (void) sd_device_get_syspath(rfkill, &s);
+                log_wiphy_debug(w, "Found rfkill device: %s", strna(s));
+        }
+
+        return 0;
+}
+
+static int wiphy_update(Wiphy *w) {
+        int r;
+
+        assert(w);
+
+        r = wiphy_update_device(w);
+        if (r < 0) {
+                if (ERRNO_IS_DEVICE_ABSENT(r))
+                        log_wiphy_debug_errno(w, r, "Failed to update wiphy device, ignoring: %m");
+                else
+                        return log_wiphy_warning_errno(w, r, "Failed to update wiphy device: %m");
+        }
+
+        r = wiphy_update_rfkill(w);
+        if (r < 0) {
+                if (ERRNO_IS_DEVICE_ABSENT(r))
+                        log_wiphy_debug_errno(w, r, "Failed to update rfkill device, ignoring: %m");
+                else
+                        return log_wiphy_warning_errno(w, r, "Failed to update rfkill device: %m");
+        }
 
         return 0;
 }
@@ -166,23 +403,27 @@ int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *mes
 
         switch (cmd) {
         case NL80211_CMD_NEW_WIPHY: {
-                bool is_new = !w;
 
                 if (!w) {
-                        r = wiphy_new(manager, index, &w);
+                        r = wiphy_new(manager, message, &w);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to save new wiphy, ignoring: %m");
+                                return 0;
+                        }
+                } else {
+                        r = wiphy_update_name(w, message);
                         if (r < 0) {
-                                log_warning_errno(r, "Failed to allocate wiphy, ignoring: %m");
+                                log_wiphy_warning_errno(w, r, "Failed to update wiphy name, ignoring: %m");
                                 return 0;
                         }
+                        if (r == 0)
+                                return 0;
                 }
 
-                r = wiphy_update(w, message);
-                if (r < 0) {
+                r = wiphy_update(w);
+                if (r < 0)
                         log_wiphy_warning_errno(w, r, "Failed to update wiphy, ignoring: %m");
-                        return 0;
-                }
 
-                log_wiphy_debug(w, "Received %s phy.", is_new ? "new" : "updated");
                 break;
         }
         case NL80211_CMD_DEL_WIPHY:
@@ -203,3 +444,57 @@ int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *mes
 
         return 0;
 }
+
+int manager_udev_process_wiphy(Manager *m, sd_device *device, sd_device_action_t action) {
+        const char *name;
+        Wiphy *w;
+        int r;
+
+        assert(m);
+        assert(device);
+
+        r = sd_device_get_sysname(device, &name);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to get sysname: %m");
+
+        r = wiphy_get_by_name(m, name, &w);
+        if (r < 0) {
+                /* This error is not critical, as the corresponding genl message may be received later. */
+                log_device_debug_errno(device, r, "Failed to get Wiphy object, ignoring: %m");
+                return 0;
+        }
+
+        return device_unref_and_replace(w->dev, action == SD_DEVICE_REMOVE ? NULL : device);
+}
+
+int manager_udev_process_rfkill(Manager *m, sd_device *device, sd_device_action_t action) {
+        _cleanup_free_ char *parent_path = NULL, *parent_name = NULL;
+        const char *s;
+        Wiphy *w;
+        int r;
+
+        assert(m);
+        assert(device);
+
+        r = sd_device_get_syspath(device, &s);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to get syspath: %m");
+
+        /* Do not use sd_device_get_parent() here, as this might be a 'remove' uevent. */
+        r = path_extract_directory(s, &parent_path);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to get parent syspath: %m");
+
+        r = path_extract_filename(parent_path, &parent_name);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to get parent name: %m");
+
+        r = wiphy_get_by_name(m, parent_name, &w);
+        if (r < 0) {
+                /* This error is not critical, as the corresponding genl message may be received later. */
+                log_device_debug_errno(device, r, "Failed to get Wiphy object: %m");
+                return 0;
+        }
+
+        return device_unref_and_replace(w->rfkill, action == SD_DEVICE_REMOVE ? NULL : device);
+}
index b40354704be3c52dd3f7f8690dad3c2c017c1f8c..b9056e802c7c29bd889d71c3c74edfacb1ed32f8 100644 (file)
@@ -7,13 +7,28 @@
 
 #include "macro.h"
 
+typedef struct Link Link;
 typedef struct Manager Manager;
 
+/* The following values are different from the ones defined in linux/rfkill.h. */
+typedef enum RFKillState {
+        RFKILL_UNKNOWN,
+        RFKILL_UNBLOCKED,
+        RFKILL_SOFT,
+        RFKILL_HARD,
+        _RFKILL_STATE_MAX,
+        _RFKILL_STATE_INVALID = -EINVAL,
+} RFKillState;
+
 typedef struct Wiphy {
         Manager *manager;
 
         uint32_t index;
         char *name;
+
+        sd_device *dev;
+        sd_device *rfkill;
+        RFKillState rfkill_state;
 } Wiphy;
 
 Wiphy *wiphy_free(Wiphy *w);
@@ -22,7 +37,11 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Wiphy*, wiphy_free);
 int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret);
 int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret);
 
+int link_rfkilled(Link *link);
+
 int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager);
+int manager_udev_process_wiphy(Manager *m, sd_device *device, sd_device_action_t action);
+int manager_udev_process_rfkill(Manager *m, sd_device *device, sd_device_action_t action);
 
 #define log_wiphy_full_errno_zerook(w, level, error, ...)               \
         ({                                                              \
index 6aef268d1f98dd7390ed92311b88a2c124fa5880..8f8a7408a587e0a7eb8ff7b26f1babc1f746f97c 100644 (file)
@@ -353,10 +353,17 @@ static bool qdisc_is_ready_to_configure(QDisc *qdisc, Link *link) {
         if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
                 return false;
 
-        if (IN_SET(qdisc->parent, TC_H_ROOT, TC_H_CLSACT)) /* TC_H_CLSACT == TC_H_INGRESS */
-                return true;
+        /* TC_H_CLSACT == TC_H_INGRESS */
+        if (!IN_SET(qdisc->parent, TC_H_ROOT, TC_H_CLSACT) &&
+            link_find_tclass(link, qdisc->parent, NULL) < 0)
+                return false;
+
+        if (QDISC_VTABLE(qdisc) &&
+            QDISC_VTABLE(qdisc)->is_ready &&
+            QDISC_VTABLE(qdisc)->is_ready(qdisc, link) <= 0)
+                return false;
 
-        return link_find_tclass(link, qdisc->parent, NULL) >= 0;
+        return true;
 }
 
 static int qdisc_process_request(Request *req, Link *link, QDisc *qdisc) {
index adaaf260c471a3161a60a91d22b47d35b6523421..155e2adf24871380d532af02a3f057c7fb913f22 100644 (file)
@@ -56,6 +56,7 @@ typedef struct QDiscVTable {
         int (*init)(QDisc *qdisc);
         int (*fill_message)(Link *link, QDisc *qdisc, sd_netlink_message *m);
         int (*verify)(QDisc *qdisc);
+        int (*is_ready)(QDisc *qdisc, Link *link);
 } QDiscVTable;
 
 extern const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX];
index 1422860f55dcc5825de92c17b7ad0c96bf796aa0..9fd9e090069e752d3c64232f6f749fdd679a179a 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "macro.h"
+#include "networkd-link.h"
 #include "parse-util.h"
 #include "string-util.h"
 #include "teql.h"
@@ -17,9 +18,24 @@ static int trivial_link_equalizer_verify(QDisc *qdisc) {
         return free_and_replace(qdisc->tca_kind, tca_kind);
 }
 
+static int trivial_link_equalizer_is_ready(QDisc *qdisc, Link *link) {
+        Link *teql;
+
+        assert(qdisc);
+        assert(qdisc->tca_kind);
+        assert(link);
+        assert(link->manager);
+
+        if (link_get_by_name(link->manager, qdisc->tca_kind, &teql) < 0)
+                return false;
+
+        return link_is_ready_to_configure(teql, /* allow_unmanaged = */ true);
+}
+
 const QDiscVTable teql_vtable = {
         .object_size = sizeof(TrivialLinkEqualizer),
         .verify = trivial_link_equalizer_verify,
+        .is_ready = trivial_link_equalizer_is_ready,
 };
 
 int config_parse_trivial_link_equalizer_id(
index b41e3663098544f6525b8902ddd4689c7e48b47e..f4c196ee10db3472a99bfd314ef1b5c0ad2e368e 100644 (file)
@@ -38,6 +38,11 @@ static const sd_bus_vtable manager_vtable[] = {
                                  SD_BUS_PARAM(fd),
                                  bus_method_dump_by_fd,
                                  SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_SIGNAL_WITH_NAMES("Killed",
+                                 "ss",
+                                 SD_BUS_PARAM(cgroup)
+                                 SD_BUS_PARAM(reason),
+                                 0),
         SD_BUS_VTABLE_END
 };
 
index a28e97a178a4b7e45337dc036a87ba3ade98a5e1..d9574fa97b2fbfa68f29f9446903b228549433f9 100644 (file)
@@ -83,7 +83,7 @@ static int process_managed_oom_message(Manager *m, uid_t uid, JsonVariant *param
 
                         r = cg_path_get_owner_uid(message.path, &cg_uid);
                         if (r < 0) {
-                                log_debug("Failed to get cgroup %s owner uid: %m", message.path);
+                                log_debug_errno(r, "Failed to get cgroup %s owner uid: %m", message.path);
                                 continue;
                         }
 
@@ -410,7 +410,7 @@ static int monitor_swap_contexts_handler(sd_event_source *s, uint64_t usec, void
                 if (r < 0)
                         log_notice_errno(r, "Failed to kill any cgroup(s) based on swap: %m");
                 else {
-                        if (selected && r > 0)
+                        if (selected && r > 0) {
                                 log_notice("Killed %s due to memory used (%"PRIu64") / total (%"PRIu64") and "
                                            "swap used (%"PRIu64") / total (%"PRIu64") being more than "
                                            PERMYRIAD_AS_PERCENT_FORMAT_STR,
@@ -418,6 +418,16 @@ static int monitor_swap_contexts_handler(sd_event_source *s, uint64_t usec, void
                                            m->system_context.mem_used, m->system_context.mem_total,
                                            m->system_context.swap_used, m->system_context.swap_total,
                                            PERMYRIAD_AS_PERCENT_FORMAT_VAL(m->swap_used_limit_permyriad));
+
+                                /* send dbus signal */
+                                (void) sd_bus_emit_signal(m->bus,
+                                                          "/org/freedesktop/oom1",
+                                                          "org.freedesktop.oom1.Manager",
+                                                          "Killed",
+                                                          "ss",
+                                                          selected,
+                                                          "memory-used");
+                        }
                         return 0;
                 }
         }
@@ -524,13 +534,23 @@ static int monitor_memory_pressure_contexts_handler(sd_event_source *s, uint64_t
                                  * it. In either case, go through the event loop again and select a new candidate if
                                  * pressure is still high. */
                                 m->mem_pressure_post_action_delay_start = usec_now;
-                                if (selected && r > 0)
+                                if (selected && r > 0) {
                                         log_notice("Killed %s due to memory pressure for %s being %lu.%02lu%% > %lu.%02lu%%"
                                                    " for > %s with reclaim activity",
                                                    selected, t->path,
                                                    LOADAVG_INT_SIDE(t->memory_pressure.avg10), LOADAVG_DECIMAL_SIDE(t->memory_pressure.avg10),
                                                    LOADAVG_INT_SIDE(t->mem_pressure_limit), LOADAVG_DECIMAL_SIDE(t->mem_pressure_limit),
                                                    FORMAT_TIMESPAN(m->default_mem_pressure_duration_usec, USEC_PER_SEC));
+
+                                        /* send dbus signal */
+                                        (void) sd_bus_emit_signal(m->bus,
+                                                                  "/org/freedesktop/oom1",
+                                                                  "org.freedesktop.oom1.Manager",
+                                                                  "Killed",
+                                                                  "ss",
+                                                                  selected,
+                                                                  "memory-pressure");
+                                }
                                 return 0;
                         }
                 }
index 8044b2eef7eb17ce346c1bda192b0bbd1f8eadd7..0c0c0de794ca7d9c96dc0f936422f0f5e4e5d6c5 100644 (file)
@@ -95,7 +95,7 @@ static bool arg_dry_run = true;
 static const char *arg_node = NULL;
 static char *arg_root = NULL;
 static char *arg_image = NULL;
-static char *arg_definitions = NULL;
+static char **arg_definitions = NULL;
 static bool arg_discard = true;
 static bool arg_can_factory_reset = false;
 static int arg_factory_reset = -1;
@@ -114,7 +114,7 @@ static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
-STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
 
@@ -133,6 +133,7 @@ typedef enum EncryptMode {
 
 struct Partition {
         char *definition_path;
+        char **drop_in_files;
 
         sd_id128_t type_uuid;
         sd_id128_t current_uuid, new_uuid;
@@ -266,6 +267,7 @@ static Partition* partition_free(Partition *p) {
         free(p->current_label);
         free(p->new_label);
         free(p->definition_path);
+        strv_free(p->drop_in_files);
 
         if (p->current_partition)
                 fdisk_unref_partition(p->current_partition);
@@ -1310,7 +1312,7 @@ static int config_parse_gpt_flags(
         return 0;
 }
 
-static int partition_read_definition(Partition *p, const char *path) {
+static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
 
         ConfigTableItem table[] = {
                 { "Partition", "Type",            config_parse_type,        0, &p->type_uuid        },
@@ -1336,13 +1338,25 @@ static int partition_read_definition(Partition *p, const char *path) {
                 {}
         };
         int r;
+        _cleanup_free_ char *filename = NULL;
+        const char* dropin_dirname;
 
-        r = config_parse(NULL, path, NULL,
-                         "Partition\0",
-                         config_item_table_lookup, table,
-                         CONFIG_PARSE_WARN,
-                         p,
-                         NULL);
+        r = path_extract_filename(path, &filename);
+        if (r < 0)
+                return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);;
+
+        dropin_dirname = strjoina(filename, ".d");
+
+        r = config_parse_many(
+                        STRV_MAKE_CONST(path),
+                        conf_file_dirs,
+                        dropin_dirname,
+                        "Partition\0",
+                        config_item_table_lookup, table,
+                        CONFIG_PARSE_WARN,
+                        p,
+                        NULL,
+                        &p->drop_in_files);
         if (r < 0)
                 return r;
 
@@ -1390,19 +1404,19 @@ static int partition_read_definition(Partition *p, const char *path) {
 
 static int context_read_definitions(
                 Context *context,
-                const char *directory,
+                char **directories,
                 const char *root) {
 
         _cleanup_strv_free_ char **files = NULL;
         Partition *last = NULL;
         int r;
+        const char *const *dirs;
 
         assert(context);
 
-        if (directory)
-                r = conf_files_list_strv(&files, ".conf", NULL, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) STRV_MAKE(directory));
-        else
-                r = conf_files_list_strv(&files, ".conf", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) CONF_PATHS_STRV("repart.d"));
+        dirs = (const char* const*) (directories ?: CONF_PATHS_STRV("repart.d"));
+
+        r = conf_files_list_strv(&files, ".conf", directories ? NULL : root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, dirs);
         if (r < 0)
                 return log_error_errno(r, "Failed to enumerate *.conf files: %m");
 
@@ -1417,7 +1431,7 @@ static int context_read_definitions(
                 if (!p->definition_path)
                         return log_oom();
 
-                r = partition_read_definition(p, *f);
+                r = partition_read_definition(p, *f, dirs);
                 if (r < 0)
                         return r;
 
@@ -1612,14 +1626,14 @@ static int context_load_partition_table(
 
         if (*backing_fd < 0) {
                 /* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */
-                *backing_fd = fcntl(fdisk_get_devfd(c), F_DUPFD_CLOEXEC, 3);
+                *backing_fd = fd_reopen(fdisk_get_devfd(c), O_RDONLY|O_CLOEXEC);
                 if (*backing_fd < 0)
-                        return log_error_errno(errno, "Failed to duplicate fdisk fd: %m");
-        }
+                        return log_error_errno(*backing_fd, "Failed to duplicate fdisk fd: %m");
 
-        /* Tell udev not to interfere while we are processing the device */
-        if (flock(fdisk_get_devfd(c), arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
-                return log_error_errno(errno, "Failed to lock block device: %m");
+                /* Tell udev not to interfere while we are processing the device */
+                if (flock(*backing_fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
+                        return log_error_errno(errno, "Failed to lock block device: %m");
+        }
 
         /* The offsets/sizes libfdisk returns to us will be in multiple of the sector size of the
          * device. This is typically 512, and sometimes 4096. Let's query libfdisk once for it, and then use
@@ -1629,8 +1643,8 @@ static int context_load_partition_table(
         secsz = fdisk_get_sector_size(c);
 
         /* Insist on a power of two, and that it's a multiple of 512, i.e. the traditional sector size. */
-        if (secsz < 512 || secsz != 1UL << log2u64(secsz))
-                return log_error_errno(errno, "Sector size %lu is not a power of two larger than 512? Refusing.", secsz);
+        if (secsz < 512 || !ISPOWEROF2(secsz))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size %lu is not a power of two larger than 512? Refusing.", secsz);
 
         /* Use at least 4K, and ensure it's a multiple of the sector size, regardless if that is smaller or
          * larger */
@@ -1988,23 +2002,26 @@ static int context_dump_partitions(Context *context, const char *node) {
         _cleanup_(table_unrefp) Table *t = NULL;
         uint64_t sum_padding = 0, sum_size = 0;
         int r;
+        const size_t dropin_files_col = 13;
+        bool no_dropin_files = true;
 
         if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) {
                 log_info("Empty partition table.");
                 return 0;
         }
 
-        t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity");
+        t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity", "drop-in files");
         if (!t)
                 return log_oom();
 
         if (!DEBUG_LOGGING) {
                 if (arg_json_format_flags & JSON_FORMAT_OFF)
                         (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
-                                                    (size_t) 8, (size_t) 11);
+                                                    (size_t) 8, (size_t) 11, dropin_files_col);
                 else
                         (void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
-                                                    (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10, (size_t) 12);
+                                                    (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10, (size_t) 12,
+                                                    dropin_files_col);
         }
 
         (void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
@@ -2058,9 +2075,12 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_UINT64, p->current_padding == UINT64_MAX ? 0 : p->current_padding,
                                 TABLE_UINT64, p->new_padding,
                                 TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL,
-                                TABLE_STRING, activity ?: "unchanged");
+                                TABLE_STRING, activity ?: "unchanged",
+                                TABLE_STRV, p->drop_in_files);
                 if (r < 0)
                         return table_log_add_error(r);
+
+                no_dropin_files = no_dropin_files && strv_isempty(p->drop_in_files);
         }
 
         if ((arg_json_format_flags & JSON_FORMAT_OFF) && (sum_padding > 0 || sum_size > 0)) {
@@ -2083,11 +2103,18 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_EMPTY,
                                 TABLE_EMPTY,
                                 TABLE_STRING, b,
+                                TABLE_EMPTY,
                                 TABLE_EMPTY);
                 if (r < 0)
                         return table_log_add_error(r);
         }
 
+        if (no_dropin_files) {
+                r = table_hide_column_from_display(t, dropin_files_col);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set columns to display: %m");
+        }
+
         return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
 }
 
@@ -3460,11 +3487,12 @@ static int context_write_partition_table(
 
                 (void) context_dump_partitions(context, node);
 
-                putc('\n', stdout);
-
-                if (arg_json_format_flags & JSON_FORMAT_OFF)
+                if (arg_json_format_flags & JSON_FORMAT_OFF) {
+                        putc('\n', stdout);
                         (void) context_dump_partition_bar(context, node);
-                putc('\n', stdout);
+                        putc('\n', stdout);
+                }
+
                 fflush(stdout);
         }
 
@@ -4251,11 +4279,15 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_pretty = r;
                         break;
 
-                case ARG_DEFINITIONS:
-                        r = parse_path_argument(optarg, false, &arg_definitions);
+                case ARG_DEFINITIONS: {
+                        _cleanup_free_ char *path = NULL;
+                        r = parse_path_argument(optarg, false, &path);
                         if (r < 0)
                                 return r;
+                        if (strv_consume(&arg_definitions, TAKE_PTR(path)) < 0)
+                                return log_oom();
                         break;
+                }
 
                 case ARG_SIZE: {
                         uint64_t parsed, rounded;
@@ -4874,6 +4906,8 @@ static int run(int argc, char *argv[]) {
         if (!context)
                 return log_oom();
 
+        strv_uniq(arg_definitions);
+
         r = context_read_definitions(context, arg_definitions, arg_root);
         if (r < 0)
                 return r;
index 2b9bebbe8ccc5fbe1986986399c027860ae4f06a..1aac7ac10810e09010717254452e73a35ab06f9d 100755 (executable)
@@ -222,3 +222,110 @@ echo "### Testing json output ###"
 "$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager --json=help
 "$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager --json=pretty
 "$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager --json=short
+
+echo "### Testing drop-in overrides ###"
+
+mkdir -p "$D/definitions-overrides"
+
+cat >"$D/definitions-overrides/root.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
+EOF
+
+mkdir -p "$D/definitions-overrides/root.conf.d"
+
+cat >"$D/definitions-overrides/root.conf.d/override1.conf" <<EOF
+[Partition]
+Label=label1
+SizeMaxBytes=32M
+EOF
+
+cat >"$D/definitions-overrides/root.conf.d/override2.conf" <<EOF
+[Partition]
+Label=label2
+EOF
+
+rm -f test-drop-in-image
+
+JSON_OUTPUT=$("$repart" --definitions="$D/definitions-overrides" --dry-run=yes --empty=create --size=100M --json=pretty test-drop-in-image)
+
+diff <(echo "$JSON_OUTPUT") - <<EOF
+[
+       {
+               "type" : "swap",
+               "label" : "label2",
+               "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+               "file" : "root.conf",
+               "node" : "test-drop-in-image1",
+               "offset" : 1048576,
+               "old_size" : 0,
+               "raw_size" : 33554432,
+               "old_padding" : 0,
+               "raw_padding" : 0,
+               "activity" : "create",
+               "drop-in_files" : [
+                       "$D/definitions-overrides/root.conf.d/override1.conf",
+                       "$D/definitions-overrides/root.conf.d/override2.conf"
+               ]
+       }
+]
+EOF
+
+echo "### Testing list of definitions directories ###"
+
+mkdir -p "$D/definitions1"
+
+cat >"$D/definitions1/root1.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=32M
+UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
+Label=label1
+EOF
+
+mkdir -p "$D/definitions2"
+
+cat >"$D/definitions2/root2.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=32M
+UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
+Label=label2
+EOF
+
+rm -f test-definitions
+
+JSON_OUTPUT=$("$repart" --definitions="$D/definitions1" --definitions="$D/definitions2" --dry-run=yes --empty=create --size=100M --json=pretty test-definitions)
+
+diff <(echo "$JSON_OUTPUT") - <<EOF
+[
+       {
+               "type" : "swap",
+               "label" : "label1",
+               "uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0",
+               "file" : "root1.conf",
+               "node" : "test-definitions1",
+               "offset" : 1048576,
+               "old_size" : 0,
+               "raw_size" : 33554432,
+               "old_padding" : 0,
+               "raw_padding" : 0,
+               "activity" : "create"
+       },
+       {
+               "type" : "swap",
+               "label" : "label2",
+               "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+               "file" : "root2.conf",
+               "node" : "test-definitions2",
+               "offset" : 34603008,
+               "old_size" : 0,
+               "raw_size" : 33554432,
+               "old_padding" : 0,
+               "raw_padding" : 0,
+               "activity" : "create"
+       }
+]
+EOF
index 4005a5c796d5060cc0af8fc0768ab32e15a28542..c3c22220c22770c3ab4db84506edcca6f17396bc 100644 (file)
@@ -597,11 +597,9 @@ static int maybe_start_stop_restart(sd_bus *bus, const char *path, const char *m
         if (!arg_now)
                 return 0;
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
+                        bus_systemd_mgr,
                         method,
                         &error,
                         &reply,
index 9a6af70b93989a73dbcd903a5ecd578402e8caae..04deeb2262e1696141b430bd19d01343be3bd49b 100644 (file)
@@ -1,7 +1,8 @@
-# The "trusted" profile for services, i.e. no restrictions are applied
+# The "trusted" profile for services, i.e. no restrictions are applied apart from a private /tmp
 
 [Service]
 MountAPIVFS=yes
+PrivateTmp=yes
 BindPaths=/run
 BindReadOnlyPaths=/etc/machine-id
 BindReadOnlyPaths=/etc/resolv.conf
index aaa1c497c02c8d6d4d71799778f68717ab4123b1..6d08821ecd63a9eff9cccc673995a0b9ccd05608 100644 (file)
@@ -138,7 +138,7 @@ int ifname_resolvconf_mangle(const char *s) {
 
         assert(s);
 
-        dot = strchr(s, '.');
+        dot = strrchr(s, '.');
         if (dot) {
                 _cleanup_free_ char *iface = NULL;
 
index 0f084b56fc60fedfb36b1dc3e6e7624868149391..594984685bcf997ac6175ddc1239b4ac7e0e36d9 100644 (file)
@@ -1242,16 +1242,14 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_
         return 1;
 }
 
-int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
+int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p, usec_t ts, unsigned max_rr) {
         unsigned ancount = 0;
         DnsCacheItem *i;
-        usec_t t;
         int r;
 
         assert(cache);
         assert(p);
-
-        t = now(CLOCK_BOOTTIME);
+        assert(p->protocol == DNS_PROTOCOL_MDNS);
 
         HASHMAP_FOREACH(i, cache->by_key)
                 LIST_FOREACH(by_key, j, i) {
@@ -1263,14 +1261,17 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
 
                         /* RFC6762 7.1: Don't append records with less than half the TTL remaining
                          * as known answers. */
-                        if (usec_sub_unsigned(j->until, t) < j->rr->ttl * USEC_PER_SEC / 2)
+                        if (usec_sub_unsigned(j->until, ts) < j->rr->ttl * USEC_PER_SEC / 2)
                                 continue;
 
                         r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
-                        if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
-                                /* For mDNS, if we're unable to stuff all known answers into the given packet,
-                                 * allocate a new one, push the RR into that one and link it to the current one.
-                                 */
+                        if (r == -EMSGSIZE) {
+                                if (max_rr == 0)
+                                        /* If max_rr == 0, do not allocate more packets. */
+                                        goto finalize;
+
+                                /* If we're unable to stuff all known answers into the given packet, allocate
+                                 * a new one, push the RR into that one and link it to the current one. */
 
                                 DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
                                 ancount = 0;
@@ -1288,8 +1289,21 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
                                 return r;
 
                         ancount++;
+                        if (max_rr > 0 && ancount >= max_rr) {
+                                DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
+                                ancount = 0;
+
+                                r = dns_packet_new_query(&p->more, p->protocol, 0, true);
+                                if (r < 0)
+                                        return r;
+
+                                p = p->more;
+
+                                max_rr = UINT_MAX;
+                        }
                 }
 
+finalize:
         DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
 
         return 0;
index 621b52f8926f8267bc55fde75fe93366bef18e1d..fb2e61a65bcab73140dd6f54d585b3ec20410454 100644 (file)
@@ -53,4 +53,4 @@ bool dns_cache_is_empty(DnsCache *cache);
 
 unsigned dns_cache_size(DnsCache *cache);
 
-int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p);
+int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p, usec_t ts, unsigned max_rr);
index a872e9d255017325d24d3e49c9e0ea9113672769..8fd987bd824d05b7b872d894974cd035673ace74 100644 (file)
@@ -692,6 +692,11 @@ DnsScopeMatch dns_scope_good_domain(
                 if (has_search_domains && dns_name_is_single_label(domain))
                         return DNS_SCOPE_YES_BASE + 1;
 
+                /* If ResolveUnicastSingleLabel=yes and the query is single-label, then bump match result
+                   to prevent LLMNR monopoly among candidates. */
+                if (s->manager->resolve_unicast_single_label && dns_name_is_single_label(domain))
+                        return DNS_SCOPE_YES_BASE + 1;
+
                 /* Let's return the number of labels in the best matching result */
                 if (n_best >= 0) {
                         assert(n_best <= DNS_SCOPE_YES_END - DNS_SCOPE_YES_BASE);
@@ -1011,7 +1016,9 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
                 return;
         }
 
-        assert(dns_question_size(p->question) == 1);
+        if (dns_question_size(p->question) != 1)
+                return (void) log_debug("Received LLMNR query without question or multiple questions, ignoring.");
+
         key = dns_question_first_key(p->question);
 
         r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
@@ -1204,7 +1211,6 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata
 }
 
 int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) {
-        usec_t jitter;
         int r;
 
         assert(scope);
@@ -1233,15 +1239,12 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) {
         if (scope->conflict_event_source)
                 return 0;
 
-        random_bytes(&jitter, sizeof(jitter));
-        jitter %= LLMNR_JITTER_INTERVAL_USEC;
-
         r = sd_event_add_time_relative(
                         scope->manager->event,
                         &scope->conflict_event_source,
                         CLOCK_BOOTTIME,
-                        jitter,
-                        LLMNR_JITTER_INTERVAL_USEC,
+                        random_u64_range(LLMNR_JITTER_INTERVAL_USEC),
+                        0,
                         on_conflict_dispatch, scope);
         if (r < 0)
                 return log_debug_errno(r, "Failed to add conflict dispatch event: %m");
@@ -1511,7 +1514,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
                                 &scope->announce_event_source,
                                 CLOCK_BOOTTIME,
                                 MDNS_ANNOUNCE_DELAY,
-                                MDNS_JITTER_RANGE_USEC,
+                                0,
                                 on_announcement_timeout, scope);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to schedule second announcement: %m");
index 27685a90a982a4d1aa92023bba711036e437fd3b..328913e1daceff0a8e4c58c914ad0474bb6c1573 100644 (file)
@@ -1551,6 +1551,33 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
         return 0;
 }
 
+static int dns_transaction_setup_timeout(
+                DnsTransaction *t,
+                usec_t timeout_usec /* relative */,
+                usec_t next_usec /* CLOCK_BOOTTIME */) {
+
+        int r;
+
+        assert(t);
+
+        dns_transaction_stop_timeout(t);
+
+        r = sd_event_add_time_relative(
+                t->scope->manager->event,
+                &t->timeout_event_source,
+                CLOCK_BOOTTIME,
+                timeout_usec, 0,
+                on_transaction_timeout, t);
+        if (r < 0)
+                return r;
+
+        (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
+
+        t->next_attempt_after = next_usec;
+        t->state = DNS_TRANSACTION_PENDING;
+        return 0;
+}
+
 static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
         assert(t);
         assert(t->scope);
@@ -1568,11 +1595,12 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
                 return DNS_TIMEOUT_USEC;
 
         case DNS_PROTOCOL_MDNS:
-                assert(t->n_attempts > 0);
                 if (t->probing)
                         return MDNS_PROBING_INTERVAL_USEC;
-                else
-                        return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
+
+                /* See RFC 6762 Section 5.1 suggests that timeout should be a few seconds. */
+                assert(t->n_attempts > 0);
+                return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
 
         case DNS_PROTOCOL_LLMNR:
                 return t->scope->resend_timeout;
@@ -1622,7 +1650,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
                 return 0;
         }
 
-        if (t->n_attempts >= dns_transaction_attempts_max(t->scope->protocol, t->probing)) {
+        if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
                 DnsTransactionState result;
 
                 if (t->scope->protocol == DNS_PROTOCOL_LLMNR)
@@ -1752,13 +1780,30 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
         return 1;
 }
 
+static int dns_packet_append_zone(DnsPacket *p, DnsTransaction *t, DnsResourceKey *k, unsigned *nscount) {
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+        bool tentative;
+        int r;
+
+        assert(p);
+        assert(t);
+        assert(k);
+
+        if (k->type != DNS_TYPE_ANY)
+                return 0;
+
+        r = dns_zone_lookup(&t->scope->zone, k, t->scope->link->ifindex, &answer, NULL, &tentative);
+        if (r < 0)
+                return r;
+
+        return dns_packet_append_answer(p, answer, nscount);
+}
+
 static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
-        bool add_known_answers = false;
-        DnsResourceKey *tkey;
         _cleanup_set_free_ Set *keys = NULL;
-        unsigned qdcount;
-        unsigned nscount = 0;
+        unsigned qdcount, ancount = 0 /* avoid false maybe-uninitialized warning */, nscount;
+        bool add_known_answers = false;
         usec_t ts;
         int r;
 
@@ -1768,6 +1813,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
         /* Discard any previously prepared packet, so we can start over and coalesce again */
         t->sent = dns_packet_unref(t->sent);
 
+        /* First, create a dummy packet to calculate packet size. */
         r = dns_packet_new_query(&p, t->scope->protocol, 0, false);
         if (r < 0)
                 return r;
@@ -1781,11 +1827,14 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
         if (dns_key_is_shared(dns_transaction_key(t)))
                 add_known_answers = true;
 
-        if (dns_transaction_key(t)->type == DNS_TYPE_ANY) {
-                r = set_ensure_put(&keys, &dns_resource_key_hash_ops, dns_transaction_key(t));
-                if (r < 0)
-                        return r;
-        }
+        r = dns_packet_append_zone(p, t, dns_transaction_key(t), NULL);
+        if (r < 0)
+                return r;
+
+        /* Save appended keys */
+        r = set_ensure_put(&keys, &dns_resource_key_hash_ops, dns_transaction_key(t));
+        if (r < 0)
+                return r;
 
         /*
          * For mDNS, we want to coalesce as many open queries in pending transactions into one single
@@ -1795,90 +1844,114 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
 
         assert_se(sd_event_now(t->scope->manager->event, CLOCK_BOOTTIME, &ts) >= 0);
 
-        LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) {
-
-                /* Skip ourselves */
-                if (other == t)
-                        continue;
+        for (bool restart = true; restart;) {
+                restart = false;
+                LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) {
+                        size_t saved_packet_size;
+                        bool append = false;
 
-                if (other->state != DNS_TRANSACTION_PENDING)
-                        continue;
-
-                if (other->next_attempt_after > ts)
-                        continue;
-
-                if (qdcount >= UINT16_MAX)
-                        break;
+                        /* Skip ourselves */
+                        if (other == t)
+                                continue;
 
-                r = dns_packet_append_key(p, dns_transaction_key(other), 0, NULL);
+                        if (other->state != DNS_TRANSACTION_PENDING)
+                                continue;
 
-                /*
-                 * If we can't stuff more questions into the packet, just give up.
-                 * One of the 'other' transactions will fire later and take care of the rest.
-                 */
-                if (r == -EMSGSIZE)
-                        break;
+                        if (other->next_attempt_after > ts)
+                                continue;
 
-                if (r < 0)
-                        return r;
+                        if (!set_contains(keys, dns_transaction_key(other))) {
+                                r = dns_packet_append_key(p, dns_transaction_key(other), 0, &saved_packet_size);
+                                /* If we can't stuff more questions into the packet, just give up.
+                                 * One of the 'other' transactions will fire later and take care of the rest. */
+                                if (r == -EMSGSIZE)
+                                        break;
+                                if (r < 0)
+                                        return r;
 
-                r = dns_transaction_prepare(other, ts);
-                if (r <= 0)
-                        continue;
+                                r = dns_packet_append_zone(p, t, dns_transaction_key(other), NULL);
+                                if (r == -EMSGSIZE)
+                                        break;
+                                if (r < 0)
+                                        return r;
 
-                ts += transaction_get_resend_timeout(other);
+                                append = true;
+                        }
 
-                r = sd_event_add_time(
-                                other->scope->manager->event,
-                                &other->timeout_event_source,
-                                CLOCK_BOOTTIME,
-                                ts, 0,
-                                on_transaction_timeout, other);
-                if (r < 0)
-                        return r;
+                        r = dns_transaction_prepare(other, ts);
+                        if (r < 0)
+                                return r;
+                        if (r == 0) {
+                                if (append)
+                                        dns_packet_truncate(p, saved_packet_size);
 
-                (void) sd_event_source_set_description(other->timeout_event_source, "dns-transaction-timeout");
+                                /* In this case, not only this transaction, but multiple transactions may be
+                                 * freed. Hence, we need to restart the loop. */
+                                restart = true;
+                                break;
+                        }
 
-                other->state = DNS_TRANSACTION_PENDING;
-                other->next_attempt_after = ts;
+                        usec_t timeout = transaction_get_resend_timeout(other);
+                        r = dns_transaction_setup_timeout(other, timeout, usec_add(ts, timeout));
+                        if (r < 0)
+                                return r;
 
-                qdcount++;
+                        if (dns_key_is_shared(dns_transaction_key(other)))
+                                add_known_answers = true;
 
-                if (dns_key_is_shared(dns_transaction_key(other)))
-                        add_known_answers = true;
+                        if (append) {
+                                r = set_ensure_put(&keys, &dns_resource_key_hash_ops, dns_transaction_key(other));
+                                if (r < 0)
+                                        return r;
+                        }
 
-                if (dns_transaction_key(other)->type == DNS_TYPE_ANY) {
-                        r = set_ensure_put(&keys, &dns_resource_key_hash_ops, dns_transaction_key(other));
-                        if (r < 0)
-                                return r;
+                        qdcount++;
+                        if (qdcount >= UINT16_MAX)
+                                break;
                 }
         }
 
-        DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount);
-
         /* Append known answer section if we're asking for any shared record */
         if (add_known_answers) {
-                r = dns_cache_export_shared_to_packet(&t->scope->cache, p);
+                r = dns_cache_export_shared_to_packet(&t->scope->cache, p, ts, 0);
                 if (r < 0)
                         return r;
+
+                ancount = be16toh(DNS_PACKET_HEADER(p)->ancount);
         }
 
-        SET_FOREACH(tkey, keys) {
-                _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-                bool tentative;
+        /* Then, create actual packet. */
+        p = dns_packet_unref(p);
+        r = dns_packet_new_query(&p, t->scope->protocol, 0, false);
+        if (r < 0)
+                return r;
 
-                r = dns_zone_lookup(&t->scope->zone, tkey, t->scope->link->ifindex, &answer, NULL, &tentative);
+        /* Questions */
+        DnsResourceKey *k;
+        SET_FOREACH(k, keys) {
+                r = dns_packet_append_key(p, k, 0, NULL);
                 if (r < 0)
                         return r;
+        }
+        DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount);
 
-                r = dns_packet_append_answer(p, answer, &nscount);
+        /* Known answers */
+        if (add_known_answers) {
+                r = dns_cache_export_shared_to_packet(&t->scope->cache, p, ts, ancount);
+                if (r < 0)
+                        return r;
+        }
+
+        /* Authorities */
+        nscount = 0;
+        SET_FOREACH(k, keys) {
+                r = dns_packet_append_zone(p, t, k, &nscount);
                 if (r < 0)
                         return r;
         }
         DNS_PACKET_HEADER(p)->nscount = htobe16(nscount);
 
         t->sent = TAKE_PTR(p);
-
         return 0;
 }
 
@@ -1950,45 +2023,36 @@ int dns_transaction_go(DnsTransaction *t) {
 
         if (!t->initial_jitter_scheduled &&
             IN_SET(t->scope->protocol, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS)) {
-                usec_t jitter, accuracy;
+                usec_t jitter;
 
-                /* RFC 4795 Section 2.7 suggests all queries should be delayed by a random time from 0 to
-                 * JITTER_INTERVAL. */
+                /* RFC 4795 Section 2.7 suggests all LLMNR queries should be delayed by a random time from 0 to
+                 * JITTER_INTERVAL.
+                 * RFC 6762 Section 8.1 suggests initial probe queries should be delayed by a random time from
+                 * 0 to 250ms. */
 
                 t->initial_jitter_scheduled = true;
+                t->n_attempts = 0;
 
                 switch (t->scope->protocol) {
 
                 case DNS_PROTOCOL_LLMNR:
                         jitter = random_u64_range(LLMNR_JITTER_INTERVAL_USEC);
-                        accuracy = LLMNR_JITTER_INTERVAL_USEC;
                         break;
 
                 case DNS_PROTOCOL_MDNS:
-                        jitter = usec_add(random_u64_range(MDNS_JITTER_RANGE_USEC), MDNS_JITTER_MIN_USEC);
-                        accuracy = MDNS_JITTER_RANGE_USEC;
+                        if (t->probing)
+                                jitter = random_u64_range(MDNS_PROBING_INTERVAL_USEC);
+                        else
+                                jitter = 0;
                         break;
                 default:
                         assert_not_reached();
                 }
 
-                assert(!t->timeout_event_source);
-
-                r = sd_event_add_time_relative(
-                                t->scope->manager->event,
-                                &t->timeout_event_source,
-                                CLOCK_BOOTTIME,
-                                jitter, accuracy,
-                                on_transaction_timeout, t);
+                r = dns_transaction_setup_timeout(t, jitter, ts);
                 if (r < 0)
                         return r;
 
-                (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
-
-                t->n_attempts = 0;
-                t->next_attempt_after = ts;
-                t->state = DNS_TRANSACTION_PENDING;
-
                 log_debug("Delaying %s transaction %" PRIu16 " for " USEC_FMT "us.",
                           dns_protocol_to_string(t->scope->protocol),
                           t->id,
@@ -2062,22 +2126,11 @@ int dns_transaction_go(DnsTransaction *t) {
                 return dns_transaction_go(t);
         }
 
-        ts += transaction_get_resend_timeout(t);
-
-        r = sd_event_add_time(
-                        t->scope->manager->event,
-                        &t->timeout_event_source,
-                        CLOCK_BOOTTIME,
-                        ts, 0,
-                        on_transaction_timeout, t);
+        usec_t timeout = transaction_get_resend_timeout(t);
+        r = dns_transaction_setup_timeout(t, timeout, usec_add(ts, timeout));
         if (r < 0)
                 return r;
 
-        (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
-
-        t->state = DNS_TRANSACTION_PENDING;
-        t->next_attempt_after = ts;
-
         return 1;
 }
 
index f4cb5d3d8dfc0b07b52106fe0cda9221baf05559..ab86f0f01f55f7c8c867c95a74369dec6b7b15f3 100644 (file)
@@ -201,10 +201,6 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
 /* LLMNR Jitter interval, see RFC 4795 Section 7 */
 #define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
 
-/* mDNS Jitter interval, see RFC 6762 Section 5.2 */
-#define MDNS_JITTER_MIN_USEC   (20 * USEC_PER_MSEC)
-#define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC)
-
 /* mDNS probing interval, see RFC 6762 Section 8.1 */
 #define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC)
 
@@ -214,31 +210,11 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
 /* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
 #define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
 
-/* Maximum attempts to send MDNS requests is one except for probe requests, see RFC 6762 Section 8.1
- * RFC 6762 differentiates between normal (single-shot/continuous) and probe requests.
- * It therefore makes sense to attempt each normal query only once with no retries.
- * Otherwise we'd be sending out three attempts for even a normal query. */
-#define MDNS_TRANSACTION_ATTEMPTS_MAX 1
-
-#define MDNS_PROBE_TRANSACTION_ATTEMPTS_MAX 3
-
-static inline unsigned dns_transaction_attempts_max(DnsProtocol p, bool probing) {
-
-        switch (p) {
-
-        case DNS_PROTOCOL_LLMNR:
-                return LLMNR_TRANSACTION_ATTEMPTS_MAX;
-
-        case DNS_PROTOCOL_MDNS:
-                if (probing)
-                        return MDNS_PROBE_TRANSACTION_ATTEMPTS_MAX;
-                else
-                        return MDNS_TRANSACTION_ATTEMPTS_MAX;
+/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */
+#define MDNS_TRANSACTION_ATTEMPTS_MAX 3
 
-        case DNS_PROTOCOL_DNS:
-                return DNS_TRANSACTION_ATTEMPTS_MAX;
-
-        default:
-                return 0;
-        }
-}
+#define TRANSACTION_ATTEMPTS_MAX(p) (((p) == DNS_PROTOCOL_LLMNR) ? \
+                                         LLMNR_TRANSACTION_ATTEMPTS_MAX : \
+                                         (((p) == DNS_PROTOCOL_MDNS) ? \
+                                             MDNS_TRANSACTION_ATTEMPTS_MAX : \
+                                             DNS_TRANSACTION_ATTEMPTS_MAX))
index 84193eacc1d7ea2c5db0cd81c126392d44303895..464892a8fe03ce411ef056cd256e994d3e67a983 100644 (file)
@@ -93,6 +93,7 @@ static int dnssd_service_load(Manager *manager, const char *filename) {
                         config_item_perf_lookup, resolved_dnssd_gperf_lookup,
                         CONFIG_PARSE_WARN,
                         service,
+                        NULL,
                         NULL);
         if (r < 0)
                 return r;
index d5c71f4080265d45858332ff523833df17d03d25..e1965c3833d80828771fa1bdecb85f2747ac0adb 100644 (file)
@@ -177,8 +177,6 @@ static int mdns_do_tiebreak(DnsResourceKey *key, DnsAnswer *answer, DnsPacket *p
         if (r < 0)
                 return r;
 
-        assert(r > 0);
-
         if (proposed_rrs_cmp(remote, r, our, size) > 0)
                 return 1;
 
@@ -207,10 +205,10 @@ static bool mdns_should_reply_using_unicast(DnsPacket *p) {
         }
 
         /* All the questions in the query had a QU bit set, RFC 6762, section 5.4 */
-        DNS_QUESTION_FOREACH_ITEM(item, p->question) {
+        DNS_QUESTION_FOREACH_ITEM(item, p->question)
                 if (!FLAGS_SET(item->flags, DNS_QUESTION_WANTS_UNICAST_REPLY))
                         return false;
-        }
+
         return true;
 }
 
@@ -256,7 +254,8 @@ static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) {
         if (r < 0)
                 return log_debug_errno(r, "Failed to extract resource records from incoming packet: %m");
 
-        assert_return((dns_question_size(p->question) > 0), -EINVAL);
+        if (dns_question_size(p->question) <= 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Received mDNS query without question, ignoring.");
 
         unicast_reply = mdns_should_reply_using_unicast(p);
         if (unicast_reply && !sender_on_local_subnet(s, p)) {
@@ -410,12 +409,28 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
                         }
                 }
 
-                LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
-                        r = dns_answer_match_key(p->answer, t->key, NULL);
-                        if (r < 0)
-                                log_debug_errno(r, "Failed to match resource key, ignoring: %m");
-                        else if (r > 0) /* This packet matches the transaction, let's pass it on as reply */
+                for (bool match = true; match;) {
+                        match = false;
+                        LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
+                                if (t->state != DNS_TRANSACTION_PENDING)
+                                        continue;
+
+                                r = dns_answer_match_key(p->answer, dns_transaction_key(t), NULL);
+                                if (r <= 0) {
+                                        if (r < 0)
+                                                log_debug_errno(r, "Failed to match resource key, ignoring: %m");
+                                        continue;
+                                }
+
+                                /* This packet matches the transaction, let's pass it on as reply */
                                 dns_transaction_process_reply(t, p, false);
+
+                                /* The dns_transaction_process_reply() -> dns_transaction_complete() ->
+                                 * dns_query_candidate_stop() may free multiple transactions. Hence, restart
+                                 * the loop. */
+                                match = true;
+                                break;
+                        }
                 }
 
                 dns_cache_put(&scope->cache, scope->manager->enable_cache, NULL, DNS_PACKET_RCODE(p), p->answer, NULL, false, _DNSSEC_RESULT_INVALID, UINT32_MAX, p->family, &p->sender);
index 2ae629f595861354bac0d4e8662bd5e34781dd54..4cb4478cf8fe82020881256ccc07bb4749d2dfd8 100644 (file)
@@ -643,11 +643,7 @@ static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **p
                         return bus_log_create_error(r);
         }
 
-        r = bus_append_unit_property_assignment_many(m, t, properties);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return bus_append_unit_property_assignment_many(m, t, properties);
 }
 
 static int transient_cgroup_set_properties(sd_bus_message *m) {
index c3b90bb22734a664526f38a58ac6ef723a1336f9..0d921cc045c178bf5ac1a8fa527ff91f08312175 100644 (file)
@@ -1,13 +1,19 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <linux/blkpg.h>
 #include <sys/file.h>
+#include <sys/ioctl.h>
 #include <unistd.h>
 
+#include "sd-device.h"
+
 #include "alloc-util.h"
 #include "blockdev-util.h"
 #include "btrfs-util.h"
+#include "device-util.h"
 #include "devnum-util.h"
 #include "dirent-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "missing_magic.h"
@@ -377,3 +383,162 @@ int path_is_encrypted(const char *path) {
 
         return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
 }
+
+int fd_get_whole_disk(int fd, bool backing, dev_t *ret) {
+        dev_t devt;
+        struct stat st;
+        int r;
+
+        assert(ret);
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (S_ISBLK(st.st_mode))
+                devt = st.st_rdev;
+        else if (!backing)
+                return -ENOTBLK;
+        else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
+                return -ENOTBLK;
+        else if (major(st.st_dev) != 0)
+                devt = st.st_dev;
+        else {
+                _cleanup_close_ int regfd = -1;
+
+                /* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
+                 * handing, to get the backing device node. */
+
+                regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+                if (regfd < 0)
+                        return regfd;
+
+                r = btrfs_get_block_device_fd(regfd, &devt);
+                if (r == -ENOTTY)
+                        return -ENOTBLK;
+                if (r < 0)
+                        return r;
+        }
+
+        return block_get_whole_disk(devt, ret);
+}
+
+int path_get_whole_disk(const char *path, bool backing, dev_t *ret) {
+        _cleanup_close_ int fd = -1;
+
+        fd = open(path, O_CLOEXEC|O_PATH);
+        if (fd < 0)
+                return -errno;
+
+        return fd_get_whole_disk(fd, backing, ret);
+}
+
+int block_device_add_partition(int fd, const char *name, int nr, uint64_t start, uint64_t size) {
+        assert(fd >= 0);
+        assert(name);
+        assert(nr > 0);
+
+        struct blkpg_partition bp = {
+                .pno = nr,
+                .start = start,
+                .length = size,
+        };
+
+        struct blkpg_ioctl_arg ba = {
+                .op = BLKPG_ADD_PARTITION,
+                .data = &bp,
+                .datalen = sizeof(bp),
+        };
+
+        if (strlen(name) >= sizeof(bp.devname))
+                return -EINVAL;
+
+        strcpy(bp.devname, name);
+
+        return RET_NERRNO(ioctl(fd, BLKPG, &ba));
+}
+
+int block_device_remove_partition(int fd, const char *name, int nr) {
+        assert(fd >= 0);
+        assert(name);
+        assert(nr > 0);
+
+        struct blkpg_partition bp = {
+                .pno = nr,
+        };
+
+        struct blkpg_ioctl_arg ba = {
+                .op = BLKPG_DEL_PARTITION,
+                .data = &bp,
+                .datalen = sizeof(bp),
+        };
+
+        if (strlen(name) >= sizeof(bp.devname))
+                return -EINVAL;
+
+        strcpy(bp.devname, name);
+
+        return RET_NERRNO(ioctl(fd, BLKPG, &ba));
+}
+
+int block_device_remove_all_partitions(int fd) {
+        struct stat stat;
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *part;
+        int r, k = 0;
+
+        if (fstat(fd, &stat) < 0)
+                return -errno;
+
+        r = sd_device_new_from_devnum(&dev, 'b', stat.st_rdev);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_new(&e);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_parent(e, dev);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_subsystem(e, "block", true);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition");
+        if (r < 0)
+                return r;
+
+        FOREACH_DEVICE(e, part) {
+                const char *v, *devname;
+                int nr;
+
+                r = sd_device_get_devname(part, &devname);
+                if (r < 0)
+                        return r;
+
+                r = sd_device_get_property_value(part, "PARTN", &v);
+                if (r < 0)
+                        return r;
+
+                r = safe_atoi(v, &nr);
+                if (r < 0)
+                        return r;
+
+                r = block_device_remove_partition(fd, devname, nr);
+                if (r == -ENODEV) {
+                        log_debug("Kernel removed partition %s before us, ignoring", devname);
+                        continue;
+                }
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to remove partition %s: %m", devname);
+                        k = k ?: r;
+                        continue;
+                }
+
+                log_debug("Removed partition %s", devname);
+        }
+
+        return k;
+}
index 05501f26570af901d98e3442167977b766a29d29..8c9401b4a784e8b270351ab38c5e18690a0a3e87 100644 (file)
@@ -27,3 +27,10 @@ int blockdev_partscan_enabled(int fd);
 
 int fd_is_encrypted(int fd);
 int path_is_encrypted(const char *path);
+
+int fd_get_whole_disk(int fd, bool backing, dev_t *ret);
+int path_get_whole_disk(const char *path, bool backing, dev_t *ret);
+
+int block_device_add_partition(int fd, const char *name, int nr, uint64_t start, uint64_t size);
+int block_device_remove_partition(int fd, const char *name, int nr);
+int block_device_remove_all_partitions(int fd);
index 3754d1dd68c88bac49f31f5a1890ce1d85b12bf9..5b9a6dbc438f681e5e817b361a8487206939ed88 100644 (file)
@@ -63,6 +63,12 @@ const BusLocator* const bus_timedate = &(BusLocator){
         .interface = "org.freedesktop.timedate1"
 };
 
+const BusLocator* const bus_hostname = &(BusLocator){
+        .destination = "org.freedesktop.hostname1",
+        .path = "/org/freedesktop/hostname1",
+        .interface = "org.freedesktop.hostname1"
+};
+
 /* Shorthand flavors of the sd-bus convenience helpers with destination,path,interface strings encapsulated
  * within a single struct. */
 int bus_call_method_async(
index fe3b8765279312d25d234f06f30049ed5a4d675d..9662c906e18d72fba31952cd138ff30d4b3fdb6a 100644 (file)
@@ -19,6 +19,7 @@ extern const BusLocator* const bus_portable_mgr;
 extern const BusLocator* const bus_resolve_mgr;
 extern const BusLocator* const bus_systemd_mgr;
 extern const BusLocator* const bus_timedate;
+extern const BusLocator* const bus_hostname;
 
 /* Shorthand flavors of the sd-bus convenience helpers with destination,path,interface strings encapsulated
  * within a single struct. */
index 737bbd3d36c1402e377885250fad4b39549f5426..27b6f88cd0557388b51f739c6899f2b45b7f97c0 100644 (file)
@@ -151,7 +151,10 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
 
                         bus_print_property_value(name, expected_value, flags, s);
 
-                } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
+                } else if (STR_IN_SET(name, "CPUWeight", "StartupCPUWeight") && u == CGROUP_WEIGHT_IDLE)
+                        bus_print_property_value(name, expected_value, flags, "idle");
+
+                else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
                            (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
                            (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) ||
                            (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == UINT64_MAX) ||
index 3693fd61dc0dc9bbf4ff739df481952009f02f3b..56d522a7d050b1ba9d122990f6c4a19c8bc2ef0d 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "bus-locator.h"
 #include "bus-unit-procs.h"
 #include "glyph-util.h"
 #include "hashmap.h"
@@ -344,11 +345,9 @@ int unit_show_processes(
 
         prefix = strempty(prefix);
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
+                        bus_systemd_mgr,
                         "GetUnitProcesses",
                         error,
                         &reply,
index a326ca30a9a49c1edd4ec6d9339897149afddc2f..2bf0d855b194ce361f5e3fb000b4b3b587168445 100644 (file)
@@ -130,6 +130,7 @@ DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec);
 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse);
 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse);
 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_weight_parse);
 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string);
 DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
 DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
@@ -466,8 +467,10 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
                 return bus_append_parse_boolean(m, field, eq);
 
         if (STR_IN_SET(field, "CPUWeight",
-                              "StartupCPUWeight",
-                              "IOWeight",
+                              "StartupCPUWeight"))
+                return bus_append_cg_cpu_weight_parse(m, field, eq);
+
+        if (STR_IN_SET(field, "IOWeight",
                               "StartupIOWeight"))
                 return bus_append_cg_weight_parse(m, field, eq);
 
@@ -2134,6 +2137,11 @@ static int bus_append_scope_property(sd_bus_message *m, const char *field, const
         if (streq(field, "TimeoutStopSec"))
                 return bus_append_parse_sec_rename(m, field, eq);
 
+        /* Scope units don't have execution context but we still want to allow setting these two,
+         * so let's handle them separately. */
+        if (STR_IN_SET(field, "User", "Group"))
+                return bus_append_string(m, field, eq);
+
         return 0;
 }
 
@@ -2632,12 +2640,10 @@ int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const cha
         case UNIT_TARGET:
         case UNIT_DEVICE:
         case UNIT_SWAP:
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Not supported unit type");
+                break;
 
         default:
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Invalid unit type");
+                assert_not_reached();
         }
 
         r = bus_append_unit_property(m, field, eq);
index a1fabc73c111e2bf6c88485b1d7b37fb225376c3..c3bf7348ffe9caa531a5e69180dffc48e28a48d1 100644 (file)
@@ -173,6 +173,12 @@ int cg_weight_parse(const char *s, uint64_t *ret) {
         return 0;
 }
 
+int cg_cpu_weight_parse(const char *s, uint64_t *ret) {
+        if (streq_ptr(s, "idle"))
+                return *ret = CGROUP_WEIGHT_IDLE;
+        return cg_weight_parse(s, ret);
+}
+
 int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
         uint64_t u;
         int r;
index 7eabce24512dedf101c27545d4a39b4946579133..95a515339d66d339711cd9528e4c5f7c23225b17 100644 (file)
@@ -12,6 +12,7 @@ bool cg_is_legacy_wanted(void);
 bool cg_is_hybrid_wanted(void);
 
 int cg_weight_parse(const char *s, uint64_t *ret);
+int cg_cpu_weight_parse(const char *s, uint64_t *ret);
 int cg_cpu_shares_parse(const char *s, uint64_t *ret);
 int cg_blkio_weight_parse(const char *s, uint64_t *ret);
 
index 640dd96eb2bdbedaa612a92bafd4136faafd7185..7a983edfd7a28dcf8c6e1bbbf75a99826e4115af 100644 (file)
@@ -22,6 +22,7 @@
 #include "cgroup-util.h"
 #include "condition.h"
 #include "cpu-set-util.h"
+#include "creds-util.h"
 #include "efi-api.h"
 #include "env-file.h"
 #include "env-util.h"
@@ -140,6 +141,46 @@ static int condition_test_kernel_command_line(Condition *c, char **env) {
         return false;
 }
 
+static int condition_test_credential(Condition *c, char **env) {
+        int (*gd)(const char **ret);
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_CREDENTIAL);
+
+        /* For now we'll do a very simple existence check and are happy with either a regular or an encrypted
+         * credential. Given that we check the syntax of the argument we have the option to later maybe allow
+         * contents checks too without breaking compatibility, but for now let's be minimalistic. */
+
+        if (!credential_name_valid(c->parameter)) /* credentials with invalid names do not exist */
+                return false;
+
+        FOREACH_POINTER(gd, get_credentials_dir, get_encrypted_credentials_dir) {
+                _cleanup_free_ char *j = NULL;
+                const char *cd;
+
+                r = gd(&cd);
+                if (r == -ENXIO) /* no env var set */
+                        continue;
+                if (r < 0)
+                        return r;
+
+                j = path_join(cd, c->parameter);
+                if (!j)
+                        return -ENOMEM;
+
+                if (laccess(j, F_OK) >= 0)
+                        return true; /* yay! */
+                if (errno != ENOENT)
+                        return -errno;
+
+                /* not found in this dir */
+        }
+
+        return false;
+}
+
 typedef enum {
         /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
          * should be listed first. */
@@ -1099,6 +1140,7 @@ int condition_test(Condition *c, char **env) {
                 [CONDITION_FILE_IS_EXECUTABLE]       = condition_test_file_is_executable,
                 [CONDITION_KERNEL_COMMAND_LINE]      = condition_test_kernel_command_line,
                 [CONDITION_KERNEL_VERSION]           = condition_test_kernel_version,
+                [CONDITION_CREDENTIAL]               = condition_test_credential,
                 [CONDITION_VIRTUALIZATION]           = condition_test_virtualization,
                 [CONDITION_SECURITY]                 = condition_test_security,
                 [CONDITION_CAPABILITY]               = condition_test_capability,
@@ -1218,6 +1260,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_HOST] = "ConditionHost",
         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
         [CONDITION_KERNEL_VERSION] = "ConditionKernelVersion",
+        [CONDITION_CREDENTIAL] = "ConditionCredential",
         [CONDITION_SECURITY] = "ConditionSecurity",
         [CONDITION_CAPABILITY] = "ConditionCapability",
         [CONDITION_AC_POWER] = "ConditionACPower",
@@ -1255,6 +1298,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_HOST] = "AssertHost",
         [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
         [CONDITION_KERNEL_VERSION] = "AssertKernelVersion",
+        [CONDITION_CREDENTIAL] = "AssertCredential",
         [CONDITION_SECURITY] = "AssertSecurity",
         [CONDITION_CAPABILITY] = "AssertCapability",
         [CONDITION_AC_POWER] = "AssertACPower",
index 2bbb7fa7f46d388460a0d24fd4df8d5419c27446..54cc904feb55e4c052f287a558e23cb280668bb4 100644 (file)
@@ -14,6 +14,7 @@ typedef enum ConditionType {
         CONDITION_HOST,
         CONDITION_KERNEL_COMMAND_LINE,
         CONDITION_KERNEL_VERSION,
+        CONDITION_CREDENTIAL,
         CONDITION_SECURITY,
         CONDITION_CAPABILITY,
         CONDITION_AC_POWER,
index 6bd3ab38db8f65e9f4358385a6c0898891cd779d..887ae0dd61644621818d431a4e76a0814f51954a 100644 (file)
@@ -445,7 +445,7 @@ int config_parse(
         return 1;
 }
 
-static int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st) {
+int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st) {
         _cleanup_free_ struct stat *st_copy = NULL;
         _cleanup_free_ char *path_copy = NULL;
         int r;
@@ -588,7 +588,8 @@ int config_parse_many(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                Hashmap **ret_stats_by_path) {
+                Hashmap **ret_stats_by_path,
+                char ***ret_dropin_files) {
 
         _cleanup_strv_free_ char **files = NULL;
         int r;
@@ -602,25 +603,29 @@ int config_parse_many(
         if (r < 0)
                 return r;
 
-        return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
+        r = config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
+        if (r < 0)
+                return r;
+
+        if (ret_dropin_files)
+                *ret_dropin_files = TAKE_PTR(files);
+
+        return 0;
 }
 
-static int config_get_stats_by_path_one(
+static int dropins_get_stats_by_path(
                 const char* conf_file,
                 const char* const* conf_file_dirs,
-                Hashmap *stats_by_path) {
+                Hashmap **stats_by_path) {
 
         _cleanup_strv_free_ char **files = NULL;
         _cleanup_free_ char *dropin_dirname = NULL;
-        struct stat st;
         int r;
 
         assert(conf_file);
         assert(conf_file_dirs);
         assert(stats_by_path);
 
-        /* Unlike config_parse(), this does not support stream. */
-
         r = path_extract_filename(conf_file, &dropin_dirname);
         if (r < 0)
                 return r;
@@ -634,17 +639,9 @@ static int config_get_stats_by_path_one(
         if (r < 0)
                 return r;
 
-        /* First read the main config file. */
-        r = RET_NERRNO(stat(conf_file, &st));
-        if (r >= 0) {
-                r = hashmap_put_stats_by_path(&stats_by_path, conf_file, &st);
-                if (r < 0)
-                        return r;
-        } else if (r != -ENOENT)
-                return r;
-
-        /* Then read all the drop-ins. */
         STRV_FOREACH(fn, files) {
+                struct stat st;
+
                 if (stat(*fn, &st) < 0) {
                         if (errno == ENOENT)
                                 continue;
@@ -652,7 +649,7 @@ static int config_get_stats_by_path_one(
                         return -errno;
                 }
 
-                r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
+                r = hashmap_put_stats_by_path(stats_by_path, *fn, &st);
                 if (r < 0)
                         return r;
         }
@@ -665,6 +662,7 @@ int config_get_stats_by_path(
                 const char *root,
                 unsigned flags,
                 const char* const* dirs,
+                bool check_dropins,
                 Hashmap **ret) {
 
         _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL;
@@ -675,16 +673,32 @@ int config_get_stats_by_path(
         assert(dirs);
         assert(ret);
 
+        /* Unlike config_parse(), this does not support stream. */
+
         r = conf_files_list_strv(&files, suffix, root, flags, dirs);
         if (r < 0)
                 return r;
 
-        stats_by_path = hashmap_new(&path_hash_ops_free_free);
-        if (!stats_by_path)
-                return -ENOMEM;
-
         STRV_FOREACH(f, files) {
-                r = config_get_stats_by_path_one(*f, dirs, stats_by_path);
+                struct stat st;
+
+                /* First read the main config file. */
+                if (stat(*f, &st) < 0) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return -errno;
+                }
+
+                r = hashmap_put_stats_by_path(&stats_by_path, *f, &st);
+                if (r < 0)
+                        return r;
+
+                if (!check_dropins)
+                        continue;
+
+                /* Then read all the drop-ins if requested. */
+                r = dropins_get_stats_by_path(*f, dirs, &stats_by_path);
                 if (r < 0)
                         return r;
         }
index 07b6490165a90d282b71a46bbc432478160a515c..32f2498b53e350576448db0fcbdcf3fe563cbef1 100644 (file)
@@ -112,15 +112,18 @@ int config_parse_many(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                Hashmap **ret_stats_by_path);   /* possibly NULL */
+                Hashmap **ret_stats_by_path,  /* possibly NULL */
+                char ***ret_drop_in_files);   /* possibly NULL */
 
 int config_get_stats_by_path(
                 const char *suffix,
                 const char *root,
                 unsigned flags,
                 const char* const* dirs,
+                bool check_dropins,
                 Hashmap **ret);
 
+int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st);
 bool stats_by_path_equal(Hashmap *a, Hashmap *b);
 
 typedef struct ConfigSection {
index 4d52b4c26b8eb3fa4095b449820876a73144a9de..182544a206b755666014d88a983d91dc1596e242 100644 (file)
@@ -676,6 +676,23 @@ static int memorize_hardlink(
         return 1;
 }
 
+static int fd_copy_tree_generic(
+                int df,
+                const char *from,
+                const struct stat *st,
+                int dt,
+                const char *to,
+                dev_t original_device,
+                unsigned depth_left,
+                uid_t override_uid,
+                gid_t override_gid,
+                CopyFlags copy_flags,
+                HardlinkContext *hardlink_context,
+                const char *display_path,
+                copy_progress_path_t progress_path,
+                copy_progress_bytes_t progress_bytes,
+                void *userdata);
+
 static int fd_copy_regular(
                 int df,
                 const char *from,
@@ -992,18 +1009,9 @@ static int fd_copy_directory(
                                 if (r > 0)
                                         continue;
                         }
+                }
 
-                        q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, child_display_path, progress_path, progress_bytes, userdata);
-                } else if (S_ISREG(buf.st_mode))
-                        q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags, hardlink_context, progress_bytes, userdata);
-                else if (S_ISLNK(buf.st_mode))
-                        q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
-                else if (S_ISFIFO(buf.st_mode))
-                        q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags, hardlink_context);
-                else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
-                        q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags, hardlink_context);
-                else
-                        q = -EOPNOTSUPP;
+                q = fd_copy_tree_generic(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, child_display_path, progress_path, progress_bytes, userdata);
 
                 if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */
                         return q;
@@ -1034,6 +1042,69 @@ static int fd_copy_directory(
         return r;
 }
 
+static int fd_copy_leaf(
+                int df,
+                const char *from,
+                const struct stat *st,
+                int dt,
+                const char *to,
+                uid_t override_uid,
+                gid_t override_gid,
+                CopyFlags copy_flags,
+                HardlinkContext *hardlink_context,
+                const char *display_path,
+                copy_progress_bytes_t progress_bytes,
+                void *userdata) {
+        int r;
+
+        if (S_ISREG(st->st_mode))
+                r = fd_copy_regular(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, progress_bytes, userdata);
+        else if (S_ISLNK(st->st_mode))
+                r = fd_copy_symlink(df, from, st, dt, to, override_uid, override_gid, copy_flags);
+        else if (S_ISFIFO(st->st_mode))
+                r = fd_copy_fifo(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context);
+        else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode) || S_ISSOCK(st->st_mode))
+                r = fd_copy_node(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context);
+        else
+                r = -EOPNOTSUPP;
+
+        return r;
+}
+
+static int fd_copy_tree_generic(
+                int df,
+                const char *from,
+                const struct stat *st,
+                int dt,
+                const char *to,
+                dev_t original_device,
+                unsigned depth_left,
+                uid_t override_uid,
+                gid_t override_gid,
+                CopyFlags copy_flags,
+                HardlinkContext *hardlink_context,
+                const char *display_path,
+                copy_progress_path_t progress_path,
+                copy_progress_bytes_t progress_bytes,
+                void *userdata) {
+        int r;
+
+        if (S_ISDIR(st->st_mode))
+                return fd_copy_directory(df, from, st, dt, to, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_path, progress_bytes, userdata);
+
+        r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
+        /* We just tried to copy a leaf node of the tree. If it failed because the node already exists *and* the COPY_REPLACE flag has been provided, we should unlink the node and re-copy. */
+        if (r == -EEXIST && (copy_flags & COPY_REPLACE)) {
+                /* This codepath is us trying to address an error to copy, if the unlink fails, lets just return the original error. */
+                if (unlinkat(dt, to, 0) < 0)
+                        return r;
+
+                r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
+        }
+
+        return r;
+}
+
 int copy_tree_at_full(
                 int fdf,
                 const char *from,
@@ -1055,18 +1126,7 @@ int copy_tree_at_full(
         if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
                 return -errno;
 
-        if (S_ISREG(st.st_mode))
-                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))
-                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))
-                r = fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
-        else if (S_ISFIFO(st.st_mode))
-                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))
-                r = fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
-        else
-                return -EOPNOTSUPP;
+        r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
         if (r < 0)
                 return r;
 
index 72ec992121f67b86c5f8fcfb99b6c6390fa86963..c1a9d35528dc4682ec709918e4ce354194d034e6 100644 (file)
@@ -83,6 +83,38 @@ int read_credential(const char *name, void **ret, size_t *ret_size) {
                         (char**) ret, ret_size);
 }
 
+int get_credential_user_password(const char *username, char **ret_password, bool *ret_is_hashed) {
+        _cleanup_(erase_and_freep) char *creds_password = NULL;
+        _cleanup_free_ char *cn = NULL;
+        int r;
+
+        /* Try to pick up the password for this account via the credentials logic */
+        cn = strjoin("passwd.hashed-password.", username);
+        if (!cn)
+                return -ENOMEM;
+
+        r = read_credential(cn, (void**) &creds_password, NULL);
+        if (r == -ENOENT) {
+                free(cn);
+                cn = strjoin("passwd.plaintext-password.", username);
+                if (!cn)
+                        return -ENOMEM;
+
+                r = read_credential(cn, (void**) &creds_password, NULL);
+                if (r < 0)
+                        log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
+                else
+                        *ret_is_hashed = false;
+        } else if (r < 0)
+                log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
+        else
+                *ret_is_hashed = true;
+
+        *ret_password = TAKE_PTR(creds_password);
+
+        return r;
+}
+
 #if HAVE_OPENSSL
 
 #define CREDENTIAL_HOST_SECRET_SIZE 4096
index ccbecaf58bd4a6495f59eb58d361f4c1edf64c8a..62e5c888eecf76f912987346a868e265cc74aa84 100644 (file)
@@ -44,6 +44,8 @@ typedef enum CredentialSecretFlags {
 
 int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *ret_size);
 
+int get_credential_user_password(const char *username, char **ret_password, bool *ret_is_hashed);
+
 /* The four modes we support: keyed only by on-disk key, only by TPM2 HMAC key, and by the combination of
  * both, as well as one with a fixed zero length key if TPM2 is missing (the latter of course provides no
  * authenticity or confidentiality, but is still useful for integrity protection, and makes things simpler
diff --git a/src/shared/daemon-util.c b/src/shared/daemon-util.c
new file mode 100644 (file)
index 0000000..32180a1
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "daemon-util.h"
+#include "fd-util.h"
+#include "log.h"
+#include "string-util.h"
+
+static int notify_remove_fd_warn(const char *name) {
+        int r;
+
+        assert(name);
+
+        r = sd_notifyf(/* unset_environment = */ false,
+                       "FDSTOREREMOVE=1\n"
+                       "FDNAME=%s", name);
+        if (r < 0)
+                return log_warning_errno(r,
+                                         "Failed to remove file descriptor \"%s\" from the store, ignoring: %m",
+                                         name);
+
+        return 0;
+}
+
+int notify_remove_fd_warnf(const char *format, ...) {
+        _cleanup_free_ char *p = NULL;
+        va_list ap;
+        int r;
+
+        assert(format);
+
+        va_start(ap, format);
+        r = vasprintf(&p, format, ap);
+        va_end(ap);
+        if (r < 0)
+                return log_oom();
+
+        return notify_remove_fd_warn(p);
+}
+
+int close_and_notify_warn(int fd, const char *name) {
+        if (name)
+                (void) notify_remove_fd_warn(name);
+
+        return safe_close(fd);
+}
+
+static int notify_push_fd(int fd, const char *name) {
+        _cleanup_free_ char *state = NULL;
+
+        assert(fd >= 0);
+        assert(name);
+
+        state = strjoin("FDSTORE=1\n"
+                        "FDNAME=", name);
+        if (!state)
+                return -ENOMEM;
+
+        return sd_pid_notify_with_fds(0, /* unset_environment = */ false, state, &fd, 1);
+}
+
+int notify_push_fdf(int fd, const char *format, ...) {
+        _cleanup_free_ char *name = NULL;
+        va_list ap;
+        int r;
+
+        assert(fd >= 0);
+        assert(format);
+
+        va_start(ap, format);
+        r = vasprintf(&name, format, ap);
+        va_end(ap);
+        if (r < 0)
+                return -ENOMEM;
+
+        return notify_push_fd(fd, name);
+}
index 585e4894a01365459792e54e2d41a45280e5f41f..711885bba462a89df59e61693b6f9973c069009d 100644 (file)
@@ -5,6 +5,8 @@
 
 #include "sd-daemon.h"
 
+#include "macro.h"
+
 #define NOTIFY_READY "READY=1\n" "STATUS=Processing requests..."
 #define NOTIFY_STOPPING "STOPPING=1\n" "STATUS=Shutting down..."
 
@@ -20,3 +22,7 @@ static inline void notify_on_cleanup(const char **p) {
         if (*p)
                 (void) sd_notify(false, *p);
 }
+
+int notify_remove_fd_warnf(const char *format, ...) _printf_(1, 2);
+int close_and_notify_warn(int fd, const char *name);
+int notify_push_fdf(int fd, const char *format, ...) _printf_(2, 3);
index c7a336d4326e3ab518ec7355266d5e4b85350ee5..87712abfb3bcb005ee74972be2f4c12244b847b9 100644 (file)
@@ -4,7 +4,6 @@
 #include <valgrind/memcheck.h>
 #endif
 
-#include <linux/blkpg.h>
 #include <linux/dm-ioctl.h>
 #include <linux/loop.h>
 #include <sys/file.h>
@@ -149,29 +148,6 @@ static void check_partition_flags(
                 log_debug("Unexpected partition flag %llu set on %s!", bit, node);
         }
 }
-
-static int ioctl_partition_remove(int fd, const char *name, int nr) {
-        assert(fd >= 0);
-        assert(name);
-        assert(nr > 0);
-
-        struct blkpg_partition bp = {
-                .pno = nr,
-        };
-
-        struct blkpg_ioctl_arg ba = {
-                .op = BLKPG_DEL_PARTITION,
-                .data = &bp,
-                .datalen = sizeof(bp),
-        };
-
-        if (strlen(name) >= sizeof(bp.devname))
-                return -EINVAL;
-
-        strcpy(bp.devname, name);
-
-        return RET_NERRNO(ioctl(fd, BLKPG, &ba));
-}
 #endif
 
 static void dissected_partition_done(int fd, DissectedPartition *p) {
@@ -182,7 +158,7 @@ static void dissected_partition_done(int fd, DissectedPartition *p) {
         if (p->node && p->partno > 0 && !p->relinquished) {
                 int r;
 
-                r = ioctl_partition_remove(fd, p->node, p->partno);
+                r = block_device_remove_partition(fd, p->node, p->partno);
                 if (r < 0)
                         log_debug_errno(r, "BLKPG_DEL_PARTITION failed, ignoring: %m");
         }
@@ -202,37 +178,6 @@ static void dissected_partition_done(int fd, DissectedPartition *p) {
 }
 
 #if HAVE_BLKID
-static int ioctl_partition_add(
-                int fd,
-                const char *name,
-                int nr,
-                uint64_t start,
-                uint64_t size) {
-
-        assert(fd >= 0);
-        assert(name);
-        assert(nr > 0);
-
-        struct blkpg_partition bp = {
-                .pno = nr,
-                .start = start,
-                .length = size,
-        };
-
-        struct blkpg_ioctl_arg ba = {
-                .op = BLKPG_ADD_PARTITION,
-                .data = &bp,
-                .datalen = sizeof(bp),
-        };
-
-        if (strlen(name) >= sizeof(bp.devname))
-                return -EINVAL;
-
-        strcpy(bp.devname, name);
-
-        return RET_NERRNO(ioctl(fd, BLKPG, &ba));
-}
-
 static int make_partition_devname(
                 const char *whole_devname,
                 int nr,
@@ -548,7 +493,7 @@ int dissect_image(
                  * Kernel returns EBUSY if there's already a partition by that number or an overlapping
                  * partition already existent. */
 
-                r = ioctl_partition_add(fd, node, nr, (uint64_t) start * 512, (uint64_t) size * 512);
+                r = block_device_add_partition(fd, node, nr, (uint64_t) start * 512, (uint64_t) size * 512);
                 if (r < 0) {
                         if (r != -EBUSY)
                                 return log_debug_errno(r, "BLKPG_ADD_PARTITION failed: %m");
@@ -831,7 +776,7 @@ int dissect_image(
 
                                         if (!PARTITION_DESIGNATOR_VERSIONED(designator) ||
                                             strverscmp_improved(m->partitions[designator].label, label) >= 0) {
-                                                r = ioctl_partition_remove(fd, node, nr);
+                                                r = block_device_remove_partition(fd, node, nr);
                                                 if (r < 0)
                                                         log_debug_errno(r, "BLKPG_DEL_PARTITION failed, ignoring: %m");
                                                 continue;
@@ -908,7 +853,7 @@ int dissect_image(
 
                                 /* First one wins */
                                 if (m->partitions[PARTITION_XBOOTLDR].found) {
-                                        r = ioctl_partition_remove(fd, node, nr);
+                                        r = block_device_remove_partition(fd, node, nr);
                                         if (r < 0)
                                                 log_debug_errno(r, "BLKPG_DEL_PARTITION failed, ignoring: %m");
                                         continue;
index e114d5ea2c60af0d3a4c686f59bfb31ad200c272..3eed44a533bb7ea4ec4da1a2624e57b53d7806f6 100644 (file)
@@ -187,6 +187,54 @@ int efi_loader_get_features(uint64_t *ret) {
         return 0;
 }
 
+int efi_stub_get_features(uint64_t *ret) {
+        _cleanup_free_ void *v = NULL;
+        size_t s;
+        int r;
+
+        assert(ret);
+
+        if (!is_efi_boot()) {
+                *ret = 0;
+                return 0;
+        }
+
+        r = efi_get_variable(EFI_LOADER_VARIABLE(StubFeatures), NULL, &v, &s);
+        if (r == -ENOENT) {
+                _cleanup_free_ char *info = NULL;
+
+                /* The new (v252+) StubFeatures variable is not supported, let's see if it's systemd-stub at all */
+                r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubInfo), &info);
+                if (r < 0) {
+                        if (r != -ENOENT)
+                                return r;
+
+                        /* Variable not set, definitely means not systemd-stub */
+
+                } else if (first_word(info, "systemd-stub")) {
+
+                        /* An older systemd-stub version. Let's hardcode the feature set, since it was pretty
+                         * static in all its versions. */
+
+                        *ret = EFI_STUB_FEATURE_REPORT_BOOT_PARTITION;
+                        return 0;
+                }
+
+                /* No features supported */
+                *ret = 0;
+                return 0;
+        }
+        if (r < 0)
+                return r;
+
+        if (s != sizeof(uint64_t))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "StubFeatures EFI variable doesn't have the right size.");
+
+        memcpy(ret, v, sizeof(uint64_t));
+        return 0;
+}
+
 int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
         _cleanup_free_ char *v = NULL;
         static struct stat cache_stat = {};
index 507160e50de60f7261f9e3f1f00c81f7ee7d1e12..84968869abe2732a8cd64f5b2a4ed89774ea7542 100644 (file)
@@ -16,6 +16,7 @@ int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader);
 int efi_loader_get_entries(char ***ret);
 
 int efi_loader_get_features(uint64_t *ret);
+int efi_stub_get_features(uint64_t *ret);
 
 int efi_loader_get_config_timeout_one_shot(usec_t *ret);
 int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat);
@@ -38,6 +39,10 @@ static inline int efi_loader_get_features(uint64_t *ret) {
         return -EOPNOTSUPP;
 }
 
+static inline int efi_stub_get_features(uint64_t *ret) {
+        return -EOPNOTSUPP;
+}
+
 static inline int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
         return -EOPNOTSUPP;
 }
index db87b23a71e1eeefd06fa7a4bc34159748ba716a..2da8e7ea94bec49d261fac8891d42da32d8a7b7b 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "alloc-util.h"
+#include "architecture.h"
 #include "env-util.h"
 #include "extension-release.h"
 #include "log.h"
@@ -15,7 +16,7 @@ int extension_release_validate(
                 const char *host_sysext_scope,
                 char **extension_release) {
 
-        const char *extension_release_id = NULL, *extension_release_sysext_level = NULL;
+        const char *extension_release_id = NULL, *extension_release_sysext_level = NULL, *extension_architecture = NULL;
 
         assert(name);
         assert(!isempty(host_os_release_id));
@@ -48,13 +49,30 @@ int extension_release_validate(
                 }
         }
 
+        /* When the architecture field is present and not '_any' it must match the host - for now just look at uname but in
+         * the future we could check if the kernel also supports 32 bit or binfmt has a translator set up for the architecture */
+        extension_architecture = strv_env_pairs_get(extension_release, "ARCHITECTURE");
+        if (!isempty(extension_architecture) && !streq(extension_architecture, "_any") &&
+            !streq(architecture_to_string(uname_architecture()), extension_architecture)) {
+                log_debug("Extension '%s' is for architecture '%s', but deployed on top of '%s'.",
+                          name, extension_architecture, architecture_to_string(uname_architecture()));
+                return 0;
+        }
+
         extension_release_id = strv_env_pairs_get(extension_release, "ID");
         if (isempty(extension_release_id)) {
-                log_debug("Extension '%s' does not contain ID in extension-release but requested to match '%s'",
+                log_debug("Extension '%s' does not contain ID in extension-release but requested to match '%s' or be '_any'",
                           name, host_os_release_id);
                 return 0;
         }
 
+        /* A sysext with no host OS dependency (static binaries or scripts) can match
+         * '_any' host OS, and VERSION_ID or SYSEXT_LEVEL are not required anywhere */
+        if (streq(extension_release_id, "_any")) {
+                log_debug("Extension '%s' matches '_any' OS.", name);
+                return 1;
+        }
+
         if (!streq(host_os_release_id, extension_release_id)) {
                 log_debug("Extension '%s' is for OS '%s', but deployed on top of '%s'.",
                           name, extension_release_id, host_os_release_id);
index 87ac9d167efa52e9df982fec49c10bbb8beba63f..14c1ce0b45c163954c4b3361a9db83cabfb626c8 100644 (file)
@@ -416,7 +416,7 @@ int find_esp_and_warn(
                                                "Failed to resolve path %s%s%s: %m",
                                                path,
                                                root ? " under directory " : "",
-                                               root ?: "");
+                                               strempty(root));
 
                 r = verify_esp(p, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags);
                 if (r < 0)
@@ -435,7 +435,7 @@ int find_esp_and_warn(
                                                "Failed to resolve path %s%s%s: %m",
                                                path,
                                                root ? " under directory " : "",
-                                               root ?: "");
+                                               strempty(root));
 
                 if (!path_is_valid(p) || !path_is_absolute(p))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -474,7 +474,7 @@ int find_esp_and_warn(
                                                "Failed to resolve path %s%s%s: %m",
                                                dir,
                                                root ? " under directory " : "",
-                                               root ?: "");
+                                               strempty(root));
 
                 r = verify_esp(p, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid,
                                flags | VERIFY_ESP_SEARCHING);
@@ -712,7 +712,7 @@ int find_xbootldr_and_warn(
                                                "Failed to resolve path %s%s%s: %m",
                                                path,
                                                root ? " under directory " : "",
-                                               root ?: "");
+                                               strempty(root));
 
                 r = verify_xbootldr(p, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
                 if (r < 0)
@@ -731,7 +731,7 @@ int find_xbootldr_and_warn(
                                                "Failed to resolve path %s%s%s: %m",
                                                path,
                                                root ? " under directory " : "",
-                                               root ?: "");
+                                               strempty(root));
 
                 if (!path_is_valid(p) || !path_is_absolute(p))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -758,7 +758,7 @@ int find_xbootldr_and_warn(
                 return log_error_errno(r,
                                        "Failed to resolve path /boot%s%s: %m",
                                        root ? " under directory " : "",
-                                       root ?: "");
+                                       strempty(root));
 
         r = verify_xbootldr(p, true, unprivileged_mode, ret_uuid, ret_devid);
         if (r >= 0)
index 87a326cafdf7ee97afea677742756d9ecb90918b..b5f0d1bab75afe184d873436878efe1a6d9187a5 100644 (file)
 #include "time-util.h"
 
 #define NFT_SYSTEMD_DNAT_MAP_NAME "map_port_ipport"
-#define NFT_SYSTEMD_TABLE_NAME   "io.systemd.nat"
+#define NFT_SYSTEMD_TABLE_NAME    "io.systemd.nat"
 #define NFT_SYSTEMD_MASQ_SET_NAME "masq_saddr"
 
 #define NFNL_DEFAULT_TIMEOUT_USECS (1ULL * USEC_PER_SEC)
 
 #define UDP_DPORT_OFFSET 2
 
-static int nfnl_netlink_sendv(
-                sd_netlink *nfnl,
-                sd_netlink_message *messages[static 1],
-                size_t msgcount) {
+static sd_netlink_message **netlink_message_unref_many(sd_netlink_message **m) {
+        if (!m)
+                return NULL;
+
+        /* This does not free array. The end of the array must be NULL. */
 
-        _cleanup_free_ uint32_t *serial = NULL;
+        for (sd_netlink_message **p = m; *p; p++)
+                *p = sd_netlink_message_unref(*p);
+
+        return m;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_netlink_message**, netlink_message_unref_many);
+
+static int nfnl_open_expr_container(sd_netlink_message *m, const char *name) {
         int r;
 
-        assert(nfnl);
-        assert(messages);
-        assert(msgcount > 0);
+        assert(m);
+        assert(name);
 
-        r = sd_netlink_sendv(nfnl, messages, msgcount, &serial);
+        r = sd_netlink_message_open_array(m, NFTA_LIST_ELEM);
         if (r < 0)
                 return r;
 
-        r = 0;
-        for (size_t i = 1; i < msgcount - 1; i++) {
-                int tmp;
-
-                /* If message is an error, this returns embedded errno */
-                tmp = sd_netlink_read(nfnl, serial[i], NFNL_DEFAULT_TIMEOUT_USECS, NULL);
-                if (tmp < 0 && r == 0)
-                        r = tmp;
-        }
-
-        return r;
+        return sd_netlink_message_open_container_union(m, NFTA_EXPR_DATA, name);
 }
 
-static int nfnl_add_open_expr_container(sd_netlink_message *m, const char *name) {
+static int nfnl_close_expr_container(sd_netlink_message *m) {
         int r;
 
-        r = sd_netlink_message_open_array(m, NFTA_LIST_ELEM);
-        if (r < 0)
-                return r;
+        assert(m);
 
-        r = sd_netlink_message_append_string(m, NFTA_EXPR_NAME, name);
+        r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
         if (r < 0)
                 return r;
 
-        return sd_netlink_message_open_container_union(m, NFTA_EXPR_DATA, name);
+        return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
 }
 
-static int nfnl_add_expr_fib(sd_netlink_message *m, uint32_t nft_fib_flags,
-                             enum nft_fib_result result,
-                             enum nft_registers dreg) {
+static int nfnl_add_expr_fib(
+                sd_netlink_message *m,
+                uint32_t nft_fib_flags,
+                enum nft_fib_result result,
+                enum nft_registers dreg) {
+
         int r;
 
-        r = nfnl_add_open_expr_container(m, "fib");
+        assert(m);
+
+        r = nfnl_open_expr_container(m, "fib");
         if (r < 0)
                 return r;
 
         r = sd_netlink_message_append_u32(m, NFTA_FIB_FLAGS, htobe32(nft_fib_flags));
         if (r < 0)
                 return r;
+
         r = sd_netlink_message_append_u32(m, NFTA_FIB_RESULT, htobe32(result));
-        if (r < 0)
-                return r;
-        r = sd_netlink_message_append_u32(m, NFTA_FIB_DREG, htobe32(dreg));
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
+        r = sd_netlink_message_append_u32(m, NFTA_FIB_DREG, htobe32(dreg));
         if (r < 0)
                 return r;
 
-        return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+        return nfnl_close_expr_container(m);
 }
 
-static int nfnl_add_expr_meta(sd_netlink_message *m, enum nft_meta_keys key,
-                              enum nft_registers dreg) {
+static int nfnl_add_expr_meta(
+                sd_netlink_message *m,
+                enum nft_meta_keys key,
+                enum nft_registers dreg) {
+
         int r;
 
-        r = nfnl_add_open_expr_container(m, "meta");
+        assert(m);
+
+        r = nfnl_open_expr_container(m, "meta");
         if (r < 0)
                 return r;
 
@@ -116,172 +120,158 @@ static int nfnl_add_expr_meta(sd_netlink_message *m, enum nft_meta_keys key,
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
-        if (r < 0)
-                return r;
-
-        return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+        return nfnl_close_expr_container(m);
 }
 
-static int nfnl_add_expr_payload(sd_netlink_message *m, enum nft_payload_bases pb,
-                                 uint32_t offset, uint32_t len, enum nft_registers dreg) {
+static int nfnl_add_expr_payload(
+                sd_netlink_message *m,
+                enum nft_payload_bases pb,
+                uint32_t offset,
+                uint32_t len,
+                enum nft_registers dreg) {
+
         int r;
 
-        r = nfnl_add_open_expr_container(m, "payload");
+        assert(m);
+
+        r = nfnl_open_expr_container(m, "payload");
         if (r < 0)
                 return r;
 
         r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_DREG, htobe32(dreg));
         if (r < 0)
                 return r;
+
         r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_BASE, htobe32(pb));
         if (r < 0)
                 return r;
+
         r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_OFFSET, htobe32(offset));
         if (r < 0)
                 return r;
+
         r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_LEN, htobe32(len));
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
-        if (r < 0)
-                return r;
-        return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+        return nfnl_close_expr_container(m);
 }
 
-static int nfnl_add_expr_lookup_set_data(sd_netlink_message *m, const char *set_name,
-                                         enum nft_registers sreg) {
-        int r;
-
-        r = nfnl_add_open_expr_container(m, "lookup");
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_string(m, NFTA_LOOKUP_SET, set_name);
-        if (r < 0)
-                return r;
-
-        return sd_netlink_message_append_u32(m, NFTA_LOOKUP_SREG, htobe32(sreg));
-}
+static int nfnl_add_expr_lookup(
+                sd_netlink_message *m,
+                const char *set_name,
+                enum nft_registers sreg,
+                enum nft_registers dreg) {
 
-static int nfnl_add_expr_lookup_set(sd_netlink_message *m, const char *set_name,
-                                    enum nft_registers sreg) {
         int r;
 
-        r = nfnl_add_expr_lookup_set_data(m, set_name, sreg);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
-        if (r < 0)
-                return r;
-        return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
-}
-
-static int nfnl_add_expr_lookup_map(sd_netlink_message *m, const char *set_name,
-                                    enum nft_registers sreg, enum nft_registers dreg) {
-        int r;
+        assert(m);
+        assert(set_name);
 
-        r = nfnl_add_expr_lookup_set_data(m, set_name, sreg);
+        r = nfnl_open_expr_container(m, "lookup");
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_DREG, htobe32(dreg));
+        r = sd_netlink_message_append_string(m, NFTA_LOOKUP_SET, set_name);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
+        r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_SREG, htobe32(sreg));
         if (r < 0)
                 return r;
 
-        return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
-}
-
-static int nfnl_add_expr_data(sd_netlink_message *m, int attr, const void *data, uint32_t dlen) {
-        int r;
-
-        r = sd_netlink_message_open_container(m, attr);
-        if (r < 0)
-                return r;
-        r = sd_netlink_message_append_data(m, NFTA_DATA_VALUE, data, dlen);
-        if (r < 0)
-                return r;
+        if (dreg != 0) {
+                r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_DREG, htobe32(dreg));
+                if (r < 0)
+                        return r;
+        }
 
-        return sd_netlink_message_close_container(m); /* attr */
+        return nfnl_close_expr_container(m);
 }
 
-static int nfnl_add_expr_cmp_data(sd_netlink_message *m, const void *data, uint32_t dlen) {
-        return nfnl_add_expr_data(m, NFTA_CMP_DATA, data, dlen);
-}
+static int nfnl_add_expr_cmp(
+                sd_netlink_message *m,
+                enum nft_cmp_ops cmp_op,
+                enum nft_registers sreg,
+                const void *data,
+                size_t dlen) {
 
-static int nfnl_add_expr_cmp(sd_netlink_message *m, enum nft_cmp_ops cmp_op,
-                             enum nft_registers sreg, const void *data, uint32_t dlen) {
         int r;
 
-        r = nfnl_add_open_expr_container(m, "cmp");
+        assert(m);
+        assert(data);
+
+        r = nfnl_open_expr_container(m, "cmp");
         if (r < 0)
                 return r;
 
         r = sd_netlink_message_append_u32(m, NFTA_CMP_OP, htobe32(cmp_op));
         if (r < 0)
                 return r;
+
         r = sd_netlink_message_append_u32(m, NFTA_CMP_SREG, htobe32(sreg));
         if (r < 0)
                 return r;
 
-        r = nfnl_add_expr_cmp_data(m, data, dlen);
+        r = sd_netlink_message_append_container_data(m, NFTA_CMP_DATA, NFTA_DATA_VALUE, data, dlen);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
-        if (r < 0)
-                return r;
-        return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+        return nfnl_close_expr_container(m);
 }
 
-static int nfnl_add_expr_bitwise(sd_netlink_message *m,
-                                 enum nft_registers sreg,
-                                 enum nft_registers dreg,
-                                 const void *and,
-                                 const void *xor, uint32_t len) {
+static int nfnl_add_expr_bitwise(
+                sd_netlink_message *m,
+                enum nft_registers sreg,
+                enum nft_registers dreg,
+                const void *and,
+                const void *xor,
+                uint32_t len) {
+
         int r;
 
-        r = nfnl_add_open_expr_container(m, "bitwise");
+        assert(m);
+        assert(and);
+        assert(xor);
+
+        r = nfnl_open_expr_container(m, "bitwise");
         if (r < 0)
                 return r;
 
         r = sd_netlink_message_append_u32(m, NFTA_BITWISE_SREG, htobe32(sreg));
         if (r < 0)
                 return r;
+
         r = sd_netlink_message_append_u32(m, NFTA_BITWISE_DREG, htobe32(dreg));
         if (r < 0)
                 return r;
+
         r = sd_netlink_message_append_u32(m, NFTA_BITWISE_LEN, htobe32(len));
         if (r < 0)
                 return r;
 
-        r = nfnl_add_expr_data(m, NFTA_BITWISE_MASK, and, len);
+        r = sd_netlink_message_append_container_data(m, NFTA_BITWISE_MASK, NFTA_DATA_VALUE, and, len);
         if (r < 0)
                 return r;
 
-        r = nfnl_add_expr_data(m, NFTA_BITWISE_XOR, xor, len);
+        r = sd_netlink_message_append_container_data(m, NFTA_BITWISE_XOR, NFTA_DATA_VALUE, xor, len);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
-        if (r < 0)
-                return r;
-        return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+        return nfnl_close_expr_container(m);
 }
 
-static int nfnl_add_expr_dnat(sd_netlink_message *m,
-                              int family,
-                              enum nft_registers areg,
-                              enum nft_registers preg) {
+static int nfnl_add_expr_dnat(
+                sd_netlink_message *m,
+                int family,
+                enum nft_registers areg,
+                enum nft_registers preg) {
+
         int r;
 
-        r = nfnl_add_open_expr_container(m, "nat");
+        assert(m);
+
+        r = nfnl_open_expr_container(m, "nat");
         if (r < 0)
                 return r;
 
@@ -296,14 +286,12 @@ static int nfnl_add_expr_dnat(sd_netlink_message *m,
         r = sd_netlink_message_append_u32(m, NFTA_NAT_REG_ADDR_MIN, htobe32(areg));
         if (r < 0)
                 return r;
+
         r = sd_netlink_message_append_u32(m, NFTA_NAT_REG_PROTO_MIN, htobe32(preg));
-        if (r < 0)
-                return r;
-        r = sd_netlink_message_close_container(m);
         if (r < 0)
                 return r;
 
-        return sd_netlink_message_close_container(m);
+        return nfnl_close_expr_container(m);
 }
 
 static int nfnl_add_expr_masq(sd_netlink_message *m) {
@@ -320,13 +308,22 @@ static int nfnl_add_expr_masq(sd_netlink_message *m) {
         return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
 }
 
-static int sd_nfnl_message_new_masq_rule(sd_netlink *nfnl, sd_netlink_message **ret, int family,
-                                         const char *chain) {
+static int sd_nfnl_message_new_masq_rule(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *chain) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
         /* -t nat -A POSTROUTING -p protocol -s source/pflen -o out_interface -d destination/pflen -j MASQUERADE */
 
+        assert(nfnl);
+        assert(ret);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(chain);
+
         r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain);
         if (r < 0)
                 return r;
@@ -346,7 +343,7 @@ static int sd_nfnl_message_new_masq_rule(sd_netlink *nfnl, sd_netlink_message **
                 return r;
 
         /* 1st statement: use reg1 content to make lookup in @masq_saddr set. */
-        r = nfnl_add_expr_lookup_set(m, NFT_SYSTEMD_MASQ_SET_NAME, NFT_REG32_01);
+        r = nfnl_add_expr_lookup(m, NFT_SYSTEMD_MASQ_SET_NAME, NFT_REG32_01, 0);
         if (r < 0)
                 return r;
 
@@ -358,12 +355,17 @@ static int sd_nfnl_message_new_masq_rule(sd_netlink *nfnl, sd_netlink_message **
         r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */
         if (r < 0)
                 return r;
+
         *ret = TAKE_PTR(m);
         return 0;
 }
 
-static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_message **ret, int family,
-                                             const char *chain) {
+static int sd_nfnl_message_new_dnat_rule_pre(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *chain) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         enum nft_registers proto_reg;
         uint32_t local = RTN_LOCAL;
@@ -372,6 +374,11 @@ static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_messag
         /* -t nat -A PREROUTING -p protocol --dport local_port -i in_interface -s source/pflen
          * -d destination/pflen -j DNAT --to-destination remote_addr:remote_port */
 
+        assert(nfnl);
+        assert(ret);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(chain);
+
         r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain);
         if (r < 0)
                 return r;
@@ -401,9 +408,8 @@ static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_messag
                 return r;
 
         /* 3rd statement: lookup 'l4proto . dport', e.g. 'tcp . 22' as key and
-         * store address and port for the dnat mapping in REG1/REG2.
-        */
-        r = nfnl_add_expr_lookup_map(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
+         * store address and port for the dnat mapping in REG1/REG2. */
+        r = nfnl_add_expr_lookup(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
         if (r < 0)
                 return r;
 
@@ -415,18 +421,27 @@ static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_messag
         r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */
         if (r < 0)
                 return r;
+
         *ret = TAKE_PTR(m);
         return 0;
 }
 
-static int sd_nfnl_message_new_dnat_rule_out(sd_netlink *nfnl, sd_netlink_message **ret,
-                                             int family, const char *chain) {
-        static const uint32_t zero = 0, one = 1;
+static int sd_nfnl_message_new_dnat_rule_out(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *chain) {
 
+        static const uint32_t zero = 0, one = 1;
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         enum nft_registers proto_reg;
         int r;
 
+        assert(nfnl);
+        assert(ret);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(chain);
+
         r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain);
         if (r < 0)
                 return r;
@@ -488,9 +503,8 @@ static int sd_nfnl_message_new_dnat_rule_out(sd_netlink *nfnl, sd_netlink_messag
          * the new destination ip and port number.
          *
          * reg1 and reg2 are clobbered and will then contain the new
-         * address/port number.
-         */
-        r = nfnl_add_expr_lookup_map(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
+         * address/port number. */
+        r = nfnl_add_expr_lookup(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
         if (r < 0)
                 return r;
 
@@ -504,18 +518,29 @@ static int sd_nfnl_message_new_dnat_rule_out(sd_netlink *nfnl, sd_netlink_messag
         r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */
         if (r < 0)
                 return r;
+
         *ret = TAKE_PTR(m);
         return 0;
 }
 
-static int nft_new_set(struct sd_netlink *nfnl,
-                       sd_netlink_message **ret,
-                       int family, const char *set_name,
-                       uint32_t set_id,
-                       uint32_t flags, uint32_t type, uint32_t klen) {
+static int nft_new_set(
+                struct sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *set_name,
+                uint32_t set_id,
+                uint32_t flags,
+                uint32_t type,
+                uint32_t klen) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
+        assert(nfnl);
+        assert(ret);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(set_name);
+
         r = sd_nfnl_nft_message_new_set(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name, set_id, klen);
         if (r < 0)
                 return r;
@@ -534,13 +559,26 @@ static int nft_new_set(struct sd_netlink *nfnl,
         return r;
 }
 
-static int nft_new_map(struct sd_netlink *nfnl,
-                       sd_netlink_message **ret,
-                       int family, const char *set_name, uint32_t set_id,
-                       uint32_t flags, uint32_t type, uint32_t klen, uint32_t dtype, uint32_t dlen) {
+static int nft_new_map(
+                struct sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *set_name,
+                uint32_t set_id,
+                uint32_t flags,
+                uint32_t type,
+                uint32_t klen,
+                uint32_t dtype,
+                uint32_t dlen) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
+        assert(nfnl);
+        assert(ret);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(set_name);
+
         r = nft_new_set(nfnl, &m, family, set_name, set_id, flags | NFT_SET_MAP, type, klen);
         if (r < 0)
                 return r;
@@ -552,17 +590,31 @@ static int nft_new_map(struct sd_netlink *nfnl,
         r = sd_netlink_message_append_u32(m, NFTA_SET_DATA_LEN, htobe32(dlen));
         if (r < 0)
                 return r;
+
         *ret = TAKE_PTR(m);
         return 0;
 }
 
-static int nft_add_element(sd_netlink *nfnl, sd_netlink_message **ret,
-                           int family, const char *set_name,
-                           const void *key, uint32_t klen,
-                           const void *data, uint32_t dlen) {
+static int nft_add_element(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *set_name,
+                const void *key,
+                uint32_t klen,
+                const void *data,
+                uint32_t dlen) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
+        assert(nfnl);
+        assert(ret);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(set_name);
+        assert(key);
+        assert(data);
+
         /*
          * Ideally there would be an API that provides:
          *
@@ -576,40 +628,64 @@ static int nft_add_element(sd_netlink *nfnl, sd_netlink_message **ret,
          * This replicated here and each element gets added to the set
          * one-by-one.
          */
-        r = sd_nfnl_nft_message_new_setelems_begin(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name);
+        r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ true, family, NFT_SYSTEMD_TABLE_NAME, set_name);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
         if (r < 0)
                 return r;
 
-        r = sd_nfnl_nft_message_add_setelem(m, 0, key, klen, data, dlen);
+        r = sd_nfnl_nft_message_append_setelem(m, 0, key, klen, data, dlen, 0);
         if (r < 0)
                 return r;
 
         /* could theoretically append more set elements to add here */
-        r = sd_nfnl_nft_message_add_setelem_end(m);
+
+        r = sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
         if (r < 0)
                 return r;
+
         *ret = TAKE_PTR(m);
         return 0;
 }
 
-static int nft_del_element(sd_netlink *nfnl,
-                           sd_netlink_message **ret, int family, const char *set_name,
-                           const void *key, uint32_t klen,
-                           const void *data, uint32_t dlen) {
+static int nft_del_element(
+                sd_netlink *nfnl,
+                sd_netlink_message **ret,
+                int family,
+                const char *set_name,
+                const void *key,
+                uint32_t klen,
+                const void *data,
+                uint32_t dlen) {
+
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
-        r = sd_nfnl_nft_message_del_setelems_begin(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name);
+        assert(nfnl);
+        assert(ret);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(set_name);
+        assert(key);
+        assert(data);
+
+        r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ false, family, NFT_SYSTEMD_TABLE_NAME, set_name);
         if (r < 0)
                return r;
 
-        r = sd_nfnl_nft_message_add_setelem(m, 0, key, klen, data, dlen);
+        r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
+        if (r < 0)
+                return r;
+
+        r = sd_nfnl_nft_message_append_setelem(m, 0, key, klen, data, dlen, 0);
         if (r < 0)
                return r;
 
-        r = sd_nfnl_nft_message_add_setelem_end(m);
+        r = sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
         if (r < 0)
                 return r;
+
         *ret = TAKE_PTR(m);
         return 0;
 }
@@ -617,15 +693,14 @@ static int nft_del_element(sd_netlink *nfnl,
 /* This is needed so 'nft' userspace tool can properly format the contents
  * of the set/map when someone uses 'nft' to inspect their content.
  *
- * The values cannot be changed, they are part of the nft tool type identifier ABI.
- */
+ * The values cannot be changed, they are part of the nft tool type identifier ABI. */
 #define TYPE_BITS 6
 
 enum nft_key_types {
-        TYPE_IPADDR = 7,
-        TYPE_IP6ADDR = 8,
+        TYPE_IPADDR        = 7,
+        TYPE_IP6ADDR       = 8,
         TYPE_INET_PROTOCOL = 12,
-        TYPE_INET_SERVICE = 13,
+        TYPE_INET_SERVICE  = 13,
 };
 
 static uint32_t concat_types2(enum nft_key_types a, enum nft_key_types b) {
@@ -637,51 +712,38 @@ static uint32_t concat_types2(enum nft_key_types a, enum nft_key_types b) {
         return type;
 }
 
-/* enough space to hold netlink messages for table skeleton */
-#define NFT_INIT_MSGS 16
 static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
-        sd_netlink_message *batch[NFT_INIT_MSGS] = {};
-        size_t msgcnt = 0, i, ip_type_size;
+        sd_netlink_message *messages[10] = {};
+        _unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages;
+        size_t msgcnt = 0, ip_type_size;
         uint32_t set_id = 0;
         int ip_type, r;
 
+        assert(nfnl);
         assert(IN_SET(family, AF_INET, AF_INET6));
 
-        r = sd_nfnl_message_batch_begin(nfnl, &batch[msgcnt]);
-        if (r < 0)
-                goto out_unref;
-
-        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);
+        r = sd_nfnl_nft_message_new_table(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME);
         if (r < 0)
-                goto out_unref;
-
-        msgcnt++;
-        assert(msgcnt < NFT_INIT_MSGS);
+                return r;
 
-        r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME,
+        r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME,
                                               "prerouting", "nat",
                                               NF_INET_PRE_ROUTING, NF_IP_PRI_NAT_DST + 1);
         if (r < 0)
-                goto out_unref;
+                return r;
 
-        msgcnt++;
-        assert(msgcnt < NFT_INIT_MSGS);
-        r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME,
+        r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME,
                                               "output", "nat",
                                               NF_INET_LOCAL_OUT, NF_IP_PRI_NAT_DST + 1);
         if (r < 0)
-                goto out_unref;
+                return r;
 
-        msgcnt++;
-        assert(msgcnt < NFT_INIT_MSGS);
-        r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME,
+        r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME,
                                               "postrouting", "nat",
                                               NF_INET_POST_ROUTING, NF_IP_PRI_NAT_SRC + 1);
         if (r < 0)
-                goto out_unref;
+                return r;
 
         if (family == AF_INET) {
                 ip_type_size = sizeof(uint32_t);
@@ -691,12 +753,10 @@ static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
                 ip_type_size = sizeof(struct in6_addr);
                 ip_type = TYPE_IP6ADDR;
         }
-        msgcnt++;
-        assert(msgcnt < NFT_INIT_MSGS);
         /* set to store ip address ranges we should masquerade for */
-        r = nft_new_set(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_MASQ_SET_NAME, ++set_id, NFT_SET_INTERVAL, ip_type, ip_type_size);
+        r = nft_new_set(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_MASQ_SET_NAME, ++set_id, NFT_SET_INTERVAL, ip_type, ip_type_size);
         if (r < 0)
-                goto out_unref;
+                return r;
 
         /*
          * map to store ip address:port pair to dnat to.  elements in concatenation
@@ -705,54 +765,39 @@ static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
          * Example: ip protocol . tcp daddr is sizeof(uint32_t) + sizeof(uint32_t), not
          * sizeof(uint8_t) + sizeof(uint16_t).
          */
-        msgcnt++;
-        assert(msgcnt < NFT_INIT_MSGS);
-        r = nft_new_map(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_DNAT_MAP_NAME, ++set_id, 0,
+        r = nft_new_map(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_DNAT_MAP_NAME, ++set_id, 0,
                         concat_types2(TYPE_INET_PROTOCOL, TYPE_INET_SERVICE), sizeof(uint32_t) * 2,
                         concat_types2(ip_type, TYPE_INET_SERVICE), ip_type_size + sizeof(uint32_t));
         if (r < 0)
-                goto out_unref;
-
-        msgcnt++;
-        assert(msgcnt < NFT_INIT_MSGS);
-        r = sd_nfnl_message_new_dnat_rule_pre(nfnl, &batch[msgcnt], family, "prerouting");
-        if (r < 0)
-                goto out_unref;
+                return r;
 
-        msgcnt++;
-        assert(msgcnt < NFT_INIT_MSGS);
-        r = sd_nfnl_message_new_dnat_rule_out(nfnl, &batch[msgcnt], family, "output");
+        r = sd_nfnl_message_new_dnat_rule_pre(nfnl, &messages[msgcnt++], family, "prerouting");
         if (r < 0)
-                goto out_unref;
+                return r;
 
-        msgcnt++;
-        r = sd_nfnl_message_new_masq_rule(nfnl, &batch[msgcnt], family, "postrouting");
+        r = sd_nfnl_message_new_dnat_rule_out(nfnl, &messages[msgcnt++], family, "output");
         if (r < 0)
-                goto out_unref;
+                return r;
 
-        msgcnt++;
-        assert(msgcnt < NFT_INIT_MSGS);
-        r = sd_nfnl_message_batch_end(nfnl, &batch[msgcnt]);
+        r = sd_nfnl_message_new_masq_rule(nfnl, &messages[msgcnt++], family, "postrouting");
         if (r < 0)
-                goto out_unref;
-
-        msgcnt++;
-        assert(msgcnt <= NFT_INIT_MSGS);
-        r = nfnl_netlink_sendv(nfnl, batch, msgcnt);
-        if (r == -EEXIST)
-                r = 0;
+                return r;
 
-out_unref:
-        for (i = 0; i < msgcnt; i++)
-                sd_netlink_message_unref(batch[i]);
+        assert(msgcnt < ELEMENTSOF(messages));
+        r = sd_nfnl_call_batch(nfnl, messages, msgcnt, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
+        if (r < 0 && r != -EEXIST)
+                return r;
 
-        return r;
+        return 0;
 }
 
 int fw_nftables_init(FirewallContext *ctx) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *nfnl = NULL;
         int r;
 
+        assert(ctx);
+        assert(!ctx->nfnl);
+
         r = sd_nfnl_socket_open(&nfnl);
         if (r < 0)
                 return r;
@@ -772,28 +817,35 @@ int fw_nftables_init(FirewallContext *ctx) {
 }
 
 void fw_nftables_exit(FirewallContext *ctx) {
+        assert(ctx);
+
         ctx->nfnl = sd_netlink_unref(ctx->nfnl);
 }
 
-static int nft_message_add_setelem_iprange(sd_netlink_message *m,
-                                           const union in_addr_union *source,
-                                           unsigned int prefixlen) {
+static int nft_message_append_setelem_iprange(
+                sd_netlink_message *m,
+                const union in_addr_union *source,
+                unsigned int prefixlen) {
+
         uint32_t mask, start, end;
         unsigned int nplen;
         int r;
 
+        assert(m);
+        assert(source);
         assert(prefixlen <= 32);
+
         nplen = 32 - prefixlen;
 
         mask = (1U << nplen) - 1U;
         mask = htobe32(~mask);
         start = source->in.s_addr & mask;
 
-        r = sd_nfnl_nft_message_add_setelem(m, 0, &start, sizeof(start), NULL, 0);
+        r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
         if (r < 0)
                 return r;
 
-        r = sd_nfnl_nft_message_add_setelem_end(m);
+        r = sd_nfnl_nft_message_append_setelem(m, 0, &start, sizeof(start), NULL, 0, 0);
         if (r < 0)
                 return r;
 
@@ -802,22 +854,14 @@ static int nft_message_add_setelem_iprange(sd_netlink_message *m,
                 end = 0U;
         end = htobe32(end);
 
-        r = sd_nfnl_nft_message_add_setelem(m, 1, &end, sizeof(end), NULL, 0);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(NFT_SET_ELEM_INTERVAL_END));
+        r = sd_nfnl_nft_message_append_setelem(m, 1, &end, sizeof(end), NULL, 0, NFT_SET_ELEM_INTERVAL_END);
         if (r < 0)
                 return r;
 
-        r = sd_nfnl_nft_message_add_setelem_end(m);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
 }
 
-static int nft_message_add_setelem_ip6range(
+static int nft_message_append_setelem_ip6range(
                 sd_netlink_message *m,
                 const union in_addr_union *source,
                 unsigned int prefixlen) {
@@ -825,79 +869,59 @@ static int nft_message_add_setelem_ip6range(
         union in_addr_union start, end;
         int r;
 
-        r = in_addr_prefix_range(AF_INET6, source, prefixlen, &start, &end);
-        if (r < 0)
-                return r;
+        assert(m);
+        assert(source);
 
-        r = sd_nfnl_nft_message_add_setelem(m, 0, &start.in6, sizeof(start.in6), NULL, 0);
+        r = in_addr_prefix_range(AF_INET6, source, prefixlen, &start, &end);
         if (r < 0)
                 return r;
 
-        r = sd_nfnl_nft_message_add_setelem_end(m);
+        r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
         if (r < 0)
                 return r;
 
-        r = sd_nfnl_nft_message_add_setelem(m, 1, &end.in6, sizeof(end.in6), NULL, 0);
+        r = sd_nfnl_nft_message_append_setelem(m, 0, &start.in6, sizeof(start.in6), NULL, 0, 0);
         if (r < 0)
                 return r;
 
-        r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(NFT_SET_ELEM_INTERVAL_END));
+        r = sd_nfnl_nft_message_append_setelem(m, 1, &end.in6, sizeof(end.in6), NULL, 0, NFT_SET_ELEM_INTERVAL_END);
         if (r < 0)
                 return r;
 
-        return sd_nfnl_nft_message_add_setelem_end(m);
+        return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
 }
 
-#define NFT_MASQ_MSGS   3
-
 static int fw_nftables_add_masquerade_internal(
-                FirewallContext *ctx,
+                sd_netlink *nfnl,
                 bool add,
                 int af,
                 const union in_addr_union *source,
                 unsigned int source_prefixlen) {
 
-        sd_netlink_message *transaction[NFT_MASQ_MSGS] = {};
-        size_t tsize;
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
         int r;
 
+        assert(nfnl);
+        assert(IN_SET(af, AF_INET, AF_INET6));
+
         if (!source || source_prefixlen == 0)
                 return -EINVAL;
 
         if (af == AF_INET6 && source_prefixlen < 8)
                 return -EINVAL;
 
-        r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]);
+        r = sd_nfnl_nft_message_new_setelems(nfnl, &m, add, af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME);
         if (r < 0)
                 return r;
-        tsize = 1;
-        if (add)
-                r = sd_nfnl_nft_message_new_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME);
-        else
-                r = sd_nfnl_nft_message_del_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME);
-        if (r < 0)
-                goto out_unref;
 
         if (af == AF_INET)
-                 r = nft_message_add_setelem_iprange(transaction[tsize], source, source_prefixlen);
+                 r = nft_message_append_setelem_iprange(m, source, source_prefixlen);
         else
-                 r = nft_message_add_setelem_ip6range(transaction[tsize], source, source_prefixlen);
-        if (r < 0)
-                goto out_unref;
-
-        ++tsize;
-        assert(tsize < NFT_MASQ_MSGS);
-        r = sd_nfnl_message_batch_end(ctx->nfnl, &transaction[tsize]);
+                 r = nft_message_append_setelem_ip6range(m, source, source_prefixlen);
         if (r < 0)
                 return r;
 
-        ++tsize;
-        r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize);
-
-out_unref:
-        while (tsize > 0)
-                sd_netlink_message_unref(transaction[--tsize]);
-        return r < 0 ? r : 0;
+        return sd_nfnl_call_batch(nfnl, &m, 1, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
 }
 
 int fw_nftables_add_masquerade(
@@ -909,10 +933,14 @@ int fw_nftables_add_masquerade(
 
         int r;
 
+        assert(ctx);
+        assert(ctx->nfnl);
+        assert(IN_SET(af, AF_INET, AF_INET6));
+
         if (!socket_ipv6_is_supported() && af == AF_INET6)
                 return -EOPNOTSUPP;
 
-        r = fw_nftables_add_masquerade_internal(ctx, add, af, source, source_prefixlen);
+        r = fw_nftables_add_masquerade_internal(ctx->nfnl, add, af, source, source_prefixlen);
         if (r != -ENOENT)
                 return r;
 
@@ -937,13 +965,11 @@ int fw_nftables_add_masquerade(
         if (r < 0)
                 return r;
 
-        return fw_nftables_add_masquerade_internal(ctx, add, af, source, source_prefixlen);
+        return fw_nftables_add_masquerade_internal(ctx->nfnl, add, af, source, source_prefixlen);
 }
 
-#define NFT_DNAT_MSGS   4
-
 static int fw_nftables_add_local_dnat_internal(
-                FirewallContext *ctx,
+                sd_netlink *nfnl,
                 bool add,
                 int af,
                 int protocol,
@@ -952,13 +978,16 @@ static int fw_nftables_add_local_dnat_internal(
                 uint16_t remote_port,
                 const union in_addr_union *previous_remote) {
 
-        sd_netlink_message *transaction[NFT_DNAT_MSGS] = {};
+        sd_netlink_message *messages[3] = {};
+        _unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages;
         static bool ipv6_supported = true;
         uint32_t data[5], key[2], dlen;
-        size_t tsize;
+        size_t msgcnt = 0;
         int r;
 
+        assert(nfnl);
         assert(add || !previous_remote);
+        assert(IN_SET(af, AF_INET, AF_INET6));
 
         if (!ipv6_supported && af == AF_INET6)
                 return -EOPNOTSUPP;
@@ -987,11 +1016,6 @@ static int fw_nftables_add_local_dnat_internal(
                 data[4] = htobe16(remote_port);
         }
 
-        r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]);
-        if (r < 0)
-                return r;
-
-        tsize = 1;
         /* If a previous remote is set, remove its entry */
         if (add && previous_remote && !in_addr_equal(af, previous_remote, remote)) {
                 if (af == AF_INET)
@@ -999,11 +1023,9 @@ static int fw_nftables_add_local_dnat_internal(
                 else
                         memcpy(data, &previous_remote->in6, sizeof(previous_remote->in6));
 
-                r = nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
+                r = nft_del_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
                 if (r < 0)
-                        goto out_unref;
-
-                tsize++;
+                        return r;
         }
 
         if (af == AF_INET)
@@ -1011,39 +1033,27 @@ static int fw_nftables_add_local_dnat_internal(
         else
                 memcpy(data, &remote->in6, sizeof(remote->in6));
 
-        assert(tsize < NFT_DNAT_MSGS);
         if (add)
-                r = nft_add_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
+                r = nft_add_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
         else
-                r = nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
+                r = nft_del_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
         if (r < 0)
-                goto out_unref;
-
-        tsize++;
-        assert(tsize < NFT_DNAT_MSGS);
-
-        r = sd_nfnl_message_batch_end(ctx->nfnl, &transaction[tsize]);
-        if (r < 0)
-                goto out_unref;
-
-        tsize++;
-        assert(tsize <= NFT_DNAT_MSGS);
+                return r;
 
-        r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize);
+        assert(msgcnt < ELEMENTSOF(messages));
+        r = sd_nfnl_call_batch(nfnl, messages, msgcnt, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
         if (r == -EOVERFLOW && af == AF_INET6) {
                 /* The current implementation of DNAT in systemd requires kernel's
                  * fdb9c405e35bdc6e305b9b4e20ebc141ed14fc81 (v5.8), and the older kernel returns
                  * -EOVERFLOW. Let's treat the error as -EOPNOTSUPP. */
                 log_debug_errno(r, "The current implementation of IPv6 DNAT in systemd requires kernel 5.8 or newer, ignoring: %m");
                 ipv6_supported = false;
-                r = -EOPNOTSUPP;
+                return -EOPNOTSUPP;
         }
+        if (r < 0)
+                return r;
 
-out_unref:
-        while (tsize > 0)
-                sd_netlink_message_unref(transaction[--tsize]);
-
-        return r < 0 ? r : 0;
+        return 0;
 }
 
 int fw_nftables_add_local_dnat(
@@ -1058,10 +1068,14 @@ int fw_nftables_add_local_dnat(
 
         int r;
 
+        assert(ctx);
+        assert(ctx->nfnl);
+        assert(IN_SET(af, AF_INET, AF_INET6));
+
         if (!socket_ipv6_is_supported() && af == AF_INET6)
                 return -EOPNOTSUPP;
 
-        r = fw_nftables_add_local_dnat_internal(ctx, add, af, protocol, local_port, remote, remote_port, previous_remote);
+        r = fw_nftables_add_local_dnat_internal(ctx->nfnl, add, af, protocol, local_port, remote, remote_port, previous_remote);
         if (r != -ENOENT)
                 return r;
 
@@ -1071,5 +1085,5 @@ int fw_nftables_add_local_dnat(
                 return r;
 
         /* table created anew; previous address already gone */
-        return fw_nftables_add_local_dnat_internal(ctx, add, af, protocol, local_port, remote, remote_port, NULL);
+        return fw_nftables_add_local_dnat_internal(ctx->nfnl, add, af, protocol, local_port, remote, remote_port, NULL);
 }
index 12170d3642b25ee5c2e591b47aaa6af26ad7e736..b4efcf6d0bd6e54c01dc79985a046e62b9bdf245 100644 (file)
@@ -537,7 +537,7 @@ int generator_hook_up_growfs(
                 "DefaultDependencies=no\n"
                 "BindsTo=%%i.mount\n"
                 "Conflicts=shutdown.target\n"
-                "After=%%i.mount\n"
+                "After=systemd-repart.service %%i.mount\n"
                 "Before=shutdown.target%s%s\n",
                 program_invocation_short_name,
                 target ? " " : "",
index 1ec861f76fe11b2d485f0d4264b47e739ad9e40b..6a680fb3be1b7747a5a86ee577a8134f10439e68 100644 (file)
@@ -680,7 +680,7 @@ int hwdb_query(const char *modalias, const char *root) {
         return 0;
 }
 
-bool hwdb_validate(sd_hwdb *hwdb) {
+bool hwdb_should_reload(sd_hwdb *hwdb) {
         bool found = false;
         const char* p;
         struct stat st;
index bfecddea421ac3e738cca7799a6d52dbd2ac4bec..cb93690ee8ddadb5e3e410cfffbeec6ef4793082 100644 (file)
@@ -5,6 +5,6 @@
 
 #include "sd-hwdb.h"
 
-bool hwdb_validate(sd_hwdb *hwdb);
+bool hwdb_should_reload(sd_hwdb *hwdb);
 int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat);
 int hwdb_query(const char *modalias, const char *root);
index 13bc44a9edab61c5e666fb6c47476c578bc3956b..67b6b75fa8319cb9e27033fb54c24c0f6f2b70cd 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <errno.h>
 #include <locale.h>
-#include <math.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -18,6 +17,7 @@
 #include "json-internal.h"
 #include "json.h"
 #include "macro.h"
+#include "math-util.h"
 #include "memory-util.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -253,9 +253,7 @@ static JsonVariant *json_variant_formalize(JsonVariant *v) {
                 return json_variant_unsigned(v) == 0 ? JSON_VARIANT_MAGIC_ZERO_UNSIGNED : v;
 
         case JSON_VARIANT_REAL:
-                DISABLE_WARNING_FLOAT_EQUAL;
-                return json_variant_real(v) == 0.0 ? JSON_VARIANT_MAGIC_ZERO_REAL : v;
-                REENABLE_WARNING;
+                return iszero_safe(json_variant_real(v)) ? JSON_VARIANT_MAGIC_ZERO_REAL : v;
 
         case JSON_VARIANT_STRING:
                 return isempty(json_variant_string(v)) ? JSON_VARIANT_MAGIC_EMPTY_STRING : v;
@@ -352,16 +350,16 @@ int json_variant_new_real(JsonVariant **ret, double d) {
 
         assert_return(ret, -EINVAL);
 
-        DISABLE_WARNING_FLOAT_EQUAL;
-        if (d == 0.0) {
-                *ret = JSON_VARIANT_MAGIC_ZERO_REAL;
+        r = fpclassify(d);
+        switch (r) {
+        case FP_NAN:
+        case FP_INFINITE:
+                /* JSON doesn't know NaN, +Infinity or -Infinity. Let's silently convert to 'null'. */
+                *ret = JSON_VARIANT_MAGIC_NULL;
                 return 0;
-        }
-        REENABLE_WARNING;
 
-        /* JSON doesn't know NaN, +Infinity or -Infinity. Let's silently convert to 'null'. */
-        if (isnan(d) || isinf(d)) {
-                *ret = JSON_VARIANT_MAGIC_NULL;
+        case FP_ZERO:
+                *ret = JSON_VARIANT_MAGIC_ZERO_REAL;
                 return 0;
         }
 
@@ -914,10 +912,8 @@ int64_t json_variant_integer(JsonVariant *v) {
 
                 converted = (int64_t) v->value.real;
 
-                DISABLE_WARNING_FLOAT_EQUAL;
-                if ((double) converted == v->value.real)
+                if (fp_equal((double) converted, v->value.real))
                         return converted;
-                REENABLE_WARNING;
 
                 log_debug("Real %g requested as integer, and cannot be converted losslessly, returning 0.", v->value.real);
                 return 0;
@@ -961,10 +957,8 @@ uint64_t json_variant_unsigned(JsonVariant *v) {
 
                 converted = (uint64_t) v->value.real;
 
-                DISABLE_WARNING_FLOAT_EQUAL;
-                if ((double) converted == v->value.real)
+                if (fp_equal((double) converted, v->value.real))
                         return converted;
-                REENABLE_WARNING;
 
                 log_debug("Real %g requested as unsigned integer, and cannot be converted losslessly, returning 0.", v->value.real);
                 return 0;
@@ -1153,15 +1147,11 @@ bool json_variant_has_type(JsonVariant *v, JsonVariantType type) {
         if (rt == JSON_VARIANT_UNSIGNED && type == JSON_VARIANT_REAL)
                 return (uint64_t) (double) v->value.unsig == v->value.unsig;
 
-        DISABLE_WARNING_FLOAT_EQUAL;
-
         /* Any real that can be converted losslessly to an integer and back may also be considered an integer */
         if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_INTEGER)
-                return (double) (int64_t) v->value.real == v->value.real;
+                return fp_equal((double) (int64_t) v->value.real, v->value.real);
         if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_UNSIGNED)
-                return (double) (uint64_t) v->value.real == v->value.real;
-
-        REENABLE_WARNING;
+                return fp_equal((double) (uint64_t) v->value.real, v->value.real);
 
         return false;
 }
@@ -1314,9 +1304,7 @@ bool json_variant_equal(JsonVariant *a, JsonVariant *b) {
                 return json_variant_unsigned(a) == json_variant_unsigned(b);
 
         case JSON_VARIANT_REAL:
-                DISABLE_WARNING_FLOAT_EQUAL;
-                return json_variant_real(a) == json_variant_real(b);
-                REENABLE_WARNING;
+                return fp_equal(json_variant_real(a), json_variant_real(b));
 
         case JSON_VARIANT_BOOLEAN:
                 return json_variant_boolean(a) == json_variant_boolean(b);
index 530688fc97f0255eddc942b77e8793f4a4190142..a5ad914577071d7acb182779eafce2a648ddeed6 100644 (file)
@@ -768,6 +768,11 @@ void loop_device_relinquish(LoopDevice *d) {
         d->relinquished = true;
 }
 
+void loop_device_unrelinquish(LoopDevice *d) {
+        assert(d);
+        d->relinquished = false;
+}
+
 int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret) {
         _cleanup_close_ int loop_fd = -1;
         _cleanup_free_ char *p = NULL;
index 964ce3ed083c1affe098d345acbaba8dc0b954e6..a33d7e3e59d42002286b68f02afe3d131713a64e 100644 (file)
@@ -27,6 +27,7 @@ LoopDevice* loop_device_unref(LoopDevice *d);
 DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref);
 
 void loop_device_relinquish(LoopDevice *d);
+void loop_device_unrelinquish(LoopDevice *d);
 
 int loop_device_refresh_size(LoopDevice *d, uint64_t offset, uint64_t size);
 
index 598a64593b533fe122442bfd373d47085ef91cc5..426a87b70cffe9844164612cdef9c4a900e53f18 100644 (file)
@@ -85,6 +85,7 @@ shared_sources = files(
         'creds-util.h',
         'cryptsetup-util.c',
         'cryptsetup-util.h',
+        'daemon-util.c',
         'daemon-util.h',
         'data-fd-util.c',
         'data-fd-util.h',
index 8cf16affcb8f6c6cf479882f1e95c57413aee6c7..a119a7c3c1b2a49bb05713365a3caaaf5db9927f 100644 (file)
@@ -7,7 +7,9 @@
 #include <sys/statvfs.h>
 #include <unistd.h>
 #include <linux/loop.h>
+#if WANT_LINUX_FS_H
 #include <linux/fs.h>
+#endif
 
 #include "alloc-util.h"
 #include "chase-symlinks.h"
index e95072e048904ab9e56fd18510acbb891972a37a..18748e537627376c612e303cb8b24cd47bb8979c 100644 (file)
@@ -24,6 +24,7 @@ static const NamingScheme naming_schemes[] = {
         { "v249", NAMING_V249 },
         { "v250", NAMING_V250 },
         { "v251", NAMING_V251 },
+        { "v252", NAMING_V252 },
         /* … add more schemes here, as the logic to name devices is updated … */
 
         EXTRA_NET_NAMING_MAP
index 5303348e063e832474b723b4801843b24452323f..4fa917096907c305e1aa77f17dc6af4ddc747d47 100644 (file)
@@ -37,6 +37,7 @@ typedef enum NamingSchemeFlags {
         NAMING_REPLACE_STRICTLY          = 1 << 12, /* Use udev_replace_ifname() for NAME= rule */
         NAMING_XEN_VIF                   = 1 << 13, /* Generate names for Xen netfront devices */
         NAMING_BRIDGE_MULTIFUNCTION_SLOT = 1 << 14, /* Use PCI hotplug slot information associated with bridge, but only if PCI device is multifunction */
+        NAMING_DEVICETREE_ALIASES        = 1 << 15, /* Generate names from devicetree aliases */
 
         /* And now the masks that combine the features above */
         NAMING_V238 = 0,
@@ -49,6 +50,7 @@ typedef enum NamingSchemeFlags {
         NAMING_V249 = NAMING_V247 | NAMING_SLOT_FUNCTION_ID | NAMING_16BIT_INDEX | NAMING_REPLACE_STRICTLY,
         NAMING_V250 = NAMING_V249 | NAMING_XEN_VIF,
         NAMING_V251 = NAMING_V250 | NAMING_BRIDGE_MULTIFUNCTION_SLOT,
+        NAMING_V252 = NAMING_V251 | NAMING_DEVICETREE_ALIASES,
 
         EXTRA_NET_NAMING_SCHEMES
 
index 80f6cec3423694fdcca3c760cb6b587088f4b70b..998dab04910c847b784e8a6155becb70ebf08c4a 100644 (file)
@@ -14,8 +14,10 @@ pcre2_code* (*sym_pcre2_compile)(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_
 int (*sym_pcre2_get_error_message)(int, PCRE2_UCHAR *, PCRE2_SIZE);
 int (*sym_pcre2_match)(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, pcre2_match_data *, pcre2_match_context *);
 PCRE2_SIZE* (*sym_pcre2_get_ovector_pointer)(pcre2_match_data *);
+#endif
 
 int dlopen_pcre2(void) {
+#if HAVE_PCRE2
         /* So here's something weird: PCRE2 actually renames the symbols exported by the library via C
          * macros, so that the exported symbols carry a suffix "_8" but when used from C the suffix is
          * gone. In the argument list below we ignore this mangling. Surprisingly (at least to me), we
@@ -33,12 +35,123 @@ int dlopen_pcre2(void) {
                         DLSYM_ARG(pcre2_get_error_message),
                         DLSYM_ARG(pcre2_match),
                         DLSYM_ARG(pcre2_get_ovector_pointer));
+#else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
+#endif
 }
 
+int pattern_compile_and_log(const char *pattern, PatternCompileCase case_, pcre2_code **ret) {
+#if HAVE_PCRE2
+        PCRE2_SIZE erroroffset;
+        pcre2_code *p;
+        unsigned flags = 0;
+        int errorcode, r;
+
+        assert(pattern);
+
+        r = dlopen_pcre2();
+        if (r < 0)
+                return r;
+
+        if (case_ == PATTERN_COMPILE_CASE_INSENSITIVE)
+                flags = PCRE2_CASELESS;
+        else if (case_ == PATTERN_COMPILE_CASE_AUTO) {
+                _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
+                bool has_case;
+                _cleanup_(sym_pcre2_code_freep) pcre2_code *cs = NULL;
+
+                md = sym_pcre2_match_data_create(1, NULL);
+                if (!md)
+                        return log_oom();
+
+                r = pattern_compile_and_log("[[:upper:]]", PATTERN_COMPILE_CASE_SENSITIVE, &cs);
+                if (r < 0)
+                        return r;
+
+                r = sym_pcre2_match(cs, (PCRE2_SPTR8) pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
+                has_case = r >= 0;
+
+                flags = !has_case * PCRE2_CASELESS;
+        }
+
+        log_debug("Doing case %s matching based on %s",
+                  flags & PCRE2_CASELESS ? "insensitive" : "sensitive",
+                  case_ != PATTERN_COMPILE_CASE_AUTO ? "request" : "pattern casing");
+
+        p = sym_pcre2_compile((PCRE2_SPTR8) pattern,
+                              PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
+        if (!p) {
+                unsigned char buf[LINE_MAX];
+
+                r = sym_pcre2_get_error_message(errorcode, buf, sizeof buf);
+
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Bad pattern \"%s\": %s", pattern,
+                                       r < 0 ? "unknown error" : (char *)buf);
+        }
+
+        if (ret)
+                *ret = p;
+
+        return 0;
 #else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
+#endif
+}
 
-int dlopen_pcre2(void) {
-        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
-                               "PCRE2 support is not compiled in.");
+int pattern_matches_and_log(pcre2_code *compiled_pattern, const char *message, size_t size, size_t *ret_ovec) {
+#if HAVE_PCRE2
+        _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
+        int r;
+
+        assert(compiled_pattern);
+        assert(message);
+        /* pattern_compile_and_log() must be called before this function is called and that function already
+         * dlopens pcre2 so we can assert on it being available here. */
+        assert(pcre2_dl);
+
+        md = sym_pcre2_match_data_create(1, NULL);
+        if (!md)
+                return log_oom();
+
+        r = sym_pcre2_match(compiled_pattern,
+                            (const unsigned char *)message,
+                            size,
+                            0,      /* start at offset 0 in the subject */
+                            0,      /* default options */
+                            md,
+                            NULL);
+        if (r == PCRE2_ERROR_NOMATCH)
+                return false;
+        if (r < 0) {
+                unsigned char buf[LINE_MAX];
+
+                r = sym_pcre2_get_error_message(r, buf, sizeof(buf));
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Pattern matching failed: %s",
+                                       r < 0 ? "unknown error" : (char*) buf);
+        }
+
+        if (ret_ovec) {
+                ret_ovec[0] = sym_pcre2_get_ovector_pointer(md)[0];
+                ret_ovec[1] = sym_pcre2_get_ovector_pointer(md)[1];
+        }
+
+        return true;
+#else
+        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
+#endif
 }
+
+void *pattern_free(pcre2_code *p) {
+#if HAVE_PCRE2
+        if (!p)
+                return NULL;
+
+        assert(pcre2_dl);
+        sym_pcre2_code_free(p);
+        return NULL;
+#else
+        assert(p == NULL);
+        return NULL;
 #endif
+}
index f17dcd5573484e9f5d7d473bcd4b7a5879f1c972..11f1d77f4f28d795ac81ff523862870769f1585e 100644 (file)
@@ -18,6 +18,24 @@ extern PCRE2_SIZE* (*sym_pcre2_get_ovector_pointer)(pcre2_match_data *);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(pcre2_match_data*, sym_pcre2_match_data_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(pcre2_code*, sym_pcre2_code_free, NULL);
+#else
+
+typedef struct {} pcre2_code;
+
 #endif
 
+typedef enum {
+        PATTERN_COMPILE_CASE_AUTO,
+        PATTERN_COMPILE_CASE_SENSITIVE,
+        PATTERN_COMPILE_CASE_INSENSITIVE,
+        _PATTERN_COMPILE_CASE_MAX,
+        _PATTERN_COMPILE_CASE_INVALID = -EINVAL,
+} PatternCompileCase;
+
+int pattern_compile_and_log(const char *pattern, PatternCompileCase case_, pcre2_code **ret);
+int pattern_matches_and_log(pcre2_code *compiled_pattern, const char *message, size_t size, size_t *ret_ovec);
+void *pattern_free(pcre2_code *p);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pattern_free);
+
 int dlopen_pcre2(void);
index a56f2ff618271cb3f6a507c6861553cb60792ef0..d9923e9de8fb367d01fe68cc8814b9338c4cc93d 100644 (file)
 #include <syslog.h>
 #include <unistd.h>
 
+#include "sd-device.h"
+
 #include "alloc-util.h"
 #include "blockdev-util.h"
 #include "btrfs-util.h"
 #include "conf-parser.h"
 #include "def.h"
+#include "device-util.h"
 #include "devnum-util.h"
 #include "env-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "hexdecoct.h"
+#include "id128-util.h"
 #include "log.h"
 #include "macro.h"
 #include "path-util.h"
 #include "sleep-config.h"
+#include "siphash24.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-table.h"
 #include "strv.h"
 #include "time-util.h"
 
+#define BATTERY_LOW_CAPACITY_LEVEL 5
+#define DISCHARGE_RATE_FILEPATH "/var/lib/systemd/sleep/battery_discharge_percentage_rate_per_hour"
+#define BATTERY_DISCHARGE_RATE_HASH_KEY SD_ID128_MAKE(5f,9a,20,18,38,76,46,07,8d,36,58,0b,bb,c4,e0,63)
+
+static void *CAPACITY_TO_PTR(int capacity) {
+        assert(capacity >= 0);
+        assert(capacity <= 100);
+        return INT_TO_PTR(capacity + 1);
+}
+
+static int PTR_TO_CAPACITY(void *p) {
+        int capacity = PTR_TO_INT(p) - 1;
+        assert(capacity >= 0);
+        assert(capacity <= 100);
+        return capacity;
+}
+
 int parse_sleep_config(SleepConfig **ret_sleep_config) {
         _cleanup_(free_sleep_configp) SleepConfig *sc = NULL;
         int allow_suspend = -1, allow_hibernate = -1,
@@ -102,6 +125,407 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) {
         return 0;
 }
 
+/* Get the list of batteries */
+static int battery_enumerator_new(sd_device_enumerator **ret) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        int r;
+
+        assert(ret);
+
+        r = sd_device_enumerator_new(&e);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_subsystem(e, "power_supply", /* match= */ true);
+        if (r < 0)
+                return r;
+
+        r = sd_device_enumerator_add_match_property(e, "POWER_SUPPLY_TYPE", "Battery");
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(e);
+
+        return 0;
+}
+
+static int get_capacity_by_name(Hashmap *capacities_by_name, const char *name) {
+        void *p;
+
+        assert(capacities_by_name);
+        assert(name);
+
+        p = hashmap_get(capacities_by_name, name);
+        if (!p)
+                return -ENOENT;
+
+        return PTR_TO_CAPACITY(p);
+}
+
+/* Battery percentage capacity fetched from capacity file and if in range 0-100 then returned */
+static int read_battery_capacity_percentage(sd_device *dev) {
+        const char *power_supply_capacity;
+        int battery_capacity, r;
+
+        assert(dev);
+
+        r = sd_device_get_property_value(dev, "POWER_SUPPLY_CAPACITY", &power_supply_capacity);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to read battery capacity: %m");
+
+        r = safe_atoi(power_supply_capacity, &battery_capacity);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to parse battery capacity: %m");
+
+        if (battery_capacity < 0 || battery_capacity > 100)
+                return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ERANGE), "Invalid battery capacity");
+
+        return battery_capacity;
+}
+
+/* If battery percentage capacity is less than equal to 5% return success */
+int battery_is_low(void) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *dev;
+        int r;
+
+         /* We have not used battery capacity_level since value is set to full
+         * or Normal in case acpi is not working properly. In case of no battery
+         * 0 will be returned and system will be suspended for 1st cycle then hibernated */
+
+        r = battery_enumerator_new(&e);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+
+        FOREACH_DEVICE(e, dev) {
+                r = read_battery_capacity_percentage(dev);
+                if (r < 0) {
+                        log_device_debug_errno(dev, r, "Failed to get battery capacity, ignoring: %m");
+                        continue;
+                }
+                if (r > BATTERY_LOW_CAPACITY_LEVEL)
+                        return false;
+        }
+
+        return true;
+}
+
+/* Store current capacity of each battery before suspension and timestamp */
+int fetch_batteries_capacity_by_name(Hashmap **ret) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        _cleanup_(hashmap_freep) Hashmap *batteries_capacity_by_name = NULL;
+        sd_device *dev;
+        int r;
+
+        assert(ret);
+
+        batteries_capacity_by_name = hashmap_new(&string_hash_ops_free);
+        if (!batteries_capacity_by_name)
+                return log_oom_debug();
+
+        r = battery_enumerator_new(&e);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+
+        FOREACH_DEVICE(e, dev) {
+                _cleanup_free_ char *battery_name_copy = NULL;
+                const char *battery_name;
+                int battery_capacity;
+
+                battery_capacity = r = read_battery_capacity_percentage(dev);
+                if (r < 0) {
+                        log_device_debug_errno(dev, r, "Failed to get battery capacity, ignoring: %m");
+                        continue;
+                }
+
+                r = sd_device_get_property_value(dev, "POWER_SUPPLY_NAME", &battery_name);
+                if (r < 0) {
+                        log_device_debug_errno(dev, r, "Failed to read battery name, ignoring: %m");
+                        continue;
+                }
+
+                battery_name_copy = strdup(battery_name);
+                if (!battery_name_copy)
+                        return log_oom_debug();
+
+                r = hashmap_put(batteries_capacity_by_name, battery_name_copy, CAPACITY_TO_PTR(battery_capacity));
+                if (r < 0)
+                        return log_device_debug_errno(dev, r, "Failed to store battery capacity: %m");
+
+                TAKE_PTR(battery_name_copy);
+        }
+
+        *ret = TAKE_PTR(batteries_capacity_by_name);
+
+        return 0;
+}
+
+/* Read file path and return hash of value in that file */
+static int get_battery_identifier(sd_device *dev, const char *property, struct siphash *state) {
+        const char *x;
+        int r;
+
+        assert(dev);
+        assert(property);
+        assert(state);
+
+        r = sd_device_get_property_value(dev, property, &x);
+        if (r == -ENOENT)
+               log_device_debug_errno(dev, r, "battery device property %s is unavailable, ignoring: %m", property);
+        else if (r < 0)
+               return log_device_debug_errno(dev, r, "Failed to read battery device property %s: %m", property);
+        else if (isempty(x))
+               log_device_debug(dev, "battery device property '%s' is null.", property);
+        else
+               siphash24_compress_string(x, state);
+
+        return 0;
+}
+
+/* Read system and battery identifier from specific location and generate hash of it */
+static int get_system_battery_identifier_hash(sd_device *dev, uint64_t *ret) {
+        struct siphash state;
+        sd_id128_t machine_id, product_id;
+        int r;
+
+        assert(ret);
+        assert(dev);
+
+        siphash24_init(&state, BATTERY_DISCHARGE_RATE_HASH_KEY.bytes);
+
+        get_battery_identifier(dev, "POWER_SUPPLY_MANUFACTURER", &state);
+        get_battery_identifier(dev, "POWER_SUPPLY_MODEL_NAME", &state);
+        get_battery_identifier(dev, "POWER_SUPPLY_SERIAL_NUMBER", &state);
+
+        r = sd_id128_get_machine(&machine_id);
+        if (r == -ENOENT)
+               log_debug_errno(r, "machine ID is unavailable: %m");
+        else if (r < 0)
+               return log_debug_errno(r, "Failed to get machine ID: %m");
+        else
+               siphash24_compress(&machine_id, sizeof(sd_id128_t), &state);
+
+        r = id128_get_product(&product_id);
+        if (r == -ENOENT)
+               log_debug_errno(r, "product_id does not exist: %m");
+        else if (r < 0)
+               return log_debug_errno(r, "Failed to get product ID: %m");
+        else
+               siphash24_compress(&product_id, sizeof(sd_id128_t), &state);
+
+        *ret = siphash24_finalize(&state);
+
+        return 0;
+}
+
+/* battery percentage discharge rate per hour is in range 1-199 then return success */
+static bool battery_discharge_rate_is_valid(int battery_discharge_rate) {
+        return battery_discharge_rate > 0 && battery_discharge_rate < 200;
+}
+
+/* Battery percentage discharge rate per hour is read from specific file. It is stored along with system
+ * and battery identifier hash to maintain the integrity of discharge rate value */
+static int get_battery_discharge_rate(sd_device *dev, int *ret) {
+        _cleanup_fclose_ FILE *f = NULL;
+        uint64_t current_hash_id;
+        const char *p;
+        int r;
+
+        assert(dev);
+        assert(ret);
+
+        f = fopen(DISCHARGE_RATE_FILEPATH, "re");
+        if (!f)
+                return log_debug_errno(errno, "Failed to read discharge rate from " DISCHARGE_RATE_FILEPATH ": %m");
+
+        r = get_system_battery_identifier_hash(dev, &current_hash_id);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to generate system battery identifier hash: %m");
+
+        for (;;) {
+                _cleanup_free_ char *stored_hash_id = NULL, *stored_discharge_rate = NULL, *line = NULL;
+                uint64_t hash_id;
+                int discharge_rate;
+
+                r = read_line(f, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to read discharge rate from " DISCHARGE_RATE_FILEPATH ": %m");
+                if (r == 0)
+                        break;
+
+                p = line;
+                r = extract_many_words(&p, NULL, 0, &stored_hash_id, &stored_discharge_rate, NULL);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to parse hash_id and discharge_rate read from " DISCHARGE_RATE_FILEPATH ": %m");
+                if (r != 2)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid number of items fetched from " DISCHARGE_RATE_FILEPATH);
+
+                r = safe_atou64(stored_hash_id, &hash_id);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to parse hash ID read from " DISCHARGE_RATE_FILEPATH " location: %m");
+
+                if (current_hash_id != hash_id)
+                        /* matching device not found, move to next line */
+                        continue;
+
+                r = safe_atoi(stored_discharge_rate, &discharge_rate);
+                if (r < 0)
+                        return log_device_debug_errno(dev, r, "Failed to parse discharge rate read from " DISCHARGE_RATE_FILEPATH ": %m");
+
+                if (!battery_discharge_rate_is_valid(discharge_rate))
+                        return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ERANGE), "Invalid battery discharge percentage rate per hour: %m");
+
+                *ret = discharge_rate;
+                return 0; /* matching device found, exit iteration */
+        }
+
+        return -ENOENT;
+}
+
+/* Write battery percentage discharge rate per hour along with system and battery identifier hash to file */
+static int put_battery_discharge_rate(int estimated_battery_discharge_rate, uint64_t system_hash_id, bool trunc) {
+        int r;
+
+        if (!battery_discharge_rate_is_valid(estimated_battery_discharge_rate))
+                return log_debug_errno(SYNTHETIC_ERRNO(ERANGE),
+                                        "Invalid battery discharge rate %d%% per hour: %m",
+                                        estimated_battery_discharge_rate);
+
+        r = write_string_filef(
+                DISCHARGE_RATE_FILEPATH,
+                WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_MKDIR_0755 | (trunc ? WRITE_STRING_FILE_TRUNCATE : 0),
+                "%"PRIu64" %d",
+                system_hash_id,
+                estimated_battery_discharge_rate);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to update %s: %m", DISCHARGE_RATE_FILEPATH);
+
+        log_debug("Estimated discharge rate %d%% per hour successfully saved to %s", estimated_battery_discharge_rate, DISCHARGE_RATE_FILEPATH);
+
+        return 0;
+}
+
+/* Estimate battery discharge rate using stored previous and current capacity over timestamp difference */
+int estimate_battery_discharge_rate_per_hour(
+                Hashmap *last_capacity,
+                Hashmap *current_capacity,
+                usec_t before_timestamp,
+                usec_t after_timestamp) {
+
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        sd_device *dev;
+        bool trunc = true;
+        int r;
+
+        assert(last_capacity);
+        assert(current_capacity);
+        assert(before_timestamp < after_timestamp);
+
+        r = battery_enumerator_new(&e);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+
+        FOREACH_DEVICE(e, dev) {
+                int battery_last_capacity, battery_current_capacity, battery_discharge_rate;
+                const char *battery_name;
+                uint64_t system_hash_id;
+
+                r = sd_device_get_property_value(dev, "POWER_SUPPLY_NAME", &battery_name);
+                if (r < 0) {
+                        log_device_debug_errno(dev, r, "Failed to read battery name, ignoring: %m");
+                        continue;
+                }
+
+                battery_last_capacity = get_capacity_by_name(last_capacity, battery_name);
+                if (battery_last_capacity < 0)
+                        continue;
+
+                battery_current_capacity = get_capacity_by_name(current_capacity, battery_name);
+                if (battery_current_capacity < 0)
+                        continue;
+
+                if (battery_current_capacity >= battery_last_capacity) {
+                        log_device_debug(dev, "Battery was not discharged during suspension");
+                        continue;
+                }
+
+                r = get_system_battery_identifier_hash(dev, &system_hash_id);
+                if (r < 0)
+                        return log_device_debug_errno(dev, r, "Failed to generate system battery identifier hash: %m");
+
+                log_device_debug(dev,
+                                 "%d%% was discharged in %s. Estimating discharge rate...",
+                                 battery_last_capacity - battery_current_capacity,
+                                 FORMAT_TIMESPAN(after_timestamp - before_timestamp, USEC_PER_SEC));
+
+                battery_discharge_rate = (battery_last_capacity - battery_current_capacity) * USEC_PER_HOUR / (after_timestamp - before_timestamp);
+                r = put_battery_discharge_rate(battery_discharge_rate, system_hash_id, trunc);
+                if (r < 0)
+                        log_device_warning_errno(dev, r, "Failed to update battery discharge rate, ignoring: %m");
+                else
+                        trunc = false;
+        }
+
+        return 0;
+}
+
+/* calculate the suspend interval for each battery and then return the sum of it */
+int get_total_suspend_interval(Hashmap *last_capacity, usec_t *ret) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        usec_t total_suspend_interval = 0;
+        sd_device *dev;
+        int r;
+
+        assert(last_capacity);
+        assert(ret);
+
+        r = battery_enumerator_new(&e);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+
+        FOREACH_DEVICE(e, dev) {
+                int battery_last_capacity, previous_discharge_rate = 0;
+                const char *battery_name;
+                usec_t suspend_interval;
+
+                r = sd_device_get_property_value(dev, "POWER_SUPPLY_NAME", &battery_name);
+                if (r < 0) {
+                        log_device_debug_errno(dev, r, "Failed to read battery name, ignoring: %m");
+                        continue;
+                }
+
+                battery_last_capacity = PTR_TO_CAPACITY(hashmap_get(last_capacity, battery_name));
+                if (battery_last_capacity <= 0)
+                        continue;
+
+                r = get_battery_discharge_rate(dev, &previous_discharge_rate);
+                if (r < 0) {
+                        log_device_debug_errno(dev, r, "Failed to get discharge rate, ignoring: %m");
+                        continue;
+                }
+
+                if (previous_discharge_rate == 0)
+                        continue;
+
+                if (battery_last_capacity * 2 <= previous_discharge_rate) {
+                        log_device_debug(dev, "Current battery capacity percentage too low compared to discharge rate");
+                        continue;
+                }
+                suspend_interval = battery_last_capacity * USEC_PER_HOUR / previous_discharge_rate;
+
+                total_suspend_interval = usec_add(total_suspend_interval, suspend_interval);
+        }
+        /* The previous discharge rate is stored in per hour basis so converted to minutes.
+         * Subtracted 30 minutes from the result to keep a buffer of 30 minutes before battery gets critical */
+        total_suspend_interval = usec_sub_unsigned(total_suspend_interval, 30 * USEC_PER_MINUTE);
+        if (total_suspend_interval == 0)
+                return -ENOENT;
+
+        *ret = total_suspend_interval;
+
+        return 0;
+}
+
 int can_sleep_state(char **types) {
         _cleanup_free_ char *text = NULL;
         int r;
index c049a55ad6d0ce4af87eeb638907db036c43ebb2..54fe65007ede17aa0eaf3baf659411f1e34d7e92 100644 (file)
@@ -2,6 +2,8 @@
 #pragma once
 
 #include <linux/fiemap.h>
+
+#include "hashmap.h"
 #include "time-util.h"
 
 typedef enum SleepOperation {
@@ -55,6 +57,14 @@ int find_hibernate_location(HibernateLocation **ret_hibernate_location);
 int can_sleep(SleepOperation operation);
 int can_sleep_disk(char **types);
 int can_sleep_state(char **types);
+int battery_is_low(void);
+int get_total_suspend_interval(Hashmap *last_capacity, usec_t *ret);
+int fetch_batteries_capacity_by_name(Hashmap **ret_current_capacity);
+int estimate_battery_discharge_rate_per_hour(
+                Hashmap *last_capacity,
+                Hashmap *current_capacity,
+                usec_t before_timestamp,
+                usec_t after_timestamp);
 
 const char* sleep_operation_to_string(SleepOperation s) _const_;
 SleepOperation sleep_operation_from_string(const char *s) _pure_;
index f0a0f5f3151c52035b69dfafe2b033f1786b00b4..9ffee5a5eb6962dd80bd432df52f0ae75b9d418d 100644 (file)
@@ -268,11 +268,7 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
         return 0;
 }
 
-int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
-        return 0;
-}
-
-int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) {
+int mac_smack_fix_full(int atfd, const char *inode_path, const char *label_path, LabelFixFlags flags) {
         return 0;
 }
 
index abcedde8a89d53ab7250e9f585af1d33340f3d2a..f88272db7b052c9eea89f1cd4825f78a32ee962c 100644 (file)
@@ -225,9 +225,12 @@ static int tpm2_init(const char *device, struct tpm2_context *ret) {
         return 0;
 }
 
+#define TPM2_CREDIT_RANDOM_FLAG_PATH "/run/systemd/tpm-rng-credited"
+
 static int tpm2_credit_random(ESYS_CONTEXT *c) {
         size_t rps, done = 0;
         TSS2_RC rc;
+        usec_t t;
         int r;
 
         assert(c);
@@ -237,6 +240,16 @@ static int tpm2_credit_random(ESYS_CONTEXT *c) {
          * but likely better. Note that we don't trust the TPM RNG very much, hence do not actually credit
          * any entropy. */
 
+        if (access(TPM2_CREDIT_RANDOM_FLAG_PATH, F_OK) < 0) {
+                if (errno != ENOENT)
+                        log_debug_errno(errno, "Failed to detect if '" TPM2_CREDIT_RANDOM_FLAG_PATH "' exists, ignoring: %m");
+        } else {
+                log_debug("Not adding TPM2 entropy to the kernel random pool again.");
+                return 0; /* Already done */
+        }
+
+        t = now(CLOCK_MONOTONIC);
+
         for (rps = random_pool_size(); rps > 0;) {
                 _cleanup_(Esys_Freep) TPM2B_DIGEST *buffer = NULL;
 
@@ -255,7 +268,7 @@ static int tpm2_credit_random(ESYS_CONTEXT *c) {
                         return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                                "Zero-sized entropy returned from TPM.");
 
-                r = random_write_entropy(-1, buffer->buffer, buffer->size, false);
+                r = random_write_entropy(-1, buffer->buffer, buffer->size, /* credit= */ false);
                 if (r < 0)
                         return log_error_errno(r, "Failed wo write entropy to kernel: %m");
 
@@ -263,7 +276,12 @@ static int tpm2_credit_random(ESYS_CONTEXT *c) {
                 rps = LESS_BY(rps, buffer->size);
         }
 
-        log_debug("Added %zu bytes of entropy to the kernel random pool.", done);
+        log_debug("Added %zu bytes of TPM2 entropy to the kernel random pool in %s.", done, FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - t, 0));
+
+        r = touch(TPM2_CREDIT_RANDOM_FLAG_PATH);
+        if (r < 0)
+                log_debug_errno(r, "Failed to touch '" TPM2_CREDIT_RANDOM_FLAG_PATH "', ignoring: %m");
+
         return 0;
 }
 
@@ -280,17 +298,15 @@ static int tpm2_make_primary(
                         .type = TPM2_ALG_ECC,
                         .nameAlg = TPM2_ALG_SHA256,
                         .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
-                        .parameters = {
-                                .eccDetail = {
-                                        .symmetric = {
-                                                .algorithm = TPM2_ALG_AES,
-                                                .keyBits.aes = 128,
-                                                .mode.aes = TPM2_ALG_CFB,
-                                        },
-                                        .scheme.scheme = TPM2_ALG_NULL,
-                                        .curveID = TPM2_ECC_NIST_P256,
-                                        .kdf.scheme = TPM2_ALG_NULL,
+                        .parameters.eccDetail = {
+                                .symmetric = {
+                                        .algorithm = TPM2_ALG_AES,
+                                        .keyBits.aes = 128,
+                                        .mode.aes = TPM2_ALG_CFB,
                                 },
+                                .scheme.scheme = TPM2_ALG_NULL,
+                                .curveID = TPM2_ECC_NIST_P256,
+                                .kdf.scheme = TPM2_ALG_NULL,
                         },
                 },
         };
@@ -300,16 +316,14 @@ static int tpm2_make_primary(
                         .type = TPM2_ALG_RSA,
                         .nameAlg = TPM2_ALG_SHA256,
                         .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
-                        .parameters = {
-                                .rsaDetail = {
-                                        .symmetric = {
-                                                .algorithm = TPM2_ALG_AES,
-                                                .keyBits.aes = 128,
-                                                .mode.aes = TPM2_ALG_CFB,
-                                        },
-                                        .scheme.scheme = TPM2_ALG_NULL,
-                                        .keyBits = 2048,
+                        .parameters.rsaDetail = {
+                                .symmetric = {
+                                        .algorithm = TPM2_ALG_AES,
+                                        .keyBits.aes = 128,
+                                        .mode.aes = TPM2_ALG_CFB,
                                 },
+                                .scheme.scheme = TPM2_ALG_NULL,
+                                .keyBits = 2048,
                         },
                 },
         };
@@ -393,7 +407,7 @@ static int tpm2_make_primary(
         return 0;
 }
 
-static void tpm2_pcr_mask_to_selecion(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret) {
+static void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret) {
         assert(ret);
 
         /* We only do 24bit here, as that's what PC TPMs are supposed to support */
@@ -401,11 +415,13 @@ static void tpm2_pcr_mask_to_selecion(uint32_t mask, uint16_t bank, TPML_PCR_SEL
 
         *ret = (TPML_PCR_SELECTION) {
                 .count = 1,
-                .pcrSelections[0].hash = bank,
-                .pcrSelections[0].sizeofSelect = 3,
-                .pcrSelections[0].pcrSelect[0] = mask & 0xFF,
-                .pcrSelections[0].pcrSelect[1] = (mask >> 8) & 0xFF,
-                .pcrSelections[0].pcrSelect[2] = (mask >> 16) & 0xFF,
+                .pcrSelections[0] = {
+                        .hash = bank,
+                        .sizeofSelect = 3,
+                        .pcrSelect[0] = mask & 0xFF,
+                        .pcrSelect[1] = (mask >> 8) & 0xFF,
+                        .pcrSelect[2] = (mask >> 16) & 0xFF,
+                }
         };
 }
 
@@ -447,7 +463,7 @@ static int tpm2_pcr_mask_good(
          * actually measure into them, or only into a suboptimal bank. If so, the PCRs should be all zero or
          * all 0xFF. Detect that, so that we can warn and maybe pick a better bank. */
 
-        tpm2_pcr_mask_to_selecion(mask, bank, &selection);
+        tpm2_pcr_mask_to_selection(mask, bank, &selection);
 
         rc = sym_Esys_PCR_Read(
                         c,
@@ -602,17 +618,13 @@ static int tpm2_get_best_pcr_bank(
 
 static int tpm2_make_encryption_session(
                 ESYS_CONTEXT *c,
-                ESYS_TR tpmKey,
+                ESYS_TR primary,
                 ESYS_TR *ret_session) {
 
         static const TPMT_SYM_DEF symmetric = {
                 .algorithm = TPM2_ALG_AES,
-                .keyBits = {
-                        .aes = 128,
-                },
-                .mode = {
-                        .aes = TPM2_ALG_CFB,
-                },
+                .keyBits.aes = 128,
+                .mode.aes = TPM2_ALG_CFB,
         };
         const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
                         TPMA_SESSION_CONTINUESESSION;
@@ -628,7 +640,7 @@ static int tpm2_make_encryption_session(
          * recover the salt, which is then used for key derivation. */
         rc = sym_Esys_StartAuthSession(
                         c,
-                        tpmKey,
+                        primary,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
@@ -663,8 +675,9 @@ static int tpm2_make_encryption_session(
 
 static int tpm2_make_pcr_session(
                 ESYS_CONTEXT *c,
-                ESYS_TR tpmKey,
+                ESYS_TR primary,
                 ESYS_TR parent_session,
+                TPM2_SE session_type,
                 uint32_t pcr_mask,
                 uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
                 bool use_pin,
@@ -674,12 +687,8 @@ static int tpm2_make_pcr_session(
 
         static const TPMT_SYM_DEF symmetric = {
                 .algorithm = TPM2_ALG_AES,
-                .keyBits = {
-                        .aes = 128
-                },
-                .mode = {
-                        .aes = TPM2_ALG_CFB,
-                }
+                .keyBits.aes = 128,
+                .mode.aes = TPM2_ALG_CFB,
         };
         _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
         TPML_PCR_SELECTION pcr_selection;
@@ -698,7 +707,7 @@ static int tpm2_make_pcr_session(
                 if (r == 0)
                         log_notice("Selected TPM2 PCRs are not initialized on this system, most likely due to a firmware issue. PCR policy is effectively not enforced. Proceeding anyway.");
 
-                tpm2_pcr_mask_to_selecion(pcr_mask, pcr_bank, &pcr_selection);
+                tpm2_pcr_mask_to_selection(pcr_mask, pcr_bank, &pcr_selection);
         } else {
                 TPMI_ALG_HASH h;
 
@@ -708,18 +717,18 @@ static int tpm2_make_pcr_session(
                 if (r < 0)
                         return r;
 
-                tpm2_pcr_mask_to_selecion(pcr_mask, h, &pcr_selection);
+                tpm2_pcr_mask_to_selection(pcr_mask, h, &pcr_selection);
         }
 
         rc = sym_Esys_StartAuthSession(
                         c,
-                        tpmKey,
+                        primary,
                         ESYS_TR_NONE,
                         parent_session,
                         ESYS_TR_NONE,
                         ESYS_TR_NONE,
                         NULL,
-                        TPM2_SE_POLICY,
+                        session_type,
                         &symmetric,
                         TPM2_ALG_SHA256,
                         &session);
@@ -888,8 +897,17 @@ int tpm2_seal(
         if (r < 0)
                 goto finish;
 
-        r = tpm2_make_pcr_session(c.esys_context, primary, session, pcr_mask, UINT16_MAX, !!pin, NULL,
-                                  &policy_digest, &pcr_bank);
+        r = tpm2_make_pcr_session(
+                        c.esys_context,
+                        primary,
+                        session,
+                        TPM2_SE_TRIAL,
+                        pcr_mask,
+                        /* pcr_bank= */ UINT16_MAX,
+                        !!pin,
+                        /* ret_session= */ NULL,
+                        &policy_digest,
+                        &pcr_bank);
         if (r < 0)
                 goto finish;
 
@@ -902,16 +920,8 @@ int tpm2_seal(
                         .type = TPM2_ALG_KEYEDHASH,
                         .nameAlg = TPM2_ALG_SHA256,
                         .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
-                        .parameters = {
-                                .keyedHashDetail = {
-                                        .scheme.scheme = TPM2_ALG_NULL,
-                                },
-                        },
-                        .unique = {
-                                .keyedHash = {
-                                        .size = 32,
-                                },
-                        },
+                        .parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
+                        .unique.keyedHash.size = 32,
                         .authPolicy = *policy_digest,
                 },
         };
@@ -1101,8 +1111,17 @@ int tpm2_unseal(
         if (r < 0)
                 goto finish;
 
-        r = tpm2_make_pcr_session(c.esys_context, primary, hmac_session, pcr_mask, pcr_bank, !!pin, &session,
-                                  &policy_digest, NULL);
+        r = tpm2_make_pcr_session(
+                        c.esys_context,
+                        primary,
+                        hmac_session,
+                        TPM2_SE_POLICY,
+                        pcr_mask,
+                        pcr_bank,
+                        !!pin,
+                        &session,
+                        &policy_digest,
+                        /* ret_pcr_bank= */ NULL);
         if (r < 0)
                 goto finish;
 
index 5eed3d95fde5f6900116a4a93b03ba8059b2d9c3..0c6dea4264d8119f2d2b0c62da06a10054a58e95 100644 (file)
@@ -29,7 +29,7 @@
 
 static const char* const resolve_name_timing_table[_RESOLVE_NAME_TIMING_MAX] = {
         [RESOLVE_NAME_NEVER] = "never",
-        [RESOLVE_NAME_LATE] = "late",
+        [RESOLVE_NAME_LATE]  = "late",
         [RESOLVE_NAME_EARLY] = "early",
 };
 
@@ -661,7 +661,7 @@ static int device_is_power_sink(sd_device *device) {
 
 int on_ac_power(void) {
         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
-        bool found_offline = false, found_online = false;
+        bool found_offline = false, found_online = false, found_battery = false;
         sd_device *d;
         int r;
 
@@ -692,6 +692,7 @@ int on_ac_power(void) {
                  * for defined power source types. Also see:
                  * https://docs.kernel.org/admin-guide/abi-testing.html#abi-file-testing-sysfs-class-power */
                 if (streq(val, "Battery")) {
+                        found_battery = true;
                         log_device_debug(d, "The power supply is battery, ignoring.");
                         continue;
                 }
@@ -732,10 +733,12 @@ int on_ac_power(void) {
                 log_debug("Found at least one online non-battery power supply, system is running on AC power.");
         else if (!found_offline)
                 log_debug("Found no offline non-battery power supply, assuming system is running on AC power.");
+        else if (!found_battery)
+                log_debug("Found no battery, assuming system is running on AC power.");
         else
                 log_debug("All non-battery power supplies are offline, assuming system is running with battery.");
 
-        return found_online || !found_offline;
+        return found_online || !found_offline || !found_battery;
 }
 
 bool udev_available(void) {
index 95895a8e458dc2b645dab62c761fcda2f172e3e1..4e46a2fe3fbad1be0bd42616612ec49bf6b94097 100644 (file)
@@ -276,7 +276,9 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
         if (hr->memory_max != UINT64_MAX)
                 printf("  Memory Max: %s\n", FORMAT_BYTES(hr->memory_max));
 
-        if (hr->cpu_weight != UINT64_MAX)
+        if (hr->cpu_weight == CGROUP_WEIGHT_IDLE)
+                printf("  CPU Weight: %s\n", "idle");
+        else if (hr->cpu_weight != UINT64_MAX)
                 printf("  CPU Weight: %" PRIu64 "\n", hr->cpu_weight);
 
         if (hr->io_weight != UINT64_MAX)
index eee956d496317757a9d24712cc0a9fcc446702e4..af9697526d38eeb725b809bebce2309dac233162 100644 (file)
@@ -20,6 +20,7 @@
 #include "cgroup-setup.h"
 #include "cgroup-util.h"
 #include "def.h"
+#include "errno-util.h"
 #include "exec-util.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -331,10 +332,13 @@ static void init_watchdog(void) {
 }
 
 int main(int argc, char *argv[]) {
+        static const char* const dirs[] = {
+                SYSTEM_SHUTDOWN_PATH,
+                NULL
+        };
         _cleanup_free_ char *cgroup = NULL;
         char *arguments[3];
         int cmd, r;
-        static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
 
         /* The log target defaults to console, but the original systemd process will pass its log target in through a
          * command line argument, which will override this default. Also, ensure we'll never log to the journal or
@@ -356,8 +360,7 @@ int main(int argc, char *argv[]) {
         umask(0022);
 
         if (getpid_cached() != 1) {
-                log_error("Not executed by init (PID 1).");
-                r = -EPERM;
+                r = log_error_errno(SYNTHETIC_ERRNO(EPERM), "Not executed by init (PID 1).");
                 goto error;
         }
 
@@ -372,8 +375,7 @@ int main(int argc, char *argv[]) {
         else if (streq(arg_verb, "exit"))
                 cmd = 0; /* ignored, just checking that arg_verb is valid */
         else {
-                log_error("Unknown action '%s'.", arg_verb);
-                r = -EINVAL;
+                r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown action '%s'.", arg_verb);
                 goto error;
         }
 
@@ -423,10 +425,8 @@ int main(int argc, char *argv[]) {
 
                 (void) watchdog_ping();
 
-                /* Let's trim the cgroup tree on each iteration so
-                   that we leave an empty cgroup tree around, so that
-                   container managers get a nice notify event when we
-                   are down */
+                /* Let's trim the cgroup tree on each iteration so that we leave an empty cgroup tree around,
+                 * so that container managers get a nice notify event when we are down */
                 if (cgroup)
                         (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
 
@@ -506,8 +506,7 @@ int main(int argc, char *argv[]) {
                         continue;
                 }
 
-                /* If in this iteration we didn't manage to
-                 * unmount/deactivate anything, we simply give up */
+                /* If in this iteration we didn't manage to unmount/deactivate anything, we simply give up */
                 if (!changed) {
                         log_info("Cannot finalize remaining%s%s%s%s%s continuing.",
                                  need_umount ? " file systems," : "",
@@ -526,12 +525,11 @@ int main(int argc, char *argv[]) {
                           need_md_detach ? " MD devices," : "");
         }
 
-        /* We're done with the watchdog. Note that the watchdog is explicitly not
-         * stopped here. It remains active to guard against any issues during the
-         * rest of the shutdown sequence. */
+        /* We're done with the watchdog. Note that the watchdog is explicitly not stopped here. It remains
+         * active to guard against any issues during the rest of the shutdown sequence. */
         watchdog_free_device();
 
-        arguments[0] = NULL;
+        arguments[0] = NULL; /* Filled in by execute_directories(), when needed */
         arguments[1] = arg_verb;
         arguments[2] = NULL;
         (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
@@ -563,10 +561,11 @@ int main(int argc, char *argv[]) {
                           need_dm_detach ? " DM devices," : "",
                           need_md_detach ? " MD devices," : "");
 
-        /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
-         * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
-         * sync'ed things already once above, but we did some more work since then which might have caused IO, hence
-         * let's do it once more. Do not remove this sync, data corruption will result. */
+        /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need
+         * to be sync'ed explicitly in advance. So let's do this here, but not needlessly slow down
+         * containers. Note that we sync'ed things already once above, but we did some more work since then
+         * which might have caused IO, hence let's do it once more. Do not remove this sync, data corruption
+         * will result. */
         if (!in_container)
                 sync_with_progress();
 
@@ -596,6 +595,7 @@ int main(int argc, char *argv[]) {
                                 /* Child */
 
                                 execv(args[0], (char * const *) args);
+                                log_debug_errno(errno, "Failed to execute '" KEXEC "' binary, proceeding with reboot(RB_KEXEC): %m");
 
                                 /* execv failed (kexec binary missing?), so try simply reboot(RB_KEXEC) */
                                 (void) reboot(cmd);
@@ -626,9 +626,8 @@ int main(int argc, char *argv[]) {
         }
 
         (void) reboot(cmd);
-        if (errno == EPERM && in_container) {
-                /* If we are in a container, and we lacked
-                 * CAP_SYS_BOOT just exit, this will kill our
+        if (ERRNO_IS_PRIVILEGE(errno) && in_container) {
+                /* If we are in a container, and we lacked CAP_SYS_BOOT just exit, this will kill our
                  * container for good. */
                 log_info("Exiting container.");
                 return EXIT_SUCCESS;
index 58003efdfefa86d88429695ba0da84eee42f0d2d..14191cfc61c593c0356d4873235a3b4d9321634a 100644 (file)
@@ -263,41 +263,81 @@ static int execute(
 }
 
 static int execute_s2h(const SleepConfig *sleep_config) {
-        _cleanup_close_ int tfd = -1;
-        struct itimerspec ts = {};
+        _cleanup_hashmap_free_ Hashmap *last_capacity = NULL, *current_capacity = NULL;
         int r;
 
         assert(sleep_config);
 
-        tfd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK|TFD_CLOEXEC);
-        if (tfd < 0)
-                return log_error_errno(errno, "Error creating timerfd: %m");
+        while (battery_is_low() == 0) {
+                _cleanup_close_ int tfd = -1;
+                struct itimerspec ts = {};
+                usec_t suspend_interval = sleep_config->hibernate_delay_sec, before_timestamp = 0, after_timestamp = 0, total_suspend_interval;
+                bool woken_by_timer;
 
-        log_debug("Set timerfd wake alarm for %s",
-                  FORMAT_TIMESPAN(sleep_config->hibernate_delay_sec, USEC_PER_SEC));
+                tfd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK|TFD_CLOEXEC);
+                if (tfd < 0)
+                        return log_error_errno(errno, "Error creating timerfd: %m");
 
-        timespec_store(&ts.it_value, sleep_config->hibernate_delay_sec);
+                /* Store current battery capacity and current time before suspension */
+                r = fetch_batteries_capacity_by_name(&last_capacity);
+                if (r >= 0)
+                        before_timestamp = now(CLOCK_BOOTTIME);
+                else if (r == -ENOENT)
+                        /* In case of no battery, system suspend interval will be set to HibernateDelaySec=. */
+                        log_debug_errno(r, "Suspend Interval value set to %s: %m", FORMAT_TIMESPAN(suspend_interval, USEC_PER_SEC));
+                else
+                        return log_error_errno(r, "Error fetching battery capacity percentage: %m");
+
+                r = get_total_suspend_interval(last_capacity, &total_suspend_interval);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to estimate suspend interval using previous discharge rate, ignoring: %m");
+                else
+                        suspend_interval = total_suspend_interval;
 
-        r = timerfd_settime(tfd, 0, &ts, NULL);
-        if (r < 0)
-                return log_error_errno(errno, "Error setting hibernate timer: %m");
+                log_debug("Set timerfd wake alarm for %s", FORMAT_TIMESPAN(suspend_interval, USEC_PER_SEC));
+                /* Wake alarm for system with or without battery to hibernate or estimate discharge rate whichever is applicable */
+                timespec_store(&ts.it_value, suspend_interval);
 
-        r = execute(sleep_config, SLEEP_SUSPEND, NULL);
-        if (r < 0)
-                return r;
+                if (timerfd_settime(tfd, 0, &ts, NULL) < 0)
+                        return log_error_errno(errno, "Error setting battery estimate timer: %m");
 
-        r = fd_wait_for_event(tfd, POLLIN, 0);
-        if (r < 0)
-                return log_error_errno(r, "Error polling timerfd: %m");
-        if (!FLAGS_SET(r, POLLIN)) /* We woke up before the alarm time, we are done. */
-                return 0;
+                r = execute(sleep_config, SLEEP_SUSPEND, NULL);
+                if (r < 0)
+                        return r;
 
-        tfd = safe_close(tfd);
+                r = fd_wait_for_event(tfd, POLLIN, 0);
+                if (r < 0)
+                        return log_error_errno(r, "Error polling timerfd: %m");
+                /* Store fd_wait status */
+                woken_by_timer = FLAGS_SET(r, POLLIN);
 
-        /* If woken up after alarm time, hibernate */
-        log_debug("Attempting to hibernate after waking from %s timer",
-                  FORMAT_TIMESPAN(sleep_config->hibernate_delay_sec, USEC_PER_SEC));
+                r = fetch_batteries_capacity_by_name(&current_capacity);
+                if (r < 0) {
+                        /* In case of no battery or error while getting charge level, no need to measure
+                         * discharge rate. Instead system should wakeup if it is manual wakeup or
+                         * hibernate if this is a timer wakeup.   */
+                        log_debug_errno(r, "Battery capacity percentage unavailable, cannot estimate discharge rate: %m");
+                        if (!woken_by_timer)
+                                return 0;
+                        break;
+                }
+
+                after_timestamp = now(CLOCK_BOOTTIME);
+                log_debug("Attempting to estimate battery discharge rate after wakeup from %s sleep", FORMAT_TIMESPAN(after_timestamp - before_timestamp, USEC_PER_HOUR));
+
+                if (after_timestamp != before_timestamp) {
+                        r = estimate_battery_discharge_rate_per_hour(last_capacity, current_capacity, before_timestamp, after_timestamp);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to estimate and update battery discharge rate, ignoring: %m");
+                } else
+                        log_debug("System woke up too early to estimate discharge rate");
+
+                if (!woken_by_timer)
+                        /* Return as manual wakeup done. This also will return in case battery was charged during suspension */
+                        return 0;
+        }
 
+        log_debug("Attempting to hibernate");
         r = execute(sleep_config, SLEEP_HIBERNATE, NULL);
         if (r < 0) {
                 log_notice("Couldn't hibernate, will try to suspend again.");
index 174f5ea3e8b0b01c8d013aa4f508fd895c0c4a45..a3d31140d8b573a8e3f93092ba9f640548e35341 100644 (file)
@@ -23,4 +23,4 @@
 #HibernateState=disk
 #HybridSleepMode=suspend platform shutdown
 #HybridSleepState=disk
-#HibernateDelaySec=180min
+#HibernateDelaySec=120min
index 718cf37607f34ef411c0b19c7901f42e64dfff74..5648dfd83baaa321914f5a201150c7e241a966b2 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "sd-bus.h"
 
+#include "bus-locator.h"
 #include "bus-util.h"
 #include "bus-error.h"
 #include "def.h"
@@ -24,12 +25,10 @@ static int reload_manager(sd_bus *bus) {
 
         log_info("Reloading system manager configuration");
 
-        r = sd_bus_message_new_method_call(
+        r = bus_message_new_method_call(
                         bus,
                         &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
+                        bus_systemd_mgr,
                         "Reload");
         if (r < 0)
                 return bus_log_create_error(r);
@@ -49,14 +48,13 @@ static int start_default_target(sd_bus *bus) {
         log_info("Starting "SPECIAL_DEFAULT_TARGET);
 
         /* Start this unit only if we can replace basic.target with it */
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.systemd1",
-                               "/org/freedesktop/systemd1",
-                               "org.freedesktop.systemd1.Manager",
-                               "StartUnit",
-                               &error,
-                               NULL,
-                               "ss", SPECIAL_DEFAULT_TARGET, "isolate");
+        r = bus_call_method(
+                        bus,
+                        bus_systemd_mgr,
+                        "StartUnit",
+                        &error,
+                        NULL,
+                        "ss", SPECIAL_DEFAULT_TARGET, "isolate");
 
         if (r < 0)
                 return log_error_errno(r, "Failed to start "SPECIAL_DEFAULT_TARGET": %s", bus_error_message(&error, r));
index 2b854a73709b42aa4333947ced1d6b7ec40fda6b..5de5d81655f1a435e528f0d7734498273cca210b 100644 (file)
@@ -10,6 +10,7 @@
 #include <sys/types.h>
 
 #include "conf-files.h"
+#include "creds-util.h"
 #include "def.h"
 #include "errno-util.h"
 #include "fd-util.h"
@@ -27,6 +28,7 @@
 
 static char **arg_prefixes = NULL;
 static bool arg_cat_config = false;
+static bool arg_strict = false;
 static PagerFlags arg_pager_flags = 0;
 
 STATIC_DESTRUCTOR_REGISTER(arg_prefixes, strv_freep);
@@ -54,18 +56,7 @@ static bool test_prefix(const char *p) {
         if (strv_isempty(arg_prefixes))
                 return true;
 
-        STRV_FOREACH(i, arg_prefixes) {
-                const char *t;
-
-                t = path_startswith(*i, "/proc/sys/");
-                if (!t)
-                        t = *i;
-
-                if (path_startswith(p, t))
-                        return true;
-        }
-
-        return false;
+        return path_startswith_strv(p, arg_prefixes);
 }
 
 static Option *option_new(
@@ -95,18 +86,21 @@ static Option *option_new(
         return TAKE_PTR(o);
 }
 
-static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_failure) {
+static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_failure, bool ignore_enoent) {
         int r;
 
         r = sysctl_write(key, value);
         if (r < 0) {
-                /* If the sysctl is not available in the kernel or we are running with reduced privileges and
-                 * cannot write it, then log about the issue, and proceed without failing. (EROFS is treated
-                 * as a permission problem here, since that's how container managers usually protected their
-                 * sysctls.) In all other cases log an error and make the tool fail. */
-                if (ignore_failure || r == -EROFS || ERRNO_IS_PRIVILEGE(r))
+                /* Proceed without failing if ignore_failure is true.
+                 * If the sysctl is not available in the kernel or we are running with reduced privileges and
+                 * cannot write it, then log about the issue, and proceed without failing. Unless strict mode
+                 * (arg_strict = true) is enabled, in which case we should fail. (EROFS is treated as a
+                 * permission problem here, since that's how container managers usually protected their
+                 * sysctls.)
+                 * In all other cases log an error and make the tool fail. */
+                if (ignore_failure || (!arg_strict && (r == -EROFS || ERRNO_IS_PRIVILEGE(r))))
                         log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
-                else if (r == -ENOENT)
+                else if (ignore_enoent && r == -ENOENT)
                         log_warning_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
                 else
                         return log_error_errno(r, "Couldn't write '%s' to '%s': %m", value, key);
@@ -115,63 +109,113 @@ static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_
         return 0;
 }
 
-static int apply_all(OrderedHashmap *sysctl_options) {
-        Option *option;
-        int r = 0;
+static int apply_glob_option_with_prefix(OrderedHashmap *sysctl_options, Option *option, const char *prefix) {
+        _cleanup_strv_free_ char **paths = NULL;
+        _cleanup_free_ char *pattern = NULL;
+        int r, k;
 
-        ORDERED_HASHMAP_FOREACH(option, sysctl_options) {
-                int k;
+        assert(sysctl_options);
+        assert(option);
 
-                /* Ignore "negative match" options, they are there only to exclude stuff from globs. */
-                if (!option->value)
+        if (prefix) {
+                _cleanup_free_ char *key = NULL;
+
+                r = path_glob_can_match(option->key, prefix, &key);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to check if the glob '%s' matches prefix '%s': %m",
+                                               option->key, prefix);
+                if (r == 0) {
+                        log_debug("The glob '%s' does not match prefix '%s'.", option->key, prefix);
+                        return 0;
+                }
+
+                log_debug("The glob '%s' is prefixed with '%s': '%s'", option->key, prefix, key);
+
+                if (!string_is_glob(key)) {
+                        /* The prefixed pattern is not glob anymore. Let's skip to call glob(). */
+                        if (ordered_hashmap_contains(sysctl_options, key)) {
+                                log_debug("Not setting %s (explicit setting exists).", key);
+                                return 0;
+                        }
+
+                        return sysctl_write_or_warn(key, option->value,
+                                                    /* ignore_failure = */ option->ignore_failure,
+                                                    /* ignore_enoent = */ true);
+                }
+
+                pattern = path_join("/proc/sys", key);
+        } else
+                pattern = path_join("/proc/sys", option->key);
+        if (!pattern)
+                return log_oom();
+
+        r = glob_extend(&paths, pattern, GLOB_NOCHECK);
+        if (r < 0) {
+                if (r == -ENOENT) {
+                        log_debug("No match for glob: %s", option->key);
+                        return 0;
+                }
+                if (option->ignore_failure || ERRNO_IS_PRIVILEGE(r)) {
+                        log_debug_errno(r, "Failed to resolve glob '%s', ignoring: %m", option->key);
+                        return 0;
+                } else
+                        return log_error_errno(r, "Couldn't resolve glob '%s': %m", option->key);
+        }
+
+        STRV_FOREACH(s, paths) {
+                const char *key;
+
+                assert_se(key = path_startswith(*s, "/proc/sys"));
+
+                if (ordered_hashmap_contains(sysctl_options, key)) {
+                        log_debug("Not setting %s (explicit setting exists).", key);
                         continue;
+                }
 
-                if (string_is_glob(option->key)) {
-                        _cleanup_strv_free_ char **paths = NULL;
-                        _cleanup_free_ char *pattern = NULL;
+                k = sysctl_write_or_warn(key, option->value,
+                                         /* ignore_failure = */ option->ignore_failure,
+                                         /* ignore_enoent = */ !arg_strict);
+                if (k < 0 && r >= 0)
+                        r = k;
+        }
 
-                        pattern = path_join("/proc/sys", option->key);
-                        if (!pattern)
-                                return log_oom();
+        return r;
+}
 
-                        k = glob_extend(&paths, pattern, GLOB_NOCHECK);
-                        if (k < 0) {
-                                if (option->ignore_failure || ERRNO_IS_PRIVILEGE(k))
-                                        log_debug_errno(k, "Failed to resolve glob '%s', ignoring: %m",
-                                                        option->key);
-                                else {
-                                        log_error_errno(k, "Couldn't resolve glob '%s': %m",
-                                                        option->key);
-                                        if (r == 0)
-                                                r = k;
-                                }
+static int apply_glob_option(OrderedHashmap *sysctl_options, Option *option) {
+        int r = 0, k;
 
-                        } else if (strv_isempty(paths))
-                                log_debug("No match for glob: %s", option->key);
+        if (strv_isempty(arg_prefixes))
+                return apply_glob_option_with_prefix(sysctl_options, option, NULL);
 
-                        STRV_FOREACH(s, paths) {
-                                const char *key;
+        STRV_FOREACH(i, arg_prefixes) {
+                k = apply_glob_option_with_prefix(sysctl_options, option, *i);
+                if (k < 0 && r >= 0)
+                        r = k;
+        }
 
-                                assert_se(key = path_startswith(*s, "/proc/sys"));
+        return r;
+}
 
-                                if (!test_prefix(key))
-                                        continue;
+static int apply_all(OrderedHashmap *sysctl_options) {
+        Option *option;
+        int r = 0;
 
-                                if (ordered_hashmap_contains(sysctl_options, key)) {
-                                        log_debug("Not setting %s (explicit setting exists).", key);
-                                        continue;
-                                }
+        ORDERED_HASHMAP_FOREACH(option, sysctl_options) {
+                int k;
 
-                                k = sysctl_write_or_warn(key, option->value, option->ignore_failure);
-                                if (r == 0)
-                                        r = k;
-                        }
+                /* Ignore "negative match" options, they are there only to exclude stuff from globs. */
+                if (!option->value)
+                        continue;
 
-                } else {
-                        k = sysctl_write_or_warn(option->key, option->value, option->ignore_failure);
-                        if (r == 0)
-                                r = k;
-                }
+                if (string_is_glob(option->key))
+                        k = apply_glob_option(sysctl_options, option);
+                else
+                        k = sysctl_write_or_warn(option->key, option->value,
+                                                 /* ignore_failure = */ option->ignore_failure,
+                                                 /* ignore_enoent = */ !arg_strict);
+                if (k < 0 && r >= 0)
+                        r = k;
         }
 
         return r;
@@ -249,9 +293,6 @@ static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ig
                     !test_prefix(p))
                         continue;
 
-                if (ordered_hashmap_ensure_allocated(sysctl_options, &option_hash_ops) < 0)
-                        return log_oom();
-
                 existing = ordered_hashmap_get(*sysctl_options, p);
                 if (existing) {
                         if (streq_ptr(value, existing->value)) {
@@ -267,7 +308,7 @@ static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ig
                 if (!new_option)
                         return log_oom();
 
-                k = ordered_hashmap_put(*sysctl_options, new_option->key, new_option);
+                k = ordered_hashmap_ensure_put(sysctl_options, &option_hash_ops, new_option->key, new_option);
                 if (k < 0)
                         return log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", p);
 
@@ -277,6 +318,25 @@ static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ig
         return r;
 }
 
+static int read_credential_lines(OrderedHashmap **sysctl_options) {
+        _cleanup_free_ char *j = NULL;
+        const char *d;
+        int r;
+
+        r = get_credentials_dir(&d);
+        if (r == -ENXIO)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to get credentials directory: %m");
+
+        j = path_join(d, "sysctl.extra");
+        if (!j)
+                return log_oom();
+
+        (void) parse_file(sysctl_options, j, /* ignore_enoent= */ true);
+        return 0;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -306,6 +366,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_CAT_CONFIG,
                 ARG_PREFIX,
                 ARG_NO_PAGER,
+                ARG_STRICT,
         };
 
         static const struct option options[] = {
@@ -314,6 +375,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "cat-config", no_argument,       NULL, ARG_CAT_CONFIG },
                 { "prefix",     required_argument, NULL, ARG_PREFIX     },
                 { "no-pager",   no_argument,       NULL, ARG_NO_PAGER   },
+                { "strict",     no_argument,       NULL, ARG_STRICT     },
                 {}
         };
 
@@ -337,6 +399,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_PREFIX: {
+                        const char *s;
                         char *p;
 
                         /* We used to require people to specify absolute paths
@@ -345,10 +408,8 @@ static int parse_argv(int argc, char *argv[]) {
                          * sysctl name available. */
                         sysctl_normalize(optarg);
 
-                        if (path_startswith(optarg, "/proc/sys"))
-                                p = strdup(optarg);
-                        else
-                                p = path_join("/proc/sys", optarg);
+                        s = path_startswith(optarg, "/proc/sys");
+                        p = strdup(s ?: optarg);
                         if (!p)
                                 return log_oom();
 
@@ -362,6 +423,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_pager_flags |= PAGER_DISABLE;
                         break;
 
+                case ARG_STRICT:
+                        arg_strict = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -416,6 +481,10 @@ static int run(int argc, char *argv[]) {
                         if (k < 0 && r == 0)
                                 r = k;
                 }
+
+                k = read_credential_lines(&sysctl_options);
+                if (k < 0 && r == 0)
+                        r = k;
         }
 
         k = apply_all(sysctl_options);
index 4f3d534bc80da0377ae963c408200e9b4c26f281..5a1311f5ea800e45ff2aaae79338390f4b913bbf 100644 (file)
@@ -3,6 +3,7 @@
 #include "sd-login.h"
 
 #include "bus-error.h"
+#include "bus-locator.h"
 #include "format-table.h"
 #include "locale-util.h"
 #include "set.h"
@@ -87,9 +88,22 @@ static int get_unit_list_recursive(
         return c;
 }
 
-static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
+static void output_legend(const char *type, size_t n_items) {
+        const char *on, *off;
+
+        assert(type);
+
+        on = n_items > 0 ? ansi_highlight() : ansi_highlight_red();
+        off = ansi_normal();
+
+        printf("\n%s%zu %ss listed.%s\n", on, n_items, type, off);
+        if (!arg_all)
+                printf("Pass --all to see loaded but inactive %ss, too.\n", type);
+}
+
+static int output_units_list(const UnitInfo *unit_infos, size_t c) {
         _cleanup_(table_unrefp) Table *table = NULL;
-        unsigned job_count = 0;
+        size_t job_count = 0;
         int r;
 
         table = table_new("", "unit", "load", "active", "sub", "job", "description");
@@ -187,13 +201,11 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
                              "SUB    = The low-level unit activation state, values depend on unit type.");
                         if (job_count > 0)
                                 puts("JOB    = Pending job for the unit.\n");
-                        on = ansi_highlight();
-                        off = ansi_normal();
-                } else {
-                        on = ansi_highlight_red();
-                        off = ansi_normal();
                 }
 
+                on = records > 0 ? ansi_highlight() : ansi_highlight_red();
+                off = ansi_normal();
+
                 if (arg_all || strv_contains(arg_states, "inactive"))
                         printf("%s%zu loaded units listed.%s\n"
                                "To show all installed unit files use 'systemctl list-unit-files'.\n",
@@ -348,11 +360,12 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_
         return strcmp(a->type, b->type);
 }
 
-static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
+static int output_sockets_list(struct socket_info *socket_infos, size_t cs) {
         _cleanup_(table_unrefp) Table *table = NULL;
-        const char *on, *off;
         int r;
 
+        assert(socket_infos || cs == 0);
+
         table = table_new("listen", "type", "unit", "activates");
         if (!table)
                 return log_oom();
@@ -370,56 +383,44 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
 
         (void) table_set_empty_string(table, "-");
 
-        if (cs) {
-                for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
-                        _cleanup_free_ char *j = NULL;
-                        const char *path;
-
-                        if (s->machine) {
-                                j = strjoin(s->machine, ":", s->path);
-                                if (!j)
-                                        return log_oom();
-                                path = j;
-                        } else
-                                path = s->path;
-
-                        r = table_add_many(table,
-                                           TABLE_STRING, path,
-                                           TABLE_STRING, s->type,
-                                           TABLE_STRING, s->id);
-                        if (r < 0)
-                                return table_log_add_error(r);
-
-                        if (strv_isempty(s->triggered))
-                                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
-                        else if (strv_length(s->triggered) == 1)
-                                r = table_add_cell(table, NULL, TABLE_STRING, s->triggered[0]);
-                        else
-                                /* This should never happen, currently our socket units can only trigger a
-                                 * single unit. But let's handle this anyway, who knows what the future
-                                 * brings? */
-                                r = table_add_cell(table, NULL, TABLE_STRV, s->triggered);
-                        if (r < 0)
-                                return table_log_add_error(r);
+        for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
+                _cleanup_free_ char *j = NULL;
+                const char *path;
 
-                }
+                if (s->machine) {
+                        j = strjoin(s->machine, ":", s->path);
+                        if (!j)
+                                return log_oom();
+                        path = j;
+                } else
+                        path = s->path;
 
-                on = ansi_highlight();
-                off = ansi_normal();
-        } else {
-                on = ansi_highlight_red();
-                off = ansi_normal();
+                r = table_add_many(table,
+                                        TABLE_STRING, path,
+                                        TABLE_STRING, s->type,
+                                        TABLE_STRING, s->id);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (strv_isempty(s->triggered))
+                        r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+                else if (strv_length(s->triggered) == 1)
+                        r = table_add_cell(table, NULL, TABLE_STRING, s->triggered[0]);
+                else
+                        /* This should never happen, currently our socket units can only trigger a
+                                * single unit. But let's handle this anyway, who knows what the future
+                                * brings? */
+                        r = table_add_cell(table, NULL, TABLE_STRV, s->triggered);
+                if (r < 0)
+                        return table_log_add_error(r);
         }
 
         r = output_table(table);
         if (r < 0)
                 return r;
 
-        if (arg_legend != 0) {
-                printf("\n%s%u sockets listed.%s\n", on, cs, off);
-                if (!arg_all)
-                        printf("Pass --all to see loaded but inactive sockets, too.\n");
-        }
+        if (arg_legend != 0)
+                output_legend("socket", cs);
 
         return 0;
 }
@@ -430,7 +431,7 @@ int verb_list_sockets(int argc, char *argv[], void *userdata) {
         _cleanup_strv_free_ char **sockets_with_suffix = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
         _cleanup_free_ struct socket_info *socket_infos = NULL;
-        unsigned cs = 0;
+        size_t cs = 0;
         int r, n;
         sd_bus *bus;
 
@@ -597,9 +598,8 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf
         return strcmp(a->id, b->id);
 }
 
-static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
+static int output_timers_list(struct timer_info *timer_infos, size_t n) {
         _cleanup_(table_unrefp) Table *table = NULL;
-        const char *on, *off;
         int r;
 
         assert(timer_infos || n == 0);
@@ -641,23 +641,12 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
                         return table_log_add_error(r);
         }
 
-        if (n > 0) {
-                on = ansi_highlight();
-                off = ansi_normal();
-        } else {
-                on = ansi_highlight_red();
-                off = ansi_normal();
-        }
-
         r = output_table(table);
         if (r < 0)
                 return r;
 
-        if (arg_legend != 0) {
-                printf("\n%s%u timers listed.%s\n", on, n, off);
-                if (!arg_all)
-                        printf("Pass --all to see loaded but inactive timers, too.\n");
-        }
+        if (arg_legend != 0)
+                output_legend("timer", n);
 
         return 0;
 }
@@ -693,10 +682,10 @@ int verb_list_timers(int argc, char *argv[], void *userdata) {
         _cleanup_strv_free_ char **timers_with_suffix = NULL;
         _cleanup_free_ struct timer_info *timer_infos = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
-        int n, c = 0;
         dual_timestamp nw;
+        size_t c = 0;
         sd_bus *bus;
-        int r;
+        int n, r;
 
         r = acquire_bus(BUS_MANAGER, &bus);
         if (r < 0)
@@ -760,3 +749,186 @@ int verb_list_timers(int argc, char *argv[], void *userdata) {
 
         return r;
 }
+
+struct automount_info {
+        const char *machine;
+        const char *id;
+        char *what;
+        char *where;
+        usec_t timeout_idle_usec;
+        bool mounted;
+};
+
+static int automount_info_compare(const struct automount_info *a, const struct automount_info *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+
+        r = strcasecmp_ptr(a->machine, b->machine);
+        if (r != 0)
+                return r;
+
+        return strcmp(a->where, b->where);
+}
+
+static int collect_automount_info(sd_bus* bus, const UnitInfo* info, struct automount_info *ret_info) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_free_ char *mount = NULL, *mount_path = NULL, *where = NULL, *what = NULL, *state = NULL;
+        usec_t timeout_idle_usec;
+        BusLocator locator;
+        int r;
+
+        assert(bus);
+        assert(info);
+        assert(ret_info);
+
+        locator = (BusLocator) {
+                .destination = "org.freedesktop.systemd1",
+                .path = info->unit_path,
+                .interface = "org.freedesktop.systemd1.Automount",
+        };
+
+        r = bus_get_property_string(bus, &locator, "Where", &error, &where);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get automount target: %s", bus_error_message(&error, r));
+
+        r = bus_get_property_trivial(bus, &locator, "TimeoutIdleUSec", &error, 't', &timeout_idle_usec);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get idle timeout: %s", bus_error_message(&error, r));
+
+        r = unit_name_from_path(where, ".mount", &mount);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate unit name from path: %m");
+
+        mount_path = unit_dbus_path_from_name(mount);
+        if (!mount_path)
+                return log_oom();
+
+        locator.path = mount_path;
+        locator.interface = "org.freedesktop.systemd1.Mount";
+
+        r = bus_get_property_string(bus, &locator, "What", &error, &what);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get mount source: %s", bus_error_message(&error, r));
+
+        locator.interface = "org.freedesktop.systemd1.Unit";
+
+        r = bus_get_property_string(bus, &locator, "ActiveState", &error, &state);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get mount state: %s", bus_error_message(&error, r));
+
+        *ret_info = (struct automount_info) {
+                .machine = info->machine,
+                .id = info->id,
+                .what = TAKE_PTR(what),
+                .where = TAKE_PTR(where),
+                .timeout_idle_usec = timeout_idle_usec,
+                .mounted = streq_ptr(state, "active"),
+        };
+
+        return 0;
+}
+
+static int output_automounts_list(struct automount_info *infos, size_t n_infos) {
+        _cleanup_(table_unrefp) Table *table = NULL;
+        int r;
+
+        assert(infos || n_infos == 0);
+
+        table = table_new("what", "where", "mounted", "idle timeout", "unit");
+        if (!table)
+                return log_oom();
+
+        table_set_header(table, arg_legend != 0);
+        if (arg_full)
+                table_set_width(table, 0);
+
+        (void) table_set_empty_string(table, "-");
+
+        for (struct automount_info *info = infos; info < infos + n_infos; info++) {
+                _cleanup_free_ char *j = NULL;
+                const char *unit;
+
+                if (info->machine) {
+                        j = strjoin(info->machine, ":", info->id);
+                        if (!j)
+                                return log_oom();
+                        unit = j;
+                } else
+                        unit = info->id;
+
+                r = table_add_many(table,
+                                   TABLE_STRING, info->what,
+                                   TABLE_STRING, info->where,
+                                   TABLE_BOOLEAN, info->mounted,
+                                   TABLE_TIMESPAN_MSEC, info->timeout_idle_usec,
+                                   TABLE_STRING, unit);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
+        r = output_table(table);
+        if (r < 0)
+                return r;
+
+        if (arg_legend != 0)
+                output_legend("automount", n_infos);
+
+        return 0;
+}
+
+int verb_list_automounts(int argc, char *argv[], void *userdata) {
+        _cleanup_(message_set_freep) Set *replies = NULL;
+        _cleanup_strv_free_ char **machines = NULL, **automounts = NULL;
+        _cleanup_free_ UnitInfo *unit_infos = NULL;
+        _cleanup_free_ struct automount_info *automount_infos = NULL;
+        size_t c = 0;
+        int r, n;
+        sd_bus *bus;
+
+        r = acquire_bus(BUS_MANAGER, &bus);
+        if (r < 0)
+                return r;
+
+        pager_open(arg_pager_flags);
+
+        r = expand_unit_names(bus, strv_skip(argv, 1), ".automount", &automounts, NULL);
+        if (r < 0)
+                return r;
+
+        if (argc == 1 || automounts) {
+                n = get_unit_list_recursive(bus, automounts, &unit_infos, &replies, &machines);
+                if (n < 0)
+                        return n;
+
+                for (const UnitInfo *u = unit_infos; u < unit_infos + n; u++) {
+                        if (!endswith(u->id, ".automount"))
+                                continue;
+
+                        if (!GREEDY_REALLOC(automount_infos, c + 1)) {
+                                r = log_oom();
+                                goto cleanup;
+                        }
+
+                        r = collect_automount_info(bus, u, &automount_infos[c]);
+                        if (r < 0)
+                                goto cleanup;
+
+                        c++;
+                }
+
+                typesafe_qsort(automount_infos, c, automount_info_compare);
+        }
+
+        output_automounts_list(automount_infos, c);
+
+ cleanup:
+        assert(c == 0 || automount_infos);
+        for (struct automount_info *info = automount_infos; info < automount_infos + c; info++) {
+                free(info->what);
+                free(info->where);
+        }
+
+        return r;
+}
index 7f4cee0d10273be42b7720664a24095ef23bd688..117b7e378afbfd0af8c662d014a5c7bd9792d2f1 100644 (file)
@@ -4,5 +4,6 @@
 int verb_list_units(int argc, char *argv[], void *userdata);
 int verb_list_sockets(int argc, char *argv[], void *userdata);
 int verb_list_timers(int argc, char *argv[], void *userdata);
+int verb_list_automounts(int argc, char *argv[], void *userdata);
 
 usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next);
index 57cb2ca26a120ea153e642e6ea20cbed9fb8ba87..b333850bec8e0e9171049d379829dfdc45dad877 100644 (file)
@@ -704,13 +704,14 @@ int maybe_extend_with_unit_dependencies(sd_bus *bus, char ***list) {
 int unit_get_dependencies(sd_bus *bus, const char *name, char ***ret) {
         _cleanup_strv_free_ char **deps = NULL;
 
-        static const struct bus_properties_map map[_DEPENDENCY_MAX][6] = {
+        static const struct bus_properties_map map[_DEPENDENCY_MAX][7] = {
                 [DEPENDENCY_FORWARD] = {
                         { "Requires",    "as", NULL, 0 },
                         { "Requisite",   "as", NULL, 0 },
                         { "Wants",       "as", NULL, 0 },
                         { "ConsistsOf",  "as", NULL, 0 },
                         { "BindsTo",     "as", NULL, 0 },
+                        { "Upholds",     "as", NULL, 0 },
                         {}
                 },
                 [DEPENDENCY_REVERSE] = {
@@ -719,6 +720,7 @@ int unit_get_dependencies(sd_bus *bus, const char *name, char ***ret) {
                         { "WantedBy",    "as", NULL, 0 },
                         { "PartOf",      "as", NULL, 0 },
                         { "BoundBy",     "as", NULL, 0 },
+                        { "UpheldBy",    "as", NULL, 0 },
                         {}
                 },
                 [DEPENDENCY_AFTER] = {
index fa32c67a08e2b8e9e823e05b24a5bb198bb0518b..fb40c478c2984ad01a8538f37a654e754df3f777 100644 (file)
@@ -138,6 +138,8 @@ static int systemctl_help(void) {
                "%5$sQuery or send control commands to the system manager.%6$s\n"
                "\n%3$sUnit Commands:%4$s\n"
                "  list-units [PATTERN...]             List units currently in memory\n"
+               "  list-automounts [PATTERN...]        List automount units currently in memory,\n"
+               "                                      ordered by path\n"
                "  list-sockets [PATTERN...]           List socket units currently in memory,\n"
                "                                      ordered by address\n"
                "  list-timers [PATTERN...]            List timer units currently in memory,\n"
@@ -1022,6 +1024,7 @@ static int systemctl_main(int argc, char *argv[]) {
         static const Verb verbs[] = {
                 { "list-units",            VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, verb_list_units },
                 { "list-unit-files",       VERB_ANY, VERB_ANY, 0,                verb_list_unit_files         },
+                { "list-automounts",       VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_automounts         },
                 { "list-sockets",          VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_sockets            },
                 { "list-timers",           VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_timers             },
                 { "list-jobs",             VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_jobs               },
index 38449463e24a909a98933ed5bdd15e860474ca04..6f657c225438a32cfb913427e68b4edb0df99eea 100644 (file)
@@ -85,7 +85,7 @@ typedef void (*_sd_destroy_t)(void *userdata);
 #endif
 
 #ifndef _SD_ARRAY_STATIC
-#  if __STDC_VERSION__ >= 199901L
+#  if __STDC_VERSION__ >= 199901L && !defined(__cplusplus)
 #    define _SD_ARRAY_STATIC static
 #  else
 #    define _SD_ARRAY_STATIC
index 19dd4d9eefae8c692446a1e4c876c086cc03a681..41d3f832913c0118a57c0144ce6ecb5a1d1177e5 100644 (file)
@@ -67,6 +67,8 @@ int sd_device_new_from_path(sd_device **ret, const char *path);
 int sd_device_new_from_ifname(sd_device **ret, const char *ifname);
 int sd_device_new_from_ifindex(sd_device **ret, int ifindex);
 
+int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix);
+
 int sd_device_get_parent(sd_device *child, sd_device **ret);
 int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret);
 
@@ -142,6 +144,8 @@ int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event);
 int sd_device_monitor_detach_event(sd_device_monitor *m);
 sd_event *sd_device_monitor_get_event(sd_device_monitor *m);
 sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m);
+int sd_device_monitor_set_description(sd_device_monitor *m, const char *description);
+int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret);
 int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata);
 int sd_device_monitor_stop(sd_device_monitor *m);
 
index 855f938b091f4a4a74fd9392fdc904a7716a16ad..6a863794a4c65c2eb97b1aa1ad0326c5a5acd5b4 100644 (file)
@@ -247,7 +247,7 @@ int sd_dhcp_client_set_ifname(
 int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret);
 int sd_dhcp_client_set_mac(
                 sd_dhcp_client *client,
-                const uint8_t *addr,
+                const uint8_t *hw_addr,
                 const uint8_t *bcast_addr,
                 size_t addr_len,
                 uint16_t arp_type);
index 7fe60c356c85e48093c6cf5b073bdd9e4172b9e4..2c66c51b78ce036a60a5271f4a41cf84b9693665 100644 (file)
@@ -262,6 +262,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
                                         int request);
 int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
                                       sd_dhcp6_option *v);
+int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable);
 
 int sd_dhcp6_client_get_lease(
                 sd_dhcp6_client *client,
index bafaa065f00cce36c80388690420d317608ce5b7..ffb9ba473956a27ffa36b4c5e553527fcd97fe8d 100644 (file)
@@ -25,191 +25,169 @@ _SD_BEGIN_DECLARATIONS;
 
 /* Hey! If you add a new message here, you *must* also update the message catalog with an appropriate explanation */
 
-/* And if you add a new ID here, make sure to generate a random one with "systemd-id128 new". Do not use any other IDs,
- * and do not count them up manually. */
-
-#define SD_MESSAGE_JOURNAL_START          SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
-#define SD_MESSAGE_JOURNAL_START_STR      SD_ID128_MAKE_STR(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
-#define SD_MESSAGE_JOURNAL_STOP           SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
-#define SD_MESSAGE_JOURNAL_STOP_STR       SD_ID128_MAKE_STR(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
-#define SD_MESSAGE_JOURNAL_DROPPED        SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
-#define SD_MESSAGE_JOURNAL_DROPPED_STR    SD_ID128_MAKE_STR(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
-#define SD_MESSAGE_JOURNAL_MISSED         SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
-#define SD_MESSAGE_JOURNAL_MISSED_STR     SD_ID128_MAKE_STR(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
-#define SD_MESSAGE_JOURNAL_USAGE          SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
-#define SD_MESSAGE_JOURNAL_USAGE_STR      SD_ID128_MAKE_STR(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
-
-#define SD_MESSAGE_COREDUMP               SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
-#define SD_MESSAGE_COREDUMP_STR           SD_ID128_MAKE_STR(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
-#define SD_MESSAGE_TRUNCATED_CORE         SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
-#define SD_MESSAGE_TRUNCATED_CORE_STR     SD_ID128_MAKE_STR(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
-#define SD_MESSAGE_BACKTRACE              SD_ID128_MAKE(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
-#define SD_MESSAGE_BACKTRACE_STR          SD_ID128_MAKE_STR(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
-
-#define SD_MESSAGE_SESSION_START          SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
-#define SD_MESSAGE_SESSION_START_STR      SD_ID128_MAKE_STR(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
-#define SD_MESSAGE_SESSION_STOP           SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
-#define SD_MESSAGE_SESSION_STOP_STR       SD_ID128_MAKE_STR(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
-#define SD_MESSAGE_SEAT_START             SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
-#define SD_MESSAGE_SEAT_START_STR         SD_ID128_MAKE_STR(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
-#define SD_MESSAGE_SEAT_STOP              SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
-#define SD_MESSAGE_SEAT_STOP_STR          SD_ID128_MAKE_STR(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
-#define SD_MESSAGE_MACHINE_START          SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
-#define SD_MESSAGE_MACHINE_START_STR      SD_ID128_MAKE_STR(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
-#define SD_MESSAGE_MACHINE_STOP           SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
-#define SD_MESSAGE_MACHINE_STOP_STR       SD_ID128_MAKE_STR(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
-
-#define SD_MESSAGE_TIME_CHANGE            SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
-#define SD_MESSAGE_TIME_CHANGE_STR        SD_ID128_MAKE_STR(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
-#define SD_MESSAGE_TIMEZONE_CHANGE        SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
-#define SD_MESSAGE_TIMEZONE_CHANGE_STR    SD_ID128_MAKE_STR(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
-
-#define SD_MESSAGE_TAINTED                SD_ID128_MAKE(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b)
-#define SD_MESSAGE_TAINTED_STR            SD_ID128_MAKE_STR(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b)
-#define SD_MESSAGE_STARTUP_FINISHED       SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
-#define SD_MESSAGE_STARTUP_FINISHED_STR   SD_ID128_MAKE_STR(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
-#define SD_MESSAGE_USER_STARTUP_FINISHED \
-                                          SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
-#define SD_MESSAGE_USER_STARTUP_FINISHED_STR \
-                                          SD_ID128_MAKE_STR(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
-
-#define SD_MESSAGE_SLEEP_START            SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
-#define SD_MESSAGE_SLEEP_START_STR        SD_ID128_MAKE_STR(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
-#define SD_MESSAGE_SLEEP_STOP             SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
-#define SD_MESSAGE_SLEEP_STOP_STR         SD_ID128_MAKE_STR(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
-
-#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. */
-#define SD_MESSAGE_UNIT_STARTING          SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
-#define SD_MESSAGE_UNIT_STARTING_STR      SD_ID128_MAKE_STR(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
-#define SD_MESSAGE_UNIT_STARTED           SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
-#define SD_MESSAGE_UNIT_STARTED_STR       SD_ID128_MAKE_STR(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
-#define SD_MESSAGE_UNIT_FAILED            SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
-#define SD_MESSAGE_UNIT_FAILED_STR        SD_ID128_MAKE_STR(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
-#define SD_MESSAGE_UNIT_STOPPING          SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
-#define SD_MESSAGE_UNIT_STOPPING_STR      SD_ID128_MAKE_STR(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
-#define SD_MESSAGE_UNIT_STOPPED           SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
-#define SD_MESSAGE_UNIT_STOPPED_STR       SD_ID128_MAKE_STR(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
-#define SD_MESSAGE_UNIT_RELOADING         SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
-#define SD_MESSAGE_UNIT_RELOADING_STR     SD_ID128_MAKE_STR(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
-#define SD_MESSAGE_UNIT_RELOADED          SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
-#define SD_MESSAGE_UNIT_RELOADED_STR      SD_ID128_MAKE_STR(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
-
-#define SD_MESSAGE_UNIT_RESTART_SCHEDULED SD_ID128_MAKE(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3)
-#define SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR                   \
-                                          SD_ID128_MAKE_STR(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3)
-
-#define SD_MESSAGE_UNIT_RESOURCES         SD_ID128_MAKE(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
-#define SD_MESSAGE_UNIT_RESOURCES_STR     SD_ID128_MAKE_STR(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
-
-#define SD_MESSAGE_UNIT_SUCCESS           SD_ID128_MAKE(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
-#define SD_MESSAGE_UNIT_SUCCESS_STR       SD_ID128_MAKE_STR(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
-#define SD_MESSAGE_UNIT_SKIPPED           SD_ID128_MAKE(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73)
-#define SD_MESSAGE_UNIT_SKIPPED_STR       SD_ID128_MAKE_STR(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73)
-#define SD_MESSAGE_UNIT_FAILURE_RESULT    SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
-#define SD_MESSAGE_UNIT_FAILURE_RESULT_STR \
-                                          SD_ID128_MAKE_STR(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
-
-#define SD_MESSAGE_SPAWN_FAILED           SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
-#define SD_MESSAGE_SPAWN_FAILED_STR       SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
-
-#define SD_MESSAGE_UNIT_PROCESS_EXIT      SD_ID128_MAKE(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
-#define SD_MESSAGE_UNIT_PROCESS_EXIT_STR  SD_ID128_MAKE_STR(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
-
-#define SD_MESSAGE_FORWARD_SYSLOG_MISSED  SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
-#define SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR \
-                                          SD_ID128_MAKE_STR(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
-
-#define SD_MESSAGE_OVERMOUNTING           SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
-#define SD_MESSAGE_OVERMOUNTING_STR       SD_ID128_MAKE_STR(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
-
-#define SD_MESSAGE_UNIT_OOMD_KILL         SD_ID128_MAKE(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed)
-#define SD_MESSAGE_UNIT_OOMD_KILL_STR     SD_ID128_MAKE_STR(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed)
-
-#define SD_MESSAGE_UNIT_OUT_OF_MEMORY     SD_ID128_MAKE(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef)
-#define SD_MESSAGE_UNIT_OUT_OF_MEMORY_STR SD_ID128_MAKE_STR(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef)
-
-#define SD_MESSAGE_LID_OPENED             SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
-#define SD_MESSAGE_LID_OPENED_STR         SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
-#define SD_MESSAGE_LID_CLOSED             SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
-#define SD_MESSAGE_LID_CLOSED_STR         SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
-#define SD_MESSAGE_SYSTEM_DOCKED          SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
-#define SD_MESSAGE_SYSTEM_DOCKED_STR      SD_ID128_MAKE_STR(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
-#define SD_MESSAGE_SYSTEM_UNDOCKED        SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
-#define SD_MESSAGE_SYSTEM_UNDOCKED_STR    SD_ID128_MAKE_STR(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
-#define SD_MESSAGE_POWER_KEY              SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
-#define SD_MESSAGE_POWER_KEY_STR          SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
-#define SD_MESSAGE_POWER_KEY_LONG_PRESS   SD_ID128_MAKE(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b)
-#define SD_MESSAGE_POWER_KEY_LONG_PRESS_STR \
-                                          SD_ID128_MAKE_STR(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b)
-#define SD_MESSAGE_REBOOT_KEY             SD_ID128_MAKE(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
-#define SD_MESSAGE_REBOOT_KEY_STR         SD_ID128_MAKE_STR(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
-#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS  SD_ID128_MAKE(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75)
-#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS_STR \
-                                          SD_ID128_MAKE_STR(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75)
-#define SD_MESSAGE_SUSPEND_KEY            SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
-#define SD_MESSAGE_SUSPEND_KEY_STR        SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
-#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS SD_ID128_MAKE(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8)
-#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS_STR \
-                                          SD_ID128_MAKE_STR(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8)
-#define SD_MESSAGE_HIBERNATE_KEY          SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
-#define SD_MESSAGE_HIBERNATE_KEY_STR      SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
-#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS \
-                                          SD_ID128_MAKE(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45)
-#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS_STR \
-                                          SD_ID128_MAKE_STR(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45)
-
-#define SD_MESSAGE_INVALID_CONFIGURATION  SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
-#define SD_MESSAGE_INVALID_CONFIGURATION_STR \
-                                          SD_ID128_MAKE_STR(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
-
-#define SD_MESSAGE_DNSSEC_FAILURE         SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
-#define SD_MESSAGE_DNSSEC_FAILURE_STR     SD_ID128_MAKE_STR(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
-#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED \
-                                          SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
-#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR \
-                                          SD_ID128_MAKE_STR(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
-#define SD_MESSAGE_DNSSEC_DOWNGRADE       SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
-#define SD_MESSAGE_DNSSEC_DOWNGRADE_STR   SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
-
-#define SD_MESSAGE_UNSAFE_USER_NAME       SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
-#define SD_MESSAGE_UNSAFE_USER_NAME_STR   SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
-
-#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE \
-                                          SD_ID128_MAKE(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
-#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR \
-                                          SD_ID128_MAKE_STR(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
-#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE \
-                                          SD_ID128_MAKE(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa)
-#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE_STR \
-                                          SD_ID128_MAKE_STR(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa)
-
-#define SD_MESSAGE_NOBODY_USER_UNSUITABLE SD_ID128_MAKE(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
-#define SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR \
-                                          SD_ID128_MAKE_STR(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
-
-#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED \
-                                          SD_ID128_MAKE(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
-#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR \
-                                          SD_ID128_MAKE_STR(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
-
-#define SD_MESSAGE_TIME_SYNC              SD_ID128_MAKE(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
-#define SD_MESSAGE_TIME_SYNC_STR          SD_ID128_MAKE_STR(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
-
-#define SD_MESSAGE_LOGIND_SHUTDOWN        SD_ID128_MAKE(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
-#define SD_MESSAGE_LOGIND_SHUTDOWN_STR    SD_ID128_MAKE_STR(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
-
-#define SD_MESSAGE_LOGIND_SHUTDOWN_CANCELED \
-                                          SD_ID128_MAKE(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
-#define SD_MESSAGE_LOGIND_SHUTDOWN_CANCELED_STR \
-                                          SD_ID128_MAKE_STR(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
+/* And if you add a new ID here, make sure to generate a random one with "systemd-id128 new". Do not use any
+ * other IDs, and do not count them up manually. */
+
+#define SD_MESSAGE_JOURNAL_START                      SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
+#define SD_MESSAGE_JOURNAL_START_STR                  SD_ID128_MAKE_STR(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
+#define SD_MESSAGE_JOURNAL_STOP                       SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
+#define SD_MESSAGE_JOURNAL_STOP_STR                   SD_ID128_MAKE_STR(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
+#define SD_MESSAGE_JOURNAL_DROPPED                    SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
+#define SD_MESSAGE_JOURNAL_DROPPED_STR                SD_ID128_MAKE_STR(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
+#define SD_MESSAGE_JOURNAL_MISSED                     SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
+#define SD_MESSAGE_JOURNAL_MISSED_STR                 SD_ID128_MAKE_STR(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
+#define SD_MESSAGE_JOURNAL_USAGE                      SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
+#define SD_MESSAGE_JOURNAL_USAGE_STR                  SD_ID128_MAKE_STR(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
+
+#define SD_MESSAGE_COREDUMP                           SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
+#define SD_MESSAGE_COREDUMP_STR                       SD_ID128_MAKE_STR(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
+#define SD_MESSAGE_TRUNCATED_CORE                     SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
+#define SD_MESSAGE_TRUNCATED_CORE_STR                 SD_ID128_MAKE_STR(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
+#define SD_MESSAGE_BACKTRACE                          SD_ID128_MAKE(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
+#define SD_MESSAGE_BACKTRACE_STR                      SD_ID128_MAKE_STR(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
+
+#define SD_MESSAGE_SESSION_START                      SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
+#define SD_MESSAGE_SESSION_START_STR                  SD_ID128_MAKE_STR(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
+#define SD_MESSAGE_SESSION_STOP                       SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
+#define SD_MESSAGE_SESSION_STOP_STR                   SD_ID128_MAKE_STR(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
+#define SD_MESSAGE_SEAT_START                         SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
+#define SD_MESSAGE_SEAT_START_STR                     SD_ID128_MAKE_STR(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
+#define SD_MESSAGE_SEAT_STOP                          SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
+#define SD_MESSAGE_SEAT_STOP_STR                      SD_ID128_MAKE_STR(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
+#define SD_MESSAGE_MACHINE_START                      SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
+#define SD_MESSAGE_MACHINE_START_STR                  SD_ID128_MAKE_STR(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
+#define SD_MESSAGE_MACHINE_STOP                       SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
+#define SD_MESSAGE_MACHINE_STOP_STR                   SD_ID128_MAKE_STR(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
+
+#define SD_MESSAGE_TIME_CHANGE                        SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
+#define SD_MESSAGE_TIME_CHANGE_STR                    SD_ID128_MAKE_STR(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
+#define SD_MESSAGE_TIMEZONE_CHANGE                    SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
+#define SD_MESSAGE_TIMEZONE_CHANGE_STR                SD_ID128_MAKE_STR(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
+
+#define SD_MESSAGE_TAINTED                            SD_ID128_MAKE(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b)
+#define SD_MESSAGE_TAINTED_STR                        SD_ID128_MAKE_STR(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b)
+#define SD_MESSAGE_STARTUP_FINISHED                   SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
+#define SD_MESSAGE_STARTUP_FINISHED_STR               SD_ID128_MAKE_STR(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
+#define SD_MESSAGE_USER_STARTUP_FINISHED              SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
+#define SD_MESSAGE_USER_STARTUP_FINISHED_STR          SD_ID128_MAKE_STR(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
+
+#define SD_MESSAGE_SLEEP_START                        SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
+#define SD_MESSAGE_SLEEP_START_STR                    SD_ID128_MAKE_STR(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
+#define SD_MESSAGE_SLEEP_STOP                         SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
+#define SD_MESSAGE_SLEEP_STOP_STR                     SD_ID128_MAKE_STR(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
+
+#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. */
+#define SD_MESSAGE_UNIT_STARTING                      SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
+#define SD_MESSAGE_UNIT_STARTING_STR                  SD_ID128_MAKE_STR(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
+#define SD_MESSAGE_UNIT_STARTED                       SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
+#define SD_MESSAGE_UNIT_STARTED_STR                   SD_ID128_MAKE_STR(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
+#define SD_MESSAGE_UNIT_FAILED                        SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
+#define SD_MESSAGE_UNIT_FAILED_STR                    SD_ID128_MAKE_STR(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
+#define SD_MESSAGE_UNIT_STOPPING                      SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
+#define SD_MESSAGE_UNIT_STOPPING_STR                  SD_ID128_MAKE_STR(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
+#define SD_MESSAGE_UNIT_STOPPED                       SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
+#define SD_MESSAGE_UNIT_STOPPED_STR                   SD_ID128_MAKE_STR(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
+#define SD_MESSAGE_UNIT_RELOADING                     SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
+#define SD_MESSAGE_UNIT_RELOADING_STR                 SD_ID128_MAKE_STR(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
+#define SD_MESSAGE_UNIT_RELOADED                      SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
+#define SD_MESSAGE_UNIT_RELOADED_STR                  SD_ID128_MAKE_STR(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
+
+#define SD_MESSAGE_UNIT_RESTART_SCHEDULED             SD_ID128_MAKE(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3)
+#define SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR         SD_ID128_MAKE_STR(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3)
+
+#define SD_MESSAGE_UNIT_RESOURCES                     SD_ID128_MAKE(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
+#define SD_MESSAGE_UNIT_RESOURCES_STR                 SD_ID128_MAKE_STR(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
+
+#define SD_MESSAGE_UNIT_SUCCESS                       SD_ID128_MAKE(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
+#define SD_MESSAGE_UNIT_SUCCESS_STR                   SD_ID128_MAKE_STR(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
+#define SD_MESSAGE_UNIT_SKIPPED                       SD_ID128_MAKE(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73)
+#define SD_MESSAGE_UNIT_SKIPPED_STR                   SD_ID128_MAKE_STR(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73)
+#define SD_MESSAGE_UNIT_FAILURE_RESULT                SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
+#define SD_MESSAGE_UNIT_FAILURE_RESULT_STR            SD_ID128_MAKE_STR(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
+
+#define SD_MESSAGE_SPAWN_FAILED                       SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
+#define SD_MESSAGE_SPAWN_FAILED_STR                   SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
+
+#define SD_MESSAGE_UNIT_PROCESS_EXIT                  SD_ID128_MAKE(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
+#define SD_MESSAGE_UNIT_PROCESS_EXIT_STR              SD_ID128_MAKE_STR(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
+
+#define SD_MESSAGE_FORWARD_SYSLOG_MISSED              SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
+#define SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR          SD_ID128_MAKE_STR(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
+
+#define SD_MESSAGE_OVERMOUNTING                       SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
+#define SD_MESSAGE_OVERMOUNTING_STR                   SD_ID128_MAKE_STR(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
+
+#define SD_MESSAGE_UNIT_OOMD_KILL                     SD_ID128_MAKE(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed)
+#define SD_MESSAGE_UNIT_OOMD_KILL_STR                 SD_ID128_MAKE_STR(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed)
+
+#define SD_MESSAGE_UNIT_OUT_OF_MEMORY                 SD_ID128_MAKE(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef)
+#define SD_MESSAGE_UNIT_OUT_OF_MEMORY_STR             SD_ID128_MAKE_STR(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef)
+
+#define SD_MESSAGE_LID_OPENED                         SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
+#define SD_MESSAGE_LID_OPENED_STR                     SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
+#define SD_MESSAGE_LID_CLOSED                         SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
+#define SD_MESSAGE_LID_CLOSED_STR                     SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
+#define SD_MESSAGE_SYSTEM_DOCKED                      SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
+#define SD_MESSAGE_SYSTEM_DOCKED_STR                  SD_ID128_MAKE_STR(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
+#define SD_MESSAGE_SYSTEM_UNDOCKED                    SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
+#define SD_MESSAGE_SYSTEM_UNDOCKED_STR                SD_ID128_MAKE_STR(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
+#define SD_MESSAGE_POWER_KEY                          SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
+#define SD_MESSAGE_POWER_KEY_STR                      SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
+#define SD_MESSAGE_POWER_KEY_LONG_PRESS               SD_ID128_MAKE(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b)
+#define SD_MESSAGE_POWER_KEY_LONG_PRESS_STR           SD_ID128_MAKE_STR(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b)
+#define SD_MESSAGE_REBOOT_KEY                         SD_ID128_MAKE(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
+#define SD_MESSAGE_REBOOT_KEY_STR                     SD_ID128_MAKE_STR(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
+#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS              SD_ID128_MAKE(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75)
+#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS_STR          SD_ID128_MAKE_STR(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75)
+#define SD_MESSAGE_SUSPEND_KEY                        SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
+#define SD_MESSAGE_SUSPEND_KEY_STR                    SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
+#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS             SD_ID128_MAKE(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8)
+#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS_STR         SD_ID128_MAKE_STR(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8)
+#define SD_MESSAGE_HIBERNATE_KEY                      SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
+#define SD_MESSAGE_HIBERNATE_KEY_STR                  SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
+#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS           SD_ID128_MAKE(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45)
+#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS_STR       SD_ID128_MAKE_STR(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45)
+
+#define SD_MESSAGE_INVALID_CONFIGURATION              SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
+#define SD_MESSAGE_INVALID_CONFIGURATION_STR          SD_ID128_MAKE_STR(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
+
+#define SD_MESSAGE_DNSSEC_FAILURE                     SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
+#define SD_MESSAGE_DNSSEC_FAILURE_STR                 SD_ID128_MAKE_STR(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
+#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED        SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR    SD_ID128_MAKE_STR(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+#define SD_MESSAGE_DNSSEC_DOWNGRADE                   SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
+#define SD_MESSAGE_DNSSEC_DOWNGRADE_STR               SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
+
+#define SD_MESSAGE_UNSAFE_USER_NAME                   SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
+#define SD_MESSAGE_UNSAFE_USER_NAME_STR               SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
+
+#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE      SD_ID128_MAKE(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
+#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR  SD_ID128_MAKE_STR(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
+#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE           SD_ID128_MAKE(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa)
+#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE_STR       SD_ID128_MAKE_STR(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa)
+
+#define SD_MESSAGE_NOBODY_USER_UNSUITABLE             SD_ID128_MAKE(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
+#define SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR         SD_ID128_MAKE_STR(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
+
+#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED     SD_ID128_MAKE(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
+#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR SD_ID128_MAKE_STR(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
+
+#define SD_MESSAGE_TIME_SYNC                          SD_ID128_MAKE(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
+#define SD_MESSAGE_TIME_SYNC_STR                      SD_ID128_MAKE_STR(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
+
+#define SD_MESSAGE_SHUTDOWN_SCHEDULED                 SD_ID128_MAKE(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
+#define SD_MESSAGE_SHUTDOWN_SCHEDULED_STR             SD_ID128_MAKE_STR(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
+
+#define SD_MESSAGE_SHUTDOWN_CANCELED                  SD_ID128_MAKE(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
+#define SD_MESSAGE_SHUTDOWN_CANCELED_STR              SD_ID128_MAKE_STR(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
 
 _SD_END_DECLARATIONS;
 
index dcdf77c4fe5c8e464890b86167062e6522969351..2d87fe011ee093a41a0b7458e31a3a9d34021fdc 100644 (file)
@@ -71,45 +71,51 @@ int sd_netlink_detach_event(sd_netlink *nl);
 int sd_netlink_attach_filter(sd_netlink *nl, size_t len, struct sock_filter *filter);
 
 /* 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);
-int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data);
-int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data);
-int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data);
-int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data);
-int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data);
-int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data);
-int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data);
-int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data);
-int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len);
-int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data);
-int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data);
-int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, unsigned short type, const struct sockaddr_in *data);
-int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, unsigned short type, const struct sockaddr_in6 *data);
-int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data);
-int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info);
-
-int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type);
-int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key);
+int sd_netlink_message_append_string(sd_netlink_message *m, uint16_t attr_type, const char *data);
+int sd_netlink_message_append_strv(sd_netlink_message *m, uint16_t attr_type, char * const *data);
+int sd_netlink_message_append_flag(sd_netlink_message *m, uint16_t attr_type);
+int sd_netlink_message_append_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t data);
+int sd_netlink_message_append_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t data);
+int sd_netlink_message_append_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t data);
+int sd_netlink_message_append_u64(sd_netlink_message *m, uint16_t attr_type, uint64_t data);
+int sd_netlink_message_append_s8(sd_netlink_message *m, uint16_t attr_type, int8_t data);
+int sd_netlink_message_append_s16(sd_netlink_message *m, uint16_t attr_type, int16_t data);
+int sd_netlink_message_append_s32(sd_netlink_message *m, uint16_t attr_type, int32_t data);
+int sd_netlink_message_append_s64(sd_netlink_message *m, uint16_t attr_type, int64_t data);
+int sd_netlink_message_append_data(sd_netlink_message *m, uint16_t attr_type, const void *data, size_t len);
+int sd_netlink_message_append_container_data(
+                sd_netlink_message *m,
+                uint16_t container_type,
+                uint16_t attr_type,
+                const void *data,
+                size_t len);
+int sd_netlink_message_append_in_addr(sd_netlink_message *m, uint16_t attr_type, const struct in_addr *data);
+int sd_netlink_message_append_in6_addr(sd_netlink_message *m, uint16_t attr_type, const struct in6_addr *data);
+int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in *data);
+int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in6 *data);
+int sd_netlink_message_append_ether_addr(sd_netlink_message *m, uint16_t attr_type, const struct ether_addr *data);
+int sd_netlink_message_append_cache_info(sd_netlink_message *m, uint16_t attr_type, const struct ifa_cacheinfo *info);
+
+int sd_netlink_message_open_container(sd_netlink_message *m, uint16_t attr_type);
+int sd_netlink_message_open_container_union(sd_netlink_message *m, uint16_t attr_type, const char *key);
 int sd_netlink_message_close_container(sd_netlink_message *m);
 
-int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data);
-int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data);
-int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data);
-int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data);
-int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data);
-int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret);
-int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data);
-int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data);
-int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data);
-int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data);
-int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info);
-int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data);
-int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data);
-int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short type);
-int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type);
-int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type);
+int sd_netlink_message_read(sd_netlink_message *m, uint16_t attr_type, size_t size, void *data);
+int sd_netlink_message_read_data(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data);
+int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data);
+int sd_netlink_message_read_string_strdup(sd_netlink_message *m, uint16_t attr_type, char **data);
+int sd_netlink_message_read_string(sd_netlink_message *m, uint16_t attr_type, const char **data);
+int sd_netlink_message_read_strv(sd_netlink_message *m, uint16_t container_type, uint16_t attr_type, char ***ret);
+int sd_netlink_message_read_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t *data);
+int sd_netlink_message_read_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t *data);
+int sd_netlink_message_read_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t *data);
+int sd_netlink_message_read_ether_addr(sd_netlink_message *m, uint16_t attr_type, struct ether_addr *data);
+int sd_netlink_message_read_cache_info(sd_netlink_message *m, uint16_t attr_type, struct ifa_cacheinfo *info);
+int sd_netlink_message_read_in_addr(sd_netlink_message *m, uint16_t attr_type, struct in_addr *data);
+int sd_netlink_message_read_in6_addr(sd_netlink_message *m, uint16_t attr_type, struct in6_addr *data);
+int sd_netlink_message_has_flag(sd_netlink_message *m, uint16_t attr_type);
+int sd_netlink_message_enter_container(sd_netlink_message *m, uint16_t attr_type);
+int sd_netlink_message_enter_array(sd_netlink_message *m, uint16_t attr_type);
 int sd_netlink_message_exit_container(sd_netlink_message *m);
 
 int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type);
index c60dee812f0d0f7b3d23ca577b16e18f17154498..9c1abf984ecdf3886d063d08ed0b349c9469a1ec 100644 (file)
@@ -507,7 +507,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
                         break;
         }
 
-        r = fflush_and_check(passwd);
+        r = fflush_sync_and_check(passwd);
         if (r < 0)
                 return log_debug_errno(r, "Failed to flush %s: %m", passwd_tmp);
 
@@ -581,7 +581,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char
 
         ORDERED_HASHMAP_FOREACH(i, todo_uids) {
                 _cleanup_(erase_and_freep) char *creds_password = NULL;
-                _cleanup_free_ char *cn = NULL;
+                bool is_hashed;
 
                 struct spwd n = {
                         .sp_namp = i->name,
@@ -595,30 +595,16 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char
                         .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */
                 };
 
-                /* Try to pick up the password for this account via the credentials logic */
-                cn = strjoin("passwd.hashed-password.", i->name);
-                if (!cn)
-                        return -ENOMEM;
-
-                r = read_credential(cn, (void**) &creds_password, NULL);
-                if (r == -ENOENT) {
-                        _cleanup_(erase_and_freep) char *plaintext_password = NULL;
-
-                        free(cn);
-                        cn = strjoin("passwd.plaintext-password.", i->name);
-                        if (!cn)
-                                return -ENOMEM;
+                r = get_credential_user_password(i->name, &creds_password, &is_hashed);
+                if (r < 0)
+                        log_debug_errno(r, "Couldn't read password credential for user '%s', ignoring: %m", i->name);
 
-                        r = read_credential(cn, (void**) &plaintext_password, NULL);
+                if (creds_password && !is_hashed) {
+                        _cleanup_(erase_and_freep) char* plaintext_password = TAKE_PTR(creds_password);
+                        r = hash_password(plaintext_password, &creds_password);
                         if (r < 0)
-                                log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
-                        else {
-                                r = hash_password(plaintext_password, &creds_password);
-                                if (r < 0)
-                                        return log_debug_errno(r, "Failed to hash password: %m");
-                        }
-                } else if (r < 0)
-                        log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
+                                return log_debug_errno(r, "Failed to hash password: %m");
+                }
 
                 if (creds_password)
                         n.sp_pwdp = creds_password;
@@ -1169,7 +1155,7 @@ static int add_user(Item *i) {
         return 0;
 }
 
-static int gid_is_ok(gid_t gid) {
+static int gid_is_ok(gid_t gid, bool check_with_uid) {
         struct group *g;
         struct passwd *p;
 
@@ -1177,13 +1163,13 @@ static int gid_is_ok(gid_t gid) {
                 return 0;
 
         /* Avoid reusing gids that are already used by a different user */
-        if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
+        if (check_with_uid && ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
                 return 0;
 
         if (hashmap_contains(database_by_gid, GID_TO_PTR(gid)))
                 return 0;
 
-        if (hashmap_contains(database_by_uid, UID_TO_PTR(gid)))
+        if (check_with_uid && hashmap_contains(database_by_uid, UID_TO_PTR(gid)))
                 return 0;
 
         if (!arg_root) {
@@ -1194,12 +1180,14 @@ static int gid_is_ok(gid_t gid) {
                 if (!IN_SET(errno, 0, ENOENT))
                         return -errno;
 
-                errno = 0;
-                p = getpwuid((uid_t) gid);
-                if (p)
-                        return 0;
-                if (!IN_SET(errno, 0, ENOENT))
-                        return -errno;
+                if (check_with_uid) {
+                        errno = 0;
+                        p = getpwuid((uid_t) gid);
+                        if (p)
+                                return 0;
+                        if (!IN_SET(errno, 0, ENOENT))
+                                return -errno;
+                }
         }
 
         return 1;
@@ -1250,7 +1238,7 @@ static int add_group(Item *i) {
 
         /* Try to use the suggested numeric GID */
         if (i->gid_set) {
-                r = gid_is_ok(i->gid);
+                r = gid_is_ok(i->gid, false);
                 if (r < 0)
                         return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
                 if (i->id_set_strict) {
@@ -1273,7 +1261,7 @@ static int add_group(Item *i) {
 
         /* Try to reuse the numeric uid, if there's one */
         if (!i->gid_set && i->uid_set) {
-                r = gid_is_ok((gid_t) i->uid);
+                r = gid_is_ok((gid_t) i->uid, true);
                 if (r < 0)
                         return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
                 if (r > 0) {
@@ -1291,7 +1279,7 @@ static int add_group(Item *i) {
                         if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
                                 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
                         else {
-                                r = gid_is_ok(c);
+                                r = gid_is_ok(c, true);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
                                 else if (r > 0) {
@@ -1313,7 +1301,7 @@ static int add_group(Item *i) {
                         if (r < 0)
                                 return log_error_errno(r, "No free group ID available for %s.", i->name);
 
-                        r = gid_is_ok(search_uid);
+                        r = gid_is_ok(search_uid, true);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
                         else if (r > 0)
@@ -1346,9 +1334,11 @@ static int process_item(Item *i) {
         switch (i->type) {
 
         case ADD_USER: {
-                Item *j;
+                Item *j = NULL;
+
+                if (!i->gid_set)
+                        j = ordered_hashmap_get(groups, i->group_name ?: i->name);
 
-                j = ordered_hashmap_get(groups, i->group_name ?: i->name);
                 if (j && j->todo_group) {
                         /* When a group with the target name is already in queue,
                          * use the information about the group and do not create
@@ -1981,7 +1971,7 @@ static int parse_arguments(char **args) {
                         /* Use (argument):n, where n==1 for the first positional arg */
                         r = parse_line("(argument)", pos, *arg);
                 else
-                        r = read_config_file(*arg, false);
+                        r = read_config_file(*arg, /* ignore_enoent= */ false);
                 if (r < 0)
                         return r;
 
@@ -2011,12 +2001,31 @@ static int read_config_files(char **args) {
                         log_debug("Reading config file \"%s\"%s", *f, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
 
                         /* Just warn, ignore result otherwise */
-                        (void) read_config_file(*f, true);
+                        (void) read_config_file(*f, /* ignore_enoent= */ true);
                 }
 
         return 0;
 }
 
+static int read_credential_lines(void) {
+        _cleanup_free_ char *j = NULL;
+        const char *d;
+        int r;
+
+        r = get_credentials_dir(&d);
+        if (r == -ENXIO)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to get credentials directory: %m");
+
+        j = path_join(d, "sysusers.extra");
+        if (!j)
+                return log_oom();
+
+        (void) read_config_file(j, /* ignore_enoent= */ true);
+        return 0;
+}
+
 static int run(int argc, char *argv[]) {
 #ifndef STANDALONE
         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
@@ -2068,12 +2077,10 @@ static int run(int argc, char *argv[]) {
         assert(!arg_image);
 #endif
 
-        /* If command line arguments are specified along with --replace, read all
-         * configuration files and insert the positional arguments at the specified
-         * place. Otherwise, if command line arguments are specified, execute just
-         * them, and finally, without --replace= or any positional arguments, just
-         * read configuration and execute it.
-         */
+        /* If command line arguments are specified along with --replace, read all configuration files and
+         * insert the positional arguments at the specified place. Otherwise, if command line arguments are
+         * specified, execute just them, and finally, without --replace= or any positional arguments, just
+         * read configuration and execute it. */
         if (arg_replace || optind >= argc)
                 r = read_config_files(argv + optind);
         else
@@ -2081,11 +2088,15 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
-         * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
-         * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
-         * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
-         * /etc. */
+        r = read_credential_lines();
+        if (r < 0)
+                return r;
+
+        /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our
+         * detection whether the names or UID/GID area already used otherwise doesn't get confused. After
+         * all, even though nss-systemd synthesizes these users/groups, they should still appear in
+         * /etc/passwd and /etc/group, as the synthesizing logic is merely supposed to be fallback for cases
+         * where we run with a completely unpopulated /etc. */
         if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0)
                 return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
 
index cc590f4f3d91522086566f96ab41097382a2fedc..31ac149b966f742a057b88377cc05558177f6f0c 100644 (file)
@@ -209,6 +209,10 @@ tests += [
 
         [files('test-macro.c')],
 
+        [files('test-math-util.c'),
+         [],
+         [libm]],
+
         [files('test-mkdir.c')],
 
         [files('test-json.c'),
@@ -228,6 +232,8 @@ tests += [
 
         [files('test-exec-util.c')],
 
+        [files('test-execve.c')],
+
         [files('test-hexdecoct.c')],
 
         [files('test-alloc-util.c')],
index fb82f44d04a5b0b1b962633426279395bbf54dea..56b5ad88a251588b88dd94cee279bd453858cea5 100644 (file)
@@ -15,7 +15,9 @@
 #include "condition.h"
 #include "cpu-set-util.h"
 #include "efi-loader.h"
+#include "env-util.h"
 #include "errno-util.h"
+#include "fs-util.h"
 #include "hostname-util.h"
 #include "id128-util.h"
 #include "ima-util.h"
 #include "macro.h"
 #include "nulstr-util.h"
 #include "os-util.h"
+#include "path-util.h"
 #include "process-util.h"
 #include "psi-util.h"
+#include "rm-rf.h"
 #include "selinux-util.h"
 #include "set.h"
 #include "smack-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tests.h"
+#include "tmpfile-util.h"
 #include "tomoyo-util.h"
 #include "udev-util.h"
 #include "uid-alloc-range.h"
@@ -460,6 +465,60 @@ TEST(condition_test_kernel_version) {
         condition_free(condition);
 }
 
+TEST(condition_test_credential) {
+        _cleanup_(rm_rf_physical_and_freep) char *n1 = NULL, *n2 = NULL;
+        _cleanup_free_ char *d1 = NULL, *d2 = NULL, *j = NULL;
+        Condition *condition;
+
+        assert_se(free_and_strdup(&d1, getenv("CREDENTIALS_DIRECTORY")) >= 0);
+        assert_se(free_and_strdup(&d2, getenv("ENCRYPTED_CREDENTIALS_DIRECTORY")) >= 0);
+
+        assert_se(unsetenv("CREDENTIALS_DIRECTORY") >= 0);
+        assert_se(unsetenv("ENCRYPTED_CREDENTIALS_DIRECTORY") >= 0);
+
+        condition = condition_new(CONDITION_CREDENTIAL, "definitelymissing", /* trigger= */ false, /* negate= */ false);
+        assert_se(condition);
+        assert_se(condition_test(condition, environ) == 0);
+        condition_free(condition);
+
+        /* invalid */
+        condition = condition_new(CONDITION_CREDENTIAL, "..", /* trigger= */ false, /* negate= */ false);
+        assert_se(condition);
+        assert_se(condition_test(condition, environ) == 0);
+        condition_free(condition);
+
+        assert_se(mkdtemp_malloc(NULL, &n1) >= 0);
+        assert_se(mkdtemp_malloc(NULL, &n2) >= 0);
+
+        assert_se(setenv("CREDENTIALS_DIRECTORY", n1, /* overwrite= */ true) >= 0);
+        assert_se(setenv("ENCRYPTED_CREDENTIALS_DIRECTORY", n2, /* overwrite= */ true) >= 0);
+
+        condition = condition_new(CONDITION_CREDENTIAL, "stillmissing", /* trigger= */ false, /* negate= */ false);
+        assert_se(condition);
+        assert_se(condition_test(condition, environ) == 0);
+        condition_free(condition);
+
+        assert_se(j = path_join(n1, "existing"));
+        assert_se(touch(j) >= 0);
+        assert_se(j);
+        condition = condition_new(CONDITION_CREDENTIAL, "existing", /* trigger= */ false, /* negate= */ false);
+        assert_se(condition);
+        assert_se(condition_test(condition, environ) > 0);
+        condition_free(condition);
+        free(j);
+
+        assert_se(j = path_join(n2, "existing-encrypted"));
+        assert_se(touch(j) >= 0);
+        assert_se(j);
+        condition = condition_new(CONDITION_CREDENTIAL, "existing-encrypted", /* trigger= */ false, /* negate= */ false);
+        assert_se(condition);
+        assert_se(condition_test(condition, environ) > 0);
+        condition_free(condition);
+
+        assert_se(set_unset_env("CREDENTIALS_DIRECTORY", d1, /* overwrite= */ true) >= 0);
+        assert_se(set_unset_env("ENCRYPTED_CREDENTIALS_DIRECTORY", d2, /* overwrite= */ true) >= 0);
+}
+
 #if defined(__i386__) || defined(__x86_64__)
 TEST(condition_test_cpufeature) {
         Condition *condition;
index a669df644baf7450a6c5303473b73629fab058df..4091b425d16b5919977e584b11e122837891e21f 100644 (file)
@@ -50,6 +50,75 @@ TEST(copy_file) {
         unlink(fn_copy);
 }
 
+static bool read_file_and_streq(const char* filepath, const char* expected_contents) {
+        _cleanup_free_ char *buf = NULL;
+
+        assert_se(read_full_file(filepath, &buf, NULL) == 0);
+        return streq(buf, expected_contents);
+}
+
+TEST(copy_tree_replace_file) {
+        _cleanup_free_ char *src = NULL, *dst = NULL;
+
+        assert_se(tempfn_random("/tmp/test-copy_file.XXXXXX", NULL, &src) >= 0);
+        assert_se(tempfn_random("/tmp/test-copy_file.XXXXXX", NULL, &dst) >= 0);
+
+        assert_se(write_string_file(src, "bar bar", WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(dst, "foo foo foo", WRITE_STRING_FILE_CREATE) == 0);
+
+        /* The file exists- now overwrite original contents, and test the COPY_REPLACE flag. */
+
+        assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK) == -EEXIST);
+
+        assert_se(read_file_and_streq(dst, "foo foo foo\n"));
+
+        assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE) == 0);
+
+        assert_se(read_file_and_streq(dst, "bar bar\n"));
+}
+
+TEST(copy_tree_replace_dirs) {
+        _cleanup_free_ char *src_path1 = NULL, *src_path2 = NULL, *dst_path1 = NULL, *dst_path2 = NULL;
+        _cleanup_(rm_rf_physical_and_freep) char *src_directory = NULL, *dst_directory = NULL;
+        const char *file1 = "foo_file", *file2 = "bar_file";
+
+        /* Create the random source/destination directories */
+        assert_se(mkdtemp_malloc("/tmp/dirXXXXXX", &src_directory) >= 0);
+        assert_se(mkdtemp_malloc("/tmp/dirXXXXXX", &dst_directory) >= 0);
+
+        /* Construct the source/destination filepaths (should have different dir name, but same file names within) */
+        assert_se(src_path1 = path_join(src_directory, file1));
+        assert_se(src_path2 = path_join(src_directory, file2));
+        assert_se(dst_path1 = path_join(dst_directory, file1));
+        assert_se(dst_path2 = path_join(dst_directory, file2));
+
+        /* Populate some data to differentiate the files. */
+        assert_se(write_string_file(src_path1, "src file 1", WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(src_path2, "src file 2", WRITE_STRING_FILE_CREATE) == 0);
+
+        assert_se(write_string_file(dst_path1, "dest file 1", WRITE_STRING_FILE_CREATE) == 0);
+        assert_se(write_string_file(dst_path2, "dest file 2", WRITE_STRING_FILE_CREATE) == 0);
+
+        /* Copying without COPY_REPLACE should fail because the destination file already exists. */
+        assert_se(copy_tree(src_directory, dst_directory, UID_INVALID, GID_INVALID, COPY_REFLINK) == -EEXIST);
+
+        {
+                assert_se(read_file_and_streq(src_path1,  "src file 1\n"));
+                assert_se(read_file_and_streq(src_path2,  "src file 2\n"));
+                assert_se(read_file_and_streq(dst_path1,  "dest file 1\n"));
+                assert_se(read_file_and_streq(dst_path2,  "dest file 2\n"));
+        }
+
+        assert_se(copy_tree(src_directory, dst_directory, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE) == 0);
+
+        {
+                assert_se(read_file_and_streq(src_path1,  "src file 1\n"));
+                assert_se(read_file_and_streq(src_path2,  "src file 2\n"));
+                assert_se(read_file_and_streq(dst_path1,  "src file 1\n"));
+                assert_se(read_file_and_streq(dst_path2,  "src file 2\n"));
+        }
+}
+
 TEST(copy_file_fd) {
         char in_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
         char out_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
diff --git a/src/test/test-execve.c b/src/test/test-execve.c
new file mode 100644 (file)
index 0000000..38adc72
--- /dev/null
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "exec-util.h"
+#include "fd-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "strv.h"
+#include "tests.h"
+
+/* This program can be used to call programs through fexecve / execveat(…, "", …, AT_EMPTY_PATH),
+ * when compiled with -Dfexecve=true, and the fallback paths, when -Dfexecve=false.
+ *
+ * Example:
+ * $ strace -e execveat build/test-execve /bin/grep Name /proc/self/status
+ * execveat(3, "", ["/bin/grep", "Name", "/proc/self/status"], NULL, AT_EMPTY_PATH) = 0
+ * Name:   3
+ *
+ * FIXME: use the new kernel api to set COMM properly when the kernel makes that available.
+ * C.f. ceedbf8185fc7593366679f02d31da63af8c4bd1.
+ */
+
+static int run(int argc, char **argv) {
+        _cleanup_close_ int fd;
+        char **args = strv_skip(argv, 1);
+        int r;
+
+        test_setup_logging(LOG_DEBUG);
+
+        args = !strv_isempty(args) ? args : STRV_MAKE("/bin/true");
+
+        fd = open(args[0], O_RDONLY | O_CLOEXEC);
+        if (fd < 0)
+                return log_error_errno(errno, "open(%s) failed: %m", args[0]);
+
+        r = fexecve_or_execve(fd, args[0], args, NULL);
+        assert_se(r < 0);
+        return log_error_errno(r, "fexecve_or_execve(%s) failed: %m", args[0]);
+}
+
+DEFINE_MAIN_FUNCTION(run);
index 1178843f68568432cb97c7a07ead0126ea92c285..d22485630ac8b2e84c1ee67e298f467461127877 100644 (file)
@@ -1,7 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <float.h>
-#include <math.h>
 
 #include "alloc-util.h"
 #include "escape.h"
@@ -9,6 +8,7 @@
 #include "fileio.h"
 #include "json-internal.h"
 #include "json.h"
+#include "math-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tests.h"
@@ -50,8 +50,8 @@ static void test_tokenizer_one(const char *data, ...) {
 
                         d = va_arg(ap, double);
 
-                        assert_se(fabsl(d - v.real) < 1e-10 ||
-                                  fabsl((d - v.real) / v.real) < 1e-10);
+                        assert_se(fabs(d - v.real) < 1e-10 ||
+                                  fabs((d - v.real) / v.real) < 1e-10);
 
                 } else if (t == JSON_TOKEN_INTEGER) {
                         int64_t i;
@@ -221,7 +221,7 @@ static void test_2(JsonVariant *v) {
 
         /* has thisisaverylongproperty */
         p = json_variant_by_key(v, "thisisaverylongproperty");
-        assert_se(p && json_variant_type(p) == JSON_VARIANT_REAL && fabsl(json_variant_real(p) - 1.27) < 0.001);
+        assert_se(p && json_variant_type(p) == JSON_VARIANT_REAL && fabs(json_variant_real(p) - 1.27) < 0.001);
 }
 
 static void test_zeroes(JsonVariant *v) {
@@ -239,9 +239,7 @@ static void test_zeroes(JsonVariant *v) {
                 assert_se(json_variant_integer(w) == 0);
                 assert_se(json_variant_unsigned(w) == 0U);
 
-                DISABLE_WARNING_FLOAT_EQUAL;
-                assert_se(json_variant_real(w) == 0.0L);
-                REENABLE_WARNING;
+                assert_se(iszero_safe(json_variant_real(w)));
 
                 assert_se(json_variant_is_integer(w));
                 assert_se(json_variant_is_unsigned(w));
@@ -511,14 +509,14 @@ static void test_float_match(JsonVariant *v) {
         const double delta = 0.0001;
 
         assert_se(json_variant_is_array(v));
-        assert_se(json_variant_elements(v) == 9);
-        assert_se(fabsl((double) 1.0 - ((double) DBL_MIN / json_variant_real(json_variant_by_index(v, 0)))) <= delta);
-        assert_se(fabsl((double) 1.0 - ((double) DBL_MAX / json_variant_real(json_variant_by_index(v, 1)))) <= delta);
+        assert_se(json_variant_elements(v) == 11);
+        assert_se(fabs(1.0 - (DBL_MIN / json_variant_real(json_variant_by_index(v, 0)))) <= delta);
+        assert_se(fabs(1.0 - (DBL_MAX / json_variant_real(json_variant_by_index(v, 1)))) <= delta);
         assert_se(json_variant_is_null(json_variant_by_index(v, 2))); /* nan is not supported by json → null */
         assert_se(json_variant_is_null(json_variant_by_index(v, 3))); /* +inf is not supported by json → null */
         assert_se(json_variant_is_null(json_variant_by_index(v, 4))); /* -inf is not supported by json → null */
         assert_se(json_variant_is_null(json_variant_by_index(v, 5)) ||
-                  fabsl((double) 1.0 - ((double) HUGE_VAL / json_variant_real(json_variant_by_index(v, 5)))) <= delta); /* HUGE_VAL might be +inf, but might also be something else */
+                  fabs(1.0 - (HUGE_VAL / json_variant_real(json_variant_by_index(v, 5)))) <= delta); /* HUGE_VAL might be +inf, but might also be something else */
         assert_se(json_variant_is_real(json_variant_by_index(v, 6)) &&
                   json_variant_is_integer(json_variant_by_index(v, 6)) &&
                   json_variant_integer(json_variant_by_index(v, 6)) == 0);
@@ -528,6 +526,12 @@ static void test_float_match(JsonVariant *v) {
         assert_se(json_variant_is_real(json_variant_by_index(v, 8)) &&
                   json_variant_is_integer(json_variant_by_index(v, 8)) &&
                   json_variant_integer(json_variant_by_index(v, 8)) == -10);
+        assert_se(json_variant_is_real(json_variant_by_index(v, 9)) &&
+                  !json_variant_is_integer(json_variant_by_index(v, 9)));
+        assert_se(fabs(1.0 - (DBL_MIN / 2 / json_variant_real(json_variant_by_index(v, 9)))) <= delta);
+        assert_se(json_variant_is_real(json_variant_by_index(v, 10)) &&
+                  !json_variant_is_integer(json_variant_by_index(v, 10)));
+        assert_se(fabs(1.0 - (-DBL_MIN / 2 / json_variant_real(json_variant_by_index(v, 10)))) <= delta);
 }
 
 TEST(float) {
@@ -543,7 +547,9 @@ TEST(float) {
                                              JSON_BUILD_REAL(HUGE_VAL),
                                              JSON_BUILD_REAL(0),
                                              JSON_BUILD_REAL(10),
-                                             JSON_BUILD_REAL(-10))) >= 0);
+                                             JSON_BUILD_REAL(-10),
+                                             JSON_BUILD_REAL(DBL_MIN / 2),
+                                             JSON_BUILD_REAL(-DBL_MIN / 2))) >= 0);
 
         json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
 
index c39f64b385edfb7995e05fd1c219b9ccd71f524a..049ea2c14e1d3d39230df0a83f66528c6cf8302c 100644 (file)
@@ -465,4 +465,60 @@ TEST(PTR_SUB1) {
         assert_se(!p);
 }
 
+TEST(ISPOWEROF2) {
+        uint64_t u;
+        int64_t i;
+
+        /* First, test constant expressions */
+        assert_se(!ISPOWEROF2(-2));
+        assert_se(!ISPOWEROF2(-1));
+        assert_se(!ISPOWEROF2(0));
+        assert_se(ISPOWEROF2(1));
+        assert_se(ISPOWEROF2(2));
+        assert_se(!ISPOWEROF2(3));
+        assert_se(ISPOWEROF2(4));
+        assert_se(!ISPOWEROF2(5));
+        assert_se(!ISPOWEROF2(6));
+        assert_se(!ISPOWEROF2(7));
+        assert_se(ISPOWEROF2(8));
+        assert_se(!ISPOWEROF2(9));
+        assert_se(!ISPOWEROF2(1022));
+        assert_se(ISPOWEROF2(1024));
+        assert_se(!ISPOWEROF2(1025));
+        assert_se(!ISPOWEROF2(UINT64_C(0xffffffff)));
+        assert_se(ISPOWEROF2(UINT64_C(0x100000000)));
+        assert_se(!ISPOWEROF2(UINT64_C(0x100000001)));
+
+        /* Then, test dynamic expressions, and if they are side-effect free */
+        i = -2;
+        assert_se(!ISPOWEROF2(i++));
+        assert_se(i == -1);
+        assert_se(!ISPOWEROF2(i++));
+        assert_se(i == 0);
+        assert_se(!ISPOWEROF2(i++));
+        assert_se(i == 1);
+        assert_se(ISPOWEROF2(i++));
+        assert_se(i == 2);
+        assert_se(ISPOWEROF2(i++));
+        assert_se(i == 3);
+        assert_se(!ISPOWEROF2(i++));
+        assert_se(i == 4);
+        assert_se(ISPOWEROF2(i++));
+        assert_se(i == 5);
+        assert_se(!ISPOWEROF2(i));
+
+        u = 0;
+        assert_se(!ISPOWEROF2(u++));
+        assert_se(u == 1);
+        assert_se(ISPOWEROF2(u++));
+        assert_se(u == 2);
+        assert_se(ISPOWEROF2(u++));
+        assert_se(u == 3);
+        assert_se(!ISPOWEROF2(u++));
+        assert_se(u == 4);
+        assert_se(ISPOWEROF2(u++));
+        assert_se(u == 5);
+        assert_se(!ISPOWEROF2(u));
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
diff --git a/src/test/test-math-util.c b/src/test/test-math-util.c
new file mode 100644 (file)
index 0000000..891bbfc
--- /dev/null
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <float.h>
+
+#include "math-util.h"
+#include "tests.h"
+
+TEST(iszero_safe) {
+        /* zeros */
+        assert_se(iszero_safe(0.0));
+        assert_se(iszero_safe(-0.0));
+        assert_se(iszero_safe(0e0));
+        assert_se(iszero_safe(-0e0));
+        assert_se(iszero_safe(0e+0));
+        assert_se(iszero_safe(0e-0));
+        assert_se(iszero_safe(-0e-0));
+        assert_se(iszero_safe(-0e000));
+        assert_se(iszero_safe(0e000));
+
+        /* non-zero normal values */
+        assert_se(!iszero_safe(42.0));
+        assert_se(!iszero_safe(M_PI));
+        assert_se(!iszero_safe(DBL_MAX));
+        assert_se(!iszero_safe(-DBL_MAX));
+        assert_se(!iszero_safe(DBL_MIN));
+        assert_se(!iszero_safe(-DBL_MIN));
+        assert_se(!iszero_safe(1 / DBL_MAX));
+
+        /* subnormal values */
+        assert_se(!iszero_safe(DBL_MIN / 2));
+        assert_se(!iszero_safe(-DBL_MIN / 42));
+        assert_se(!iszero_safe(1 / DBL_MAX / 2));
+
+        /* too small values which cannot be in subnormal form */
+        assert_se( iszero_safe(DBL_MIN / DBL_MAX));
+        assert_se( iszero_safe(DBL_MIN / -DBL_MAX));
+        assert_se( iszero_safe(-DBL_MIN / DBL_MAX));
+        assert_se( iszero_safe(-DBL_MIN / -DBL_MAX));
+
+        /* NaN or infinity */
+        assert_se(!iszero_safe(NAN));
+        assert_se(!iszero_safe(INFINITY));
+        assert_se(!iszero_safe(-INFINITY));
+        assert_se(!iszero_safe(1 / NAN));
+
+        /* inverse of infinity */
+        assert_se( iszero_safe(1 / INFINITY));
+        assert_se( iszero_safe(1 / -INFINITY));
+        assert_se( iszero_safe(-1 / INFINITY));
+        assert_se( iszero_safe(-1 / -INFINITY));
+        assert_se( iszero_safe(42 / -INFINITY));
+        assert_se( iszero_safe(-42 / -INFINITY));
+        assert_se( iszero_safe(DBL_MIN / INFINITY));
+        assert_se( iszero_safe(DBL_MIN / -INFINITY));
+        assert_se( iszero_safe(DBL_MAX / INFINITY / 2));
+        assert_se( iszero_safe(DBL_MAX / -INFINITY * DBL_MAX));
+        assert_se(!iszero_safe(DBL_MAX * 2 / INFINITY));
+
+        /* infinity / infinity is NaN */
+        assert_se(!iszero_safe(INFINITY / INFINITY));
+        assert_se(!iszero_safe(INFINITY * 2 / INFINITY));
+        assert_se(!iszero_safe(INFINITY / DBL_MAX / INFINITY));
+}
+
+TEST(fp_equal) {
+        /* normal values */
+        assert_se( fp_equal(0.0, -0e0));
+        assert_se( fp_equal(3.0, 3));
+        assert_se(!fp_equal(3.000001, 3));
+        assert_se( fp_equal(M_PI, M_PI));
+        assert_se(!fp_equal(M_PI, -M_PI));
+        assert_se( fp_equal(DBL_MAX, DBL_MAX));
+        assert_se(!fp_equal(DBL_MAX, -DBL_MAX));
+        assert_se(!fp_equal(-DBL_MAX, DBL_MAX));
+        assert_se( fp_equal(-DBL_MAX, -DBL_MAX));
+        assert_se( fp_equal(DBL_MIN, DBL_MIN));
+        assert_se(!fp_equal(DBL_MIN, -DBL_MIN));
+        assert_se(!fp_equal(-DBL_MIN, DBL_MIN));
+        assert_se( fp_equal(-DBL_MIN, -DBL_MIN));
+
+        /* subnormal values */
+        assert_se( fp_equal(DBL_MIN / 10, DBL_MIN / 10));
+        assert_se(!fp_equal(DBL_MIN / 10, -DBL_MIN / 10));
+        assert_se(!fp_equal(-DBL_MIN / 10, DBL_MIN / 10));
+        assert_se( fp_equal(-DBL_MIN / 10, -DBL_MIN / 10));
+        assert_se(!fp_equal(DBL_MIN / 10, DBL_MIN / 15));
+        assert_se(!fp_equal(DBL_MIN / 10, DBL_MIN / 15));
+
+        /* subnormal difference */
+        assert_se(!fp_equal(DBL_MIN / 10, DBL_MIN + DBL_MIN / 10));
+        assert_se( fp_equal(3.0, 3.0 + DBL_MIN / 2)); /* 3.0 + DBL_MIN / 2 is truncated to 3.0 */
+
+        /* too small values */
+        assert_se( fp_equal(DBL_MIN / DBL_MAX, -DBL_MIN / DBL_MAX));
+
+        /* NaN or infinity */
+        assert_se(!fp_equal(NAN, NAN));
+        assert_se(!fp_equal(NAN, 0));
+        assert_se(!fp_equal(NAN, INFINITY));
+        assert_se(!fp_equal(INFINITY, INFINITY));
+        assert_se(!fp_equal(INFINITY, -INFINITY));
+        assert_se(!fp_equal(-INFINITY, INFINITY));
+        assert_se(!fp_equal(-INFINITY, -INFINITY));
+
+        /* inverse of infinity */
+        assert_se( fp_equal(0, 1 / INFINITY));
+        assert_se( fp_equal(42 / INFINITY, 1 / -INFINITY));
+        assert_se(!fp_equal(42 / INFINITY, INFINITY / INFINITY));
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
index 6daab022aa15cc7cf8b9afdc8deb562f4c8b3fed..bf0de0c4c78ab1b02c55bf638b2893ba75416227 100644 (file)
@@ -265,6 +265,7 @@ TEST(path_is_mount_point) {
 
 TEST(fd_is_mount_point) {
         _cleanup_close_ int fd = -1;
+        int r;
 
         fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
         assert_se(fd >= 0);
@@ -290,6 +291,22 @@ TEST(fd_is_mount_point) {
          * the system is borked. Let's allow for it to be missing though. */
         assert_se(IN_SET(fd_is_mount_point(fd, "root", 0), -ENOENT, 0));
         assert_se(IN_SET(fd_is_mount_point(fd, "root/", 0), -ENOENT, 0));
+
+        safe_close(fd);
+        fd = open("/proc", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
+        assert_se(fd >= 0);
+
+        assert_se(fd_is_mount_point(fd, NULL, 0) > 0);
+        assert_se(fd_is_mount_point(fd, "", 0) == -EINVAL);
+        assert_se(fd_is_mount_point(fd, "version", 0) == 0);
+
+        safe_close(fd);
+        fd = open("/proc/version", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        assert_se(fd >= 0);
+
+        r = fd_is_mount_point(fd, NULL, 0);
+        assert_se(IN_SET(r, 0, -ENOTDIR)); /* on old kernels we can't determine if regular files are mount points if we have no directory fd */
+        assert_se(fd_is_mount_point(fd, "", 0) == -EINVAL);
 }
 
 static int intro(void) {
index 4c56a7d52008a7b0f29bb7d7b9b808ae9a4534b2..e0583cfbaba762ce1ff375691a16053ea72521e0 100644 (file)
@@ -1027,6 +1027,44 @@ TEST(path_startswith_strv) {
         assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL));
 }
 
+static void test_path_glob_can_match_one(const char *pattern, const char *prefix, const char *expected) {
+        _cleanup_free_ char *result = NULL;
+
+        log_debug("%s(%s, %s, %s)", __func__, pattern, prefix, strnull(expected));
+
+        assert_se(path_glob_can_match(pattern, prefix, &result) == !!expected);
+        assert_se(streq_ptr(result, expected));
+}
+
+TEST(path_glob_can_match) {
+        test_path_glob_can_match_one("/foo/hoge/aaa", "/foo/hoge/aaa/bbb", NULL);
+        test_path_glob_can_match_one("/foo/hoge/aaa", "/foo/hoge/aaa", "/foo/hoge/aaa");
+        test_path_glob_can_match_one("/foo/hoge/aaa", "/foo/hoge", "/foo/hoge/aaa");
+        test_path_glob_can_match_one("/foo/hoge/aaa", "/foo", "/foo/hoge/aaa");
+        test_path_glob_can_match_one("/foo/hoge/aaa", "/", "/foo/hoge/aaa");
+
+        test_path_glob_can_match_one("/foo/*/aaa", "/foo/hoge/aaa/bbb", NULL);
+        test_path_glob_can_match_one("/foo/*/aaa", "/foo/hoge/aaa", "/foo/hoge/aaa");
+        test_path_glob_can_match_one("/foo/*/aaa", "/foo/hoge", "/foo/hoge/aaa");
+        test_path_glob_can_match_one("/foo/*/aaa", "/foo", "/foo/*/aaa");
+        test_path_glob_can_match_one("/foo/*/aaa", "/", "/foo/*/aaa");
+
+        test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx/yyy/aaa/bbb", NULL);
+        test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx/yyy/aaa", "/foo/xxx/yyy/aaa");
+        test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx/yyy", "/foo/xxx/yyy/aaa");
+        test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx", "/foo/xxx/*/aaa");
+        test_path_glob_can_match_one("/foo/*/*/aaa", "/foo", "/foo/*/*/aaa");
+        test_path_glob_can_match_one("/foo/*/*/aaa", "/", "/foo/*/*/aaa");
+
+        test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/aaa/bbb/ccc", NULL);
+        test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/aaa/bbb", "/foo/xxx/aaa/bbb");
+        test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/ccc", NULL);
+        test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/aaa", "/foo/xxx/aaa/*");
+        test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx", "/foo/xxx/aaa/*");
+        test_path_glob_can_match_one("/foo/*/aaa/*", "/foo", "/foo/*/aaa/*");
+        test_path_glob_can_match_one("/foo/*/aaa/*", "/", "/foo/*/aaa/*");
+}
+
 TEST(print_MAX) {
         log_info("PATH_MAX=%zu\n"
                  "FILENAME_MAX=%zu\n"
index 0069f444e930c50adaaf1cb5a6c3ca46e0d778cc..07432a1e51ecc0d34378da1aa2f2bb918255a783 100644 (file)
@@ -25,6 +25,7 @@
 #include "chattr-util.h"
 #include "conf-files.h"
 #include "copy.h"
+#include "creds-util.h"
 #include "def.h"
 #include "devnum-util.h"
 #include "dirent-util.h"
@@ -36,6 +37,7 @@
 #include "format-util.h"
 #include "fs-util.h"
 #include "glob-util.h"
+#include "hexdecoct.h"
 #include "io-util.h"
 #include "label.h"
 #include "log.h"
@@ -127,6 +129,8 @@ typedef struct Item {
 
         char *path;
         char *argument;
+        void *binary_argument;        /* set if binary data, in which case it takes precedence over 'argument' */
+        size_t binary_argument_size;
         char **xattrs;
 #if HAVE_ACL
         acl_t acl_access;
@@ -461,6 +465,17 @@ static bool unix_socket_alive(const char *fn) {
         return set_contains(unix_sockets, fn);
 }
 
+/* Accessors for the argument in binary format */
+static const void* item_binary_argument(const Item *i) {
+        assert(i);
+        return i->binary_argument ?: i->argument;
+}
+
+static size_t item_binary_argument_size(const Item *i) {
+        assert(i);
+        return i->binary_argument ? i->binary_argument_size : strlen_ptr(i->argument);
+}
+
 static DIR* xopendirat_nomod(int dirfd, const char *path) {
         DIR *dir;
 
@@ -985,10 +1000,8 @@ static int parse_xattrs_from_arg(Item *i) {
         int r;
 
         assert(i);
-        assert(i->argument);
-
-        p = i->argument;
 
+        assert_se(p = i->argument);
         for (;;) {
                 _cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL;
 
@@ -1329,6 +1342,27 @@ static int path_set_attribute(Item *item, const char *path) {
         return fd_set_attribute(item, fd, path, NULL);
 }
 
+static int write_argument_data(Item *i, int fd, const char *path) {
+        int r;
+
+        assert(i);
+        assert(fd >= 0);
+        assert(path);
+
+        if (item_binary_argument_size(i) == 0)
+                return 0;
+
+        assert(item_binary_argument(i));
+
+        log_debug("Writing to \"%s\".", path);
+
+        r = loop_write(fd, item_binary_argument(i), item_binary_argument_size(i), /* do_poll= */ false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write file \"%s\": %m", path);
+
+        return 0;
+}
+
 static int write_one_file(Item *i, const char *path) {
         _cleanup_close_ int fd = -1, dir_fd = -1;
         _cleanup_free_ char *bn = NULL;
@@ -1336,7 +1370,6 @@ static int write_one_file(Item *i, const char *path) {
 
         assert(i);
         assert(path);
-        assert(i->argument);
         assert(i->type == WRITE_FILE);
 
         r = path_extract_filename(path, &bn);
@@ -1368,11 +1401,10 @@ static int write_one_file(Item *i, const char *path) {
         }
 
         /* 'w' is allowed to write into any kind of files. */
-        log_debug("Writing to \"%s\".", path);
 
-        r = loop_write(fd, i->argument, strlen(i->argument), false);
+        r = write_argument_data(i, fd, path);
         if (r < 0)
-                return log_error_errno(r, "Failed to write file \"%s\": %m", path);
+                return r;
 
         return fd_set_perms(i, fd, path, NULL);
 }
@@ -1432,17 +1464,10 @@ static int create_file(Item *i, const char *path) {
                                                path);
 
                 st = &stbuf;
-        } else {
-
-                log_debug("\"%s\" has been created.", path);
-
-                if (i->argument) {
-                        log_debug("Writing to \"%s\".", path);
-
-                        r = loop_write(fd, i->argument, strlen(i->argument), false);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to write file \"%s\": %m", path);
-                }
+        } else if (item_binary_argument(i)) {
+                r = write_argument_data(i, fd, path);
+                if (r < 0)
+                        return r;
         }
 
         return fd_set_perms(i, fd, path, st);
@@ -1522,12 +1547,10 @@ static int truncate_file(Item *i, const char *path) {
 
         log_debug("\"%s\" has been created.", path);
 
-        if (i->argument) {
-                log_debug("Writing to \"%s\".", path);
-
-                r = loop_write(fd, i->argument, strlen(i->argument), false);
+        if (item_binary_argument(i)) {
+                r = write_argument_data(i, fd, path);
                 if (r < 0)
-                        return log_error_errno(erofs ? -EROFS : r, "Failed to write file %s: %m", path);
+                        return r;
         }
 
         return fd_set_perms(i, fd, path, st);
@@ -2643,6 +2666,7 @@ static void item_free_contents(Item *i) {
         assert(i);
         free(i->path);
         free(i->argument);
+        free(i->binary_argument);
         strv_free(i->xattrs);
 
 #if HAVE_ACL
@@ -2684,7 +2708,8 @@ static bool item_compatible(const Item *a, const Item *b) {
 
         if (takes_ownership(a->type) && takes_ownership(b->type))
                 /* check if the items are the same */
-                return  streq_ptr(a->argument, b->argument) &&
+                return memcmp_nn(item_binary_argument(a), item_binary_argument_size(a),
+                                 item_binary_argument(b), item_binary_argument_size(b)) == 0 &&
 
                         a->uid_set == b->uid_set &&
                         a->uid == b->uid &&
@@ -2953,7 +2978,7 @@ static int parse_line(
         ItemArray *existing;
         OrderedHashmap *h;
         int r, pos;
-        bool append_or_force = false, boot = false, allow_failure = false, try_replace = false;
+        bool append_or_force = false, boot = false, allow_failure = false, try_replace = false, unbase64 = false, from_cred = false;
 
         assert(fname);
         assert(line >= 1);
@@ -3024,6 +3049,10 @@ static int parse_line(
                         allow_failure = true;
                 else if (action[pos] == '=' && !try_replace)
                         try_replace = true;
+                else if (action[pos] == '~' && !unbase64)
+                        unbase64 = true;
+                else if (action[pos] == '^' && !from_cred)
+                        from_cred = true;
                 else {
                         *invalid_config = true;
                         return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action);
@@ -3079,6 +3108,11 @@ static int parse_line(
                 break;
 
         case CREATE_SYMLINK:
+                if (unbase64) {
+                        *invalid_config = true;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for symlink targets.");
+                }
+
                 if (!i.argument) {
                         i.argument = path_join("/usr/share/factory", i.path);
                         if (!i.argument)
@@ -3094,11 +3128,15 @@ static int parse_line(
                 break;
 
         case COPY_FILES:
+                if (unbase64) {
+                        *invalid_config = true;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for copy sources.");
+                }
+
                 if (!i.argument) {
                         i.argument = path_join("/usr/share/factory", i.path);
                         if (!i.argument)
                                 return log_oom();
-
                 } else if (!path_is_absolute(i.argument)) {
                         *invalid_config = true;
                         return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Source path '%s' is not absolute.", i.argument);
@@ -3119,6 +3157,11 @@ static int parse_line(
 
         case CREATE_CHAR_DEVICE:
         case CREATE_BLOCK_DEVICE:
+                if (unbase64) {
+                        *invalid_config = true;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for device node creation.");
+                }
+
                 if (!i.argument) {
                         *invalid_config = true;
                         return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Device file requires argument.");
@@ -3134,6 +3177,10 @@ static int parse_line(
 
         case SET_XATTR:
         case RECURSIVE_SET_XATTR:
+                if (unbase64) {
+                        *invalid_config = true;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for extended attributes.");
+                }
                 if (!i.argument) {
                         *invalid_config = true;
                         return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
@@ -3146,6 +3193,10 @@ static int parse_line(
 
         case SET_ACL:
         case RECURSIVE_SET_ACL:
+                if (unbase64) {
+                        *invalid_config = true;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for ACLs.");
+                }
                 if (!i.argument) {
                         *invalid_config = true;
                         return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
@@ -3158,6 +3209,10 @@ static int parse_line(
 
         case SET_ATTRIBUTE:
         case RECURSIVE_SET_ATTRIBUTE:
+                if (unbase64) {
+                        *invalid_config = true;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for file attributes.");
+                }
                 if (!i.argument) {
                         *invalid_config = true;
                         return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
@@ -3187,13 +3242,45 @@ static int parse_line(
         if (!should_include_path(i.path))
                 return 0;
 
-        r = specifier_expansion_from_arg(specifier_table, &i);
-        if (r == -ENXIO)
-                return log_unresolvable_specifier(fname, line);
-        if (r < 0) {
-                if (IN_SET(r, -EINVAL, -EBADSLT))
-                        *invalid_config = true;
-                return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m");
+        if (!unbase64) {
+                /* Do specifier expansion except if base64 mode is enabled */
+                r = specifier_expansion_from_arg(specifier_table, &i);
+                if (r == -ENXIO)
+                        return log_unresolvable_specifier(fname, line);
+                if (r < 0) {
+                        if (IN_SET(r, -EINVAL, -EBADSLT))
+                                *invalid_config = true;
+                        return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m");
+                }
+        }
+
+        if (from_cred) {
+                if (!i.argument)
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Reading from credential requested, but no credential name specified.");
+                if (!credential_name_valid(i.argument))
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Credential name not valid: %s", i.argument);
+
+                r = read_credential(i.argument, &i.binary_argument, &i.binary_argument_size);
+                if (IN_SET(r, -ENXIO, -ENOENT)) {
+                        /* Silently skip over lines that have no credentials passed */
+                        log_syntax(NULL, LOG_INFO, fname, line, 0, "Credential '%s' not specified, skipping line.", i.argument);
+                        return 0;
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read credential '%s': %m", i.argument);
+        }
+
+        /* If base64 decoding is requested, do so now */
+        if (unbase64 && item_binary_argument(&i)) {
+                _cleanup_free_ void *data = NULL;
+                size_t data_size = 0;
+
+                r = unbase64mem(item_binary_argument(&i), item_binary_argument_size(&i), &data, &data_size);
+                if (r < 0)
+                        return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to base64 decode specified argument '%s': %m", i.argument);
+
+                free_and_replace(i.binary_argument, data);
+                i.binary_argument_size = data_size;
         }
 
         if (!empty_or_root(arg_root)) {
@@ -3534,7 +3621,12 @@ static int parse_argv(int argc, char *argv[]) {
         return 1;
 }
 
-static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
+static int read_config_file(
+                char **config_dirs,
+                const char *fn,
+                bool ignore_enoent,
+                bool *invalid_config) {
+
         _cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
         _cleanup_fclose_ FILE *_f = NULL;
         _cleanup_free_ char *pp = NULL;
@@ -3676,6 +3768,25 @@ static int read_config_files(char **config_dirs, char **args, bool *invalid_conf
         return 0;
 }
 
+static int read_credential_lines(bool *invalid_config) {
+        _cleanup_free_ char *j = NULL;
+        const char *d;
+        int r;
+
+        r = get_credentials_dir(&d);
+        if (r == -ENXIO)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to get credentials directory: %m");
+
+        j = path_join(d, "tmpfiles.extra");
+        if (!j)
+                return log_oom();
+
+        (void) read_config_file(/* config_dirs= */ NULL, j, /* ignore_enoent= */ true, invalid_config);
+        return 0;
+}
+
 static int link_parent(ItemArray *a) {
         const char *path;
         char *prefix;
@@ -3832,6 +3943,10 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
+        r = read_credential_lines(&invalid_config);
+        if (r < 0)
+                return r;
+
         /* Let's now link up all child/parent relationships */
         ORDERED_HASHMAP_FOREACH(a, items) {
                 r = link_parent(a);
index 85cf5201c6a1d9960debfb60ddcfef89e9bd6460..d2c126212182993f56aa714f2c3be3c7dc0d709d 100644 (file)
@@ -704,7 +704,7 @@ static int cd_media_toc(Context *c) {
         /* Take care to not iterate beyond the last valid track as specified in
          * the TOC, but also avoid going beyond the TOC length, just in case
          * the last track number is invalidly large */
-        for (size_t i = 4; i + 8 < len && num_tracks > 0; i += 8, --num_tracks) {
+        for (size_t i = 4; i + 8 <= len && num_tracks > 0; i += 8, --num_tracks) {
                 bool is_data_track;
                 uint32_t block;
 
index 96280148c7bc78e4867a40e746f4fc42bfb811e3..240f16e251130ceeb0daafb1def7d44ef5401ee1 100644 (file)
@@ -34,6 +34,7 @@ Match.Host,                                config_parse_net_condition,
 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.Credential,                          config_parse_net_condition,            CONDITION_CREDENTIAL,          offsetof(LinkConfig, conditions)
 Match.Architecture,                        config_parse_net_condition,            CONDITION_ARCHITECTURE,        offsetof(LinkConfig, conditions)
 Match.Firmware,                            config_parse_net_condition,            CONDITION_FIRMWARE,            offsetof(LinkConfig, conditions)
 Link.Description,                          config_parse_string,                   0,                             offsetof(LinkConfig, description)
index 8fdb48f315b357730e58b3d63469e5563e1111d6..3863808fb030b51d3a432a9aaf24400356b6415d 100644 (file)
@@ -264,7 +264,8 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
                         "Link\0"
                         "SR-IOV\0",
                         config_item_perf_lookup, link_config_gperf_lookup,
-                        CONFIG_PARSE_WARN, config, &stats_by_path);
+                        CONFIG_PARSE_WARN, config, &stats_by_path,
+                        NULL);
         if (r < 0)
                 return r; /* config_parse_many() logs internally. */
 
@@ -347,9 +348,9 @@ bool link_config_should_reload(LinkConfigContext *ctx) {
 
         assert(ctx);
 
-        r = config_get_stats_by_path(".link", NULL, 0, NETWORK_DIRS, &stats_by_path);
+        r = config_get_stats_by_path(".link", NULL, 0, NETWORK_DIRS, /* check_dropins = */ true, &stats_by_path);
         if (r < 0) {
-                log_warning_errno(r, "Failed to get stats of .link files: %m");
+                log_warning_errno(r, "Failed to get stats of .link files, ignoring: %m");
                 return true;
         }
 
index c30c78fad5184592c475e6011214104def370db5..f992c8f4c5d216c035a24d979cccdf5d56756678 100644 (file)
@@ -23,6 +23,7 @@
 #include "gpt.h"
 #include "parse-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "strxcpyx.h"
 #include "udev-builtin.h"
 
@@ -58,6 +59,10 @@ static void print_property(sd_device *dev, bool test, const char *name, const ch
                 blkid_encode_string(value, s, sizeof(s));
                 udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s);
 
+        } else if (STR_IN_SET(name, "FSSIZE", "FSLASTBLOCK", "FSBLOCKSIZE")) {
+                strscpyl(s, sizeof(s), "ID_FS_", name + 2, NULL);
+                udev_builtin_add_property(dev, test, s, value);
+
         } else if (streq(name, "PTTYPE")) {
                 udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value);
 
@@ -293,6 +298,9 @@ static int builtin_blkid(sd_device *dev, sd_netlink **rtnl, int argc, char *argv
         blkid_probe_set_superblocks_flags(pr,
                 BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
                 BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
+#ifdef BLKID_SUBLKS_FSINFO
+                BLKID_SUBLKS_FSINFO |
+#endif
                 BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION);
 
         if (noraid)
index 1241c08f2c5c94a7ada757cfeec6949fcab1e88e..8d652e46feebdbf0bc9635ea3e146412be54e4c4 100644 (file)
@@ -207,8 +207,13 @@ static void builtin_hwdb_exit(void) {
 }
 
 /* called every couple of seconds during event activity; 'true' if config has changed */
-static bool builtin_hwdb_validate(void) {
-        return hwdb_validate(hwdb);
+static bool builtin_hwdb_should_reload(void) {
+        if (hwdb_should_reload(hwdb)) {
+                log_debug("hwdb needs reloading.");
+                return true;
+        }
+
+        return false;
 }
 
 const UdevBuiltin udev_builtin_hwdb = {
@@ -216,6 +221,6 @@ const UdevBuiltin udev_builtin_hwdb = {
         .cmd = builtin_hwdb,
         .init = builtin_hwdb_init,
         .exit = builtin_hwdb_exit,
-        .validate = builtin_hwdb_validate,
+        .should_reload = builtin_hwdb_should_reload,
         .help = "Hardware database",
 };
index bc6a11f2410637576ec463f870f38667db723d2e..0ba2d2bd13d125db5ffa053e8cf28108d495779b 100644 (file)
@@ -43,7 +43,7 @@ static int builtin_kmod_init(void) {
         if (!ctx)
                 return -ENOMEM;
 
-        log_debug("Load module index");
+        log_debug("Loading kernel module index.");
         kmod_set_log_fn(ctx, udev_kmod_log, NULL);
         kmod_load_resources(ctx);
         return 0;
@@ -51,16 +51,21 @@ static int builtin_kmod_init(void) {
 
 /* called on udev shutdown and reload request */
 static void builtin_kmod_exit(void) {
-        log_debug("Unload module index");
+        log_debug("Unload kernel module index.");
         ctx = kmod_unref(ctx);
 }
 
 /* called every couple of seconds during event activity; 'true' if config has changed */
-static bool builtin_kmod_validate(void) {
-        log_debug("Validate module index");
+static bool builtin_kmod_should_reload(void) {
         if (!ctx)
                 return false;
-        return (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK);
+
+        if (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) {
+                log_debug("Kernel module index needs reloading.");
+                return true;
+        }
+
+        return false;
 }
 
 const UdevBuiltin udev_builtin_kmod = {
@@ -68,7 +73,7 @@ const UdevBuiltin udev_builtin_kmod = {
         .cmd = builtin_kmod,
         .init = builtin_kmod_init,
         .exit = builtin_kmod_exit,
-        .validate = builtin_kmod_validate,
+        .should_reload = builtin_kmod_should_reload,
         .help = "Kernel module loader",
         .run_once = false,
 };
index 59db7641b58f4457013924393901f1e001c01a4e..33fce6e140613fabd172c166b81adb3535acce3d 100644 (file)
@@ -53,6 +53,7 @@ typedef enum NetNameType {
         NET_XENVIF,
         NET_PLATFORM,
         NET_NETDEVSIM,
+        NET_DEVICETREE,
 } NetNameType;
 
 typedef struct NetNames {
@@ -71,6 +72,7 @@ typedef struct NetNames {
         char xen_slot[ALTIFNAMSIZ];
         char platform_path[ALTIFNAMSIZ];
         char netdevsim_path[ALTIFNAMSIZ];
+        char devicetree_onboard[ALTIFNAMSIZ];
 } NetNames;
 
 /* skip intermediate virtio devices */
@@ -109,8 +111,7 @@ static int get_virtfn_info(sd_device *pcidev, sd_device **ret_physfn_pcidev, cha
                 return r;
 
         /* Get physical function's pci device. */
-        physfn_syspath = strjoina(syspath, "/physfn");
-        r = sd_device_new_from_syspath(&physfn_pcidev, physfn_syspath);
+        r = sd_device_new_child(&physfn_pcidev, pcidev, "physfn");
         if (r < 0)
                 return r;
 
@@ -124,21 +125,20 @@ static int get_virtfn_info(sd_device *pcidev, sd_device **ret_physfn_pcidev, cha
                 return -errno;
 
         FOREACH_DIRENT_ALL(de, dir, break) {
-                _cleanup_free_ char *virtfn_link_file = NULL, *virtfn_pci_syspath = NULL;
-                const char *n;
+                _cleanup_(sd_device_unrefp) sd_device *virtfn_pcidev = NULL;
+                const char *n, *s;
 
                 n = startswith(de->d_name, "virtfn");
-                if (!n)
+                if (isempty(n))
                         continue;
 
-                virtfn_link_file = path_join(physfn_syspath, de->d_name);
-                if (!virtfn_link_file)
-                        return -ENOMEM;
+                if (sd_device_new_child(&virtfn_pcidev, physfn_pcidev, de->d_name) < 0)
+                        continue;
 
-                if (chase_symlinks(virtfn_link_file, NULL, 0, &virtfn_pci_syspath, NULL) < 0)
+                if (sd_device_get_syspath(virtfn_pcidev, &s) < 0)
                         continue;
 
-                if (streq(syspath, virtfn_pci_syspath)) {
+                if (streq(ssyspath)) {
                         char *suffix;
 
                         suffix = strjoin("v", n);
@@ -603,6 +603,94 @@ static int names_platform(sd_device *dev, NetNames *names, bool test) {
         return 0;
 }
 
+static int dev_devicetree_onboard(sd_device *dev, NetNames *names) {
+        _cleanup_(sd_device_unrefp) sd_device *aliases_dev = NULL, *ofnode_dev = NULL, *devicetree_dev = NULL;
+        const char *alias, *ofnode_path, *ofnode_syspath, *devicetree_syspath;
+        sd_device *parent;
+        int r;
+
+        if (!naming_scheme_has(NAMING_DEVICETREE_ALIASES))
+                return 0;
+
+        /* check if our direct parent has an of_node */
+        r = sd_device_get_parent(dev, &parent);
+        if (r < 0)
+                return r;
+
+        r = sd_device_new_child(&ofnode_dev, parent, "of_node");
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_syspath(ofnode_dev, &ofnode_syspath);
+        if (r < 0)
+                return r;
+
+        /* /proc/device-tree should be a symlink to /sys/firmware/devicetree/base. */
+        r = sd_device_new_from_path(&devicetree_dev, "/proc/device-tree");
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_syspath(devicetree_dev, &devicetree_syspath);
+        if (r < 0)
+                return r;
+
+        /*
+         * Example paths:
+         * devicetree_syspath = /sys/firmware/devicetree/base
+         * ofnode_syspath = /sys/firmware/devicetree/base/soc/ethernet@deadbeef
+         * ofnode_path = soc/ethernet@deadbeef
+         */
+        ofnode_path = path_startswith(ofnode_syspath, devicetree_syspath);
+        if (!ofnode_path)
+                return -ENOENT;
+
+        /* Get back our leading / to match the contents of the aliases */
+        ofnode_path--;
+        assert(path_is_absolute(ofnode_path));
+
+        r = sd_device_new_child(&aliases_dev, devicetree_dev, "aliases");
+        if (r < 0)
+                return r;
+
+        FOREACH_DEVICE_SYSATTR(aliases_dev, alias) {
+                const char *alias_path, *alias_index, *conflict;
+                unsigned i;
+
+                alias_index = startswith(alias, "ethernet");
+                if (!alias_index)
+                        continue;
+
+                if (sd_device_get_sysattr_value(aliases_dev, alias, &alias_path) < 0)
+                        continue;
+
+                if (!path_equal(ofnode_path, alias_path))
+                        continue;
+
+                /* If there's no index, we default to 0... */
+                if (isempty(alias_index)) {
+                        i = 0;
+                        conflict = "ethernet0";
+                } else {
+                        r = safe_atou(alias_index, &i);
+                        if (r < 0)
+                                return log_device_debug_errno(dev, r,
+                                                "Could not get index of alias %s: %m", alias);
+                        conflict = "ethernet";
+                }
+
+                /* ...but make sure we don't have an alias conflict */
+                if (i == 0 && sd_device_get_sysattr_value(aliases_dev, conflict, NULL) >= 0)
+                        return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
+                                        "Ethernet alias conflict: ethernet and ethernet0 both exist");
+
+                xsprintf(names->devicetree_onboard, "d%u", i);
+                names->type = NET_DEVICETREE;
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
 static int names_pci(sd_device *dev, const LinkInfo *info, NetNames *names) {
         _cleanup_(sd_device_unrefp) sd_device *physfn_pcidev = NULL;
         _cleanup_free_ char *virtfn_suffix = NULL;
@@ -1039,6 +1127,15 @@ static int builtin_net_id(sd_device *dev, sd_netlink **rtnl, int argc, char *arg
                 ieee_oui(dev, &info, test);
         }
 
+        /* get devicetree aliases; only ethernet supported for now  */
+        if (streq(prefix, "en") && dev_devicetree_onboard(dev, &names) >= 0 &&
+            names.type == NET_DEVICETREE) {
+                char str[ALTIFNAMSIZ];
+
+                if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.devicetree_onboard))
+                        udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
+        }
+
         /* get path names for Linux on System z network devices */
         if (names_ccw(dev, &names) >= 0 && names.type == NET_CCW) {
                 char str[ALTIFNAMSIZ];
index d4095c7684ce0054948befaf7aa49fd14a541367..ea7b1c5f602260a22c1cc67c6d93a19df0adfc5e 100644 (file)
@@ -74,12 +74,16 @@ static void builtin_net_setup_link_exit(void) {
         log_debug("Unloaded link configuration context.");
 }
 
-static bool builtin_net_setup_link_validate(void) {
-        log_debug("Check if link configuration needs reloading.");
+static bool builtin_net_setup_link_should_reload(void) {
         if (!ctx)
                 return false;
 
-        return link_config_should_reload(ctx);
+        if (link_config_should_reload(ctx)) {
+                log_debug("Link configuration context needs reloading.");
+                return true;
+        }
+
+        return false;
 }
 
 const UdevBuiltin udev_builtin_net_setup_link = {
@@ -87,7 +91,7 @@ const UdevBuiltin udev_builtin_net_setup_link = {
         .cmd = builtin_net_setup_link,
         .init = builtin_net_setup_link_init,
         .exit = builtin_net_setup_link_exit,
-        .validate = builtin_net_setup_link_validate,
+        .should_reload = builtin_net_setup_link_should_reload,
         .help = "Configure network link",
         .run_once = false,
 };
index bf827097719af7f2d259250324dc37c5fb77f7ac..c98c6fa714fcc6a815827a3c141df8c23ced4bf9 100644 (file)
@@ -53,9 +53,9 @@ void udev_builtin_exit(void) {
         initialized = false;
 }
 
-bool udev_builtin_validate(void) {
+bool udev_builtin_should_reload(void) {
         for (UdevBuiltinCommand i = 0; i < _UDEV_BUILTIN_MAX; i++)
-                if (builtins[i] && builtins[i]->validate && builtins[i]->validate())
+                if (builtins[i] && builtins[i]->should_reload && builtins[i]->should_reload())
                         return true;
         return false;
 }
index b0f2f919a9ab193c7023026023cb81fc8062c6df..bcfec03aaeb01ae043f0e4334c09495a19f644cd 100644 (file)
@@ -34,7 +34,7 @@ typedef struct UdevBuiltin {
         const char *help;
         int (*init)(void);
         void (*exit)(void);
-        bool (*validate)(void);
+        bool (*should_reload)(void);
         bool run_once;
 } UdevBuiltin;
 
@@ -76,7 +76,7 @@ const char *udev_builtin_name(UdevBuiltinCommand cmd);
 bool udev_builtin_run_once(UdevBuiltinCommand cmd);
 int udev_builtin_run(sd_device *dev, sd_netlink **rtnl, UdevBuiltinCommand cmd, const char *command, bool test);
 void udev_builtin_list(void);
-bool udev_builtin_validate(void);
+bool udev_builtin_should_reload(void);
 int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val);
 int udev_builtin_hwdb_lookup(sd_device *dev, const char *prefix, const char *modalias,
                              const char *filter, bool test);
index b977338c385cba8dfa5878b1097042019ccace6c..eaa3578489bc2ba6e83cd52ac0e8029dc91d447f 100644 (file)
@@ -5,6 +5,7 @@
 #include "alloc-util.h"
 #include "architecture.h"
 #include "conf-files.h"
+#include "conf-parser.h"
 #include "def.h"
 #include "device-private.h"
 #include "device-util.h"
@@ -177,10 +178,10 @@ struct UdevRuleFile {
 };
 
 struct UdevRules {
-        usec_t dirs_ts_usec;
         ResolveNameTiming resolve_name_timing;
         Hashmap *known_users;
         Hashmap *known_groups;
+        Hashmap *stats_by_path;
         UdevRuleFile *current_file;
         LIST_HEAD(UdevRuleFile, rule_files);
 };
@@ -315,6 +316,7 @@ UdevRules *udev_rules_free(UdevRules *rules) {
 
         hashmap_free_free_key(rules->known_users);
         hashmap_free_free_key(rules->known_groups);
+        hashmap_free(rules->stats_by_path);
         return mfree(rules);
 }
 
@@ -1176,6 +1178,7 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename) {
         UdevRuleFile *rule_file;
         bool ignore_line = false;
         unsigned line_nr = 0;
+        struct stat st;
         int r;
 
         f = fopen(filename, "re");
@@ -1183,16 +1186,23 @@ int udev_rules_parse_file(UdevRules *rules, const char *filename) {
                 if (errno == ENOENT)
                         return 0;
 
-                return -errno;
+                return log_warning_errno(errno, "Failed to open %s, ignoring: %m", filename);
         }
 
-        (void) fd_warn_permissions(filename, fileno(f));
+        if (fstat(fileno(f), &st) < 0)
+                return log_warning_errno(errno, "Failed to stat %s, ignoring: %m", filename);
 
-        if (null_or_empty_fd(fileno(f))) {
+        if (null_or_empty(&st)) {
                 log_debug("Skipping empty file: %s", filename);
                 return 0;
         }
 
+        r = hashmap_put_stats_by_path(&rules->stats_by_path, filename, &st);
+        if (r < 0)
+                return log_warning_errno(errno, "Failed to save stat for %s, ignoring: %m", filename);
+
+        (void) fd_warn_permissions(filename, fileno(f));
+
         log_debug("Reading rules file: %s", filename);
 
         name = strdup(filename);
@@ -1296,8 +1306,6 @@ int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing
         if (!rules)
                 return -ENOMEM;
 
-        (void) udev_rules_check_timestamp(rules);
-
         r = conf_files_list_strv(&files, ".rules", NULL, 0, RULES_DIRS);
         if (r < 0)
                 return log_debug_errno(r, "Failed to enumerate rules files: %m");
@@ -1312,11 +1320,25 @@ int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing
         return 0;
 }
 
-bool udev_rules_check_timestamp(UdevRules *rules) {
+bool udev_rules_should_reload(UdevRules *rules) {
+        _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL;
+        int r;
+
         if (!rules)
-                return false;
+                return true;
+
+        r = config_get_stats_by_path(".rules", NULL, 0, RULES_DIRS, /* check_dropins = */ false, &stats_by_path);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to get stats of udev rules, ignoring: %m");
+                return true;
+        }
+
+        if (!stats_by_path_equal(rules->stats_by_path, stats_by_path)) {
+                log_debug("Udev rules need reloading");
+                return true;
+        }
 
-        return paths_check_timestamp(RULES_DIRS, &rules->dirs_ts_usec, true);
+        return false;
 }
 
 static bool token_match_string(UdevRuleToken *token, const char *str) {
index d11297da857c73e82954e50cd85241b0d9f2bcf1..860fe7c8e4e6cc01029f7de82b063b7e8a38d535 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 #pragma once
 
+#include "alloc-util.h"
 #include "hashmap.h"
 #include "time-util.h"
 #include "udev-util.h"
@@ -21,8 +22,9 @@ UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing);
 int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing);
 UdevRules *udev_rules_free(UdevRules *rules);
 DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free);
+#define udev_rules_free_and_replace(a, b) free_and_replace_full(a, b, udev_rules_free)
 
-bool udev_rules_check_timestamp(UdevRules *rules);
+bool udev_rules_should_reload(UdevRules *rules);
 int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event,
                               usec_t timeout_usec,
                               int timeout_signal,
index 60d6507aaf27800ea85b448b08a7925f36601f8d..a1e04f651663a8327ab6e9ccd088a12db6d311d6 100644 (file)
@@ -144,9 +144,7 @@ static int find_devno(
                 const char *device,
                 bool backing) {
 
-        _cleanup_close_ int fd = -1;
-        dev_t devt, whole_devt;
-        struct stat st;
+        dev_t devt;
         int r;
 
         assert(devnos);
@@ -154,51 +152,19 @@ static int find_devno(
         assert(*devnos || *n_devnos == 0);
         assert(device);
 
-        fd = open(device, O_CLOEXEC|O_PATH);
-        if (fd < 0)
-                return log_error_errno(errno, "Failed to open '%s': %m", device);
-
-        if (fstat(fd, &st) < 0)
-                return log_error_errno(errno, "Failed to stat '%s': %m", device);
-
-        if (S_ISBLK(st.st_mode))
-                devt = st.st_rdev;
-        else if (!backing)
-                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Not a block device: %s", device);
-        else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
-                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Not a block device, regular file or directory: %s", device);
-        else if (major(st.st_dev) != 0)
-                devt = st.st_dev;
-        else {
-                _cleanup_close_ int regfd = -1;
-
-                /* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
-                 * handing, to get the backing device node. */
-
-                regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
-                if (regfd < 0)
-                        return log_error_errno(regfd, "Failed to open '%s': %m", device);
-
-                r = btrfs_get_block_device_fd(regfd, &devt);
-                if (r == -ENOTTY)
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Path '%s' not backed by block device.", device);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to acquire btrfs backing device of '%s': %m", device);
-        }
-
-        r = block_get_whole_disk(devt, &whole_devt);
+        r = path_get_whole_disk(device, backing, &devt);
         if (r < 0)
                 return log_error_errno(r, "Failed to find whole block device for '%s': %m", device);
 
-        if (typesafe_bsearch(&whole_devt, *devnos, *n_devnos, devt_compare_func)) {
-                log_debug("Device %u:%u already listed for locking, ignoring.", major(whole_devt), minor(whole_devt));
+        if (typesafe_bsearch(&devt, *devnos, *n_devnos, devt_compare_func)) {
+                log_debug("Device %u:%u already listed for locking, ignoring.", major(devt), minor(devt));
                 return 0;
         }
 
         if (!GREEDY_REALLOC(*devnos, *n_devnos + 1))
                 return log_oom();
 
-        (*devnos)[(*n_devnos)++] = whole_devt;
+        (*devnos)[(*n_devnos)++] = devt;
 
         /* Immediately sort again, to ensure the binary search above will work for the next device we add */
         typesafe_qsort(*devnos, *n_devnos, devt_compare_func);
index 63f41210ddec3b27efa96ebc21e4cc6032d3ec4a..a6926bbfb71d97b4cf0d0ef9d15edfc5269dfb6a 100644 (file)
@@ -351,16 +351,40 @@ static void notify_ready(void) {
 }
 
 /* reload requested, HUP signal received, rules changed, builtin changed */
-static void manager_reload(Manager *manager) {
+static void manager_reload(Manager *manager, bool force) {
+        _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
+        usec_t now_usec;
+        int r;
+
         assert(manager);
 
+        assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &now_usec) >= 0);
+        if (!force && now_usec < usec_add(manager->last_usec, 3 * USEC_PER_SEC))
+                /* check for changed config, every 3 seconds at most */
+                return;
+        manager->last_usec = now_usec;
+
+        /* Reload SELinux label database, to make the child inherit the up-to-date database. */
+        mac_selinux_maybe_reload();
+
+        /* Nothing changed. It is not necessary to reload. */
+        if (!udev_rules_should_reload(manager->rules) && !udev_builtin_should_reload())
+                return;
+
         sd_notify(false,
                   "RELOADING=1\n"
                   "STATUS=Flushing configuration...");
 
         manager_kill_workers(manager, false);
-        manager->rules = udev_rules_free(manager->rules);
+
         udev_builtin_exit();
+        udev_builtin_init();
+
+        r = udev_rules_load(&rules, arg_resolve_name_timing);
+        if (r < 0)
+                log_warning_errno(r, "Failed to read udev rules, using the previously loaded rules, ignoring: %m");
+        else
+                udev_rules_free_and_replace(manager->rules, rules);
 
         notify_ready();
 }
@@ -773,6 +797,8 @@ static int worker_spawn(Manager *manager, Event *event) {
         if (r < 0)
                 return r;
 
+        (void) sd_device_monitor_set_description(worker_monitor, "worker");
+
         /* allow the main daemon netlink address to send devices to the worker */
         r = device_monitor_allow_unicast_sender(worker_monitor, manager->monitor);
         if (r < 0)
@@ -937,7 +963,6 @@ no_blocker:
 }
 
 static int event_queue_start(Manager *manager) {
-        usec_t usec;
         int r;
 
         assert(manager);
@@ -945,32 +970,11 @@ static int event_queue_start(Manager *manager) {
         if (!manager->events || manager->exit || manager->stop_exec_queue)
                 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 > usec_add(manager->last_usec, 3 * USEC_PER_SEC)) {
-                if (udev_rules_check_timestamp(manager->rules) ||
-                    udev_builtin_validate())
-                        manager_reload(manager);
-
-                manager->last_usec = usec;
-        }
-
         r = event_source_disable(manager->kill_workers_event);
         if (r < 0)
                 log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m");
 
-        udev_builtin_init();
-
-        if (!manager->rules) {
-                r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
-                if (r < 0)
-                        return log_warning_errno(r, "Failed to read udev rules: %m");
-        }
-
-        /* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
-         * and, until the next SELinux policy changes, we safe further reloads in future children */
-        mac_selinux_maybe_reload();
+        manager_reload(manager, /* force = */ false);
 
         LIST_FOREACH(event, event, manager->events) {
                 if (event->state != EVENT_QUEUED)
@@ -1248,11 +1252,11 @@ static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrl
         case UDEV_CTRL_START_EXEC_QUEUE:
                 log_debug("Received udev control message (START_EXEC_QUEUE)");
                 manager->stop_exec_queue = false;
-                event_queue_start(manager);
+                /* It is not necessary to call event_queue_start() here, as it will be called in on_post() if necessary. */
                 break;
         case UDEV_CTRL_RELOAD:
                 log_debug("Received udev control message (RELOAD)");
-                manager_reload(manager);
+                manager_reload(manager, /* force = */ true);
                 break;
         case UDEV_CTRL_SET_ENV: {
                 _unused_ _cleanup_free_ char *old_val = NULL;
@@ -1501,7 +1505,7 @@ static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void
 
         assert(manager);
 
-        manager_reload(manager);
+        manager_reload(manager, /* force = */ true);
 
         return 1;
 }
@@ -1585,9 +1589,10 @@ static int on_post(sd_event_source *s, void *userdata) {
 
         if (!hashmap_isempty(manager->workers)) {
                 /* There are idle workers */
-                (void) event_reset_time(manager->event, &manager->kill_workers_event, CLOCK_MONOTONIC,
-                                        now(CLOCK_MONOTONIC) + 3 * USEC_PER_SEC, USEC_PER_SEC,
-                                        on_kill_workers_event, manager, 0, "kill-workers-event", false);
+                (void) event_reset_time_relative(manager->event, &manager->kill_workers_event,
+                                                 CLOCK_MONOTONIC, 3 * USEC_PER_SEC, USEC_PER_SEC,
+                                                 on_kill_workers_event, manager,
+                                                 0, "kill-workers-event", false);
                 return 1;
         }
 
@@ -1916,6 +1921,8 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent) {
                         log_warning_errno(r, "Failed to set receive buffer size for device monitor, ignoring: %m");
         }
 
+        (void) sd_device_monitor_set_description(manager->monitor, "manager");
+
         r = device_monitor_enable_receiving(manager->monitor);
         if (r < 0)
                 return log_error_errno(r, "Failed to bind netlink socket: %m");
@@ -2010,15 +2017,17 @@ static int main_loop(Manager *manager) {
         if (r < 0)
                 return log_error_errno(r, "Failed to create post event source: %m");
 
+        manager->last_usec = now(CLOCK_MONOTONIC);
+
         udev_builtin_init();
 
         r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
-        if (!manager->rules)
+        if (r < 0)
                 return log_error_errno(r, "Failed to read udev rules: %m");
 
         r = udev_rules_apply_static_dev_perms(manager->rules);
         if (r < 0)
-                log_error_errno(r, "Failed to apply permissions on static device nodes: %m");
+                log_warning_errno(r, "Failed to apply permissions on static device nodes, ignoring: %m");
 
         notify_ready();
 
index add862c962aad78f4bc47b1dd74fd7911d3cc438..808f94a58ff546aa702db15d52557194f76925dd 100755 (executable)
@@ -8,7 +8,7 @@ TEST_DESCRIPTION="Tests for systemd-logind"
 . "${TEST_BASE_DIR:?}/test-functions"
 
 test_append_files() {
-    image_install -o evemu-device evemu-event crond crontab
+    image_install -o evemu-device evemu-event
 }
 
 do_test "$@"
index 3b2c24132326a83c94ccd23cf39bfbe088a9f670..5269eaa437e632b9522332e047af00b2af84b590 100755 (executable)
@@ -3,9 +3,29 @@
 set -e
 
 TEST_DESCRIPTION="test credentials"
-NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} --set-credential=mynspawncredential:strangevalue"
-QEMU_OPTIONS="${QEMU_OPTIONS:-} -fw_cfg  name=opt/io.systemd.credentials/myqemucredential,string=othervalue"
-KERNEL_APPEND="${KERNEL_APPEND:-} systemd.set_credential=kernelcmdlinecred:uff rd.systemd.import_credentials=no"
+
+NSPAWN_CREDS=(
+    "--set-credential=mynspawncredential:strangevalue"
+)
+NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} ${NSPAWN_CREDS[*]}"
+
+QEMU_CREDS=(
+    "-fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue"
+    "-smbios type=11,value=io.systemd.credential:smbioscredential=magicdata"
+    "-smbios type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh"
+    "-smbios type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK"
+    "-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg=="
+)
+QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}"
+
+KERNEL_CREDS=(
+    "systemd.set_credential=kernelcmdlinecred:uff"
+    "systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest"
+    "systemd.set_credential=login.motd:hello"
+    "systemd.set_credential=login.issue:welcome"
+    "rd.systemd.import_credentials=no"
+)
+KERNEL_APPEND="${KERNEL_APPEND:-} ${KERNEL_CREDS[*]}"
 
 # shellcheck source=test/test-functions
 . "${TEST_BASE_DIR:?}/test-functions"
index baf75aaf1119ae0e0753e2574e813529ed438c18..90197a71b9e07f70c5b99e01d72295412222d32c 100755 (executable)
@@ -20,11 +20,6 @@ test_append_files() {
         install_dmevent
         generate_module_dependencies
         inst_binary tpm2_pcrextend
-
-        # On Ubuntu, cryptsetup does not link against libgcc_s.so.1
-        if get_bool "$LOOKS_LIKE_DEBIAN"; then
-            inst_library "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/libgcc_s.so.1"
-        fi
 }
 
 TEST_70_TPM_DEVICE="tpm-tis"
diff --git a/test/TEST-76-SYSCTL/Makefile b/test/TEST-76-SYSCTL/Makefile
new file mode 100644 (file)
index 0000000..9f65d4c
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+all setup run clean clean-again:
+       @TEST_BASE_DIR=../ ./test.sh --$@
+
+.PHONY: all setup run clean clean-again
diff --git a/test/TEST-76-SYSCTL/test.sh b/test/TEST-76-SYSCTL/test.sh
new file mode 100755 (executable)
index 0000000..11a44af
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+TEST_DESCRIPTION="Sysctl-related tests"
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+do_test "$@"
index d6c6cc6f2effb54da39ff021e099df13fad7c564..a1c797a297b80cb786e6638734ddb10bbd7c5573 100644 (file)
@@ -1,18 +1,19 @@
 [Match]
+Architecture=
+Credential=
+Driver=
+Firmware=
+Host=
+KernelCommandLine=
+KernelVersion=
+Kind=
 MACAddress=
-PermanentMACAddress=
 OriginalName=
 Path=
-Driver=
-Type=
-Kind=
+PermanentMACAddress=
 Property=
-Host=
+Type=
 Virtualization=
-KernelCommandLine=
-KernelVersion=
-Architecture=
-Firmware=
 [Link]
 Description=
 MACAddressPolicy=
index d97f81512bb335e75bf2537167dd54a2badc7cd5..309941f58defe87980d182fb650f36e12d9b4ada 100644 (file)
@@ -24,11 +24,12 @@ Mode=
 SourceMACAddress=
 [Match]
 Architecture=
+Credential=
 Firmware=
 Host=
+KernelCommandLine=
 KernelVersion=
 Virtualization=
-KernelCommandLine=
 [GENEVE]
 DestinationPort=
 TTL=
@@ -170,6 +171,7 @@ User=
 Group=
 PacketInfo=
 VNetHeader=
+KeepCarrier=
 [IPVLAN]
 Mode=
 Flags=
@@ -183,6 +185,7 @@ PacketInfo=
 VNetHeader=
 Group=
 User=
+KeepCarrier=
 [NetDev]
 Kind=
 MACAddress=
index 276f3c9307678a317ecc712d82cd313d7e2a0e19..b7e8f1683487cfb9f9a9af88ec22a00dd9b7b090 100644 (file)
@@ -15,23 +15,24 @@ ProxyARP=
 ProxyARPWiFi=
 MulticastRouter=
 [Match]
-KernelVersion=
-Type=
-Kind=
-Driver=
 Architecture=
-Firmware=
-Path=
-WLANInterfaceType=
-SSID=
 BSSID=
-Name=
-Property=
-Virtualization=
-KernelCommandLine=
+Credential=
+Driver=
+Firmware=
 Host=
+KernelCommandLine=
+KernelVersion=
+Kind=
 MACAddress=
+Name=
+Path=
 PermanentMACAddress=
+Property=
+SSID=
+Type=
+Virtualization=
+WLANInterfaceType=
 [Link]
 ActivationPolicy=
 RequiredForOnline=
@@ -235,6 +236,7 @@ VXLAN=
 L2TP=
 MACsec=
 LinkLocalAddressing=
+IPv4LLStartAddress=
 IPv6LinkLocalAddressGenerationMode=
 IPv6StableSecretAddress=
 ConfigureWithoutCarrier=
index 487001b6b78ea5c74329f98564f8444c93196af2..621fb1cf1b684f7464bb647a2bac15fb76209af1 100644 (file)
@@ -10,9 +10,10 @@ Also=
 AmbientCapabilities=
 AssertACPower=
 AssertArchitecture=
+AssertCPUPressure=
 AssertCapability=
 AssertControlGroupController=
-AssertCPUPressure=
+AssertCredential=
 AssertDirectoryNotEmpty=
 AssertFileIsExecutable=
 AssertFileNotEmpty=
@@ -59,6 +60,7 @@ ConditionACPower=
 ConditionArchitecture=
 ConditionCapability=
 ConditionControlGroupController=
+ConditionCredential=
 ConditionCPUPressure=
 ConditionDirectoryNotEmpty=
 ConditionFileIsExecutable=
@@ -481,6 +483,7 @@ InitialCongestionWindow=
 InputKey=
 InvertRule=
 KernelCommandLine=
+Credential=
 KernelVersion=
 Key=
 Kind=
index 3c33d947fe2bbc3348c9ab9b01610e1bac9de9c5..056edb9f172488fb5529a7956d1fbc43136bf3a7 100644 (file)
@@ -9,6 +9,7 @@ AssertCPUPressure=
 AssertCPUs=
 AssertCapability=
 AssertControlGroupController=
+AssertCredential=
 AssertDirectoryNotEmpty=
 AssertEnvironment=
 AssertFileIsExecutable=
@@ -47,6 +48,7 @@ ConditionCPUs=
 ConditionFirmware=
 ConditionCapability=
 ConditionControlGroupController=
+ConditionCredential=
 ConditionDirectoryNotEmpty=
 ConditionEnvironment=
 ConditionFileIsExecutable=
index 6423417b81c12fe9d11b619642dfb053f6f2a180..ed3604ccdf1cd233caf7749886287af6567e22b6 100644 (file)
@@ -21,7 +21,7 @@ Output=networkd-test.raw
 [Partitions]
 RootSize=3G
 
-[Packages]
+[Content]
 BuildPackages=
         audit-libs-devel
         bzip2-devel
index 4b5ba622fa6f31e6c33e15299d271dd651c0b772..0a5ba11f89e08e6a4439306a518236d370102288 100755 (executable)
@@ -291,8 +291,8 @@ Priority=23
         self.assertEqual(self.read_attr('port2', 'brport/path_cost'), '555')
         self.assertEqual(self.read_attr('port2', 'brport/multicast_fast_leave'), '1')
         self.assertEqual(self.read_attr('port2', 'brport/unicast_flood'), '1')
-        self.assertEqual(self.read_attr('port2', 'brport/bpdu_guard'), '1')
-        self.assertEqual(self.read_attr('port2', 'brport/root_block'), '1')
+        self.assertEqual(self.read_attr('port2', 'brport/bpdu_guard'), '0')
+        self.assertEqual(self.read_attr('port2', 'brport/root_block'), '0')
 
 class ClientTestBase(NetworkdTestingUtilities):
     """Provide common methods for testing networkd against servers."""
index 1fc695feb5de177a9efd4fe6817923831cfc326e..5dec5e28dec1e04b1270ee432a28d7c0b19c71f5 100644 (file)
@@ -712,6 +712,13 @@ EOF
         chmod +x "$initdir/opt/script1.sh"
         echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file"
         mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw" -noappend
+
+        export initdir="$TESTDIR/app-nodistro"
+        mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system"
+        ( echo "ID=_any"
+          echo "ARCHITECTURE=_any" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro"
+        echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
+        mksquashfs "$initdir" "$oldinitdir/usr/share/app-nodistro.raw" -noappend
     )
 }
 
@@ -967,12 +974,6 @@ install_multipath() {
         # Install the library itself and create necessary symlinks
         inst_library "$file"
     done < <(find /lib*/multipath -type f)
-
-    if get_bool "$LOOKS_LIKE_ARCH"; then
-        # On Arch the multipath libraries are not linked against libgcc_s.so.1,
-        # but it's still required at runtime
-        inst_library "/lib64/libgcc_s.so.1"
-    fi
 }
 
 install_lvm() {
@@ -1227,6 +1228,14 @@ install_missing_libraries() {
         LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath "$i")" inst_libs "$i"
     done
 
+    # Install libgcc_s.so if available, since it's dlopen()ed by libpthread
+    # and might cause unexpected failures during pthread_exit()/pthread_cancel()
+    # if not present
+    # See: https://github.com/systemd/systemd/pull/23858
+    while read -r libgcc_s; do
+        [[ -e "$libgcc_s" ]] && inst_library "$libgcc_s"
+    done < <(ldconfig -p | awk '/\/libgcc_s.so.1$/ { print $4 }')
+
     local lib path
     # A number of dependencies is now optional via dlopen, so the install
     # script will not pick them up, since it looks at linkage.
@@ -2427,6 +2436,7 @@ inst_library() {
         inst_simple "$reallib" "$reallib"
         inst_dir "${dest%/*}"
         [[ -d "${dest%/*}" ]] && dest="$(readlink -f "${dest%/*}")/${dest##*/}"
+        ddebug "Creating symlink $reallib -> $dest"
         ln -sfn -- "$(convert_abs_rel "${dest}" "${reallib}")" "${initdir}/${dest}"
     else
         inst_simple "$src" "$dest"
index 07ef254ddabed4e6ef239b76e152f2d58067113d..dbeb04ea0452866e58a98dbe0683a5dfc885598a 100644 (file)
@@ -2,6 +2,9 @@
 [Match]
 Name=bridge99
 
+[Link]
+MTUBytes=9000
+
 [Network]
 Address=192.168.0.15/24
 Gateway=192.168.0.1
index 5a810260659471da43443dc0cb25abb3140ae7f2..6ea9aff9e141cbe6c3c3d35a76a90c99ce7c09e9 100644 (file)
@@ -6,3 +6,4 @@ Name=veth99
 DHCP=ipv4
 LinkLocalAddressing=yes
 IPv6AcceptRA=no
+IPv4LLStartAddress=169.254.133.11
diff --git a/test/test-network/conf/25-qdisc-clsact-and-htb.network b/test/test-network/conf/25-qdisc-clsact-and-htb.network
deleted file mode 100644 (file)
index c91666d..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-[QDisc]
-Parent=clsact
-
-[HierarchyTokenBucket]
-Parent=root
-Handle=0002
-DefaultClass=30
-RateToQuantum=20
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0030
-Priority=1
-QuantumBytes=4000
-MTUBytes=1700
-OverheadBytes=100
-Rate=1M
-BufferBytes=123456
-CeilRate=0.5M
-CeilBufferBytes=123457
-
-[NetworkEmulator]
-Parent=2:30
-Handle=0030
-DelaySec=50ms
-DelayJitterSec=10ms
-LossRate=20%
-PacketLimit=100
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0031
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[TrivialLinkEqualizer]
-Parent=2:31
-Handle=0031
-Id=1
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0032
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[FairQueueing]
-Parent=2:32
-Handle=0032
-PacketLimit=1000
-FlowLimit=200
-QuantumBytes=1500
-InitialQuantumBytes=13000
-MaximumRate=1M
-Buckets=512
-OrphanMask=511
-Pacing=yes
-CEThresholdSec=100ms
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0033
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[ControlledDelay]
-Parent=2:33
-Handle=0033
-PacketLimit=2000
-TargetSec=10ms
-IntervalSec=50ms
-ECN=yes
-CEThresholdSec=100ms
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0034
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[FairQueueingControlledDelay]
-Parent=2:34
-Handle=0034
-PacketLimit=20480
-MemoryLimitBytes=64M
-Flows=2048
-TargetSec=10ms
-IntervalSec=200ms
-QuantumBytes=1400
-ECN=yes
-CEThresholdSec=100ms
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0035
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[TokenBucketFilter]
-Parent=2:35
-Handle=0035
-Rate=1G
-BurstBytes=5000
-LatencySec=70msec
-PeakRate=100G
-MTUBytes=1000000
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0036
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[StochasticFairnessQueueing]
-Parent=2:36
-Handle=0036
-PerturbPeriodSec=5sec
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0037
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[PFIFO]
-Parent=2:37
-Handle=0037
-PacketLimit=100000
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0038
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[GenericRandomEarlyDetection]
-Parent=2:38
-Handle=0038
-VirtualQueues=12
-DefaultVirtualQueue=10
-GenericRIO=yes
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0039
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[StochasticFairBlue]
-Parent=2:39
-Handle=0039
-PacketLimit=200000
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:003a
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[BFIFO]
-Parent=2:3a
-Handle=003a
-LimitBytes=1000000
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:003b
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[PFIFOHeadDrop]
-Parent=2:3b
-Handle=003b
-PacketLimit=1023
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:003c
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[PFIFOFast]
-Parent=2:3c
-Handle=003c
diff --git a/test/test-network/conf/25-qdisc-clsact.network b/test/test-network/conf/25-qdisc-clsact.network
new file mode 100644 (file)
index 0000000..8a16618
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[QDisc]
+Parent=clsact
diff --git a/test/test-network/conf/25-qdisc-codel.network b/test/test-network/conf/25-qdisc-codel.network
new file mode 100644 (file)
index 0000000..a332fa8
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[ControlledDelay]
+Parent=root
+Handle=0033
+PacketLimit=2000
+TargetSec=10ms
+IntervalSec=50ms
+ECN=yes
+CEThresholdSec=100ms
diff --git a/test/test-network/conf/25-qdisc-fq.network b/test/test-network/conf/25-qdisc-fq.network
new file mode 100644 (file)
index 0000000..5539a64
--- /dev/null
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[FairQueueing]
+Parent=root
+Handle=0032
+PacketLimit=1000
+FlowLimit=200
+QuantumBytes=1500
+InitialQuantumBytes=13000
+MaximumRate=1M
+Buckets=512
+OrphanMask=511
+Pacing=yes
+CEThresholdSec=100ms
diff --git a/test/test-network/conf/25-qdisc-fq_codel.network b/test/test-network/conf/25-qdisc-fq_codel.network
new file mode 100644 (file)
index 0000000..0e7d62d
--- /dev/null
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[FairQueueingControlledDelay]
+Parent=root
+Handle=0034
+PacketLimit=20480
+MemoryLimitBytes=64M
+Flows=2048
+TargetSec=10ms
+IntervalSec=200ms
+QuantumBytes=1400
+ECN=yes
+CEThresholdSec=100ms
diff --git a/test/test-network/conf/25-qdisc-gred.network b/test/test-network/conf/25-qdisc-gred.network
new file mode 100644 (file)
index 0000000..a49955f
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[GenericRandomEarlyDetection]
+Parent=root
+Handle=0038
+VirtualQueues=12
+DefaultVirtualQueue=10
+GenericRIO=yes
diff --git a/test/test-network/conf/25-qdisc-htb-fifo.network b/test/test-network/conf/25-qdisc-htb-fifo.network
new file mode 100644 (file)
index 0000000..1e092a9
--- /dev/null
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[HierarchyTokenBucket]
+Parent=root
+Handle=0002
+DefaultClass=30
+RateToQuantum=20
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0037
+Priority=1
+Rate=1M
+CeilRate=0.5M
+QuantumBytes=4000
+MTUBytes=1700
+OverheadBytes=100
+BufferBytes=123456
+CeilBufferBytes=123457
+
+[PFIFO]
+Parent=2:37
+Handle=0037
+PacketLimit=100000
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:003a
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[BFIFO]
+Parent=2:3a
+Handle=003a
+LimitBytes=1000000
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:003b
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[PFIFOHeadDrop]
+Parent=2:3b
+Handle=003b
+PacketLimit=1023
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:003c
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[PFIFOFast]
+Parent=2:3c
+Handle=003c
diff --git a/test/test-network/conf/25-qdisc-ingress.network b/test/test-network/conf/25-qdisc-ingress.network
new file mode 100644 (file)
index 0000000..6fb5fef
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.4/16
+
+[QDisc]
+Parent=ingress
similarity index 84%
rename from test/test-network/conf/25-qdisc-ingress-netem-compat.network
rename to test/test-network/conf/25-qdisc-netem-compat.network
index d895f7f34fe5756b17e70fc807a2b56f760dd959..15e0f7f36767d64e7cf1fe28b29ef4a95efb48e5 100644 (file)
@@ -12,6 +12,3 @@ NetworkEmulatorDelaySec=50ms
 NetworkEmulatorDelayJitterSec=10ms
 NetworkEmulatorLossRate=20%
 NetworkEmulatorPacketLimit=100
-
-[TrafficControlQueueingDiscipline]
-Parent=ingress
diff --git a/test/test-network/conf/25-qdisc-netem.network b/test/test-network/conf/25-qdisc-netem.network
new file mode 100644 (file)
index 0000000..9848788
--- /dev/null
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[NetworkEmulator]
+Parent=root
+Handle=0030
+DelaySec=50ms
+DelayJitterSec=10ms
+LossRate=20%
+PacketLimit=100
index 1c8dbc2461dfb916a084cbdb6b473391c452e334..b09e99c4f2634c2a4a139b24106b9989db487b8c 100644 (file)
@@ -1,10 +1,10 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Match]
-Name=test1
+Name=dummy98
 
 [Network]
 IPv6AcceptRA=no
-Address=10.1.2.4/16
+Address=10.1.2.3/16
 
 [QuickFairQueueing]
 Parent=root
diff --git a/test/test-network/conf/25-qdisc-sfb.network b/test/test-network/conf/25-qdisc-sfb.network
new file mode 100644 (file)
index 0000000..bfcda00
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[StochasticFairBlue]
+Parent=root
+Handle=0039
+PacketLimit=200000
diff --git a/test/test-network/conf/25-qdisc-sfq.network b/test/test-network/conf/25-qdisc-sfq.network
new file mode 100644 (file)
index 0000000..263cd6d
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[StochasticFairnessQueueing]
+Parent=root
+Handle=0036
+PerturbPeriodSec=5sec
diff --git a/test/test-network/conf/25-qdisc-tbf.network b/test/test-network/conf/25-qdisc-tbf.network
new file mode 100644 (file)
index 0000000..c5f28fb
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[TokenBucketFilter]
+Parent=root
+Handle=0035
+Rate=1G
+BurstBytes=5000
+LatencySec=70msec
+PeakRate=100G
+MTUBytes=1000000
diff --git a/test/test-network/conf/25-qdisc-teql.network b/test/test-network/conf/25-qdisc-teql.network
new file mode 100644 (file)
index 0000000..ed7e21f
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[TrivialLinkEqualizer]
+Parent=root
+Handle=0031
+Id=1
index 5c1f97be9ea306356966c3fe24657db47f26227b..5cbfd0e5b1c4d2355012507f86b84a59369d797d 100644 (file)
@@ -1,9 +1,10 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [NetDev]
-Name=tap99
+Name=testtap99
 Kind=tap
 
 [Tap]
 MultiQueue=true
 PacketInfo=true
 VNetHeader=true
+KeepCarrier=yes
index ed25026a95925898311312939ccfe0700b58ebc4..a354ebb2b219d1e31b0f89f1552e26354f820d3c 100644 (file)
@@ -1,9 +1,10 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [NetDev]
-Name=tun99
+Name=testtun99
 Kind=tun
 
 [Tun]
 MultiQueue=true
 PacketInfo=true
 VNetHeader=true
+KeepCarrier=yes
index d3b0ea4e6ce1c85e0f08ee5f4f2176320a1cc704..bd3c735f4ce03923ef7a10d2b57362199f61b15b 100644 (file)
@@ -4,4 +4,6 @@ Name=wg99
 
 [Network]
 Address=192.168.124.1/24
+Address=fe80::1/64
+Address=169.254.11.1/24
 IPv6AcceptRA=no
index 8858cbf0008f176f744bc509f6a67426a2edca82..cdb3daa2b1bed74d96a27838fcde6249eba4be0b 100644 (file)
@@ -2,6 +2,9 @@
 [Match]
 Name=dummy98
 
+[Link]
+MTUBytes=9000
+
 [Network]
 Bridge=bridge99
 
index 26ab7d8af6abba835ed9a4842fe8d3e400ea6e58..42b197eeef12060557e5ac04fb4dafab1229d576 100644 (file)
@@ -2,6 +2,9 @@
 [Match]
 Name=test1
 
+[Link]
+MTUBytes=9000
+
 [Network]
 Bridge=bridge99
 
index 8ec0190bcb830b5cf1f6b11c9e8f9c7532850864..71521258d3fdb17a9fa7589fda0c2050757b9d3b 100644 (file)
@@ -22,6 +22,8 @@ Name=nlmon99
 Name=xfrm98 xfrm99
 Name=vxlan98
 Name=hogehogehogehogehogehoge
+Name=testtun99
+Name=testtap99
 
 [Network]
 LinkLocalAddressing=yes
index 50ce95384989674b22cd6f44ccac1bbfa486f6dc..978cc828295e592102e9a403a0f4ed21aea4943c 100755 (executable)
@@ -13,6 +13,7 @@ import errno
 import itertools
 import os
 import pathlib
+import psutil
 import re
 import shutil
 import signal
@@ -38,13 +39,16 @@ which_paths = ':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(
 
 networkd_bin = shutil.which('systemd-networkd', path=which_paths)
 resolved_bin = shutil.which('systemd-resolved', path=which_paths)
+timesyncd_bin = shutil.which('systemd-timesyncd', path=which_paths)
 udevd_bin = shutil.which('systemd-udevd', path=which_paths)
 wait_online_bin = shutil.which('systemd-networkd-wait-online', path=which_paths)
 networkctl_bin = shutil.which('networkctl', path=which_paths)
 resolvectl_bin = shutil.which('resolvectl', path=which_paths)
 timedatectl_bin = shutil.which('timedatectl', path=which_paths)
+udevadm_bin = shutil.which('udevadm', path=which_paths)
 
 use_valgrind = False
+valgrind_cmd = ''
 enable_debug = True
 env = {}
 wait_online_env = {}
@@ -92,33 +96,41 @@ def mkdir_p(path):
 def touch(path):
     pathlib.Path(path).touch()
 
-def check_output(*command, text=True, **kwargs):
+def check_output(*command, **kwargs):
     # This checks the result and returns stdout (and stderr) on success.
     command = command[0].split() + list(command[1:])
-    return subprocess.run(command, check=True, universal_newlines=text, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs).stdout.rstrip()
-
-def call(*command, text=True, **kwargs):
+    ret = subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
+    if ret.returncode == 0:
+        return ret.stdout.rstrip()
+    # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
+    print(ret.stdout)
+    ret.check_returncode()
+
+def call(*command, **kwargs):
     # This returns returncode. stdout and stderr are merged and shown in console
     command = command[0].split() + list(command[1:])
-    return subprocess.run(command, check=False, universal_newlines=text, stderr=subprocess.STDOUT, **kwargs).returncode
+    return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).returncode
 
-def call_quiet(*command, text=True, **kwargs):
+def call_quiet(*command, **kwargs):
     command = command[0].split() + list(command[1:])
-    return subprocess.run(command, check=False, universal_newlines=text, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs).returncode
+    return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs).returncode
 
-def run(*command, text=True, **kwargs):
+def run(*command, **kwargs):
     # This returns CompletedProcess instance.
     command = command[0].split() + list(command[1:])
-    return subprocess.run(command, check=False, universal_newlines=text, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
+    return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
 
-def is_module_available(module_name):
-    lsmod_output = check_output('lsmod')
-    module_re = re.compile(rf'^{re.escape(module_name)}\b', re.MULTILINE)
-    return module_re.search(lsmod_output) or call_quiet('modprobe', module_name) == 0
+def is_module_available(*module_names):
+    for module_name in module_names:
+        lsmod_output = check_output('lsmod')
+        module_re = re.compile(rf'^{re.escape(module_name)}\b', re.MULTILINE)
+        if not module_re.search(lsmod_output) and call_quiet('modprobe', module_name) != 0:
+            return False
+    return True
 
-def expectedFailureIfModuleIsNotAvailable(module_name):
+def expectedFailureIfModuleIsNotAvailable(*module_names):
     def f(func):
-        return func if is_module_available(module_name) else unittest.expectedFailure(func)
+        return func if is_module_available(*module_names) else unittest.expectedFailure(func)
 
     return f
 
@@ -231,50 +243,8 @@ def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
 
     return f
 
-def expectedFailureIfCAKEIsNotAvailable():
-    def f(func):
-        call_quiet('ip link add dummy98 type dummy')
-        rc = call_quiet('tc qdisc add dev dummy98 parent root cake')
-        remove_link('dummy98')
-        return func if rc == 0 else unittest.expectedFailure(func)
-
-    return f
-
-def expectedFailureIfPIEIsNotAvailable():
-    def f(func):
-        call_quiet('ip link add dummy98 type dummy')
-        rc = call_quiet('tc qdisc add dev dummy98 parent root pie')
-        remove_link('dummy98')
-        return func if rc == 0 else unittest.expectedFailure(func)
-
-    return f
-
-def expectedFailureIfHHFIsNotAvailable():
-    def f(func):
-        call_quiet('ip link add dummy98 type dummy')
-        rc = call_quiet('tc qdisc add dev dummy98 parent root hhf')
-        remove_link('dummy98')
-        return func if rc == 0 else unittest.expectedFailure(func)
-
-    return f
-
-def expectedFailureIfETSIsNotAvailable():
-    def f(func):
-        call_quiet('ip link add dummy98 type dummy')
-        rc = call_quiet('tc qdisc add dev dummy98 parent root ets bands 10')
-        remove_link('dummy98')
-        return func if rc == 0 else unittest.expectedFailure(func)
-
-    return f
-
-def expectedFailureIfFQPIEIsNotAvailable():
-    def f(func):
-        call_quiet('ip link add dummy98 type dummy')
-        rc = call_quiet('tc qdisc add dev dummy98 parent root fq_pie')
-        remove_link('dummy98')
-        return func if rc == 0 else unittest.expectedFailure(func)
-
-    return f
+def udev_reload():
+    check_output(*udevadm_cmd, 'control', '--reload')
 
 def copy_network_unit(*units, copy_dropins=True):
     """
@@ -287,31 +257,57 @@ def copy_network_unit(*units, copy_dropins=True):
 
     When a drop-in file is specified, its unit file is also copied in automatically.
     """
+    has_link = False
     mkdir_p(network_unit_dir)
     for unit in units:
         if copy_dropins and os.path.exists(os.path.join(networkd_ci_temp_dir, unit + '.d')):
             cp_r(os.path.join(networkd_ci_temp_dir, unit + '.d'), os.path.join(network_unit_dir, unit + '.d'))
+
         if unit.endswith('.conf'):
             dropin = unit
             unit = os.path.dirname(dropin).rstrip('.d')
             dropindir = os.path.join(network_unit_dir, unit + '.d')
             mkdir_p(dropindir)
             cp(os.path.join(networkd_ci_temp_dir, dropin), dropindir)
+
         cp(os.path.join(networkd_ci_temp_dir, unit), network_unit_dir)
 
+        if unit.endswith('.link'):
+            has_link = True
+
+    if has_link:
+        udev_reload()
+
 def remove_network_unit(*units):
     """
     Remove previously copied unit files from the testbed.
 
     Drop-ins will be removed automatically.
     """
+    has_link = False
     for unit in units:
         rm_f(os.path.join(network_unit_dir, unit))
         rm_rf(os.path.join(network_unit_dir, unit + '.d'))
 
+        if unit.endswith('.link') or unit.endswith('.link.d'):
+            has_link = True
+
+    if has_link:
+        udev_reload()
+
 def clear_network_units():
+    has_link = False
+    if os.path.exists(network_unit_dir):
+        units = os.listdir(network_unit_dir)
+        for unit in units:
+            if unit.endswith('.link') or unit.endswith('.link.d'):
+                has_link = True
+
     rm_rf(network_unit_dir)
 
+    if has_link:
+        udev_reload()
+
 def copy_networkd_conf_dropin(*dropins):
     """Copy networkd.conf dropin files into the testbed."""
     mkdir_p(networkd_conf_dropin_dir)
@@ -341,9 +337,8 @@ def clear_udev_rules():
     rm_rf(udev_rules_dir)
 
 def save_active_units():
-    for u in ['systemd-udevd-kernel.socket', 'systemd-udevd-control.socket', 'systemd-udevd.service',
-              'systemd-networkd.socket', 'systemd-networkd.service',
-              'systemd-resolved.service',
+    for u in ['systemd-networkd.socket', 'systemd-networkd.service',
+              'systemd-resolved.service', 'systemd-timesyncd.service',
               'firewalld.service']:
         if call(f'systemctl is-active --quiet {u}') == 0:
             call(f'systemctl stop {u}')
@@ -352,11 +347,52 @@ def save_active_units():
 def restore_active_units():
     if 'systemd-networkd.socket' in active_units:
         call('systemctl stop systemd-networkd.socket systemd-networkd.service')
-    if 'systemd-udevd-kernel.socket' in active_units or 'systemd-udevd-control.socket' in active_units:
-        call('systemctl stop systemd-udevd-kernel.socket systemd-udevd-control.socket systemd-udevd.service')
     for u in active_units:
         call(f'systemctl restart {u}')
 
+def create_unit_dropin(unit, contents):
+    mkdir_p(f'/run/systemd/system/{unit}.d')
+    with open(f'/run/systemd/system/{unit}.d/00-override.conf', mode='w', encoding='utf-8') as f:
+        f.write('\n'.join(contents))
+
+def create_service_dropin(service, command, reload_command=None, additional_settings=None):
+    drop_in = [
+        '[Service]',
+        'ExecStart=',
+        f'ExecStart=!!{valgrind_cmd}{command}',
+    ]
+    if reload_command:
+        drop_in += [
+            'ExecReload=',
+            f'ExecReload={valgrind_cmd}{reload_command}',
+        ]
+    if enable_debug:
+        drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
+    if asan_options:
+        drop_in += [f'Environment=ASAN_OPTIONS="{asan_options}"']
+    if lsan_options:
+        drop_in += [f'Environment=LSAN_OPTIONS="{lsan_options}"']
+    if ubsan_options:
+        drop_in += [f'Environment=UBSAN_OPTIONS="{ubsan_options}"']
+    if asan_options or lsan_options or ubsan_options:
+        drop_in += ['SystemCallFilter=']
+    if use_valgrind or asan_options or lsan_options or ubsan_options:
+        drop_in += ['MemoryDenyWriteExecute=no']
+    if use_valgrind:
+        drop_in += [
+            'Environment=SYSTEMD_MEMPOOL=0',
+            'PrivateTmp=yes',
+        ]
+    if with_coverage:
+        drop_in += [
+            'ProtectSystem=no',
+            'ProtectHome=no',
+        ]
+    if additional_settings:
+        drop_in += additional_settings
+
+    create_unit_dropin(f'{service}.service', drop_in)
+
 def link_exists(link):
     return os.path.exists(os.path.join('/sys/class/net', link, 'ifindex'))
 
@@ -449,8 +485,10 @@ def flush_fou_ports():
 
 def flush_l2tp_tunnels():
     tids = []
-    output = check_output('ip l2tp show tunnel')
-    for line in output.splitlines():
+    ret = run('ip l2tp show tunnel')
+    if ret.returncode != 0:
+        return # l2tp may not be supported
+    for line in ret.stdout.splitlines():
         words = line.split()
         if words[0] == 'Tunnel':
             tid = words[1].rstrip(',')
@@ -469,14 +507,14 @@ def flush_l2tp_tunnels():
 
 def save_timezone():
     global saved_timezone
-    r = run('timedatectl show --value --property Timezone')
+    r = run(*timedatectl_cmd, 'show', '--value', '--property', 'Timezone', env=env)
     if r.returncode == 0:
         saved_timezone = r.stdout.rstrip()
         print(f'### Saved timezone: {saved_timezone}')
 
 def restore_timezone():
     if saved_timezone:
-        call(f'timedatectl set-timezone {saved_timezone}')
+        call(*timedatectl_cmd, 'set-timezone', f'{saved_timezone}', env=env)
 
 def read_link_attr(*args):
     with open(os.path.join('/sys/class/net', *args), encoding='utf-8') as f:
@@ -567,8 +605,14 @@ def start_networkd():
     check_output('systemctl start systemd-networkd')
 
 def restart_networkd(show_logs=True):
-    stop_networkd(show_logs)
-    start_networkd()
+    if show_logs:
+        invocation_id = check_output('systemctl show systemd-networkd.service -p InvocationID --value')
+    check_output('systemctl restart systemd-networkd.service')
+    if show_logs:
+        print(check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id))
+
+def networkd_pid():
+    return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
 
 def networkctl_reconfigure(*links):
     check_output(*networkctl_cmd, 'reconfigure', *links, env=env)
@@ -600,7 +644,7 @@ def tear_down_common():
     flush_links()
 
     # 5. stop networkd
-    stop_networkd(show_logs=True)
+    stop_networkd()
 
     # 6. remove configs
     clear_network_units()
@@ -629,93 +673,40 @@ def setUpModule():
     save_routing_policy_rules()
     save_timezone()
 
-    drop_in = [
-        '[Unit]',
-        'StartLimitIntervalSec=0',
-        '[Service]',
-        'Restart=no',
-        'ExecStart=',
-        'ExecReload=',
-    ]
-    if use_valgrind:
-        drop_in += [
-            'ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + networkd_bin,
-            f'ExecReload=valgrind {networkctl_bin} reload',
-            'PrivateTmp=yes'
-        ]
-    else:
-        drop_in += [
-            'ExecStart=!!' + networkd_bin,
-            f'ExecReload={networkctl_bin} reload',
-        ]
-    if enable_debug:
-        drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
-    if asan_options:
-        drop_in += ['Environment=ASAN_OPTIONS="' + asan_options + '"']
-    if lsan_options:
-        drop_in += ['Environment=LSAN_OPTIONS="' + lsan_options + '"']
-    if ubsan_options:
-        drop_in += ['Environment=UBSAN_OPTIONS="' + ubsan_options + '"']
-    if asan_options or lsan_options or ubsan_options:
-        drop_in += ['SystemCallFilter=']
-    if use_valgrind or asan_options or lsan_options or ubsan_options:
-        drop_in += ['MemoryDenyWriteExecute=no']
-    if with_coverage:
-        drop_in += [
-            'ProtectSystem=no',
-            'ProtectHome=no',
+    create_service_dropin('systemd-networkd', networkd_bin,
+                          f'{networkctl_bin} reload',
+                          ['[Service]', 'Restart=no', '[Unit]', 'StartLimitIntervalSec=0'])
+    create_service_dropin('systemd-resolved', resolved_bin)
+    create_service_dropin('systemd-timesyncd', timesyncd_bin)
+
+    # TODO: also run udevd with sanitizers, valgrind, or coverage
+    #create_service_dropin('systemd-udevd', udevd_bin,
+    #                      f'{udevadm_bin} control --reload --timeout 0')
+    create_unit_dropin(
+        'systemd-udevd.service',
+        [
+            '[Service]',
+            'ExecStart=',
+            f'ExecStart=!!{udevd_bin}',
+            'ExecReload=',
+            f'ExecReload={udevadm_bin} control --reload --timeout 0',
         ]
-
-    mkdir_p('/run/systemd/system/systemd-networkd.service.d')
-    with open('/run/systemd/system/systemd-networkd.service.d/00-override.conf', mode='w', encoding='utf-8') as f:
-        f.write('\n'.join(drop_in))
-
-    drop_in = [
-        '[Service]',
-        'Restart=no',
-        'ExecStart=',
-    ]
-    if use_valgrind:
-        drop_in += ['ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + resolved_bin]
-    else:
-        drop_in += ['ExecStart=!!' + resolved_bin]
-    if enable_debug:
-        drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
-    if asan_options:
-        drop_in += ['Environment=ASAN_OPTIONS="' + asan_options + '"']
-    if lsan_options:
-        drop_in += ['Environment=LSAN_OPTIONS="' + lsan_options + '"']
-    if ubsan_options:
-        drop_in += ['Environment=UBSAN_OPTIONS="' + ubsan_options + '"']
-    if asan_options or lsan_options or ubsan_options:
-        drop_in += ['SystemCallFilter=']
-    if use_valgrind or asan_options or lsan_options or ubsan_options:
-        drop_in += ['MemoryDenyWriteExecute=no']
-    if with_coverage:
-        drop_in += [
-            'ProtectSystem=no',
-            'ProtectHome=no',
+    )
+    create_unit_dropin(
+        'systemd-networkd.socket',
+        [
+            '[Unit]',
+            'StartLimitIntervalSec=0',
         ]
-
-    mkdir_p('/run/systemd/system/systemd-resolved.service.d')
-    with open('/run/systemd/system/systemd-resolved.service.d/00-override.conf', mode='w', encoding='utf-8') as f:
-        f.write('\n'.join(drop_in))
-
-    drop_in = [
-        '[Service]',
-        'ExecStart=',
-        'ExecStart=!!' + udevd_bin,
-    ]
-
-    mkdir_p('/run/systemd/system/systemd-udevd.service.d')
-    with open('/run/systemd/system/systemd-udevd.service.d/00-override.conf', mode='w', encoding='utf-8') as f:
-        f.write('\n'.join(drop_in))
+    )
 
     check_output('systemctl daemon-reload')
     print(check_output('systemctl cat systemd-networkd.service'))
     print(check_output('systemctl cat systemd-resolved.service'))
+    print(check_output('systemctl cat systemd-timesyncd.service'))
     print(check_output('systemctl cat systemd-udevd.service'))
     check_output('systemctl restart systemd-resolved.service')
+    check_output('systemctl restart systemd-timesyncd.service')
     check_output('systemctl restart systemd-udevd.service')
 
 def tearDownModule():
@@ -727,9 +718,12 @@ def tearDownModule():
     restore_timezone()
 
     rm_rf('/run/systemd/system/systemd-networkd.service.d')
+    rm_rf('/run/systemd/system/systemd-networkd.socket.d')
     rm_rf('/run/systemd/system/systemd-resolved.service.d')
+    rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
     rm_rf('/run/systemd/system/systemd-udevd.service.d')
     check_output('systemctl daemon-reload')
+    check_output('systemctl restart systemd-udevd.service')
     restore_active_units()
 
 class Utilities():
@@ -857,7 +851,7 @@ class Utilities():
         try:
             check_output(*args, env=wait_online_env)
         except subprocess.CalledProcessError as e:
-            print(e.stdout) # show logs only on failure
+            # show detailed status on failure
             for link in links_with_operstate:
                 name = link.split(':')[0]
                 if link_exists(name):
@@ -898,7 +892,6 @@ class NetworkctlTests(unittest.TestCase, Utilities):
     @expectedFailureIfAlternativeNameIsNotAvailable()
     def test_altname(self):
         copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
-        check_output('udevadm control --reload')
         start_networkd()
         self.wait_online(['dummy98:degraded'])
 
@@ -1334,27 +1327,84 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'link/ether 12:34:56:78:9a:bf')
         self.assertRegex(output, 'mtu 1800')
 
-    def test_tun(self):
-        copy_network_unit('25-tun.netdev')
+    def test_tuntap(self):
+        copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
         start_networkd()
 
-        self.wait_online(['tun99:off'], setup_state='unmanaged')
+        self.wait_online(['testtun99:degraded', 'testtap99:degraded'])
+
+        pid = networkd_pid()
+        name = psutil.Process(pid).name()[:15]
+
+        output = check_output('ip -d tuntap show')
+        print(output)
+        self.assertRegex(output, f'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
+        self.assertRegex(output, f'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
 
-        output = check_output('ip -d link show tun99')
+        output = check_output('ip -d link show testtun99')
         print(output)
         # Old ip command does not support IFF_ flags
         self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
+        self.assertIn('UP,LOWER_UP', output)
 
-    def test_tap(self):
-        copy_network_unit('25-tap.netdev')
-        start_networkd()
+        output = check_output('ip -d link show testtap99')
+        print(output)
+        self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
+        self.assertIn('UP,LOWER_UP', output)
+
+        remove_network_unit('26-netdev-link-local-addressing-yes.network')
+
+        restart_networkd()
+        self.wait_online(['testtun99:degraded', 'testtap99:degraded'], setup_state='unmanaged')
 
-        self.wait_online(['tap99:off'], setup_state='unmanaged')
+        pid = networkd_pid()
+        name = psutil.Process(pid).name()[:15]
 
-        output = check_output('ip -d link show tap99')
+        output = check_output('ip -d tuntap show')
+        print(output)
+        self.assertRegex(output, f'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
+        self.assertRegex(output, f'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
+
+        output = check_output('ip -d link show testtun99')
+        print(output)
+        self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
+        self.assertIn('UP,LOWER_UP', output)
+
+        output = check_output('ip -d link show testtap99')
         print(output)
-        # Old ip command does not support IFF_ flags
         self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
+        self.assertIn('UP,LOWER_UP', output)
+
+        clear_network_units()
+        restart_networkd()
+        self.wait_online(['testtun99:off', 'testtap99:off'], setup_state='unmanaged')
+
+        output = check_output('ip -d tuntap show')
+        print(output)
+        self.assertRegex(output, f'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
+        self.assertRegex(output, f'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
+
+        for i in range(10):
+            if i != 0:
+                time.sleep(1)
+            output = check_output('ip -d link show testtun99')
+            print(output)
+            self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
+            if 'NO-CARRIER' in output:
+                break
+        else:
+            self.fail()
+
+        for i in range(10):
+            if i != 0:
+                time.sleep(1)
+            output = check_output('ip -d link show testtap99')
+            print(output)
+            self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
+            if 'NO-CARRIER' in output:
+                break
+        else:
+            self.fail()
 
     @expectedFailureIfModuleIsNotAvailable('vrf')
     def test_vrf(self):
@@ -1390,6 +1440,14 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         print(output)
         self.assertIn('inet 192.168.124.1/24 scope global wg99', output)
 
+        output = check_output('ip -4 address show dev wg99')
+        print(output)
+        self.assertIn('inet 169.254.11.1/24 scope link wg99', output)
+
+        output = check_output('ip -6 address show dev wg99')
+        print(output)
+        self.assertIn('inet6 fe80::1/64 scope link', output)
+
         output = check_output('ip -4 address show dev wg98')
         print(output)
         self.assertIn('inet 192.168.123.123/24 scope global wg98', output)
@@ -1998,7 +2056,7 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities):
     def tearDown(self):
         tear_down_common()
 
-    @expectedFailureIfModuleIsNotAvailable('l2tp_eth')
+    @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
     def test_l2tp_udp(self):
         copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
                           '25-l2tp-udp.netdev', '25-l2tp.network')
@@ -2026,7 +2084,7 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities):
         self.assertRegex(output, "Peer session 18, tunnel 11")
         self.assertRegex(output, "interface name: l2tp-ses2")
 
-    @expectedFailureIfModuleIsNotAvailable('l2tp_ip')
+    @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
     def test_l2tp_ip(self):
         copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
                           '25-l2tp-ip.netdev', '25-l2tp.network')
@@ -3131,9 +3189,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'Search Domains: one')
 
     def test_keep_configuration_static(self):
-        check_output('systemctl stop systemd-networkd.socket')
-        check_output('systemctl stop systemd-networkd.service')
-
         check_output('ip link add name dummy98 type dummy')
         check_output('ip address add 10.1.2.3/16 dev dummy98')
         check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
@@ -3248,59 +3303,146 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         print(output)
         self.assertEqual(output, '')
 
-    def test_qdisc(self):
-        copy_network_unit('25-qdisc-clsact-and-htb.network', '12-dummy.netdev',
-                          '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')
-        check_output('modprobe sch_teql max_equalizers=2')
+class NetworkdTCTests(unittest.TestCase, Utilities):
+
+    def setUp(self):
+        setup_common()
+
+    def tearDown(self):
+        tear_down_common()
+
+    @expectedFailureIfModuleIsNotAvailable('sch_cake')
+    def test_qdisc_cake(self):
+        copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
         start_networkd()
-        self.wait_online(['dummy98:routable', 'test1:routable'])
+        self.wait_online(['dummy98:routable'])
 
-        output = check_output('tc qdisc show dev test1')
+        output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc netem')
-        self.assertRegex(output, 'limit 100 delay 50(.0)?ms  10(.0)?ms loss 20%')
-        self.assertRegex(output, 'qdisc ingress')
+        self.assertIn('qdisc cake 3a: root', output)
+        self.assertIn('bandwidth 500Mbit', output)
+        self.assertIn('autorate-ingress', output)
+        self.assertIn('diffserv8', output)
+        self.assertIn('dual-dsthost', output)
+        self.assertIn(' nat', output)
+        self.assertIn(' wash', output)
+        self.assertIn(' split-gso', output)
+        self.assertIn(' raw', output)
+        self.assertIn(' atm', output)
+        self.assertIn('overhead 128', output)
+        self.assertIn('mpu 20', output)
+        self.assertIn('fwmark 0xff00', output)
+
+    @expectedFailureIfModuleIsNotAvailable('sch_codel')
+    def test_qdisc_codel(self):
+        copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc clsact')
+        self.assertRegex(output, 'qdisc codel 33: root')
+        self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
 
-        self.assertRegex(output, 'qdisc htb 2: root')
-        self.assertRegex(output, r'default (0x30|30)')
+    @expectedFailureIfModuleIsNotAvailable('sch_drr')
+    def test_qdisc_drr(self):
+        copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
 
-        self.assertRegex(output, 'qdisc netem 30: parent 2:30')
-        self.assertRegex(output, 'limit 100 delay 50(.0)?ms  10(.0)?ms loss 20%')
-        self.assertRegex(output, 'qdisc fq_codel')
-        self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn')
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'qdisc drr 2: root')
+        output = check_output('tc class show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
+
+    @expectedFailureIfModuleIsNotAvailable('sch_ets')
+    def test_qdisc_ets(self):
+        copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
+
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+
+        self.assertRegex(output, 'qdisc ets 3a: root')
+        self.assertRegex(output, 'bands 10 strict 3')
+        self.assertRegex(output, 'quanta 1 2 3 4 5')
+        self.assertRegex(output, 'priomap 3 4 5 6 7')
 
-        self.assertRegex(output, 'qdisc teql1 31: parent 2:31')
+    @expectedFailureIfModuleIsNotAvailable('sch_fq')
+    def test_qdisc_fq(self):
+        copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
 
-        self.assertRegex(output, 'qdisc fq 32: parent 2:32')
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'qdisc fq 32: root')
         self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
         self.assertRegex(output, 'quantum 1500')
         self.assertRegex(output, 'initial_quantum 13000')
         self.assertRegex(output, 'maxrate 1Mbit')
 
-        self.assertRegex(output, 'qdisc codel 33: parent 2:33')
-        self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
+    @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
+    def test_qdisc_fq_codel(self):
+        copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
 
-        self.assertRegex(output, 'qdisc fq_codel 34: parent 2:34')
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'qdisc fq_codel 34: root')
         self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn')
 
-        self.assertRegex(output, 'qdisc tbf 35: parent 2:35')
-        self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
+    @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
+    def test_qdisc_fq_pie(self):
+        copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
 
-        self.assertRegex(output, 'qdisc sfq 36: parent 2:36')
-        self.assertRegex(output, 'perturb 5sec')
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
 
-        self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
-        self.assertRegex(output, 'limit 100000p')
+        self.assertRegex(output, 'qdisc fq_pie 3a: root')
+        self.assertRegex(output, 'limit 200000p')
+
+    @expectedFailureIfModuleIsNotAvailable('sch_gred')
+    def test_qdisc_gred(self):
+        copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
 
-        self.assertRegex(output, 'qdisc gred 38: parent 2:38')
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'qdisc gred 38: root')
         self.assertRegex(output, 'vqs 12 default 10 grio')
 
-        self.assertRegex(output, 'qdisc sfb 39: parent 2:39')
-        self.assertRegex(output, 'limit 200000')
+    @expectedFailureIfModuleIsNotAvailable('sch_hhf')
+    def test_qdisc_hhf(self):
+        copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
+
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'qdisc hhf 3a: root')
+        self.assertRegex(output, 'limit 1022p')
+
+    @expectedFailureIfModuleIsNotAvailable('sch_htb')
+    def test_qdisc_htb_fifo(self):
+        copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
+
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'qdisc htb 2: root')
+        self.assertRegex(output, r'default (0x30|30)')
+
+        self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
+        self.assertRegex(output, 'limit 100000p')
 
         self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a')
         self.assertRegex(output, 'limit 1000000')
@@ -3312,16 +3454,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
 
         output = check_output('tc -d class show dev dummy98')
         print(output)
-        self.assertRegex(output, 'class htb 2:30 root leaf 30:')
-        self.assertRegex(output, 'class htb 2:31 root leaf 31:')
-        self.assertRegex(output, 'class htb 2:32 root leaf 32:')
-        self.assertRegex(output, 'class htb 2:33 root leaf 33:')
-        self.assertRegex(output, 'class htb 2:34 root leaf 34:')
-        self.assertRegex(output, 'class htb 2:35 root leaf 35:')
-        self.assertRegex(output, 'class htb 2:36 root leaf 36:')
         self.assertRegex(output, 'class htb 2:37 root leaf 37:')
-        self.assertRegex(output, 'class htb 2:38 root leaf 38:')
-        self.assertRegex(output, 'class htb 2:39 root leaf 39:')
         self.assertRegex(output, 'class htb 2:3a root leaf 3a:')
         self.assertRegex(output, 'class htb 2:3b root leaf 3b:')
         self.assertRegex(output, 'class htb 2:3c root leaf 3c:')
@@ -3329,50 +3462,39 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'burst 123456')
         self.assertRegex(output, 'cburst 123457')
 
-    def test_qdisc2(self):
-        copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev',
-                          '25-qdisc-qfq.network', '11-dummy.netdev')
+    @expectedFailureIfModuleIsNotAvailable('sch_ingress')
+    def test_qdisc_ingress(self):
+        copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
+                          '25-qdisc-ingress.network', '11-dummy.netdev')
         start_networkd()
         self.wait_online(['dummy98:routable', 'test1:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc drr 2: root')
-        output = check_output('tc class show dev dummy98')
-        print(output)
-        self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
+        self.assertRegex(output, 'qdisc clsact')
 
         output = check_output('tc qdisc show dev test1')
         print(output)
-        self.assertRegex(output, 'qdisc qfq 2: root')
-        output = check_output('tc class show dev test1')
-        print(output)
-        self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
-        self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
+        self.assertRegex(output, 'qdisc ingress')
 
-    @expectedFailureIfCAKEIsNotAvailable()
-    def test_qdisc_cake(self):
-        copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
+    @expectedFailureIfModuleIsNotAvailable('sch_netem')
+    def test_qdisc_netem(self):
+        copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
+                          '25-qdisc-netem-compat.network', '11-dummy.netdev')
         start_networkd()
-        self.wait_online(['dummy98:routable'])
+        self.wait_online(['dummy98:routable', 'test1:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertIn('qdisc cake 3a: root', output)
-        self.assertIn('bandwidth 500Mbit', output)
-        self.assertIn('autorate-ingress', output)
-        self.assertIn('diffserv8', output)
-        self.assertIn('dual-dsthost', output)
-        self.assertIn(' nat', output)
-        self.assertIn(' wash', output)
-        self.assertIn(' split-gso', output)
-        self.assertIn(' raw', output)
-        self.assertIn(' atm', output)
-        self.assertIn('overhead 128', output)
-        self.assertIn('mpu 20', output)
-        self.assertIn('fwmark 0xff00', output)
+        self.assertRegex(output, 'qdisc netem 30: root')
+        self.assertRegex(output, 'limit 100 delay 50(.0)?ms  10(.0)?ms loss 20%')
 
-    @expectedFailureIfPIEIsNotAvailable()
+        output = check_output('tc qdisc show dev test1')
+        print(output)
+        self.assertRegex(output, 'qdisc netem [0-9a-f]*: root')
+        self.assertRegex(output, 'limit 100 delay 50(.0)?ms  10(.0)?ms loss 20%')
+
+    @expectedFailureIfModuleIsNotAvailable('sch_pie')
     def test_qdisc_pie(self):
         copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
         start_networkd()
@@ -3383,43 +3505,76 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'qdisc pie 3a: root')
         self.assertRegex(output, 'limit 200000')
 
-    @expectedFailureIfHHFIsNotAvailable()
-    def test_qdisc_hhf(self):
-        copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
+    @expectedFailureIfModuleIsNotAvailable('sch_qfq')
+    def test_qdisc_qfq(self):
+        copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
         start_networkd()
         self.wait_online(['dummy98:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc hhf 3a: root')
-        self.assertRegex(output, 'limit 1022p')
+        self.assertRegex(output, 'qdisc qfq 2: root')
+        output = check_output('tc class show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
+        self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
 
-    @expectedFailureIfETSIsNotAvailable()
-    def test_qdisc_ets(self):
-        copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
+    @expectedFailureIfModuleIsNotAvailable('sch_sfb')
+    def test_qdisc_sfb(self):
+        copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
         start_networkd()
         self.wait_online(['dummy98:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
+        self.assertRegex(output, 'qdisc sfb 39: root')
+        self.assertRegex(output, 'limit 200000')
 
-        self.assertRegex(output, 'qdisc ets 3a: root')
-        self.assertRegex(output, 'bands 10 strict 3')
-        self.assertRegex(output, 'quanta 1 2 3 4 5')
-        self.assertRegex(output, 'priomap 3 4 5 6 7')
+    @expectedFailureIfModuleIsNotAvailable('sch_sfq')
+    def test_qdisc_sfq(self):
+        copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
 
-    @expectedFailureIfFQPIEIsNotAvailable()
-    def test_qdisc_fq_pie(self):
-        copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'qdisc sfq 36: root')
+        self.assertRegex(output, 'perturb 5sec')
+
+    @expectedFailureIfModuleIsNotAvailable('sch_tbf')
+    def test_qdisc_tbf(self):
+        copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
         start_networkd()
         self.wait_online(['dummy98:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
+        self.assertRegex(output, 'qdisc tbf 35: root')
+        self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
 
-        self.assertRegex(output, 'qdisc fq_pie 3a: root')
-        self.assertRegex(output, 'limit 200000p')
+    @expectedFailureIfModuleIsNotAvailable('sch_teql')
+    def test_qdisc_teql(self):
+        call_quiet('rmmod sch_teql')
+
+        copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_links('dummy98')
+        check_output('modprobe sch_teql max_equalizers=2')
+        self.wait_online(['dummy98:routable'])
+
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'qdisc teql1 31: root')
 
+class NetworkWaitOnlineTests(unittest.TestCase, Utilities):
+
+    def setUp(self):
+        setup_common()
+
+    def tearDown(self):
+        tear_down_common()
+
+    @expectedFailureIfModuleIsNotAvailable('sch_netem')
     def test_wait_online_ipv4(self):
         copy_network_unit('25-veth.netdev', '25-dhcp-server-with-ipv6-prefix.network', '25-dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network')
         start_networkd()
@@ -3428,6 +3583,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
 
         self.wait_address('veth99', r'192.168.5.[0-9]+', ipv='-4', timeout_sec=1)
 
+    @expectedFailureIfModuleIsNotAvailable('sch_netem')
     def test_wait_online_ipv6(self):
         copy_network_unit('25-veth.netdev', '25-ipv6-prefix-with-delay.network', '25-ipv6ra-prefix-client-with-static-ipv4-address.network')
         start_networkd()
@@ -3702,8 +3858,8 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
         self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress',       '1', allow_enoent=True)
         self.check_bridge_port_attr('bridge99', 'dummy98', 'learning',             '0')
         self.check_bridge_port_attr('bridge99', 'dummy98', 'priority',             '23')
-        self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard',           '1')
-        self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block',           '1')
+        self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard',           '0')
+        self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block',           '0')
 
     def test_bridge_property(self):
         copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
@@ -3712,19 +3868,25 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
 
+        output = check_output('ip -d link show bridge99')
+        print(output)
+        self.assertIn('mtu 9000 ', output)
+
         output = check_output('ip -d link show test1')
         print(output)
-        self.assertRegex(output, 'master')
-        self.assertRegex(output, 'bridge')
+        self.assertIn('master bridge99 ', output)
+        self.assertIn('bridge_slave', output)
+        self.assertIn('mtu 9000 ', output)
 
         output = check_output('ip -d link show dummy98')
         print(output)
-        self.assertRegex(output, 'master')
-        self.assertRegex(output, 'bridge')
+        self.assertIn('master bridge99 ', output)
+        self.assertIn('bridge_slave', output)
+        self.assertIn('mtu 9000 ', output)
 
         output = check_output('ip addr show bridge99')
         print(output)
-        self.assertRegex(output, '192.168.0.15/24')
+        self.assertIn('192.168.0.15/24', output)
 
         output = check_output('bridge -d link show dummy98')
         print(output)
@@ -3739,8 +3901,8 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
         self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress',       '1', allow_enoent=True)
         self.check_bridge_port_attr('bridge99', 'dummy98', 'learning',             '0')
         self.check_bridge_port_attr('bridge99', 'dummy98', 'priority',             '23')
-        self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard',           '1')
-        self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block',           '1')
+        self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard',           '0')
+        self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block',           '0')
 
         output = check_output('bridge -d link show test1')
         print(output)
@@ -3749,7 +3911,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
         check_output('ip address add 192.168.0.16/24 dev bridge99')
         output = check_output('ip addr show bridge99')
         print(output)
-        self.assertRegex(output, '192.168.0.16/24')
+        self.assertIn('192.168.0.16/24', output)
 
         # for issue #6088
         print('### ip -6 route list table all dev bridge99')
@@ -3758,24 +3920,49 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
 
         remove_link('test1')
-
         self.wait_operstate('bridge99', 'degraded-carrier')
 
-        remove_link('dummy98')
+        output = check_output('ip -d link show bridge99')
+        print(output)
+        self.assertIn('mtu 9000 ', output)
+
+        output = check_output('ip -d link show dummy98')
+        print(output)
+        self.assertIn('master bridge99 ', output)
+        self.assertIn('bridge_slave', output)
+        self.assertIn('mtu 9000 ', output)
 
+        remove_link('dummy98')
         self.wait_operstate('bridge99', 'no-carrier')
 
+        output = check_output('ip -d link show bridge99')
+        print(output)
+        # When no carrier, the kernel may reset the MTU
+        self.assertIn('NO-CARRIER', output)
+
         output = check_output('ip address show bridge99')
         print(output)
-        self.assertRegex(output, 'NO-CARRIER')
-        self.assertNotRegex(output, '192.168.0.15/24')
-        self.assertRegex(output, '192.168.0.16/24') # foreign address is kept
+        self.assertNotIn('192.168.0.15/24', output)
+        self.assertIn('192.168.0.16/24', output) # foreign address is kept
 
         print('### ip -6 route list table all dev bridge99')
         output = check_output('ip -6 route list table all dev bridge99')
         print(output)
         self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
 
+        check_output('ip link add dummy98 type dummy')
+        self.wait_online(['dummy98:enslaved', 'bridge99:routable'])
+
+        output = check_output('ip -d link show bridge99')
+        print(output)
+        self.assertIn('mtu 9000 ', output)
+
+        output = check_output('ip -d link show dummy98')
+        print(output)
+        self.assertIn('master bridge99 ', output)
+        self.assertIn('bridge_slave', output)
+        self.assertIn('mtu 9000 ', output)
+
     def test_bridge_configure_without_carrier(self):
         copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
                           '11-dummy.netdev')
@@ -3896,7 +4083,6 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
     @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
     def test_sriov_udev(self):
         copy_network_unit('25-sriov.link', '25-sriov-udev.network')
-        call('udevadm control --reload')
 
         call('modprobe netdevsim')
 
@@ -3919,8 +4105,8 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
         with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
             f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
 
-        call('udevadm control --reload')
-        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+        udev_reload()
+        call(*udevadm_cmd, 'trigger', '--action=add', '--settle', '/sys/devices/netdevsim99/net/eni99np1')
 
         output = check_output('ip link show dev eni99np1')
         print(output)
@@ -3935,8 +4121,8 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
         with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
             f.write('[Link]\nSR-IOVVirtualFunctions=\n')
 
-        call('udevadm control --reload')
-        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+        udev_reload()
+        call(*udevadm_cmd, 'trigger', '--action=add', '--settle', '/sys/devices/netdevsim99/net/eni99np1')
 
         output = check_output('ip link show dev eni99np1')
         print(output)
@@ -3951,8 +4137,8 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
         with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
             f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
 
-        call('udevadm control --reload')
-        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+        udev_reload()
+        call(*udevadm_cmd, 'trigger', '--action=add', '--settle', '/sys/devices/netdevsim99/net/eni99np1')
 
         output = check_output('ip link show dev eni99np1')
         print(output)
@@ -3967,8 +4153,8 @@ class NetworkdSRIOVTests(unittest.TestCase, Utilities):
         with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
             f.write('[Link]\nSR-IOVVirtualFunctions=\n')
 
-        call('udevadm control --reload')
-        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+        udev_reload()
+        call(*udevadm_cmd, 'trigger', '--action=add', '--settle', '/sys/devices/netdevsim99/net/eni99np1')
 
         output = check_output('ip link show dev eni99np1')
         print(output)
@@ -4170,6 +4356,44 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, 'token :: dev veth99')
 
+        print('## dnsmasq log')
+        output = read_dnsmasq_log_file()
+        print(output)
+        self.assertIn('DHCPSOLICIT(veth-peer)', output)
+        self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
+        self.assertNotIn('DHCPREQUEST(veth-peer)', output)
+        self.assertIn('DHCPREPLY(veth-peer)', output)
+        self.assertIn('sent size:  0 option: 14 rapid-commit', output)
+
+        with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
+            f.write('\n[DHCPv6]\nRapidCommit=no\n')
+
+        stop_dnsmasq()
+        start_dnsmasq()
+
+        networkctl_reload()
+        self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
+        # checking address
+        output = check_output('ip address show dev veth99 scope global')
+        print(output)
+        self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
+        self.assertNotIn('192.168.5', output)
+
+        # checking semi-static route
+        output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
+        print(output)
+        self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
+
+        print('## dnsmasq log')
+        output = read_dnsmasq_log_file()
+        print(output)
+        self.assertIn('DHCPSOLICIT(veth-peer)', output)
+        self.assertIn('DHCPADVERTISE(veth-peer)', output)
+        self.assertIn('DHCPREQUEST(veth-peer)', output)
+        self.assertIn('DHCPREPLY(veth-peer)', output)
+        self.assertNotIn('rapid-commit', output)
+
     def test_dhcp_client_ipv4_only(self):
         copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
 
@@ -4570,7 +4794,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         output = check_output('ip -4 address show dev veth99')
         print(output)
         self.assertNotIn('192.168.5.', output)
-        self.assertRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
+        self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
 
         start_dnsmasq()
         print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
@@ -4587,12 +4811,12 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         stop_dnsmasq()
         print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
         self.wait_address_dropped('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4', timeout_sec=130)
-        self.wait_address('veth99', r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
+        self.wait_address('veth99', r'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
 
         output = check_output('ip -4 address show dev veth99')
         print(output)
         self.assertNotIn('192.168.5.', output)
-        self.assertRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
+        self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
 
     def test_dhcp_client_use_dns(self):
         def check(self, ipv4, ipv6):
@@ -5203,8 +5427,6 @@ class NetworkdMTUTests(unittest.TestCase, Utilities):
 
     def test_mtu_link(self):
         copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins=False)
-        # must reload udev because it only picks up new files after 3 second delay
-        call('udevadm control --reload')
         # note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
         self.check_mtu('1600', reset=False)
 
@@ -5231,8 +5453,6 @@ class NetworkdMTUTests(unittest.TestCase, Utilities):
     def test_mtu_link_ipv6_mtu(self):
         ''' set ipv6 mtu and set device mtu via link file '''
         copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
-        # must reload udev because it only picks up new files after 3 second delay
-        call('udevadm control --reload')
         self.check_mtu('1600', '1550', reset=False)
 
 
@@ -5241,11 +5461,13 @@ if __name__ == '__main__':
     parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
     parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin')
     parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin')
+    parser.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest='timesyncd_bin')
     parser.add_argument('--udevd', help='Path to systemd-udevd', dest='udevd_bin')
     parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin')
     parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin')
     parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin')
     parser.add_argument('--timedatectl', help='Path to timedatectl', dest='timedatectl_bin')
+    parser.add_argument('--udevadm', help='Path to udevadm', dest='udevadm_bin')
     parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
     parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
     parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
@@ -5255,20 +5477,25 @@ if __name__ == '__main__':
     ns, unknown_args = parser.parse_known_args(namespace=unittest)
 
     if ns.build_dir:
-        if ns.networkd_bin or ns.resolved_bin or ns.udevd_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin:
-            print('WARNING: --networkd, --resolved, --wait-online, --networkctl, --resolvectl, or --timedatectl options are ignored when --build-dir is specified.')
+        if ns.networkd_bin or ns.resolved_bin or ns.timesyncd_bin or ns.udevd_bin or \
+           ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin or ns.udevadm_bin:
+            print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
         networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
         resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
+        timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
         udevd_bin = os.path.join(ns.build_dir, 'systemd-udevd')
         wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
         networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
         resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
         timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
+        udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
     else:
         if ns.networkd_bin:
             networkd_bin = ns.networkd_bin
         if ns.resolved_bin:
             resolved_bin = ns.resolved_bin
+        if ns.timesyncd_bin:
+            timesyncd_bin = ns.timesyncd_bin
         if ns.udevd_bin:
             udevd_bin = ns.udevd_bin
         if ns.wait_online_bin:
@@ -5279,6 +5506,8 @@ if __name__ == '__main__':
             resolvectl_bin = ns.resolvectl_bin
         if ns.timedatectl_bin:
             timedatectl_bin = ns.timedatectl_bin
+        if ns.udevadm_bin:
+            udevadm_bin = ns.udevadm_bin
 
     use_valgrind = ns.use_valgrind
     enable_debug = ns.enable_debug
@@ -5288,15 +5517,14 @@ if __name__ == '__main__':
     with_coverage = ns.with_coverage
 
     if use_valgrind:
-        networkctl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', networkctl_bin]
-        resolvectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', resolvectl_bin]
-        timedatectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', timedatectl_bin]
-        wait_online_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', wait_online_bin]
-    else:
-        networkctl_cmd = [networkctl_bin]
-        resolvectl_cmd = [resolvectl_bin]
-        timedatectl_cmd = [timedatectl_bin]
-        wait_online_cmd = [wait_online_bin]
+        # Do not forget the trailing space.
+        valgrind_cmd = 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
+
+    networkctl_cmd = valgrind_cmd.split() + [networkctl_bin]
+    resolvectl_cmd = valgrind_cmd.split() + [resolvectl_bin]
+    timedatectl_cmd = valgrind_cmd.split() + [timedatectl_bin]
+    udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
+    wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
 
     if asan_options:
         env.update({'ASAN_OPTIONS': asan_options})
@@ -5304,6 +5532,8 @@ if __name__ == '__main__':
         env.update({'LSAN_OPTIONS': lsan_options})
     if ubsan_options:
         env.update({'UBSAN_OPTIONS': ubsan_options})
+    if use_valgrind:
+        env.update({'SYSTEMD_MEMPOOL': '0'})
 
     wait_online_env = env.copy()
     if enable_debug:
index ba42b3fa3702833cada8fc530807aa2d7b95d499..4b3bdf33644b14562bef5474db9e72d8106cf19c 100755 (executable)
@@ -188,6 +188,14 @@ def test_hard_cleanup(*, user):
     label = 'valid_symlink-deep'
     test_content('f= {} - - - - ' + label, label, user=user, subpath='/deep/1/2', path_cb=valid_symlink)
 
+def test_base64():
+    test_line('f~ /tmp/base64-test - - - - UGlmZgpQYWZmClB1ZmYgCg==', user=False, returncode=0)
+
+    with open("/tmp/base64-test", mode='r') as f:
+        d = f.read()
+
+    assert d == "Piff\nPaff\nPuff \n"
+
 if __name__ == '__main__':
     test_invalids(user=False)
     test_invalids(user=True)
@@ -198,3 +206,5 @@ if __name__ == '__main__':
 
     test_hard_cleanup(user=False)
     test_hard_cleanup(user=True)
+
+    test_base64()
index 66357ab688bf5c36062ba078ba1558bc78f5d695..2f4d93ab8cd11b7cb896cda8abaaab440728c48f 100644 (file)
@@ -4,10 +4,10 @@
 # utility functions for shell tests
 
 assert_true() {(
-    local rc
-
     set +ex
 
+    local rc
+
     "$@"
     rc=$?
     if [[ $rc -ne 0 ]]; then
@@ -47,10 +47,10 @@ assert_not_in() {(
 )}
 
 assert_rc() {(
-    local rc exp="${1?}"
-
     set +ex
 
+    local rc exp="${1?}"
+
     shift
     "$@"
     rc=$?
diff --git a/test/units/testsuite-17.07.sh b/test/units/testsuite-17.07.sh
new file mode 100755 (executable)
index 0000000..549107a
--- /dev/null
@@ -0,0 +1,205 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/assert.sh
+. "$(dirname "$0")"/assert.sh
+
+wait_service_active() {(
+    set +ex
+    for (( i = 0; i < 20; i++ )); do
+        if (( i != 0 )); then sleep 0.5; fi
+        if systemctl --quiet is-active "${1?}"; then
+            return 0
+        fi
+    done
+    return 1
+)}
+
+wait_service_inactive() {(
+    set +ex
+    for (( i = 0; i < 20; i++ )); do
+        if (( i != 0 )); then sleep 0.5; fi
+        systemctl --quiet is-active "${1?}"
+        if [[ "$?" == "3" ]]; then
+            return 0
+        fi
+    done
+    return 1
+)}
+
+mkdir -p /run/systemd/system
+cat >/run/systemd/system/both.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+cat >/run/systemd/system/on-add.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+cat >/run/systemd/system/on-change.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+systemctl daemon-reload
+
+mkdir -p /run/udev/rules.d/
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="net", KERNEL=="dummy9?", OPTIONS="log_level=debug"
+SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="add",    TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-add.service"
+SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="change", TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-change.service"
+EOF
+
+udevadm control --reload
+
+# StopWhenUnneeded=no
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+systemctl stop both.service on-add.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+udevadm info /sys/class/net/dummy99
+wait_service_active both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_active on-change.service
+systemctl stop both.service on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+assert_rc 3 systemctl --quiet is-active both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes
+cat >/run/systemd/system/both.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=sleep 1000
+Type=simple
+EOF
+
+cat >/run/systemd/system/on-add.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=sleep 1000
+Type=simple
+EOF
+
+cat >/run/systemd/system/on-change.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=echo changed
+RemainAfterExit=true
+Type=oneshot
+EOF
+
+systemctl daemon-reload
+
+# StopWhenUnneeded=yes (single device, only add event)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+wait_service_inactive on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes (single device, add and change event)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+wait_service_inactive on-add.service
+wait_service_active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_inactive on-change.service
+
+# StopWhenUnneeded=yes (multiple devices, only add events)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link add dummy98 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy98
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
+wait_service_inactive both.service
+wait_service_inactive on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes (multiple devices, add and change events)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link add dummy98 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+wait_service_active on-change.service
+
+ip link del dummy98
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+wait_service_inactive on-add.service
+assert_rc 0 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_inactive on-change.service
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+rm -f /run/systemd/system/on-add.service
+rm -f /run/systemd/system/on-change.service
+systemctl daemon-reload
+
+exit 0
index ee4eb8431eef0ee74edc5946c00db03ab21968c3..6ce6d3d42918fea52e37170b018a6dd39f10f638 100755 (executable)
@@ -3,6 +3,16 @@
 set -eux
 set -o pipefail
 
+test_scope_unpriv_delegation() {
+    useradd test ||:
+    trap "userdel -r test" RETURN
+
+    systemd-run --uid=test -p User=test -p Delegate=yes --slice workload.slice --unit workload0.scope --scope \
+            test -w /sys/fs/cgroup/workload.slice/workload0.scope -a \
+            -w /sys/fs/cgroup/workload.slice/workload0.scope/cgroup.procs -a \
+            -w /sys/fs/cgroup/workload.slice/workload0.scope/cgroup.subtree_control
+}
+
 if grep -q cgroup2 /proc/filesystems ; then
     systemd-run --wait --unit=test0.service -p "DynamicUser=1" -p "Delegate=" \
                 test -w /sys/fs/cgroup/system.slice/test0.service/ -a \
@@ -31,6 +41,10 @@ if grep -q cgroup2 /proc/filesystems ; then
 
     # And now check again, "io" should have vanished
     grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+    # Check that unprivileged delegation works for scopes
+    test_scope_unpriv_delegation
+
 else
     echo "Skipping TEST-19-DELEGATE, as the kernel doesn't actually support cgroup v2" >&2
 fi
index 33b5fbf1ee3f0873f3e944f83fd99ae62fce54b4..eae2cb801d4c39fffa1029fde4169e15ec0ffc40 100755 (executable)
@@ -221,13 +221,43 @@ test_shutdown() {
 cleanup_session() (
     set +ex
 
+    local uid s
+
+    uid=$(id -u logind-test-user)
+
+    loginctl disable-linger logind-test-user
+
     systemctl stop getty@tty2.service
-    rm -rf /run/systemd/system/getty@tty2.service.d
-    systemctl daemon-reload
 
-    pkill -u "$(id -u logind-test-user)"
+    for s in $(loginctl --no-legend list-sessions | awk '$3 == "logind-test-user" { print $1 }'); do
+        echo "INFO: stopping session $s"
+        loginctl terminate-session "$s"
+    done
+
+    loginctl terminate-user logind-test-user
+
+    if ! timeout 30 bash -c "while loginctl --no-legend | grep -q logind-test-user; do sleep 1; done"; then
+        echo "WARNING: session for logind-test-user still active, ignoring."
+    fi
+
+    pkill -u "$uid"
     sleep 1
-    pkill -KILL -u "$(id -u logind-test-user)"
+    pkill -KILL -u "$uid"
+
+    if ! timeout 30 bash -c "while systemctl is-active --quiet user@${uid}.service; do sleep 1; done"; then
+        echo "WARNING: user@${uid}.service is still active, ignoring."
+    fi
+
+    if ! timeout 30 bash -c "while systemctl is-active --quiet user-runtime-dir@${uid}.service; do sleep 1; done"; then
+        echo "WARNING: user-runtime-dir@${uid}.service is still active, ignoring."
+    fi
+
+    if ! timeout 30 bash -c "while systemctl is-active --quiet user-${uid}.slice; do sleep 1; done"; then
+        echo "WARNING: user-${uid}.slice is still active, ignoring."
+    fi
+
+    rm -rf /run/systemd/system/getty@tty2.service.d
+    systemctl daemon-reload
 
     return 0
 )
@@ -260,7 +290,7 @@ check_session() (
         return 1
     fi
 
-    session=$(loginctl --no-legend | grep "logind-test-user" | awk '{ print $1 }')
+    session=$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')
     if [[ -z "$session" ]]; then
         echo "no session found for user logind-test-user" >&2
         return 1
@@ -271,7 +301,7 @@ check_session() (
         return 1
     fi
 
-    leader_pid=$(loginctl session-status "$session" | grep "Leader:" | awk '{ print $2 }')
+    leader_pid=$(loginctl session-status "$session" | awk '$1 == "Leader:" { print $2 }')
     if [[ -z "$leader_pid" ]]; then
         echo "cannot found leader process for session $session" >&2
         return 1
@@ -293,6 +323,7 @@ create_session() {
 Type=simple
 ExecStart=
 ExecStart=-/sbin/agetty --autologin logind-test-user --noclear %I $TERM
+Restart=no
 EOF
     systemctl daemon-reload
 
@@ -351,7 +382,7 @@ EOF
     udevadm info "$dev"
 
     # trigger logind and activate session
-    loginctl activate "$(loginctl --no-legend | grep "logind-test-user" | awk '{ print $1 }')"
+    loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')"
 
     # check ACL
     sleep 1
@@ -376,11 +407,11 @@ EOF
 teardown_lock_idle_action() (
     set +eux
 
-    cleanup_session
-
     rm -f /run/systemd/logind.conf.d/idle-action-lock.conf
     systemctl restart systemd-logind.service
 
+    cleanup_session
+
     return 0
 )
 
@@ -430,53 +461,22 @@ EOF
     fi
 }
 
-teardown_cron() (
-    set +ex
-
-    pkill -u "$(id -u logind-test-user)"
-    sleep 1
-    pkill -KILL -u "$(id -u logind-test-user)"
-    pkill crond
-    crontab -r -u logind-test-user
-
-    return 0
-)
+test_session_properties() {
+    local s
 
-test_no_user_instance_for_cron() {
-    if ! command -v crond || ! command -v crontab ; then
-        echo "Skipping test for background cron sessions because cron is missing."
+    if [[ ! -c /dev/tty2 ]]; then
+        echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
         return
     fi
 
-    trap teardown_cron RETURN
-
-    # Setup cron
-    crond -s -n &
-    # Install crontab for the test user that runs sleep every minute. But let's sleep for
-    # 65 seconds to make sure there is overlap between two consecutive runs, i.e. we have
-    # always a cron session running.
-    crontab -u logind-test-user - <<EOF
-RANDOM_DELAY=0
-* * * * * /bin/sleep 65
-EOF
-
-    # Let's wait (at most one interval plus 10s to accommodate for slow machines) for at least one session
-    # of the test user
-    timeout 70 bash -c "while ! loginctl --no-legend list-sessions | grep -q logind-test-user; do sleep 1; done"
-
-    # Check that all sessions of test user have class=background and no user instance was started
-    # for the test user.
-    while read -r s _; do
-        assert_eq "$(loginctl --property Class --value show-session "$s")" "background"
-    done < <(loginctl --no-legend list-sessions | grep logind-test-user)
+    trap cleanup_session RETURN
+    create_session
 
-    assert_eq "$(systemctl --property ActiveState --value show user@"$(id -u logind-test-user)".service)" "inactive"
-    assert_eq "$(systemctl --property SubState --value show user@"$(id -u logind-test-user)".service)" "dead"
+    s=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }')
+    /usr/lib/systemd/tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}"
 }
 
-test_session_properties() {
-    local s
-
+test_list_users() {
     if [[ ! -c /dev/tty2 ]]; then
         echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
         return
@@ -485,8 +485,12 @@ test_session_properties() {
     trap cleanup_session RETURN
     create_session
 
-    s=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }')
-    /usr/lib/systemd/tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}"
+    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $1 }')" "$(id -ru logind-test-user)"
+    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" no
+
+    loginctl enable-linger logind-test-user
+
+    assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes
 }
 
 : >/failed
@@ -499,8 +503,8 @@ test_suspend_on_lid
 test_shutdown
 test_session
 test_lock_idle_action
-test_no_user_instance_for_cron
 test_session_properties
+test_list_users
 
 touch /testok
 rm /failed
index 2f1844ccf7463f717c70bdff87dd6b70e0dea907..f2d937daba3a5f8e6776e26da177f234115012db 100755 (executable)
@@ -58,8 +58,8 @@ if [ "${verity_count}" -lt 1 ]; then
     echo "Verity device ${image}.raw not found in /dev/mapper/"
     exit 1
 fi
-umount "${image_dir}/mount"
-umount "${image_dir}/mount2"
+systemd-dissect --umount "${image_dir}/mount"
+systemd-dissect --umount "${image_dir}/mount2"
 
 systemd-run -P -p RootImage="${image}.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1"
 mv "${image}.verity" "${image}.fooverity"
@@ -207,7 +207,7 @@ systemd-dissect --root-hash "${roothash}" --mount "${image}.gpt" "${image_dir}/m
 grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
 grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
 grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
-umount "${image_dir}/mount"
+systemd-dissect --umount "${image_dir}/mount"
 
 # add explicit -p MountAPIVFS=yes once to test the parser
 systemd-run -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
@@ -285,7 +285,7 @@ Type=notify
 RemainAfterExit=yes
 MountAPIVFS=yes
 PrivateTmp=yes
-ExecStart=/bin/sh -c 'systemd-notify --ready; while ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do sleep 0.1; done; mount | grep -F "/dev/mapper/${roothash}-verity" | grep -q -F "nosuid"'
+ExecStart=/bin/sh -c 'systemd-notify --ready; while ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do sleep 0.1; done; mount | grep -e "/dev/mapper/${roothash}-verity" -e "/dev/mapper/loop[0-9]*-verity" | grep -q -F "nosuid"'
 EOF
 systemctl start testservice-50d.service
 
@@ -305,6 +305,7 @@ systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.r
 systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
 systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
 systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P --property ExtensionImages=/usr/share/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
 cat >/run/systemd/system/testservice-50e.service <<EOF
 [Service]
 MountAPIVFS=yes
@@ -323,17 +324,19 @@ systemctl start testservice-50e.service
 systemctl is-active testservice-50e.service
 
 # ExtensionDirectories will set up an overlay
-mkdir -p "${image_dir}/app0" "${image_dir}/app1"
+mkdir -p "${image_dir}/app0" "${image_dir}/app1" "${image_dir}/app-nodistro"
 systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; }
 systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; }
 systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0"
 systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1"
+systemd-dissect --mount /usr/share/app-nodistro.raw "${image_dir}/app-nodistro"
 systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
 systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
 systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
 systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
 systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
 systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app-nodistro" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
 cat >/run/systemd/system/testservice-50f.service <<EOF
 [Service]
 MountAPIVFS=yes
@@ -350,8 +353,21 @@ RemainAfterExit=yes
 EOF
 systemctl start testservice-50f.service
 systemctl is-active testservice-50f.service
-umount "${image_dir}/app0"
-umount "${image_dir}/app1"
+systemd-dissect --umount "${image_dir}/app0"
+systemd-dissect --umount "${image_dir}/app1"
+
+# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
+mkdir -p /var/lib/extensions/
+ln -s /usr/share/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
+systemd-sysext merge
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+mkdir -p /etc/extensions/app-nodistro
+systemd-sysext merge
+test ! -e /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+rmdir /etc/extensions/app-nodistro
+rm /var/lib/extensions/app-nodistro.raw
 
 echo OK >/testok
 
index 84cd66129d007e40c166bf8b772e26161a5a7b14..f12b7379fdb2720b073ded257371a7b008641a38 100755 (executable)
@@ -5,27 +5,44 @@ set -o pipefail
 
 : >/failed
 
+# Run a timer for every 15 minutes before setting the current time
+systemd-run --unit test-timer-1 --on-calendar "*:0/15:0" true
+
 # Reset host date to current time, 3 days in the past.
 date -s "-3 days"
 
-# Run a timer for every 15 minutes.
-systemd-run --unit test-timer --on-calendar "*:0/15:0" true
+# Run another timer for every 15 minutes, after setting the time
+systemd-run --unit test-timer-2 --on-calendar "*:0/15:0" true
+
+next_elapsed_t1=$(systemctl show test-timer-1.timer -p NextElapseUSecRealtime --value)
+next_elapsed_t1=$(date -d "${next_elapsed_t1}" +%s)
+now=$(date +%s)
+time_delta_t1=$((next_elapsed_t1 - now))
 
-next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value)
-next_elapsed=$(date -d "${next_elapsed}" +%s)
+next_elapsed_t2=$(systemctl show test-timer-2.timer -p NextElapseUSecRealtime --value)
+next_elapsed_t2=$(date -d "${next_elapsed_t2}" +%s)
 now=$(date +%s)
-time_delta=$((next_elapsed - now))
+time_delta_t2=$((next_elapsed_t2 - now))
+
+# Check that the timer will elapse in less than 20 minutes.
+((0 < time_delta_t1 && time_delta_t1 < 1200)) || {
+    echo 'Timer elapse outside of the expected 20 minute window.'
+    echo "  next_elapsed_t1=${next_elapsed_t1}"
+    echo "  now=${now}"
+    echo "  time_delta_t1=${time_delta_t1}"
+    echo ''
+} >>/failed_t1
 
 # Check that the timer will elapse in less than 20 minutes.
-((0 < time_delta && time_delta < 1200)) || {
+((0 < time_delta_t2 && time_delta_t2 < 1200)) || {
     echo 'Timer elapse outside of the expected 20 minute window.'
-    echo "  next_elapsed=${next_elapsed}"
+    echo "  next_elapsed_t2=${next_elapsed_t2}"
     echo "  now=${now}"
-    echo "  time_delta=${time_delta}"
+    echo "  time_delta_t2=${time_delta_t2}"
     echo ''
-} >>/failed
+} >>/failed_t2
 
-if test ! -s /failed ; then
-    rm -f /failed
+if test ! -s /failed_t1 && test ! -s /failed_t2; then
+    rm -f /failed_t*
     touch /testok
 fi
index c5347e351ec74326062324afbf96f92870a8c7ce..dc0c5f554f15cd454e55b712e5b8d2731aa363c4 100755 (executable)
@@ -29,10 +29,25 @@ elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
     # Verify that passing creds through kernel cmdline works
     [ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ]
 
+    # And that it also works via SMBIOS
+    [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ]
+    [ "$(systemd-creds --system cat binarysmbioscredential)" = "magicbinarydata" ]
+
     # If we aren't run in nspawn, we are run in qemu
     systemd-detect-virt -q -v
     expected_credential=myqemucredential
     expected_value=othervalue
+
+    # Verify that writing a sysctl via the kernel cmdline worked
+    [ "$(cat /proc/sys/kernel/domainname)" = "sysctltest" ]
+
+    # Verify that creating a user via sysusers via the kernel cmdline worked
+    grep -q ^credtestuser: /etc/passwd
+
+    # Verify that writing a file via tmpfiles worked
+    [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
+    [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
+    [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
 else
     echo "qemu_fw_cfg support missing in kernel. Sniff!"
     expected_credential=""
@@ -51,6 +66,12 @@ if [ "$expected_credential" != "" ] ; then
 
     # Combine it with a fallback (which should have no effect, given the cred should be passed down)
     [ "$(systemd-run -p LoadCredential="$expected_credential" -p SetCredential="$expected_credential":zzz --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
+
+    # This should succeed
+    systemd-run -p AssertCredential="$expected_credential" -p Type=oneshot true
+
+    # And this should fail
+    systemd-run -p AssertCredential="undefinedcredential" -p Type=oneshot true && { echo 'unexpected success'; exit 1; }
 fi
 
 # Verify that the creds are immutable
index 3c391c8efa6ddf488eab3ebc9ce1e7c34cc570e7..c221b19c36eaeba515c5904174fdd126e4931ed0 100755 (executable)
@@ -3,6 +3,155 @@
 set -eux
 set -o pipefail
 
+# shellcheck source=test/units/assert.sh
+. "$(dirname "$0")"/assert.sh
+
+teardown_test_dependencies() (
+    set +eux
+
+    if mountpoint /tmp/deptest; then
+        umount /tmp/deptest
+    fi
+
+    if [[ -n "${LOOP}" ]]; then
+        losetup -d "${LOOP}" || :
+    fi
+    if [[ -n "${LOOP_0}" ]]; then
+        losetup -d "${LOOP_0}" || :
+    fi
+    if [[ -n "${LOOP_1}" ]]; then
+        losetup -d "${LOOP_1}" || :
+    fi
+
+    rm -f /tmp/testsuite-60-dependencies-0.img
+    rm -f /tmp/testsuite-60-dependencies-1.img
+
+    rm -f /run/systemd/system/tmp-deptest.mount
+    systemctl daemon-reload
+
+    return 0
+)
+
+setup_loop() {
+    truncate -s 30m "/tmp/testsuite-60-dependencies-${1?}.img"
+    sfdisk --wipe=always "/tmp/testsuite-60-dependencies-${1?}.img" <<EOF
+label:gpt
+
+name="loop${1?}-part1"
+EOF
+    LOOP=$(losetup -P --show -f "/tmp/testsuite-60-dependencies-${1?}.img")
+    udevadm wait --settle --timeout=10 "${LOOP}p1"
+    udevadm lock --device="${LOOP}p1" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
+}
+
+check_dependencies() {
+    local escaped_0 escaped_1 after
+
+    escaped_0=$(systemd-escape -p "${LOOP_0}p1")
+    escaped_1=$(systemd-escape -p "${LOOP_1}p1")
+
+    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+        after=$(systemctl show --property=After --value tmp-deptest.mount)
+        assert_not_in "local-fs-pre.target" "$after"
+        assert_in "remote-fs-pre.target" "$after"
+        assert_in "network.target" "$after"
+    fi
+
+    # mount LOOP_0
+    mount -t ext4 "${LOOP_0}p1" /tmp/deptest
+    sleep 1
+    after=$(systemctl show --property=After --value tmp-deptest.mount)
+    assert_in "local-fs-pre.target" "$after"
+    assert_not_in "remote-fs-pre.target" "$after"
+    assert_not_in "network.target" "$after"
+    assert_in "${escaped_0}.device" "$after"
+    assert_in "blockdev@${escaped_0}.target" "$after"
+    assert_not_in "${escaped_1}.device" "$after"
+    assert_not_in "blockdev@${escaped_1}.target" "$after"
+    umount /tmp/deptest
+
+    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+        after=$(systemctl show --property=After --value tmp-deptest.mount)
+        assert_not_in "local-fs-pre.target" "$after"
+        assert_in "remote-fs-pre.target" "$after"
+        assert_in "network.target" "$after"
+    fi
+
+    # mount LOOP_1 (using fake _netdev option)
+    mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
+    sleep 1
+    after=$(systemctl show --property=After --value tmp-deptest.mount)
+    assert_not_in "local-fs-pre.target" "$after"
+    assert_in "remote-fs-pre.target" "$after"
+    assert_in "network.target" "$after"
+    assert_not_in "${escaped_0}.device" "$after"
+    assert_not_in "blockdev@${escaped_0}.target" "$after"
+    assert_in "${escaped_1}.device" "$after"
+    assert_in "blockdev@${escaped_1}.target" "$after"
+    umount /tmp/deptest
+
+    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+        after=$(systemctl show --property=After --value tmp-deptest.mount)
+        assert_not_in "local-fs-pre.target" "$after"
+        assert_in "remote-fs-pre.target" "$after"
+        assert_in "network.target" "$after"
+    fi
+
+    # mount tmpfs
+    mount -t tmpfs tmpfs /tmp/deptest
+    sleep 1
+    after=$(systemctl show --property=After --value tmp-deptest.mount)
+    assert_in "local-fs-pre.target" "$after"
+    assert_not_in "remote-fs-pre.target" "$after"
+    assert_not_in "network.target" "$after"
+    assert_not_in "${escaped_0}.device" "$after"
+    assert_not_in "blockdev@${escaped_0}.target" "$after"
+    assert_not_in "${escaped_1}.device" "$after"
+    assert_not_in "blockdev@${escaped_1}.target" "$after"
+    umount /tmp/deptest
+
+    if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+        after=$(systemctl show --property=After --value tmp-deptest.mount)
+        assert_not_in "local-fs-pre.target" "$after"
+        assert_in "remote-fs-pre.target" "$after"
+        assert_in "network.target" "$after"
+    fi
+}
+
+test_dependencies() {
+    if systemd-detect-virt --quiet --container; then
+        echo "Skipping test_dependencies in container"
+        return
+    fi
+
+    trap teardown_test_dependencies RETURN
+
+    setup_loop 0
+    LOOP_0="${LOOP}"
+    LOOP=
+    setup_loop 1
+    LOOP_1="${LOOP}"
+    LOOP=
+
+    mkdir -p /tmp/deptest
+
+    # without .mount file
+    check_dependencies
+
+    # create .mount file
+    mkdir -p /run/systemd/system
+    cat >/run/systemd/system/tmp-deptest.mount <<EOF
+[Mount]
+Where=/tmp/deptest
+What=192.168.0.1:/tmp/mnt
+Type=nfs
+EOF
+    systemctl daemon-reload
+
+    # with .mount file
+    check_dependencies
+}
+
 test_issue_20329() {
     local tmpdir unit
     tmpdir="$(mktemp -d)"
@@ -108,6 +257,9 @@ fi
 # Give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units.
 timeout 2m bash -c 'while systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; do systemctl daemon-reload; sleep 10; done'
 
+# test for issue #19983 and #23552.
+test_dependencies
+
 # test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
 test_issue_20329
 
index 3d0995b2f2ded7039db1ac574db50f8911e09ea3..05a012b2b128ab9942816cf3fb08901514616511 100755 (executable)
@@ -531,7 +531,7 @@ testcase_long_sysfs_path() {
     echo "UUID=deadbeef-dead-dead-beef-222222222222 $mpoint ext4 defaults 0 0" >>/etc/fstab
     systemctl daemon-reload
     mount "$mpoint"
-    systemctl status "$mpoint"
+    timeout 30 bash -c "while ! systemctl -q is-active '$mpoint'; do sleep .2; done"
     test -e "$mpoint/test"
     umount "$mpoint"
 
index ac7e7ba01633ed9f6760392a2d65bc853dd3f1f1..b813621055f4ce9c69d7594e706910c414b1a482 100755 (executable)
@@ -41,6 +41,7 @@ if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
 fi
 
 dd if=/dev/zero of="${image_dir}/image" bs=1048576 count=64 || exit 1
+dd if=/dev/zero of="${image_dir}/data" bs=1048576 count=64 || exit 1
 loop="$(losetup --show -f "${image_dir}/image")"
 
 if [[ ! -e ${loop} ]]; then
@@ -48,10 +49,18 @@ if [[ ! -e ${loop} ]]; then
     exit 1
 fi
 
+# Do one iteration with a separate data device, to test those branches
+separate_data=1
+
 for algorithm in crc32c crc32 sha1 sha256
 do
-    integritysetup format "${loop}" --batch-mode -I "${algorithm}" || exit 1
-    integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" || exit 1
+    if [ "${separate_data}" -eq 1 ]; then
+        data_option="--data-device=${image_dir}/data"
+    else
+        data_option=""
+    fi
+    integritysetup format "${loop}" --batch-mode -I "${algorithm}" "${data_option}" || exit 1
+    integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" "${data_option}" || exit 1
     mkfs.ext4 -U "${FS_UUID}" "${FULL_DM_DEV_NAME}" || exit 1
 
     # Give userspace time to handle udev events for new FS showing up ...
@@ -60,7 +69,12 @@ do
     integritysetup close "${DM_NAME}" || exit 1
 
     # create integritytab, generate units, start service
-    build_integrity_tab ${algorithm}
+    if [ "${separate_data}" -eq 1 ]; then
+        data_option=",data-device=${image_dir}/data"
+    else
+        data_option=""
+    fi
+    build_integrity_tab "${algorithm}${data_option}"
 
     # Cause the generator to re-run
     systemctl daemon-reload || exit 1
@@ -77,7 +91,13 @@ do
 
     # Check the signature on the FS to ensure we can retrieve it and that is matches
     if [ -e "${FULL_DM_DEV_NAME}" ]; then
-        if [ "${FULL_DM_DEV_NAME}" != "$(blkid -U "${FS_UUID}")" ]; then
+        # If a separate device is used for the metadata storage, then blkid will return one of the loop devices
+        if [ "${separate_data}" -eq 1 ]; then
+            dev_name="$(integritysetup status ${DM_NAME} | grep '^\s*device:' | awk '{print $2}')"
+        else
+            dev_name="${FULL_DM_DEV_NAME}"
+        fi
+        if [ "${dev_name}" != "$(blkid -U "${FS_UUID}")" ]; then
             echo "Failed to locate FS with matching UUID!"
             exit 1
         fi
@@ -93,6 +113,7 @@ do
         exit 1
     fi
 
+    separate_data=0
 done
 
 echo OK >/testok
index 09f78a0226b274543746a6771339063687db01f1..fdd24e2862556abf9e09264284060eea6511813e 100755 (executable)
@@ -11,6 +11,9 @@ dd if=/dev/zero of=$img bs=1024k count=20 status=none
 echo -n passphrase >/tmp/passphrase
 cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom $img /tmp/passphrase
 
+# Unlocking via keyfile
+systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto $img
+
 # Enroll unlock with default PCR policy
 env PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto $img
 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1
@@ -29,6 +32,17 @@ env PIN=123456 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm
 # Check failure with wrong PIN
 env PIN=123457 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; }
 
+# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto)
+if cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in'; then
+    env PIN=123456 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - headless=1
+    /usr/lib/systemd/systemd-cryptsetup detach test-volume
+
+    # Check failure with wrong PIN
+    env PIN=123457 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - headless=1 && { echo 'unexpected success'; exit 1; }
+else
+    echo 'cryptsetup has no LUKS2 token plugin support, skipping'
+fi
+
 # Check failure with wrong PCR (and correct PIN)
 tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
 env PIN=123456 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; }
@@ -43,6 +57,22 @@ env PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 $
 tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && exit 1
 
+if [[ -e /usr/lib/systemd/sytemd-measure ]]; then
+    echo HALLO > /tmp/tpmdata1
+    echo foobar > /tmp/tpmdata2
+
+    cat >/tmp/result <<EOF
+    11:sha1=5177e4ad69db92192c10e5f80402bf81bfec8a81
+    11:sha256=37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13
+    11:sha384=5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087
+    11:sha512=961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b
+EOF
+
+    /usr/lib/systemd/systemd-measure calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 | cmp - /tmp/result
+else
+    echo "/usr/lib/systemd/systemd-measure not found, skipping the test case"
+fi
+
 echo OK >/testok
 
 exit 0
diff --git a/test/units/testsuite-76.service b/test/units/testsuite-76.service
new file mode 100644 (file)
index 0000000..3c8a9e8
--- /dev/null
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-76-SYSCTL
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-76.sh b/test/units/testsuite-76.sh
new file mode 100755 (executable)
index 0000000..cb571f8
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/assert.sh
+. "$(dirname "$0")"/assert.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+
+echo "foo.bar=42" > /tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
+assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
+
+echo "-foo.foo=42" > /tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
+
+if ! systemd-detect-virt --quiet --container; then
+    ip link add hoge type dummy
+    udevadm wait /sys/class/net/hoge
+
+    cat >/tmp/foo.conf <<EOF
+net.ipv4.conf.*.drop_gratuitous_arp=1
+net.ipv4.*.*.bootp_relay=1
+net.ipv4.aaa.*.disable_policy=1
+EOF
+
+    echo 0 > /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp
+    echo 0 > /proc/sys/net/ipv4/conf/hoge/bootp_relay
+    echo 0 > /proc/sys/net/ipv4/conf/hoge/disable_policy
+
+    assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf
+    assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1"
+    assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1"
+    assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0"
+fi
+
+touch /testok
index 454616969443e022c4b1c86c9715beec76fee49a..ca1abbf3fecf5cc0eba940b690e33d18a1a17de6 100644 (file)
@@ -14,6 +14,7 @@ files = [['README',               ''],
          ['systemd-tmp.conf',     ''],
          ['tmp.conf',             ''],
          ['x11.conf',             ''],
+         ['provision.conf',       ''],
         ]
 
 foreach pair : files
diff --git a/tmpfiles.d/provision.conf b/tmpfiles.d/provision.conf
new file mode 100644 (file)
index 0000000..a4d7aaf
--- /dev/null
@@ -0,0 +1,17 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+# See tmpfiles.d(5) for details
+
+# Provision additional login messages from credentials, if they are set. Note
+# that these lines are NOPs if the credentials are not set or if the files
+# already exist.
+f^ /etc/motd.d/50-provision.conf - - - - login.motd
+f^ /etc/issue.d/50-provision.conf - - - - login.issue
+
+# Provision a /etc/hosts file from credentials.
+f^ /etc/hosts - - - - network.hosts
index f140b7817406b798b371f1b19774466dbeecf3e5..361376fd21e3ecb6a61b4bbe0f4b79c873b39976 100755 (executable)
 #!/usr/bin/env bash
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
-# The official unmodified version of the script can be found at
-# https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh
+set -eux
 
-set -e
+COVERITY_SCAN_TOOL_BASE="/tmp/coverity-scan-analysis"
+COVERITY_SCAN_PROJECT_NAME="systemd/systemd"
 
-# Declare build command
-COVERITY_SCAN_BUILD_COMMAND="ninja -C cov-build"
+function coverity_install_script {
+    local platform tool_url tool_archive
 
-# Environment check
-# Use default values if not set
-SCAN_URL=${SCAN_URL:="https://scan.coverity.com"}
-TOOL_BASE=${TOOL_BASE:="/tmp/coverity-scan-analysis"}
-UPLOAD_URL=${UPLOAD_URL:="https://scan.coverity.com/builds"}
+    platform=$(uname)
+    tool_url="https://scan.coverity.com/download/${platform}"
+    tool_archive="/tmp/cov-analysis-${platform}.tgz"
 
-# These must be set by environment
-echo -e "\033[33;1mNote: COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN are available on Project Settings page on scan.coverity.com\033[0m"
-[ -z "$COVERITY_SCAN_PROJECT_NAME" ] && echo "ERROR: COVERITY_SCAN_PROJECT_NAME must be set" && exit 1
-[ -z "$COVERITY_SCAN_NOTIFICATION_EMAIL" ] && echo "ERROR: COVERITY_SCAN_NOTIFICATION_EMAIL must be set" && exit 1
-[ -z "$COVERITY_SCAN_BRANCH_PATTERN" ] && echo "ERROR: COVERITY_SCAN_BRANCH_PATTERN must be set" && exit 1
-[ -z "$COVERITY_SCAN_BUILD_COMMAND" ] && echo "ERROR: COVERITY_SCAN_BUILD_COMMAND must be set" && exit 1
-[ -z "$COVERITY_SCAN_TOKEN" ] && echo "ERROR: COVERITY_SCAN_TOKEN must be set" && exit 1
+    set +x # this is supposed to hide COVERITY_SCAN_TOKEN
+    echo -e "\033[33;1mDownloading Coverity Scan Analysis Tool...\033[0m"
+    wget -nv -O "$tool_archive" "$tool_url" --post-data "project=$COVERITY_SCAN_PROJECT_NAME&token=${COVERITY_SCAN_TOKEN:?}"
+    set -x
 
-# Verify this branch should run
-if [[ "${CURRENT_REF^^}" =~ "${COVERITY_SCAN_BRANCH_PATTERN^^}" ]]; then
-    echo -e "\033[33;1mCoverity Scan configured to run on branch ${CURRENT_REF}\033[0m"
-else
-    echo -e "\033[33;1mCoverity Scan NOT configured to run on branch ${CURRENT_REF}\033[0m"
-    exit 1
-fi
-
-# Verify upload is permitted
-AUTH_RES=`curl -s --form project="$COVERITY_SCAN_PROJECT_NAME" --form token="$COVERITY_SCAN_TOKEN" $SCAN_URL/api/upload_permitted`
-if [ "$AUTH_RES" = "Access denied" ]; then
-    echo -e "\033[33;1mCoverity Scan API access denied. Check COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN.\033[0m"
-    exit 1
-else
-    AUTH=`echo $AUTH_RES | jq .upload_permitted`
-    if [ "$AUTH" = "true" ]; then
-        echo -e "\033[33;1mCoverity Scan analysis authorized per quota.\033[0m"
-    else
-        WHEN=`echo $AUTH_RES | jq .next_upload_permitted_at`
-        echo -e "\033[33;1mCoverity Scan analysis NOT authorized until $WHEN.\033[0m"
-        exit 1
-    fi
-fi
-
-TOOL_DIR=`find $TOOL_BASE -type d -name 'cov-analysis*'`
-export PATH="$TOOL_DIR/bin:$PATH"
-
-# Disable CCACHE for cov-build to compilation units correctly
-export CCACHE_DISABLE=1
-
-# FUNCTION DEFINITIONS
-# --------------------
-_help()
-{
-    # displays help and exits
-    cat <<-EOF
-               USAGE: $0 [CMD] [OPTIONS]
-
-               CMD
-                 build   Issue Coverity build
-                 upload  Upload coverity archive for analysis
-              Note: By default, archive is created from default results directory.
-                    To provide custom archive or results directory, see --result-dir
-                    and --tar options below.
-
-               OPTIONS
-                 -h,--help     Display this menu and exits
-
-                 Applicable to build command
-                 ---------------------------
-                 -o,--out-dir  Specify Coverity intermediate directory (defaults to 'cov-int')
-                 -t,--tar      bool, archive the output to .tgz file (defaults to false)
-
-                 Applicable to upload command
-                 ----------------------------
-                 -d, --result-dir   Specify result directory if different from default ('cov-int')
-                 -t, --tar ARCHIVE  Use custom .tgz archive instead of intermediate directory or pre-archived .tgz
-                         (by default 'analysis-result.tgz'
-       EOF
-    return;
-}
-
-_pack()
-{
-    RESULTS_ARCHIVE=${RESULTS_ARCHIVE:-'analysis-results.tgz'}
-
-    echo -e "\033[33;1mTarring Coverity Scan Analysis results...\033[0m"
-    tar czf $RESULTS_ARCHIVE $RESULTS_DIR
-    SHA=`git rev-parse --short HEAD`
-
-    PACKED=true
+    mkdir -p "$COVERITY_SCAN_TOOL_BASE"
+    pushd "$COVERITY_SCAN_TOOL_BASE"
+    tar xzf "$tool_archive"
+    popd
 }
 
+function run_coverity {
+    local results_dir tool_dir results_archive sha response status_code
 
-_build()
-{
-    echo -e "\033[33;1mRunning Coverity Scan Analysis Tool...\033[0m"
-    local _cov_build_options=""
-    #local _cov_build_options="--return-emit-failures 8 --parse-error-threshold 85"
-    eval "${COVERITY_SCAN_BUILD_COMMAND_PREPEND}"
-    COVERITY_UNSUPPORTED=1 cov-build --dir $RESULTS_DIR $_cov_build_options sh -c "$COVERITY_SCAN_BUILD_COMMAND"
-    cov-import-scm --dir $RESULTS_DIR --scm git --log $RESULTS_DIR/scm_log.txt
+    results_dir="cov-int"
+    tool_dir=$(find "$COVERITY_SCAN_TOOL_BASE" -type d -name 'cov-analysis*')
+    results_archive="analysis-results.tgz"
+    sha=$(git rev-parse --short HEAD)
 
-    if [ $? != 0 ]; then
-       echo -e "\033[33;1mCoverity Scan Build failed: $TEXT.\033[0m"
-       return 1
-    fi
-
-    [ -z $TAR ] || [ $TAR = false ] && return 0
+    meson -Dman=false build
+    COVERITY_UNSUPPORTED=1 "$tool_dir/bin/cov-build" --dir "$results_dir" sh -c "ninja -C ./build -v"
+    "$tool_dir/bin/cov-import-scm" --dir "$results_dir" --scm git --log "$results_dir/scm_log.txt"
 
-    if [ "$TAR" = true ]; then
-       _pack
-    fi
-}
+    tar czf "$results_archive" "$results_dir"
 
-
-_upload()
-{
-    # pack results
-    [ -z $PACKED ] || [ $PACKED = false ] && _pack
-
-    # Upload results
+    set +x # this is supposed to hide COVERITY_SCAN_TOKEN
     echo -e "\033[33;1mUploading Coverity Scan Analysis results...\033[0m"
     response=$(curl \
-                  --silent --write-out "\n%{http_code}\n" \
-                  --form project=$COVERITY_SCAN_PROJECT_NAME \
-                  --form token=$COVERITY_SCAN_TOKEN \
-                  --form email=$COVERITY_SCAN_NOTIFICATION_EMAIL \
-                  --form file=@$RESULTS_ARCHIVE \
-                  --form version=$SHA \
-                  --form description="Travis CI build" \
-                  $UPLOAD_URL)
+               --silent --write-out "\n%{http_code}\n" \
+               --form project="$COVERITY_SCAN_PROJECT_NAME" \
+               --form token="${COVERITY_SCAN_TOKEN:?}" \
+               --form email="${COVERITY_SCAN_NOTIFICATION_EMAIL:?}" \
+               --form file="@$results_archive" \
+               --form version="$sha" \
+               --form description="Daily build" \
+               https://scan.coverity.com/builds)
     printf "\033[33;1mThe response is\033[0m\n%s\n" "$response"
     status_code=$(echo "$response" | sed -n '$p')
-    # Coverity Scan used to respond with 201 on successfully receiving analysis results.
-    # Now for some reason it sends 200 and may change back in the foreseeable future.
-    # See https://github.com/pmem/pmdk/commit/7b103fd2dd54b2e5974f71fb65c81ab3713c12c5
     if [ "$status_code" != "200" ]; then
-       TEXT=$(echo "$response" | sed '$d')
-       echo -e "\033[33;1mCoverity Scan upload failed: $TEXT.\033[0m"
-       exit 1
+        echo -e "\033[33;1mCoverity Scan upload failed: $(echo "$response" | sed '$d').\033[0m"
+        return 1
     fi
-
-    echo -e "\n\033[33;1mCoverity Scan Analysis completed successfully.\033[0m"
-    exit 0
+    set -x
 }
 
-# PARSE COMMAND LINE OPTIONS
-# --------------------------
-
-case $1 in
-    -h|--help)
-       _help
-       exit 0
-       ;;
-    build)
-       CMD='build'
-       TEMP=`getopt -o ho:t --long help,out-dir:,tar -n '$0' -- "$@"`
-       _ec=$?
-       [[ $_ec -gt 0 ]] && _help && exit $_ec
-       shift
-       ;;
-    upload)
-       CMD='upload'
-       TEMP=`getopt -o hd:t: --long help,result-dir:tar: -n '$0' -- "$@"`
-       _ec=$?
-       [[ $_ec -gt 0 ]] && _help && exit $_ec
-       shift
-       ;;
-    *)
-       _help && exit 1 ;;
-esac
-
-RESULTS_DIR='cov-int'
-
-eval set -- "$TEMP"
-if [ $? != 0 ] ; then exit 1 ; fi
-
-# extract options and their arguments into variables.
-if [[ $CMD == 'build' ]]; then
-    TAR=false
-    while true ; do
-       case $1 in
-           -h|--help)
-               _help
-               exit 0
-               ;;
-           -o|--out-dir)
-               RESULTS_DIR="$2"
-               shift 2
-               ;;
-           -t|--tar)
-               TAR=true
-               shift
-               ;;
-           --) _build; shift ; break ;;
-           *) echo "Internal error" ; _help && exit 6 ;;
-       esac
-    done
-
-elif [[ $CMD == 'upload' ]]; then
-    while true ; do
-       case $1 in
-           -h|--help)
-               _help
-               exit 0
-               ;;
-           -d|--result-dir)
-               CHANGE_DEFAULT_DIR=true
-               RESULTS_DIR="$2"
-               shift 2
-               ;;
-           -t|--tar)
-               RESULTS_ARCHIVE="$2"
-               [ -z $CHANGE_DEFAULT_DIR ] || [ $CHANGE_DEFAULT_DIR = false ] && PACKED=true
-               shift 2
-               ;;
-           --) _upload; shift ; break ;;
-           *) echo "Internal error" ; _help && exit 6 ;;
-       esac
-    done
-
-fi
+coverity_install_script
+run_coverity
diff --git a/tools/get-coverity.sh b/tools/get-coverity.sh
deleted file mode 100755 (executable)
index b067ed2..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-# Download and extract coverity tool
-
-set -e
-set -o pipefail
-
-# Environment check
-if [ -z "$COVERITY_SCAN_TOKEN" ]; then
-    echo >&2 'ERROR: COVERITY_SCAN_TOKEN must be set'
-    exit 1
-fi
-
-# Use default values if not set
-PLATFORM="$(uname)"
-TOOL_BASE="${TOOL_BASE:-/tmp/coverity-scan-analysis}"
-TOOL_ARCHIVE="${TOOL_ARCHIVE:-/tmp/cov-analysis-${PLATFORM}.tgz}"
-TOOL_URL="https://scan.coverity.com/download/${PLATFORM}"
-
-# Make sure wget is installed
-sudo apt-get update && sudo apt-get -y install wget
-
-# Get coverity tool
-if [ ! -d "$TOOL_BASE" ]; then
-    # Download Coverity Scan Analysis Tool
-    if [ ! -e "$TOOL_ARCHIVE" ]; then
-        echo -e "\033[33;1mDownloading Coverity Scan Analysis Tool...\033[0m"
-        wget -nv -O "$TOOL_ARCHIVE" "$TOOL_URL" --post-data "project=$COVERITY_SCAN_PROJECT_NAME&token=$COVERITY_SCAN_TOKEN"
-    fi
-
-    # Extract Coverity Scan Analysis Tool
-    echo -e "\033[33;1mExtracting Coverity Scan Analysis Tool...\033[0m"
-    mkdir -p "$TOOL_BASE"
-    pushd "$TOOL_BASE"
-    tar xzf "$TOOL_ARCHIVE"
-    popd
-fi
-
-echo -e "\033[33;1mCoverity Scan Analysis Tool can be found at $TOOL_BASE ...\033[0m"
index 73871d6f50a8115c5653e49aab7eeb1d877e0f6a..606b7dbe16b1c3d9d286db7b514ff49d5c56c851 100644 (file)
@@ -20,9 +20,8 @@ Before=getty.target
 ConditionPathExists=/dev/console
 
 [Service]
-# The '-o' option value tells agetty to replace 'login' arguments with an
-# option to preserve environment (-p), followed by '--' for safety, and then
-# the entered username.
+# The '-o' option value tells agetty to replace 'login' arguments with an option to preserve environment (-p),
+# followed by '--' for safety, and then the entered username.
 ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear --keep-baud - 115200,38400,9600 $TERM
 Type=idle
 Restart=always
index a6e3f94e2a5c21a39409f07f989201586ed0545e..8d7e20d5ecf9c646765654db0394726396f36a50 100644 (file)
@@ -25,10 +25,9 @@ Conflicts=rescue.service
 Before=rescue.service
 
 [Service]
-# The '-o' option value tells agetty to replace 'login' arguments with an
-# option to preserve environment (-p), followed by '--' for safety, and then
-# the entered username.
-ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear --keep-baud - 115200,38400,9600 $TERM
+# The '-o' option value tells agetty to replace 'login' arguments with an option to preserve environment (-p),
+# followed by '--' for safety, and then the entered username.
+ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear - $TERM
 Type=idle
 Restart=always
 RestartSec=0
index 95dd2665b28132713635e713897e8eb8ae62ba36..d15129e7f0ace9d4f15b791846fd1c150a9dcb6f 100644 (file)
@@ -25,6 +25,7 @@ CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_N
 DeviceAllow=char-* rw
 ExecStart=!!{{ROOTLIBEXECDIR}}/systemd-networkd
 ExecReload=networkctl reload
+FileDescriptorStoreMax=512
 LockPersonality=yes
 MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
index 44b885352142844ca84c39f84edf11b7d83b62dc..77793f38948fdfe015da08a73b7eb70227ec82c3 100644 (file)
@@ -21,3 +21,4 @@ Type=oneshot
 RemainAfterExit=yes
 ExecStart={{ROOTLIBEXECDIR}}/systemd-sysctl
 TimeoutSec=90s
+LoadCredential=sysctl.extra
index 47373307b32a527bf9cb0caf9232afe11c3a4938..91132dafa98d8d24f64f55f7215dbc086027fe85 100644 (file)
@@ -14,7 +14,8 @@ DefaultDependencies=no
 Conflicts=shutdown.target
 After=systemd-remount-fs.service
 Before=sysinit.target shutdown.target systemd-update-done.service
-ConditionNeedsUpdate=/etc
+ConditionNeedsUpdate=|/etc
+ConditionCredential=|sysusers.extra
 
 [Service]
 Type=oneshot
@@ -28,3 +29,6 @@ TimeoutSec=90s
 LoadCredential=passwd.hashed-password.root
 LoadCredential=passwd.plaintext-password.root
 LoadCredential=passwd.shell.root
+
+# Also, allow configuring extra sysusers lines via a credential
+LoadCredential=sysusers.extra
index 7aee6463bd54c5cac86379c5640fbf78826a800e..6ae4e74ddd07db22867678dc336cb3290ccaa0d0 100644 (file)
@@ -20,3 +20,4 @@ Type=oneshot
 ExecStart=systemd-tmpfiles --clean
 SuccessExitStatus=DATAERR
 IOSchedulingClass=idle
+LoadCredential=tmpfiles.extra
index 0babe78767a420b78fe77054099dd448fb0fe68b..ad0e54fcc418e7370db0456d0ebf9f50e150a2ee 100644 (file)
@@ -20,3 +20,4 @@ Type=oneshot
 RemainAfterExit=yes
 ExecStart=systemd-tmpfiles --prefix=/dev --create --boot
 SuccessExitStatus=DATAERR CANTCREAT
+LoadCredential=tmpfiles.extra
index bc29dbc8c9c1793a514f0d61faa81ddcf51389e8..fc6a4a0e0be2b03f02791600f439d7a5314a4270 100644 (file)
@@ -21,3 +21,7 @@ Type=oneshot
 RemainAfterExit=yes
 ExecStart=systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
 SuccessExitStatus=DATAERR CANTCREAT
+LoadCredential=tmpfiles.extra
+LoadCredential=login.motd
+LoadCredential=login.issue
+LoadCredential=network.hosts
index 6f8ff43b79959f2ce9324f9c64ac8ab2eb90456f..b777009086166c58b685329d98d2ee9906029dde 100644 (file)
@@ -7,6 +7,6 @@
 #  the Free Software Foundation; either version 2.1 of the License, or
 #  (at your option) any later version.
 
-# Empty file to mask its counterpart for unpriviledged users and thus cancels
+# Empty file to mask its counterpart for unprivileged users and thus cancels
 # "After=systemd-user-session.service" ordering constraint so that root can log
 # in even if the boot process is not yet finished.