]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #30513 from rpigott/resolved-ede
authorLennart Poettering <lennart@poettering.net>
Thu, 4 Jan 2024 08:59:19 +0000 (09:59 +0100)
committerGitHub <noreply@github.com>
Thu, 4 Jan 2024 08:59:19 +0000 (09:59 +0100)
resolved: support RFC 8914 EDE error codes

216 files changed:
.github/labeler.yml
.github/workflows/cifuzz.yml
.github/workflows/codeql.yml
.github/workflows/gather-pr-metadata.yml
.github/workflows/labeler.yml
.github/workflows/requirements.txt
TODO
coccinelle/parsing_hacks.h
coccinelle/run-coccinelle.sh
coccinelle/siphash24.cocci
docs/ENVIRONMENT.md
man/logind.conf.xml
man/org.freedesktop.systemd1.xml
man/rules/meson.build
man/systemd-backlight@.service.xml
man/systemd-dissect.xml
man/systemd-nspawn.xml
man/systemd-stub.xml
man/systemd-tpm2-generator.xml [new file with mode: 0644]
man/systemd-vpick.xml [new file with mode: 0644]
man/systemd.exec.xml
man/systemd.network.xml
man/systemd.resource-control.xml
man/systemd.special.xml
man/systemd.v.xml [new file with mode: 0644]
meson.build
po/pa.po
rules.d/99-systemd.rules.in
src/analyze/analyze-image-policy.c
src/backlight/backlight.c
src/basic/fd-util.c
src/basic/fd-util.h
src/basic/sigbus.c
src/basic/special.h
src/basic/stat-util.c
src/basic/stat-util.h
src/basic/string-util.c
src/basic/string-util.h
src/basic/strv.c
src/basic/strv.h
src/boot/bootctl-status.c
src/boot/efi/cpio.c
src/boot/efi/cpio.h
src/boot/efi/stub.c
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-manager.c
src/core/dbus-unit.c
src/core/exec-invoke.c
src/dissect/dissect.c
src/fundamental/efivars-fundamental.h
src/fundamental/sha256.c
src/hibernate-resume/hibernate-resume-config.c
src/hibernate-resume/hibernate-resume-config.h
src/libsystemd-network/dhcp-client-id-internal.h [new file with mode: 0644]
src/libsystemd-network/dhcp-duid-internal.h [moved from src/libsystemd-network/dhcp-identifier.h with 60% similarity]
src/libsystemd-network/dhcp-identifier.c [deleted file]
src/libsystemd-network/dhcp-lease-internal.h
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/dhcp-server-lease-internal.h [new file with mode: 0644]
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/fuzz-dhcp-server.c
src/libsystemd-network/meson.build
src/libsystemd-network/sd-dhcp-client-id.c [new file with mode: 0644]
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-duid.c [new file with mode: 0644]
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp-server-lease.c [new file with mode: 0644]
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd-network/test-dhcp-client.c
src/libsystemd-network/test-dhcp-server.c
src/libsystemd-network/test-dhcp6-client.c
src/libsystemd/sd-device/device-enumerator.c
src/libsystemd/sd-device/device-monitor.c
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-device-util.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/journal-internal.h
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-netlink/netlink-message-rtnl.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-util.h
src/libsystemd/sd-network/sd-network.c
src/login/logind-action.c
src/login/logind-core.c
src/login/logind-dbus.c
src/login/logind-session-device.c
src/login/logind-session-device.h
src/login/logind-session.c
src/mount/mount-tool.c
src/network/netdev/macsec.c
src/network/netdev/tuntap.c
src/network/networkctl.c
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-bridge-vlan.c
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-dhcp-prefix-delegation.c
src/network/networkd-dhcp-server-bus.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-ipv4acd.c
src/network/networkd-ipv4ll.c
src/network/networkd-json.c
src/network/networkd-link.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-network-gperf.gperf
src/network/networkd-queue.c
src/network/networkd-route.c
src/network/networkd-route.h
src/network/networkd-wiphy.c
src/nspawn/nspawn.c
src/resolve/dns-type.h
src/resolve/resolved-dns-cache.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-llmnr.c
src/resolve/resolved-mdns.c
src/shared/async.c
src/shared/blockdev-util.c
src/shared/bus-print-properties.c
src/shared/bus-wait-for-jobs.c
src/shared/bus-wait-for-jobs.h
src/shared/bus-wait-for-units.c
src/shared/bus-wait-for-units.h
src/shared/discover-image.c
src/shared/edit-util.c
src/shared/edit-util.h
src/shared/efi-loader.c
src/shared/fdset.c
src/shared/image-policy.c
src/shared/image-policy.h
src/shared/meson.build
src/shared/tpm2-util.c
src/shared/utmp-wtmp.h
src/shared/vpick.c [new file with mode: 0644]
src/shared/vpick.h [new file with mode: 0644]
src/storagetm/storagetm.c
src/sysext/sysext.c
src/systemd/meson.build
src/systemd/sd-dhcp-client-id.h [new file with mode: 0644]
src/systemd/sd-dhcp-client.h
src/systemd/sd-dhcp-duid.h [new file with mode: 0644]
src/systemd/sd-dhcp-lease.h
src/systemd/sd-dhcp-server-lease.h [new file with mode: 0644]
src/systemd/sd-dhcp6-client.h
src/systemd/sd-netlink.h
src/test/meson.build
src/test/test-aux-scope.c [new file with mode: 0644]
src/test/test-image-policy.c
src/test/test-stat-util.c
src/test/test-string-util.c
src/test/test-strv.c
src/test/test-vpick.c [new file with mode: 0644]
src/tpm2-setup/meson.build
src/tpm2-setup/tpm2-generator.c [new file with mode: 0644]
src/udev/dmi_memory_id/dmi_memory_id.c
src/udev/test-udev-rule-runner.c
src/udev/test-udev-spawn.c
src/udev/udev-builtin-hwdb.c
src/udev/udev-builtin-net_id.c
src/udev/udev-builtin-path_id.c
src/udev/udev-builtin-usb_id.c
src/udev/udev-event.c
src/udev/udev-event.h
src/udev/udev-format.c
src/udev/udev-format.h
src/udev/udev-manager.c
src/udev/udev-node.c
src/udev/udev-rules.c
src/udev/udev-rules.h
src/udev/udev-spawn.c
src/udev/udev-spawn.h
src/udev/udev-watch.c
src/udev/udev-worker.c
src/udev/udevadm-test-builtin.c
src/udev/udevadm-test.c
src/udev/udevadm.c
src/vpick/meson.build [new file with mode: 0644]
src/vpick/vpick-tool.c [new file with mode: 0644]
test/TEST-74-AUX-UTILS/test.sh
test/test-exec-deserialization.py [deleted file]
test/test-functions
test/units/testsuite-05.effective-limit.sh [new file with mode: 0755]
test/units/testsuite-05.rlimit.sh [new file with mode: 0755]
test/units/testsuite-05.service
test/units/testsuite-05.sh
test/units/testsuite-07.aux-scope.sh [new file with mode: 0755]
test/units/testsuite-07.exec-deserialization.sh [new file with mode: 0755]
test/units/testsuite-17.03.sh
test/units/testsuite-17.10.sh
test/units/testsuite-21.sh
test/units/testsuite-50.sh
test/units/testsuite-64.sh
test/units/testsuite-74.vpick.sh [new file with mode: 0755]
test/units/util.sh
units/meson.build
units/systemd-pcrextend.socket
units/systemd-pcrextend@.service.in
units/systemd-pcrfs-root.service.in
units/systemd-pcrfs@.service.in
units/systemd-pcrmachine.service.in
units/systemd-pcrphase-initrd.service.in
units/systemd-pcrphase-sysinit.service.in
units/systemd-pcrphase.service.in
units/systemd-resolved.service.in
units/systemd-tpm2-setup-early.service.in
units/systemd-tpm2-setup.service.in
units/tpm2.target [new file with mode: 0644]

index fd5fc9113b4824ec2974212225b6773885bbaff0..45b1ff49eebab0cf8e3a8833897b2d7642e00ae7 100644 (file)
 # SPDX-License-Identifier: LGPL-2.1-or-later
+# vi: sw=2 ts=2 et:
 
 apparmor:
-  - '**/*apparmor*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*apparmor*'
 binfmt:
-  - '**/*binfmt*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*binfmt*'
 btrfs:
-  - '**/*btrfs*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*btrfs*'
 build-system:
-  - meson_options.txt
-  - '**/meson.build'
-  - Makefile
-  - configure
+  - changed-files:
+    - any-glob-to-any-file: ['meson_options.txt', '**/meson.build', 'Makefile', 'configure']
 busctl:
-  - '**/*busctl*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*busctl*'
 cgls:
-  - '**/*cgls*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*cgls*'
 cgtop:
-  - '**/*cgtop*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*cgtop*'
 ci:
-  - '.github/*'
+  - changed-files:
+    - any-glob-to-any-file: '.github/*'
 coccinelle:
-  - coccinelle/*
+  - changed-files:
+    - any-glob-to-any-file: 'coccinelle/*'
 coredump:
-  - '**/*coredump*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*coredump*'
 documentation:
-  - NEWS
-  - README*
-  - docs/*
-  - man/*
+  - changed-files:
+    - any-glob-to-any-file: ['NEWS', 'README*', 'docs/*', 'man/*']
 env-generator:
-  - '**/*environment*generator*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*environment*generator*'
 debug-generator:
-  - '**/debug-generator*'
+  - changed-files:
+    - any-glob-to-any-file: '**/debug-generator*'
 fstab-generator:
-  - '**/*fstab-generator*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*fstab-generator*'
 growfs:
-  - '**/*growfs*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*growfs*'
 hwdb:
-  - hwdb.d/**/*
+  - changed-files:
+    - any-glob-to-any-file: 'hwdb.d/**/*'
 journal:
-  - src/journal/*
-  - src/libsystemd/sd-journal/*
+  - changed-files:
+    - any-glob-to-any-file: ['src/journal/*', 'src/libsystemd/sd-journal/*']
 journal-remote:
-  - src/journal-remote/*
+  - changed-files:
+    - any-glob-to-any-file: 'src/journal-remote/*'
+login:
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-login*/**'
+logind:
+  - changed-files:
+    - any-glob-to-any-file: 'src/login/*'
 meson:
-  - meson_options.txt
-  - '**/meson.build'
+  - changed-files:
+    - any-glob-to-any-file: ['meson_options.txt', '**/meson.build']
 mkosi:
-  - .mkosi/*
-  - mkosi.build
+  - changed-files:
+    - any-glob-to-any-file: ['.mkosi/*', 'mkosi.build']
 network:
-  - src/libsystemd-network/**/*
-  - src/network/**/*
+  - changed-files:
+    - any-glob-to-any-file: ['src/libsystemd-network/**/*', 'src/network/**/*']
 portable:
-  - src/portable/**/*
+  - changed-files:
+    - any-glob-to-any-file: 'src/portable/**/*'
 rc-local-generator:
-  - src/rc-local-generator/*
+  - changed-files:
+    - any-glob-to-any-file: 'src/rc-local-generator/*'
 repart:
-  - '**/*repart*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*repart*'
 resolve:
-  - '**/*resolve*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*resolve*'
 rfkill:
-  - '**/*rfkill*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*rfkill*'
 rpm:
-  - src/rpm/*
+  - changed-files:
+    - any-glob-to-any-file: 'src/rpm/*'
 run:
-  - src/run/*
-  - man/systemd-run*
+  - changed-files:
+    - any-glob-to-any-file: ['src/run/*', 'man/systemd-run*']
 sd-boot/sd-stub/bootctl:
-  - src/boot/**/*
-  - man/bootctl*
-  - man/systemd-boot.xml
+  - changed-files:
+    - any-glob-to-any-file: ['src/boot/**/*', 'man/bootctl*', 'man/systemd-boot.xml']
 sd-bus:
-  - '**/sd-bus*/**'
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-bus*/**'
 sd-daemon:
-  - '**/sd-daemon*/**'
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-daemon*/**'
 sd-device:
-  - '**/sd-device*/**'
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-device*/**'
 sd-event:
-  - '**/sd-event*/**'
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-event*/**'
 sd-hwdb:
-  - '**/sd-hwdb*/**'
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-hwdb*/**'
 sd-id128:
-  - '**/sd-id128*/**'
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-id128*/**'
 sd-netlink:
-  - '**/sd-netlink*/**'
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-netlink*/**'
 sd-path:
-  - '**/sd-path*/**'
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-path*/**'
 sd-resolve:
-  - '**/sd-resolve*/**'
+  - changed-files:
+    - any-glob-to-any-file: '**/sd-resolve*/**'
 selinux:
-  - '**/*selinux*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*selinux*'
 sleep:
-  - '**/*sleep*'
+  - changed-files:
+    - any-glob-to-any-file: ['src/shared/*sleep*', 'src/sleep/*']
 smack:
-  - '**/*smack*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*smack*'
 socket-proxy:
-  - '**/*socket-proxy*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*socket-proxy*'
 sysv-generator:
-  - '**/*sysv-generator*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*sysv-generator*'
 systemctl:
-  - '**/systemctl*'
+  - changed-files:
+    - any-glob-to-any-file: '**/systemctl*'
 sysupdate:
-  - '**/*sysupdate*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*sysupdate*'
 sysvcompat:
-  - '**/*sysv*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*sysv*'
 sysctl:
-  - '**/*sysctl*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*sysctl*'
 timedate:
-  - '**/*timedate*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*timedate*'
 timesync:
-  - '**/*timesync*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*timesync*'
 tests:
-  - src/test/**/*
-  - src/fuzz/**/*
-  - test/**/*
-  - '**/test-*'
-  - .github/workflows/*
+  - changed-files:
+    - any-glob-to-any-file: [
+      'src/test/**/*',
+      'src/fuzz/**/*',
+      'test/**/*',
+      '**/test-*',
+      '.github/workflows/*'
+  ]
 tpm2:
-  - '**/*tpm2*'
-  - '**/*tpm-*'
+  - changed-files:
+    - any-glob-to-any-file: ['**/*tpm2*', '**/*tpm-*']
 udev:
-  - src/udev/**/*
-  - src/libudev/*
-  - man/*udev*
+  - changed-files:
+    - any-glob-to-any-file: ['src/udev/**/*', 'src/libudev/*', 'man/*udev*']
 uki:
-  - '**/ukify*'
+  - changed-files:
+    - any-glob-to-any-file: '**/ukify*'
 units:
-  - units/**/*
+  - changed-files:
+    - any-glob-to-any-file: 'units/**/*'
 userdb:
-  - '**/*userdb*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*userdb*'
 util-lib:
-  - src/fundamental/**/*
-  - src/basic/**/*
-  - src/shared/**/*
+  - changed-files:
+    - any-glob-to-any-file: ['src/fundamental/**/*', 'src/basic/**/*', 'src/shared/**/*']
 vconsole:
-  - '**/*vconsole*'
+  - changed-files:
+    - any-glob-to-any-file: '**/*vconsole*'
 xdg-autostart:
-  - '**/**xdg-autostart-generator*'
+  - changed-files:
+    - any-glob-to-any-file: '**/**xdg-autostart-generator*'
index 66714c225ad8a8710952b1aa7ba49d0eea501708..3584cff12b6ef07aa2d01b7b113dd179f242afdb 100644 (file)
@@ -60,14 +60,14 @@ jobs:
           sanitizer: ${{ matrix.sanitizer }}
           output-sarif: true
       - name: Upload Crash
-        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
+        uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392
         if: failure() && steps.build.outcome == 'success'
         with:
           name: ${{ matrix.sanitizer }}-${{ matrix.architecture }}-artifacts
           path: ./out/artifacts
       - name: Upload Sarif
         if: always() && steps.build.outcome == 'success'
-        uses: github/codeql-action/upload-sarif@407ffafae6a767df3e0230c3df91b6443ae8df75
+        uses: github/codeql-action/upload-sarif@012739e5082ff0c22ca6d6ab32e07c36df03c4a4
         with:
           # Path to SARIF file relative to the root of the repository
           sarif_file: cifuzz-sarif/results.sarif
index 2c02ef5cb9ab8884623e4d37ed97ae38b344b3e4..92e5308e036f32b76addae852ac51d5aafa05369 100644 (file)
@@ -45,7 +45,7 @@ jobs:
       uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
 
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75
+      uses: github/codeql-action/init@012739e5082ff0c22ca6d6ab32e07c36df03c4a4
       with:
         languages: ${{ matrix.language }}
         config-file: ./.github/codeql-config.yml
@@ -53,7 +53,7 @@ jobs:
     - run: sudo -E .github/workflows/unit_tests.sh SETUP
 
     - name: Autobuild
-      uses: github/codeql-action/autobuild@407ffafae6a767df3e0230c3df91b6443ae8df75
+      uses: github/codeql-action/autobuild@012739e5082ff0c22ca6d6ab32e07c36df03c4a4
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75
+      uses: github/codeql-action/analyze@012739e5082ff0c22ca6d6ab32e07c36df03c4a4
index 5b3c360d70404ae48335b3f0fdb279e5c94de60c..e0ed749c2d2ccb1b73ff8ea18d923f64c7e97358 100644 (file)
@@ -30,7 +30,7 @@ jobs:
           echo ${{ github.event.number }} >./${{ env.PULL_REQUEST_METADATA_DIR }}/${{ env.PULL_REQUEST_METADATA_FILE }}
 
       - name: Upload Pull Request Metadata artifact
-        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
+        uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392
         with:
           name: ${{ env.PULL_REQUEST_METADATA_FILE }}
           path: ${{ env.PULL_REQUEST_METADATA_DIR }}
index 7f66a53f95bd63d671bee238538780546b29ea2e..559f589dbfaf8884ba62d5ce9a4ffa2a955cfad8 100644 (file)
@@ -7,6 +7,14 @@ name: "Pull Request Labeler"
 on:
   pull_request_target:
     types: [opened, synchronize, reopened, ready_for_review, closed]
+    paths-ignore:
+      - '.github/labeler.yml'
+      - '.github/workflows/labeler.yml'
+  # Allow testing changes made to the labeler configuration
+  pull_request:
+    paths:
+      - '.github/labeler.yml'
+      - '.github/workflows/labeler.yml'
   issue_comment:
     types: [created]
 
@@ -21,17 +29,21 @@ jobs:
       pull-requests: write
 
     steps:
+    - name: Repository checkout
+      uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
+      if: github.event_name == 'pull_request'
+
     - name: Label PR based on policy in labeler.yml
-      uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594
-      if: github.event_name == 'pull_request_target' && github.event.action != 'closed'
+      uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9
+      if: startsWith(github.event_name, 'pull_request') && github.event.action != 'closed'
       with:
         repo-token: "${{ secrets.GITHUB_TOKEN }}"
         configuration-path: .github/labeler.yml
-        sync-labels: "" # This is a workaround for issue 18671
+        sync-labels: false
 
     - name: Set or remove labels based on systemd development workflow
       uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
-      if: github.event_name == 'pull_request_target' && github.event.action != 'closed' && !github.event.pull_request.draft
+      if: startsWith(github.event_name, 'pull_request') && github.event.action != 'closed' && !github.event.pull_request.draft
       with:
         script: |
           response = await github.rest.issues.listLabelsOnIssue({
@@ -88,7 +100,7 @@ jobs:
 
     - name: Remove specific labels when PR is closed or merged
       uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
-      if: github.event_name == 'pull_request_target' && github.event.action == 'closed'
+      if: startsWith(github.event_name, 'pull_request') && github.event.action == 'closed'
       with:
         script: |
           for (const label of ["please-review",
index b42b98e98aa6323fd162603933ee8044b04037d5..0c41a98fef9b4f84d69aafeadcfe1066c66b01d6 100644 (file)
@@ -1,6 +1,6 @@
-meson==1.3.0 \
-    --hash=sha256:4ba253ef60e454e23234696119cbafa082a0aead0bd3bbf6991295054795f5dc \
-    --hash=sha256:e9f54046ce5b9a1f3024f7a7d52f19f085fd57c9d26a5db0cfcf0750572a8fd8
+meson==1.3.1 \
+    --hash=sha256:6020568bdede1643d4fb41e28215be38eff5d52da28ac7d125457c59e0032ad7 \
+    --hash=sha256:d5223ecca9564d735d36daaba2571abc6c032c8c3a7ffa0674e803ef0c7e0219
 ninja==1.11.1.1 \
     --hash=sha256:18302d96a5467ea98b68e1cae1ae4b4fb2b2a56a82b955193c637557c7273dbd \
     --hash=sha256:185e0641bde601e53841525c4196278e9aaf4463758da6dd1e752c0a0f54136a \
diff --git a/TODO b/TODO
index 06e6500757cbabc5ac6dde8157e9b007267c16f8..ee6bfc304beb2df4fecaa0c1339de02c5fae656b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -473,6 +473,17 @@ Features:
   line, and then generate a mount unit for it using a udev generated symlink
   based on lo_file_name.
 
+* teach systemd-nspawn the boot assessment logic: hook up vpick's try counters
+  with success notifications from nspawn payloads. When this is enabled,
+  automatically support reverting back to older OS versin images if newer ones
+  fail to boot.
+
+* implement new "systemd-fsrebind" tool that works like gpt-auto-generator but
+  looks at a root dir and then applies vpick on various dirs/images to pick a
+  root tree, a /usr/ tree, a /home/, a /srv/, a /var/ tree and so on. Dirs
+  could also be btrfs subvols (combine with btrfs auto-snapshort approach for
+  creating versions like these automatically).
+
 * remove tomoyo support, it's obsolete and unmaintained apparently
 
 * In .socket units, add ConnectStream=, ConnectDatagram=,
@@ -704,17 +715,6 @@ Features:
 * automatic boot assessment: add one more default success check that just waits
   for a bit after boot, and blesses the boot if the system stayed up that long.
 
-* implement concept of "versioned" resources inside a dir, and write a spec for
-  it. Make all tools in systemd, in particular
-  RootImage=/RootDirectory=/--image=/--directory= implement this. Idea:
-  directories ending in ".v/" indicate a directory with versioned resources in
-  them. Versioned resources inside a .v dir are always named in the pattern
-  <prefix>_<version>[+<tries-left>[-<tries-done>]].<suffix>
-
-* add support for using this .v/ logic on the root fs itself: in the initrd,
-  after mounting the rootfs, look for root-<arch>.v/ in the root fs, and then
-  apply the logic, moving the switch root logic there.
-
 * systemd-repart: add support for generating ISO9660 images
 
 * systemd-repart: in addition to the existing "factory reset" mode (which
@@ -1170,26 +1170,6 @@ Features:
   passwords, not just the first. i.e. if there are multiple defined, prefer
   unlocked over locked and prefer non-empty over empty.
 
-* maybe add a tool inspired by the GPT auto discovery spec that runs in the
-  initrd and rearranges the rootfs hierarchy via bind mounts, if
-  enabled. Specifically in some top-level dir /@auto/ it will look for
-  dirs/symlinks/subvolumes that are named after their purpose, and optionally
-  encode a version as well as assessment counters, and then mount them into the
-  file system tree to boot into, similar to how we do that for the gpt auto
-  logic. Maybe then bind mount the original root into /.superior or something
-  like that (so that update tools can look there). Further discussion in this
-  thread:
-  https://lists.freedesktop.org/archives/systemd-devel/2021-November/047059.html
-  The GPT dissection logic should automatically enable this tool whenever we
-  detect a specially marked root fs (i.e introduce a new generic root gpt type
-  for this, that is arch independent). The also implement this in the image
-  dissection logic, so that nspawn/RootImage= and so on grok it. Maybe make
-  generic enough so that it can also work for ostrees arrangements.
-
-* if a path ending in ".auto.d/" is set for RootDirectory=/RootImage= then do a
-  strverscmp() of everything inside that dir and use that. i.e. implement very
-  simple version control. Also use this in systemd-nspawn --image= and so on.
-
 * homed: while a home dir is not activated generate slightly different NSS
   records for it, that reports the home dir as "/" and the shell as some binary
   provided by us. Then, when an SSH login happens and SSH permits it our binary
@@ -1346,9 +1326,6 @@ Features:
   - make user manager instances create and use a user-specific key (the one in
     /var/lib is root-only) and add --user switch to systemd-creds to use it
 
-* add tpm.target or so which is delayed until TPM2 device showed up in case
-  firmware indicates there is one.
-
 * TPM2: auto-reenroll in cryptsetup, as fallback for hosed firmware upgrades
   and such
 
index e16aa9fdaedf229cbae21686da3958d45a99de91..9e77e514dd034629df8d6857298b3c5d9a1c8103 100644 (file)
@@ -2,12 +2,11 @@
 
 /* FIXME
  *  - issues with parsing stuff like
- *      * int foo[ELEMENTSOF(bar)] = {};
- *      * validchars = UPPERCASE_LETTERS DIGITS;
- *      * multiline compound literals (some instances)
- *      * compound literals in function calls (some instances)
- *      * keywords in macro invocations like FOREACH_DIRENT_ALL(de, d, return -errno)
- *        (also, see FIXME in the TEST stuff below)
+ *      - validchars = UPPERCASE_LETTERS DIGITS;
+ *          - see: https://github.com/coccinelle/coccinelle/issues/341
+ *      - keywords in macro invocations like FOREACH_DIRENT_ALL(de, d, return -errno)
+ *          - see: https://github.com/coccinelle/coccinelle/issues/340
+ *          - also see the FIXME in the TEST() stuff below
  */
 
 /* This file contains parsing hacks for Coccinelle (spatch), to make it happy with some of our more complex
@@ -18,7 +17,7 @@
  * special cases in which the parser incorrectly infers information that then causes issues in valid code
  * later down the line.
  *
- * Inspired by a similarly named file [0] from the Coccinelle sources, and the original bultin macros [1].
+ * Inspired by a similarly named file [0] from the Coccinelle sources, and the original builtin macros [1].
  *
  * [0] https://github.com/coccinelle/coccinelle/blob/master/parsing_c/parsing_hacks.ml
  * [1] https://github.com/coccinelle/coccinelle/blob/master/standard.h
@@ -62,8 +61,9 @@
 /* Coccinelle doesn't know this keyword, so just drop it, since it's not important for any of our rules. */
 #define thread_local
 
-/* Coccinelle fails to get this one from the included headers, so let's just drop it. */
+/* Coccinelle fails to parse these from the included headers, so let's just drop them. */
 #define PAM_EXTERN
+#define STACK_OF(x)
 
 /* Mark a couple of iterator explicitly as iterators, otherwise Coccinelle gets a bit confused. Coccinelle
  * can usually infer this information automagically, but in these specific cases it needs a bit of help. */
@@ -74,7 +74,9 @@
 #define LIST_FOREACH(name, i, head) YACFE_ITERATOR
 #define ORDERED_HASHMAP_FOREACH(e, h) YACFE_ITERATOR
 #define SET_FOREACH(e, s) YACFE_ITERATOR
+#define STRV_FOREACH_BACKWARDS YACFE_ITERATOR
 
 /* Coccinelle really doesn't like multiline macros that are not in the "usual" do { ... } while(0) format, so
  * let's help it a little here by providing simplified one-line versions. */
 #define CMSG_BUFFER_TYPE(x) union { uint8_t align_check[(size) >= CMSG_SPACE(0) && (size) == CMSG_ALIGN(size) ? 1 : -1]; }
+#define SD_ID128_MAKE(...) ((const sd_id128) {})
index af5dd359884d4ad6c09f500a09e01ce039f9a289..d4bccdc505bec612473277e28e105a8002b5247d 100755 (executable)
@@ -52,16 +52,16 @@ for script in "${SCRIPTS[@]}"; do
     # A couple of notes:
     #
     # 1) Limit this to 10 files at once, as processing the ASTs is _very_ memory hungry - e.g. with 20 files
-    # at once one spatch process can take around 2.5 GiB of RAM, which can easily eat up all available RAM
-    # when paired together with parallel
+    #    at once one spatch process can take around 2.5 GiB of RAM, which can easily eat up all available RAM
+    #    when paired together with parallel
     #
     # 2) Make sure spatch can find our includes via -I <dir>, similarly as we do when compiling stuff.
     #    Also, include the system include path as well, since we're not kernel and we make use of the stdlib
     #    (and other libraries).
     #
     # 3) Make sure to include includes from includes (--recursive-includes), but use them only to get type
-    # definitions (--include-headers-for-types) - otherwise we'd start formatting them as well, which might
-    # be unwanted, especially for includes we fetch verbatim from third-parties
+    #    definitions (--include-headers-for-types) - otherwise we'd start formatting them as well, which might
+    #    be unwanted, especially for includes we fetch verbatim from third-parties
     #
     # 4) Explicitly undefine the SD_BOOT symbol, so Coccinelle ignores includes guarded by #if SD_BOOT
     #
@@ -74,6 +74,7 @@ for script in "${SCRIPTS[@]}"; do
                --recursive-includes \
                --include-headers-for-types \
                --undefined SD_BOOT \
+               --undefined ENABLE_DEBUG_HASHMAP \
                --macro-file-builtins "coccinelle/parsing_hacks.h" \
                --smpl-spacing \
                --sp-file "$script" \
index a9afd90aa60b89dbc842744c601139dffdcdae53..c577120f7b9f0e0288696881fce413e4cf100d05 100644 (file)
@@ -4,3 +4,10 @@ expression p, s;
 @@
 - siphash24_compress(&p, sizeof(p), s);
 + siphash24_compress_typesafe(p, s);
+
+@@
+union in_addr_union p;
+expression f, s;
+@@
+- siphash24_compress(&p, FAMILY_ADDRESS_SIZE(f), s);
++ in_addr_hash_func(&p, f, s);
index 819d5367738bf974fe7d9aa944536bfdeebf5b44..b9a96be82c770be4d378c6dc11e582e74280c788 100644 (file)
@@ -249,6 +249,12 @@ All tools:
   devices sysfs path are actually backed by sysfs. Relaxing this verification
   is useful for testing purposes.
 
+* `$SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC=` — Specifies an extra timespan that the
+  udev manager process waits for a worker process kills slow programs specified
+  by IMPORT{program}=, PROGRAM=, or RUN=, and finalizes the processing event.
+  If the worker process cannot finalize the event within the specified timespan,
+  the worker process is killed by the manager process. Defaults to 10 seconds.
+
 `udevadm` and `systemd-hwdb`:
 
 * `SYSTEMD_HWDB_UPDATE_BYPASS=` — If set to "1", execution of hwdb updates is skipped
index ff19ba91fa01b7af3468f6b35d39b988d085050e..d74c9b410fd3973aa4911780cd534b79ac1afcfc 100644 (file)
         <term><varname>StopIdleSessionSec=</varname></term>
 
         <listitem><para>Specifies a timeout in seconds, or a time span value after which
-        <filename>systemd-logind</filename> checks the idle state of all sessions. Every session that is idle for
-        longer then the timeout will be stopped. Defaults to <literal>infinity</literal>
-        (<filename>systemd-logind</filename> is not checking the idle state of sessions). For details about the syntax
-        of time spans, see
+        <filename>systemd-logind</filename> checks the idle state of all sessions. Every session that is idle
+        for longer than the timeout will be stopped. Note that this option doesn't apply to
+        <literal>greeter</literal> or <literal>lock-screen</literal> sessions. Defaults to
+        <literal>infinity</literal> (<filename>systemd-logind</filename> is not checking the idle state
+        of sessions). For details about the syntax of time spans, see
         <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
         </para>
 
index f6d327dfd3ef5334e9e2800b4bbfe5bc7212f77d..878bd0d99d95a428529138b70b7a006b886ff0cc 100644 (file)
@@ -277,6 +277,11 @@ node /org/freedesktop/systemd1 {
       GetDynamicUsers(out a(us) users);
       DumpUnitFileDescriptorStore(in  s name,
                                   out a(suuutuusu) entries);
+      StartAuxiliaryScope(in  s name,
+                          in  ah pidfds,
+                          in  t flags,
+                          in  a(sv) properties,
+                          out o job);
     signals:
       UnitNew(s id,
               o unit);
@@ -990,6 +995,8 @@ node /org/freedesktop/systemd1 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="DumpUnitFileDescriptorStore()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="StartAuxiliaryScope()"/>
+
     <variablelist class="dbus-signal" generated="True" extra-ref="UnitNew"/>
 
     <variablelist class="dbus-signal" generated="True" extra-ref="UnitRemoved"/>
@@ -1567,6 +1574,13 @@ node /org/freedesktop/systemd1 {
       file descriptors currently in the file descriptor store of the specified unit. This call is equivalent
       to <function>DumpFileDescriptorStore()</function> on the
       <interfacename>org.freedesktop.systemd1.Service</interfacename>. For further details, see below.</para>
+
+      <para><function>StartAuxiliaryScope()</function> creates a new scope unit from a service where calling
+      process resides. Set of processes that will be migrated to newly created scope is passed in as an array
+      of pidfds. This is useful for creating auxiliary scopes that should contain worker processes and their lifecycle
+      shouldn't be bound to a lifecycle of the service, e.g. they should continue running after the restart
+      of the service. Note that the main PID of the service can not be migrated to an auxiliary scope.
+      Also, <varname>flags</varname> argument must be 0 and is reserved for future extensions.</para>
     </refsect2>
 
     <refsect2>
@@ -2792,6 +2806,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -2800,6 +2818,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -3425,6 +3445,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -3433,6 +3457,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -4067,6 +4093,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -4075,6 +4105,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -4871,6 +4903,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -4879,6 +4915,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -5514,6 +5552,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -5522,6 +5564,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -6138,6 +6182,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -6146,6 +6194,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -6816,6 +6866,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -6824,6 +6878,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -7387,6 +7443,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -7395,6 +7455,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -7925,6 +7987,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -7933,6 +7999,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -8726,6 +8794,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -8734,6 +8806,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -9283,6 +9357,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -9291,6 +9369,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -9807,6 +9887,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -9815,6 +9899,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -10467,6 +10553,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -10475,6 +10565,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -10650,6 +10742,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -10658,6 +10754,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -10838,6 +10936,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -10846,6 +10948,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -11052,6 +11156,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t MemoryAvailable = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveMemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t CPUUsageNSec = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly ay EffectiveCPUs = [...];
@@ -11060,6 +11168,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t TasksCurrent = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t EffectiveTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressBytes = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
       readonly t IPIngressPackets = ...;
@@ -11255,6 +11365,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <!--property MemoryZSwapCurrent is not documented!-->
 
+    <!--property EffectiveMemoryMax is not documented!-->
+
+    <!--property EffectiveMemoryHigh is not documented!-->
+
     <!--property CPUUsageNSec is not documented!-->
 
     <!--property EffectiveCPUs is not documented!-->
@@ -11263,6 +11377,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <!--property TasksCurrent is not documented!-->
 
+    <!--property EffectiveTasksMax is not documented!-->
+
     <!--property IPIngressBytes is not documented!-->
 
     <!--property IPIngressPackets is not documented!-->
@@ -11473,6 +11589,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
@@ -11481,6 +11601,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
 
     <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@@ -11826,6 +11948,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <function>QueueSignalUnit()</function>,
       <function>SoftReboot()</function>, and
       <function>DumpUnitFileDescriptorStore()</function> were added in version 254.</para>
+      <para><function>StartAuxiliaryScope()</function> was added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Unit Objects</title>
@@ -11873,6 +11996,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Socket Unit Objects</title>
@@ -11904,6 +12030,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Mount Unit Objects</title>
@@ -11933,6 +12062,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Swap Unit Objects</title>
@@ -11962,6 +12094,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Slice Unit Objects</title>
@@ -11982,6 +12117,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Scope Unit Objects</title>
@@ -12003,6 +12141,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemorySwapCurrent</varname>,
       <varname>MemorySwapPeak</varname>, and
       <varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+      <para><varname>EffectiveMemoryHigh</varname>,
+      <varname>EffectiveMemoryMax</varname>,
+      <varname>EffectiveTasksMax</varname> were added in version 256.</para>
     </refsect2>
     <refsect2>
       <title>Job Objects</title>
index e99b77eb50051b458f657a1814fd90c86de417b0..3592b862f718d1633d7bbc3800aa285358e08469 100644 (file)
@@ -1100,6 +1100,7 @@ manpages = [
    'systemd-tmpfiles-setup-dev.service',
    'systemd-tmpfiles-setup.service'],
   ''],
+ ['systemd-tpm2-generator', '8', [], ''],
  ['systemd-tpm2-setup.service',
   '8',
   ['systemd-tpm2-setup', 'systemd-tpm2-setup-early.service'],
@@ -1130,6 +1131,7 @@ manpages = [
   'HAVE_LIBCRYPTSETUP'],
  ['systemd-vmspawn', '1', [], 'ENABLE_VMSPAWN'],
  ['systemd-volatile-root.service', '8', ['systemd-volatile-root'], ''],
+ ['systemd-vpick', '1', [], ''],
  ['systemd-xdg-autostart-generator', '8', [], 'ENABLE_XDG_AUTOSTART'],
  ['systemd', '1', ['init'], ''],
  ['systemd.automount', '5', [], ''],
@@ -1164,6 +1166,7 @@ manpages = [
  ['systemd.time', '7', [], ''],
  ['systemd.timer', '5', [], ''],
  ['systemd.unit', '5', [], ''],
+ ['systemd.v', '7', [], ''],
  ['sysupdate.d', '5', [], 'ENABLE_SYSUPDATE'],
  ['sysusers.d', '5', [], 'ENABLE_SYSUSERS'],
  ['telinit', '8', [], 'HAVE_SYSV_COMPAT'],
index 59e01815f481d0a0dafba852e952f832113b50ce..bb7f2b6b3e77c2397e9d73e9bf9189749ea26805 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para><filename>systemd-backlight@.service</filename> is a service that restores the display backlight
-    brightness at early boot and saves it at shutdown. On disk, the backlight brightness is stored in
-    <filename>/var/lib/systemd/backlight/</filename>. During loading, if the udev property
-    <option>ID_BACKLIGHT_CLAMP</option> is not set to false, the brightness is clamped to a value of at least
-    1 or 5% of maximum brightness, whichever is greater. The percentage can be adjusted by specifying a
-    percentage (needs to be suffixed with <literal>%</literal>, e.g. <literal>30%</literal>) to the property
-    <option>ID_BACKLIGHT_CLAMP</option>.</para>
+    <para><filename>systemd-backlight@.service</filename> is a service that restores the brightness of
+    a display backlight or LED (e.g. keyboard backlight) device at early boot, and saves it at shutdown.
+    The brightness is stored in <filename>/var/lib/systemd/backlight/</filename>.</para>
+
+    <para>On restoring brightness of a display backlight device, <command>systemd-backlight</command> reads
+    <varname>ID_BACKLIGHT_CLAMP</varname> udev property, that takes a boolean value or a percentage (needs to
+    be suffixed with <literal>%</literal>, e.g. <literal>30%</literal>). When a percentage is specified, the
+    saved brightness is clamped to a value of at least 1 or the specified percentage of the maximum
+    brightness, whichever is greater. When unset or set to true, the brightness is clamped in the same way
+    with percentage 5%. When false, the saved brightness will not be clamped, and loaded as is.</para>
+
+    <para>On restoring brightness of a LED device, <command>systemd-backlight</command> reads
+    <varname>ID_LEDS_CLAMP</varname> udev property, that also takes a boolean value or a percentage. When a
+    percentage is specified, the saved brightness is clamped to the specified percentage of the maximum
+    brightness. When set to true, the brightness is clamped in the same way with percentage 5%. When unset or
+    set to false, the saved brightness will not be clamped, and loaded as is.</para>
   </refsect1>
 
   <refsect1>
index b238a9f7d06ca89338a9e9c9524c3b976aeb96bf..85166f23d352ce6aace565e40b2fbc8f92b0d5cf 100644 (file)
     mounted directly by <command>mount</command> and <citerefentry
     project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>. For
     details see below.</para>
+
+    <para>In place of the image path a <literal>.v/</literal> versioned directory may be specified, see
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+    details.</para>
   </refsect1>
 
   <refsect1>
       <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
       <member><ulink url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions Specification</ulink></member>
       <member><citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
       <member><citerefentry project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
index e721f0e5ab4acbf3bec7dfe324464db7807eb583..1a9fb09aaeae288588d2e6f8e0a5f44c3da6114c 100644 (file)
         <term><option>-D</option></term>
         <term><option>--directory=</option></term>
 
-        <listitem><para>Directory to use as file system root for the
-        container.</para>
+        <listitem><para>Directory to use as file system root for the container.</para>
 
-        <para>If neither <option>--directory=</option>, nor
-        <option>--image=</option> is specified the directory is
-        determined by searching for a directory named the same as the
-        machine name specified with <option>--machine=</option>. See
+        <para>If neither <option>--directory=</option>, nor <option>--image=</option> is specified the
+        directory is determined by searching for a directory named the same as the machine name specified
+        with <option>--machine=</option>. See
         <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         section "Files and Directories" for the precise search path.</para>
 
-        <para>If neither <option>--directory=</option>,
-        <option>--image=</option>, nor <option>--machine=</option>
-        are specified, the current directory will
-        be used. May not be specified together with
-        <option>--image=</option>.</para></listitem>
+        <para>In place of the directory path a <literal>.v/</literal> versioned directory may be specified, see
+        <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para>
+
+        <para>If neither <option>--directory=</option>, <option>--image=</option>, nor
+        <option>--machine=</option> are specified, the current directory will be used. May not be specified
+        together with <option>--image=</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
         together with <option>--directory=</option>, <option>--template=</option>.</para>
 
+        <para>In place of the image path a <literal>.v/</literal> versioned directory may be specified, see
+        <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para>
+
         <xi:include href="version-info.xml" xpointer="v211"/></listitem>
       </varlistentry>
 
index 5b47d752cac166a998813058468328486f9bcc07..e489a138d64daee9d11b248c5b7ba44abebea4f0 100644 (file)
@@ -32,6 +32,8 @@
       <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename></member>
       <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.cred</filename></member>
       <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.raw</filename></member>
+      <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.sysext.raw</filename></member>
+      <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.confext.raw</filename></member>
       <member><filename><replaceable>ESP</replaceable>/loader/addons/*.addon.efi</filename></member>
       <member><filename><replaceable>ESP</replaceable>/loader/credentials/*.cred</filename></member>
     </simplelist></para>
       details on encrypted credentials. The generated <command>cpio</command> archive is measured into TPM
       PCR 12 (if a TPM is present).</para></listitem>
 
-      <listitem><para>Similarly, files <filename><replaceable>foo</replaceable>.efi.extra.d/*.raw</filename>
-      are packed up in a <command>cpio</command> archive and placed in the <filename>/.extra/sysext/</filename>
-      directory in the initrd file hierarchy. This is supposed to be used to pass additional system extension
-      images to the initrd. See
+      <listitem><para>Similarly, files
+      <filename><replaceable>foo</replaceable>.efi.extra.d/*.sysext.raw</filename> are packed up in a
+      <command>cpio</command> archive and placed in the <filename>/.extra/sysext/</filename> directory in the
+      initrd file hierarchy. This is supposed to be used to pass additional system extension 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 13 (if a TPM is present).</para></listitem>
 
+      <!-- Note: the actual suffix we look for for sysexts is just *.raw (not *.sysext.raw), for
+           compatibility reasons with old versions. But we want people to name their system extensions
+           properly, hence we document the *.sysext.raw suffix only. -->
+
+      <listitem><para>Similarly, files
+      <filename><replaceable>foo</replaceable>.efi.extra.d/*.confext.raw</filename> are packed up in a
+      <command>cpio</command> archive and placed in the <filename>/.extra/confext/</filename> directory in
+      the initrd file hierarchy. This is supposed to be used to pass additional configuration extension
+      images to the initrd. See
+      <citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
+      details on configuration extension images. The generated <command>cpio</command> archive containing
+      these system extension images is measured into TPM PCR 12 (if a TPM is present).</para></listitem>
+
       <listitem><para>Similarly, files
       <filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename> are loaded and verified as
       PE binaries, and a <literal>.cmdline</literal> section is parsed from them. Addons are supposed to be
 
     <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 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>
+    measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials (and the one synthesized
+    from configuration extensions) 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>
             <entry>System Extensions (synthesized initrd from companion files)</entry>
             <entry>9 + 13</entry>
           </row>
+
+          <row>
+            <entry>Configuration Extensions (synthesized initrd from companion files)</entry>
+            <entry>9 + 12</entry>
+          </row>
         </tbody>
       </tgroup>
     </table>
       <varlistentry>
         <term><varname>StubPcrInitRDSysExts</varname></term>
 
-        <listitem><para>The PCR register index the systemd extensions for the initrd, which are picked up
-        from the file system the kernel image is located on. Formatted as decimal ASCII string (e.g.
+        <listitem><para>The PCR register index the system extensions for the initrd, which are picked up from
+        the file system the kernel image is located on. Formatted as decimal ASCII string (e.g.
         <literal>13</literal>). This variable is set if a measurement was successfully completed, and remains
         unset otherwise.</para>
 
         <xi:include href="version-info.xml" xpointer="v252"/></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>StubPcrInitRDConfExts</varname></term>
+
+        <listitem><para>The PCR register index the configuration extensions for the initrd, which are picked
+        up from the file system the kernel image is located on. Formatted as decimal ASCII string (e.g.
+        <literal>12</literal>). This variable is set if a measurement was successfully completed, and remains
+        unset otherwise.</para>
+
+        <xi:include href="version-info.xml" xpointer="v255"/></listitem>
+      </varlistentry>
     </variablelist>
 
     <para>Note that some of the variables above may also be set by the boot loader. The stub will only set
       </varlistentry>
 
       <varlistentry>
-        <term><filename>/.extra/sysext/*.raw</filename></term>
-        <listitem><para>System extension image files (suffix <literal>.raw</literal>) that are placed next to
-        the unified kernel image (as described above) are copied into the
+        <term><filename>/.extra/sysext/*.sysext.raw</filename></term>
+        <listitem><para>System extension image files (suffix <literal>.sysext.raw</literal>) that are placed
+        next to the unified kernel image (as described above) are copied into the
         <filename>/.extra/sysext/</filename> directory in the initrd execution environment.</para>
 
         <xi:include href="version-info.xml" xpointer="v252"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><filename>/.extra/confext/*.confext.raw</filename></term>
+        <listitem><para>Configuration extension image files (suffix <literal>.confext.raw</literal>) that are
+        placed next to the unified kernel image (as described above) are copied into the
+        <filename>/.extra/confext/</filename> directory in the initrd execution environment.</para>
+
+        <xi:include href="version-info.xml" xpointer="v255"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><filename>/.extra/tpm2-pcr-signature.json</filename></term>
         <listitem><para>The TPM2 PCR signature JSON object included in the <literal>.pcrsig</literal> PE
diff --git a/man/systemd-tpm2-generator.xml b/man/systemd-tpm2-generator.xml
new file mode 100644 (file)
index 0000000..51950ee
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+<refentry id="systemd-tpm2-generator">
+
+  <refentryinfo>
+    <title>systemd-tpm2-generator</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-tpm2-generator</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-tpm2-generator</refname>
+    <refpurpose>Generator for inserting TPM2 synchronization point in the boot process</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>/usr/lib/systemd/system-generators/systemd-tpm2-generator</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>systemd-tpm2-generator</filename> is a generator that adds a <varname>Wants=</varname>
+    dependency from <filename>sysinit.target</filename> to <filename>tpm2.target</filename> when it detects
+    that the firmware discovered a TPM2 device but the OS kernel so far did
+    not. <filename>tpm2.target</filename> is supposed to act as synchronization point for all services that
+    require TPM2 device access. See
+    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+    details.</para>
+
+    <para>The <option>systemd.tpm2_wait=</option> kernel command line option may be used to override
+    behaviour of the generator. It accepts a boolean value: if true then <filename>tpm2.target</filename>
+    will be added as synchronization point even if the firmware has not detected a TPM2 device. If false, the
+    target will not be inserted even if firmware reported a device but the OS kernel doesn't expose a device
+    for it yet. The latter might be useful in environments where a suitable TPM2 driver for the available
+    hardware is not available.</para>
+
+    <para><filename>systemd-tpm2-generator</filename> implements
+    <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para><simplelist type="inline">
+      <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+    </simplelist></para>
+  </refsect1>
+</refentry>
diff --git a/man/systemd-vpick.xml b/man/systemd-vpick.xml
new file mode 100644 (file)
index 0000000..95f946a
--- /dev/null
@@ -0,0 +1,198 @@
+<?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-vpick"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>systemd-vpick</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-vpick</refentrytitle>
+    <manvolnum>1</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-vpick</refname>
+    <refpurpose>Resolve paths to <literal>.v/</literal> versioned directories</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>systemd-vpick <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">PATH</arg></command>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><command>systemd-vpick</command> resolves a file system path referencing a <literal>.v/</literal>
+    versioned directory to a path to the newest (by version) file contained therein. This tool provides a
+    command line interface for the
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    logic.</para>
+
+    <para>The tool expects a path to a <literal>.v/</literal> directory as argument (either directly, or with
+    a triple underscore pattern as final component). It then determines the newest file contained in that
+    directory, and writes its path to standard output.</para>
+
+    <para>Unless the triple underscore pattern is passed as last component of the path, it is typically
+    necessary to at least specify the <option>--suffix=</option> switch to configure the file suffix to look
+    for.</para>
+
+    <para>If the specified path does not reference a <literal>.v/</literal> path (i.e. neither the final
+    component ends in <literal>.v</literal>, nor the penultimate does or the final one does contain a triple
+    underscore) it specified path is written unmodified to standard output.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are understood:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><option>--basename=</option></term>
+        <term><option>-B</option></term>
+
+        <listitem><para>Overrides the "basename" of the files to look for, i.e. the part to the left of the
+        variable part of the filenames. Normally this is derived automatically from the filename of the
+        <literal>.v</literal> component of the specified path, or from the triple underscore pattern in the
+        last component of the specified path.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-V</option></term>
+
+        <listitem><para>Explicitly configures the version to select. If specified, a filename with the
+        specified version string will be looked for, instead of the newest version
+        available.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-A</option></term>
+
+        <listitem><para>Explicitly configures the architecture to select. If specified, a filename with the
+        specified architecture identifier will be looked for. If not specified only filenames with a locally
+        supported architecture are considered, or those without any architecture identifier.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--suffix=</option></term>
+        <term><option>-S</option></term>
+
+        <listitem><para>Configures the suffix of the filenames to consider. For the <literal>.v/</literal>
+        logic it is necessary to specify the suffix to look for, and the <literal>.v/</literal> component
+        must also carry the suffix immediately before <literal>.v</literal> in its name.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--type=</option></term>
+        <term><option>-t</option></term>
+
+        <listitem><para>Configures the inode type to look for in the <literal>.v/</literal> directory. Takes
+        one of <literal>reg</literal>, <literal>dir</literal>, <literal>sock</literal>,
+        <literal>fifo</literal>, <literal>blk</literal>, <literal>chr</literal>, <literal>lnk</literal> as
+        argument, each identifying an inode type. See <citerefentry
+        project='man-pages'><refentrytitle>inode</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details about inode types. If this option is used inodes not matching the specified type are filtered
+        and not taken into consideration.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--print=</option></term>
+        <term><option>-p</option></term>
+
+        <listitem><para>Configures what precisely to write to standard output. If not specified prints the
+        full, resolved path of the newest matching file in the <literal>.v/</literal> directory. This switch can be set to one of the following:</para>
+
+        <itemizedlist>
+          <listitem><para>If set to <literal>filename</literal>, will print only the filename instead of the full path of the resolved file.</para></listitem>
+          <listitem><para>If set to <literal>version</literal>, will print only the version of the resolved file.</para></listitem>
+          <listitem><para>If set to <literal>type</literal>, will print only the inode type of the resolved
+          file (i.e. a string such as <literal>reg</literal> for regular files, or <literal>dir</literal> for
+          directories).</para></listitem>
+          <listitem><para>If set to <literal>arch</literal>, will print only the architecture of the resolved
+          file.</para></listitem>
+          <listitem><para>If set to <literal>tries</literal>, will print only the tries left/tries done of the
+          resolved file.</para></listitem>
+          <listitem><para>If set to <literal>all</literal>, will print all of the above in a simple tabular
+          output.</para></listitem>
+        </itemizedlist>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--resolve=</option></term>
+
+        <listitem><para>Takes a boolean argument. If true the path to the versioned file is fully
+        canonicalized (i.e. symlinks resolved, and redundant path components removed) before it is shown. If
+        false (the default) this is not done, and the path is shown without canonicalization.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <xi:include href="standard-options.xml" xpointer="help" />
+      <xi:include href="standard-options.xml" xpointer="version" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>Use a command like the following to automatically pick the newest raw disk image from a
+    <literal>.v/</literal> directory:</para>
+
+    <programlisting>$ systemd-vpick --suffix=.raw --type=reg /var/lib/machines/quux.raw.v/</programlisting>
+
+    <para>This will enumerate all regular files matching
+    <filename>/var/lib/machines/quux.raw.v/quux*.raw</filename>, filter and sort them according to the rules
+    described in
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>, and then
+    write the path to the newest (by version) file to standard output.</para>
+
+    <para>Use a command like the following to automatically pick the newest OS directory tree from a
+    <literal>.v/</literal> directory:</para>
+
+    <programlisting>$ systemd-vpick --type=dir /var/lib/machines/waldo.v/</programlisting>
+
+    <para>This will enumerate all directory inodes matching
+    <filename>/var/lib/machines/waldo.v/waldo*</filename>, filter and sort them according to the rules
+    described in
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>, and then
+    write the path to the newest (by version) directory to standard output.</para>
+
+    <para>For further examples see
+    <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+  </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><simplelist type="inline">
+      <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+    </simplelist></para>
+  </refsect1>
+</refentry>
index e2b291008452eab5140f1fe6888780aef16b0920..42e6ff8fd751433b2754d92e42d93cce66e1c6a5 100644 (file)
           <programlisting>BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout</programlisting>
         </example>
 
+        <para>In place of the directory path a <literal>.v/</literal> versioned directory may be specified,
+        see <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para>
+
         <xi:include href="system-or-user-ns.xml" xpointer="singular"/></listitem>
       </varlistentry>
 
         <citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>),
         in case the service is configured to survive it.</para>
 
+        <para>In place of the image path a <literal>.v/</literal> versioned directory may be specified, see
+        <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para>
+
         <xi:include href="system-only.xml" xpointer="singular"/>
 
         <xi:include href="version-info.xml" xpointer="v233"/></listitem>
index 6b210cb341766c01f4c7ed4a1bddacc3ef060fec..76f9f4d042c584f33ad563eb3d9f90ad4fdeff17 100644 (file)
@@ -2040,16 +2040,6 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
         </listitem>
       </varlistentry>
 
-      <varlistentry>
-        <term><varname>TTLPropagate=</varname></term>
-        <listitem>
-          <para>Takes a boolean. When true enables TTL propagation at Label Switched Path (LSP) egress.
-          When unset, the kernel's default will be used.</para>
-
-          <xi:include href="version-info.xml" xpointer="v243"/>
-        </listitem>
-      </varlistentry>
-
       <varlistentry>
         <term><varname>MTUBytes=</varname></term>
         <listitem>
index 2c5785cca9bf9f9eaeb46ab477edd95f416939a0..2eb6797f4c9d05d70bb6962f916fc73e145bcf2e 100644 (file)
@@ -409,7 +409,9 @@ CPUWeight=20   DisableControllers=cpu              /          \
           system. If assigned the
           special value <literal>infinity</literal>, no memory throttling is applied. This controls the
           <literal>memory.high</literal> control group attribute. For details about this control group attribute, see
-          <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
+          <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.
+          The effective configuration is reported as <varname>EffectiveMemoryHigh=</varname>
+          (see also <varname>EffectiveMemoryMax=</varname>).</para>
 
           <para>While <varname>StartupMemoryHigh=</varname> applies to the startup and shutdown phases of the system,
           <varname>MemoryHigh=</varname> applies to normal runtime of the system, and if the former is not set also to
@@ -437,7 +439,9 @@ CPUWeight=20   DisableControllers=cpu              /          \
           percentage value may be specified, which is taken relative to the installed physical memory on the system. If
           assigned the special value <literal>infinity</literal>, no memory limit is applied. This controls the
           <literal>memory.max</literal> control group attribute. For details about this control group attribute, see
-          <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
+          <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.
+          The effective configuration is reported as <varname>EffectiveMemoryMax=</varname> (the value is
+          the most stringent limit of the unit and parent slices and it is capped by physical memory).</para>
 
           <para>While <varname>StartupMemoryMax=</varname> applies to the startup and shutdown phases of the system,
           <varname>MemoryMax=</varname> applies to normal runtime of the system, and if the former is not set also to
@@ -563,7 +567,8 @@ CPUWeight=20   DisableControllers=cpu              /          \
           limit is applied. This controls the <literal>pids.max</literal> control group attribute. For
           details about this control group attribute, the
           <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#pid">pids controller
-          </ulink>.</para>
+          </ulink>.
+          The effective configuration is reported as <varname>EffectiveTasksMax=</varname>.</para>
 
           <para>The system default for this setting may be controlled with
           <varname>DefaultTasksMax=</varname> in
index ff0f73f191890ed2fe522c5b722097c136fb1288..988b7175ba8dd90ebd44074efa2ab896f7fc3cf9 100644 (file)
@@ -92,6 +92,7 @@
     <filename>time-set.target</filename>,
     <filename>time-sync.target</filename>,
     <filename>timers.target</filename>,
+    <filename>tpm2.target</filename>,
     <filename>umount.target</filename>,
     <filename>usb-gadget.target</filename>,
     <!-- slices --><filename>-.slice</filename>,
             <xi:include href="version-info.xml" xpointer="v242"/>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><filename>tpm2.target</filename></term>
+          <listitem>
+            <para>This target is started automatically if a TPM2 device is discovered, either by the OS or by
+            the firmware. It acts as synchronization point for services that require TPM2 device access. The
+            target unit is enqueued by
+            <citerefentry><refentrytitle>systemd-tpm2-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            if it detects that the firmware has discovered a TPM2 device but the OS kernel has not activated
+            a driver for it yet. It is also pulled in whenever
+            <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            discovers a TPM2 device. The target unit is ordered after the <filename>/dev/tpmrm0</filename>
+            device node, so that it only becomes active once the TPM2 device is actually accessible. Early
+            boot programs that intend to access the TPM2 device should hence order themselves after this
+            target unit, but not pull it in.</para>
+
+            <xi:include href="version-info.xml" xpointer="v256"/>
+          </listitem>
+        </varlistentry>
       </variablelist>
     </refsect2>
 
diff --git a/man/systemd.v.xml b/man/systemd.v.xml
new file mode 100644 (file)
index 0000000..6cc6122
--- /dev/null
@@ -0,0 +1,163 @@
+<?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.v">
+
+  <refentryinfo>
+    <title>systemd.v</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd.v</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd.v</refname>
+    <refpurpose>Directory with Versioned Resources</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>In various places systemd components accept paths whose trailing components have the
+    <literal>.v/</literal> suffix, pointing to a directory. These components will then automatically look for
+    suitable files inside the directory, do a version comparison and open the newest file found (by
+    version). Specifically, two expressions are supported:</para>
+
+    <itemizedlist>
+
+      <listitem><para>When looking for files with a suffix <replaceable>.SUFFIX</replaceable>, and a path
+      <filename>…<replaceable>PATH</replaceable>/<replaceable>NAME</replaceable><replaceable>.SUFFIX</replaceable>.v/</filename>
+      is specified, then all files
+      <filename>…<replaceable>PATH</replaceable>/<replaceable>NAME</replaceable><replaceable>.SUFFIX</replaceable>.v/<replaceable>NAME</replaceable>_*<replaceable>.SUFFIX</replaceable></filename>
+      are enumerated, filtered, sorted and the newest file used. The primary sorting key is the
+      <emphasis>variable part</emphasis>, here indicated by the wildcard
+      <literal>*</literal>.</para></listitem>
+
+      <listitem><para>When a path
+      <filename>…<replaceable>PATH</replaceable>.v/<replaceable>NAME</replaceable>___<replaceable>.SUFFIX</replaceable></filename>
+      is specified (i.e. the penultimate component of the path ends in <literal>.v</literal> and the final
+      component contains a triple underscore), then all files
+      <filename>…<replaceable>PATH</replaceable>.v/<replaceable>NAME</replaceable>_*<replaceable>.SUFFIX</replaceable></filename>
+      are enumerated, filtered, sorted and the newest file used (again, by the <emphasis>variable
+      part</emphasis>, here indicated by the wildcard <literal>*</literal>).</para></listitem>
+    </itemizedlist>
+
+    <para>To illustrate this in an example, consider a directory <filename>/var/lib/machines/mymachine.raw.v/</filename>, which is populated with three files:</para>
+
+    <itemizedlist>
+      <listitem><para><filename>mymachine_7.5.13.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.5.14.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.6.0.raw</filename></para></listitem>
+    </itemizedlist>
+
+    <para>Invoke a tool such as <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> with a command line like the following:</para>
+
+    <programlisting># systemd-nspawn --image=/var/lib/machines/mymachine.raw.v --boot</programlisting>
+
+    <para>Then this would automatically be resolved to the equivalent of:</para>
+
+    <programlisting># systemd-nspawn --image=/var/lib/machines/mymachine.raw.v/mymachine_7.6.0.raw --boot</programlisting>
+
+    <para>Much of systemd's functionality that expects a path to a disk image or OS directory hierarchy
+    support the <literal>.v/</literal> versioned directory mechanism, for example
+    <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry> or
+    the <varname>RootDirectory=</varname>/<varname>RootImage=</varname> settings of service files (see
+    <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para>
+
+    <para>Use the
+    <citerefentry><refentrytitle>systemd-vpick</refentrytitle><manvolnum>1</manvolnum></citerefentry> tool to
+    resolve <literal>.v/</literal> paths from the command line, for example for usage in shell
+    scripts.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Filtering and Sorting</title>
+
+    <para>The variable part of the filenames in the <literal>.v/</literal> directories are filtered and
+    compared primarily with a version comparison, implementing <ulink
+    url="https://uapi-group.org/specifications/specs/version_format_specification/">Version Format
+    Specification</ulink>. However, additional rules apply:</para>
+
+    <itemizedlist>
+      <listitem><para>If the variable part is suffixed by one or two integer values ("tries left" and "tries
+      done") in the formats <filename>+<replaceable>LEFT</replaceable></filename> or
+      <filename>+<replaceable>LEFT</replaceable>-<replaceable>DONE</replaceable></filename>, then these
+      indicate usage attempt counters. The idea is that each time before a file is attempted to be used, its
+      "tries left" counter is decreased, and the "tries done" counter increased (simply by renaming the
+      file). When the file is successfully used (which for example could mean for an OS image: successfully
+      booted) the counters are removed from the file name, indicating that the file has been validated to
+      work correctly. This mechanism mirrors the boot assessment counters defined by <ulink
+      url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/">Automatic Boot Assessment</ulink>. Any filenames
+      with no boot counters or with a non-zero "tries left" counter are sorted before filenames with a zero
+      "tries left" counter.</para></listitem>
+
+      <listitem><para>Preceeding the use counters (if they are specified), an optional CPU architecture
+      identifier may be specified in the filename (separated from the version with an underscore), as defined
+      in the architecture vocabulary of the <varname>ConditionArchitecture=</varname> unit file setting, as
+      documented in
+      <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Files
+      whose name indicates an architecture not supported locally are filtered and not considered for the
+      version comparison.</para></listitem>
+
+      <listitem><para>The rest of the variable part is the version string.</para></listitem>
+    </itemizedlist>
+
+    <para>Or in other words, the files in the <literal>.v/</literal> directories should follow one of these
+    naming structures:</para>
+
+    <itemizedlist>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>_<replaceable>ARCHITECTURE</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>+<replaceable>LEFT</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>+<replaceable>LEFT</replaceable>-<replaceable>DONE</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>_<replaceable>ARCHITECTURE</replaceable>+<replaceable>LEFT</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+      <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>_<replaceable>ARCHITECTURE</replaceable>+<replaceable>LEFT</replaceable>-<replaceable>DONE</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Example</title>
+
+    <para>Here's a more comprehensive example, further extending the one described above. Consider a
+    directory <filename>/var/lib/machines/mymachine.raw.v/</filename>, which is populated with the following
+    files:</para>
+
+    <itemizedlist>
+      <listitem><para><filename>mymachine_7.5.13.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.5.14_x86-64.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.6.0_arm64.raw</filename></para></listitem>
+      <listitem><para><filename>mymachine_7.7.0_x86-64+0-5.raw</filename></para></listitem>
+    </itemizedlist>
+
+    <para>Now invoke the following command on an x86-64 machine:</para>
+
+    <programlisting>$ systemd-vpick --suffix=.raw /var/lib/machines/mymachine.raw.v/</programlisting>
+
+    <para>This would resolve the specified path to
+    <filename>/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw</filename>. Explanation: even
+    though <filename>mymachine_7.7.0_x86-64+0-5.raw</filename> has the newest version, it is not preferred
+    because its tries left counter is zero. And even though <filename>mymachine_7.6.0_arm64.raw</filename>
+    has the second newest version it is also not considered, in this case because we operate on an x86_64
+    system and the image is intended for arm64 CPUs. Finally, the <filename>mymachine_7.5.13.raw</filename>
+    image is not considered because it is older than <filename>mymachine_7.5.14_x86-64.raw</filename>.</para>
+  </refsect1>
+
+  <refsect1>
+      <title>See Also</title>
+      <para><simplelist type="inline">
+        <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd-vpick</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+        <member><citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      </simplelist></para>
+  </refsect1>
+
+</refentry>
index d1433e3dbc750f0ae043acef7a845e71483622b7..d2d255391d523f009976acbb287a4e0f71ae4fd1 100644 (file)
@@ -2230,6 +2230,7 @@ subdir('src/vconsole')
 subdir('src/veritysetup')
 subdir('src/vmspawn')
 subdir('src/volatile-root')
+subdir('src/vpick')
 subdir('src/xdg-autostart-generator')
 
 subdir('src/systemd')
index 057b2937c7242f4bd2a8f886975c94873305d44c..1af7cf8531f7859c59244412d677424d6d0df6c1 100644 (file)
--- a/po/pa.po
+++ b/po/pa.po
@@ -1,19 +1,21 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 #
 # A S Alam <amanpreet.alam@gmail.com>, 2020, 2021, 2023.
+# A S Alam <aalam@users.noreply.translate.fedoraproject.org>, 2023.
 msgid ""
 msgstr ""
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2023-11-14 21:25+0000\n"
-"PO-Revision-Date: 2023-03-14 02:20+0000\n"
-"Last-Translator: A S Alam <amanpreet.alam@gmail.com>\n"
-"Language-Team: Punjabi <https://translate.fedoraproject.org/projects/systemd/master/pa/>\n"
+"PO-Revision-Date: 2023-12-28 15:36+0000\n"
+"Last-Translator: A S Alam <aalam@users.noreply.translate.fedoraproject.org>\n"
+"Language-Team: Punjabi <https://translate.fedoraproject.org/projects/systemd/"
+"master/pa/>\n"
 "Language: pa\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.3\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -66,19 +68,19 @@ msgstr "systemd ਹਾਲਤ ਲੋਡ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤ
 
 #: src/home/org.freedesktop.home1.policy:13
 msgid "Create a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਬਣਾਓ"
 
 #: src/home/org.freedesktop.home1.policy:14
 msgid "Authentication is required to create a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਨੂੰ ਬਣਾਉਣ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/home/org.freedesktop.home1.policy:23
 msgid "Remove a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਨੂੰ ਹਟਾਓ"
 
 #: src/home/org.freedesktop.home1.policy:24
 msgid "Authentication is required to remove a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਨੂੰ ਹਟਾਉਣ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/home/org.freedesktop.home1.policy:33
 msgid "Check credentials of a home area"
@@ -90,27 +92,27 @@ msgstr ""
 
 #: src/home/org.freedesktop.home1.policy:43
 msgid "Update a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਨੂੰ ਅੱਪਡੇਟ ਕਰੋ"
 
 #: src/home/org.freedesktop.home1.policy:44
 msgid "Authentication is required to update a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/home/org.freedesktop.home1.policy:53
 msgid "Resize a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਦਾ ਆਕਾਰ ਬਦਲੋ"
 
 #: src/home/org.freedesktop.home1.policy:54
 msgid "Authentication is required to resize a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਦੇ ਆਕਰ ਨੂੰ ਬਦਲਣ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/home/org.freedesktop.home1.policy:63
 msgid "Change password of a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਲਈ ਪਾਸਵਰਡ ਬਦਲੋ"
 
 #: src/home/org.freedesktop.home1.policy:64
 msgid "Authentication is required to change the password of a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਲਈ ਪਾਸਵਰਡ ਨੂੰ ਬਦਲਣ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/home/pam_systemd_home.c:287
 #, c-format
@@ -121,32 +123,34 @@ msgstr ""
 #, c-format
 msgid "Too frequent login attempts for user %s, try again later."
 msgstr ""
+"ਵਰਤੋਂਕਾਰ %s ਲਈ ਬਹੁਤ ਛੇਤੀ ਛੇਤੀ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ਾਂ ਕੀਤੀਆਂ, ਬਾਅਦ ਵਿੱਚ ਕੋਸ਼ਿਸ਼ ਕਰਿਓ।"
 
 #: src/home/pam_systemd_home.c:304
 msgid "Password: "
-msgstr ""
+msgstr "ਪਾਸਵਰਡ: "
 
 #: src/home/pam_systemd_home.c:306
 #, c-format
 msgid "Password incorrect or not sufficient for authentication of user %s."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ %s ਦੀ ਪਰਮਾਣਿਕਤਾ ਲਈ ਪਾਸਵਰਡ ਗਲਤ ਹੈ ਜਾਂ ਕਾਫ਼ੀ ਨਹੀਂ ਹੈ।"
 
 #: src/home/pam_systemd_home.c:307
 msgid "Sorry, try again: "
-msgstr ""
+msgstr "ਅਫ਼ਸੋਸ, ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰਿਓ: "
 
 #: src/home/pam_systemd_home.c:329
 msgid "Recovery key: "
-msgstr ""
+msgstr "ਰਿਕਵਰੀ ਕੁੰਜੀ: "
 
 #: src/home/pam_systemd_home.c:331
 #, c-format
 msgid "Password/recovery key incorrect or not sufficient for authentication of user %s."
 msgstr ""
+"ਵਰਤੋਂਕਾਰ %s ਦੀ ਪਰਮਾਣਿਕਤਾ ਲਈ ਪਾਸਵਰਡ/ਰਿਕਵਰੀ ਕੁੰਜੀ ਗਲਤ ਹੈ ਜਾਂ ਕਾਫ਼ੀ ਨਹੀਂ ਹੈ।"
 
 #: src/home/pam_systemd_home.c:332
 msgid "Sorry, reenter recovery key: "
-msgstr ""
+msgstr "ਅਫ਼ਸੋਸ, ਰਿਕਵਰੀ ਕੁੰਜੀ ਫੇਰ ਭਰਿਓ: "
 
 #: src/home/pam_systemd_home.c:352
 #, c-format
@@ -155,7 +159,7 @@ msgstr ""
 
 #: src/home/pam_systemd_home.c:353 src/home/pam_systemd_home.c:356
 msgid "Try again with password: "
-msgstr ""
+msgstr "ਪਾਸਵਰਡ ਨਾਲ ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰਿਓ: "
 
 #: src/home/pam_systemd_home.c:355
 #, c-format
@@ -164,7 +168,7 @@ msgstr ""
 
 #: src/home/pam_systemd_home.c:376
 msgid "Security token PIN: "
-msgstr ""
+msgstr "ਸੁਰੱਖਿਆ ਟੋਕਨ ਪਿੰਨ: "
 
 #: src/home/pam_systemd_home.c:393
 #, c-format
@@ -214,6 +218,7 @@ msgstr ""
 #, c-format
 msgid "Home of user %s is currently locked, please unlock locally first."
 msgstr ""
+"%s ਵਰਤੋਂਕਾਰ ਦਾ ਹੋਮ ਇਸ ਵੇਲੇ ਲਾਕ ਹੈ, ਪਹਿਲਾਂ ਇਸ ਨੂੰ ਲੋਕਲ ਰੂਪ ਵਿੱਚ ਅਣ-ਲਾਕ ਕਰੋ।"
 
 #: src/home/pam_systemd_home.c:645
 #, c-format
@@ -222,7 +227,7 @@ msgstr ""
 
 #: src/home/pam_systemd_home.c:868
 msgid "User record is blocked, prohibiting access."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਰਿਕਾਰਡ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਈ ਹੈ, ਪਹੁੰਚ ਤੋਂ ਰੋਕਿਆ ਜਾ ਰਿਹਾ ਹੈ।"
 
 #: src/home/pam_systemd_home.c:872
 msgid "User record is not valid yet, prohibiting access."
@@ -239,31 +244,32 @@ msgstr ""
 #: src/home/pam_systemd_home.c:893
 #, c-format
 msgid "Too many logins, try again in %s."
-msgstr ""
+msgstr "ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ ਕੀਤੀਆਂ, %s ਵਿੱਚ ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
 
 #: src/home/pam_systemd_home.c:904
 msgid "Password change required."
-msgstr ""
+msgstr "ਪਾਸਵਰਡ ਬਦਲਣ ਦੀ ਲੋੜ ਹੈ।"
 
 #: src/home/pam_systemd_home.c:908
 msgid "Password expired, change required."
-msgstr ""
+msgstr "ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ ਪੁੱਗੀ ਹੈ, ਇਹ ਬਦਲਣ ਦੀ ਲੋੜ ਹੈ।"
 
 #: src/home/pam_systemd_home.c:914
 msgid "Password is expired, but can't change, refusing login."
 msgstr ""
+"ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ ਪੁੱਗੀ, ਪਰ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ, ਲਾਗਇਨ ਤੋਂ ਇਨਕਾਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"
 
 #: src/home/pam_systemd_home.c:918
 msgid "Password will expire soon, please change."
-msgstr ""
+msgstr "ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ ਛੇਤੀ ਹੀ ਪੁੱਗ ਜਾਵੇਗੀ, ਇਸ ਨੂੰ ਬਦਲ ਲਵੋ।"
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
 msgid "Set hostname"
-msgstr ""
+msgstr "ਹੋਸਟ ਨਾਂ ਸੈੱਟ ਕਰੋ"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
 msgid "Authentication is required to set the local hostname."
-msgstr ""
+msgstr "ਲੋਕਲ ਹੋਸਟ-ਨਾਂ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
 msgid "Set static hostname"
@@ -275,11 +281,11 @@ msgstr ""
 
 #: src/hostname/org.freedesktop.hostname1.policy:41
 msgid "Set machine information"
-msgstr ""
+msgstr "ਮਸ਼ੀਨ ਜਾਣਕਾਰੀ ਸੈੱਟ ਕਰੋ"
 
 #: src/hostname/org.freedesktop.hostname1.policy:42
 msgid "Authentication is required to set local machine information."
-msgstr ""
+msgstr "ਲੋਕਲ ਮਸ਼ੀਨ ਜਾਣਕਾਰੀ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/hostname/org.freedesktop.hostname1.policy:51
 msgid "Get product UUID"
@@ -291,21 +297,19 @@ msgstr ""
 
 #: src/hostname/org.freedesktop.hostname1.policy:61
 msgid "Get hardware serial number"
-msgstr ""
+msgstr "ਹਾਰਡਵੇਅਰ ਲੜੀ ਨੰਬਰ ਲਵੋ"
 
 #: src/hostname/org.freedesktop.hostname1.policy:62
-#, fuzzy
 msgid "Authentication is required to get hardware serial number."
-msgstr "ਸਿਸà¨\9fਮ à¨¸à©\87ਵਾਵਾà¨\82 à¨\9cਾà¨\82 à¨¯à©\82ਨਿà¨\9f à¨«à¨¼à¨¾à¨\87ਲਾà¨\82 à¨¦à¨¾ à¨\87ੰਤà©\9bਾਮ à¨\95ਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
+msgstr "ਹਾਰਡਵà©\87à¨\85ਰ à¨²à©\9cà©\80 à¨¨à©°à¨¬à¨° à¨²à©\88ਣ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/hostname/org.freedesktop.hostname1.policy:71
 msgid "Get system description"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਜਾਣਕਾਰੀ ਲਵੋ"
 
 #: src/hostname/org.freedesktop.hostname1.policy:72
-#, fuzzy
 msgid "Authentication is required to get system description."
-msgstr "ਸਿਸà¨\9fਮ à¨¸à©\87ਵਾਵਾà¨\82 à¨\9cਾà¨\82 à¨¯à©\82ਨਿà¨\9f à¨«à¨¼à¨¾à¨\87ਲਾà¨\82 à¨¦à¨¾ à¨\87ੰਤà©\9bਾਮ à¨\95ਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
+msgstr "ਸਿਸà¨\9fਮ à¨\9cਾਣà¨\95ਾਰà©\80 à¨²à©\88ਣ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/import/org.freedesktop.import1.policy:22
 msgid "Import a VM or container image"
@@ -333,19 +337,19 @@ msgstr ""
 
 #: src/locale/org.freedesktop.locale1.policy:22
 msgid "Set system locale"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਲੋਕੇਲ ਸੈੱਟ ਕਰੋ"
 
 #: src/locale/org.freedesktop.locale1.policy:23
 msgid "Authentication is required to set the system locale."
-msgstr ""
+msgstr "ਸਿਸਟਮ ਲੋਕੇਲ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/locale/org.freedesktop.locale1.policy:33
 msgid "Set system keyboard settings"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਕੀਬੋਰਡ ਸੈਟਿੰਗਾਂ ਸੈੱਟ ਕਰੋ"
 
 #: src/locale/org.freedesktop.locale1.policy:34
 msgid "Authentication is required to set the system keyboard settings."
-msgstr ""
+msgstr "ਸਿਸਟਮ ਕੀਬੋਰਡ ਸੈਟਿੰਗਾਂ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/login/org.freedesktop.login1.policy:22
 msgid "Allow applications to inhibit system shutdown"
@@ -357,7 +361,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:33
 msgid "Allow applications to delay system shutdown"
-msgstr ""
+msgstr "ਐਪਲੀਕੇਸ਼ਨਾਂ ਨੂੰ ਸਿਸਟਮ ਬੰਦ ਕਰਨ ਵਿੱਚ ਦੇਰੀ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ"
 
 #: src/login/org.freedesktop.login1.policy:34
 msgid "Authentication is required for an application to delay system shutdown."
@@ -534,7 +538,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:268
 msgid "Suspend the system"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਨੂੰ ਸਸਪੈਂਡ"
 
 #: src/login/org.freedesktop.login1.policy:269
 msgid "Authentication is required to suspend the system."
@@ -558,7 +562,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:300
 msgid "Hibernate the system"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਨੂੰ ਹਾਈਬਰਨੇਟ"
 
 #: src/login/org.freedesktop.login1.policy:301
 msgid "Authentication is required to hibernate the system."
@@ -590,7 +594,7 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:342
 msgid "Lock or unlock active sessions"
-msgstr ""
+msgstr "ਸਰਗਰਮ ਸ਼ੈਸ਼ਨਾਂ ਨੂੰ ਲਾਕ ਜਾਂ ਅਣ-ਲਾਕ"
 
 #: src/login/org.freedesktop.login1.policy:343
 msgid "Authentication is required to lock or unlock active sessions."
@@ -638,27 +642,27 @@ msgstr ""
 
 #: src/login/org.freedesktop.login1.policy:406
 msgid "Change Session"
-msgstr ""
+msgstr "ਸ਼ੈਸ਼ਨ ਨੂੰ ਬਦਲੋ"
 
 #: src/login/org.freedesktop.login1.policy:407
 msgid "Authentication is required to change the virtual terminal."
-msgstr ""
+msgstr "ਵਰਚੁਅਲ ਟਰਮੀਨਲ ਨੂੰ ਬਦਲਣ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
-msgstr ""
+msgstr "ਲੋਕਲ ਕਨਟੇਨਰ ਵਿੱਚ ਲਾਗਇਨ ਕਰੋ"
 
 #: src/machine/org.freedesktop.machine1.policy:23
 msgid "Authentication is required to log into a local container."
-msgstr ""
+msgstr "ਲੋਕਲ ਕਨਟੇਨਰ ਵਿੱਚ ਲਾਗਇਨ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/machine/org.freedesktop.machine1.policy:32
 msgid "Log into the local host"
-msgstr ""
+msgstr "ਲੋਕਲ ਹੋਸਟ ਵਿੱਚ ਲਾਗਇਨ ਕਰੋ"
 
 #: src/machine/org.freedesktop.machine1.policy:33
 msgid "Authentication is required to log into the local host."
-msgstr ""
+msgstr "ਲੋਕਲ ਹੋਸਟ ਵਿੱਚ ਲਾਗ ਇਨ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
 
 #: src/machine/org.freedesktop.machine1.policy:42
 msgid "Acquire a shell in a local container"
index 455a2368eb7aff0f949422be69be8a234bf06b10..33624f88a5ffe5a2586545405480e457ac88405f 100644 (file)
@@ -85,4 +85,7 @@ SUBSYSTEM=="misc", KERNEL=="rfkill", TAG+="systemd", ENV{SYSTEMD_WANTS}+="system
 SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount"
 SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount"
 
+# Pull in tpm2.target whenever /dev/tpmrm* shows up
+SUBSYSTEM=="tpmrm", KERNEL=="tpmrm[0-9]*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="tpm2.target"
+
 LABEL="systemd_end"
index 0146b50c78c765dac45588f4e74715cb9019a1c0..7d4f5498e840e1f832aa5b2f95b90044f3b83bc9 100644 (file)
@@ -94,6 +94,8 @@ int verb_image_policy(int argc, char *argv[], void *userdata) {
                         p = &image_policy_sysext_strict;
                 else if (streq(argv[i], "@confext"))
                         p = &image_policy_confext;
+                else if (streq(argv[i], "@confext-strict"))
+                        p = &image_policy_confext_strict;
                 else if (streq(argv[i], "@container"))
                         p = &image_policy_container;
                 else if (streq(argv[i], "@service"))
index 2f893e92226a07653330c118b36f01aba0b24315..6a2ad17fbf6880733ce4099f8d20089c40ab550d 100644 (file)
@@ -87,8 +87,8 @@ static int has_multiple_graphics_cards(void) {
 }
 
 static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
-        const char *subsystem, *sysname, *value;
         sd_device *parent;
+        const char *s;
         int r;
 
         assert(device);
@@ -98,34 +98,29 @@ static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
         if (r < 0)
                 return r;
 
-        r = sd_device_get_subsystem(parent, &subsystem);
-        if (r < 0)
-                return r;
+        if (device_in_subsystem(parent, "drm")) {
 
-        r = sd_device_get_sysname(parent, &sysname);
-        if (r < 0)
-                return r;
-
-        if (streq(subsystem, "drm")) {
-                const char *c;
+                r = sd_device_get_sysname(parent, &s);
+                if (r < 0)
+                        return r;
 
-                c = startswith(sysname, "card");
-                if (!c)
+                s = startswith(s, "card");
+                if (!s)
                         return -ENODATA;
 
-                c += strspn(c, DIGITS);
-                if (*c == '-' && !STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-", "-eDP-"))
+                s += strspn(s, DIGITS);
+                if (*s == '-' && !STARTSWITH_SET(s, "-LVDS-", "-Embedded DisplayPort-", "-eDP-"))
                         /* A connector DRM device, let's ignore all but LVDS and eDP! */
                         return -EOPNOTSUPP;
 
-        } else if (streq(subsystem, "pci") &&
-                   sd_device_get_sysattr_value(parent, "class", &value) >= 0) {
+        } else if (device_in_subsystem(parent, "pci") &&
+                   sd_device_get_sysattr_value(parent, "class", &s)) {
+
                 unsigned long class;
 
-                r = safe_atolu(value, &class);
+                r = safe_atolu(s, &class);
                 if (r < 0)
-                        return log_warning_errno(r, "Cannot parse PCI class '%s' of device %s:%s: %m",
-                                                 value, subsystem, sysname);
+                        return log_device_warning_errno(parent, r, "Cannot parse PCI class '%s': %m", s);
 
                 /* Graphics card */
                 if (class == PCI_CLASS_GRAPHICS_CARD) {
@@ -133,7 +128,7 @@ static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
                         return 0;
                 }
 
-        } else if (streq(subsystem, "platform")) {
+        } else if (device_in_subsystem(parent, "platform")) {
                 *ret = parent;
                 return 0;
         }
@@ -172,7 +167,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, *sysname, *subsystem;
+        const char *v, *sysname;
         sd_device *parent;
         int r;
 
@@ -191,11 +186,8 @@ static int validate_device(sd_device *device) {
         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 log_device_debug_errno(device, r, "Failed to get subsystem: %m");
-        if (!streq(subsystem, "backlight"))
-                return true;
+        if (!device_in_subsystem(device, "backlight"))
+                return true; /* We assume LED device is always valid. */
 
         r = sd_device_get_sysattr_value(device, "type", &v);
         if (r < 0)
@@ -207,15 +199,12 @@ static int validate_device(sd_device *device) {
         if (r < 0)
                 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 log_device_debug_errno(parent, r, "Failed to get subsystem: %m");
-
         if (DEBUG_LOGGING) {
-                const char *s = NULL;
+                const char *s = NULL, *subsystem = NULL;
 
                 (void) sd_device_get_syspath(parent, &s);
-                log_device_debug(device, "Found %s parent device: %s", subsystem, strna(s));
+                (void) sd_device_get_subsystem(parent, &subsystem);
+                log_device_debug(device, "Found %s parent device: %s", strna(subsystem), strna(s));
         }
 
         r = sd_device_enumerator_new(&enumerate);
@@ -242,7 +231,7 @@ static int validate_device(sd_device *device) {
         if (r < 0)
                 return log_debug_errno(r, "Failed to add sysattr match: %m");
 
-        if (streq(subsystem, "pci")) {
+        if (device_in_subsystem(parent, "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");
@@ -260,7 +249,6 @@ static int validate_device(sd_device *device) {
         }
 
         FOREACH_DEVICE(enumerate, other) {
-                const char *other_subsystem;
                 sd_device *other_parent;
 
                 /* OK, so there's another backlight device, and it's a platform or firmware device.
@@ -285,13 +273,7 @@ static int validate_device(sd_device *device) {
                         return false;
                 }
 
-                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")) {
+                if (device_in_subsystem(other_parent, "platform") && device_in_subsystem(parent, "pci")) {
                         /* The other is connected to the platform bus and we are a PCI device, that also means we are out. */
                         if (DEBUG_LOGGING) {
                                 const char *other_sysname = NULL, *other_type = NULL;
@@ -347,8 +329,6 @@ static int clamp_brightness(
                 unsigned *brightness) {
 
         unsigned new_brightness, min_brightness;
-        const char *subsystem;
-        int r;
 
         assert(device);
         assert(brightness);
@@ -358,14 +338,9 @@ static int clamp_brightness(
          * avoids preserving an unreadably dim screen, which would otherwise force the user to disable
          * state restoration. */
 
-        r = sd_device_get_subsystem(device, &subsystem);
-        if (r < 0)
-                return log_device_warning_errno(device, r, "Failed to get device subsystem: %m");
-
-        if (streq(subsystem, "backlight"))
-                min_brightness = MAX(1U, (unsigned) ((double) max_brightness * percent / 100));
-        else
-                min_brightness = 0;
+        min_brightness = (unsigned) ((double) max_brightness * percent / 100);
+        if (device_in_subsystem(device, "backlight"))
+                min_brightness = MAX(1U, min_brightness);
 
         new_brightness = CLAMP(*brightness, min_brightness, max_brightness);
         if (new_brightness != *brightness)
@@ -380,19 +355,28 @@ static int clamp_brightness(
         return 0;
 }
 
-static bool shall_clamp(sd_device *d, unsigned *ret) {
-        const char *s;
+static bool shall_clamp(sd_device *device, unsigned *ret) {
+        const char *property, *s;
+        unsigned default_percent;
         int r;
 
-        assert(d);
+        assert(device);
         assert(ret);
 
-        r = sd_device_get_property_value(d, "ID_BACKLIGHT_CLAMP", &s);
+        if (device_in_subsystem(device, "backlight")) {
+                property = "ID_BACKLIGHT_CLAMP";
+                default_percent = 5;
+        } else {
+                property = "ID_LEDS_CLAMP";
+                default_percent = 0;
+        }
+
+        r = sd_device_get_property_value(device, property, &s);
         if (r < 0) {
                 if (r != -ENOENT)
-                        log_device_debug_errno(d, r, "Failed to get ID_BACKLIGHT_CLAMP property, ignoring: %m");
-                *ret = 5; /* defaults to 5% */
-                return true;
+                        log_device_debug_errno(device, r, "Failed to get %s property, ignoring: %m", property);
+                *ret = default_percent;
+                return default_percent > 0;
         }
 
         r = parse_boolean(s);
@@ -403,9 +387,9 @@ static bool shall_clamp(sd_device *d, unsigned *ret) {
 
         r = parse_percent(s);
         if (r < 0) {
-                log_device_debug_errno(d, r, "Failed to parse ID_BACKLIGHT_CLAMP property, ignoring: %m");
-                *ret = 5;
-                return true;
+                log_device_debug_errno(device, r, "Failed to parse %s property, ignoring: %m", property);
+                *ret = default_percent;
+                return default_percent > 0;
         }
 
         *ret = r;
@@ -413,18 +397,14 @@ static bool shall_clamp(sd_device *d, unsigned *ret) {
 }
 
 static int read_brightness(sd_device *device, unsigned max_brightness, unsigned *ret_brightness) {
-        const char *subsystem, *value;
+        const char *value;
         unsigned brightness;
         int r;
 
         assert(device);
         assert(ret_brightness);
 
-        r = sd_device_get_subsystem(device, &subsystem);
-        if (r < 0)
-                return log_device_debug_errno(device, r, "Failed to get subsystem: %m");
-
-        if (streq(subsystem, "backlight")) {
+        if (device_in_subsystem(device, "backlight")) {
                 r = sd_device_get_sysattr_value(device, "actual_brightness", &value);
                 if (r == -ENOENT) {
                         log_device_debug_errno(device, r, "Failed to read 'actual_brightness' attribute, "
index 9904e3e48409182e43e4ec6c79f69294dd93a1c1..1a279690d245c678b6e11612adb697e52584b6b5 100644 (file)
@@ -92,22 +92,22 @@ void safe_close_pair(int p[static 2]) {
         p[1] = safe_close(p[1]);
 }
 
-void close_many(const int fds[], size_t n_fd) {
-        assert(fds || n_fd <= 0);
+void close_many(const int fds[], size_t n_fds) {
+        assert(fds || n_fds == 0);
 
-        for (size_t i = 0; i < n_fd; i++)
-                safe_close(fds[i]);
+        FOREACH_ARRAY(fd, fds, n_fds)
+                safe_close(*fd);
 }
 
-void close_many_unset(int fds[], size_t n_fd) {
-        assert(fds || n_fd <= 0);
+void close_many_unset(int fds[], size_t n_fds) {
+        assert(fds || n_fds == 0);
 
-        for (size_t i = 0; i < n_fd; i++)
-                fds[i] = safe_close(fds[i]);
+        FOREACH_ARRAY(fd, fds, n_fds)
+                *fd = safe_close(*fd);
 }
 
 void close_many_and_free(int *fds, size_t n_fds) {
-        assert(fds || n_fds <= 0);
+        assert(fds || n_fds == 0);
 
         close_many(fds, n_fds);
         free(fds);
@@ -187,32 +187,32 @@ int fd_cloexec(int fd, bool cloexec) {
 }
 
 int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
-        int ret = 0, r;
+        int r = 0;
 
-        assert(n_fds == 0 || fds);
+        assert(fds || n_fds == 0);
 
-        for (size_t i = 0; i < n_fds; i++) {
-                if (fds[i] < 0) /* Skip gracefully over already invalidated fds */
+        FOREACH_ARRAY(fd, fds, n_fds) {
+                if (*fd < 0) /* Skip gracefully over already invalidated fds */
                         continue;
 
-                r = fd_cloexec(fds[i], cloexec);
-                if (r < 0 && ret >= 0) /* Continue going, but return first error */
-                        ret = r;
-                else
-                        ret = 1; /* report if we did anything */
+                RET_GATHER(r, fd_cloexec(*fd, cloexec));
+
+                if (r >= 0)
+                        r = 1; /* report if we did anything */
         }
 
-        return ret;
+        return r;
 }
 
-static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
-        assert(n_fdset == 0 || fdset);
+static bool fd_in_set(int fd, const int fds[], size_t n_fds) {
+        assert(fd >= 0);
+        assert(fds || n_fds == 0);
 
-        for (size_t i = 0; i < n_fdset; i++) {
-                if (fdset[i] < 0)
+        FOREACH_ARRAY(i, fds, n_fds) {
+                if (*i < 0)
                         continue;
 
-                if (fdset[i] == fd)
+                if (*i == fd)
                         return true;
         }
 
@@ -243,7 +243,7 @@ int get_max_fd(void) {
 static int close_all_fds_frugal(const int except[], size_t n_except) {
         int max_fd, r = 0;
 
-        assert(n_except == 0 || except);
+        assert(except || n_except == 0);
 
         /* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
          * and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
@@ -258,8 +258,7 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
          * spin the CPU for a long time. */
         if (max_fd > MAX_FD_LOOP_LIMIT)
                 return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
-                                       "Refusing to loop over %d potential fds.",
-                                       max_fd);
+                                       "Refusing to loop over %d potential fds.", max_fd);
 
         for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) {
                 int q;
@@ -268,8 +267,8 @@ static int close_all_fds_frugal(const int except[], size_t n_except) {
                         continue;
 
                 q = close_nointr(fd);
-                if (q < 0 && q != -EBADF && r >= 0)
-                        r = q;
+                if (q != -EBADF)
+                        RET_GATHER(r, q);
         }
 
         return r;
@@ -598,7 +597,7 @@ int move_fd(int from, int to, int cloexec) {
                 if (fl < 0)
                         return -errno;
 
-                cloexec = !!(fl & FD_CLOEXEC);
+                cloexec = FLAGS_SET(fl, FD_CLOEXEC);
         }
 
         r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
index 64918a4861325d72d4653ac2aa4709547282576e..4bdd61fe54350dce2408cd1e004241811b5d58de 100644 (file)
@@ -32,8 +32,8 @@ static inline int safe_close_above_stdio(int fd) {
         return safe_close(fd);
 }
 
-void close_many(const int fds[], size_t n_fd);
-void close_many_unset(int fds[], size_t n_fd);
+void close_many(const int fds[], size_t n_fds);
+void close_many_unset(int fds[], size_t n_fds);
 void close_many_and_free(int *fds, size_t n_fds);
 
 int fclose_nointr(FILE *f);
index 7e5a493f6b03b57e75c6be10368e588e39f75222..47ab0b81d887622cb769b229fa19da014e9a6205 100644 (file)
@@ -40,14 +40,14 @@ static void sigbus_push(void *addr) {
         }
 
         /* If we can't, make sure the queue size is out of bounds, to
-         * mark it as overflow */
+         * mark it as overflowed */
         for (;;) {
                 sig_atomic_t c;
 
                 __atomic_thread_fence(__ATOMIC_SEQ_CST);
                 c = n_sigbus_queue;
 
-                if (c > SIGBUS_QUEUE_MAX) /* already overflow */
+                if (c > SIGBUS_QUEUE_MAX) /* already overflowed */
                         return;
 
                 /* OK if we clobber c here, since we either immediately return
@@ -70,7 +70,7 @@ int sigbus_pop(void **ret) {
                 if (_likely_(c == 0))
                         return 0;
 
-                if (_unlikely_(c >= SIGBUS_QUEUE_MAX))
+                if (_unlikely_(c > SIGBUS_QUEUE_MAX))
                         return -EOVERFLOW;
 
                 for (u = 0; u < SIGBUS_QUEUE_MAX; u++) {
index a625e75bedf7d26cd4e182b585f0dbcafd1395b8..27d2c26e48e028a1f1febde2ce2f9121ed5c11e8 100644 (file)
@@ -47,6 +47,7 @@
 #define SPECIAL_TIME_SYNC_TARGET "time-sync.target"       /* LSB's $time */
 #define SPECIAL_TIME_SET_TARGET "time-set.target"
 #define SPECIAL_BASIC_TARGET "basic.target"
+#define SPECIAL_TPM2_TARGET "tpm2.target"
 
 /* LSB compatibility */
 #define SPECIAL_NETWORK_TARGET "network.target"           /* LSB's $network */
index 9bcd63d3e9461e270a4e44d70c124a1e13732fdc..51715668fe718f3447ae802c43a55cfd877728d2 100644 (file)
@@ -516,3 +516,25 @@ const char* inode_type_to_string(mode_t m) {
 
         return NULL;
 }
+
+mode_t inode_type_from_string(const char *s) {
+        if (!s)
+                return MODE_INVALID;
+
+        if (streq(s, "reg"))
+                return S_IFREG;
+        if (streq(s, "dir"))
+                return S_IFDIR;
+        if (streq(s, "lnk"))
+                return S_IFLNK;
+        if (streq(s, "chr"))
+                return S_IFCHR;
+        if (streq(s, "blk"))
+                return S_IFBLK;
+        if (streq(s, "fifo"))
+                return S_IFIFO;
+        if (streq(s, "sock"))
+                return S_IFSOCK;
+
+        return MODE_INVALID;
+}
index ae0aaf8f512a4051e482aef0233c61dd3649cfa4..cb736c36dd66311608211cebbc289e4c8303dd30 100644 (file)
@@ -114,3 +114,4 @@ int inode_compare_func(const struct stat *a, const struct stat *b);
 extern const struct hash_ops inode_hash_ops;
 
 const char* inode_type_to_string(mode_t m);
+mode_t inode_type_from_string(const char *s);
index 7329bfacdf05b91ca4ad2c0f8b320644163fc6b2..f6453990bd9e9b73edd02b2e639dcef3c7be2b04 100644 (file)
@@ -1519,3 +1519,26 @@ ssize_t strlevenshtein(const char *x, const char *y) {
 
         return t1[yl];
 }
+
+char *strrstr(const char *haystack, const char *needle) {
+        const char *f = NULL;
+        size_t l;
+
+        /* Like strstr() but returns the last rather than the first occurence of "needle" in "haystack". */
+
+        if (!haystack || !needle)
+                return NULL;
+
+        l = strlen(needle);
+
+        /* Special case: for the empty string we return the very last possible occurence, i.e. *after* the
+         * last char, not before. */
+        if (l == 0)
+                return strchr(haystack, 0);
+
+        for (const char *p = haystack; *p; p++)
+                if (strncmp(p, needle, l) == 0)
+                        f = p;
+
+        return (char*) f;
+}
index b6d8be30834e5adbfa357e80d8734bfea698c71b..bf427cd7f7a67144c5272504e93739c3ddf8acb0 100644 (file)
@@ -322,3 +322,5 @@ static inline int strdup_or_null(const char *s, char **ret) {
         *ret = c;
         return 1;
 }
+
+char *strrstr(const char *haystack, const char *needle);
index c2109d35bc25ed42e7898965512cc2a3bd87cab2..43a4f569bd25e492fd3f897058268b8f02972c14 100644 (file)
@@ -905,3 +905,13 @@ int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const
 }
 
 DEFINE_HASH_OPS_FULL(string_strv_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free);
+
+char* strv_endswith(const char *s, char **l) {
+        STRV_FOREACH(i, l) {
+                char *e = endswith(s, *i);
+                if (e)
+                        return (char*) e;
+        }
+
+        return NULL;
+}
index fec26167339dbd37e4c793331b54d9e1c2e46203..18df0f23f246fe3ff0c02d588da5843b1534abd0 100644 (file)
@@ -252,3 +252,5 @@ int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HA
 int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS);
 #define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
 #define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
+
+char* strv_endswith(const char *s, char **l);
index c8041c4bd1f4c67e3ad791d97769de8094232940..337b3eb4b557979b363bd771ba3927797ccc2e08 100644 (file)
@@ -374,14 +374,15 @@ int verb_status(int argc, char *argv[], void *userdata) {
                         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"                 },
-                        { EFI_STUB_FEATURE_RANDOM_SEED,               "Support for passing random seed to OS"                },
-                        { EFI_STUB_FEATURE_CMDLINE_ADDONS,            "Pick up .cmdline from addons"                         },
-                        { EFI_STUB_FEATURE_CMDLINE_SMBIOS,            "Pick up .cmdline from SMBIOS Type 11"                 },
-                        { EFI_STUB_FEATURE_DEVICETREE_ADDONS,         "Pick up .dtb from addons"                             },
+                        { 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_PICK_UP_CONFEXTS,          "Picks up configuration extension images from boot partition" },
+                        { EFI_STUB_FEATURE_THREE_PCRS,                "Measures kernel+command line+sysexts"                        },
+                        { EFI_STUB_FEATURE_RANDOM_SEED,               "Support for passing random seed to OS"                       },
+                        { EFI_STUB_FEATURE_CMDLINE_ADDONS,            "Pick up .cmdline from addons"                                },
+                        { EFI_STUB_FEATURE_CMDLINE_SMBIOS,            "Pick up .cmdline from SMBIOS Type 11"                        },
+                        { EFI_STUB_FEATURE_DEVICETREE_ADDONS,         "Pick up .dtb from addons"                                    },
                 };
                 _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;
index 5b90e17c41df2fd406e2856fe26f949631963df0..1408d74bb60b0bb03be0c8b30294bc9addf648ea 100644 (file)
@@ -305,6 +305,7 @@ EFI_STATUS pack_cpio(
                 EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
                 const char16_t *dropin_dir,
                 const char16_t *match_suffix,
+                const char16_t *exclude_suffix,
                 const char *target_dir_prefix,
                 uint32_t dir_mode,
                 uint32_t access_mode,
@@ -367,6 +368,8 @@ EFI_STATUS pack_cpio(
                         continue;
                 if (match_suffix && !endswith_no_case(dirent->FileName, match_suffix))
                         continue;
+                if (exclude_suffix && endswith_no_case(dirent->FileName, exclude_suffix))
+                        continue;
                 if (!is_ascii(dirent->FileName))
                         continue;
                 if (strlen16(dirent->FileName) > 255) /* Max filename size on Linux */
index 26851e3c158c08f063977eda935b810fb5b43d17..9d14fa15a2ad0570c319438a6149b2ea72c73381 100644 (file)
@@ -8,6 +8,7 @@ EFI_STATUS pack_cpio(
                 EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
                 const char16_t *dropin_dir,
                 const char16_t *match_suffix,
+                const char16_t *exclude_suffix,
                 const char *target_dir_prefix,
                 uint32_t dir_mode,
                 uint32_t access_mode,
index 7ef3e765446d372c812df2d975177c000b3d1de2..58586f942faa4ccb6bee87450be72e260944af76 100644 (file)
@@ -91,6 +91,7 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
                 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_PICK_UP_CONFEXTS |         /* We pick up configuration extensions from the boot partition */
                 EFI_STUB_FEATURE_THREE_PCRS |               /* We can measure kernel image, parameters and sysext */
                 EFI_STUB_FEATURE_RANDOM_SEED |              /* We pass a random seed to the kernel */
                 EFI_STUB_FEATURE_CMDLINE_ADDONS |           /* We pick up .cmdline addons */
@@ -497,8 +498,8 @@ static EFI_STATUS load_addons(
 }
 
 static EFI_STATUS run(EFI_HANDLE image) {
-        _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
-        size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
+        _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *confext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
+        size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, confext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
         void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL;
         char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL;
         _cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL;
@@ -510,7 +511,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
         _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons_global = NULL, *cmdline_addons_uki = NULL;
         int sections_measured = -1, parameters_measured = -1;
         _cleanup_free_ char *uname = NULL;
-        bool sysext_measured = false, m;
+        bool sysext_measured = false, confext_measured = false, m;
         uint64_t loader_features = 0;
         EFI_STATUS err;
 
@@ -660,8 +661,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
         export_variables(loaded_image);
 
         if (pack_cpio(loaded_image,
-                      NULL,
+                      /* dropin_dir= */ NULL,
                       u".cred",
+                      /* exclude_suffix= */ NULL,
                       ".extra/credentials",
                       /* dir_mode= */ 0500,
                       /* access_mode= */ 0400,
@@ -675,6 +677,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
         if (pack_cpio(loaded_image,
                       u"\\loader\\credentials",
                       u".cred",
+                      /* exclude_suffix= */ NULL,
                       ".extra/global_credentials",
                       /* dir_mode= */ 0500,
                       /* access_mode= */ 0400,
@@ -686,8 +689,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
                 parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
 
         if (pack_cpio(loaded_image,
-                      NULL,
-                      u".raw",
+                      /* dropin_dir= */ NULL,
+                      u".raw",         /* ideally we'd pick up only *.sysext.raw here, but for compat we pick up *.raw instead … */
+                      u".confext.raw", /* … but then exclude *.confext.raw again */
                       ".extra/sysext",
                       /* dir_mode= */ 0555,
                       /* access_mode= */ 0444,
@@ -698,6 +702,20 @@ static EFI_STATUS run(EFI_HANDLE image) {
                       &m) == EFI_SUCCESS)
                 sysext_measured = m;
 
+        if (pack_cpio(loaded_image,
+                      /* dropin_dir= */ NULL,
+                      u".confext.raw",
+                      /* exclude_suffix= */ NULL,
+                      ".extra/confext",
+                      /* dir_mode= */ 0555,
+                      /* access_mode= */ 0444,
+                      /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG,
+                      u"Configuration extension initrd",
+                      &confext_initrd,
+                      &confext_initrd_size,
+                      &m) == EFI_SUCCESS)
+                confext_measured = m;
+
         dt_size = szs[UNIFIED_SECTION_DTB];
         dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0;
 
@@ -728,6 +746,8 @@ static EFI_STATUS run(EFI_HANDLE image) {
                 (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0);
         if (sysext_measured)
                 (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM2_PCR_SYSEXTS, 0);
+        if (confext_measured)
+                (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0);
 
         /* If the PCR signature was embedded in the PE image, then let's wrap it in a cpio and also pass it
          * to the kernel, so that it can be read from /.extra/tpm2-pcr-signature.json. Note that this section
@@ -773,7 +793,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
         initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
 
         _cleanup_pages_ Pages initrd_pages = {};
-        if (credential_initrd || global_credential_initrd || sysext_initrd || pcrsig_initrd || pcrpkey_initrd) {
+        if (credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
                 /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
                 err = combine_initrd(
                                 initrd_base, initrd_size,
@@ -781,6 +801,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
                                         credential_initrd,
                                         global_credential_initrd,
                                         sysext_initrd,
+                                        confext_initrd,
                                         pcrsig_initrd,
                                         pcrpkey_initrd,
                                 },
@@ -788,10 +809,11 @@ static EFI_STATUS run(EFI_HANDLE image) {
                                         credential_initrd_size,
                                         global_credential_initrd_size,
                                         sysext_initrd_size,
+                                        confext_initrd_size,
                                         pcrsig_initrd_size,
                                         pcrpkey_initrd_size,
                                 },
-                                5,
+                                6,
                                 &initrd_pages, &initrd_size);
                 if (err != EFI_SUCCESS)
                         return err;
@@ -802,6 +824,7 @@ static EFI_STATUS run(EFI_HANDLE image) {
                 credential_initrd = mfree(credential_initrd);
                 global_credential_initrd = mfree(global_credential_initrd);
                 sysext_initrd = mfree(sysext_initrd);
+                confext_initrd = mfree(confext_initrd);
                 pcrsig_initrd = mfree(pcrsig_initrd);
                 pcrpkey_initrd = mfree(pcrpkey_initrd);
         }
index 5e66ef76b55909c2058ed0656fa1e81864759884..411f59d55de1dbc2de64fec7749d867c05e746cd 100644 (file)
@@ -33,6 +33,7 @@
 #include "process-util.h"
 #include "procfs-util.h"
 #include "restrict-ifaces.h"
+#include "set.h"
 #include "special.h"
 #include "stdio-util.h"
 #include "string-table.h"
@@ -189,6 +190,313 @@ void cgroup_context_init(CGroupContext *c) {
         };
 }
 
+int cgroup_context_add_io_device_weight_dup(CGroupContext *c, CGroupIODeviceWeight *w) {
+        _cleanup_free_ CGroupIODeviceWeight *n = NULL;
+
+        assert(c);
+        assert(w);
+
+        n = new0(CGroupIODeviceWeight, 1);
+        if (!n)
+                return -ENOMEM;
+
+        n->path = strdup(w->path);
+        if (!n->path)
+                return -ENOMEM;
+        n->weight = w->weight;
+
+        LIST_PREPEND(device_weights, c->io_device_weights, TAKE_PTR(n));
+        return 0;
+}
+
+int cgroup_context_add_io_device_limit_dup(CGroupContext *c, CGroupIODeviceLimit *l) {
+        _cleanup_free_ CGroupIODeviceLimit *n = NULL;
+
+        assert(c);
+        assert(l);
+
+        n = new0(CGroupIODeviceLimit, 1);
+        if (!l)
+                return -ENOMEM;
+
+        n->path = strdup(l->path);
+        if (!n->path)
+                return -ENOMEM;
+
+        for (CGroupIOLimitType type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++)
+                n->limits[type] = l->limits[type];
+
+        LIST_PREPEND(device_limits, c->io_device_limits, TAKE_PTR(n));
+        return 0;
+}
+
+int cgroup_context_add_io_device_latency_dup(CGroupContext *c, CGroupIODeviceLatency *l) {
+        _cleanup_free_ CGroupIODeviceLatency *n = NULL;
+
+        assert(c);
+        assert(l);
+
+        n = new0(CGroupIODeviceLatency, 1);
+        if (!n)
+                return -ENOMEM;
+
+        n->path = strdup(l->path);
+        if (!n->path)
+                return -ENOMEM;
+
+        n->target_usec = l->target_usec;
+
+        LIST_PREPEND(device_latencies, c->io_device_latencies, TAKE_PTR(n));
+        return 0;
+}
+
+int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
+        _cleanup_free_ CGroupBlockIODeviceWeight *n = NULL;
+
+        assert(c);
+        assert(w);
+
+        n = new0(CGroupBlockIODeviceWeight, 1);
+        if (!n)
+                return -ENOMEM;
+
+        n->path = strdup(w->path);
+        if (!n->path)
+                return -ENOMEM;
+
+        n->weight = w->weight;
+
+        LIST_PREPEND(device_weights, c->blockio_device_weights, TAKE_PTR(n));
+        return 0;
+}
+
+int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
+        _cleanup_free_ CGroupBlockIODeviceBandwidth *n = NULL;
+
+        assert(c);
+        assert(b);
+
+        n = new0(CGroupBlockIODeviceBandwidth, 1);
+        if (!n)
+                return -ENOMEM;
+
+        *n = (CGroupBlockIODeviceBandwidth) {
+                .rbps = b->rbps,
+                .wbps = b->wbps,
+        };
+
+        LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, TAKE_PTR(n));
+        return 0;
+}
+
+int cgroup_context_add_device_allow_dup(CGroupContext *c, CGroupDeviceAllow *a) {
+        _cleanup_free_ CGroupDeviceAllow *n = NULL;
+
+        assert(c);
+        assert(a);
+
+        n = new0(CGroupDeviceAllow, 1);
+        if (!n)
+                return -ENOMEM;
+
+        n->path = strdup(a->path);
+        if (!n->path)
+                return -ENOMEM;
+
+        n->permissions = a->permissions;
+
+        LIST_PREPEND(device_allow, c->device_allow, TAKE_PTR(n));
+        return 0;
+}
+
+static int cgroup_context_add_socket_bind_item_dup(CGroupContext *c, CGroupSocketBindItem *i, CGroupSocketBindItem *h) {
+        _cleanup_free_ CGroupSocketBindItem *n = NULL;
+
+        assert(c);
+        assert(i);
+
+        n = new0(CGroupSocketBindItem, 1);
+        if (!n)
+                return -ENOMEM;
+
+        *n = (CGroupSocketBindItem) {
+                .address_family = i->address_family,
+                .ip_protocol    = i->ip_protocol,
+                .nr_ports       = i->nr_ports,
+                .port_min       = i->port_min,
+        };
+
+        LIST_PREPEND(socket_bind_items, h, TAKE_PTR(n));
+        return 0;
+}
+
+int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, CGroupSocketBindItem *i) {
+        return cgroup_context_add_socket_bind_item_dup(c, i, c->socket_bind_allow);
+}
+
+int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, CGroupSocketBindItem *i) {
+        return cgroup_context_add_socket_bind_item_dup(c, i, c->socket_bind_deny);
+}
+
+int cgroup_context_copy(CGroupContext *dst, const CGroupContext *src) {
+        struct in_addr_prefix *i;
+        char *iface;
+        int r;
+
+        assert(src);
+        assert(dst);
+
+        dst->cpu_accounting = src->cpu_accounting;
+        dst->io_accounting = src->io_accounting;
+        dst->blockio_accounting = src->blockio_accounting;
+        dst->memory_accounting = src->memory_accounting;
+        dst->tasks_accounting = src->tasks_accounting;
+        dst->ip_accounting = src->ip_accounting;
+
+        dst->memory_oom_group = dst->memory_oom_group;
+
+        dst->cpu_weight = src->cpu_weight;
+        dst->startup_cpu_weight = src->startup_cpu_weight;
+        dst->cpu_quota_per_sec_usec = src->cpu_quota_per_sec_usec;
+        dst->cpu_quota_period_usec = src->cpu_quota_period_usec;
+
+        dst->cpuset_cpus = src->cpuset_cpus;
+        dst->startup_cpuset_cpus = src->startup_cpuset_cpus;
+        dst->cpuset_mems = src->cpuset_mems;
+        dst->startup_cpuset_mems = src->startup_cpuset_mems;
+
+        dst->io_weight = src->io_weight;
+        dst->startup_io_weight = src->startup_io_weight;
+
+        LIST_FOREACH_BACKWARDS(device_weights, w, LIST_FIND_TAIL(device_weights, src->io_device_weights)) {
+                r = cgroup_context_add_io_device_weight_dup(dst, w);
+                if (r < 0)
+                        return r;
+        }
+
+        LIST_FOREACH_BACKWARDS(device_limits, l, LIST_FIND_TAIL(device_limits, src->io_device_limits)) {
+                r = cgroup_context_add_io_device_limit_dup(dst, l);
+                if (r < 0)
+                        return r;
+        }
+
+        LIST_FOREACH_BACKWARDS(device_latencies, l, LIST_FIND_TAIL(device_latencies, src->io_device_latencies)) {
+                r = cgroup_context_add_io_device_latency_dup(dst, l);
+                if (r < 0)
+                        return r;
+        }
+
+        dst->default_memory_min = src->default_memory_min;
+        dst->default_memory_low = src->default_memory_low;
+        dst->default_startup_memory_low = src->default_startup_memory_low;
+        dst->memory_min = src->memory_min;
+        dst->memory_low = src->memory_low;
+        dst->startup_memory_low = src->startup_memory_low;
+        dst->memory_high = src->memory_high;
+        dst->startup_memory_high = src->startup_memory_high;
+        dst->memory_max = src->memory_max;
+        dst->startup_memory_max = src->startup_memory_max;
+        dst->memory_swap_max = src->memory_swap_max;
+        dst->startup_memory_swap_max = src->startup_memory_swap_max;
+        dst->memory_zswap_max = src->memory_zswap_max;
+        dst->startup_memory_zswap_max = src->startup_memory_zswap_max;
+
+        dst->default_memory_min_set = src->default_memory_min_set;
+        dst->default_memory_low_set = src->default_memory_low_set;
+        dst->default_startup_memory_low_set = src->default_startup_memory_low_set;
+        dst->memory_min_set = src->memory_min_set;
+        dst->memory_low_set = src->memory_low_set;
+        dst->startup_memory_low_set = src->startup_memory_low_set;
+        dst->startup_memory_high_set = src->startup_memory_high_set;
+        dst->startup_memory_max_set = src->startup_memory_max_set;
+        dst->startup_memory_swap_max_set = src->startup_memory_swap_max_set;
+        dst->startup_memory_zswap_max_set = src->startup_memory_zswap_max_set;
+
+        SET_FOREACH(i, src->ip_address_allow) {
+                r = in_addr_prefix_add(&dst->ip_address_allow, i);
+                if (r < 0)
+                        return r;
+        }
+
+        SET_FOREACH(i, src->ip_address_deny) {
+                r = in_addr_prefix_add(&dst->ip_address_deny, i);
+                if (r < 0)
+                        return r;
+        }
+
+        dst->ip_address_allow_reduced = src->ip_address_allow_reduced;
+        dst->ip_address_deny_reduced = src->ip_address_deny_reduced;
+
+        if (!strv_isempty(src->ip_filters_ingress)) {
+                dst->ip_filters_ingress = strv_copy(src->ip_filters_ingress);
+                if (!dst->ip_filters_ingress)
+                        return -ENOMEM;
+        }
+
+        if (!strv_isempty(src->ip_filters_egress)) {
+                dst->ip_filters_egress = strv_copy(src->ip_filters_egress);
+                if (!dst->ip_filters_egress)
+                        return -ENOMEM;
+        }
+
+        LIST_FOREACH_BACKWARDS(programs, l, LIST_FIND_TAIL(programs, src->bpf_foreign_programs)) {
+                r = cgroup_context_add_bpf_foreign_program_dup(dst, l);
+                if (r < 0)
+                        return r;
+        }
+
+        SET_FOREACH(iface, src->restrict_network_interfaces) {
+                r = set_put_strdup(&dst->restrict_network_interfaces, iface);
+                if (r < 0)
+                        return r;
+        }
+        dst->restrict_network_interfaces_is_allow_list = src->restrict_network_interfaces_is_allow_list;
+
+        dst->cpu_shares = src->cpu_shares;
+        dst->startup_cpu_shares = src->startup_cpu_shares;
+
+        dst->blockio_weight = src->blockio_weight;
+        dst->startup_blockio_weight = src->startup_blockio_weight;
+
+        LIST_FOREACH_BACKWARDS(device_weights, l, LIST_FIND_TAIL(device_weights, src->blockio_device_weights)) {
+                r = cgroup_context_add_block_io_device_weight_dup(dst, l);
+                if (r < 0)
+                        return r;
+        }
+
+        LIST_FOREACH_BACKWARDS(device_bandwidths, l, LIST_FIND_TAIL(device_bandwidths, src->blockio_device_bandwidths)) {
+                r = cgroup_context_add_block_io_device_bandwidth_dup(dst, l);
+                if (r < 0)
+                        return r;
+        }
+
+        dst->memory_limit = src->memory_limit;
+
+        dst->device_policy = src->device_policy;
+        LIST_FOREACH_BACKWARDS(device_allow, l, LIST_FIND_TAIL(device_allow, src->device_allow)) {
+                r = cgroup_context_add_device_allow_dup(dst, l);
+                if (r < 0)
+                        return r;
+        }
+
+        LIST_FOREACH_BACKWARDS(socket_bind_items, l, LIST_FIND_TAIL(socket_bind_items, src->socket_bind_allow)) {
+                r = cgroup_context_add_socket_bind_item_allow_dup(dst, l);
+                if (r < 0)
+                        return r;
+
+        }
+
+        LIST_FOREACH_BACKWARDS(socket_bind_items, l, LIST_FIND_TAIL(socket_bind_items, src->socket_bind_deny)) {
+                r = cgroup_context_add_socket_bind_item_deny_dup(dst, l);
+                if (r < 0)
+                        return r;
+        }
+
+        dst->tasks_max = src->tasks_max;
+
+        return 0;
+}
+
 void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
         assert(c);
         assert(a);
@@ -4247,6 +4555,57 @@ int unit_get_ip_accounting(
         return r;
 }
 
+static uint64_t unit_get_effective_limit_one(Unit *u, CGroupLimitType type) {
+        CGroupContext *cc;
+
+        assert(u);
+        assert(UNIT_HAS_CGROUP_CONTEXT(u));
+
+        if (unit_has_name(u, SPECIAL_ROOT_SLICE))
+                switch (type) {
+                        case CGROUP_LIMIT_MEMORY_MAX:
+                        case CGROUP_LIMIT_MEMORY_HIGH:
+                                return physical_memory();
+                        case CGROUP_LIMIT_TASKS_MAX:
+                                return system_tasks_max();
+                        default:
+                                assert_not_reached();
+                }
+
+        cc = unit_get_cgroup_context(u);
+        switch (type) {
+                /* Note: on legacy/hybrid hierarchies memory_max stays CGROUP_LIMIT_MAX unless configured
+                 * explicitly. Effective value of MemoryLimit= (cgroup v1) is not implemented. */
+                case CGROUP_LIMIT_MEMORY_MAX:
+                        return cc->memory_max;
+                case CGROUP_LIMIT_MEMORY_HIGH:
+                        return cc->memory_high;
+                case CGROUP_LIMIT_TASKS_MAX:
+                        return cgroup_tasks_max_resolve(&cc->tasks_max);
+                default:
+                        assert_not_reached();
+        }
+}
+
+int unit_get_effective_limit(Unit *u, CGroupLimitType type, uint64_t *ret) {
+        uint64_t infimum;
+
+        assert(u);
+        assert(ret);
+        assert(type >= 0);
+        assert(type < _CGROUP_LIMIT_TYPE_MAX);
+
+        if (!UNIT_HAS_CGROUP_CONTEXT(u))
+                return -EINVAL;
+
+        infimum = unit_get_effective_limit_one(u, type);
+        for (Unit *slice = UNIT_GET_SLICE(u); slice; slice = UNIT_GET_SLICE(slice))
+                infimum = MIN(infimum, unit_get_effective_limit_one(slice, type));
+
+        *ret = infimum;
+        return 0;
+}
+
 static int unit_get_io_accounting_raw(Unit *u, uint64_t ret[static _CGROUP_IO_ACCOUNTING_METRIC_MAX]) {
         static const char *const field_names[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
                 [CGROUP_IO_READ_BYTES]       = "rbytes=",
@@ -4667,3 +5026,11 @@ static const char* const cgroup_memory_accounting_metric_table[_CGROUP_MEMORY_AC
 };
 
 DEFINE_STRING_TABLE_LOOKUP(cgroup_memory_accounting_metric, CGroupMemoryAccountingMetric);
+
+static const char *const cgroup_limit_type_table[_CGROUP_LIMIT_TYPE_MAX] = {
+        [CGROUP_LIMIT_MEMORY_MAX]  = "EffectiveMemoryMax",
+        [CGROUP_LIMIT_MEMORY_HIGH] = "EffectiveMemoryHigh",
+        [CGROUP_LIMIT_TASKS_MAX]   = "EffectiveTasksMax",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(cgroup_limit_type, CGroupLimitType);
index f1b674b4b74710b3af4b7fb408afd6dcde3afc63..1d6938aa1812d4c055b9ef45c857c601f97942c3 100644 (file)
@@ -129,6 +129,7 @@ typedef enum CGroupPressureWatch {
         _CGROUP_PRESSURE_WATCH_INVALID = -EINVAL,
 } CGroupPressureWatch;
 
+/* When adding members make sure to update cgroup_context_copy() accordingly */
 struct CGroupContext {
         bool cpu_accounting;
         bool io_accounting;
@@ -276,6 +277,15 @@ typedef enum CGroupMemoryAccountingMetric {
         _CGROUP_MEMORY_ACCOUNTING_METRIC_INVALID = -EINVAL,
 } CGroupMemoryAccountingMetric;
 
+/* Used for limits whose value sets have infimum */
+typedef enum CGroupLimitType {
+        CGROUP_LIMIT_MEMORY_MAX,
+        CGROUP_LIMIT_MEMORY_HIGH,
+        CGROUP_LIMIT_TASKS_MAX,
+        _CGROUP_LIMIT_TYPE_MAX,
+        _CGROUP_LIMIT_INVALID = -EINVAL,
+} CGroupLimitType;
+
 typedef struct Unit Unit;
 typedef struct Manager Manager;
 typedef enum ManagerState ManagerState;
@@ -285,6 +295,7 @@ uint64_t cgroup_context_cpu_weight(CGroupContext *c, ManagerState state);
 usec_t cgroup_cpu_adjust_period(usec_t period, usec_t quota, usec_t resolution, usec_t max_period);
 
 void cgroup_context_init(CGroupContext *c);
+int cgroup_context_copy(CGroupContext *dst, const CGroupContext *src);
 void cgroup_context_done(CGroupContext *c);
 void cgroup_context_dump(Unit *u, FILE* f, const char *prefix);
 void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE *f);
@@ -309,6 +320,18 @@ static inline bool cgroup_context_want_memory_pressure(const CGroupContext *c) {
 int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
 int cgroup_context_add_or_update_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
 int cgroup_context_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
+int cgroup_context_add_io_device_limit_dup(CGroupContext *c, CGroupIODeviceLimit *l);
+int cgroup_context_add_io_device_weight_dup(CGroupContext *c, CGroupIODeviceWeight *w);
+int cgroup_context_add_io_device_latency_dup(CGroupContext *c, CGroupIODeviceLatency *l);
+int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, CGroupBlockIODeviceWeight *w);
+int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
+int cgroup_context_add_device_allow_dup(CGroupContext *c, CGroupDeviceAllow *a);
+int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, CGroupSocketBindItem *i);
+int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, CGroupSocketBindItem *i);
+
+static inline int cgroup_context_add_bpf_foreign_program_dup(CGroupContext *c, CGroupBPFForeignProgram *p) {
+        return cgroup_context_add_bpf_foreign_program(c, p->attach_type, p->bpffs_path);
+}
 
 void unit_modify_nft_set(Unit *u, bool add);
 
@@ -374,6 +397,7 @@ int unit_get_tasks_current(Unit *u, uint64_t *ret);
 int unit_get_cpu_usage(Unit *u, nsec_t *ret);
 int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret);
 int unit_get_ip_accounting(Unit *u, CGroupIPAccountingMetric metric, uint64_t *ret);
+int unit_get_effective_limit(Unit *u, CGroupLimitType type, uint64_t *ret);
 
 int unit_reset_cpu_accounting(Unit *u);
 void unit_reset_memory_accounting_last(Unit *u);
@@ -425,5 +449,8 @@ CGroupIPAccountingMetric cgroup_ip_accounting_metric_from_string(const char *s)
 const char* cgroup_io_accounting_metric_to_string(CGroupIOAccountingMetric m) _const_;
 CGroupIOAccountingMetric cgroup_io_accounting_metric_from_string(const char *s) _pure_;
 
+const char* cgroup_limit_type_to_string(CGroupLimitType m) _const_;
+CGroupLimitType cgroup_limit_type_from_string(const char *s) _pure_;
+
 const char* cgroup_memory_accounting_metric_to_string(CGroupMemoryAccountingMetric m) _const_;
 CGroupMemoryAccountingMetric cgroup_memory_accounting_metric_from_string(const char *s) _pure_;
index 745f5cc17c6ffbe7136a79134361873377e09673..a62133a4c21fbc662626b77984e7740ca2d15a03 100644 (file)
@@ -2933,6 +2933,180 @@ static int method_dump_unit_descriptor_store(sd_bus_message *message, void *user
         return method_generic_unit_operation(message, userdata, error, bus_service_method_dump_file_descriptor_store, 0);
 }
 
+static int aux_scope_from_message(Manager *m, sd_bus_message *message, Unit **ret_scope, sd_bus_error *error) {
+        _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+        _cleanup_free_ PidRef *pidrefs = NULL;
+        const char *name;
+        Unit *from, *scope;
+        PidRef *main_pid;
+        CGroupContext *cc;
+        size_t n_pids = 0;
+        uint64_t flags;
+        pid_t pid;
+        int r;
+
+        assert(ret_scope);
+
+        r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_creds_get_pid(creds, &pid);
+        if (r < 0)
+                return r;
+
+        from = manager_get_unit_by_pid(m, pid);
+        if (!from)
+                return sd_bus_error_set(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
+
+        if (!IN_SET(from->type, UNIT_SERVICE, UNIT_SCOPE))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Starting auxiliary scope is supported only for service and scope units, refusing.");
+
+        if (!unit_name_is_valid(from->id, UNIT_NAME_PLAIN))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Auxiliary scope can be started only for non-template service units and scope units, refusing.");
+
+        r = sd_bus_message_read(message, "s", &name);
+        if (r < 0)
+                return r;
+
+        if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Invalid name \"%s\" for auxiliary scope.", name);
+
+        if (unit_name_to_type(name) != UNIT_SCOPE)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Name \"%s\" of auxiliary scope doesn't have .scope suffix.", name);
+
+        main_pid = unit_main_pid(from);
+
+        r = sd_bus_message_enter_container(message, 'a', "h");
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                _cleanup_(pidref_done) PidRef p = PIDREF_NULL;
+                Unit *unit;
+                int fd;
+
+                r = sd_bus_message_read(message, "h", &fd);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                r = pidref_set_pidfd(&p, fd);
+                if (r < 0) {
+                        log_unit_warning_errno(from, r, "Failed to create process reference from PIDFD, ignoring: %m");
+                        continue;
+                }
+
+                unit = manager_get_unit_by_pidref(m, &p);
+                if (!unit) {
+                        log_unit_warning_errno(from, SYNTHETIC_ERRNO(ENOENT), "Failed to get unit from PIDFD, ingoring: %m");
+                        continue;
+                }
+
+                if (!streq(unit->id, from->id)) {
+                        log_unit_warning(from, "PID " PID_FMT " is not running in the same service as the calling process, ignoring.", p.pid);
+                        continue;
+                }
+
+                if (pidref_equal(main_pid, &p)) {
+                        log_unit_warning(from, "Main PID cannot be migrated into auxiliary scope, ignoring.");
+                        continue;
+                }
+
+                if (!GREEDY_REALLOC(pidrefs, n_pids+1))
+                        return -ENOMEM;
+
+                pidrefs[n_pids++] = TAKE_PIDREF(p);
+        }
+
+        if (n_pids == 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "No processes can be migrated to auxiliary scope.");
+
+        r = sd_bus_message_exit_container(message);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_read(message, "t", &flags);
+        if (r < 0)
+                return r;
+
+        if (flags != 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
+
+        r = manager_load_unit(m, name, NULL, error, &scope);
+        if (r < 0)
+                return r;
+
+        if (!unit_is_pristine(scope))
+                return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
+                                         "Unit %s was already loaded or has a fragment file.", name);
+
+        r = unit_set_slice(scope, UNIT_GET_SLICE(from));
+        if (r < 0)
+                return r;
+
+        cc = unit_get_cgroup_context(scope);
+
+        r = cgroup_context_copy(cc, unit_get_cgroup_context(from));
+        if (r < 0)
+                return r;
+
+        r = unit_make_transient(scope);
+        if (r < 0)
+                return r;
+
+        r = bus_unit_set_properties(scope, message, UNIT_RUNTIME, true, error);
+        if (r < 0)
+                return r;
+
+        FOREACH_ARRAY(p, pidrefs, n_pids) {
+                r = unit_pid_attachable(scope, p, error);
+                if (r < 0)
+                        return r;
+
+                r = unit_watch_pidref(scope, p, /* exclusive= */ false);
+                if (r < 0 && r != -EEXIST)
+                        return r;
+        }
+
+        /* Now load the missing bits of the unit we just created */
+        unit_add_to_load_queue(scope);
+        manager_dispatch_load_queue(m);
+
+        *ret_scope = TAKE_PTR(scope);
+
+        return 1;
+}
+
+static int method_start_aux_scope(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = ASSERT_PTR(userdata);
+        Unit *u = NULL; /* avoid false maybe-uninitialized warning */
+        int r;
+
+        assert(message);
+
+        r = mac_selinux_access_check(message, "start", error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_manage_units_async(m, message, error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        r = aux_scope_from_message(m, message, &u, error);
+        if (r < 0)
+                return r;
+
+        return bus_unit_queue_job(message, u, JOB_START, JOB_REPLACE, 0, error);
+}
+
 const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
@@ -3491,6 +3665,11 @@ const sd_bus_vtable bus_manager_vtable[] = {
                                 SD_BUS_RESULT("a(suuutuusu)", entries),
                                 method_dump_unit_descriptor_store,
                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("StartAuxiliaryScope",
+                                SD_BUS_ARGS("s", name, "ah", pidfds, "t", flags, "a(sv)", properties),
+                                SD_BUS_RESULT("o", job),
+                                method_start_aux_scope,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
 
         SD_BUS_SIGNAL_WITH_ARGS("UnitNew",
                                 SD_BUS_ARGS("s", id, "o", unit),
index 8b4983dcb5f9e874c9fd3a52c23b71eb3028e0e1..3aeb85e45248e146f4bbc6a9ee0adba6647f20a9 100644 (file)
@@ -1426,6 +1426,28 @@ static int property_get_io_counter(
         return sd_bus_message_append(reply, "t", value);
 }
 
+static int property_get_effective_limit(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        uint64_t value = CGROUP_LIMIT_MAX;
+        Unit *u = ASSERT_PTR(userdata);
+        ssize_t type;
+
+        assert(bus);
+        assert(reply);
+        assert(property);
+
+        assert_se((type = cgroup_limit_type_from_string(property)) >= 0);
+        (void) unit_get_effective_limit(u, type, &value);
+        return sd_bus_message_append(reply, "t", value);
+}
+
 int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
         _cleanup_set_free_ Set *pids = NULL;
@@ -1547,10 +1569,13 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
         SD_BUS_PROPERTY("MemorySwapPeak", "t", property_get_memory_accounting, 0, 0),
         SD_BUS_PROPERTY("MemoryZSwapCurrent", "t", property_get_memory_accounting, 0, 0),
         SD_BUS_PROPERTY("MemoryAvailable", "t", property_get_available_memory, 0, 0),
+        SD_BUS_PROPERTY("EffectiveMemoryMax", "t", property_get_effective_limit, 0, 0),
+        SD_BUS_PROPERTY("EffectiveMemoryHigh", "t", property_get_effective_limit, 0, 0),
         SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
         SD_BUS_PROPERTY("EffectiveCPUs", "ay", property_get_cpuset_cpus, 0, 0),
         SD_BUS_PROPERTY("EffectiveMemoryNodes", "ay", property_get_cpuset_mems, 0, 0),
         SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
+        SD_BUS_PROPERTY("EffectiveTasksMax", "t", property_get_effective_limit, 0, 0),
         SD_BUS_PROPERTY("IPIngressBytes", "t", property_get_ip_counter, 0, 0),
         SD_BUS_PROPERTY("IPIngressPackets", "t", property_get_ip_counter, 0, 0),
         SD_BUS_PROPERTY("IPEgressBytes", "t", property_get_ip_counter, 0, 0),
index f76db133b114da6d28dae5c33b7a4f7e4c6029db..3b3250f32ed045017454993e46277ca47ff81f37 100644 (file)
@@ -59,6 +59,7 @@
 #include "strv.h"
 #include "terminal-util.h"
 #include "utmp-wtmp.h"
+#include "vpick.h"
 
 #define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
 #define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
@@ -2859,13 +2860,33 @@ static bool insist_on_sandboxing(
         return false;
 }
 
-static int setup_ephemeral(const ExecContext *context, ExecRuntime *runtime) {
+static int setup_ephemeral(
+                const ExecContext *context,
+                ExecRuntime *runtime,
+                char **root_image,            /* both input and output! modified if ephemeral logic enabled */
+                char **root_directory) {      /* ditto */
+
         _cleanup_close_ int fd = -EBADF;
+        _cleanup_free_ char *new_root = NULL;
         int r;
 
+        assert(context);
+        assert(root_image);
+        assert(root_directory);
+
+        if (!*root_image && !*root_directory)
+                return 0;
+
         if (!runtime || !runtime->ephemeral_copy)
                 return 0;
 
+        assert(runtime->ephemeral_storage_socket[0] >= 0);
+        assert(runtime->ephemeral_storage_socket[1] >= 0);
+
+        new_root = strdup(runtime->ephemeral_copy);
+        if (!new_root)
+                return log_oom_debug();
+
         r = posix_lock(runtime->ephemeral_storage_socket[0], LOCK_EX);
         if (r < 0)
                 return log_debug_errno(r, "Failed to lock ephemeral storage socket: %m");
@@ -2876,28 +2897,23 @@ static int setup_ephemeral(const ExecContext *context, ExecRuntime *runtime) {
         if (fd >= 0)
                 /* We got an fd! That means ephemeral has already been set up, so nothing to do here. */
                 return 0;
-
         if (fd != -EAGAIN)
                 return log_debug_errno(fd, "Failed to receive file descriptor queued on ephemeral storage socket: %m");
 
-        log_debug("Making ephemeral snapshot of %s to %s",
-                  context->root_image ?: context->root_directory, runtime->ephemeral_copy);
+        if (*root_image) {
+                log_debug("Making ephemeral copy of %s to %s", *root_image, new_root);
 
-        if (context->root_image)
-                fd = copy_file(context->root_image, runtime->ephemeral_copy, O_EXCL, 0600,
-                               COPY_LOCK_BSD|COPY_REFLINK|COPY_CRTIME);
-        else
-                fd = btrfs_subvol_snapshot_at(AT_FDCWD, context->root_directory,
-                                              AT_FDCWD, runtime->ephemeral_copy,
-                                              BTRFS_SNAPSHOT_FALLBACK_COPY |
-                                              BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
-                                              BTRFS_SNAPSHOT_RECURSIVE |
-                                              BTRFS_SNAPSHOT_LOCK_BSD);
-        if (fd < 0)
-                return log_debug_errno(fd, "Failed to snapshot %s to %s: %m",
-                                       context->root_image ?: context->root_directory, runtime->ephemeral_copy);
+                fd = copy_file(*root_image,
+                               new_root,
+                               O_EXCL,
+                               0600,
+                               COPY_LOCK_BSD|
+                               COPY_REFLINK|
+                               COPY_CRTIME);
+                if (fd < 0)
+                        return log_debug_errno(fd, "Failed to copy image %s to %s: %m",
+                                               *root_image, new_root);
 
-        if (context->root_image) {
                 /* A root image might be subject to lots of random writes so let's try to disable COW on it
                  * which tends to not perform well in combination with lots of random writes.
                  *
@@ -2906,13 +2922,35 @@ static int setup_ephemeral(const ExecContext *context, ExecRuntime *runtime) {
                  */
                 r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
                 if (r < 0)
-                        log_debug_errno(fd, "Failed to disable copy-on-write for %s, ignoring: %m", runtime->ephemeral_copy);
+                        log_debug_errno(fd, "Failed to disable copy-on-write for %s, ignoring: %m", new_root);
+        } else {
+                assert(*root_directory);
+
+                log_debug("Making ephemeral snapshot of %s to %s", *root_directory, new_root);
+
+                fd = btrfs_subvol_snapshot_at(
+                                AT_FDCWD, *root_directory,
+                                AT_FDCWD, new_root,
+                                BTRFS_SNAPSHOT_FALLBACK_COPY |
+                                BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
+                                BTRFS_SNAPSHOT_RECURSIVE |
+                                BTRFS_SNAPSHOT_LOCK_BSD);
+                if (fd < 0)
+                        return log_debug_errno(fd, "Failed to snapshot directory %s to %s: %m",
+                                               *root_directory, new_root);
         }
 
         r = send_one_fd(runtime->ephemeral_storage_socket[1], fd, MSG_DONTWAIT);
         if (r < 0)
                 return log_debug_errno(r, "Failed to queue file descriptor on ephemeral storage socket: %m");
 
+        if (*root_image)
+                free_and_replace(*root_image, new_root);
+        else {
+                assert(*root_directory);
+                free_and_replace(*root_directory, new_root);
+        }
+
         return 1;
 }
 
@@ -2972,6 +3010,63 @@ static int verity_settings_prepare(
         return 0;
 }
 
+static int pick_versions(
+                const ExecContext *context,
+                const ExecParameters *params,
+                char **ret_root_image,
+                char **ret_root_directory) {
+
+        int r;
+
+        assert(context);
+        assert(params);
+        assert(ret_root_image);
+        assert(ret_root_directory);
+
+        if (context->root_image) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+
+                r = path_pick(/* toplevel_path= */ NULL,
+                              /* toplevel_fd= */ AT_FDCWD,
+                              context->root_image,
+                              &pick_filter_image_raw,
+                              PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+                              &result);
+                if (r < 0)
+                        return r;
+
+                if (!result.path)
+                        return log_exec_debug_errno(context, params, SYNTHETIC_ERRNO(ENOENT), "No matching entry in .v/ directory %s found.", context->root_image);
+
+                *ret_root_image = TAKE_PTR(result.path);
+                *ret_root_directory = NULL;
+                return r;
+        }
+
+        if (context->root_directory) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+
+                r = path_pick(/* toplevel_path= */ NULL,
+                              /* toplevel_fd= */ AT_FDCWD,
+                              context->root_directory,
+                              &pick_filter_image_dir,
+                              PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+                              &result);
+                if (r < 0)
+                        return r;
+
+                if (!result.path)
+                        return log_exec_debug_errno(context, params, SYNTHETIC_ERRNO(ENOENT), "No matching entry in .v/ directory %s found.", context->root_directory);
+
+                *ret_root_image = NULL;
+                *ret_root_directory = TAKE_PTR(result.path);
+                return r;
+        }
+
+        *ret_root_image = *ret_root_directory = NULL;
+        return 0;
+}
+
 static int apply_mount_namespace(
                 ExecCommandFlags command_flags,
                 const ExecContext *context,
@@ -2984,8 +3079,8 @@ static int apply_mount_namespace(
         _cleanup_strv_free_ char **empty_directories = NULL, **symlinks = NULL,
                         **read_write_paths_cleanup = NULL;
         _cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL,
-                        *extension_dir = NULL, *host_os_release_stage = NULL;
-        const char *root_dir = NULL, *root_image = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL;
+                *extension_dir = NULL, *host_os_release_stage = NULL, *root_image = NULL, *root_dir = NULL;
+        const char *tmp_dir = NULL, *var_tmp_dir = NULL;
         char **read_write_paths;
         bool needs_sandboxing, setup_os_release_symlink;
         BindMount *bind_mounts = NULL;
@@ -2997,14 +3092,21 @@ static int apply_mount_namespace(
         CLEANUP_ARRAY(bind_mounts, n_bind_mounts, bind_mount_free_many);
 
         if (params->flags & EXEC_APPLY_CHROOT) {
-                r = setup_ephemeral(context, runtime);
+                r = pick_versions(
+                                context,
+                                params,
+                                &root_image,
+                                &root_dir);
                 if (r < 0)
                         return r;
 
-                if (context->root_image)
-                        root_image = (runtime ? runtime->ephemeral_copy : NULL) ?: context->root_image;
-                else
-                        root_dir = (runtime ? runtime->ephemeral_copy : NULL) ?: context->root_directory;
+                r = setup_ephemeral(
+                                context,
+                                runtime,
+                                &root_image,
+                                &root_dir);
+                if (r < 0)
+                        return r;
         }
 
         r = compile_bind_mounts(context, params, &bind_mounts, &n_bind_mounts, &empty_directories);
index 571683cd4b33f287706062a4eca292ef6be49955..10af26888e5bc2bca15205868a537252516bb196 100644 (file)
@@ -48,6 +48,7 @@
 #include "tmpfile-util.h"
 #include "uid-alloc-range.h"
 #include "user-util.h"
+#include "vpick.h"
 
 static enum {
         ACTION_DISSECT,
@@ -1741,26 +1742,13 @@ static int action_detach(const char *path) {
 
                 FOREACH_DEVICE(e, d) {
                         _cleanup_(loop_device_unrefp) LoopDevice *entry_loop = NULL;
-                        const char *name, *devtype;
 
-                        r = sd_device_get_sysname(d, &name);
-                        if (r < 0) {
-                                log_warning_errno(r, "Failed to get enumerated device's sysname, skipping: %m");
-                                continue;
-                        }
-
-                        r = sd_device_get_devtype(d, &devtype);
-                        if (r < 0) {
-                                log_warning_errno(r, "Failed to get devtype of '%s', skipping: %m", name);
-                                continue;
-                        }
-
-                        if (!streq(devtype, "disk")) /* Filter out partition block devices */
+                        if (!device_is_devtype(d, "disk")) /* Filter out partition block devices */
                                 continue;
 
                         r = loop_device_open(d, O_RDONLY, LOCK_SH, &entry_loop);
                         if (r < 0) {
-                                log_warning_errno(r, "Failed to open loopback block device '%s', skipping: %m", name);
+                                log_device_warning_errno(d, r, "Failed to open loopback block device, skipping: %m");
                                 continue;
                         }
 
@@ -1830,6 +1818,16 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
+        if (arg_image) {
+                r = path_pick_update_warn(
+                                &arg_image,
+                                &pick_filter_image_raw,
+                                PICK_ARCHITECTURE|PICK_TRIES,
+                                /* ret_result= */ NULL);
+                if (r < 0)
+                        return r;
+        }
+
         switch (arg_action) {
         case ACTION_UMOUNT:
                 return action_umount(arg_path);
index 2d25d2248f6f40f4223786eef0d6930489378074..01b18ec2416a6ab3879ebdb3777d6ef6544b1426 100644 (file)
@@ -33,6 +33,7 @@
 #define EFI_STUB_FEATURE_CMDLINE_ADDONS            (UINT64_C(1) << 5)
 #define EFI_STUB_FEATURE_CMDLINE_SMBIOS            (UINT64_C(1) << 6)
 #define EFI_STUB_FEATURE_DEVICETREE_ADDONS         (UINT64_C(1) << 7)
+#define EFI_STUB_FEATURE_PICK_UP_CONFEXTS          (UINT64_C(1) << 8)
 
 typedef enum SecureBootMode {
         SECURE_BOOT_UNSUPPORTED,
index 4389e9e37c5a54b50ff9cdb812e4ced80ae64c33..4447ad8a66074afa585e179185b45804f3051ec7 100644 (file)
 
 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 # define SWAP(n)                                                        \
-        (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+        __builtin_bswap32(n)
 # define SWAP64(n)                              \
-        (((n) << 56)                            \
-         | (((n) & 0xff00) << 40)               \
-         | (((n) & 0xff0000) << 24)             \
-         | (((n) & 0xff000000) << 8)            \
-         | (((n) >> 8) & 0xff000000)            \
-         | (((n) >> 24) & 0xff0000)             \
-         | (((n) >> 40) & 0xff00)               \
-         | ((n) >> 56))
+        __builtin_bswap64(n)
 #else
 # define SWAP(n) (n)
 # define SWAP64(n) (n)
index 201e14bc83bbd495cf53bdc758b96e6961df1ba5..a751a0ac4cdad9dc07963c494c40e2321f68bfda 100644 (file)
 #include "proc-cmdline.h"
 #include "efivars.h"
 
+typedef struct KernelHibernateLocation {
+        char *device;
+        uint64_t offset;
+        bool offset_set;
+} KernelHibernateLocation;
+
 static KernelHibernateLocation* kernel_hibernate_location_free(KernelHibernateLocation *k) {
         if (!k)
                 return NULL;
@@ -22,6 +28,19 @@ static KernelHibernateLocation* kernel_hibernate_location_free(KernelHibernateLo
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(KernelHibernateLocation*, kernel_hibernate_location_free);
 
+typedef struct EFIHibernateLocation {
+        char *device;
+
+        sd_id128_t uuid;
+        uint64_t offset;
+
+        char *kernel_version;
+        char *id;
+        char *image_id;
+        char *version_id;
+        char *image_version;
+} EFIHibernateLocation;
+
 static EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e) {
         if (!e)
                 return NULL;
index be759ec5d6bff5360df71ec958d6eccb54e583cc..c0aa9e00ad1348f53b51351124849ddb6995be46 100644 (file)
@@ -5,24 +5,8 @@
 
 #include "sd-id128.h"
 
-typedef struct KernelHibernateLocation {
-        char *device;
-        uint64_t offset;
-        bool offset_set;
-} KernelHibernateLocation;
-
-typedef struct EFIHibernateLocation {
-        char *device;
-
-        sd_id128_t uuid;
-        uint64_t offset;
-
-        char *kernel_version;
-        char *id;
-        char *image_id;
-        char *version_id;
-        char *image_version;
-} EFIHibernateLocation;
+typedef struct KernelHibernateLocation KernelHibernateLocation;
+typedef struct EFIHibernateLocation EFIHibernateLocation;
 
 typedef struct HibernateInfo {
         const char *device;
diff --git a/src/libsystemd-network/dhcp-client-id-internal.h b/src/libsystemd-network/dhcp-client-id-internal.h
new file mode 100644 (file)
index 0000000..72f13de
--- /dev/null
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp-client-id.h"
+
+#include "dhcp-duid-internal.h"
+#include "macro.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+
+/* RFC 2132 section 9.14: its minimum length is 2.
+ * Note, its maximum is not mentioend in the RFC. Hence, 255. */
+#define MIN_CLIENT_ID_LEN 2
+#define MAX_CLIENT_ID_LEN 255
+#define MIN_CLIENT_ID_DATA_LEN (MIN_CLIENT_ID_LEN - sizeof(uint8_t))
+#define MAX_CLIENT_ID_DATA_LEN (MAX_CLIENT_ID_LEN - sizeof(uint8_t))
+
+typedef struct sd_dhcp_client_id {
+        size_t size;
+        union {
+                struct {
+                        uint8_t type;
+                        union {
+                                struct {
+                                        /* 0: Generic (non-LL) (RFC 2132) */
+                                        uint8_t data[MAX_CLIENT_ID_DATA_LEN];
+                                } _packed_ gen;
+                                struct {
+                                        /* 1: Ethernet Link-Layer (RFC 2132) */
+                                        uint8_t haddr[ETH_ALEN];
+                                } _packed_ eth;
+                                struct {
+                                        /* 2 - 254: ARP/Link-Layer (RFC 2132) */
+                                        uint8_t haddr[0];
+                                } _packed_ ll;
+                                struct {
+                                        /* 255: Node-specific (RFC 4361) */
+                                        be32_t iaid;
+                                        struct duid duid;
+                                } _packed_ ns;
+                                uint8_t data[MAX_CLIENT_ID_DATA_LEN];
+                        };
+                } _packed_ id;
+                uint8_t raw[MAX_CLIENT_ID_LEN];
+        };
+} sd_dhcp_client_id;
+
+static inline bool client_id_size_is_valid(size_t size) {
+        return size >= MIN_CLIENT_ID_LEN && size <= MAX_CLIENT_ID_LEN;
+}
+
+static inline bool client_id_data_size_is_valid(size_t size) {
+        return size >= MIN_CLIENT_ID_DATA_LEN && size <= MAX_CLIENT_ID_DATA_LEN;
+}
+
+void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state);
+int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b);
similarity index 60%
rename from src/libsystemd-network/dhcp-identifier.h
rename to src/libsystemd-network/dhcp-duid-internal.h
index 96db5884e51069617b9b44bfdbe1ab08e4b534eb..f8bc15c47eeadbc6a56f1c9be0def833f5e1d86b 100644 (file)
@@ -2,31 +2,31 @@
 #pragma once
 
 #include "sd-device.h"
+#include "sd-dhcp-duid.h"
 #include "sd-id128.h"
 
 #include "ether-addr-util.h"
 #include "macro.h"
 #include "sparse-endian.h"
-#include "time-util.h"
-#include "unaligned.h"
 
 #define SYSTEMD_PEN    43793
 
 typedef enum DUIDType {
-        DUID_TYPE_LLT       = 1,
-        DUID_TYPE_EN        = 2,
-        DUID_TYPE_LL        = 3,
-        DUID_TYPE_UUID      = 4,
+        DUID_TYPE_LLT      = SD_DUID_TYPE_LLT,
+        DUID_TYPE_EN       = SD_DUID_TYPE_EN,
+        DUID_TYPE_LL       = SD_DUID_TYPE_LL,
+        DUID_TYPE_UUID     = SD_DUID_TYPE_UUID,
         _DUID_TYPE_MAX,
-        _DUID_TYPE_INVALID  = -EINVAL,
-        _DUID_TYPE_FORCE_U16 = UINT16_MAX,
+        _DUID_TYPE_INVALID = -EINVAL,
 } DUIDType;
 
 /* RFC 8415 section 11.1:
  * A DUID consists of a 2-octet type code represented in network byte order, followed by a variable number of
  * octets that make up the actual identifier. The length of the DUID (not including the type code) is at
  * least 1 octet and at most 128 octets. */
+#define MIN_DUID_DATA_LEN 1
 #define MAX_DUID_DATA_LEN 128
+#define MIN_DUID_LEN (sizeof(be16_t) + MIN_DUID_DATA_LEN)
 #define MAX_DUID_LEN (sizeof(be16_t) + MAX_DUID_DATA_LEN)
 
 /* https://tools.ietf.org/html/rfc3315#section-9.1 */
@@ -53,35 +53,31 @@ struct duid {
                         /* DUID_TYPE_UUID */
                         sd_id128_t uuid;
                 } _packed_ uuid;
-                struct {
-                        uint8_t data[MAX_DUID_DATA_LEN];
-                } _packed_ raw;
+                uint8_t data[MAX_DUID_DATA_LEN];
         };
 } _packed_;
 
-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);
-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);
-int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid_raw(
-                DUIDType duid_type,
-                const uint8_t *buf,
-                size_t buf_len,
-                struct duid *ret_duid,
-                size_t *ret_len);
+typedef struct sd_dhcp_duid {
+        size_t size;
+        union {
+                struct duid duid;
+                uint8_t raw[MAX_DUID_LEN];
+        };
+} sd_dhcp_duid;
+
+static inline bool duid_size_is_valid(size_t size) {
+        return size >= MIN_DUID_LEN && size <= MAX_DUID_LEN;
+}
+
+static inline bool duid_data_size_is_valid(size_t size) {
+        return size >= MIN_DUID_DATA_LEN && size <= MAX_DUID_DATA_LEN;
+}
+
+const char *duid_type_to_string(DUIDType t) _const_;
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret);
+
 int dhcp_identifier_set_iaid(
                 sd_device *dev,
                 const struct hw_addr_data *hw_addr,
                 bool legacy_unstable_byteorder,
                 void *ret);
-
-const char *duid_type_to_string(DUIDType t) _const_;
diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c
deleted file mode 100644 (file)
index f65cdbe..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <linux/if_infiniband.h>
-#include <net/ethernet.h>
-#include <net/if_arp.h>
-
-#include "dhcp-identifier.h"
-#include "netif-util.h"
-#include "network-common.h"
-#include "siphash24.h"
-#include "sparse-endian.h"
-#include "string-table.h"
-
-#define HASH_KEY       SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
-#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
-#define USEC_2000       ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
-
-static const char * const duid_type_table[_DUID_TYPE_MAX] = {
-        [DUID_TYPE_LLT]  = "DUID-LLT",
-        [DUID_TYPE_EN]   = "DUID-EN/Vendor",
-        [DUID_TYPE_LL]   = "DUID-LL",
-        [DUID_TYPE_UUID] = "UUID",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
-
-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(hw_addr);
-        assert(ret_duid);
-        assert(ret_len);
-
-        if (hw_addr->length == 0)
-                return -EOPNOTSUPP;
-
-        if (arp_type == ARPHRD_ETHER)
-                assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
-        else if (arp_type == ARPHRD_INFINIBAND)
-                assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
-        else
-                return -EOPNOTSUPP;
-
-        if (t < USEC_2000)
-                time_from_2000y = 0;
-        else
-                time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
-
-        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, hw_addr->bytes, hw_addr->length);
-
-        *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
-
-        return 0;
-}
-
-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 (hw_addr->length == 0)
-                return -EOPNOTSUPP;
-
-        if (arp_type == ARPHRD_ETHER)
-                assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
-        else if (arp_type == ARPHRD_INFINIBAND)
-                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, hw_addr->bytes, hw_addr->length);
-
-        *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
-
-        return 0;
-}
-
-int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len) {
-        sd_id128_t machine_id;
-        bool test_mode;
-        uint64_t hash;
-        int r;
-
-        assert(ret_duid);
-        assert(ret_len);
-
-        test_mode = network_test_mode_enabled();
-
-        if (!test_mode) {
-                r = sd_id128_get_machine(&machine_id);
-                if (r < 0)
-                        return r;
-        } else
-                /* For tests, especially for fuzzers, reproducibility is important.
-                 * Hence, use a static and constant machine ID.
-                 * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
-                machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
-
-        unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN);
-        unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN);
-
-        /* a bit of snake-oil perhaps, but no need to expose the machine-id
-         * directly; duid->en.id might not be aligned, so we need to copy */
-        hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
-        memcpy(ret_duid->en.id, &hash, sizeof(hash));
-
-        *ret_len = offsetof(struct duid, en.id) + sizeof(hash);
-
-        if (test_mode)
-                assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
-
-        return 0;
-}
-
-int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
-        sd_id128_t machine_id;
-        int r;
-
-        assert(ret_duid);
-        assert(ret_len);
-
-        r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
-        if (r < 0)
-                return r;
-
-        unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
-        memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id));
-
-        *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
-
-        return 0;
-}
-
-int dhcp_identifier_set_duid_raw(
-                DUIDType duid_type,
-                const uint8_t *buf,
-                size_t buf_len,
-                struct duid *ret_duid,
-                size_t *ret_len) {
-
-        assert(buf || buf_len == 0);
-        assert(ret_duid);
-        assert(ret_len);
-
-        if (duid_type < 0 || duid_type > UINT16_MAX)
-                return -EINVAL;
-
-        if (buf_len > MAX_DUID_DATA_LEN)
-                return -EINVAL;
-
-        unaligned_write_be16(&ret_duid->type, duid_type);
-        memcpy_safe(ret_duid->raw.data, buf, buf_len);
-
-        *ret_len = offsetof(struct duid, raw.data) + buf_len;
-        return 0;
-}
-
-int dhcp_identifier_set_iaid(
-                sd_device *dev,
-                const struct hw_addr_data *hw_addr,
-                bool legacy_unstable_byteorder,
-                void *ret) {
-
-        const char *name = NULL;
-        uint32_t id32;
-        uint64_t id;
-
-        assert(hw_addr);
-        assert(ret);
-
-        if (dev)
-                name = net_get_persistent_name(dev);
-        if (name)
-                id = siphash24(name, strlen(name), HASH_KEY.bytes);
-        else
-                /* fall back to MAC address if no predictable name available */
-                id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
-
-        id32 = (id & 0xffffffff) ^ (id >> 32);
-
-        if (legacy_unstable_byteorder)
-                /* for historical reasons (a bug), the bits were swapped and thus
-                 * the result was endianness dependent. Preserve that behavior. */
-                id32 = bswap_32(id32);
-        else
-                /* the fixed behavior returns a stable byte order. Since LE is expected
-                 * to be more common, swap the bytes on LE to give the same as legacy
-                 * behavior. */
-                id32 = be32toh(id32);
-
-        unaligned_write_ne32(ret, id32);
-        return 0;
-}
index a3d8bb45b397ae527e3d9ce29cfdb88f282394e0..b7bc142ef78f3652f77ff5f77da5bb7f52e7cfc8 100644 (file)
@@ -8,6 +8,7 @@
 #include "sd-dhcp-client.h"
 
 #include "alloc-util.h"
+#include "dhcp-client-id-internal.h"
 #include "dhcp-option.h"
 #include "list.h"
 #include "time-util.h"
@@ -67,8 +68,7 @@ struct sd_dhcp_lease {
         char *root_path;
         char *captive_portal;
 
-        void *client_id;
-        size_t client_id_len;
+        sd_dhcp_client_id client_id;
 
         void *vendor_specific;
         size_t vendor_specific_len;
@@ -92,7 +92,7 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const vo
 
 void dhcp_lease_set_timestamp(sd_dhcp_lease *lease, const triple_timestamp *timestamp);
 int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id *client_id);
 
 #define dhcp_lease_unref_and_replace(a, b)                              \
         unref_and_replace_full(a, b, sd_dhcp_lease_ref, sd_dhcp_lease_unref)
index da9e56b943f2d09c30faeec29284370c322de9f6..6e4404fb735fec9f086b4e1844e55b3e2ecd6d88 100644 (file)
@@ -8,6 +8,7 @@
 #include "sd-dhcp-server.h"
 #include "sd-event.h"
 
+#include "dhcp-client-id-internal.h"
 #include "dhcp-option.h"
 #include "network-common.h"
 #include "ordered-set.h"
@@ -24,25 +25,6 @@ typedef enum DHCPRawOption {
         _DHCP_RAW_OPTION_DATA_INVALID,
 } DHCPRawOption;
 
-typedef struct DHCPClientId {
-        size_t length;
-        uint8_t *data;
-} DHCPClientId;
-
-typedef struct DHCPLease {
-        sd_dhcp_server *server;
-
-        DHCPClientId client_id;
-
-        uint8_t htype; /* e.g. ARPHRD_ETHER */
-        uint8_t hlen;  /* e.g. ETH_ALEN */
-        be32_t address;
-        be32_t gateway;
-        uint8_t chaddr[16];
-        usec_t expiration;
-        char *hostname;
-} DHCPLease;
-
 struct sd_dhcp_server {
         unsigned n_ref;
 
@@ -100,7 +82,7 @@ typedef struct DHCPRequest {
         DHCPMessage *message;
 
         /* options */
-        DHCPClientId client_id;
+        sd_dhcp_client_id client_id;
         size_t max_optlen;
         be32_t server_id;
         be32_t requested_ip;
@@ -113,20 +95,12 @@ typedef struct DHCPRequest {
         triple_timestamp timestamp;
 } DHCPRequest;
 
-extern const struct hash_ops dhcp_lease_hash_ops;
-
 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                                size_t length, const triple_timestamp *timestamp);
 int dhcp_server_send_packet(sd_dhcp_server *server,
                             DHCPRequest *req, DHCPPacket *packet,
                             int type, size_t optoffset);
 
-void client_id_hash_func(const DHCPClientId *p, struct siphash *state);
-int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b);
-
-DHCPLease *dhcp_lease_free(DHCPLease *lease);
-DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, dhcp_lease_free);
-
 #define log_dhcp_server_errno(server, error, fmt, ...)          \
         log_interface_prefix_full_errno(                        \
                 "DHCPv4 server: ",                              \
diff --git a/src/libsystemd-network/dhcp-server-lease-internal.h b/src/libsystemd-network/dhcp-server-lease-internal.h
new file mode 100644 (file)
index 0000000..49bc173
--- /dev/null
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp-server-lease.h"
+
+#include "dhcp-client-id-internal.h"
+#include "dhcp-server-internal.h"
+#include "time-util.h"
+
+typedef struct sd_dhcp_server_lease {
+        unsigned n_ref;
+
+        sd_dhcp_server *server;
+
+        sd_dhcp_client_id client_id;
+
+        uint8_t htype; /* e.g. ARPHRD_ETHER */
+        uint8_t hlen;  /* e.g. ETH_ALEN */
+        be32_t address;
+        be32_t gateway;
+        uint8_t chaddr[16];
+        usec_t expiration;
+        char *hostname;
+} sd_dhcp_server_lease;
+
+extern const struct hash_ops dhcp_server_lease_hash_ops;
+
+int dhcp_server_put_lease(sd_dhcp_server *server, sd_dhcp_server_lease *lease, bool is_static);
+
+int dhcp_server_set_lease(sd_dhcp_server *server, be32_t address, DHCPRequest *req, usec_t expiration);
+int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server);
+
+sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req);
index e5b3b1302c1f1ff715ee627ceae4c000e2376704..3fbfc028e99e70ab83dc44cd1e081439d33d1fb6 100644 (file)
@@ -11,7 +11,7 @@
 #include "sd-event.h"
 #include "sd-dhcp6-client.h"
 
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
 #include "dhcp6-client-internal.h"
 #include "dhcp6-option.h"
 #include "dhcp6-protocol.h"
@@ -64,8 +64,7 @@ struct sd_dhcp6_client {
         DHCP6IA ia_na;
         DHCP6IA ia_pd;
         DHCP6RequestIA request_ia;
-        struct duid duid;
-        size_t duid_len;
+        sd_dhcp_duid duid;
         be16_t *req_opts;
         size_t n_req_opts;
         char *fqdn;
index fddb3a59ebef6137ac65828edaa2bbcdab02ed06..3e1efcf7fe550034c5930ebb5dff5d24395edd77 100644 (file)
@@ -18,16 +18,17 @@ ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
 }
 
 static int add_lease(sd_dhcp_server *server, const struct in_addr *server_address, uint8_t i) {
-        _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
+        _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
         int r;
 
         assert(server);
 
-        lease = new(DHCPLease, 1);
+        lease = new(sd_dhcp_server_lease, 1);
         if (!lease)
                 return -ENOMEM;
 
-        *lease = (DHCPLease) {
+        *lease = (sd_dhcp_server_lease) {
+                .n_ref = 1,
                 .address = htobe32(UINT32_C(10) << 24 | i),
                 .chaddr = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
                 .expiration = UINT64_MAX,
@@ -35,23 +36,13 @@ static int add_lease(sd_dhcp_server *server, const struct in_addr *server_addres
                 .hlen = ETH_ALEN,
                 .htype = ARPHRD_ETHER,
 
-                .client_id.length = 2,
+                .client_id.size = 2,
         };
 
-        lease->client_id.data = new(uint8_t, lease->client_id.length);
-        if (!lease->client_id.data)
-                return -ENOMEM;
-
-        lease->client_id.data[0] = 2;
-        lease->client_id.data[1] = i;
-
-        lease->server = server; /* This must be set just before hashmap_put(). */
-
-        r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
-        if (r < 0)
-                return r;
+        lease->client_id.raw[0] = 2;
+        lease->client_id.raw[1] = i;
 
-        r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+        r = dhcp_server_put_lease(server, lease, /* is_static = */ false);
         if (r < 0)
                 return r;
 
index 93186e23a1abffdc38a74f4366cae56c1916d86b..277935296fba923602dfd007d23f15767f835a8e 100644 (file)
@@ -2,7 +2,6 @@
 
 sources = files(
         'arp-util.c',
-        'dhcp-identifier.c',
         'dhcp-network.c',
         'dhcp-option.c',
         'dhcp-packet.c',
@@ -16,8 +15,11 @@ sources = files(
         'ndisc-router.c',
         'network-common.c',
         'network-internal.c',
+        'sd-dhcp-client-id.c',
         'sd-dhcp-client.c',
+        'sd-dhcp-duid.c',
         'sd-dhcp-lease.c',
+        'sd-dhcp-server-lease.c',
         'sd-dhcp-server.c',
         'sd-dhcp6-client.c',
         'sd-dhcp6-lease.c',
diff --git a/src/libsystemd-network/sd-dhcp-client-id.c b/src/libsystemd-network/sd-dhcp-client-id.c
new file mode 100644 (file)
index 0000000..6feec25
--- /dev/null
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "dhcp-client-id-internal.h"
+#include "unaligned.h"
+#include "utf8.h"
+
+int sd_dhcp_client_id_clear(sd_dhcp_client_id *client_id) {
+        assert_return(client_id, -EINVAL);
+
+        *client_id = (sd_dhcp_client_id) {};
+        return 0;
+}
+
+int sd_dhcp_client_id_is_set(const sd_dhcp_client_id *client_id) {
+        if (!client_id)
+                return false;
+
+        return client_id_size_is_valid(client_id->size);
+}
+
+int sd_dhcp_client_id_get(const sd_dhcp_client_id *client_id, uint8_t *ret_type, const void **ret_data, size_t *ret_size) {
+        assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL);
+        assert_return(ret_type, -EINVAL);
+        assert_return(ret_data, -EINVAL);
+        assert_return(ret_size, -EINVAL);
+
+        *ret_type = client_id->id.type;
+        *ret_data = client_id->id.data;
+        *ret_size = client_id->size - offsetof(typeof(client_id->id), data);
+        return 0;
+}
+
+int sd_dhcp_client_id_get_raw(const sd_dhcp_client_id *client_id, const void **ret_data, size_t *ret_size) {
+        assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL);
+        assert_return(ret_data, -EINVAL);
+        assert_return(ret_size, -EINVAL);
+
+        /* Unlike sd_dhcp_client_id_get(), this returns whole client ID including its type. */
+
+        *ret_data = client_id->raw;
+        *ret_size = client_id->size;
+        return 0;
+}
+
+int sd_dhcp_client_id_set(
+                sd_dhcp_client_id *client_id,
+                uint8_t type,
+                const void *data,
+                size_t data_size) {
+
+        assert_return(client_id, -EINVAL);
+        assert_return(data, -EINVAL);
+        assert_return(client_id_data_size_is_valid(data_size), -EINVAL);
+
+        client_id->id.type = type;
+        memcpy(client_id->id.data, data, data_size);
+
+        client_id->size = offsetof(typeof(client_id->id), data) + data_size;
+        return 0;
+}
+
+int sd_dhcp_client_id_set_raw(
+                sd_dhcp_client_id *client_id,
+                const void *data,
+                size_t data_size) {
+
+        assert_return(client_id, -EINVAL);
+        assert_return(data, -EINVAL);
+        assert_return(client_id_size_is_valid(data_size), -EINVAL);
+
+        /* Unlike sd_dhcp_client_id_set(), this takes whole client ID including its type. */
+
+        memcpy(client_id->raw, data, data_size);
+
+        client_id->size = data_size;
+        return 0;
+}
+
+int sd_dhcp_client_id_set_iaid_duid(
+                sd_dhcp_client_id *client_id,
+                uint32_t iaid,
+                sd_dhcp_duid *duid) {
+
+        assert_return(client_id, -EINVAL);
+        assert_return(duid, -EINVAL);
+        assert_return(sd_dhcp_duid_is_set(duid), -ESTALE);
+
+        client_id->id.type = 255;
+        unaligned_write_be32(&client_id->id.ns.iaid, iaid);
+        memcpy(&client_id->id.ns.duid, &duid->duid, duid->size);
+
+        client_id->size = offsetof(typeof(client_id->id), ns.duid) + duid->size;
+        return 0;
+}
+
+int sd_dhcp_client_id_to_string(const sd_dhcp_client_id *client_id, char **ret) {
+        _cleanup_free_ char *t = NULL;
+        size_t len;
+        int r;
+
+        assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        len = client_id->size - offsetof(typeof(client_id->id), data);
+
+        switch (client_id->id.type) {
+        case 0:
+                if (utf8_is_printable((char *) client_id->id.gen.data, len))
+                        r = asprintf(&t, "%.*s", (int) len, client_id->id.gen.data);
+                else
+                        r = asprintf(&t, "DATA");
+                break;
+        case 1:
+                if (len == sizeof_field(sd_dhcp_client_id, id.eth))
+                        r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
+                                     client_id->id.eth.haddr[0],
+                                     client_id->id.eth.haddr[1],
+                                     client_id->id.eth.haddr[2],
+                                     client_id->id.eth.haddr[3],
+                                     client_id->id.eth.haddr[4],
+                                     client_id->id.eth.haddr[5]);
+                else
+                        r = asprintf(&t, "ETHER");
+                break;
+        case 2 ... 254:
+                r = asprintf(&t, "ARP/LL");
+                break;
+        case 255:
+                if (len < sizeof(uint32_t))
+                        r = asprintf(&t, "IAID/DUID");
+                else {
+                        uint32_t iaid = be32toh(client_id->id.ns.iaid);
+                        /* TODO: check and stringify DUID */
+                        r = asprintf(&t, "IAID:0x%x/DUID", iaid);
+                }
+                break;
+        }
+        if (r < 0)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(t);
+        return 0;
+}
+
+int sd_dhcp_client_id_to_string_from_raw(const void *data, size_t data_size, char **ret) {
+        sd_dhcp_client_id client_id;
+        int r;
+
+        assert_return(data, -EINVAL);
+        assert_return(client_id_size_is_valid(data_size), -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = sd_dhcp_client_id_set_raw(&client_id, data, data_size);
+        if (r < 0)
+                return r;
+
+        return sd_dhcp_client_id_to_string(&client_id, ret);
+}
+
+void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state) {
+        assert(sd_dhcp_client_id_is_set(client_id));
+        assert(state);
+
+        siphash24_compress_typesafe(client_id->size, state);
+        siphash24_compress(client_id->raw, client_id->size, state);
+}
+
+int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b) {
+        assert(sd_dhcp_client_id_is_set(a));
+        assert(sd_dhcp_client_id_is_set(b));
+
+        return memcmp_nn(a->raw, a->size, b->raw, b->size);
+}
index 67911a2b93aed8a400a8fb4f0a32f3d542830ae1..dc1c17035d1ff01e20fb94b34737b3aba9b6570c 100644 (file)
@@ -15,8 +15,8 @@
 
 #include "alloc-util.h"
 #include "device-util.h"
+#include "dhcp-client-id-internal.h"
 #include "dhcp-client-internal.h"
-#include "dhcp-identifier.h"
 #include "dhcp-lease-internal.h"
 #include "dhcp-network.h"
 #include "dhcp-option.h"
@@ -39,7 +39,6 @@
 #include "utf8.h"
 #include "web-util.h"
 
-#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN)  /* Arbitrary limit */
 #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
 
 #define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
 #define TRANSIENT_FAILURE_ATTEMPTS 3 /* Arbitrary limit: how many attempts are considered enough to report
                                       * transient failure. */
 
-typedef struct sd_dhcp_client_id {
-        uint8_t type;
-        union {
-                struct {
-                        /* 0: Generic (non-LL) (RFC 2132) */
-                        uint8_t data[MAX_CLIENT_ID_LEN];
-                } _packed_ gen;
-                struct {
-                        /* 1: Ethernet Link-Layer (RFC 2132) */
-                        uint8_t haddr[ETH_ALEN];
-                } _packed_ eth;
-                struct {
-                        /* 2 - 254: ARP/Link-Layer (RFC 2132) */
-                        uint8_t haddr[0];
-                } _packed_ ll;
-                struct {
-                        /* 255: Node-specific (RFC 4361) */
-                        be32_t iaid;
-                        struct duid duid;
-                } _packed_ ns;
-                struct {
-                        uint8_t data[MAX_CLIENT_ID_LEN];
-                } _packed_ raw;
-        };
-} _packed_ sd_dhcp_client_id;
-
 struct sd_dhcp_client {
         unsigned n_ref;
 
@@ -100,7 +73,6 @@ struct sd_dhcp_client {
         struct hw_addr_data bcast_addr;
         uint16_t arp_type;
         sd_dhcp_client_id client_id;
-        size_t client_id_len;
         char *hostname;
         char *vendor_class_identifier;
         char *mudurl;
@@ -178,58 +150,6 @@ static int client_receive_message_udp(
                 void *userdata);
 static void client_stop(sd_dhcp_client *client, int error);
 
-int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
-        const sd_dhcp_client_id *client_id = data;
-        _cleanup_free_ char *t = NULL;
-        int r = 0;
-
-        assert_return(data, -EINVAL);
-        assert_return(len >= 1, -EINVAL);
-        assert_return(ret, -EINVAL);
-
-        len -= 1;
-        if (len > MAX_CLIENT_ID_LEN)
-                return -EINVAL;
-
-        switch (client_id->type) {
-        case 0:
-                if (utf8_is_printable((char *) client_id->gen.data, len))
-                        r = asprintf(&t, "%.*s", (int) len, client_id->gen.data);
-                else
-                        r = asprintf(&t, "DATA");
-                break;
-        case 1:
-                if (len == sizeof_field(sd_dhcp_client_id, eth))
-                        r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
-                                     client_id->eth.haddr[0],
-                                     client_id->eth.haddr[1],
-                                     client_id->eth.haddr[2],
-                                     client_id->eth.haddr[3],
-                                     client_id->eth.haddr[4],
-                                     client_id->eth.haddr[5]);
-                else
-                        r = asprintf(&t, "ETHER");
-                break;
-        case 2 ... 254:
-                r = asprintf(&t, "ARP/LL");
-                break;
-        case 255:
-                if (len < sizeof(uint32_t))
-                        r = asprintf(&t, "IAID/DUID");
-                else {
-                        uint32_t iaid = be32toh(client_id->ns.iaid);
-                        /* TODO: check and stringify DUID */
-                        r = asprintf(&t, "IAID:0x%x/DUID", iaid);
-                }
-                break;
-        }
-        if (r < 0)
-                return -ENOMEM;
-
-        *ret = TAKE_PTR(t);
-        return 0;
-}
-
 int dhcp_client_set_state_callback(
                 sd_dhcp_client *client,
                 sd_dhcp_client_callback_t cb,
@@ -360,34 +280,14 @@ int sd_dhcp_client_set_mac(
         return 0;
 }
 
-int sd_dhcp_client_get_client_id(
-                sd_dhcp_client *client,
-                uint8_t *ret_type,
-                const uint8_t **ret_data,
-                size_t *ret_data_len) {
-
+int sd_dhcp_client_get_client_id(sd_dhcp_client *client, const sd_dhcp_client_id **ret) {
         assert_return(client, -EINVAL);
+        assert_return(ret, -EINVAL);
 
-        if (client->client_id_len > 0) {
-                if (client->client_id_len <= offsetof(sd_dhcp_client_id, raw.data))
-                        return -EINVAL;
-
-                if (ret_type)
-                        *ret_type = client->client_id.type;
-                if (ret_data)
-                        *ret_data = client->client_id.raw.data;
-                if (ret_data_len)
-                        *ret_data_len = client->client_id_len - offsetof(sd_dhcp_client_id, raw.data);
-                return 1;
-        }
-
-        if (ret_type)
-                *ret_type = 0;
-        if (ret_data)
-                *ret_data = NULL;
-        if (ret_data_len)
-                *ret_data_len = 0;
+        if (!sd_dhcp_client_id_is_set(&client->client_id))
+                return -ENODATA;
 
+        *ret = &client->client_id;
         return 0;
 }
 
@@ -400,7 +300,7 @@ int sd_dhcp_client_set_client_id(
         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);
+        assert_return(client_id_data_size_is_valid(data_len), -EINVAL);
 
         /* For hardware types, log debug message about unexpected data length.
          *
@@ -413,42 +313,28 @@ int sd_dhcp_client_set_client_id(
                                 "Changing client ID to hardware type %u with unexpected address length %zu",
                                 type, data_len);
 
-        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);
-
-        return 0;
+        return sd_dhcp_client_id_set(&client->client_id, type, data, data_len);
 }
 
-/**
- * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid
- * without further modification. Otherwise, if duid_type is supported, DUID
- * is set based on that type. Otherwise, an error is returned.
- */
-static int dhcp_client_set_iaid(
+static int dhcp_client_set_iaid_duid(
                 sd_dhcp_client *client,
                 bool iaid_set,
-                uint32_t iaid) {
+                uint32_t iaid,
+                sd_dhcp_duid *duid) {
 
         int r;
 
-        assert_return(client, -EINVAL);
-        assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
-
-        zero(client->client_id);
-        client->client_id.type = 255;
-
-        if (iaid_set)
-                client->client_id.ns.iaid = htobe32(iaid);
-        else {
+        if (!iaid_set) {
                 r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr,
                                              /* legacy_unstable_byteorder = */ true,
-                                             &client->client_id.ns.iaid);
+                                             &iaid);
                 if (r < 0)
-                        return log_dhcp_client_errno(client, r, "Failed to set IAID: %m");
+                        return r;
+
+                iaid = be32toh(iaid);
         }
 
-        return 0;
+        return sd_dhcp_client_id_set_iaid_duid(&client->client_id, iaid, duid);
 }
 
 int sd_dhcp_client_set_iaid_duid_llt(
@@ -457,23 +343,17 @@ int sd_dhcp_client_set_iaid_duid_llt(
                 uint32_t iaid,
                 usec_t llt_time) {
 
-        size_t len;
+        sd_dhcp_duid duid;
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
-        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        r = sd_dhcp_duid_set_llt(&duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
         if (r < 0)
                 return r;
 
-        r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->client_id.ns.duid, &len);
-        if (r < 0)
-                return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
-
-        client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
-        return 0;
+        return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
 }
 
 int sd_dhcp_client_set_iaid_duid_ll(
@@ -481,23 +361,17 @@ int sd_dhcp_client_set_iaid_duid_ll(
                 bool iaid_set,
                 uint32_t iaid) {
 
-        size_t len;
+        sd_dhcp_duid duid;
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
-        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        r = sd_dhcp_duid_set_ll(&duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
         if (r < 0)
                 return r;
 
-        r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->client_id.ns.duid, &len);
-        if (r < 0)
-                return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
-
-        client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
-        return 0;
+        return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
 }
 
 int sd_dhcp_client_set_iaid_duid_en(
@@ -505,23 +379,17 @@ int sd_dhcp_client_set_iaid_duid_en(
                 bool iaid_set,
                 uint32_t iaid) {
 
-        size_t len;
+        sd_dhcp_duid duid;
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
-        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        r = sd_dhcp_duid_set_en(&duid);
         if (r < 0)
                 return r;
 
-        r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
-        if (r < 0)
-                return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
-
-        client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
-        return 0;
+        return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
 }
 
 int sd_dhcp_client_set_iaid_duid_uuid(
@@ -529,23 +397,17 @@ int sd_dhcp_client_set_iaid_duid_uuid(
                 bool iaid_set,
                 uint32_t iaid) {
 
-        size_t len;
+        sd_dhcp_duid duid;
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
 
-        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        r = sd_dhcp_duid_set_uuid(&duid);
         if (r < 0)
                 return r;
 
-        r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
-        if (r < 0)
-                return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
-
-        client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
-        return 0;
+        return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
 }
 
 int sd_dhcp_client_set_iaid_duid_raw(
@@ -553,27 +415,21 @@ int sd_dhcp_client_set_iaid_duid_raw(
                 bool iaid_set,
                 uint32_t iaid,
                 uint16_t duid_type,
-                const uint8_t *duid,
-                size_t duid_len) {
+                const uint8_t *duid_data,
+                size_t duid_data_len) {
 
-        size_t len;
+        sd_dhcp_duid duid;
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
-        assert_return(duid || duid_len == 0, -EINVAL);
+        assert_return(duid_data || duid_data_len == 0, -EINVAL);
 
-        r = dhcp_client_set_iaid(client, iaid_set, iaid);
+        r = sd_dhcp_duid_set(&duid, duid_type, duid_data, duid_data_len);
         if (r < 0)
                 return r;
 
-        r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->client_id.ns.duid, &len);
-        if (r < 0)
-                return log_dhcp_client_errno(client, r, "Failed to set DUID: %m");
-
-        client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
-        return 0;
+        return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
 }
 
 int sd_dhcp_client_set_rapid_commit(sd_dhcp_client *client, bool rapid_commit) {
@@ -921,8 +777,8 @@ static int client_message_init(
            Identifier option is not set */
         r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_CLIENT_IDENTIFIER,
-                               client->client_id_len,
-                               &client->client_id);
+                               client->client_id.size,
+                               client->client_id.raw);
         if (r < 0)
                 return r;
 
@@ -1579,10 +1435,8 @@ static int client_parse_message(
         if (r < 0)
                 return r;
 
-        if (client->client_id_len > 0) {
-                r = dhcp_lease_set_client_id(lease,
-                                             (uint8_t *) &client->client_id,
-                                             client->client_id_len);
+        if (sd_dhcp_client_id_is_set(&client->client_id)) {
+                r = dhcp_lease_set_client_id(lease, &client->client_id);
                 if (r < 0)
                         return r;
         }
@@ -2279,7 +2133,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
                 return r;
 
         /* If no client identifier exists, construct an RFC 4361-compliant one */
-        if (client->client_id_len == 0) {
+        if (!sd_dhcp_client_id_is_set(&client->client_id)) {
                 r = sd_dhcp_client_set_iaid_duid_en(client, /* iaid_set = */ false, /* iaid = */ 0);
                 if (r < 0)
                         return r;
diff --git a/src/libsystemd-network/sd-dhcp-duid.c b/src/libsystemd-network/sd-dhcp-duid.c
new file mode 100644 (file)
index 0000000..297deb8
--- /dev/null
@@ -0,0 +1,282 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <linux/if_infiniband.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+
+#include "dhcp-duid-internal.h"
+#include "hexdecoct.h"
+#include "netif-util.h"
+#include "network-common.h"
+#include "siphash24.h"
+#include "string-table.h"
+#include "unaligned.h"
+
+#define HASH_KEY       SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
+#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
+#define USEC_2000       ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
+
+static const char * const duid_type_table[_DUID_TYPE_MAX] = {
+        [DUID_TYPE_LLT]  = "DUID-LLT",
+        [DUID_TYPE_EN]   = "DUID-EN/Vendor",
+        [DUID_TYPE_LL]   = "DUID-LL",
+        [DUID_TYPE_UUID] = "UUID",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
+
+int sd_dhcp_duid_clear(sd_dhcp_duid *duid) {
+        assert_return(duid, -EINVAL);
+
+        *duid = (sd_dhcp_duid) {};
+        return 0;
+}
+
+int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid) {
+        if (!duid)
+                return false;
+
+        return duid_size_is_valid(duid->size);
+}
+
+int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size) {
+        assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+        assert_return(ret_type, -EINVAL);
+        assert_return(ret_data, -EINVAL);
+        assert_return(ret_size, -EINVAL);
+
+        *ret_type = be16toh(duid->duid.type);
+        *ret_data = duid->duid.data;
+        *ret_size = duid->size - offsetof(struct duid, data);
+        return 0;
+}
+
+int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size) {
+        assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+        assert_return(ret_data, -EINVAL);
+        assert_return(ret_size, -EINVAL);
+
+        /* Unlike sd_dhcp_duid_get(), this returns whole DUID including its type. */
+
+        *ret_data = duid->raw;
+        *ret_size = duid->size;
+        return 0;
+}
+
+int sd_dhcp_duid_set(
+                sd_dhcp_duid *duid,
+                uint16_t duid_type,
+                const void *data,
+                size_t data_size) {
+
+        assert_return(duid, -EINVAL);
+        assert_return(data, -EINVAL);
+        assert_return(duid_data_size_is_valid(data_size), -EINVAL);
+
+        unaligned_write_be16(&duid->duid.type, duid_type);
+        memcpy(duid->duid.data, data, data_size);
+
+        duid->size = offsetof(struct duid, data) + data_size;
+        return 0;
+}
+
+int sd_dhcp_duid_set_raw(
+                sd_dhcp_duid *duid,
+                const void *data,
+                size_t data_size) {
+
+        assert_return(duid, -EINVAL);
+        assert_return(data, -EINVAL);
+        assert_return(duid_size_is_valid(data_size), -EINVAL);
+
+        /* Unlike sd_dhcp_duid_set(), this takes whole DUID including its type. */
+
+        memcpy(duid->raw, data, data_size);
+
+        duid->size = data_size;
+        return 0;
+}
+
+int sd_dhcp_duid_set_llt(
+                sd_dhcp_duid *duid,
+                const void *hw_addr,
+                size_t hw_addr_size,
+                uint16_t arp_type,
+                uint64_t usec) {
+
+        uint16_t time_from_2000y;
+
+        assert_return(duid, -EINVAL);
+        assert_return(hw_addr, -EINVAL);
+
+        if (arp_type == ARPHRD_ETHER)
+                assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+        else if (arp_type == ARPHRD_INFINIBAND)
+                assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+        else
+                return -EOPNOTSUPP;
+
+        time_from_2000y = (uint16_t) ((usec_sub_unsigned(usec, USEC_2000) / USEC_PER_SEC) & 0xffffffff);
+
+        unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LLT);
+        unaligned_write_be16(&duid->duid.llt.htype, arp_type);
+        unaligned_write_be32(&duid->duid.llt.time, time_from_2000y);
+        memcpy(duid->duid.llt.haddr, hw_addr, hw_addr_size);
+
+        duid->size = offsetof(struct duid, llt.haddr) + hw_addr_size;
+        return 0;
+}
+
+int sd_dhcp_duid_set_ll(
+                sd_dhcp_duid *duid,
+                const void *hw_addr,
+                size_t hw_addr_size,
+                uint16_t arp_type) {
+
+        assert_return(duid, -EINVAL);
+        assert_return(hw_addr, -EINVAL);
+
+        if (arp_type == ARPHRD_ETHER)
+                assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+        else if (arp_type == ARPHRD_INFINIBAND)
+                assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+        else
+                return -EOPNOTSUPP;
+
+        unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LL);
+        unaligned_write_be16(&duid->duid.ll.htype, arp_type);
+        memcpy(duid->duid.ll.haddr, hw_addr, hw_addr_size);
+
+        duid->size = offsetof(struct duid, ll.haddr) + hw_addr_size;
+        return 0;
+}
+
+int sd_dhcp_duid_set_en(sd_dhcp_duid *duid) {
+        sd_id128_t machine_id;
+        bool test_mode;
+        uint64_t hash;
+        int r;
+
+        assert_return(duid, -EINVAL);
+
+        test_mode = network_test_mode_enabled();
+
+        if (!test_mode) {
+                r = sd_id128_get_machine(&machine_id);
+                if (r < 0)
+                        return r;
+        } else
+                /* For tests, especially for fuzzers, reproducibility is important.
+                 * Hence, use a static and constant machine ID.
+                 * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
+                machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
+
+        unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_EN);
+        unaligned_write_be32(&duid->duid.en.pen, SYSTEMD_PEN);
+
+        /* a bit of snake-oil perhaps, but no need to expose the machine-id
+         * directly; duid->en.id might not be aligned, so we need to copy */
+        hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
+        memcpy(duid->duid.en.id, &hash, sizeof(hash));
+
+        duid->size = offsetof(struct duid, en.id) + sizeof(hash);
+
+        if (test_mode)
+                assert_se(memcmp(&duid->duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, duid->size) == 0);
+
+        return 0;
+}
+
+int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid) {
+        sd_id128_t machine_id;
+        int r;
+
+        assert_return(duid, -EINVAL);
+
+        r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
+        if (r < 0)
+                return r;
+
+        unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_UUID);
+        memcpy(&duid->duid.uuid.uuid, &machine_id, sizeof(machine_id));
+
+        duid->size = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
+        return 0;
+}
+
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret) {
+        _cleanup_free_ char *p = NULL, *x = NULL;
+        const char *t;
+
+        assert(data);
+        assert(duid_data_size_is_valid(data_size));
+        assert(ret);
+
+        x = hexmem(data, data_size);
+        if (!x)
+                return -ENOMEM;
+
+        t = duid_type_to_string(type);
+        if (!t)
+                return asprintf(ret, "%04x:%s", htobe16(type), x);
+
+        p = strjoin(t, ":", x);
+        if (!p)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(p);
+        return 0;
+}
+
+int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret) {
+        uint16_t type;
+        const void *data;
+        size_t data_size;
+        int r;
+
+        assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = sd_dhcp_duid_get(duid, &type, &data, &data_size);
+        if (r < 0)
+                return r;
+
+        return dhcp_duid_to_string_internal(type, data, data_size, ret);
+}
+
+int dhcp_identifier_set_iaid(
+                sd_device *dev,
+                const struct hw_addr_data *hw_addr,
+                bool legacy_unstable_byteorder,
+                void *ret) {
+
+        const char *name = NULL;
+        uint32_t id32;
+        uint64_t id;
+
+        assert(hw_addr);
+        assert(ret);
+
+        if (dev)
+                name = net_get_persistent_name(dev);
+        if (name)
+                id = siphash24(name, strlen(name), HASH_KEY.bytes);
+        else
+                /* fall back to MAC address if no predictable name available */
+                id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
+
+        id32 = (id & 0xffffffff) ^ (id >> 32);
+
+        if (legacy_unstable_byteorder)
+                /* for historical reasons (a bug), the bits were swapped and thus
+                 * the result was endianness dependent. Preserve that behavior. */
+                id32 = bswap_32(id32);
+        else
+                /* the fixed behavior returns a stable byte order. Since LE is expected
+                 * to be more common, swap the bytes on LE to give the same as legacy
+                 * behavior. */
+                id32 = be32toh(id32);
+
+        unaligned_write_ne32(ret, id32);
+        return 0;
+}
index 4e3be98a33a2001890987c04c369ca30aa6e3d02..fd32fbb049028a2be1bc110de9418cb51c1bea5e 100644 (file)
@@ -420,7 +420,6 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
 
         free(lease->static_routes);
         free(lease->classless_routes);
-        free(lease->client_id);
         free(lease->vendor_specific);
         strv_free(lease->search_domains);
         free(lease->sixrd_br_addresses);
@@ -1066,8 +1065,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         _cleanup_fclose_ FILE *f = NULL;
         struct in_addr address;
         const struct in_addr *addresses;
-        const void *client_id, *data;
-        size_t client_id_len, data_len;
+        const void *data;
+        size_t data_len;
         const char *string;
         uint16_t mtu;
         _cleanup_free_ sd_dhcp_route **routes = NULL;
@@ -1183,11 +1182,10 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         if (r >= 0)
                 fprintf(f, "TIMEZONE=%s\n", string);
 
-        r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
-        if (r >= 0) {
+        if (sd_dhcp_client_id_is_set(&lease->client_id)) {
                 _cleanup_free_ char *client_id_hex = NULL;
 
-                client_id_hex = hexmem(client_id, client_id_len);
+                client_id_hex = hexmem(lease->client_id.raw, lease->client_id.size);
                 if (!client_id_hex)
                         return -ENOMEM;
                 fprintf(f, "CLIENTID=%s\n", client_id_hex);
@@ -1478,9 +1476,16 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
         }
 
         if (client_id_hex) {
-                r = unhexmem(client_id_hex, SIZE_MAX, &lease->client_id, &lease->client_id_len);
+                _cleanup_free_ void *data = NULL;
+                size_t data_size;
+
+                r = unhexmem(client_id_hex, SIZE_MAX, &data, &data_size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
+
+                r = sd_dhcp_client_id_set_raw(&lease->client_id, data, data_size);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to assign client ID, ignoring: %m");
         }
 
         if (vendor_specific_hex) {
@@ -1537,36 +1542,25 @@ int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
         return 0;
 }
 
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id **ret) {
         assert_return(lease, -EINVAL);
-        assert_return(client_id, -EINVAL);
-        assert_return(client_id_len, -EINVAL);
+        assert_return(ret, -EINVAL);
 
-        if (!lease->client_id)
+        if (!sd_dhcp_client_id_is_set(&lease->client_id))
                 return -ENODATA;
 
-        *client_id = lease->client_id;
-        *client_id_len = lease->client_id_len;
+        *ret = &lease->client_id;
 
         return 0;
 }
 
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) {
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id *client_id) {
         assert_return(lease, -EINVAL);
-        assert_return(client_id || client_id_len <= 0, -EINVAL);
-
-        if (client_id_len <= 0)
-                lease->client_id = mfree(lease->client_id);
-        else {
-                void *p;
 
-                p = memdup(client_id, client_id_len);
-                if (!p)
-                        return -ENOMEM;
+        if (!sd_dhcp_client_id_is_set(client_id))
+                return sd_dhcp_client_id_clear(&lease->client_id);
 
-                free_and_replace(lease->client_id, p);
-                lease->client_id_len = client_id_len;
-        }
+        lease->client_id = *client_id;
 
         return 0;
 }
diff --git a/src/libsystemd-network/sd-dhcp-server-lease.c b/src/libsystemd-network/sd-dhcp-server-lease.c
new file mode 100644 (file)
index 0000000..16880a8
--- /dev/null
@@ -0,0 +1,193 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "dhcp-server-lease-internal.h"
+
+static sd_dhcp_server_lease* dhcp_server_lease_free(sd_dhcp_server_lease *lease) {
+        if (!lease)
+                return NULL;
+
+        if (lease->server) {
+                hashmap_remove_value(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
+                hashmap_remove_value(lease->server->bound_leases_by_client_id, &lease->client_id, lease);
+                hashmap_remove_value(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address), lease);
+                hashmap_remove_value(lease->server->static_leases_by_client_id, &lease->client_id, lease);
+        }
+
+        free(lease->hostname);
+        return mfree(lease);
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server_lease, sd_dhcp_server_lease, dhcp_server_lease_free);
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        dhcp_server_lease_hash_ops,
+        sd_dhcp_client_id,
+        client_id_hash_func,
+        client_id_compare_func,
+        sd_dhcp_server_lease,
+        sd_dhcp_server_lease_unref);
+
+int dhcp_server_put_lease(sd_dhcp_server *server, sd_dhcp_server_lease *lease, bool is_static) {
+        int r;
+
+        assert(server);
+        assert(lease);
+
+        lease->server = server; /* This must be set before hashmap_put(). */
+
+        r = hashmap_ensure_put(is_static ? &server->static_leases_by_client_id : &server->bound_leases_by_client_id,
+                               &dhcp_server_lease_hash_ops, &lease->client_id, lease);
+        if (r < 0)
+                return r;
+
+        r = hashmap_ensure_put(is_static ? &server->static_leases_by_address : &server->bound_leases_by_address,
+                               NULL, UINT32_TO_PTR(lease->address), lease);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int dhcp_server_set_lease(sd_dhcp_server *server, be32_t address, DHCPRequest *req, usec_t expiration) {
+        _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
+        int r;
+
+        assert(server);
+        assert(address != 0);
+        assert(req);
+        assert(expiration != 0);
+
+        /* If a lease for the host already exists, update it. */
+        lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
+        if (lease) {
+                if (lease->address != address) {
+                        hashmap_remove_value(server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
+                        lease->address = address;
+
+                        r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+                        if (r < 0)
+                                return r;
+                }
+
+                lease->expiration = expiration;
+
+                TAKE_PTR(lease);
+                return 0;
+        }
+
+        /* Otherwise, add a new lease. */
+
+        lease = new(sd_dhcp_server_lease, 1);
+        if (!lease)
+                return -ENOMEM;
+
+        *lease = (sd_dhcp_server_lease) {
+                .n_ref = 1,
+                .address = address,
+                .client_id = req->client_id,
+                .htype = req->message->htype,
+                .hlen = req->message->hlen,
+                .gateway = req->message->giaddr,
+                .expiration = expiration,
+        };
+
+        memcpy(lease->chaddr, req->message->chaddr, req->message->hlen);
+
+        if (req->hostname) {
+                lease->hostname = strdup(req->hostname);
+                if (!lease->hostname)
+                        return -ENOMEM;
+        }
+
+        r = dhcp_server_put_lease(server, lease, /* is_static = */ false);
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(lease);
+        return 0;
+}
+
+int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) {
+        sd_dhcp_server_lease *lease;
+        usec_t time_now;
+        int r;
+
+        assert(server);
+
+        r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now);
+        if (r < 0)
+                return r;
+
+        HASHMAP_FOREACH(lease, server->bound_leases_by_client_id)
+                if (lease->expiration < time_now) {
+                        log_dhcp_server(server, "CLEAN (0x%x)", be32toh(lease->address));
+                        sd_dhcp_server_lease_unref(lease);
+                }
+
+        return 0;
+}
+
+sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req) {
+        sd_dhcp_server_lease *static_lease;
+        sd_dhcp_client_id client_id;
+
+        assert(server);
+        assert(req);
+
+        static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
+        if (static_lease)
+                return static_lease;
+
+        /* when no lease is found based on the client id fall back to chaddr */
+        if (!client_id_data_size_is_valid(req->message->hlen))
+                return NULL;
+
+        if (sd_dhcp_client_id_set(&client_id, /* type = */ 1, req->message->chaddr, req->message->hlen) < 0)
+                return NULL;
+
+        return hashmap_get(server->static_leases_by_client_id, &client_id);
+}
+
+int sd_dhcp_server_set_static_lease(
+                sd_dhcp_server *server,
+                const struct in_addr *address,
+                uint8_t *client_id_raw,
+                size_t client_id_size) {
+
+        _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
+        sd_dhcp_client_id client_id;
+        int r;
+
+        assert_return(server, -EINVAL);
+        assert_return(client_id_raw, -EINVAL);
+        assert_return(client_id_size_is_valid(client_id_size), -EINVAL);
+        assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
+
+        r = sd_dhcp_client_id_set_raw(&client_id, client_id_raw, client_id_size);
+        if (r < 0)
+                return r;
+
+        /* Static lease with an empty or omitted address is a valid entry,
+         * the server removes any static lease with the specified mac address. */
+        if (!address || address->s_addr == 0) {
+                sd_dhcp_server_lease_unref(hashmap_get(server->static_leases_by_client_id, &client_id));
+                return 0;
+        }
+
+        lease = new(sd_dhcp_server_lease, 1);
+        if (!lease)
+                return -ENOMEM;
+
+        *lease = (sd_dhcp_server_lease) {
+                .n_ref = 1,
+                .address = address->s_addr,
+                .client_id = client_id,
+        };
+
+        r = dhcp_server_put_lease(server, lease, /* is_static = */ true);
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(lease);
+        return 0;
+}
index 7eaca0bb710b9eca09577e10a2e800082ec74d65..b1c35064ce8c24fa50c4df209a762c5bc8b4686f 100644 (file)
@@ -14,6 +14,7 @@
 #include "dhcp-option.h"
 #include "dhcp-packet.h"
 #include "dhcp-server-internal.h"
+#include "dhcp-server-lease-internal.h"
 #include "dns-domain.h"
 #include "fd-util.h"
 #include "in-addr-util.h"
 #define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
 #define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
 
-DHCPLease *dhcp_lease_free(DHCPLease *lease) {
-        if (!lease)
-                return NULL;
-
-        if (lease->server) {
-                hashmap_remove_value(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
-                hashmap_remove_value(lease->server->bound_leases_by_client_id, &lease->client_id, lease);
-                hashmap_remove_value(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address), lease);
-                hashmap_remove_value(lease->server->static_leases_by_client_id, &lease->client_id, lease);
-        }
-
-        free(lease->client_id.data);
-        free(lease->hostname);
-        return mfree(lease);
-}
-
 /* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
  * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
  * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
@@ -127,38 +112,6 @@ int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) {
         return in4_addr_is_set(&server->relay_target);
 }
 
-void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
-        assert(id);
-        assert(id->length > 0);
-        assert(id->data);
-
-        siphash24_compress_typesafe(id->length, state);
-        siphash24_compress(id->data, id->length, state);
-}
-
-int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
-        int r;
-
-        assert(a->length > 0);
-        assert(a->data);
-        assert(b->length > 0);
-        assert(b->data);
-
-        r = CMP(a->length, b->length);
-        if (r != 0)
-                return r;
-
-        return memcmp(a->data, b->data, a->length);
-}
-
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
-        dhcp_lease_hash_ops,
-        DHCPClientId,
-        client_id_hash_func,
-        client_id_compare_func,
-        DHCPLease,
-        dhcp_lease_free);
-
 static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
         assert(server);
 
@@ -786,16 +739,8 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
 
                 break;
         case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
-                if (len >= 2) {
-                        uint8_t *data;
-
-                        data = memdup(option, len);
-                        if (!data)
-                                return -ENOMEM;
-
-                        free_and_replace(req->client_id.data, data);
-                        req->client_id.length = len;
-                }
+                if (client_id_size_is_valid(len))
+                        (void) sd_dhcp_client_id_set_raw(&req->client_id, option, len);
 
                 break;
         case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
@@ -833,7 +778,6 @@ static DHCPRequest* dhcp_request_free(DHCPRequest *req) {
         if (!req)
                 return NULL;
 
-        free(req->client_id.data);
         free(req->hostname);
         return mfree(req);
 }
@@ -841,6 +785,8 @@ static DHCPRequest* dhcp_request_free(DHCPRequest *req) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
 
 static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
+        int r;
+
         assert(req);
         assert(message);
 
@@ -850,39 +796,39 @@ static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMes
                 return -EBADMSG;
 
         /* set client id based on MAC address if client did not send an explicit one */
-        if (!req->client_id.data) {
-                uint8_t *data;
-
-                if (message->hlen == 0)
+        if (!sd_dhcp_client_id_is_set(&req->client_id)) {
+                if (!client_id_data_size_is_valid(message->hlen))
                         return -EBADMSG;
 
-                data = new0(uint8_t, message->hlen + 1);
-                if (!data)
-                        return -ENOMEM;
-
-                data[0] = 0x01;
-                memcpy(data + 1, message->chaddr, message->hlen);
-
-                req->client_id.length = message->hlen + 1;
-                req->client_id.data = data;
+                r = sd_dhcp_client_id_set(&req->client_id, /* type = */ 1, message->chaddr, message->hlen);
+                if (r < 0)
+                        return r;
         }
 
         if (message->hlen == 0 || memeqzero(message->chaddr, message->hlen)) {
+                uint8_t type;
+                const void *data;
+                size_t size;
+
                 /* See RFC2131 section 4.1.1.
                  * hlen and chaddr may not be set for non-ethernet interface.
                  * Let's try to retrieve it from the client ID. */
 
-                if (!req->client_id.data)
+                if (!sd_dhcp_client_id_is_set(&req->client_id))
                         return -EBADMSG;
 
-                if (req->client_id.length <= 1 || req->client_id.length > sizeof(message->chaddr) + 1)
+                r = sd_dhcp_client_id_get(&req->client_id, &type, &data, &size);
+                if (r < 0)
+                        return r;
+
+                if (type != 1)
                         return -EBADMSG;
 
-                if (req->client_id.data[0] != 0x01)
+                if (size > sizeof(message->chaddr))
                         return -EBADMSG;
 
-                message->hlen = req->client_id.length - 1;
-                memcpy(message->chaddr, req->client_id.data + 1, message->hlen);
+                memcpy(message->chaddr, data, size);
+                message->hlen = size;
         }
 
         if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
@@ -1018,44 +964,7 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag
         return -EBADMSG;
 }
 
-static int prepare_new_lease(DHCPLease **ret_lease, be32_t address, DHCPRequest *req, usec_t expiration) {
-        _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
-
-        assert(ret_lease);
-        assert(address != 0);
-        assert(req);
-        assert(expiration != 0);
-
-        lease = new(DHCPLease, 1);
-        if (!lease)
-                return -ENOMEM;
-
-        *lease = (DHCPLease) {
-                .address = address,
-                .client_id.length = req->client_id.length,
-                .htype = req->message->htype,
-                .hlen = req->message->hlen,
-                .gateway = req->message->giaddr,
-                .expiration = expiration,
-        };
-        lease->client_id.data = memdup(req->client_id.data, req->client_id.length);
-        if (!lease->client_id.data)
-                return -ENOMEM;
-
-        memcpy(lease->chaddr, req->message->chaddr, req->message->hlen);
-
-        if (req->hostname) {
-                lease->hostname = strdup(req->hostname);
-                if (!lease->hostname)
-                        return -ENOMEM;
-        }
-
-        *ret_lease = TAKE_PTR(lease);
-
-        return 0;
-}
-
-static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLease *existing_lease, be32_t address) {
+static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
         usec_t expiration;
         int r;
 
@@ -1067,30 +976,9 @@ static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLeas
         if (r < 0)
                 return r;
 
-        if (existing_lease) {
-                assert(existing_lease->server);
-                assert(existing_lease->address == address);
-                existing_lease->expiration = expiration;
-
-        } else {
-                _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
-
-                r = prepare_new_lease(&lease, address, req, expiration);
-                if (r < 0)
-                        return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
-
-                lease->server = server; /* This must be set just before hashmap_put(). */
-
-                r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
-                if (r < 0)
-                        return log_dhcp_server_errno(server, r, "Could not save lease: %m");
-
-                r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
-                if (r < 0)
-                        return log_dhcp_server_errno(server, r, "Could not save lease: %m");
-
-                TAKE_PTR(lease);
-        }
+        r = dhcp_server_set_lease(server, address, req, expiration);
+        if (r < 0)
+                return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
 
         r = server_send_offer_or_ack(server, req, address, DHCP_ACK);
         if (r < 0)
@@ -1104,26 +992,6 @@ static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLeas
         return DHCP_ACK;
 }
 
-static int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) {
-        DHCPLease *lease;
-        usec_t time_now;
-        int r;
-
-        assert(server);
-
-        r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now);
-        if (r < 0)
-                return r;
-
-        HASHMAP_FOREACH(lease, server->bound_leases_by_client_id)
-                if (lease->expiration < time_now) {
-                        log_dhcp_server(server, "CLEAN (0x%x)", be32toh(lease->address));
-                        dhcp_lease_free(lease);
-                }
-
-        return 0;
-}
-
 static bool address_available(sd_dhcp_server *server, be32_t address) {
         assert(server);
 
@@ -1135,46 +1003,12 @@ static bool address_available(sd_dhcp_server *server, be32_t address) {
         return true;
 }
 
-static int server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req, DHCPLease **ret) {
-        DHCPLease *static_lease;
-        _cleanup_free_ uint8_t *data = NULL;
-
-        assert(server);
-        assert(req);
-        assert(ret);
-
-        static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
-        if (static_lease) {
-                *ret = static_lease;
-                return 0;
-        }
-
-        /* when no lease is found based on the client id fall back to chaddr */
-        data = new(uint8_t, req->message->hlen + 1);
-        if (!data)
-                return -ENOMEM;
-
-        /* set client id type to 1: Ethernet Link-Layer (RFC 2132) */
-        data[0] = 0x01;
-        memcpy(data + 1, req->message->chaddr, req->message->hlen);
-
-        static_lease = hashmap_get(server->static_leases_by_client_id,
-                                   &(DHCPClientId) {
-                                           .length = req->message->hlen + 1,
-                                           .data = data,
-                                   });
-
-        *ret = static_lease;
-
-        return 0;
-}
-
 #define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
 
 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length, const triple_timestamp *timestamp) {
         _cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
         _cleanup_free_ char *error_message = NULL;
-        DHCPLease *existing_lease, *static_lease;
+        sd_dhcp_server_lease *existing_lease, *static_lease;
         int type, r;
 
         assert(server);
@@ -1202,9 +1036,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                 return r;
 
         existing_lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
-        r = server_get_static_lease(server, req, &static_lease);
-        if (r < 0)
-                return r;
+        static_lease = dhcp_server_get_static_lease(server, req);
 
         switch (type) {
 
@@ -1250,7 +1082,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                         return 0;
 
                 if (server->rapid_commit && req->rapid_commit)
-                        return server_ack_request(server, req, existing_lease, address);
+                        return server_ack_request(server, req, address);
 
                 r = server_send_offer_or_ack(server, req, address, DHCP_OFFER);
                 if (r < 0)
@@ -1330,7 +1162,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                                 /* The client requested an address which is different from the static lease. Refuse. */
                                 return server_send_nak_or_ignore(server, init_reboot, req);
 
-                        return server_ack_request(server, req, existing_lease, address);
+                        return server_ack_request(server, req, address);
                 }
 
                 if (address_is_in_pool(server, address)) {
@@ -1340,7 +1172,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                                 /* We previously assigned an address, but the client requested another one. Refuse. */
                                 return server_send_nak_or_ignore(server, init_reboot, req);
 
-                        return server_ack_request(server, req, existing_lease, address);
+                        return server_ack_request(server, req, address);
                 }
 
                 return server_send_nak_or_ignore(server, init_reboot, req);
@@ -1356,7 +1188,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                 if (existing_lease->address != req->message->ciaddr)
                         return 0;
 
-                dhcp_lease_free(existing_lease);
+                sd_dhcp_server_lease_unref(existing_lease);
 
                 if (server->callback)
                         server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
@@ -1523,7 +1355,7 @@ on_error:
 }
 
 int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
-        DHCPLease *lease;
+        sd_dhcp_server_lease *lease;
         int r = 0;
 
         assert_return(server, -EINVAL);
@@ -1737,56 +1569,3 @@ int sd_dhcp_server_set_relay_agent_information(
         free_and_replace(server->agent_remote_id, remote_id_dup);
         return 0;
 }
-
-int sd_dhcp_server_set_static_lease(
-                sd_dhcp_server *server,
-                const struct in_addr *address,
-                uint8_t *client_id,
-                size_t client_id_size) {
-
-        _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
-        int r;
-
-        assert_return(server, -EINVAL);
-        assert_return(client_id, -EINVAL);
-        assert_return(client_id_size > 0, -EINVAL);
-        assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
-
-        /* Static lease with an empty or omitted address is a valid entry,
-         * the server removes any static lease with the specified mac address. */
-        if (!address || address->s_addr == 0) {
-                DHCPClientId c;
-
-                c = (DHCPClientId) {
-                        .length = client_id_size,
-                        .data = client_id,
-                };
-
-                dhcp_lease_free(hashmap_get(server->static_leases_by_client_id, &c));
-                return 0;
-        }
-
-        lease = new(DHCPLease, 1);
-        if (!lease)
-                return -ENOMEM;
-
-        *lease = (DHCPLease) {
-                .address = address->s_addr,
-                .client_id.length = client_id_size,
-        };
-        lease->client_id.data = memdup(client_id, client_id_size);
-        if (!lease->client_id.data)
-                return -ENOMEM;
-
-        lease->server = server; /* This must be set just before hashmap_put(). */
-
-        r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
-        if (r < 0)
-                return r;
-        r = hashmap_ensure_put(&server->static_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
-        if (r < 0)
-                return r;
-
-        TAKE_PTR(lease);
-        return 0;
-}
index c20367dfc95baa9f3a02e0622e211d8be6776d1b..db4e0f9c5919ffa99ea43beb978e0969b7e37096 100644 (file)
 
 #include "alloc-util.h"
 #include "device-util.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
 #include "dns-domain.h"
 #include "event-util.h"
 #include "fd-util.h"
-#include "hexdecoct.h"
 #include "hostname-util.h"
 #include "in-addr-util.h"
 #include "iovec-util.h"
@@ -191,10 +190,10 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *
 static int client_ensure_duid(sd_dhcp6_client *client) {
         assert(client);
 
-        if (client->duid_len != 0)
+        if (sd_dhcp_duid_is_set(&client->duid))
                 return 0;
 
-        return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+        return sd_dhcp6_client_set_duid_en(client);
 }
 
 /**
@@ -208,7 +207,7 @@ int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) {
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
 
-        r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->duid, &client->duid_len);
+        r = sd_dhcp_duid_set_llt(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
         if (r < 0)
                 return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
 
@@ -221,7 +220,7 @@ int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) {
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
 
-        r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->duid, &client->duid_len);
+        r = sd_dhcp_duid_set_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
         if (r < 0)
                 return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
 
@@ -234,7 +233,7 @@ int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) {
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
 
-        r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+        r = sd_dhcp_duid_set_en(&client->duid);
         if (r < 0)
                 return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
 
@@ -247,7 +246,7 @@ int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) {
         assert_return(client, -EINVAL);
         assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
 
-        r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
+        r = sd_dhcp_duid_set_uuid(&client->duid);
         if (r < 0)
                 return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
 
@@ -261,46 +260,41 @@ int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, co
         assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
         assert_return(duid || duid_len == 0, -EINVAL);
 
-        r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->duid, &client->duid_len);
+        r = sd_dhcp_duid_set(&client->duid, duid_type, duid, duid_len);
         if (r < 0)
                 return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
 
         return 0;
 }
 
-int sd_dhcp6_client_duid_as_string(
-                sd_dhcp6_client *client,
-                char **duid) {
-        _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
-        const char *v;
-        int r;
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid) {
+        assert_return(client, -EINVAL);
+        assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+        assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
 
+        client->duid = *duid;
+        return 0;
+}
+
+int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret) {
         assert_return(client, -EINVAL);
-        assert_return(client->duid_len > offsetof(struct duid, raw.data), -ENODATA);
-        assert_return(duid, -EINVAL);
+        assert_return(ret, -EINVAL);
 
-        v = duid_type_to_string(be16toh(client->duid.type));
-        if (v) {
-                s = strdup(v);
-                if (!s)
-                        return -ENOMEM;
-        } else {
-                r = asprintf(&s, "%0x", client->duid.type);
-                if (r < 0)
-                        return -ENOMEM;
-        }
+        if (!sd_dhcp_duid_is_set(&client->duid))
+                return -ENODATA;
 
-        t = hexmem(client->duid.raw.data, client->duid_len - offsetof(struct duid, raw.data));
-        if (!t)
-                return -ENOMEM;
+        *ret = &client->duid;
+        return 0;
+}
 
-        p = strjoin(s, ":", t);
-        if (!p)
-                return -ENOMEM;
+int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret) {
+        assert_return(client, -EINVAL);
+        assert_return(ret, -EINVAL);
 
-        *duid = TAKE_PTR(p);
+        if (!sd_dhcp_duid_is_set(&client->duid))
+                return -ENODATA;
 
-        return 0;
+        return sd_dhcp_duid_to_string(&client->duid, ret);
 }
 
 int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
@@ -825,9 +819,9 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
         if (r < 0)
                 return r;
 
-        assert(client->duid_len > 0);
+        assert(sd_dhcp_duid_is_set(&client->duid));
         r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
-                                client->duid_len, &client->duid);
+                                client->duid.size, &client->duid.duid);
         if (r < 0)
                 return r;
 
index 674248b83da258b74860c6a8e71e25fdc8067694..e5d65475881628794ae2d69f9059abfb9add8923 100644 (file)
@@ -10,6 +10,7 @@
 #include "dhcp6-lease-internal.h"
 #include "network-common.h"
 #include "strv.h"
+#include "unaligned.h"
 
 #define IRT_DEFAULT (1 * USEC_PER_DAY)
 #define IRT_MINIMUM (600 * USEC_PER_SEC)
@@ -866,7 +867,7 @@ static int dhcp6_lease_parse_message(
                                               "%s message does not contain client ID. Ignoring.",
                                               dhcp6_message_type_to_string(message->type));
 
-        if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
+        if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0)
                 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
                                               "The client ID in %s message does not match. Ignoring.",
                                               dhcp6_message_type_to_string(message->type));
index 7c5b282fe2fa56173e7f45537cf505e533bf52bf..2c489605a8d9c509c294e63160454e994f61cb6b 100644 (file)
@@ -17,7 +17,7 @@
 #include "sd-event.h"
 
 #include "alloc-util.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
 #include "dhcp-network.h"
 #include "dhcp-option.h"
 #include "dhcp-packet.h"
@@ -165,19 +165,18 @@ static int check_options(uint8_t code, uint8_t len, const void *option, void *us
         switch (code) {
         case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
         {
+                sd_dhcp_duid duid;
                 uint32_t iaid;
-                struct duid duid;
-                size_t duid_len;
 
-                assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0);
+                assert_se(sd_dhcp_duid_set_en(&duid) >= 0);
                 assert_se(dhcp_identifier_set_iaid(NULL, &hw_addr, /* legacy = */ true, &iaid) >= 0);
 
-                assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
+                assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid.size);
                 assert_se(len == 19);
                 assert_se(((uint8_t*) option)[0] == 0xff);
 
                 assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0);
-                assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0);
+                assert_se(memcmp((uint8_t*) option + 5, &duid.duid, duid.size) == 0);
                 break;
         }
 
index e04a819eea78617120390ad44cfcde029a4e44d1..9c343f8368d77217052c7071ecbd6ac632d4bec3 100644 (file)
@@ -223,7 +223,7 @@ static void test_message_handler(void) {
         assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
 }
 
-static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) {
+static uint64_t client_id_hash_helper(sd_dhcp_client_id *id, uint8_t key[HASH_KEY_SIZE]) {
         struct siphash state;
 
         siphash24_init(&state, key);
@@ -233,10 +233,10 @@ static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZ
 }
 
 static void test_client_id_hash(void) {
-        DHCPClientId a = {
-                .length = 4,
+        sd_dhcp_client_id a = {
+                .size = 4,
         }, b = {
-                .length = 4,
+                .size = 4,
         };
         uint8_t hash_key[HASH_KEY_SIZE] = {
                 '0', '1', '2', '3', '4', '5', '6', '7',
@@ -245,29 +245,25 @@ static void test_client_id_hash(void) {
 
         log_debug("/* %s */", __func__);
 
-        a.data = (uint8_t*)strdup("abcd");
-        b.data = (uint8_t*)strdup("abcd");
+        memcpy(a.raw, "abcd", 4);
+        memcpy(b.raw, "abcd", 4);
 
         assert_se(client_id_compare_func(&a, &b) == 0);
         assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
-        a.length = 3;
+        a.size = 3;
         assert_se(client_id_compare_func(&a, &b) != 0);
-        a.length = 4;
+        a.size = 4;
         assert_se(client_id_compare_func(&a, &b) == 0);
         assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
 
-        b.length = 3;
+        b.size = 3;
         assert_se(client_id_compare_func(&a, &b) != 0);
-        b.length = 4;
+        b.size = 4;
         assert_se(client_id_compare_func(&a, &b) == 0);
         assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
 
-        free(b.data);
-        b.data = (uint8_t*)strdup("abce");
+        memcpy(b.raw, "abce", 4);
         assert_se(client_id_compare_func(&a, &b) != 0);
-
-        free(a.data);
-        free(b.data);
 }
 
 static void test_static_lease(void) {
index ae3cdb86320185624829cb840830359411bd876e..ecf3f095c39ec268fcfd98c26766cdebcf86c1a3 100644 (file)
@@ -13,7 +13,7 @@
 #include "sd-dhcp6-client.h"
 #include "sd-event.h"
 
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
 #include "dhcp6-protocol.h"
@@ -25,6 +25,7 @@
 #include "strv.h"
 #include "tests.h"
 #include "time-util.h"
+#include "unaligned.h"
 
 #define DHCP6_CLIENT_EVENT_TEST_ADVERTISED 77
 #define IA_ID_BYTES                                                     \
index 11e418e247bfce17257147d9141eba26d742ab8b..71ab3d8f68e94f40dec700ed10a95ba26dd7fa23 100644 (file)
@@ -370,12 +370,8 @@ static int enumerator_sort_devices(sd_device_enumerator *enumerator) {
 
                         HASHMAP_FOREACH_KEY(device, syspath, enumerator->devices_by_syspath) {
                                 _cleanup_free_ char *p = NULL;
-                                const char *subsys;
 
-                                if (sd_device_get_subsystem(device, &subsys) < 0)
-                                        continue;
-
-                                if (!streq(subsys, *prioritized_subsystem))
+                                if (!device_in_subsystem(device, *prioritized_subsystem))
                                         continue;
 
                                 devices[n++] = sd_device_ref(device);
index bb4f9bd513e222e71a6308557598ac4ba1e99c9d..49ab56adcbbadc401748b65a8e7423b34a719524 100644 (file)
@@ -402,8 +402,7 @@ static sd_device_monitor *device_monitor_free(sd_device_monitor *m) {
 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_monitor, sd_device_monitor, device_monitor_free);
 
 static int check_subsystem_filter(sd_device_monitor *m, sd_device *device) {
-        const char *s, *subsystem, *d, *devtype = NULL;
-        int r;
+        const char *s, *d;
 
         assert(m);
         assert(device);
@@ -411,20 +410,14 @@ static int check_subsystem_filter(sd_device_monitor *m, sd_device *device) {
         if (hashmap_isempty(m->subsystem_filter))
                 return true;
 
-        r = sd_device_get_subsystem(device, &subsystem);
-        if (r < 0)
-                return r;
-
-        r = sd_device_get_devtype(device, &devtype);
-        if (r < 0 && r != -ENOENT)
-                return r;
-
         HASHMAP_FOREACH_KEY(d, s, m->subsystem_filter) {
-                if (!streq(s, subsystem))
+                if (!device_in_subsystem(device, s))
                         continue;
 
-                if (!d || streq_ptr(d, devtype))
-                        return true;
+                if (d && !device_is_devtype(device, d))
+                        continue;
+
+                return true;
         }
 
         return false;
index 529eff2fd1991e6bc7aa4ede7bb8afeda6141dd1..8318373fb24c53e121272c4b2b7c2bad5bb3659e 100644 (file)
@@ -139,3 +139,21 @@ char** device_make_log_fields(sd_device *device) {
 
         return TAKE_PTR(strv);
 }
+
+bool device_in_subsystem(sd_device *device, const char *subsystem) {
+        const char *s = NULL;
+
+        assert(device);
+
+        (void) sd_device_get_subsystem(device, &s);
+        return streq_ptr(s, subsystem);
+}
+
+bool device_is_devtype(sd_device *device, const char *devtype) {
+        const char *s = NULL;
+
+        assert(device);
+
+        (void) sd_device_get_devtype(device, &s);
+        return streq_ptr(s, devtype);
+}
index bf86ddcbe36c20f51df2fe84ffe51f1b6ccedd5e..a9a9b7ad988165304c092fc974b00c8888a87e50 100644 (file)
@@ -102,3 +102,6 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
 int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
 
 char** device_make_log_fields(sd_device *device);
+
+bool device_in_subsystem(sd_device *device, const char *subsystem);
+bool device_is_devtype(sd_device *device, const char *devtype);
index 071e72707eaecab3b6aa0f3143d1ac98a61186a2..f18c8e69220bcb9502a835a61192e101db181e55 100644 (file)
@@ -283,7 +283,7 @@ _public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
 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;
-        const char *t, *subsystem = NULL;
+        const char *t;
         dev_t n;
         int r;
 
@@ -314,10 +314,7 @@ int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum)
         if (n != devnum)
                 return -ENXIO;
 
-        r = sd_device_get_subsystem(dev, &subsystem);
-        if (r < 0 && r != -ENOENT)
-                return r;
-        if (streq_ptr(subsystem, "block") != !!S_ISBLK(mode))
+        if (device_in_subsystem(dev, "block") != !!S_ISBLK(mode))
                 return -ENXIO;
 
         *ret = TAKE_PTR(dev);
@@ -1222,37 +1219,27 @@ _public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
         return !!device->devtype;
 }
 
-_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) {
-        sd_device *parent = NULL;
+_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *device, const char *subsystem, const char *devtype, sd_device **ret) {
         int r;
 
-        assert_return(child, -EINVAL);
+        assert_return(device, -EINVAL);
         assert_return(subsystem, -EINVAL);
 
-        r = sd_device_get_parent(child, &parent);
-        while (r >= 0) {
-                const char *parent_subsystem = NULL;
+        for (;;) {
+                r = sd_device_get_parent(device, &device);
+                if (r < 0)
+                        return r;
 
-                (void) sd_device_get_subsystem(parent, &parent_subsystem);
-                if (streq_ptr(parent_subsystem, subsystem)) {
-                        const char *parent_devtype = NULL;
+                if (!device_in_subsystem(device, subsystem))
+                        continue;
 
-                        if (!devtype)
-                                break;
+                if (devtype && !device_is_devtype(device, devtype))
+                        continue;
 
-                        (void) sd_device_get_devtype(parent, &parent_devtype);
-                        if (streq_ptr(parent_devtype, devtype))
-                                break;
-                }
-                r = sd_device_get_parent(parent, &parent);
+                if (ret)
+                        *ret = device;
+                return 0;
         }
-
-        if (r < 0)
-                return r;
-
-        if (ret)
-                *ret = parent;
-        return 0;
 }
 
 _public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
@@ -2591,7 +2578,7 @@ _public_ int sd_device_trigger_with_uuid(
 
 _public_ int sd_device_open(sd_device *device, int flags) {
         _cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
-        const char *devname, *subsystem = NULL;
+        const char *devname;
         uint64_t q, diskseq = 0;
         struct stat st;
         dev_t devnum;
@@ -2612,10 +2599,6 @@ _public_ int sd_device_open(sd_device *device, int flags) {
         if (r < 0)
                 return r;
 
-        r = sd_device_get_subsystem(device, &subsystem);
-        if (r < 0 && r != -ENOENT)
-                return r;
-
         fd = open(devname, FLAGS_SET(flags, O_PATH) ? flags : O_CLOEXEC|O_NOFOLLOW|O_PATH);
         if (fd < 0)
                 return -errno;
@@ -2626,7 +2609,7 @@ _public_ int sd_device_open(sd_device *device, int flags) {
         if (st.st_rdev != devnum)
                 return -ENXIO;
 
-        if (streq_ptr(subsystem, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode))
+        if (device_in_subsystem(device, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode))
                 return -ENXIO;
 
         /* If flags has O_PATH, then we cannot check diskseq. Let's return earlier. */
index bc8ab66716e5882b6957d678b404a24335b9db57..4d38982e348da15dd69c44f2431be38656daac1f 100644 (file)
@@ -1,23 +1,91 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "device-util.h"
+#include "mountpoint-util.h"
 #include "tests.h"
 
 TEST(log_device_full) {
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
         int r;
 
+        (void) sd_device_new_from_subsystem_sysname(&dev, "net", "lo");
+
         for (int level = LOG_ERR; level <= LOG_DEBUG; level++) {
-                log_device_full(NULL, level, "test level=%d: %m", level);
+                log_device_full(dev, level, "test level=%d: %m", level);
 
-                r = log_device_full_errno(NULL, level, EUCLEAN, "test level=%d errno=EUCLEAN: %m", level);
+                r = log_device_full_errno(dev, level, EUCLEAN, "test level=%d errno=EUCLEAN: %m", level);
                 assert_se(r == -EUCLEAN);
 
-                r = log_device_full_errno(NULL, level, 0, "test level=%d errno=0: %m", level);
+                r = log_device_full_errno(dev, level, 0, "test level=%d errno=0: %m", level);
                 assert_se(r == 0);
 
-                r = log_device_full_errno(NULL, level, SYNTHETIC_ERRNO(ENODATA), "test level=%d errno=S(ENODATA): %m", level);
+                r = log_device_full_errno(dev, level, SYNTHETIC_ERRNO(ENODATA), "test level=%d errno=S(ENODATA): %m", level);
                 assert_se(r == -ENODATA);
         }
 }
 
-DEFINE_TEST_MAIN(LOG_INFO);
+TEST(device_in_subsystem) {
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+        int r;
+
+        r = sd_device_new_from_subsystem_sysname(&dev, "net", "lo");
+        if (r == -ENODEV)
+                return (void) log_tests_skipped("net/lo does not exist");
+        assert_se(r >= 0);
+
+        assert_se(device_in_subsystem(dev, "net"));
+        assert_se(!device_in_subsystem(dev, "disk"));
+        assert_se(!device_in_subsystem(dev, "subsystem"));
+        assert_se(!device_in_subsystem(dev, ""));
+        assert_se(!device_in_subsystem(dev, NULL));
+
+        dev = sd_device_unref(dev);
+
+        assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net") >= 0);
+        assert_se(!device_in_subsystem(dev, "net"));
+        assert_se(!device_in_subsystem(dev, "disk"));
+        assert_se(device_in_subsystem(dev, "subsystem"));
+        assert_se(!device_in_subsystem(dev, ""));
+        assert_se(!device_in_subsystem(dev, NULL));
+
+        dev = sd_device_unref(dev);
+
+        assert_se(sd_device_new_from_syspath(&dev, "/sys/class") >= 0);
+        assert_se(!device_in_subsystem(dev, "net"));
+        assert_se(!device_in_subsystem(dev, "disk"));
+        assert_se(!device_in_subsystem(dev, "subsystem"));
+        assert_se(!device_in_subsystem(dev, ""));
+        assert_se(device_in_subsystem(dev, NULL));
+}
+
+TEST(device_is_devtype) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+
+        assert_se(sd_device_enumerator_new(&e) >= 0);
+        assert_se(sd_device_enumerator_add_match_subsystem(e, "disk", true) >= 0);
+
+        FOREACH_DEVICE(e, d) {
+                const char *t;
+
+                assert_se(sd_device_get_devtype(d, &t) >= 0);
+                assert_se(device_is_devtype(d, t));
+                assert_se(!device_is_devtype(d, "hoge"));
+                assert_se(!device_is_devtype(d, ""));
+                assert_se(!device_is_devtype(d, NULL));
+        }
+
+        assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net") >= 0);
+        assert_se(!device_is_devtype(dev, "hoge"));
+        assert_se(!device_is_devtype(dev, ""));
+        assert_se(device_is_devtype(dev, NULL));
+}
+
+static int intro(void) {
+        if (path_is_mount_point("/sys", NULL, 0) <= 0)
+                return log_tests_skipped("/sys is not mounted");
+
+        return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
index cef59c8944ed337ba1b551407506faf4833c3a3d..5e6ff66e3c5732814a734f8043d66f79fbcd4889 100644 (file)
@@ -2460,6 +2460,11 @@ int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t)
         assert(e);
         assert(t);
 
+        /* If we are already going down, we cannot install the timer.
+         * In such case, the caller needs to call journal_file_post_change() explicitly. */
+        if (IN_SET(sd_event_get_state(e), SD_EVENT_EXITING, SD_EVENT_FINISHED))
+                return 0;
+
         r = sd_event_add_time(e, &timer, CLOCK_MONOTONIC, 0, 0, post_change_thunk, f);
         if (r < 0)
                 return r;
@@ -2471,7 +2476,7 @@ int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t)
         f->post_change_timer = TAKE_PTR(timer);
         f->post_change_timer_period = t;
 
-        return r;
+        return 1;
 }
 
 static int entry_item_cmp(const EntryItem *a, const EntryItem *b) {
index 259aac847d6645488960b41901657d7c1ae26636..1b86d3d3c45e6d234e677447606c4f8fab8a9ae2 100644 (file)
@@ -62,6 +62,7 @@ struct Location {
 };
 
 struct Directory {
+        sd_journal *journal;
         char *path;
         int wd;
         bool is_root;
index f2a0c666703be94be7657459414e99ad453c0a69..18e62e7ac07f1fe437010f5198210b045fcd7b00 100644 (file)
@@ -1684,6 +1684,100 @@ static int directory_open(sd_journal *j, const char *path, DIR **ret) {
         return 0;
 }
 
+static Directory* directory_free(Directory *d) {
+        if (!d)
+                return NULL;
+
+        if (d->journal) {
+                if (d->wd > 0 &&
+                    hashmap_remove_value(d->journal->directories_by_wd, INT_TO_PTR(d->wd), d) &&
+                    d->journal->inotify_fd >= 0)
+                        (void) inotify_rm_watch(d->journal->inotify_fd, d->wd);
+
+                if (d->path)
+                        hashmap_remove_value(d->journal->directories_by_path, d->path, d);
+        }
+
+        if (d->path) {
+                if (d->is_root)
+                        log_debug("Root directory %s removed.", d->path);
+                else
+                        log_debug("Directory %s removed.", d->path);
+
+                free(d->path);
+        }
+
+        return mfree(d);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Directory*, directory_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        directories_by_path_hash_ops,
+        char,
+        path_hash_func,
+        path_compare,
+        Directory,
+        directory_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+        directories_by_wd_hash_ops,
+        void,
+        trivial_hash_func,
+        trivial_compare_func,
+        Directory,
+        directory_free);
+
+static int add_directory_impl(sd_journal *j, const char *path, bool is_root, Directory **ret) {
+        _cleanup_(directory_freep) Directory *m = NULL;
+        Directory *existing;
+        int r;
+
+        assert(j);
+        assert(path);
+        assert(ret);
+
+        existing = hashmap_get(j->directories_by_path, path);
+        if (existing) {
+                if (existing->is_root != is_root) {
+                        /* Don't 'downgrade' from root directory */
+                        *ret = NULL;
+                        return 0;
+                }
+
+                *ret = existing;
+                return 1;
+        }
+
+        m = new(Directory, 1);
+        if (!m)
+                return -ENOMEM;
+
+        *m = (Directory) {
+                .journal = j,
+                .is_root = is_root,
+                .path = strdup(path),
+                .wd = -1,
+        };
+
+        if (!m->path)
+                return -ENOMEM;
+
+        r = hashmap_ensure_put(&j->directories_by_path, &directories_by_path_hash_ops, m->path, m);
+        if (r < 0)
+                return r;
+
+        j->current_invalidate_counter++;
+
+        if (is_root)
+                log_debug("Root directory %s added.", m->path);
+        else
+                log_debug("Directory %s added.", m->path);
+
+        *ret = TAKE_PTR(m);
+        return 1;
+}
+
 static int add_directory(sd_journal *j, const char *prefix, const char *dirname);
 
 static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
@@ -1724,12 +1818,14 @@ static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask)
                 return;
         }
 
-        r = hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m);
-        if (r == -EEXIST)
-                log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path);
+        r = hashmap_ensure_put(&j->directories_by_wd, &directories_by_wd_hash_ops, INT_TO_PTR(m->wd), m);
         if (r < 0) {
-                log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path);
-                (void) inotify_rm_watch(j->inotify_fd, m->wd);
+                if (r == -EEXIST)
+                        log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path);
+                else {
+                        log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path);
+                        (void) inotify_rm_watch(j->inotify_fd, m->wd);
+                }
                 m->wd = -1;
         }
 }
@@ -1775,32 +1871,11 @@ static int add_directory(
                 goto fail;
         }
 
-        m = hashmap_get(j->directories_by_path, path);
-        if (!m) {
-                m = new(Directory, 1);
-                if (!m) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
-                *m = (Directory) {
-                        .is_root = false,
-                        .path = path,
-                };
-
-                if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
-                        free(m);
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
-                path = NULL; /* avoid freeing in cleanup */
-                j->current_invalidate_counter++;
-
-                log_debug("Directory %s added.", m->path);
-
-        } else if (m->is_root)
-                return 0; /* Don't 'downgrade' from root directory */
+        r = add_directory_impl(j, path, /* is_root = */ false, &m);
+        if (r < 0)
+                goto fail;
+        if (r == 0)
+                return 0;
 
         m->last_seen_generation = j->generation;
 
@@ -1878,35 +1953,10 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
                 rewinddir(d);
         }
 
-        m = hashmap_get(j->directories_by_path, p);
-        if (!m) {
-                m = new0(Directory, 1);
-                if (!m) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
-                m->is_root = true;
-
-                m->path = strdup(p);
-                if (!m->path) {
-                        free(m);
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
-                if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
-                        free(m->path);
-                        free(m);
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
-                j->current_invalidate_counter++;
-
-                log_debug("Root directory %s added.", m->path);
-
-        } else if (!m->is_root)
+        r = add_directory_impl(j, p, /* is_root = */ true, &m);
+        if (r < 0)
+                goto fail;
+        if (r == 0)
                 return 0;
 
         directory_watch(j, m, dirfd(d),
@@ -1928,27 +1978,6 @@ fail:
         return r;
 }
 
-static void remove_directory(sd_journal *j, Directory *d) {
-        assert(j);
-
-        if (d->wd > 0) {
-                hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
-
-                if (j->inotify_fd >= 0)
-                        (void) inotify_rm_watch(j->inotify_fd, d->wd);
-        }
-
-        hashmap_remove(j->directories_by_path, d->path);
-
-        if (d->is_root)
-                log_debug("Root directory %s removed.", d->path);
-        else
-                log_debug("Directory %s removed.", d->path);
-
-        free(d->path);
-        free(d);
-}
-
 static int add_search_paths(sd_journal *j) {
 
         static const char search_paths[] =
@@ -2003,7 +2032,7 @@ static int allocate_inotify(sd_journal *j) {
                         return -errno;
         }
 
-        return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
+        return 0;
 }
 
 static sd_journal *journal_new(int flags, const char *path, const char *namespace) {
@@ -2045,9 +2074,8 @@ static sd_journal *journal_new(int flags, const char *path, const char *namespac
                 return NULL;
 
         j->files_cache = ordered_hashmap_iterated_cache_new(j->files);
-        j->directories_by_path = hashmap_new(&path_hash_ops);
         j->mmap = mmap_cache_new();
-        if (!j->files_cache || !j->directories_by_path || !j->mmap)
+        if (!j->files_cache || !j->mmap)
                 return NULL;
 
         return TAKE_PTR(j);
@@ -2270,7 +2298,6 @@ fail:
 }
 
 _public_ void sd_journal_close(sd_journal *j) {
-        Directory *d;
         Prioq *p;
 
         if (!j || journal_origin_changed(j))
@@ -2285,12 +2312,6 @@ _public_ void sd_journal_close(sd_journal *j) {
         ordered_hashmap_free_with_destructor(j->files, journal_file_close);
         iterated_cache_free(j->files_cache);
 
-        while ((d = hashmap_first(j->directories_by_path)))
-                remove_directory(j, d);
-
-        while ((d = hashmap_first(j->directories_by_wd)))
-                remove_directory(j, d);
-
         hashmap_free(j->directories_by_path);
         hashmap_free(j->directories_by_wd);
 
@@ -2839,7 +2860,7 @@ static void process_q_overflow(sd_journal *j) {
                         continue;
 
                 log_debug("Directory '%s' hasn't been seen in this enumeration, removing.", f->path);
-                remove_directory(j, m);
+                directory_free(m);
         }
 
         log_debug("Reiteration complete.");
@@ -2875,7 +2896,7 @@ static void process_inotify_event(sd_journal *j, const struct inotify_event *e)
                         /* Event for a subdirectory */
 
                         if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
-                                remove_directory(j, d);
+                                directory_free(d);
 
                 } else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && id128_is_valid(e->name)) {
 
index 4d09b156536c06a875749b6b804f00e83a6ac867..3e88d349e1689cc37bea398734cc7c0b152fe5b0 100644 (file)
@@ -1275,7 +1275,7 @@ _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
 
 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
         if (m)
-                (void) close_nointr(MONITOR_TO_FD(m));
+                (void) close(MONITOR_TO_FD(m));
 
         return NULL;
 }
index 09116b14380ce445451feb8fc37d3028e9c81818..f84097e16de294b79c0144f26340f328ca93c5fd 100644 (file)
@@ -92,6 +92,20 @@ int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char
         return 0;
 }
 
+int sd_rtnl_message_route_set_tos(sd_netlink_message *m, unsigned char tos) {
+        struct rtmsg *rtm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+        rtm = NLMSG_DATA(m->hdr);
+
+        rtm->rtm_tos = tos;
+
+        return 0;
+}
+
 int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope) {
         struct rtmsg *rtm;
 
index 000a50ee3e2558f9471408caa599ace1247b15ae..abe316b64bdc021a995628b0fcdbbece8e4f729d 100644 (file)
@@ -17,9 +17,6 @@
 
 #define GET_CONTAINER(m, i) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset))
 
-#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
-#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
-
 int message_new_empty(sd_netlink *nl, sd_netlink_message **ret) {
         sd_netlink_message *m;
 
index 369f5d50e47a09c7fc70f4df2a81c7fd5b190e17..56bbfd30d11d39c6e60aa83e8f7405a2cef85fc9 100644 (file)
@@ -10,6 +10,9 @@
 #include "ordered-set.h"
 #include "socket-util.h"
 
+#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
+#define RTA_TYPE(rta)  ((rta)->rta_type & NLA_TYPE_MASK)
+
 /* See struct rtvia in rtnetlink.h */
 typedef struct RouteVia {
         uint16_t family;
index cf3c400dbc4dfda21f7046e09ce201d1fb310ed4..9d11dce2e8f8a2806c021384a020f7c19176b5a0 100644 (file)
@@ -394,7 +394,7 @@ int sd_network_monitor_new(sd_network_monitor **m, const char *category) {
 
 sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
         if (m)
-                (void) close_nointr(MONITOR_TO_FD(m));
+                (void) close(MONITOR_TO_FD(m));
 
         return NULL;
 }
index e8951073788e88753cf78ceb9a88c95ec2b70851..acef32679a1adce19aa990b23565460f17ba6274 100644 (file)
@@ -209,8 +209,8 @@ static int handle_action_execute(
 
         if (m->delayed_action)
                 return log_debug_errno(SYNTHETIC_ERRNO(EALREADY),
-                                       "Action already in progress (%s), ignoring requested %s operation.",
-                                       inhibit_what_to_string(m->delayed_action->inhibit_what),
+                                       "Action %s already in progress, ignoring requested %s operation.",
+                                       handle_action_to_string(m->delayed_action->handle),
                                        handle_action_to_string(handle));
 
         inhibit_operation = ASSERT_PTR(handle_action_lookup(handle))->inhibit_what;
index becc21b0de6936cc6a56ec88ea83b13553d6409b..26133ee6efceece88d6b716ff8b09ebb5aa814bf 100644 (file)
@@ -588,7 +588,7 @@ static int manager_count_external_displays(Manager *m) {
                 return r;
 
         FOREACH_DEVICE(e, d) {
-                const char *status, *enabled, *dash, *nn, *subsys;
+                const char *status, *enabled, *dash, *nn;
                 sd_device *p;
 
                 if (sd_device_get_parent(d, &p) < 0)
@@ -597,7 +597,7 @@ static int manager_count_external_displays(Manager *m) {
                 /* If the parent shares the same subsystem as the
                  * device we are looking at then it is a connector,
                  * which is what we are interested in. */
-                if (sd_device_get_subsystem(p, &subsys) < 0 || !streq(subsys, "drm"))
+                if (!device_in_subsystem(p, "drm"))
                         continue;
 
                 if (sd_device_get_sysname(d, &nn) < 0)
index 898c6f752b6e70434f8e6c8244d8c7b15b8fb670..b310a7cee8638ce27b074abc2aa34dfc3e9a8123 100644 (file)
@@ -4011,7 +4011,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
 
         if (m->action_job && streq(m->action_job, path)) {
                 assert(m->delayed_action);
-                log_info("Operation '%s' finished.", inhibit_what_to_string(m->delayed_action->inhibit_what));
+                log_info("Operation '%s' finished.", handle_action_to_string(m->delayed_action->handle));
 
                 /* Tell people that they now may take a lock again */
                 (void) send_prepare_for(m, m->delayed_action, false);
index 44d8d525ee01c240ac4033dc0dae78374c129f76..cd5c0ec82dbb7a512e7d4adee67e80adf93dbbb3 100644 (file)
@@ -11,6 +11,7 @@
 #include "alloc-util.h"
 #include "bus-util.h"
 #include "daemon-util.h"
+#include "device-util.h"
 #include "fd-util.h"
 #include "logind-session-dbus.h"
 #include "logind-session-device.h"
@@ -114,7 +115,8 @@ static int sd_drmdropmaster(int fd) {
 }
 
 static int session_device_open(SessionDevice *sd, bool active) {
-        int fd, r;
+        _cleanup_close_ int fd = -EBADF;
+        int r;
 
         assert(sd);
         assert(sd->type != DEVICE_TYPE_UNKNOWN);
@@ -132,10 +134,8 @@ static int session_device_open(SessionDevice *sd, bool active) {
                         /* Weird legacy DRM semantics might return an error even though we're master. No way to detect
                          * that so fail at all times and let caller retry in inactive state. */
                         r = sd_drmsetmaster(fd);
-                        if (r < 0) {
-                                (void) close_nointr(fd);
+                        if (r < 0)
                                 return r;
-                        }
                 } else
                         /* DRM-Master is granted to the first user who opens a device automatically (ughh,
                          * racy!). Hence, we just drop DRM-Master in case we were the first. */
@@ -153,7 +153,7 @@ static int session_device_open(SessionDevice *sd, bool active) {
                 break;
         }
 
-        return fd;
+        return TAKE_FD(fd);
 }
 
 static int session_device_start(SessionDevice *sd) {
@@ -239,22 +239,21 @@ static void session_device_stop(SessionDevice *sd) {
 }
 
 static DeviceType detect_device_type(sd_device *dev) {
-        const char *sysname, *subsystem;
-        DeviceType type = DEVICE_TYPE_UNKNOWN;
+        const char *sysname;
 
-        if (sd_device_get_sysname(dev, &sysname) < 0 ||
-            sd_device_get_subsystem(dev, &subsystem) < 0)
-                return type;
+        if (sd_device_get_sysname(dev, &sysname) < 0)
+                return DEVICE_TYPE_UNKNOWN;
 
-        if (streq(subsystem, "drm")) {
+        if (device_in_subsystem(dev, "drm")) {
                 if (startswith(sysname, "card"))
-                        type = DEVICE_TYPE_DRM;
-        } else if (streq(subsystem, "input")) {
+                        return DEVICE_TYPE_DRM;
+
+        } else if (device_in_subsystem(dev, "input")) {
                 if (startswith(sysname, "event"))
-                        type = DEVICE_TYPE_EVDEV;
+                        return DEVICE_TYPE_EVDEV;
         }
 
-        return type;
+        return DEVICE_TYPE_UNKNOWN;
 }
 
 static int session_device_verify(SessionDevice *sd) {
@@ -322,24 +321,25 @@ static int session_device_verify(SessionDevice *sd) {
         return 0;
 }
 
-int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out) {
+int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **ret) {
         SessionDevice *sd;
         int r;
 
         assert(s);
-        assert(out);
 
         if (!s->seat)
                 return -EPERM;
 
-        sd = new0(SessionDevice, 1);
+        sd = new(SessionDevice, 1);
         if (!sd)
                 return -ENOMEM;
 
-        sd->session = s;
-        sd->dev = dev;
-        sd->fd = -EBADF;
-        sd->type = DEVICE_TYPE_UNKNOWN;
+        *sd = (SessionDevice) {
+                .session = s,
+                .dev = dev,
+                .fd = -EBADF,
+                .type = DEVICE_TYPE_UNKNOWN,
+        };
 
         r = session_device_verify(sd);
         if (r < 0)
@@ -370,7 +370,9 @@ int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **
 
         LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
 
-        *out = sd;
+        if (ret)
+                *ret = sd;
+
         return 0;
 
 error:
index 04654d12524aaaea684e80f4b00a9141f7bf596a..06a8f80c060c1a3754752b97f6b59ba5a9590c51 100644 (file)
@@ -27,7 +27,7 @@ struct SessionDevice {
         LIST_FIELDS(struct SessionDevice, sd_by_device);
 };
 
-int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out);
+int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **ret);
 SessionDevice *session_device_free(SessionDevice *sd);
 DEFINE_TRIVIAL_CLEANUP_FUNC(SessionDevice*, session_device_free);
 
index 3f5decd8d9bd1a62b5ba77e9492378d49e612584..b4277c33f1489d1f2c826bb4909bfc59baadcc53 100644 (file)
@@ -377,33 +377,27 @@ static int session_load_devices(Session *s, const char *devices) {
 
         for (const char *p = devices;;) {
                 _cleanup_free_ char *word = NULL;
-                SessionDevice *sd;
                 dev_t dev;
                 int k;
 
                 k = extract_first_word(&p, &word, NULL, 0);
-                if (k == 0)
-                        break;
-                if (k < 0) {
-                        r = k;
+                if (k <= 0) {
+                        RET_GATHER(r, k);
                         break;
                 }
 
                 k = parse_devnum(word, &dev);
                 if (k < 0) {
-                        r = k;
+                        RET_GATHER(r, k);
                         continue;
                 }
 
                 /* The file descriptors for loaded devices will be reattached later. */
-                k = session_device_new(s, dev, false, &sd);
-                if (k < 0)
-                        r = k;
+                RET_GATHER(r, session_device_new(s, dev, /* open_device = */ false, /* ret = */ NULL));
         }
 
         if (r < 0)
-                log_error_errno(r, "Loading session devices for session %s failed: %m", s->id);
-
+                log_error_errno(r, "Failed to load some session devices for session '%s': %m", s->id);
         return r;
 }
 
@@ -744,7 +738,7 @@ static int session_setup_stop_on_idle_timer(Session *s) {
 
         assert(s);
 
-        if (s->manager->stop_idle_session_usec == USEC_INFINITY)
+        if (s->manager->stop_idle_session_usec == USEC_INFINITY || IN_SET(s->class, SESSION_GREETER, SESSION_LOCK_SCREEN))
                 return 0;
 
         r = sd_event_add_time_relative(
index 2c276ef22a4f2b54e7aaa3d5909dc9053781b936..d8c37534d94e898f0ed052b1e5e850e1ecfb8cd0 100644 (file)
@@ -1267,7 +1267,7 @@ static int acquire_removable(sd_device *d) {
                 if (sd_device_get_parent(d, &d) < 0)
                         return 0;
 
-                if (sd_device_get_subsystem(d, &v) < 0 || !streq(v, "block"))
+                if (!device_in_subsystem(d, "block"))
                         return 0;
         }
 
index 17d6acefb6662f3a8b0b5daeb6cdd0f60180e583..877898c06d468d470a4c0754d946c9940a56c332 100644 (file)
@@ -18,6 +18,7 @@
 #include "socket-util.h"
 #include "string-table.h"
 #include "string-util.h"
+#include "unaligned.h"
 
 static void security_association_clear(SecurityAssociation *sa) {
         if (!sa)
index 1a37ba34652e3a92ddd145210da4202f8dbdc4ee..9e909d1abce8bb39d47a3c477002eb48141bb5d3 100644 (file)
@@ -137,6 +137,19 @@ static int netdev_create_tuntap(NetDev *netdev) {
         if (ioctl(fd, TUNSETIFF, &ifr) < 0)
                 return log_netdev_error_errno(netdev, errno, "TUNSETIFF failed: %m");
 
+        if (t->multi_queue) {
+                /* If we don't detach the queue, the kernel will send packets to our queue and they
+                 * will be dropped because we never read them, which is especially important in case
+                 * of KeepCarrier option which persists open FD. So detach our queue right after
+                 * device create/attach to make kernel not send the packets to it. The option is
+                 * available for multi-queue devices only.
+                 *
+                 * See https://github.com/systemd/systemd/pull/30504 for details. */
+                struct ifreq detach_request = { .ifr_flags = IFF_DETACH_QUEUE };
+                if (ioctl(fd, TUNSETQUEUE, &detach_request) < 0)
+                        return log_netdev_error_errno(netdev, errno, "TUNSETQUEUE failed: %m");
+        }
+
         if (t->user_name) {
                 const char *user = t->user_name;
                 uid_t uid;
index fa4a0bb42fa1c8441918999b2e377b964e2197a7..8e677cc01085b4b0ac706994cc2b071f942d9d4c 100644 (file)
@@ -792,14 +792,14 @@ static void acquire_ether_link_info(int *fd, LinkInfo *link) {
 
 static void acquire_wlan_link_info(LinkInfo *link) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
-        const char *type = NULL;
         int r, k = 0;
 
         assert(link);
 
-        if (link->sd_device)
-                (void) sd_device_get_devtype(link->sd_device, &type);
-        if (!streq_ptr(type, "wlan"))
+        if (!link->sd_device)
+                return;
+
+        if (!device_is_devtype(link->sd_device, "wlan"))
                 return;
 
         r = sd_genl_socket_open(&genl);
@@ -1458,7 +1458,7 @@ static int dump_dhcp_leases(Table *table, const char *prefix, sd_bus *bus, const
                 if (r < 0)
                         return bus_log_parse_error(r);
 
-                r = sd_dhcp_client_id_to_string(client_id, client_id_sz, &id);
+                r = sd_dhcp_client_id_to_string_from_raw(client_id, client_id_sz, &id);
                 if (r < 0)
                         return bus_log_parse_error(r);
 
@@ -2271,8 +2271,7 @@ static int link_status_one(
         }
 
         if (lease) {
-                const void *client_id;
-                size_t client_id_len;
+                const sd_dhcp_client_id *client_id;
                 const char *tz;
 
                 r = sd_dhcp_lease_get_timezone(lease, &tz);
@@ -2284,11 +2283,11 @@ static int link_status_one(
                                 return table_log_add_error(r);
                 }
 
-                r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
+                r = sd_dhcp_lease_get_client_id(lease, &client_id);
                 if (r >= 0) {
                         _cleanup_free_ char *id = NULL;
 
-                        r = sd_dhcp_client_id_to_string(client_id, client_id_len, &id);
+                        r = sd_dhcp_client_id_to_string(client_id, &id);
                         if (r >= 0) {
                                 r = table_add_many(table,
                                                    TABLE_FIELD, "DHCP4 Client ID",
index 4f27e3845fb353934256d9eed6ce1648b858917b..f0924c93228f8b716768c7a2440dbf503e3a4065 100644 (file)
@@ -1087,20 +1087,16 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
         return 1;
 }
 
-int address_remove(Address *address) {
+int address_remove(Address *address, Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
-        Request *req;
-        Link *link;
         int r;
 
         assert(address);
         assert(IN_SET(address->family, AF_INET, AF_INET6));
-        assert(address->link);
-        assert(address->link->ifindex > 0);
-        assert(address->link->manager);
-        assert(address->link->manager->rtnl);
-
-        link = address->link;
+        assert(link);
+        assert(link->ifindex > 0);
+        assert(link->manager);
+        assert(link->manager->rtnl);
 
         log_address_debug(address, "Removing", link);
 
@@ -1122,8 +1118,6 @@ int address_remove(Address *address) {
         link_ref(link);
 
         address_enter_removing(address);
-        if (address_get_request(link, address, &req) >= 0)
-                address_enter_removing(req->userdata);
 
         /* The operational state is determined by address state and carrier state. Hence, if we remove
          * an address, the operational state may be changed. */
@@ -1131,16 +1125,30 @@ int address_remove(Address *address) {
         return 0;
 }
 
-int address_remove_and_drop(Address *address) {
-        if (!address)
-                return 0;
+int address_remove_and_cancel(Address *address, Link *link) {
+        bool waiting = false;
+        Request *req;
+
+        assert(address);
+        assert(link);
+        assert(link->manager);
+
+        /* If the address is remembered by the link, then use the remembered object. */
+        (void) address_get(link, address, &address);
 
-        address_cancel_request(address);
+        /* Cancel the request for the address.  If the request is already called but we have not received the
+         * notification about the request, then explicitly remove the address. */
+        if (address_get_request(link, address, &req) >= 0) {
+                waiting = req->waiting_reply;
+                request_detach(link->manager, req);
+                address_cancel_requesting(address);
+        }
 
-        if (address_exists(address))
-                return address_remove(address);
+        /* If we know the address will come or already exists, remove it. */
+        if (waiting || (address->link && address_exists(address)))
+                return address_remove(address, link);
 
-        return address_drop(address);
+        return 0;
 }
 
 bool link_address_is_dynamic(const Link *link, const Address *address) {
@@ -1206,7 +1214,6 @@ int link_drop_ipv6ll_addresses(Link *link) {
                 _cleanup_(address_freep) Address *a = NULL;
                 unsigned char flags, prefixlen;
                 struct in6_addr address;
-                Address *existing;
                 int ifindex;
 
                 /* NETLINK_GET_STRICT_CHK socket option is supported since kernel 4.20. To support
@@ -1252,15 +1259,7 @@ int link_drop_ipv6ll_addresses(Link *link) {
                 a->prefixlen = prefixlen;
                 a->flags = flags;
 
-                if (address_get(link, a, &existing) < 0) {
-                        r = address_add(link, a);
-                        if (r < 0)
-                                return r;
-
-                        existing = TAKE_PTR(a);
-                }
-
-                r = address_remove(existing);
+                r = address_remove(a, link);
                 if (r < 0)
                         return r;
         }
@@ -1320,7 +1319,7 @@ int link_drop_foreign_addresses(Link *link) {
                 if (!address_is_marked(address))
                         continue;
 
-                RET_GATHER(r, address_remove(address));
+                RET_GATHER(r, address_remove(address, link));
         }
 
         return r;
@@ -1341,7 +1340,7 @@ int link_drop_managed_addresses(Link *link) {
                 if (!address_exists(address))
                         continue;
 
-                RET_GATHER(r, address_remove(address));
+                RET_GATHER(r, address_remove(address, link));
         }
 
         return r;
@@ -1644,27 +1643,6 @@ int link_request_static_addresses(Link *link) {
         return 0;
 }
 
-void address_cancel_request(Address *address) {
-        Request req;
-
-        assert(address);
-        assert(address->link);
-
-        if (!address_is_requesting(address))
-                return;
-
-        req = (Request) {
-                .link = address->link,
-                .type = REQUEST_TYPE_ADDRESS,
-                .userdata = address,
-                .hash_func = (hash_func_t) address_hash_func,
-                .compare_func = (compare_func_t) address_compare_func,
-        };
-
-        request_detach(address->link->manager, &req);
-        address_cancel_requesting(address);
-}
-
 int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
         _cleanup_(address_freep) Address *tmp = NULL;
         struct ifa_cacheinfo cinfo;
index 8273184d868f14654a0a908a90d975d381014199..53e7a798212578e60ffc120bb071211b59835217 100644 (file)
@@ -89,8 +89,8 @@ Address* address_free(Address *address);
 int address_get(Link *link, const Address *in, Address **ret);
 int address_get_harder(Link *link, const Address *in, Address **ret);
 int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
-int address_remove(Address *address);
-int address_remove_and_drop(Address *address);
+int address_remove(Address *address, Link *link);
+int address_remove_and_cancel(Address *address, Link *link);
 int address_dup(const Address *src, Address **ret);
 bool address_is_ready(const Address *a);
 bool link_check_addresses_ready(Link *link, NetworkConfigSource source);
@@ -114,7 +114,6 @@ static inline int link_get_ipv4_address(Link *link, const struct in_addr *addres
 int manager_get_address(Manager *manager, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret);
 bool manager_has_address(Manager *manager, int family, const union in_addr_union *address);
 
-void address_cancel_request(Address *address);
 int link_request_address(
                 Link *link,
                 const Address *address,
index 3c052675217611d5c9179bb727a49eb98a33970f..0deffa46510d5df536c6b2d618a22a1aad86bc8c 100644 (file)
@@ -267,8 +267,6 @@ int bridge_vlan_set_message(Link *link, sd_netlink_message *m, bool is_set) {
         return 0;
 }
 
-#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
-
 int link_update_bridge_vlan(Link *link, sd_netlink_message *m) {
         _cleanup_free_ void *data = NULL;
         size_t len;
index 080b15387c3a128e7d993496db98470b53e9414b..ac8baeb94ca063c8c25a5057a860cc1abf421bd0 100644 (file)
@@ -5,7 +5,6 @@
 
 #include "bus-error.h"
 #include "bus-locator.h"
-#include "dhcp-identifier.h"
 #include "dhcp-option.h"
 #include "dhcp6-internal.h"
 #include "escape.h"
index 6e3f3b2a1ef25eed4b7d839c3d9f64b8f31fb6a3..f888e03b961876d52894139ff0915ef69cb58984 100644 (file)
@@ -4,7 +4,7 @@
 #include <netinet/in.h>
 
 #include "conf-parser.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
 #include "in-addr-util.h"
 #include "set.h"
 #include "time-util.h"
index ad49f2ee8b2b50f125cafd931d151be9a43525c0..f5d97dd7af2399903a6e559b5cf0632b04d23510 100644 (file)
@@ -192,7 +192,7 @@ int dhcp_pd_remove(Link *link, bool only_marked) {
 
                         link_remove_dhcp_pd_subnet_prefix(link, &prefix);
 
-                        RET_GATHER(ret, address_remove_and_drop(address));
+                        RET_GATHER(ret, address_remove_and_cancel(address, link));
                 }
         }
 
index e3397c32082d55267f2d611cc51cbd60bb3ec649..470e5599e27d28e0e8284fb23b5bb2fe2937e263 100644 (file)
@@ -3,7 +3,7 @@
 #include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "bus-util.h"
-#include "dhcp-server-internal.h"
+#include "dhcp-server-lease-internal.h"
 #include "networkd-dhcp-server-bus.h"
 #include "networkd-link-bus.h"
 #include "networkd-manager.h"
@@ -19,7 +19,7 @@ static int property_get_leases(
                 sd_bus_error *error) {
         Link *l = ASSERT_PTR(userdata);
         sd_dhcp_server *s;
-        DHCPLease *lease;
+        sd_dhcp_server_lease *lease;
         int r;
 
         assert(reply);
@@ -44,7 +44,7 @@ static int property_get_leases(
                 if (r < 0)
                         return r;
 
-                r = sd_bus_message_append_array(reply, 'y', lease->client_id.data, lease->client_id.length);
+                r = sd_bus_message_append_array(reply, 'y', lease->client_id.raw, lease->client_id.size);
                 if (r < 0)
                         return r;
 
index bca426f623774c2df5bcdcc6af05fbc9e7aa4a68..8aee30e7266435f7f9ec4b7bfc41945caa28a739 100644 (file)
@@ -263,7 +263,7 @@ static int dhcp4_remove_address_and_routes(Link *link, bool only_marked) {
                 if (only_marked && !address_is_marked(address))
                         continue;
 
-                RET_GATHER(ret, address_remove_and_drop(address));
+                RET_GATHER(ret, address_remove_and_cancel(address, link));
         }
 
         return ret;
index 1227a72f6dc4998df926eec5359f00b1199f217a..6d8b3c3ce26bc6f92f510f9ad954ade7ad0854c0 100644 (file)
@@ -71,7 +71,7 @@ static int dhcp6_remove(Link *link, bool only_marked) {
                 if (only_marked && !address_is_marked(address))
                         continue;
 
-                RET_GATHER(ret, address_remove_and_drop(address));
+                RET_GATHER(ret, address_remove_and_cancel(address, link));
         }
 
         return ret;
@@ -827,7 +827,7 @@ int link_serialize_dhcp6_client(Link *link, FILE *f) {
         if (r >= 0)
                 fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid);
 
-        r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid);
+        r = sd_dhcp6_client_get_duid_as_string(link->dhcp6_client, &duid);
         if (r >= 0)
                 fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid);
 
index 3d5e2036aa764b66325e6ffd31ddaaf3ed831761..de03293fff330fdfae5c8e8ef282382c0eb12ac2 100644 (file)
@@ -92,7 +92,9 @@ static int static_ipv4acd_address_remove(Link *link, Address *address, bool on_c
         else
                 log_link_debug(link, "Removing address %s, as the ACD client is stopped.", IN4_ADDR_TO_STRING(&address->in_addr.in));
 
-        r = address_remove(address);
+        /* Do not call address_remove_and_cancel() here. Otherwise, the request is cancelled, and the
+         * interface may be in configured state without the address. */
+        r = address_remove(address, link);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to remove address %s: %m", IN4_ADDR_TO_STRING(&address->in_addr.in));
 
index c35738252bb15fdb8529b7204d1bcc2183314290..629a3734095f2f1c872f154a07c1ed0ac7b96155 100644 (file)
@@ -57,7 +57,6 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) {
 
 static int ipv4ll_address_lost(Link *link) {
         _cleanup_(address_freep) Address *address = NULL;
-        Address *existing;
         int r;
 
         assert(link);
@@ -70,19 +69,10 @@ static int ipv4ll_address_lost(Link *link) {
         if (r < 0)
                 return r;
 
-        if (address_get(link, address, &existing) < 0)
-                return 0;
-
-        if (existing->source != NETWORK_CONFIG_SOURCE_IPV4LL)
-                return 0;
-
-        if (!address_exists(existing))
-                return 0;
-
         log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR,
                        IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
 
-        return address_remove(existing);
+        return address_remove_and_cancel(address, link);
 }
 
 static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
index bc7e51ebe305e391885b762c49d3c71b7fd1c02d..d972588b3ea0d5017ad8d93634ebb4699da7da0b 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <linux/nexthop.h>
 
-#include "dhcp-server-internal.h"
+#include "dhcp-server-lease-internal.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
 #include "dns-domain.h"
@@ -892,7 +892,7 @@ static int pref64_append_json(Link *link, JsonVariant **v) {
 
 static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) {
         _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
-        DHCPLease *lease;
+        sd_dhcp_server_lease *lease;
         int r;
 
         assert(link);
@@ -909,8 +909,8 @@ static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) {
                                 JSON_BUILD_OBJECT(
                                                 JSON_BUILD_PAIR_BYTE_ARRAY(
                                                                 "ClientId",
-                                                                lease->client_id.data,
-                                                                lease->client_id.length),
+                                                                lease->client_id.raw,
+                                                                lease->client_id.size),
                                                 JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address),
                                                 JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname),
                                                 JSON_BUILD_PAIR_FINITE_USEC(
@@ -924,7 +924,7 @@ static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) {
 
 static int dhcp_server_static_leases_append_json(Link *link, JsonVariant **v) {
         _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
-        DHCPLease *lease;
+        sd_dhcp_server_lease *lease;
         int r;
 
         assert(link);
@@ -941,8 +941,8 @@ static int dhcp_server_static_leases_append_json(Link *link, JsonVariant **v) {
                                JSON_BUILD_OBJECT(
                                                JSON_BUILD_PAIR_BYTE_ARRAY(
                                                                "ClientId",
-                                                               lease->client_id.data,
-                                                               lease->client_id.length),
+                                                               lease->client_id.raw,
+                                                               lease->client_id.size),
                                                JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address)));
                 if (r < 0)
                         return r;
index 5391b4bcf4c855b7ec5e2ed13e6648e479bbc7a9..26fa8a115d48441e48baf409badd616198e18036 100644 (file)
@@ -17,7 +17,6 @@
 #include "bus-util.h"
 #include "device-private.h"
 #include "device-util.h"
-#include "dhcp-identifier.h"
 #include "dhcp-lease-internal.h"
 #include "env-file.h"
 #include "ethtool-util.h"
index c8df73d444249904f54bb0f32f916c1988a72dc8..e81905ad98a99deedde2a34a08e8199290b6c943 100644 (file)
@@ -164,7 +164,6 @@ static int manager_connect_bus(Manager *m) {
 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);
@@ -173,20 +172,12 @@ static int manager_process_uevent(sd_device_monitor *monitor, sd_device *device,
         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"))
+        if (device_in_subsystem(device, "net"))
                 r = manager_udev_process_link(m, device, action);
-        else if (streq(s, "ieee80211"))
+        else if (device_in_subsystem(device, "ieee80211"))
                 r = manager_udev_process_wiphy(m, device, action);
-        else if (streq(s, "rfkill"))
+        else if (device_in_subsystem(device, "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));
index 2554e0e0310f888ee23a49250d577ee96a6f30b4..3f3569f44d5a2e27f5824bc52ae8ba04ae47526d 100644 (file)
@@ -8,7 +8,7 @@
 #include "sd-netlink.h"
 #include "sd-resolve.h"
 
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
 #include "firewall-util.h"
 #include "hashmap.h"
 #include "networkd-link.h"
index ddd60a1e574914d027e333fa8120458b2ec6df7d..369e205a99a229b49c346772c1ac8a1dac761bf3 100644 (file)
@@ -1137,7 +1137,7 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
                 if (address->lifetime_valid_usec >= timestamp_usec)
                         continue; /* the address is still valid */
 
-                r = address_remove_and_drop(address);
+                r = address_remove_and_cancel(address, link);
                 if (r < 0)
                         RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC address, ignoring: %m"));
         }
index c3f0e6416044a9854ba7f5744e20c089baeb3fa7..ab11047f7e807266233aad2a6adeb5179bcd00d0 100644 (file)
@@ -206,7 +206,7 @@ Route.TCPAdvertisedMaximumSegmentSize,       config_parse_tcp_advmss,
 Route.TCPCongestionControlAlgorithm,         config_parse_tcp_congestion,                              0,                             0
 Route.QuickAck,                              config_parse_route_boolean,                               0,                             0
 Route.FastOpenNoCookie,                      config_parse_route_boolean,                               0,                             0
-Route.TTLPropagate,                          config_parse_route_boolean,                               0,                             0
+Route.TTLPropagate,                          config_parse_warn_compat,                                 DISABLED_LEGACY,               0
 Route.MultiPathRoute,                        config_parse_multipath_route,                             0,                             0
 Route.NextHop,                               config_parse_route_nexthop,                               0,                             0
 NextHop.Id,                                  config_parse_nexthop_id,                                  0,                             0
index 14cd3d93b7b7f08b870a43b3341c04c17ee02ed3..6fafe42c0f857b9c76bdb21698bd332953f653c7 100644 (file)
@@ -137,11 +137,8 @@ static int request_new(
         assert(process);
 
         req = new(Request, 1);
-        if (!req) {
-                if (free_func)
-                        free_func(userdata);
+        if (!req)
                 return -ENOMEM;
-        }
 
         *req = (Request) {
                 .n_ref = 1,
@@ -183,12 +180,19 @@ int netdev_queue_request(
                 request_process_func_t process,
                 Request **ret) {
 
+        int r;
+
         assert(netdev);
 
-        return request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
-                           netdev_ref(netdev), (mfree_func_t) netdev_unref,
-                           trivial_hash_func, trivial_compare_func,
-                           process, NULL, NULL, ret);
+        r = request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
+                        netdev, (mfree_func_t) netdev_unref,
+                        trivial_hash_func, trivial_compare_func,
+                        process, NULL, NULL, ret);
+        if (r <= 0)
+                return r;
+
+        netdev_ref(netdev);
+        return 1;
 }
 
 int link_queue_request_full(
index 9f93e0f04bbb3e0958682ab66e5759a5746da4da..e7cc252243b7f76bcbb61e99c6acb49f062cfa28 100644 (file)
@@ -38,7 +38,6 @@ int route_new(Route **ret) {
                 .quickack = -1,
                 .fast_open_no_cookie = -1,
                 .gateway_onlink = -1,
-                .ttl_propagate = -1,
         };
 
         *ret = TAKE_PTR(route);
@@ -1188,12 +1187,6 @@ static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link
                         return r;
         }
 
-        if (route->ttl_propagate >= 0) {
-                r = sd_netlink_message_append_u8(m, RTA_TTL_PROPAGATE, route->ttl_propagate);
-                if (r < 0)
-                        return r;
-        }
-
         r = sd_netlink_message_open_container(m, RTA_METRICS);
         if (r < 0)
                 return r;
@@ -2417,8 +2410,6 @@ int config_parse_route_boolean(
                 n->quickack = r;
         else if (streq(lvalue, "FastOpenNoCookie"))
                 n->fast_open_no_cookie = r;
-        else if (streq(lvalue, "TTLPropagate"))
-                n->ttl_propagate = r;
         else
                 assert_not_reached();
 
index 3d85889a2fa753291a123ee1b3724f946b8cc146..2e7f6290630dcb6fe51a6ec3ebf3e3e40ddbefe3 100644 (file)
@@ -36,7 +36,6 @@ struct Route {
         uint32_t gw_weight;
         int quickack;
         int fast_open_no_cookie;
-        int ttl_propagate;
 
         unsigned char dst_prefixlen;
         unsigned char src_prefixlen;
index 13f2d7202eb2b9c06d2d387f1f12068f9d2ab29b..441713fa0c876700e36a68b61b10cb704b5bd6c5 100644 (file)
@@ -118,11 +118,7 @@ static int link_get_wiphy(Link *link, Wiphy **ret) {
         if (!link->dev)
                 return -ENODEV;
 
-        r = sd_device_get_devtype(link->dev, &s);
-        if (r < 0)
-                return r;
-
-        if (!streq_ptr(s, "wlan"))
+        if (!device_is_devtype(link->dev, "wlan"))
                 return -EOPNOTSUPP;
 
         r = sd_device_new_child(&phy, link->dev, "phy80211");
index 4d3783e00a943e44b3fa214be2d656270d0efa5e..9e53c51f1ac2e295b3ee17ef14fdc08e7c54b974 100644 (file)
 #include "umask-util.h"
 #include "unit-name.h"
 #include "user-util.h"
+#include "vpick.h"
 
 /* The notify socket inside the container it can use to talk to nspawn using the sd_notify(3) protocol */
 #define NSPAWN_NOTIFY_SOCKET_PATH "/run/host/notify"
@@ -2911,14 +2912,72 @@ static int on_request_stop(sd_bus_message *m, void *userdata, sd_bus_error *erro
         return 0;
 }
 
+static int pick_paths(void) {
+        int r;
+
+        if (arg_directory) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                PickFilter filter = pick_filter_image_dir;
+
+                filter.architecture = arg_architecture;
+
+                r = path_pick_update_warn(
+                                &arg_directory,
+                                &filter,
+                                PICK_ARCHITECTURE|PICK_TRIES,
+                                &result);
+                if (r < 0) {
+                        /* Accept ENOENT here so that the --template= logic can work */
+                        if (r != -ENOENT)
+                                return r;
+                } else
+                        arg_architecture = result.architecture;
+        }
+
+        if (arg_image) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                PickFilter filter = pick_filter_image_raw;
+
+                filter.architecture = arg_architecture;
+
+                r = path_pick_update_warn(
+                                &arg_image,
+                                &filter,
+                                PICK_ARCHITECTURE|PICK_TRIES,
+                                &result);
+                if (r < 0)
+                        return r;
+
+                arg_architecture = result.architecture;
+        }
+
+        if (arg_template) {
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                PickFilter filter = pick_filter_image_dir;
+
+                filter.architecture = arg_architecture;
+
+                r = path_pick_update_warn(
+                                &arg_template,
+                                &filter,
+                                PICK_ARCHITECTURE,
+                                &result);
+                if (r < 0)
+                        return r;
+
+                arg_architecture = result.architecture;
+        }
+
+        return 0;
+}
+
 static int determine_names(void) {
         int r;
 
         if (arg_template && !arg_directory && arg_machine) {
 
-                /* If --template= was specified then we should not
-                 * search for a machine, but instead create a new one
-                 * in /var/lib/machine. */
+                /* If --template= was specified then we should not search for a machine, but instead create a
+                 * new one in /var/lib/machine. */
 
                 arg_directory = path_join("/var/lib/machines", arg_machine);
                 if (!arg_directory)
@@ -5406,6 +5465,10 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 goto finish;
 
+        r = pick_paths();
+        if (r < 0)
+                goto finish;
+
         r = determine_names();
         if (r < 0)
                 goto finish;
index 9c294f4e5fd8ab6e39da1868e7ac5c331257630d..404256c2f88b65388d177f0c4e3881b913361088 100644 (file)
@@ -7,7 +7,7 @@
  * http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml.
  */
 enum {
-        /* Normal records */
+        /* 0 is reserved */
         DNS_TYPE_A          = 0x01,
         DNS_TYPE_NS,
         DNS_TYPE_MD,
@@ -60,7 +60,8 @@ enum {
         DNS_TYPE_NSEC3,
         DNS_TYPE_NSEC3PARAM,
         DNS_TYPE_TLSA,
-
+        DNS_TYPE_SMIMEA, /* RFC 8162 */
+        /* 0x36 (54) is not assigned */
         DNS_TYPE_HIP        = 0x37,
         DNS_TYPE_NINFO,
         DNS_TYPE_RKEY,
@@ -68,15 +69,23 @@ enum {
         DNS_TYPE_CDS,
         DNS_TYPE_CDNSKEY,
         DNS_TYPE_OPENPGPKEY,
-
+        DNS_TYPE_CSYNC,
+        DNS_TYPE_ZONEMD,
+        DNS_TYPE_SVCB, /* RFC 9460 */
+        DNS_TYPE_HTTPS, /* RFC 9460 */
+        /* 0x42…0x62 (66…98) are not assigned */
         DNS_TYPE_SPF        = 0x63,
+        DNS_TYPE_UINFO,
+        DNS_TYPE_UID,
+        DNS_TYPE_GID,
+        DNS_TYPE_UNSPEC,
         DNS_TYPE_NID,
         DNS_TYPE_L32,
         DNS_TYPE_L64,
         DNS_TYPE_LP,
         DNS_TYPE_EUI48,
         DNS_TYPE_EUI64,
-
+        /* 0x6e…0xf8 (110…248) are not assigned */
         DNS_TYPE_TKEY       = 0xF9,
         DNS_TYPE_TSIG,
         DNS_TYPE_IXFR,
@@ -86,15 +95,24 @@ enum {
         DNS_TYPE_ANY,
         DNS_TYPE_URI,
         DNS_TYPE_CAA,
+        DNS_TYPE_AVC,
+        DNS_TYPE_DOA,
+        DNS_TYPE_AMTRELAY,
+        DNS_TYPE_RESINFO,
+        /* 0x106…0x7fff (262…32767) are not assigned */
         DNS_TYPE_TA         = 0x8000,
         DNS_TYPE_DLV,
-
+        /* 32770…65279 are not assigned */
+        /* 65280…65534 are for private use */
+        /* 65535 is reserved */
         _DNS_TYPE_MAX,
         _DNS_TYPE_INVALID = -EINVAL,
 };
 
-assert_cc(DNS_TYPE_SSHFP == 44);
-assert_cc(DNS_TYPE_TLSA == 52);
+assert_cc(DNS_TYPE_SMIMEA == 53);
+assert_cc(DNS_TYPE_HTTPS == 65);
+assert_cc(DNS_TYPE_EUI64 == 109);
+assert_cc(DNS_TYPE_RESINFO == 261);
 assert_cc(DNS_TYPE_ANY == 255);
 
 /* DNS record classes, see RFC 1035 */
index c5c5037cec6193296ad53c9bab568cf81c16125d..ec92406a7e5b22c2e4fb1ba165b88ed2b29763e3 100644 (file)
@@ -1303,6 +1303,10 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p, usec_t ts,
                         if (!j->shared_owner)
                                 continue;
 
+                        /* Ignore cached goodby packet. See on_mdns_packet() and RFC 6762 section 10.1. */
+                        if (j->rr->ttl <= 1)
+                                continue;
+
                         /* RFC6762 7.1: Don't append records with less than half the TTL remaining
                          * as known answers. */
                         if (usec_sub_unsigned(j->until, ts) < j->rr->ttl * USEC_PER_SEC / 2)
index 96b379ee057718c54ea58f3b679c9605a4c9d7b9..e3c70e88e27d95a78be669ec64af1e38d903c271 100644 (file)
@@ -74,8 +74,12 @@ static void dns_transaction_close_connection(
          * and the reply we might still get from the server will be eaten up instead of resulting in an ICMP
          * port unreachable error message. */
 
-        /* Skip the graveyard stuff when we're shutting down, since that requires running event loop */
-        if (!t->scope->manager->event || sd_event_get_state(t->scope->manager->event) == SD_EVENT_FINISHED)
+        /* Skip the graveyard stuff when we're shutting down, since that requires running event loop.
+         * Note that this is also called from dns_transaction_free(). In that case, scope may be NULL. */
+        if (!t->scope ||
+            !t->scope->manager ||
+            !t->scope->manager->event ||
+            sd_event_get_state(t->scope->manager->event) == SD_EVENT_FINISHED)
                 use_graveyard = false;
 
         if (use_graveyard && t->dns_udp_fd >= 0 && t->sent && !t->received) {
index 8fac351ee6da033f97041a2cadc27227f83c6bff..9469bdac8690a8a2ca7b1fca128022803834dd30 100644 (file)
@@ -45,7 +45,7 @@ int manager_llmnr_start(Manager *m) {
         if (r < 0)
                 return r;
 
-        if (socket_ipv6_is_supported()) {
+        if (socket_ipv6_is_enabled()) {
                 r = manager_llmnr_ipv6_udp_fd(m);
                 if (r == -EADDRINUSE)
                         goto eaddrinuse;
index b63073af7fd40579156db7323fa725626a9f70ec..3e6e83fe62586d6f3a196242f5f2e953a8c58983 100644 (file)
@@ -36,7 +36,7 @@ int manager_mdns_start(Manager *m) {
         if (r < 0)
                 return r;
 
-        if (socket_ipv6_is_supported()) {
+        if (socket_ipv6_is_enabled()) {
                 r = manager_mdns_ipv6_fd(m);
                 if (r == -EADDRINUSE)
                         goto eaddrinuse;
index 41f6b97e02861934466f599914585305ea44a833..bbb8b81011a15dc1cbcf83e5f162e6506cf82b33 100644 (file)
@@ -94,7 +94,7 @@ int asynchronous_close(int fd) {
 
         pid = clone_with_nested_stack(close_func, CLONE_FILES | ((v & NEED_DOUBLE_FORK) ? 0 : SIGCHLD), UINT_TO_PTR(v));
         if (pid < 0)
-                assert_se(close_nointr(fd) != -EBADF); /* local fallback */
+                safe_close(fd); /* local fallback */
         else if (v & NEED_DOUBLE_FORK) {
 
                 /* Reap the intermediate child. Key here is that we specify __WCLONE, since we didn't ask for
index 347cd787c3a21173114b1cc5f7a95c3652f646c5..0bc68e1721e215acec5dd257d343ee35e941caaa 100644 (file)
@@ -56,23 +56,12 @@ static int fd_get_devnum(int fd, BlockDeviceLookupFlag flags, dev_t *ret) {
 }
 
 int block_device_is_whole_disk(sd_device *dev) {
-        const char *s;
-        int r;
-
         assert(dev);
 
-        r = sd_device_get_subsystem(dev, &s);
-        if (r < 0)
-                return r;
-
-        if (!streq(s, "block"))
+        if (!device_in_subsystem(dev, "block"))
                 return -ENOTBLK;
 
-        r = sd_device_get_devtype(dev, &s);
-        if (r < 0)
-                return r;
-
-        return streq(s, "disk");
+        return device_is_devtype(dev, "disk");
 }
 
 int block_device_get_whole_disk(sd_device *dev, sd_device **ret) {
index 6704e1ef3dfa8ae5b070832fd3d8798c86beebb5..99b1cc7c709922503d4e59bc8753ae0183cad294 100644 (file)
@@ -164,9 +164,11 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
 
                         bus_print_property_value(name, expected_value, flags, "[not set]");
 
-                else if ((ENDSWITH_SET(name, "MemoryLow", "MemoryMin", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryZSwapMax", "MemoryLimit") &&
+                else if ((ENDSWITH_SET(name, "MemoryLow", "MemoryMin",
+                                             "MemoryHigh", "MemoryMax",
+                                             "MemorySwapMax", "MemoryZSwapMax", "MemoryLimit") &&
                           u == CGROUP_LIMIT_MAX) ||
-                         (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
+                         (endswith(name, "TasksMax") && u == UINT64_MAX) ||
                          (startswith(name, "Limit") && u == UINT64_MAX) ||
                          (startswith(name, "DefaultLimit") && u == UINT64_MAX))
 
index b3280c15dfede0e5a974c56e6565375d9bb2fdae..70f2043347f1f6fb7295b2075f60f68c271792f4 100644 (file)
@@ -1,13 +1,13 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "alloc-util.h"
-#include "bus-wait-for-jobs.h"
-#include "set.h"
-#include "bus-util.h"
 #include "bus-internal.h"
-#include "unit-def.h"
+#include "bus-util.h"
+#include "bus-wait-for-jobs.h"
 #include "escape.h"
+#include "set.h"
 #include "strv.h"
+#include "unit-def.h"
 
 typedef struct BusWaitForJobs {
         sd_bus *bus;
@@ -23,60 +23,56 @@ typedef struct BusWaitForJobs {
         sd_bus_slot *slot_disconnected;
 } BusWaitForJobs;
 
+BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d) {
+        if (!d)
+                return NULL;
+
+        set_free(d->jobs);
+
+        sd_bus_slot_unref(d->slot_disconnected);
+        sd_bus_slot_unref(d->slot_job_removed);
+
+        sd_bus_unref(d->bus);
+
+        free(d->name);
+        free(d->result);
+
+        return mfree(d);
+}
+
 static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         assert(m);
 
-        log_error("Warning! D-Bus connection terminated.");
+        log_warning("D-Bus connection terminated while waiting for jobs.");
         sd_bus_close(sd_bus_message_get_bus(m));
 
         return 0;
 }
 
 static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
-        const char *path, *unit, *result;
         BusWaitForJobs *d = ASSERT_PTR(userdata);
-        uint32_t id;
-        char *found;
+        _cleanup_free_ char *job_found = NULL;
+        const char *path, *unit, *result;
         int r;
 
         assert(m);
 
-        r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
+        r = sd_bus_message_read(m, "uoss", /* id = */ NULL, &path, &unit, &result);
         if (r < 0) {
                 bus_log_parse_error(r);
                 return 0;
         }
 
-        found = set_remove(d->jobs, (char*) path);
-        if (!found)
+        job_found = set_remove(d->jobs, (char*) path);
+        if (!job_found)
                 return 0;
 
-        free(found);
-
-        (void) free_and_strdup(&d->result, empty_to_null(result));
-
         (void) free_and_strdup(&d->name, empty_to_null(unit));
+        (void) free_and_strdup(&d->result, empty_to_null(result));
 
         return 0;
 }
 
-BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d) {
-        if (!d)
-                return NULL;
-
-        set_free(d->jobs);
-
-        sd_bus_slot_unref(d->slot_disconnected);
-        sd_bus_slot_unref(d->slot_job_removed);
-
-        sd_bus_unref(d->bus);
-
-        free(d->name);
-        free(d->result);
-
-        return mfree(d);
-}
-
 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
         int r;
@@ -92,9 +88,8 @@ int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
                 .bus = sd_bus_ref(bus),
         };
 
-        /* When we are a bus client we match by sender. Direct
-         * connections OTOH have no initialized sender field, and
-         * hence we ignore the sender then */
+        /* When we are a bus client we match by sender. Direct connections OTOH have no initialized sender
+         * field, and hence we ignore the sender then */
         r = sd_bus_match_signal_async(
                         bus,
                         &d->slot_job_removed,
@@ -138,12 +133,12 @@ static int bus_process_wait(sd_bus *bus) {
         }
 }
 
-static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
+static int bus_job_get_service_result(BusWaitForJobs *d, char **ret) {
         _cleanup_free_ char *dbus_path = NULL;
 
         assert(d);
         assert(d->name);
-        assert(result);
+        assert(ret);
 
         if (!endswith(d->name, ".service"))
                 return -EINVAL;
@@ -158,67 +153,57 @@ static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
                                           "org.freedesktop.systemd1.Service",
                                           "Result",
                                           NULL,
-                                          result);
+                                          ret);
 }
 
 static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
-        _cleanup_free_ char *service_shell_quoted = NULL;
-        const char *systemctl = "systemctl", *journalctl = "journalctl";
 
         static const struct {
                 const char *result, *explanation;
         } explanations[] = {
-                { "resources",   "of unavailable resources or another system error" },
+                { "resources",   "of unavailable resources or another system error"                      },
                 { "protocol",    "the service did not take the steps required by its unit configuration" },
-                { "timeout",     "a timeout was exceeded" },
-                { "exit-code",   "the control process exited with error code" },
-                { "signal",      "a fatal signal was delivered to the control process" },
+                { "timeout",     "a timeout was exceeded"                                                },
+                { "exit-code",   "the control process exited with error code"                            },
+                { "signal",      "a fatal signal was delivered to the control process"                   },
                 { "core-dump",   "a fatal signal was delivered causing the control process to dump core" },
-                { "watchdog",    "the service failed to send watchdog ping" },
-                { "start-limit", "start of the service was attempted too often" }
+                { "watchdog",    "the service failed to send watchdog ping"                              },
+                { "start-limit", "start of the service was attempted too often"                          },
         };
 
+        _cleanup_free_ char *service_shell_quoted = NULL;
+        const char *systemctl = "systemctl", *journalctl = "journalctl";
+
         assert(service);
 
         service_shell_quoted = shell_maybe_quote(service, 0);
 
-        if (!strv_isempty((char**) extra_args)) {
+        if (!strv_isempty((char* const*) extra_args)) {
                 _cleanup_free_ char *t = NULL;
 
-                t = strv_join((char**) extra_args, " ");
+                t = strv_join((char* const*) extra_args, " ");
                 systemctl = strjoina("systemctl ", t ?: "<args>");
                 journalctl = strjoina("journalctl ", t ?: "<args>");
         }
 
-        if (!isempty(result)) {
-                size_t i;
-
-                for (i = 0; i < ELEMENTSOF(explanations); ++i)
-                        if (streq(result, explanations[i].result))
-                                break;
-
-                if (i < ELEMENTSOF(explanations)) {
-                        log_error("Job for %s failed because %s.\n"
-                                  "See \"%s status %s\" and \"%s -xeu %s\" for details.\n",
-                                  service,
-                                  explanations[i].explanation,
-                                  systemctl,
-                                  service_shell_quoted ?: "<service>",
-                                  journalctl,
-                                  service_shell_quoted ?: "<service>");
-                        goto finish;
-                }
-        }
+        if (!isempty(result))
+                FOREACH_ARRAY(i, explanations, ELEMENTSOF(explanations))
+                        if (streq(result, i->result)) {
+                                log_error("Job for %s failed because %s.\n"
+                                          "See \"%s status %s\" and \"%s -xeu %s\" for details.\n",
+                                          service, i->explanation,
+                                          systemctl, service_shell_quoted ?: "<service>",
+                                          journalctl, service_shell_quoted ?: "<service>");
+                                goto extra;
+                        }
 
         log_error("Job for %s failed.\n"
                   "See \"%s status %s\" and \"%s -xeu %s\" for details.\n",
                   service,
-                  systemctl,
-                  service_shell_quoted ?: "<service>",
-                  journalctl,
-                  service_shell_quoted ?: "<service>");
+                  systemctl, service_shell_quoted ?: "<service>",
+                  journalctl, service_shell_quoted ?: "<service>");
 
-finish:
+extra:
         /* For some results maybe additional explanation is required */
         if (streq_ptr(result, "start-limit"))
                 log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
@@ -228,41 +213,53 @@ finish:
 }
 
 static int check_wait_response(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) {
+        int r;
+
         assert(d);
         assert(d->name);
         assert(d->result);
 
+        if (streq(d->result, "done")) {
+                if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
+                        log_info("Job for %s finished.", d->name);
+
+                return 0;
+        } else if (streq(d->result, "skipped")) {
+                if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
+                        log_info("Job for %s was skipped.", d->name);
+
+                return 0;
+        }
+
         if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_ERROR)) {
                 if (streq(d->result, "canceled"))
-                        log_error("Job for %s canceled.", strna(d->name));
+                        log_error("Job for %s canceled.", d->name);
                 else if (streq(d->result, "timeout"))
-                        log_error("Job for %s timed out.", strna(d->name));
+                        log_error("Job for %s timed out.", d->name);
                 else if (streq(d->result, "dependency"))
-                        log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
+                        log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", d->name);
                 else if (streq(d->result, "invalid"))
-                        log_error("%s is not active, cannot reload.", strna(d->name));
+                        log_error("%s is not active, cannot reload.", d->name);
                 else if (streq(d->result, "assert"))
-                        log_error("Assertion failed on job for %s.", strna(d->name));
+                        log_error("Assertion failed on job for %s.", d->name);
                 else if (streq(d->result, "unsupported"))
-                        log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
+                        log_error("Operation on or unit type of %s not supported on this system.", d->name);
                 else if (streq(d->result, "collected"))
-                        log_error("Queued job for %s was garbage collected.", strna(d->name));
+                        log_error("Queued job for %s was garbage collected.", d->name);
                 else if (streq(d->result, "once"))
-                        log_error("Unit %s was started already once and can't be started again.", strna(d->name));
-                else if (!STR_IN_SET(d->result, "done", "skipped")) {
-
-                        if (d->name && endswith(d->name, ".service")) {
-                                _cleanup_free_ char *result = NULL;
-                                int q;
-
-                                q = bus_job_get_service_result(d, &result);
-                                if (q < 0)
-                                        log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
-
-                                log_job_error_with_service_result(d->name, result, extra_args);
-                        } else
-                                log_error("Job failed. See \"journalctl -xe\" for details.");
-                }
+                        log_error("Unit %s was started already once and can't be started again.", d->name);
+                else if (endswith(d->name, ".service")) {
+                        /* Job result is unknown. For services, let's also try Result property. */
+                        _cleanup_free_ char *result = NULL;
+
+                        r = bus_job_get_service_result(d, &result);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to get Result property of unit %s, ignoring: %m",
+                                                d->name);
+
+                        log_job_error_with_service_result(d->name, result, extra_args);
+                } else /* Otherwise we just show a generic message. */
+                        log_error("Job failed. See \"journalctl -xe\" for details.");
         }
 
         if (STR_IN_SET(d->result, "canceled", "collected"))
@@ -279,18 +276,10 @@ static int check_wait_response(BusWaitForJobs *d, WaitJobsFlags flags, const cha
                 return -EOPNOTSUPP;
         else if (streq(d->result, "once"))
                 return -ESTALE;
-        else if (streq(d->result, "done")) {
-                if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
-                        log_info("Job for %s finished.", strna(d->name));
-                return 0;
-        } else if (streq(d->result, "skipped")) {
-                if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
-                        log_info("Job for %s was skipped.", strna(d->name));
-                return 0;
-        }
 
-        return log_debug_errno(SYNTHETIC_ERRNO(EIO),
-                               "Unexpected job result, assuming server side newer than us: %s", d->result);
+        return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
+                               "Unexpected job result '%s' for unit '%s', assuming server side newer than us.",
+                               d->result, d->name);
 }
 
 int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) {
@@ -307,13 +296,11 @@ int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const*
 
                 if (d->name && d->result) {
                         q = check_wait_response(d, flags, extra_args);
-                        /* Return the first error as it is most likely to be
-                         * meaningful. */
-                        if (q < 0 && r == 0)
-                                r = q;
+                        /* Return the first error as it is most likely to be meaningful. */
+                        RET_GATHER(r, q);
 
                         log_full_errno_zerook(LOG_DEBUG, q,
-                                              "Got result %s/%m for job %s", d->result, d->name);
+                                              "Got result %s/%m for job %s.", d->result, d->name);
                 }
 
                 d->name = mfree(d->name);
index 4d56c079ab354fd7959f9136f2fe04dd49da5a6f..2336b1332a89479cbef259fd6fc62b69e7d509e8 100644 (file)
@@ -5,17 +5,17 @@
 
 #include "macro.h"
 
+typedef struct BusWaitForJobs BusWaitForJobs;
+
 typedef enum WaitJobsFlags {
         BUS_WAIT_JOBS_LOG_ERROR   = 1 << 0,
         BUS_WAIT_JOBS_LOG_SUCCESS = 1 << 1,
 } WaitJobsFlags;
 
-typedef struct BusWaitForJobs BusWaitForJobs;
+BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d);
+DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
 
 int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
-BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d);
 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
 int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args);
 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, WaitJobsFlags flags, const char* const* extra_args);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
index 0dd2a295dd9b122d17af8757d3d89a2cd9a17c72..49963ffe3412a553da69e3a838a7ba658481a22c 100644 (file)
@@ -63,7 +63,7 @@ static WaitForItem *wait_for_item_free(WaitForItem *item) {
                                 log_debug_errno(r, "Failed to drop reference to unit %s, ignoring: %m", item->bus_path);
                 }
 
-                assert_se(hashmap_remove(item->parent->items, item->bus_path) == item);
+                assert_se(hashmap_remove_value(item->parent->items, item->bus_path, item));
 
                 if (item->parent->current == item)
                         item->parent->current = NULL;
@@ -109,12 +109,12 @@ static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *e
 
         assert(m);
 
-        log_error("Warning! D-Bus connection terminated.");
+        log_warning("D-Bus connection terminated while waiting for unit.");
 
         bus_wait_for_units_clear(d);
 
         if (d->ready_callback)
-                d->ready_callback(d, false, d->userdata);
+                d->ready_callback(d, BUS_WAIT_FAILURE, d->userdata);
         else /* If no ready callback is specified close the connection so that the event loop exits */
                 sd_bus_close(sd_bus_message_get_bus(m));
 
index 2623e72ced2378381b2a9c4460d63a2f6a29eece..c87eabcbbceb89aaa9ab6d4ebeecf7304fcbba73 100644 (file)
@@ -25,11 +25,11 @@ typedef void (*bus_wait_for_units_ready_callback)(BusWaitForUnits *d, BusWaitFor
 typedef void (*bus_wait_for_units_unit_callback)(BusWaitForUnits *d, const char *unit_path, bool good, void *userdata);
 
 int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret);
+
 BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d);
+DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForUnits*, bus_wait_for_units_free);
 
 BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d);
 void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata);
 int bus_wait_for_units_add_unit(BusWaitForUnits *d, const char *unit, BusWaitForUnitsFlags flags, bus_wait_for_units_unit_callback callback, void *userdata);
 int bus_wait_for_units_run(BusWaitForUnits *d);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForUnits*, bus_wait_for_units_free);
index b3f4c9ab766cb0f61664b58c030de13f637fa218..e7e3a4d71c68c83754f8d885410ee5691c6e1649 100644 (file)
@@ -45,6 +45,7 @@
 #include "strv.h"
 #include "time-util.h"
 #include "utf8.h"
+#include "vpick.h"
 #include "xattr-util.h"
 
 static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
@@ -75,15 +76,20 @@ static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
                             "/usr/lib/confexts\0",
 };
 
-/* Inside the initrd, use a slightly different set of search path (i.e. include .extra/sysext in extension
- * search dir) */
+/* Inside the initrd, use a slightly different set of search path (i.e. include .extra/sysext/ and
+ * .extra/confext/ in extension search dir) */
 static const char* const image_search_path_initrd[_IMAGE_CLASS_MAX] = {
         /* (entries that aren't listed here will get the same search path as for the non initrd-case) */
 
         [IMAGE_SYSEXT] =    "/etc/extensions\0"            /* only place symlinks here */
                             "/run/extensions\0"            /* and here too */
                             "/var/lib/extensions\0"        /* the main place for images */
-                            "/.extra/sysext\0"             /* put sysext picked up by systemd-stub last, since not trusted */
+                            "/.extra/sysext\0",            /* put sysext picked up by systemd-stub last, since not trusted */
+
+        [IMAGE_CONFEXT] =   "/run/confexts\0"              /* only place symlinks here */
+                            "/var/lib/confexts\0"          /* the main place for images */
+                            "/usr/local/lib/confexts\0"
+                            "/.extra/confext\0",           /* put confext picked up by systemd-stub last, since not trusted */
 };
 
 static const char* image_class_suffix_table[_IMAGE_CLASS_MAX] = {
@@ -210,40 +216,60 @@ static int image_new(
         return 0;
 }
 
-static int extract_pretty(
+static int extract_image_basename(
                 const char *path,
-                const char *class_suffix,
-                const char *format_suffix,
-                char **ret) {
+                const char *class_suffix,  /* e.g. ".sysext" (this is an optional suffix) */
+                char **format_suffixes,    /* e.g. ".raw"    (one of these will be required) */
+                char **ret_basename,
+                char **ret_suffix) {
 
-        _cleanup_free_ char *name = NULL;
+        _cleanup_free_ char *name = NULL, *suffix = NULL;
         int r;
 
         assert(path);
-        assert(ret);
 
         r = path_extract_filename(path, &name);
         if (r < 0)
                 return r;
 
-        if (format_suffix) {
-                char *e = endswith(name, format_suffix);
+        if (format_suffixes) {
+                char *e = strv_endswith(name, format_suffixes);
                 if (!e) /* Format suffix is required */
                         return -EINVAL;
 
+                if (ret_suffix) {
+                        suffix = strdup(e);
+                        if (!suffix)
+                                return -ENOMEM;
+                }
+
                 *e = 0;
         }
 
         if (class_suffix) {
                 char *e = endswith(name, class_suffix);
-                if (e) /* Class suffix is optional */
+                if (e) { /* Class suffix is optional */
+                        if (ret_suffix) {
+                                _cleanup_free_ char *j = strjoin(e, suffix);
+                                if (!j)
+                                        return -ENOMEM;
+
+                                free_and_replace(suffix, j);
+                        }
+
                         *e = 0;
+                }
         }
 
         if (!image_name_is_valid(name))
                 return -EINVAL;
 
-        *ret = TAKE_PTR(name);
+        if (ret_suffix)
+                *ret_suffix = TAKE_PTR(suffix);
+
+        if (ret_basename)
+                *ret_basename = TAKE_PTR(name);
+
         return 0;
 }
 
@@ -298,7 +324,12 @@ static int image_make(
                         return 0;
 
                 if (!pretty) {
-                        r = extract_pretty(filename, image_class_suffix_to_string(c), NULL, &pretty_buffer);
+                        r = extract_image_basename(
+                                        filename,
+                                        image_class_suffix_to_string(c),
+                                        /* format_suffix= */ NULL,
+                                        &pretty_buffer,
+                                        /* ret_suffix= */ NULL);
                         if (r < 0)
                                 return r;
 
@@ -385,7 +416,12 @@ static int image_make(
                 (void) fd_getcrtime_at(dfd, filename, AT_SYMLINK_FOLLOW, &crtime);
 
                 if (!pretty) {
-                        r = extract_pretty(filename, image_class_suffix_to_string(c), ".raw", &pretty_buffer);
+                        r = extract_image_basename(
+                                        filename,
+                                        image_class_suffix_to_string(c),
+                                        STRV_MAKE(".raw"),
+                                        &pretty_buffer,
+                                        /* ret_suffix= */ NULL);
                         if (r < 0)
                                 return r;
 
@@ -419,7 +455,12 @@ static int image_make(
                         return 0;
 
                 if (!pretty) {
-                        r = extract_pretty(filename, NULL, NULL, &pretty_buffer);
+                        r = extract_image_basename(
+                                        filename,
+                                        /* class_suffix= */ NULL,
+                                        /* format_suffix= */ NULL,
+                                        &pretty_buffer,
+                                        /* ret_suffix= */ NULL);
                         if (r < 0)
                                 return r;
 
@@ -483,6 +524,37 @@ static const char *pick_image_search_path(ImageClass class) {
         return in_initrd() && image_search_path_initrd[class] ? image_search_path_initrd[class] : image_search_path[class];
 }
 
+static char **make_possible_filenames(ImageClass class, const char *image_name) {
+        _cleanup_strv_free_ char **l = NULL;
+
+        assert(image_name);
+
+        FOREACH_STRING(v_suffix, "", ".v")
+                FOREACH_STRING(format_suffix, "", ".raw") {
+                        _cleanup_free_ char *j = NULL;
+                        const char *class_suffix;
+
+                        class_suffix = image_class_suffix_to_string(class);
+                        if (class_suffix) {
+                                j = strjoin(image_name, class_suffix, format_suffix, v_suffix);
+                                if (!j)
+                                        return NULL;
+
+                                if (strv_consume(&l, TAKE_PTR(j)) < 0)
+                                        return NULL;
+                        }
+
+                        j = strjoin(image_name, format_suffix, v_suffix);
+                        if (!j)
+                                return NULL;
+
+                        if (strv_consume(&l, TAKE_PTR(j)) < 0)
+                                return NULL;
+                }
+
+        return TAKE_PTR(l);
+}
+
 int image_find(ImageClass class,
                const char *name,
                const char *root,
@@ -498,6 +570,10 @@ int image_find(ImageClass class,
         if (!image_name_is_valid(name))
                 return -ENOENT;
 
+        _cleanup_strv_free_ char **names = make_possible_filenames(class, name);
+        if (!names)
+                return -ENOMEM;
+
         NULSTR_FOREACH(path, pick_image_search_path(class)) {
                 _cleanup_free_ char *resolved = NULL;
                 _cleanup_closedir_ DIR *d = NULL;
@@ -514,43 +590,97 @@ int image_find(ImageClass class,
                  * to symlink block devices into the search path. (For now, we disable that when operating
                  * relative to some root directory.) */
                 flags = root ? AT_SYMLINK_NOFOLLOW : 0;
-                if (fstatat(dirfd(d), name, &st, flags) < 0) {
-                        _cleanup_free_ char *raw = NULL;
 
-                        if (errno != ENOENT)
-                                return -errno;
+                STRV_FOREACH(n, names) {
+                        _cleanup_free_ char *fname_buf = NULL;
+                        const char *fname = *n;
 
-                        raw = strjoin(name, ".raw");
-                        if (!raw)
-                                return -ENOMEM;
+                        if (fstatat(dirfd(d), fname, &st, flags) < 0) {
+                                if (errno != ENOENT)
+                                        return -errno;
 
-                        if (fstatat(dirfd(d), raw, &st, flags) < 0) {
-                                if (errno == ENOENT)
+                                continue; /* Vanished while we were looking at it */
+                        }
+
+                        if (endswith(fname, ".raw")) {
+                                if (!S_ISREG(st.st_mode)) {
+                                        log_debug("Ignoring non-regular file '%s' with .raw suffix.", fname);
                                         continue;
+                                }
 
-                                return -errno;
-                        }
+                        } else if (endswith(fname, ".v")) {
 
-                        if (!S_ISREG(st.st_mode))
-                                continue;
+                                if (!S_ISDIR(st.st_mode)) {
+                                        log_debug("Ignoring non-directory file '%s' with .v suffix.", fname);
+                                        continue;
+                                }
+
+                                _cleanup_free_ char *suffix = NULL;
+                                suffix = strdup(ASSERT_PTR(startswith(fname, name)));
+                                if (!suffix)
+                                        return -ENOMEM;
+
+                                *ASSERT_PTR(endswith(suffix, ".v")) = 0;
+
+                                _cleanup_free_ char *vp = path_join(resolved, fname);
+                                if (!vp)
+                                        return -ENOMEM;
+
+                                PickFilter filter = {
+                                        .type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
+                                        .basename = name,
+                                        .architecture = _ARCHITECTURE_INVALID,
+                                        .suffix = suffix,
+                                };
+
+                                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                                r = path_pick(root,
+                                              /* toplevel_fd= */ AT_FDCWD,
+                                              vp,
+                                              &filter,
+                                              PICK_ARCHITECTURE|PICK_TRIES,
+                                              &result);
+                                if (r < 0) {
+                                        log_debug_errno(r, "Failed to pick versioned image on '%s', skipping: %m", vp);
+                                        continue;
+                                }
+                                if (!result.path) {
+                                        log_debug("Found versioned directory '%s', without matching entry, skipping: %m", vp);
+                                        continue;
+                                }
 
-                        r = image_make(class, name, dirfd(d), resolved, raw, &st, ret);
+                                /* Refresh the stat data for the discovered target */
+                                st = result.st;
 
-                } else {
-                        if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode))
+                                _cleanup_free_ char *bn = NULL;
+                                r = path_extract_filename(result.path, &bn);
+                                if (r < 0) {
+                                        log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", result.path);
+                                        continue;
+                                }
+
+                                fname_buf = path_join(fname, bn);
+                                if (!fname_buf)
+                                        return log_oom();
+
+                                fname = fname_buf;
+
+                        } else if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+                                log_debug("Ignoring non-directory and non-block device file '%s' without suffix.", fname);
                                 continue;
+                        }
 
-                        r = image_make(class, name, dirfd(d), resolved, name, &st, ret);
-                }
-                if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
-                        continue;
-                if (r < 0)
-                        return r;
+                        r = image_make(class, name, dirfd(d), resolved, fname, &st, ret);
+                        if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
+                                continue;
+                        if (r < 0)
+                                return r;
 
-                if (ret)
-                        (*ret)->discoverable = true;
+                        if (ret)
+                                (*ret)->discoverable = true;
 
-                return 1;
+                        return 1;
+                }
         }
 
         if (class == IMAGE_MACHINE && streq(name, ".host")) {
@@ -561,7 +691,7 @@ int image_find(ImageClass class,
                 if (ret)
                         (*ret)->discoverable = true;
 
-                return r;
+                return 1;
         }
 
         return -ENOENT;
@@ -608,43 +738,133 @@ int image_discover(
                         return r;
 
                 FOREACH_DIRENT_ALL(de, d, return -errno) {
+                        _cleanup_free_ char *pretty = NULL, *fname_buf = NULL;
                         _cleanup_(image_unrefp) Image *image = NULL;
-                        _cleanup_free_ char *pretty = NULL;
+                        const char *fname = de->d_name;
                         struct stat st;
                         int flags;
 
-                        if (dot_or_dot_dot(de->d_name))
+                        if (dot_or_dot_dot(fname))
                                 continue;
 
                         /* As mentioned above, we follow symlinks on this fstatat(), because we want to
                          * permit people to symlink block devices into the search path. */
                         flags = root ? AT_SYMLINK_NOFOLLOW : 0;
-                        if (fstatat(dirfd(d), de->d_name, &st, flags) < 0) {
+                        if (fstatat(dirfd(d), fname, &st, flags) < 0) {
                                 if (errno == ENOENT)
                                         continue;
 
                                 return -errno;
                         }
 
-                        if (S_ISREG(st.st_mode))
-                                r = extract_pretty(de->d_name, image_class_suffix_to_string(class), ".raw", &pretty);
-                        else if (S_ISDIR(st.st_mode))
-                                r = extract_pretty(de->d_name, image_class_suffix_to_string(class), NULL, &pretty);
-                        else if (S_ISBLK(st.st_mode))
-                                r = extract_pretty(de->d_name, NULL, NULL, &pretty);
-                        else {
-                                log_debug("Skipping directory entry '%s', which is neither regular file, directory nor block device.", de->d_name);
-                                continue;
-                        }
-                        if (r < 0) {
-                                log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", de->d_name);
+                        if (S_ISREG(st.st_mode)) {
+                                r = extract_image_basename(
+                                                fname,
+                                                image_class_suffix_to_string(class),
+                                                STRV_MAKE(".raw"),
+                                                &pretty,
+                                                /* suffix= */ NULL);
+                                if (r < 0) {
+                                        log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", fname);
+                                        continue;
+                                }
+                        } else if (S_ISDIR(st.st_mode)) {
+                                const char *v;
+
+                                v = endswith(fname, ".v");
+                                if (v) {
+                                        _cleanup_free_ char *suffix = NULL, *nov = NULL;
+
+                                        nov = strndup(fname, v - fname); /* Chop off the .v */
+                                        if (!nov)
+                                                return -ENOMEM;
+
+                                        r = extract_image_basename(
+                                                        nov,
+                                                        image_class_suffix_to_string(class),
+                                                        STRV_MAKE(".raw", ""),
+                                                        &pretty,
+                                                        &suffix);
+                                        if (r < 0) {
+                                                log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like a versioned image.", fname);
+                                                continue;
+                                        }
+
+                                        _cleanup_free_ char *vp = path_join(resolved, fname);
+                                        if (!vp)
+                                                return -ENOMEM;
+
+                                        PickFilter filter = {
+                                                .type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
+                                                .basename = pretty,
+                                                .architecture = _ARCHITECTURE_INVALID,
+                                                .suffix = suffix,
+                                        };
+
+                                        _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                                        r = path_pick(root,
+                                                      /* toplevel_fd= */ AT_FDCWD,
+                                                      vp,
+                                                      &filter,
+                                                      PICK_ARCHITECTURE|PICK_TRIES,
+                                                      &result);
+                                        if (r < 0) {
+                                                log_debug_errno(r, "Failed to pick versioned image on '%s', skipping: %m", vp);
+                                                continue;
+                                        }
+                                        if (!result.path) {
+                                                log_debug("Found versioned directory '%s', without matching entry, skipping: %m", vp);
+                                                continue;
+                                        }
+
+                                        /* Refresh the stat data for the discovered target */
+                                        st = result.st;
+
+                                        _cleanup_free_ char *bn = NULL;
+                                        r = path_extract_filename(result.path, &bn);
+                                        if (r < 0) {
+                                                log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", result.path);
+                                                continue;
+                                        }
+
+                                        fname_buf = path_join(fname, bn);
+                                        if (!fname_buf)
+                                                return log_oom();
+
+                                        fname = fname_buf;
+                                } else {
+                                        r = extract_image_basename(
+                                                        fname,
+                                                        image_class_suffix_to_string(class),
+                                                        /* format_suffix= */ NULL,
+                                                        &pretty,
+                                                        /* ret_suffix= */ NULL);
+                                        if (r < 0) {
+                                                log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", fname);
+                                                continue;
+                                        }
+                                }
+
+                        } else if (S_ISBLK(st.st_mode)) {
+                                r = extract_image_basename(
+                                                fname,
+                                                /* class_suffix= */ NULL,
+                                                /* format_suffix= */ NULL,
+                                                &pretty,
+                                                /* ret_v_suffix= */ NULL);
+                                if (r < 0) {
+                                        log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", fname);
+                                        continue;
+                                }
+                        } else {
+                                log_debug("Skipping directory entry '%s', which is neither regular file, directory nor block device.", fname);
                                 continue;
                         }
 
                         if (hashmap_contains(h, pretty))
                                 continue;
 
-                        r = image_make(class, pretty, dirfd(d), resolved, de->d_name, &st, &image);
+                        r = image_make(class, pretty, dirfd(d), resolved, fname, &st, &image);
                         if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
                                 continue;
                         if (r < 0)
index fb9bdbc1601ab42daf601207f7a328ca9325440d..cfb2828f4e3b4c7caba8c3218d492cac8b23841e 100644 (file)
 #include "strv.h"
 #include "tmpfile-util-label.h"
 
+typedef struct EditFile {
+        EditFileContext *context;
+        char *path;
+        char *original_path;
+        char **comment_paths;
+        char *temp;
+        unsigned line;
+} EditFile;
+
 void edit_file_context_done(EditFileContext *context) {
         int r;
 
index 29ff81f4e7440affd61732d43fac9c01fe9d2def..9d9c890f2a97ef590d26788c0b1b6814c59e5371 100644 (file)
@@ -7,18 +7,8 @@
 #define DROPIN_MARKER_END "### Edits below this comment will be discarded"
 
 typedef struct EditFile EditFile;
-typedef struct EditFileContext EditFileContext;
-
-struct EditFile {
-        EditFileContext *context;
-        char *path;
-        char *original_path;
-        char **comment_paths;
-        char *temp;
-        unsigned line;
-};
-
-struct EditFileContext {
+
+typedef struct EditFileContext {
         EditFile *files;
         size_t n_files;
         const char *marker_start;
@@ -26,7 +16,7 @@ struct EditFileContext {
         bool remove_parent;
         bool overwrite_with_origin; /* Always overwrite target with original file. */
         bool stdin;                 /* Read contents from stdin instead of launching an editor. */
-};
+} EditFileContext;
 
 void edit_file_context_done(EditFileContext *context);
 
index 0822364535e66b7b3c7a5cc50f07bdb2e1a03200..758aaa13c169ac09ae037ae7114d73aeca5c0ef1 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "alloc-util.h"
+#include "efi-api.h"
 #include "efi-loader.h"
 #include "env-util.h"
 #include "parse-util.h"
@@ -247,8 +248,8 @@ int efi_measured_uki(int log_level) {
         if (cached >= 0)
                 return cached;
 
-        /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11. Or in
-         * other words, if we are running on a TPM enabled UKI.
+        /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11 on a TPM2
+         * chip. Or in other words, if we are running on a TPM enabled UKI. (TPM 1.2 situations are ignored.)
          *
          * Returns == 0 and > 0 depending on the result of the test. Returns -EREMOTE if we detected a stub
          * being used, but it measured things into a different PCR than we are configured for in
@@ -261,7 +262,7 @@ int efi_measured_uki(int log_level) {
         if (r != -ENXIO)
                 log_debug_errno(r, "Failed to parse $SYSTEMD_FORCE_MEASURE, ignoring: %m");
 
-        if (!is_efi_boot())
+        if (!efi_has_tpm2())
                 return (cached = 0);
 
         r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
index b408a38c3710e96c7013de4d8639fbc49461cfca..cb5a69ef22467f66098c13e20772adc1b4efd1c8 100644 (file)
@@ -3,6 +3,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <stddef.h>
+#include <unistd.h>
 
 #include "sd-daemon.h"
 
@@ -40,8 +41,8 @@ int fdset_new_array(FDSet **ret, const int fds[], size_t n_fds) {
         if (!s)
                 return -ENOMEM;
 
-        for (size_t i = 0; i < n_fds; i++) {
-                r = fdset_put(s, fds[i]);
+        FOREACH_ARRAY(fd, fds, n_fds) {
+                r = fdset_put(s, *fd);
                 if (r < 0)
                         return r;
         }
@@ -71,7 +72,7 @@ void fdset_close(FDSet *s) {
                         log_debug("Closing set fd %i (%s)", fd, strna(path));
                 }
 
-                (void) close_nointr(fd);
+                (void) close(fd);
         }
 }
 
index 3c3de5097987bcb9fba60896c94c8d883cc08907..47ca62c7313446193df09fad2bf4f846b8749cb2 100644 (file)
@@ -726,6 +726,14 @@ const ImagePolicy image_policy_confext = {
         .default_flags = PARTITION_POLICY_IGNORE,
 };
 
+const ImagePolicy image_policy_confext_strict = {
+        .n_policies = 1,
+        .policies = {
+                { PARTITION_ROOT,     PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
+        },
+        .default_flags = PARTITION_POLICY_IGNORE,
+};
+
 const ImagePolicy image_policy_container = {
         /* For systemd-nspawn containers we use all partitions, with the exception of swap */
         .n_policies = 8,
index f59c16e173ba41e62641542fab0d7e4cf52c04bd..c2a0a5ac40f7957d947f07445293c7c46a75a314 100644 (file)
@@ -58,9 +58,10 @@ struct ImagePolicy {
 extern const ImagePolicy image_policy_allow;
 extern const ImagePolicy image_policy_deny;
 extern const ImagePolicy image_policy_ignore;
-extern const ImagePolicy image_policy_sysext;        /* No verity required */
-extern const ImagePolicy image_policy_sysext_strict; /* Signed verity required */
-extern const ImagePolicy image_policy_confext;       /* No verity required */
+extern const ImagePolicy image_policy_sysext;         /* No verity required */
+extern const ImagePolicy image_policy_sysext_strict;  /* Signed verity required */
+extern const ImagePolicy image_policy_confext;        /* No verity required */
+extern const ImagePolicy image_policy_confext_strict; /* Signed verity required */
 extern const ImagePolicy image_policy_container;
 extern const ImagePolicy image_policy_service;
 extern const ImagePolicy image_policy_host;
index b2cee6fa2c869ac6d9f949e79fe09b5887e7fdd0..1b95430f8843f703696c3a488b520515fc89765d 100644 (file)
@@ -189,6 +189,7 @@ shared_sources = files(
         'verbs.c',
         'vlan-util.c',
         'volatile-util.c',
+        'vpick.c',
         'wall.c',
         'watchdog.c',
         'web-util.c',
index 80a0d5f2dc60838c7765113723177e1e870051df..8c683254a2a7098cfacc99aab6952425cea8f2c2 100644 (file)
@@ -228,11 +228,14 @@ static int tpm2_get_capability(
                         count,
                         &more,
                         &capabilities);
+        if (rc == TPM2_RC_VALUE)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENXIO),
+                                       "Requested TPM2 capability 0x%04" PRIx32 " property 0x%04" PRIx32 " apparently doesn't exist: %s",
+                                       capability, property, sym_Tss2_RC_Decode(rc));
         if (rc != TSS2_RC_SUCCESS)
                 return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                        "Failed to get TPM2 capability 0x%04" PRIx32 " property 0x%04" PRIx32 ": %s",
                                        capability, property, sym_Tss2_RC_Decode(rc));
-
         if (capabilities->capability != capability)
                 return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
                                        "TPM provided wrong capability: 0x%04" PRIx32 " instead of 0x%04" PRIx32 ".",
@@ -333,6 +336,8 @@ static int tpm2_cache_capabilities(Tpm2Context *c) {
                                 current_ecc_curve,
                                 TPM2_MAX_ECC_CURVES,
                                 &capability);
+                if (r == -ENXIO) /* If the TPM doesn't support ECC, it might return TPM2_RC_VALUE rather than capability.eccCurves == 0 */
+                        break;
                 if (r < 0)
                         return r;
 
index ec1e27771e9b142b1b113d09e49b0849776689ee..2e04fac404726ef6e048d2da144f74aafea954a4 100644 (file)
@@ -23,7 +23,8 @@ static inline bool utxent_start(void) {
         return true;
 }
 static inline void utxent_cleanup(bool *initialized) {
-        if (initialized)
+        assert(initialized);
+        if (*initialized)
                 endutxent();
 }
 
diff --git a/src/shared/vpick.c b/src/shared/vpick.c
new file mode 100644 (file)
index 0000000..4a4d1eb
--- /dev/null
@@ -0,0 +1,694 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/stat.h>
+
+#include "architecture.h"
+#include "chase.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "recurse-dir.h"
+#include "vpick.h"
+
+void pick_result_done(PickResult *p) {
+        assert(p);
+
+        free(p->path);
+        safe_close(p->fd);
+        free(p->version);
+
+        *p = PICK_RESULT_NULL;
+}
+
+static int format_fname(
+                const PickFilter *filter,
+                PickFlags flags,
+                char **ret) {
+
+        _cleanup_free_ char *fn = NULL;
+        int r;
+
+        assert(filter);
+        assert(ret);
+
+        if (FLAGS_SET(flags, PICK_TRIES) || !filter->version) /* Underspecified? */
+                return -ENOEXEC;
+
+        /* The format for names we match goes like this:
+         *
+         *        <basename><suffix>
+         *  or:
+         *        <basename>_<version><suffix>
+         *  or:
+         *        <basename>_<version>_<architecture><suffix>
+         *  or:
+         *        <basename>_<architecture><suffix>
+         *
+         * (Note that basename can be empty, in which case the leading "_" is suppressed)
+         *
+         * Examples: foo.raw, foo_1.3-7.raw, foo_1.3-7_x86-64.raw, foo_x86-64.raw
+         *
+         * Why use "_" as separator here? Primarily because it is not used by Semver 2.0. In RPM it is used
+         * for "unsortable" versions, i.e. doesn't show up in "sortable" versions, which we matter for this
+         * usecase here. In Debian the underscore is not allowed (and it uses it itself for separating
+         * fields).
+         *
+         * This is very close to Debian's way to name packages, but allows arbitrary suffixes, and makes the
+         * architecture field redundant.
+         *
+         * Compare with RPM's "NEVRA" concept. Here we have "BVAS" (basename, version, architecture, suffix).
+         */
+
+        if (filter->basename) {
+                fn = strdup(filter->basename);
+                if (!fn)
+                        return -ENOMEM;
+        }
+
+        if (filter->version) {
+                if (isempty(fn)) {
+                        r = free_and_strdup(&fn, filter->version);
+                        if (r < 0)
+                                return r;
+                } else if (!strextend(&fn, "_", filter->version))
+                        return -ENOMEM;
+        }
+
+        if (FLAGS_SET(flags, PICK_ARCHITECTURE) && filter->architecture >= 0) {
+                const char *as = ASSERT_PTR(architecture_to_string(filter->architecture));
+                if (isempty(fn)) {
+                        r = free_and_strdup(&fn, as);
+                        if (r < 0)
+                                return r;
+                } else if (!strextend(&fn, "_", as))
+                        return -ENOMEM;
+        }
+
+        if (filter->suffix && !strextend(&fn, filter->suffix))
+                return -ENOMEM;
+
+        if (!filename_is_valid(fn))
+                return -EINVAL;
+
+        *ret = TAKE_PTR(fn);
+        return 0;
+}
+
+static int errno_from_mode(uint32_t type_mask, mode_t found) {
+        /* Returns the most appropriate error code if we are lookging for an inode of type of those in the
+         * 'type_mask' but found 'found' instead.
+         *
+         * type_mask is a mask of 1U << DT_REG, 1U << DT_DIR, … flags, while found is a S_IFREG, S_IFDIR, …
+         * mode value. */
+
+        if (type_mask == 0) /* type doesn't matter */
+                return 0;
+
+        if (FLAGS_SET(type_mask, UINT32_C(1) << IFTODT(found)))
+                return 0;
+
+        if (type_mask == (UINT32_C(1) << DT_BLK))
+                return -ENOTBLK;
+        if (type_mask == (UINT32_C(1) << DT_DIR))
+                return -ENOTDIR;
+        if (type_mask == (UINT32_C(1) << DT_SOCK))
+                return -ENOTSOCK;
+
+        if (S_ISLNK(found))
+                return -ELOOP;
+        if (S_ISDIR(found))
+                return -EISDIR;
+
+        return -EBADF;
+}
+
+static int pin_choice(
+                const char *toplevel_path,
+                int toplevel_fd,
+                const char *inode_path,
+                int _inode_fd, /* we always take ownership of the fd, even on failure */
+                unsigned tries_left,
+                unsigned tries_done,
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret) {
+
+        _cleanup_close_ int inode_fd = TAKE_FD(_inode_fd);
+        _cleanup_free_ char *resolved_path = NULL;
+        int r;
+
+        assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
+        assert(inode_path);
+        assert(filter);
+
+        toplevel_path = strempty(toplevel_path);
+
+        if (inode_fd < 0 || FLAGS_SET(flags, PICK_RESOLVE)) {
+                r = chaseat(toplevel_fd,
+                            inode_path,
+                            CHASE_AT_RESOLVE_IN_ROOT,
+                            FLAGS_SET(flags, PICK_RESOLVE) ? &resolved_path : 0,
+                            inode_fd < 0 ? &inode_fd : NULL);
+                if (r < 0)
+                        return r;
+
+                if (resolved_path)
+                        inode_path = resolved_path;
+        }
+
+        struct stat st;
+        if (fstat(inode_fd, &st) < 0)
+                return log_debug_errno(errno, "Failed to stat discovered inode '%s/%s': %m", toplevel_path, inode_path);
+
+        if (filter->type_mask != 0 &&
+            !FLAGS_SET(filter->type_mask, UINT32_C(1) << IFTODT(st.st_mode)))
+                return log_debug_errno(
+                                SYNTHETIC_ERRNO(errno_from_mode(filter->type_mask, st.st_mode)),
+                                "Inode '%s/%s' has wrong type, found '%s'.",
+                                toplevel_path, inode_path,
+                                inode_type_to_string(st.st_mode));
+
+        _cleanup_(pick_result_done) PickResult result = {
+                .fd = TAKE_FD(inode_fd),
+                .st = st,
+                .architecture = filter->architecture,
+                .tries_left = tries_left,
+                .tries_done = tries_done,
+        };
+
+        result.path = strdup(inode_path);
+        if (!result.path)
+                return log_oom_debug();
+
+        if (filter->version) {
+                result.version = strdup(filter->version);
+                if (!result.version)
+                        return log_oom_debug();
+        }
+
+        *ret = TAKE_PICK_RESULT(result);
+        return 1;
+}
+
+static int parse_tries(const char *s, unsigned *ret_tries_left, unsigned *ret_tries_done) {
+        unsigned left, done;
+        size_t n;
+
+        assert(s);
+        assert(ret_tries_left);
+        assert(ret_tries_done);
+
+        if (s[0] != '+')
+                goto nomatch;
+
+        s++;
+
+        n = strspn(s, DIGITS);
+        if (n == 0)
+                goto nomatch;
+
+        if (s[n] == 0) {
+                if (safe_atou(s, &left) < 0)
+                        goto nomatch;
+
+                done = 0;
+        } else if (s[n] == '-') {
+                _cleanup_free_ char *c = NULL;
+
+                c = strndup(s, n);
+                if (!c)
+                        return -ENOMEM;
+
+                if (safe_atou(c, &left) < 0)
+                        goto nomatch;
+
+                s += n + 1;
+
+                if (!in_charset(s, DIGITS))
+                        goto nomatch;
+
+                if (safe_atou(s, &done) < 0)
+                        goto nomatch;
+        } else
+                goto nomatch;
+
+        *ret_tries_left = left;
+        *ret_tries_done = done;
+        return 1;
+
+nomatch:
+        *ret_tries_left = *ret_tries_done = UINT_MAX;
+        return 0;
+}
+
+static int make_choice(
+                const char *toplevel_path,
+                int toplevel_fd,
+                const char *inode_path,
+                int _inode_fd, /* we always take ownership of the fd, even on failure */
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret) {
+
+        static const Architecture local_architectures[] = {
+                /* In order of preference */
+                native_architecture(),
+#ifdef ARCHITECTURE_SECONDARY
+                ARCHITECTURE_SECONDARY,
+#endif
+                _ARCHITECTURE_INVALID, /* accept any arch, as last resort */
+        };
+
+        _cleanup_free_ DirectoryEntries *de = NULL;
+        _cleanup_free_ char *best_version = NULL, *best_filename = NULL, *p = NULL, *j = NULL;
+        _cleanup_close_ int dir_fd = -EBADF, object_fd = -EBADF, inode_fd = TAKE_FD(_inode_fd);
+        const Architecture *architectures;
+        unsigned best_tries_left = UINT_MAX, best_tries_done = UINT_MAX;
+        size_t n_architectures, best_architecture_index = SIZE_MAX;
+        int r;
+
+        assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
+        assert(inode_path);
+        assert(filter);
+
+        toplevel_path = strempty(toplevel_path);
+
+        if (inode_fd < 0) {
+                r = chaseat(toplevel_fd, inode_path, CHASE_AT_RESOLVE_IN_ROOT, NULL, &inode_fd);
+                if (r < 0)
+                        return r;
+        }
+
+        /* Maybe the filter is fully specified? Then we can generate the file name directly */
+        r = format_fname(filter, flags, &j);
+        if (r >= 0) {
+                _cleanup_free_ char *object_path = NULL;
+
+                /* Yay! This worked! */
+                p = path_join(inode_path, j);
+                if (!p)
+                        return log_oom_debug();
+
+                r = chaseat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, &object_path, &object_fd);
+                if (r < 0) {
+                        if (r != -ENOENT)
+                                return log_debug_errno(r, "Failed to open '%s/%s': %m", toplevel_path, p);
+
+                        *ret = PICK_RESULT_NULL;
+                        return 0;
+                }
+
+                return pin_choice(
+                                toplevel_path,
+                                toplevel_fd,
+                                FLAGS_SET(flags, PICK_RESOLVE) ? object_path : p,
+                                TAKE_FD(object_fd), /* unconditionally pass ownership of the fd */
+                                /* tries_left= */ UINT_MAX,
+                                /* tries_done= */ UINT_MAX,
+                                filter,
+                                flags & ~PICK_RESOLVE,
+                                ret);
+
+        } else if (r != -ENOEXEC)
+                return log_debug_errno(r, "Failed to format file name: %m");
+
+        /* Underspecified, so we do our enumeration dance */
+
+        /* Convert O_PATH to a regular directory fd */
+        dir_fd = fd_reopen(inode_fd, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+        if (dir_fd < 0)
+                return log_debug_errno(dir_fd, "Failed to reopen '%s/%s' as directory: %m", toplevel_path, inode_path);
+
+        r = readdir_all(dir_fd, 0, &de);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to read directory '%s/%s': %m", toplevel_path, inode_path);
+
+        if (filter->architecture < 0) {
+                architectures = local_architectures;
+                n_architectures = ELEMENTSOF(local_architectures);
+        } else {
+                architectures = &filter->architecture;
+                n_architectures = 1;
+        }
+
+        FOREACH_ARRAY(entry, de->entries, de->n_entries) {
+                unsigned found_tries_done = UINT_MAX, found_tries_left = UINT_MAX;
+                _cleanup_free_ char *chopped = NULL;
+                size_t found_architecture_index = SIZE_MAX;
+                const char *e;
+
+                if (!isempty(filter->basename)) {
+                        e = startswith((*entry)->d_name, filter->basename);
+                        if (!e)
+                                continue;
+
+                        if (e[0] != '_')
+                                continue;
+
+                        e++;
+                } else
+                        e = (*entry)->d_name;
+
+                if (!isempty(filter->suffix)) {
+                        const char *sfx;
+
+                        sfx = endswith(e, filter->suffix);
+                        if (!sfx)
+                                continue;
+
+                        chopped = strndup(e, sfx - e);
+                        if (!chopped)
+                                return log_oom_debug();
+
+                        e = chopped;
+                }
+
+                if (FLAGS_SET(flags, PICK_TRIES)) {
+                        char *plus = strrchr(e, '+');
+                        if (plus) {
+                                r = parse_tries(plus, &found_tries_left, &found_tries_done);
+                                if (r < 0)
+                                        return r;
+                                if (r > 0) /* Found and parsed, now chop off */
+                                        *plus = 0;
+                        }
+                }
+
+                if (FLAGS_SET(flags, PICK_ARCHITECTURE)) {
+                        char *underscore = strrchr(e, '_');
+                        Architecture a;
+
+                        a = underscore ? architecture_from_string(underscore + 1) : _ARCHITECTURE_INVALID;
+
+                        for (size_t i = 0; i < n_architectures; i++)
+                                if (architectures[i] == a) {
+                                        found_architecture_index = i;
+                                        break;
+                                }
+
+                        if (found_architecture_index == SIZE_MAX) { /* No matching arch found */
+                                log_debug("Found entry with architecture '%s' which is not what we are looking for, ignoring entry.", a < 0 ? "any" : architecture_to_string(a));
+                                continue;
+                        }
+
+                        /* Chop off architecture from string */
+                        if (underscore)
+                                *underscore = 0;
+                }
+
+                if (!version_is_valid(e)) {
+                        log_debug("Version string '%s' of entry '%s' is invalid, ignoring entry.", e, (*entry)->d_name);
+                        continue;
+                }
+
+                if (filter->version && !streq(filter->version, e)) {
+                        log_debug("Found entry with version string '%s', but was looking for '%s', ignoring entry.", e, filter->version);
+                        continue;
+                }
+
+                if (best_filename) { /* Already found one matching entry? Then figure out the better one */
+                        int d = 0;
+
+                        /* First, prefer entries with tries left over those without */
+                        if (FLAGS_SET(flags, PICK_TRIES))
+                                d = CMP(found_tries_left != 0, best_tries_left != 0);
+
+                        /* Second, prefer newer versions */
+                        if (d == 0)
+                                d = strverscmp_improved(e, best_version);
+
+                        /* Third, prefer native architectures over secondary architectures */
+                        if (d == 0 &&
+                            FLAGS_SET(flags, PICK_ARCHITECTURE) &&
+                            found_architecture_index != SIZE_MAX && best_architecture_index != SIZE_MAX)
+                                d = -CMP(found_architecture_index, best_architecture_index);
+
+                        /* Fourth, prefer entries with more tries left */
+                        if (FLAGS_SET(flags, PICK_TRIES)) {
+                                if (d == 0)
+                                        d = CMP(found_tries_left, best_tries_left);
+
+                                /* Fifth, prefer entries with fewer attempts done so far */
+                                if (d == 0)
+                                        d = -CMP(found_tries_done, best_tries_done);
+                        }
+
+                        /* Last, just compare the filenames as strings */
+                        if (d == 0)
+                                d = strcmp((*entry)->d_name, best_filename);
+
+                        if (d < 0) {
+                                log_debug("Found entry '%s' but previously found entry '%s' matches better, hence skipping entry.", (*entry)->d_name, best_filename);
+                                continue;
+                        }
+                }
+
+                r = free_and_strdup_warn(&best_version, e);
+                if (r < 0)
+                        return r;
+
+                r = free_and_strdup_warn(&best_filename, (*entry)->d_name);
+                if (r < 0)
+                        return r;
+
+                best_architecture_index = found_architecture_index;
+                best_tries_left = found_tries_left;
+                best_tries_done = found_tries_done;
+        }
+
+        if (!best_filename) { /* Everything was good, but we didn't find any suitable entry */
+                *ret = PICK_RESULT_NULL;
+                return 0;
+        }
+
+        p = path_join(inode_path, best_filename);
+        if (!p)
+                return log_oom_debug();
+
+        object_fd = openat(dir_fd, best_filename, O_CLOEXEC|O_PATH);
+        if (object_fd < 0)
+                return log_debug_errno(errno, "Failed to open '%s/%s': %m", toplevel_path, p);
+
+        return pin_choice(
+                        toplevel_path,
+                        toplevel_fd,
+                        p,
+                        TAKE_FD(object_fd),
+                        best_tries_left,
+                        best_tries_done,
+                        &(const PickFilter) {
+                                .type_mask = filter->type_mask,
+                                .basename = filter->basename,
+                                .version = empty_to_null(best_version),
+                                .architecture = best_architecture_index != SIZE_MAX ? architectures[best_architecture_index] : _ARCHITECTURE_INVALID,
+                                .suffix = filter->suffix,
+                        },
+                        flags,
+                        ret);
+}
+
+int path_pick(const char *toplevel_path,
+              int toplevel_fd,
+              const char *path,
+              const PickFilter *filter,
+              PickFlags flags,
+              PickResult *ret) {
+
+        _cleanup_free_ char *filter_bname = NULL, *dir = NULL, *parent = NULL, *fname = NULL;
+        const char *filter_suffix, *enumeration_path;
+        uint32_t filter_type_mask;
+        int r;
+
+        assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
+        assert(path);
+
+        toplevel_path = strempty(toplevel_path);
+
+        /* Given a path, resolve .v/ subdir logic (if used!), and returns the choice made. This supports
+         * three ways to be called:
+         *
+         * • with a path referring a directory of any name, and filter→basename *explicitly* specified, in
+         *   which case we'll use a pattern "<filter→basename>_*<filter→suffix>" on the directory's files.
+         *
+         * • with no filter→basename explicitly specified and a path referring to a directory named in format
+         *   "<somestring><filter→suffix>.v" . In this case the filter basename to search for inside the dir
+         *   is derived from the directory name. Example: "/foo/bar/baz.suffix.v" → we'll search for
+         *   "/foo/bar/baz.suffix.v/baz_*.suffix".
+         *
+         * • with a path whose penultimate component ends in ".v/". In this case the final component of the
+         *   path refers to the pattern. Example: "/foo/bar/baz.v/waldo__.suffix" → we'll search for
+         *   "/foo/bar/baz.v/waldo_*.suffix".
+         */
+
+        /* Explicit basename specified, then shortcut things and do .v mode regardless of the path name. */
+        if (filter->basename)
+                return make_choice(
+                                toplevel_path,
+                                toplevel_fd,
+                                path,
+                                /* inode_fd= */ -EBADF,
+                                filter,
+                                flags,
+                                ret);
+
+        r = path_extract_filename(path, &fname);
+        if (r < 0) {
+                if (r != -EADDRNOTAVAIL) /* root dir or "." */
+                        return r;
+
+                /* If there's no path element we can derive a pattern off, the don't */
+                goto bypass;
+        }
+
+        /* Remember if the path ends in a dash suffix */
+        bool slash_suffix = r == O_DIRECTORY;
+
+        const char *e = endswith(fname, ".v");
+        if (e) {
+                /* So a path in the form /foo/bar/baz.v is specified. In this case our search basename is
+                 * "baz", possibly with a suffix chopped off if there's one specified. */
+                filter_bname = strndup(fname, e - fname);
+                if (!filter_bname)
+                        return -ENOMEM;
+
+                if (filter->suffix) {
+                        /* Chop off suffix, if specified */
+                        char *f = endswith(filter_bname, filter->suffix);
+                        if (f)
+                                *f = 0;
+                }
+
+                filter_suffix = filter->suffix;
+                filter_type_mask = filter->type_mask;
+
+                enumeration_path = path;
+        } else {
+                /* The path does not end in '.v', hence see if the last element is a pattern. */
+
+                char *wildcard = strrstr(fname, "___");
+                if (!wildcard)
+                        goto bypass; /* Not a pattern, then bypass */
+
+                /* We found the '___' wildcard, hence evertyhing after it is our filter suffix, and
+                 * evertyhing before is our filter basename */
+                *wildcard = 0;
+                filter_suffix = empty_to_null(wildcard + 3);
+
+                filter_bname = TAKE_PTR(fname);
+
+                r = path_extract_directory(path, &dir);
+                if (r < 0) {
+                        if (!IN_SET(r, -EDESTADDRREQ, -EADDRNOTAVAIL)) /* only filename specified (no dir), or root or "." */
+                                return r;
+
+                        goto bypass; /* No dir extractable, can't check if parent ends in ".v" */
+                }
+
+                r = path_extract_filename(dir, &parent);
+                if (r < 0) {
+                        if (r != -EADDRNOTAVAIL) /* root dir or "." */
+                                return r;
+
+                        goto bypass; /* Cannot extract fname from parent dir, can't check if it ends in ".v" */
+                }
+
+                e = endswith(parent, ".v");
+                if (!e)
+                        goto bypass; /* Doesn't end in ".v", shortcut */
+
+                filter_type_mask = filter->type_mask;
+                if (slash_suffix) {
+                        /* If the pattern is suffixed by a / then we are looking for directories apparently. */
+                        if (filter_type_mask != 0 && !FLAGS_SET(filter_type_mask, UINT32_C(1) << DT_DIR))
+                                return log_debug_errno(SYNTHETIC_ERRNO(errno_from_mode(filter_type_mask, S_IFDIR)),
+                                                       "Specified pattern ends in '/', but not looking for directories, refusing.");
+                        filter_type_mask = UINT32_C(1) << DT_DIR;
+                }
+
+                enumeration_path = dir;
+        }
+
+        return make_choice(
+                        toplevel_path,
+                        toplevel_fd,
+                        enumeration_path,
+                        /* inode_fd= */ -EBADF,
+                        &(const PickFilter) {
+                                .type_mask = filter_type_mask,
+                                .basename = filter_bname,
+                                .version = filter->version,
+                                .architecture = filter->architecture,
+                                .suffix = filter_suffix,
+                        },
+                        flags,
+                        ret);
+
+bypass:
+        /* Don't make any choice, but just use the passed path literally */
+        return pin_choice(
+                        toplevel_path,
+                        toplevel_fd,
+                        path,
+                        /* inode_fd= */ -EBADF,
+                        /* tries_left= */ UINT_MAX,
+                        /* tries_done= */ UINT_MAX,
+                        filter,
+                        flags,
+                        ret);
+}
+
+int path_pick_update_warn(
+                char **path,
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret_result) {
+
+        _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+        int r;
+
+        assert(path);
+        assert(*path);
+
+        /* This updates the first argument if needed! */
+
+        r = path_pick(/* toplevel_path= */ NULL,
+                      /* toplevel_fd= */ AT_FDCWD,
+                      *path,
+                      filter,
+                      flags,
+                      &result);
+        if (r == -ENOENT) {
+                log_debug("Path '%s' doesn't exist, leaving as is.", *path);
+                *ret_result = PICK_RESULT_NULL;
+                return 0;
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to pick version on path '%s': %m", *path);
+        if (r == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No matching entries in versioned directory '%s' found.", *path);
+
+        log_debug("Resolved versioned directory pattern '%s' to file '%s' as version '%s'.", result.path, *path, strna(result.version));
+
+        if (ret_result) {
+                r = free_and_strdup_warn(path, result.path);
+                if (r < 0)
+                        return r;
+
+                *ret_result = TAKE_PICK_RESULT(result);
+        } else
+                free_and_replace(*path, result.path);
+
+        return 1;
+}
+
+const PickFilter pick_filter_image_raw = {
+        .type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK),
+        .architecture = _ARCHITECTURE_INVALID,
+        .suffix = ".raw",
+};
+
+const PickFilter pick_filter_image_dir = {
+        .type_mask = UINT32_C(1) << DT_DIR,
+        .architecture = _ARCHITECTURE_INVALID,
+};
diff --git a/src/shared/vpick.h b/src/shared/vpick.h
new file mode 100644 (file)
index 0000000..0e0d41e
--- /dev/null
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/types.h>
+
+#include "architecture.h"
+
+typedef enum PickFlags {
+        PICK_ARCHITECTURE = 1 << 0,   /* Look for an architecture suffix */
+        PICK_TRIES        = 1 << 1,   /* Look for tries left/tries done counters */
+        PICK_RESOLVE      = 1 << 2,   /* Return the fully resolved (chased) path, rather than the path to the entry */
+} PickFlags;
+
+typedef struct PickFilter {
+        uint32_t type_mask;           /* A mask of 1U << DT_REG, 1U << DT_DIR, … */
+        const char *basename;         /* Can be overriden by search pattern */
+        const char *version;
+        Architecture architecture;
+        const char *suffix;           /* Can be overriden by search pattern */
+} PickFilter;
+
+typedef struct PickResult {
+        char *path;
+        int fd; /* O_PATH */
+        struct stat st;
+        char *version;
+        Architecture architecture;
+        unsigned tries_left;
+        unsigned tries_done;
+} PickResult;
+
+#define PICK_RESULT_NULL                                \
+        (const PickResult) {                            \
+                .fd = -EBADF,                           \
+                .st.st_mode = MODE_INVALID,             \
+                .architecture = _ARCHITECTURE_INVALID,  \
+                .tries_left = UINT_MAX,                 \
+                .tries_done = UINT_MAX,                 \
+        }
+
+#define TAKE_PICK_RESULT(ptr) TAKE_GENERIC(ptr, PickResult, PICK_RESULT_NULL)
+
+void pick_result_done(PickResult *p);
+
+int path_pick(const char *toplevel_path,
+              int toplevel_fd,
+              const char *path,
+              const PickFilter *filter,
+              PickFlags flags,
+              PickResult *ret);
+
+int path_pick_update_warn(
+                char **path,
+                const PickFilter *filter,
+                PickFlags flags,
+                PickResult *ret);
+
+extern const PickFilter pick_filter_image_raw;
+extern const PickFilter pick_filter_image_dir;
index b28924a8b456eaf73dfe24a8a0d4d419c275a3c9..62b78a350fa049deb54a0ba4e0477ee8b651c415 100644 (file)
@@ -381,7 +381,7 @@ static int nvme_subsystem_add(const char *node, int consumed_fd, sd_device *devi
                 return log_error_errno(errno, "Failed to fstat '%s': %m", node);
         if (S_ISBLK(st.st_mode)) {
                 if (!device) {
-                        r = sd_device_new_from_devnum(&allocated_device, 'b', st.st_dev);
+                        r = sd_device_new_from_devnum(&allocated_device, 'b', st.st_rdev);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to get device information for device '%s': %m", node);
 
@@ -788,9 +788,10 @@ typedef struct Context {
 static void device_hash_func(const struct stat *q, struct siphash *state) {
         assert(q);
 
+        mode_t m = q->st_mode & S_IFMT;
+        siphash24_compress_typesafe(m, state);
+
         if (S_ISBLK(q->st_mode) || S_ISCHR(q->st_mode)) {
-                mode_t m = q->st_mode & S_IFMT;
-                siphash24_compress_typesafe(m, state);
                 siphash24_compress_typesafe(q->st_rdev, state);
                 return;
         }
@@ -950,9 +951,14 @@ static int device_added(Context *c, sd_device *device) {
                 .st_mode = S_IFBLK,
         };
 
-        r = sd_device_get_devnum(device, &lookup_key.st_rdev);
+        /* MIPS OABI declares st_rdev as unsigned long instead of dev_t.
+         * Use a temp var to avoid passing an incompatible pointer.
+         * https://sourceware.org/bugzilla/show_bug.cgi?id=21278 */
+        dev_t devnum;
+        r = sd_device_get_devnum(device, &devnum);
         if (r < 0)
                 return log_device_error_errno(device, r, "Failed to get major/minor from device: %m");
+        lookup_key.st_rdev = devnum;
 
         if (hashmap_contains(c->subsystems, &lookup_key)) {
                 log_debug("Device '%s' already seen.", devname);
@@ -1006,9 +1012,14 @@ static int device_removed(Context *c, sd_device *device) {
                 .st_mode = S_IFBLK,
         };
 
-        r = sd_device_get_devnum(device, &lookup_key.st_rdev);
+        /* MIPS OABI declares st_rdev as unsigned long instead of dev_t.
+         * Use a temp var to avoid passing an incompatible pointer.
+         * https://sourceware.org/bugzilla/show_bug.cgi?id=21278 */
+        dev_t devnum;
+        r = sd_device_get_devnum(device, &devnum);
         if (r < 0)
                 return log_device_error_errno(device, r, "Failed to get major/minor from device: %m");
+        lookup_key.st_rdev = devnum;
 
         NvmeSubsystem *s = hashmap_remove(c->subsystems, &lookup_key);
         if (!s)
index fe11fe0f07c980362c92cc7c5153554ebaba81e9..fc793707814102e8d7583ec9072ec239be9641d2 100644 (file)
@@ -659,8 +659,16 @@ static const ImagePolicy *pick_image_policy(const Image *img) {
          * picked up from an untrusted ESP. Thus, require a stricter policy by default for them. (For the
          * other directories we assume the appropriate level of trust was already established already.  */
 
-        if (in_initrd() && path_startswith(img->path, "/.extra/sysext/"))
-                return &image_policy_sysext_strict;
+        if (in_initrd()) {
+                if (path_startswith(img->path, "/.extra/sysext/"))
+                        return &image_policy_sysext_strict;
+                if (path_startswith(img->path, "/.extra/confext/"))
+                        return &image_policy_confext_strict;
+
+                /* Better safe than sorry, refuse everything else passed in via the untrusted /.extra/ dir */
+                if (path_startswith(img->path, "/.extra/"))
+                        return &image_policy_deny;
+        }
 
         return image_class_info[img->class].default_image_policy;
 }
index a9cdcd24a0a175ac0618f4eae5ccc98f88fd06f4..76d87cc9d4c5064f445895148e80d568bab8e019 100644 (file)
@@ -20,10 +20,13 @@ _systemd_headers = [
 systemd_headers = files(_systemd_headers)
 
 _not_installed_headers = [
+        'sd-dhcp-client-id.h',
         'sd-dhcp-client.h',
+        'sd-dhcp-duid.h',
         'sd-dhcp-lease.h',
         'sd-dhcp-option.h',
         'sd-dhcp-protocol.h',
+        'sd-dhcp-server-lease.h',
         'sd-dhcp-server.h',
         'sd-dhcp6-client.h',
         'sd-dhcp6-lease.h',
diff --git a/src/systemd/sd-dhcp-client-id.h b/src/systemd/sd-dhcp-client-id.h
new file mode 100644 (file)
index 0000000..c128efc
--- /dev/null
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcpclientidhfoo
+#define foosddhcpclientidhfoo
+
+/***
+  Copyright © 2013 Intel Corporation. All rights reserved.
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "sd-dhcp-duid.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_dhcp_client_id sd_dhcp_client_id;
+
+int sd_dhcp_client_id_new(sd_dhcp_client_id **ret);
+sd_dhcp_client_id* sd_dhcp_client_id_free(sd_dhcp_client_id *client_id);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_client_id, sd_dhcp_client_id_free);
+
+int sd_dhcp_client_id_clear(sd_dhcp_client_id *client_id);
+
+int sd_dhcp_client_id_is_set(const sd_dhcp_client_id *client_id);
+
+int sd_dhcp_client_id_get(const sd_dhcp_client_id *client_id, uint8_t *ret_type, const void **ret_data, size_t *ret_size);
+int sd_dhcp_client_id_get_raw(const sd_dhcp_client_id *client_id, const void **ret_data, size_t *ret_size);
+
+int sd_dhcp_client_id_set(
+                sd_dhcp_client_id *client_id,
+                uint8_t type,
+                const void *data,
+                size_t data_size);
+int sd_dhcp_client_id_set_raw(
+                sd_dhcp_client_id *client_id,
+                const void *data,
+                size_t data_size);
+int sd_dhcp_client_id_set_iaid_duid(
+                sd_dhcp_client_id *client_id,
+                uint32_t iaid,
+                sd_dhcp_duid *duid);
+
+int sd_dhcp_client_id_to_string(const sd_dhcp_client_id *client_id, char **ret);
+int sd_dhcp_client_id_to_string_from_raw(const void *data, size_t data_size, char **ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
index 3a8abc82ed4fcf0b957b046eebaba55c45d23c3f..dcbd79e6b4ddb4515face15b10e639da13c7a2d3 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdbool.h>
 
 #include "sd-device.h"
+#include "sd-dhcp-client-id.h"
 #include "sd-dhcp-lease.h"
 #include "sd-dhcp-option.h"
 #include "sd-event.h"
@@ -75,6 +76,9 @@ int sd_dhcp_client_set_mac(
                 const uint8_t *bcast_addr,
                 size_t addr_len,
                 uint16_t arp_type);
+int sd_dhcp_client_get_client_id(
+                sd_dhcp_client *client,
+                const sd_dhcp_client_id **ret);
 int sd_dhcp_client_set_client_id(
                 sd_dhcp_client *client,
                 uint8_t type,
@@ -107,11 +111,6 @@ __extension__ int sd_dhcp_client_set_iaid_duid_raw(
 __extension__ int sd_dhcp_client_set_rapid_commit(
                 sd_dhcp_client *client,
                 bool rapid_commit);
-int sd_dhcp_client_get_client_id(
-                sd_dhcp_client *client,
-                uint8_t *ret_type,
-                const uint8_t **ret_data,
-                size_t *ret_data_len);
 int sd_dhcp_client_set_mtu(
                 sd_dhcp_client *client,
                 uint32_t mtu);
@@ -165,8 +164,6 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
  * options when using RFC7844 Anonymity Profiles */
 int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize);
 
-int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret);
-
 int sd_dhcp_client_attach_event(
                 sd_dhcp_client *client,
                 sd_event *event,
diff --git a/src/systemd/sd-dhcp-duid.h b/src/systemd/sd-dhcp-duid.h
new file mode 100644 (file)
index 0000000..b1d2772
--- /dev/null
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcpduidhfoo
+#define foosddhcpduidhfoo
+
+/***
+  Copyright © 2013 Intel Corporation. All rights reserved.
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+        SD_DUID_TYPE_LLT        = 1,
+        SD_DUID_TYPE_EN         = 2,
+        SD_DUID_TYPE_LL         = 3,
+        SD_DUID_TYPE_UUID       = 4
+};
+
+typedef struct sd_dhcp_duid sd_dhcp_duid;
+
+int sd_dhcp_duid_clear(sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size);
+int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size);
+
+int sd_dhcp_duid_set(
+                sd_dhcp_duid *duid,
+                uint16_t duid_type,
+                const void *data,
+                size_t data_size);
+int sd_dhcp_duid_set_raw(
+                sd_dhcp_duid *duid,
+                const void *data,
+                size_t data_size);
+int sd_dhcp_duid_set_llt(
+                sd_dhcp_duid *duid,
+                const void *hw_addr,
+                size_t hw_addr_size,
+                uint16_t arp_type,
+                uint64_t usec);
+int sd_dhcp_duid_set_ll(
+                sd_dhcp_duid *duid,
+                const void *hw_addr,
+                size_t hw_addr_size,
+                uint16_t arp_type);
+int sd_dhcp_duid_set_en(sd_dhcp_duid *duid);
+int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
index 1ef53ccf1ea83a10ecae933aae026ddb8c9943c6..95b0dfef22c098cff5c4c2e57cfcb5e3fb6ec2a9 100644 (file)
@@ -24,6 +24,8 @@
 #include <netinet/in.h>
 #include <sys/types.h>
 
+#include "sd-dhcp-client-id.h"
+
 #include "_sd-common.h"
 
 _SD_BEGIN_DECLARATIONS;
@@ -76,7 +78,7 @@ int sd_dhcp_lease_get_captive_portal(sd_dhcp_lease *lease, const char **captive_
 int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
 int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
 int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id **ret);
 int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
 int sd_dhcp_lease_get_6rd(
                 sd_dhcp_lease *lease,
diff --git a/src/systemd/sd-dhcp-server-lease.h b/src/systemd/sd-dhcp-server-lease.h
new file mode 100644 (file)
index 0000000..754f719
--- /dev/null
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcpserverleasehfoo
+#define foosddhcpserverleasehfoo
+
+/***
+  Copyright © 2013 Intel Corporation. All rights reserved.
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_dhcp_server_lease sd_dhcp_server_lease;
+
+sd_dhcp_server_lease *sd_dhcp_server_lease_ref(sd_dhcp_server_lease *lease);
+sd_dhcp_server_lease *sd_dhcp_server_lease_unref(sd_dhcp_server_lease *lease);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server_lease, sd_dhcp_server_lease_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
index 0ceadb8b8a8120cf5b9d0758340788d253d4408f..d551b4dd901826a53ee0f6b636a7e6ef5ea6ffd4 100644 (file)
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 
 #include "sd-device.h"
+#include "sd-dhcp-duid.h"
 #include "sd-dhcp6-lease.h"
 #include "sd-dhcp6-option.h"
 #include "sd-event.h"
@@ -68,15 +69,15 @@ int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client);
 int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client);
 int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client);
 int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len);
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid);
+int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret);
+int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret);
 int sd_dhcp6_client_set_iaid(
                 sd_dhcp6_client *client,
                 uint32_t iaid);
 int sd_dhcp6_client_get_iaid(
                 sd_dhcp6_client *client,
                 uint32_t *iaid);
-int sd_dhcp6_client_duid_as_string(
-                sd_dhcp6_client *client,
-                char **duid);
 int sd_dhcp6_client_set_fqdn(
                 sd_dhcp6_client *client,
                 const char *fqdn);
index 4119c453fb8525b1131275c689f9c16cb81207ac..c35bfc47c96d9981b530db58bd4cc9317ae78a1a 100644 (file)
@@ -162,6 +162,7 @@ int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *type);
 int sd_rtnl_message_new_route(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type, int rtm_family, unsigned char rtm_protocol);
 int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
 int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
+int sd_rtnl_message_route_set_tos(sd_netlink_message *m, unsigned char tos);
 int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope);
 int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags);
 int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table);
index aec125d483ac9d68fb4170f24c0bd341c1b6c0bb..4a50fc9b5b723a5740685c0796af944813bb54ef 100644 (file)
@@ -178,6 +178,7 @@ simple_tests += files(
         'test-user-util.c',
         'test-utf8.c',
         'test-verbs.c',
+        'test-vpick.c',
         'test-web-util.c',
         'test-xattr-util.c',
         'test-xml.c',
@@ -600,4 +601,8 @@ executables += [
                         libudev_basic,
                 ],
         },
+        test_template + {
+                'sources' : files('test-aux-scope.c'),
+                'type' : 'manual',
+        },
 ]
diff --git a/src/test/test-aux-scope.c b/src/test/test-aux-scope.c
new file mode 100644 (file)
index 0000000..175757b
--- /dev/null
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "sd-event.h"
+
+#include "bus-error.h"
+#include "bus-message.h"
+#include "bus-wait-for-jobs.h"
+#include "fd-util.h"
+#include "log.h"
+#include "missing_syscall.h"
+#include "process-util.h"
+#include "tests.h"
+
+static int on_sigusr1(sd_event_source *s, const struct signalfd_siginfo *ssi, void *userdata) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL, *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+        _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+        PidRef *pids = (PidRef *) userdata;
+        const char *job;
+        int r;
+
+        assert(pids);
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire bus: %m");
+
+        r = sd_bus_message_new_method_call(bus, &message,
+                                           "org.freedesktop.systemd1",
+                                           "/org/freedesktop/systemd1",
+                                           "org.freedesktop.systemd1.Manager",
+                                           "StartAuxiliaryScope");
+        if (r < 0)
+                return log_error_errno(r, "Failed to create bus message: %m");
+
+        r = sd_bus_message_append_basic(message, 's', "test-aux-scope.scope");
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach scope name: %m");
+
+        r = sd_bus_message_open_container(message, 'a', "h");
+        if (r < 0)
+                return log_error_errno(r, "Failed to create array of FDs: %m");
+
+        for (size_t i = 0; i < 10; i++) {
+                r = sd_bus_message_append_basic(message, 'h', &pids[i].fd);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to append PIDFD to message: %m");
+        }
+
+        r = sd_bus_message_close_container(message);
+        if (r < 0)
+                return log_error_errno(r, "Failed to close container: %m");
+
+        r = sd_bus_message_append(message, "ta(sv)", UINT64_C(0), 1, "Description", "s", "Test auxiliary scope");
+        if (r < 0)
+                return log_error_errno(r, "Failed to append unit properties: %m");
+
+        r = sd_bus_call(bus, message, 0, &error, &reply);
+        if (r < 0)
+                return log_error_errno(r, "Failed to start auxiliary scope: %s", bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "o", &job);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read reply: %m");
+
+        r = bus_wait_for_jobs_new(bus, &w);
+        if (r < 0)
+                return log_error_errno(r, "Could not watch jobs: %m");
+
+        r = bus_wait_for_jobs_one(w, job, false, NULL);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static void destroy_pidrefs(PidRef *pids, size_t npids) {
+        assert(pids || npids == 0);
+
+        for (size_t i = 0; i < npids; i++)
+                pidref_done(&pids[i]);
+
+        free(pids);
+}
+
+int main(int argc, char *argv[]) {
+        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+        PidRef *pids = NULL;
+        size_t npids = 0;
+        int r, fd;
+
+        CLEANUP_ARRAY(pids, npids, destroy_pidrefs);
+
+        test_setup_logging(LOG_INFO);
+
+        fd = pidfd_open(getpid_cached(), 0);
+        if (fd < 0 && (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)))
+                return log_tests_skipped("pidfds are not available");
+        else if (fd < 0) {
+                log_error_errno(errno, "pidfd_open() failed: %m");
+                return EXIT_FAILURE;
+        }
+        safe_close(fd);
+
+        r = sd_event_new(&event);
+        if (r < 0) {
+                log_error_errno(r, "Failed to allocate event loop: %m");
+                return EXIT_FAILURE;
+        }
+
+        npids = 10;
+        pids = new0(PidRef, npids);
+        assert(pids);
+
+        r = sd_event_add_signal(event, NULL, SIGUSR1|SD_EVENT_SIGNAL_PROCMASK, on_sigusr1, pids);
+        if (r < 0) {
+                log_error_errno(r, "Failed to setup SIGUSR1 signal handling: %m");
+                return EXIT_FAILURE;
+        }
+
+        for (size_t i = 0; i < npids; i++) {
+                PidRef pidref = PIDREF_NULL;
+                pid_t pid;
+
+                r = safe_fork("(worker)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_CLOSE_ALL_FDS, &pid);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to fork(): %m");
+                        return EXIT_FAILURE;
+                }
+
+                if (r == 0) {
+                        /* Worker */
+                        sleep(3600);
+                        _exit(EXIT_SUCCESS);
+                }
+
+                r = pidref_set_pid(&pidref, pid);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to initialize PID ref: %m");
+                        return EXIT_FAILURE;
+                }
+
+                assert_se(pidref.pid == pid);
+                assert_se(pidref.fd != -EBADF);
+
+                pids[i] = TAKE_PIDREF(pidref);
+        }
+
+        r = sd_event_loop(event);
+        if (r < 0)
+                return log_error_errno(r, "Failed to run event loop: %m");
+
+        return 0;
+}
index d9fe5562a1be1af27ae322c86887e50264116822..1572093c9dce2d168eb3e9ffc0d64886ec4a4991 100644 (file)
@@ -79,6 +79,7 @@ TEST_RET(test_image_policy_to_string) {
         test_policy(&image_policy_sysext, "sysext");
         test_policy(&image_policy_sysext_strict, "sysext-strict");
         test_policy(&image_policy_confext, "confext");
+        test_policy(&image_policy_confext_strict, "confext-strict");
         test_policy(&image_policy_container, "container");
         test_policy(&image_policy_host, "host");
         test_policy(&image_policy_service, "service");
index 5aca207fa42c9202f87287a3c9c56d999d24abae..95137ffcf12e198a7b1cb8d321d8a16a43877794 100644 (file)
@@ -180,6 +180,21 @@ TEST(dir_is_empty) {
         assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ false) > 0);
 }
 
+TEST(inode_type_from_string) {
+        static const mode_t types[] = {
+                S_IFREG,
+                S_IFDIR,
+                S_IFLNK,
+                S_IFCHR,
+                S_IFBLK,
+                S_IFIFO,
+                S_IFSOCK,
+        };
+
+        FOREACH_ARRAY(m, types, ELEMENTSOF(types))
+                assert_se(inode_type_from_string(inode_type_to_string(*m)) == *m);
+}
+
 static int intro(void) {
         log_show_color(true);
         return EXIT_SUCCESS;
index a8fd45df733e77aed6d42e46a971e725c11fb53c..e78e299ed293c2d753589e49ac18b9278a6d1f7a 100644 (file)
@@ -1324,4 +1324,27 @@ TEST(strlevenshtein) {
         assert_se(strlevenshtein("sunday", "saturday") == 3);
 }
 
+TEST(strrstr) {
+        assert_se(!strrstr(NULL, NULL));
+        assert_se(!strrstr("foo", NULL));
+        assert_se(!strrstr(NULL, "foo"));
+
+        const char *p = "foo";
+        assert_se(strrstr(p, "foo") == p);
+        assert_se(strrstr(p, "fo") == p);
+        assert_se(strrstr(p, "f") == p);
+        assert_se(strrstr(p, "oo") == p + 1);
+        assert_se(strrstr(p, "o") == p + 2);
+        assert_se(strrstr(p, "") == p + strlen(p));
+        assert_se(!strrstr(p, "bar"));
+
+        p = "xoxoxox";
+        assert_se(strrstr(p, "") == p + strlen(p));
+        assert_se(strrstr(p, "x") == p + 6);
+        assert_se(strrstr(p, "ox") == p + 5);
+        assert_se(strrstr(p, "xo") == p + 4);
+        assert_se(strrstr(p, "xox") == p + 4);
+        assert_se(!strrstr(p, "xx"));
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index cfd662b329d952d091469b9d4d2e0693c0b89b12..f4a45703d0d433e68d137dc3f07f64662961b8e6 100644 (file)
@@ -1006,4 +1006,12 @@ TEST(strv_find_first_field) {
         assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("i", "k", "l", "m", "d", "c", "a", "b"), haystack), "j"));
 }
 
+TEST(strv_endswith) {
+        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("xxx", "yyy", "ldo", "zzz")), "ldo"));
+        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("xxx", "yyy", "zzz")), NULL));
+        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("waldo")), "waldo"));
+        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("w", "o", "ldo")), "o"));
+        assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("knurz", "", "waldo")), ""));
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
diff --git a/src/test/test-vpick.c b/src/test/test-vpick.c
new file mode 100644 (file)
index 0000000..1a288e8
--- /dev/null
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+#include "vpick.h"
+
+TEST(path_pick) {
+        _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
+        _cleanup_close_ int dfd = -EBADF, sub_dfd = -EBADF;
+
+        dfd = mkdtemp_open(NULL, O_DIRECTORY|O_CLOEXEC, &p);
+        assert(dfd >= 0);
+
+        sub_dfd = open_mkdir_at(dfd, "foo.v", O_CLOEXEC, 0777);
+        assert(sub_dfd >= 0);
+
+        assert_se(write_string_file_at(sub_dfd, "foo_5.5.raw", "5.5", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_55.raw", "55", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_5.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_5_ia64.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_7.raw", "7", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_7_x86-64.raw", "7 64bit", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_55_x86-64.raw", "55 64bit", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_55_x86.raw", "55 32bit", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "foo_99_x86.raw", "99 32bit", WRITE_STRING_FILE_CREATE) >= 0);
+
+        /* Let's add an entry for sparc (which is a valid arch, but almost certainly not what we test
+         * on). This entry should hence always be ignored */
+        if (native_architecture() != ARCHITECTURE_SPARC)
+                assert_se(write_string_file_at(sub_dfd, "foo_100_sparc.raw", "100 sparc", WRITE_STRING_FILE_CREATE) >= 0);
+
+        assert_se(write_string_file_at(sub_dfd, "quux_1_s390.raw", "waldo1", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "quux_2_s390+4-6.raw", "waldo2", WRITE_STRING_FILE_CREATE) >= 0);
+        assert_se(write_string_file_at(sub_dfd, "quux_3_s390+0-10.raw", "waldo3", WRITE_STRING_FILE_CREATE) >= 0);
+
+        _cleanup_free_ char *pp = NULL;
+        pp = path_join(p, "foo.v");
+        assert_se(pp);
+
+        _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+
+        PickFilter filter = {
+                .architecture = _ARCHITECTURE_INVALID,
+                .suffix = ".raw",
+        };
+
+        if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
+                assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+                assert_se(S_ISREG(result.st.st_mode));
+                assert_se(streq_ptr(result.version, "99"));
+                assert_se(result.architecture == ARCHITECTURE_X86);
+                assert_se(endswith(result.path, "/foo_99_x86.raw"));
+
+                pick_result_done(&result);
+        }
+
+        filter.architecture = ARCHITECTURE_X86_64;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "55"));
+        assert_se(result.architecture == ARCHITECTURE_X86_64);
+        assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
+        pick_result_done(&result);
+
+        filter.architecture = ARCHITECTURE_IA64;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "5"));
+        assert_se(result.architecture == ARCHITECTURE_IA64);
+        assert_se(endswith(result.path, "/foo_5_ia64.raw"));
+        pick_result_done(&result);
+
+        filter.architecture = _ARCHITECTURE_INVALID;
+        filter.version = "5";
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "5"));
+        if (native_architecture() != ARCHITECTURE_IA64) {
+                assert_se(result.architecture == _ARCHITECTURE_INVALID);
+                assert_se(endswith(result.path, "/foo_5.raw"));
+        }
+        pick_result_done(&result);
+
+        filter.architecture = ARCHITECTURE_IA64;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "5"));
+        assert_se(result.architecture == ARCHITECTURE_IA64);
+        assert_se(endswith(result.path, "/foo_5_ia64.raw"));
+        pick_result_done(&result);
+
+        filter.architecture = ARCHITECTURE_CRIS;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) == 0);
+        assert_se(result.st.st_mode == MODE_INVALID);
+        assert_se(!result.version);
+        assert_se(result.architecture < 0);
+        assert_se(!result.path);
+
+        assert_se(unlinkat(sub_dfd, "foo_99_x86.raw", 0) >= 0);
+
+        filter.architecture = _ARCHITECTURE_INVALID;
+        filter.version = NULL;
+        if (IN_SET(native_architecture(), ARCHITECTURE_X86_64, ARCHITECTURE_X86)) {
+                assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+                assert_se(S_ISREG(result.st.st_mode));
+                assert_se(streq_ptr(result.version, "55"));
+
+                if (native_architecture() == ARCHITECTURE_X86_64) {
+                        assert_se(result.architecture == ARCHITECTURE_X86_64);
+                        assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
+                } else {
+                        assert_se(result.architecture == ARCHITECTURE_X86);
+                        assert_se(endswith(result.path, "/foo_55_x86.raw"));
+                }
+                pick_result_done(&result);
+        }
+
+        /* Test explicit patterns in last component of path not being .v */
+        free(pp);
+        pp = path_join(p, "foo.v/foo___.raw");
+        assert_se(pp);
+
+        if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
+                assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+                assert_se(S_ISREG(result.st.st_mode));
+                assert_se(streq_ptr(result.version, "55"));
+                assert_se(result.architecture == native_architecture());
+                assert_se(endswith(result.path, ".raw"));
+                assert_se(strrstr(result.path, "/foo_55_x86"));
+                pick_result_done(&result);
+        }
+
+        /* Specify an explicit path */
+        free(pp);
+        pp = path_join(p, "foo.v/foo_5.raw");
+        assert_se(pp);
+
+        filter.type_mask = UINT32_C(1) << DT_DIR;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) == -ENOTDIR);
+
+        filter.type_mask = UINT32_C(1) << DT_REG;
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(!result.version);
+        assert_se(result.architecture == _ARCHITECTURE_INVALID);
+        assert_se(path_equal(result.path, pp));
+        pick_result_done(&result);
+
+        free(pp);
+        pp = path_join(p, "foo.v");
+        assert_se(pp);
+
+        filter.architecture = ARCHITECTURE_S390;
+        filter.basename = "quux";
+
+        assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+        assert_se(S_ISREG(result.st.st_mode));
+        assert_se(streq_ptr(result.version, "2"));
+        assert_se(result.tries_left == 4);
+        assert_se(result.tries_done == 6);
+        assert_se(endswith(result.path, "quux_2_s390+4-6.raw"));
+        assert_se(result.architecture == ARCHITECTURE_S390);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
index c85721c98ee07ed4a330f1d91ca5c46a1b99feb0..77fad97b7fa1714d8f28aa66bb267fbf1c9b4456 100644 (file)
@@ -13,4 +13,10 @@ executables += [
                         libopenssl,
                 ],
         },
+
+        generator_template + {
+                'name' : 'systemd-tpm2-generator',
+                'sources' : files('tpm2-generator.c'),
+        },
+
 ]
diff --git a/src/tpm2-setup/tpm2-generator.c b/src/tpm2-setup/tpm2-generator.c
new file mode 100644 (file)
index 0000000..1ba8a7f
--- /dev/null
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "generator.h"
+#include "proc-cmdline.h"
+#include "special.h"
+#include "tpm2-util.h"
+#include "parse-util.h"
+
+/* A small generator that enqueues tpm2.target as synchronization point if the TPM2 device hasn't shown up
+ * yet, but the firmware reports it to exist. This is supposed to deal with systems where the TPM2 driver
+ * support is built as kmod and must be loaded before it's ready to be used. The tpm2.target is only enqueued
+ * if firmware says there is a TPM2 device, our userspace support for TPM2 is fully available but the TPM2
+ * device hasn't shown up in /dev/ yet. */
+
+static const char *arg_dest = NULL;
+static int arg_tpm2_wait = -1; /* tri-state: negative → don't know */
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+        int r;
+
+        assert(key);
+
+        if (proc_cmdline_key_streq(key, "systemd.tpm2-wait")) {
+                r = value ? parse_boolean(value) : 1;
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse 'systemd.tpm2-wait' kernel command line argument, ignoring: %s", value);
+                else
+                        arg_tpm2_wait = r;
+        }
+
+        return 0;
+}
+
+static int generate_tpm_target_symlink(void) {
+        int r;
+
+        if (arg_tpm2_wait == 0) {
+                log_debug("Not generating tpm2.target synchronization point, as this was explicitly turned off via kernel command line.");
+                return 0;
+        }
+
+        if (arg_tpm2_wait < 0) {
+                Tpm2Support support = tpm2_support();
+
+                if (FLAGS_SET(support, TPM2_SUPPORT_DRIVER)) {
+                        log_debug("Not generating tpm2.target synchronization point, as TPM2 device is already present.");
+                        return 0;
+                }
+
+                if (!FLAGS_SET(support, TPM2_SUPPORT_FIRMWARE)) {
+                        log_debug("Not generating tpm2.target synchronization point, as firmware reports no TPM2 present.");
+                        return 0;
+                }
+
+                if (!FLAGS_SET(support, TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_LIBRARIES)) {
+                        log_debug("Not generating tpm2.target synchronization point, as userspace support for TPM2 is not complete.");
+                        return 0;
+                }
+        }
+
+        r = generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_TPM2_TARGET);
+        if (r < 0)
+                return log_error_errno(r, "Failed to hook in tpm2.target: %m");
+
+        return 0;
+}
+
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+        int r;
+
+        assert_se(arg_dest = dest);
+
+        r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
+        if (r < 0)
+                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+
+        return generate_tpm_target_symlink();
+}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
index 9ad41403bab02ff108337f937729155be0c9f4a3..0393d29a733cb510e1f3e6bb64da0cf15f78e1e3 100644 (file)
@@ -7,7 +7,7 @@
  *   Copyright (C) 2020 Bastien Nocera <hadess@hadess.net>
  *
  * Unless specified otherwise, all references are aimed at the "System
- * Management BIOS Reference Specification, Version 3.2.0" document,
+ * Management BIOS Reference Specification, Version 3.7.0" document,
  * available from http://www.dmtf.org/standards/smbios.
  *
  * Note to contributors:
@@ -145,7 +145,7 @@ static void dmi_memory_array_location(uint8_t code) {
                 [0x01] = "PC-98/C24 Add-on Card",       /* 0xA1 */
                 [0x02] = "PC-98/E Add-on Card",         /* 0xA2 */
                 [0x03] = "PC-98/Local Bus Add-on Card", /* 0xA3 */
-                [0x04] = "CXL Flexbus 1.0",             /* 0xA4 */
+                [0x04] = "CXL Add-on Card",             /* 0xA4 */
         };
         const char *str = OUT_OF_SPEC_STR;
 
@@ -301,6 +301,9 @@ static void dmi_memory_device_type(unsigned slot_num, uint8_t code) {
                 [0x1F] = "Logical non-volatile device",
                 [0x20] = "HBM",
                 [0x21] = "HBM2",
+                [0x22] = "DDR5",
+                [0x23] = "LPDDR5",
+                [0x24] = "HBM3",
         };
 
         printf("MEMORY_DEVICE_%u_TYPE=%s\n", slot_num,
@@ -315,7 +318,7 @@ static void dmi_memory_device_type_detail(unsigned slot_num, uint16_t code) {
                 [3]  = "Fast-paged",
                 [4]  = "Static Column",
                 [5]  = "Pseudo-static",
-                [6]  = "RAMBus",
+                [6]  = "RAMBUS",
                 [7]  = "Synchronous",
                 [8]  = "CMOS",
                 [9]  = "EDO",
@@ -358,7 +361,7 @@ static void dmi_memory_device_technology(unsigned slot_num, uint8_t code) {
                 [0x04] = "NVDIMM-N",
                 [0x05] = "NVDIMM-F",
                 [0x06] = "NVDIMM-P",
-                [0x07] = "Intel Optane DC persistent memory",
+                [0x07] = "Intel Optane persistent memory",
         };
 
         printf("MEMORY_DEVICE_%u_MEMORY_TECHNOLOGY=%s\n", slot_num,
index 72296b3f0e69cbf0fc3b4edeabbe49cfbff53af9..fa6c59c7627f645f48869bb6623013cb7acb996d 100644 (file)
@@ -12,6 +12,7 @@
 #include <unistd.h>
 
 #include "device-private.h"
+#include "device-util.h"
 #include "fs-util.h"
 #include "log.h"
 #include "main-func.h"
@@ -141,16 +142,15 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_debug_errno(r, "Failed to open device '%s'", devpath);
 
-        assert_se(event = udev_event_new(dev, 0, NULL, log_get_max_level()));
+        assert_se(event = udev_event_new(dev, NULL));
 
         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0);
 
         /* do what devtmpfs usually provides us */
         if (sd_device_get_devname(dev, &devname) >= 0) {
-                const char *subsystem;
                 mode_t mode = 0600;
 
-                if (sd_device_get_subsystem(dev, &subsystem) >= 0 && streq(subsystem, "block"))
+                if (device_in_subsystem(dev, "block"))
                         mode |= S_IFBLK;
                 else
                         mode |= S_IFCHR;
@@ -169,8 +169,8 @@ static int run(int argc, char *argv[]) {
                 }
         }
 
-        udev_event_execute_rules(event, -1, 3 * USEC_PER_SEC, SIGKILL, NULL, rules);
-        udev_event_execute_run(event, 3 * USEC_PER_SEC, SIGKILL);
+        udev_event_execute_rules(event, rules);
+        udev_event_execute_run(event);
 
         return 0;
 }
index 447e50d978fcb90fe26ce8610f3a93997e3835e8..3a11dd9b2017f3982be1aa7cc7a41d7c5e5b5551 100644 (file)
@@ -17,8 +17,8 @@ static void test_event_spawn_core(bool with_pidfd, const char *cmd, char *result
         assert_se(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1) >= 0);
 
         assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net/lo") >= 0);
-        assert_se(event = udev_event_new(dev, 0, NULL, LOG_DEBUG));
-        assert_se(udev_event_spawn(event, 5 * USEC_PER_SEC, SIGKILL, false, cmd, result_buf, buf_size, NULL) == 0);
+        assert_se(event = udev_event_new(dev, NULL));
+        assert_se(udev_event_spawn(event, false, cmd, result_buf, buf_size, NULL) == 0);
 
         assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
 }
index 19e07e734f1afea6e73cc9c80f56255d816149fa..4540c33d982e6ce7b69d80a14dbc2ccc3b4d3046 100644 (file)
@@ -77,20 +77,15 @@ static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
                 srcdev = dev;
 
         for (sd_device *d = srcdev; d; ) {
-                const char *dsubsys, *devtype, *modalias = NULL;
-
-                if (sd_device_get_subsystem(d, &dsubsys) < 0)
-                        goto next;
+                const char *modalias = NULL;
 
                 /* look only at devices of a specific subsystem */
-                if (subsystem && !streq(dsubsys, subsystem))
+                if (subsystem && !device_in_subsystem(d, subsystem))
                         goto next;
 
                 (void) sd_device_get_property_value(d, "MODALIAS", &modalias);
 
-                if (streq(dsubsys, "usb") &&
-                    sd_device_get_devtype(d, &devtype) >= 0 &&
-                    streq(devtype, "usb_device")) {
+                if (device_in_subsystem(d, "usb") && device_is_devtype(d, "usb_device")) {
                         /* if the usb_device does not have a modalias, compose one */
                         if (!modalias)
                                 modalias = modalias_usb(d, s, sizeof(s));
index 841e4615fcb1456d36a6e360e1fa75fefacfb614..2482436006250864b20603d20d654c7c96ba1228 100644 (file)
@@ -49,12 +49,7 @@ static sd_device *device_skip_virtio(sd_device *dev) {
          * safely ignore any virtio buses. see
          * http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html */
         while (dev) {
-                const char *subsystem;
-
-                if (sd_device_get_subsystem(dev, &subsystem) < 0)
-                        break;
-
-                if (!streq(subsystem, "virtio"))
+                if (!device_in_subsystem(dev, "virtio"))
                         break;
 
                 if (sd_device_get_parent(dev, &dev) < 0)
@@ -86,22 +81,15 @@ static int get_matching_parent(
                         return -ENODEV;
         }
 
-        if (!strv_isempty(parent_subsystems)) {
-                const char *subsystem;
-
-                /* check if our direct parent is in an expected subsystem. */
-                r = sd_device_get_subsystem(parent, &subsystem);
-                if (r < 0)
-                        return r;
-
-                if (!strv_contains(parent_subsystems, subsystem))
-                        return -ENODEV;
-        }
-
-        if (ret)
-                *ret = parent;
+        /* check if our direct parent is in an expected subsystem. */
+        STRV_FOREACH(s, parent_subsystems)
+                if (device_in_subsystem(parent, *s)) {
+                        if (ret)
+                                *ret = parent;
+                        return 0;
+                }
 
-        return 0;
+        return -ENODEV;
 }
 
 static int get_first_syspath_component(sd_device *dev, const char *prefix, char **ret) {
@@ -1272,15 +1260,9 @@ static int get_ifname_prefix(sd_device *dev, const char **ret) {
         /* handle only ARPHRD_ETHER, ARPHRD_SLIP and ARPHRD_INFINIBAND devices */
         switch (iftype) {
         case ARPHRD_ETHER: {
-                const char *s = NULL;
-
-                r = sd_device_get_devtype(dev, &s);
-                if (r < 0 && r != -ENOENT)
-                        return r;
-
-                if (streq_ptr(s, "wlan"))
+                if (device_is_devtype(dev, "wlan"))
                         *ret = "wl";
-                else if (streq_ptr(s, "wwan"))
+                else if (device_is_devtype(dev, "wwan"))
                         *ret = "ww";
                 else
                         *ret = "en";
index f1370e60608ceff37b35aa574a6be0c480e2f52a..ebeadc3f3d105d52a48cb85ff266f5f9de0d7b14 100644 (file)
@@ -95,12 +95,7 @@ static sd_device *skip_subsystem(sd_device *dev, const char *subsys) {
          */
 
         for (parent = dev; ; ) {
-                const char *subsystem;
-
-                if (sd_device_get_subsystem(parent, &subsystem) < 0)
-                        break;
-
-                if (!streq(subsystem, subsys))
+                if (!device_in_subsystem(parent, subsys))
                         break;
 
                 dev = parent;
@@ -417,10 +412,9 @@ static sd_device *handle_scsi_hyperv(sd_device *parent, char **path, size_t guid
 }
 
 static sd_device *handle_scsi(sd_device *parent, char **path, char **compat_path, bool *supported_parent) {
-        const char *devtype, *id, *name;
+        const char *id, *name;
 
-        if (sd_device_get_devtype(parent, &devtype) < 0 ||
-            !streq(devtype, "scsi_device"))
+        if (!device_is_devtype(parent, "scsi_device"))
                 return parent;
 
         /* firewire */
@@ -532,12 +526,10 @@ static int get_usb_revision(sd_device *dev) {
 }
 
 static sd_device *handle_usb(sd_device *parent, char **path) {
-        const char *devtype, *str, *port;
+        const char *str, *port;
         int r;
 
-        if (sd_device_get_devtype(parent, &devtype) < 0)
-                return parent;
-        if (!STR_IN_SET(devtype, "usb_interface", "usb_device"))
+        if (!device_is_devtype(parent, "usb_interface") && !device_is_devtype(parent, "usb_device"))
                 return parent;
 
         if (sd_device_get_sysname(parent, &str) < 0)
@@ -715,105 +707,103 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test)
         _cleanup_(sd_device_unrefp) sd_device *dev_other_branch = NULL;
         _cleanup_free_ char *path = NULL, *compat_path = NULL;
         bool supported_transport = false, supported_parent = false;
-        const char *subsystem;
         int r;
 
         /* walk up the chain of devices and compose path */
         for (sd_device *parent = dev; parent; ) {
-                const char *subsys, *sysname;
+                const char *sysname;
 
-                if (sd_device_get_subsystem(parent, &subsys) < 0 ||
-                    sd_device_get_sysname(parent, &sysname) < 0) {
+                if (sd_device_get_sysname(parent, &sysname) < 0) {
                         ;
-                } else if (streq(subsys, "scsi_tape")) {
+                } else if (device_in_subsystem(parent, "scsi_tape")) {
                         handle_scsi_tape(parent, &path);
-                } else if (streq(subsys, "scsi")) {
+                } else if (device_in_subsystem(parent, "scsi")) {
                         parent = handle_scsi(parent, &path, &compat_path, &supported_parent);
                         supported_transport = true;
-                } else if (streq(subsys, "cciss")) {
+                } else if (device_in_subsystem(parent, "cciss")) {
                         parent = handle_cciss(parent, &path);
                         supported_transport = true;
-                } else if (streq(subsys, "usb")) {
+                } else if (device_in_subsystem(parent, "usb")) {
                         parent = handle_usb(parent, &path);
                         supported_transport = true;
-                } else if (streq(subsys, "bcma")) {
+                } else if (device_in_subsystem(parent, "bcma")) {
                         parent = handle_bcma(parent, &path);
                         supported_transport = true;
-                } else if (streq(subsys, "serio")) {
+                } else if (device_in_subsystem(parent, "serio")) {
                         const char *sysnum;
 
                         if (sd_device_get_sysnum(parent, &sysnum) >= 0 && sysnum) {
                                 path_prepend(&path, "serio-%s", sysnum);
                                 parent = skip_subsystem(parent, "serio");
                         }
-                } else if (streq(subsys, "pci")) {
+                } else if (device_in_subsystem(parent, "pci")) {
                         path_prepend(&path, "pci-%s", sysname);
                         if (compat_path)
                                 path_prepend(&compat_path, "pci-%s", sysname);
                         parent = skip_subsystem(parent, "pci");
                         supported_parent = true;
-                } else if (streq(subsys, "platform")) {
+                } else if (device_in_subsystem(parent, "platform")) {
                         path_prepend(&path, "platform-%s", sysname);
                         if (compat_path)
                                 path_prepend(&compat_path, "platform-%s", sysname);
                         parent = skip_subsystem(parent, "platform");
                         supported_transport = true;
                         supported_parent = true;
-                } else if (streq(subsys, "amba")) {
+                } else if (device_in_subsystem(parent, "amba")) {
                         path_prepend(&path, "amba-%s", sysname);
                         if (compat_path)
                                 path_prepend(&compat_path, "amba-%s", sysname);
                         parent = skip_subsystem(parent, "amba");
                         supported_transport = true;
                         supported_parent = true;
-                } else if (streq(subsys, "acpi")) {
+                } else if (device_in_subsystem(parent, "acpi")) {
                         path_prepend(&path, "acpi-%s", sysname);
                         if (compat_path)
                                 path_prepend(&compat_path, "acpi-%s", sysname);
                         parent = skip_subsystem(parent, "acpi");
                         supported_parent = true;
-                } else if (streq(subsys, "xen")) {
+                } else if (device_in_subsystem(parent, "xen")) {
                         path_prepend(&path, "xen-%s", sysname);
                         if (compat_path)
                                 path_prepend(&compat_path, "xen-%s", sysname);
                         parent = skip_subsystem(parent, "xen");
                         supported_parent = true;
-                } else if (streq(subsys, "virtio")) {
+                } else if (device_in_subsystem(parent, "virtio")) {
                         parent = skip_subsystem(parent, "virtio");
                         supported_transport = true;
-                } else if (streq(subsys, "scm")) {
+                } else if (device_in_subsystem(parent, "scm")) {
                         path_prepend(&path, "scm-%s", sysname);
                         if (compat_path)
                                 path_prepend(&compat_path, "scm-%s", sysname);
                         parent = skip_subsystem(parent, "scm");
                         supported_transport = true;
                         supported_parent = true;
-                } else if (streq(subsys, "ccw")) {
+                } else if (device_in_subsystem(parent, "ccw")) {
                         path_prepend(&path, "ccw-%s", sysname);
                         if (compat_path)
                                 path_prepend(&compat_path, "ccw-%s", sysname);
                         parent = skip_subsystem(parent, "ccw");
                         supported_transport = true;
                         supported_parent = true;
-                } else if (streq(subsys, "ccwgroup")) {
+                } else if (device_in_subsystem(parent, "ccwgroup")) {
                         path_prepend(&path, "ccwgroup-%s", sysname);
                         if (compat_path)
                                 path_prepend(&compat_path, "ccwgroup-%s", sysname);
                         parent = skip_subsystem(parent, "ccwgroup");
                         supported_transport = true;
                         supported_parent = true;
-                } else if (streq(subsys, "ap")) {
+                } else if (device_in_subsystem(parent, "ap")) {
                         parent = handle_ap(parent, &path);
                         supported_transport = true;
                         supported_parent = true;
-                } else if (streq(subsys, "iucv")) {
+                } else if (device_in_subsystem(parent, "iucv")) {
                         path_prepend(&path, "iucv-%s", sysname);
                         if (compat_path)
                                 path_prepend(&compat_path, "iucv-%s", sysname);
                         parent = skip_subsystem(parent, "iucv");
                         supported_transport = true;
                         supported_parent = true;
-                } else if (STR_IN_SET(subsys, "nvme", "nvme-subsystem")) {
+                } else if (device_in_subsystem(parent, "nvme") || device_in_subsystem(parent, "nvme-subsystem")) {
                         const char *nsid;
 
                         if (sd_device_get_sysattr_value(dev, "nsid", &nsid) >= 0) {
@@ -821,7 +811,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test)
                                 if (compat_path)
                                         path_prepend(&compat_path, "nvme-%s", nsid);
 
-                                if (streq(subsys, "nvme-subsystem")) {
+                                if (device_in_subsystem(parent, "nvme-subsystem")) {
                                         r = find_real_nvme_parent(dev, &dev_other_branch);
                                         if (r < 0)
                                                 return r;
@@ -833,7 +823,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test)
                                 supported_parent = true;
                                 supported_transport = true;
                         }
-                } else if (streq(subsys, "spi")) {
+                } else if (device_in_subsystem(parent, "spi")) {
                         const char *sysnum;
 
                         if (sd_device_get_sysnum(parent, &sysnum) >= 0 && sysnum) {
@@ -864,9 +854,7 @@ static int builtin_path_id(UdevEvent *event, int argc, char *argv[], bool test)
          * devices do not expose their buses and do not provide a unique
          * and predictable name that way.
          */
-        if (sd_device_get_subsystem(dev, &subsystem) >= 0 &&
-            streq(subsystem, "block") &&
-            !supported_transport)
+        if (device_in_subsystem(dev, "block") && !supported_transport)
                 return -ENOENT;
 
         add_id_with_usb_revision(dev, test, path);
index 8e83c9c3426615960c3308c110daf4a3505fea93..f5b8fe9e9adf4916010ed02fc82ff21bd000f740 100644 (file)
@@ -248,7 +248,7 @@ static int builtin_usb_id(UdevEvent *event, int argc, char *argv[], bool test) {
         size_t l;
         char *s;
 
-        const char *syspath, *sysname, *devtype, *interface_syspath;
+        const char *syspath, *sysname, *interface_syspath;
         int r;
 
         r = sd_device_get_syspath(dev, &syspath);
@@ -260,7 +260,7 @@ static int builtin_usb_id(UdevEvent *event, int argc, char *argv[], bool test) {
                 return r;
 
         /* shortcut, if we are called directly for a "usb_device" type */
-        if (sd_device_get_devtype(dev, &devtype) >= 0 && streq(devtype, "usb_device")) {
+        if (device_is_devtype(dev, "usb_device")) {
                 dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str));
                 dev_usb = dev;
                 goto fallback;
index ed22c8b6793f301571edc7cc39dcd606ad8bef09..4d75bf34f74766d79913f70f224d839fec9bd4bb 100644 (file)
 #include "udev-node.h"
 #include "udev-trace.h"
 #include "udev-util.h"
-#include "udev-watch.h"
 #include "user-util.h"
 
-UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl, int log_level) {
+UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker) {
+        int log_level = worker ? worker->log_level : log_get_max_level();
         UdevEvent *event;
 
         assert(dev);
@@ -27,10 +27,10 @@ UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rt
                 return NULL;
 
         *event = (UdevEvent) {
+                .worker = worker,
+                .rtnl = worker ? sd_netlink_ref(worker->rtnl) : NULL,
                 .dev = sd_device_ref(dev),
                 .birth_usec = now(CLOCK_MONOTONIC),
-                .exec_delay_usec = exec_delay_usec,
-                .rtnl = sd_netlink_ref(rtnl),
                 .uid = UID_INVALID,
                 .gid = GID_INVALID,
                 .mode = MODE_INVALID,
@@ -276,14 +276,7 @@ static int update_devnode(UdevEvent *event) {
         return udev_node_update(dev, event->dev_db_clone);
 }
 
-static int event_execute_rules_on_remove(
-                UdevEvent *event,
-                int inotify_fd,
-                usec_t timeout_usec,
-                int timeout_signal,
-                Hashmap *properties_list,
-                UdevRules *rules) {
-
+static int event_execute_rules_on_remove(UdevEvent *event, UdevRules *rules) {
         sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
         int r;
 
@@ -299,11 +292,7 @@ static int event_execute_rules_on_remove(
         if (r < 0)
                 log_device_debug_errno(dev, r, "Failed to delete database under /run/udev/data/, ignoring: %m");
 
-        r = udev_watch_end(inotify_fd, dev);
-        if (r < 0)
-                log_device_warning_errno(dev, r, "Failed to remove inotify watch, ignoring: %m");
-
-        r = udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list);
+        r = udev_rules_apply_to_event(rules, event);
 
         if (sd_device_get_devnum(dev, NULL) >= 0)
                 (void) udev_node_remove(dev);
@@ -328,14 +317,7 @@ static int copy_all_tags(sd_device *d, sd_device *s) {
         return 0;
 }
 
-int udev_event_execute_rules(
-                UdevEvent *event,
-                int inotify_fd, /* This may be negative */
-                usec_t timeout_usec,
-                int timeout_signal,
-                Hashmap *properties_list,
-                UdevRules *rules) {
-
+int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) {
         sd_device_action_t action;
         sd_device *dev;
         int r;
@@ -348,12 +330,7 @@ int udev_event_execute_rules(
                 return log_device_error_errno(dev, r, "Failed to get ACTION: %m");
 
         if (action == SD_DEVICE_REMOVE)
-                return event_execute_rules_on_remove(event, inotify_fd, timeout_usec, timeout_signal, properties_list, rules);
-
-        /* Disable watch during event processing. */
-        r = udev_watch_end(inotify_fd, dev);
-        if (r < 0)
-                log_device_warning_errno(dev, r, "Failed to remove inotify watch, ignoring: %m");
+                return event_execute_rules_on_remove(event, rules);
 
         r = device_clone_with_db(dev, &event->dev_db_clone);
         if (r < 0)
@@ -373,7 +350,7 @@ int udev_event_execute_rules(
 
         DEVICE_TRACE_POINT(rules_start, dev);
 
-        r = udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list);
+        r = udev_rules_apply_to_event(rules, event);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to apply udev rules: %m");
 
index 6b94fd08aba29af39908da1fc4492074c7d0ded5..5ff7bd7ec1b9f7f23f170ab0bf8673cdd23bc7e0 100644 (file)
 #include "macro.h"
 #include "time-util.h"
 #include "udev-rules.h"
+#include "udev-worker.h"
 #include "user-util.h"
 
 typedef struct UdevEvent {
+        UdevWorker *worker;
+        sd_netlink *rtnl;
+
         sd_device *dev;
         sd_device *dev_parent;
         sd_device *dev_db_clone;
@@ -29,9 +33,7 @@ typedef struct UdevEvent {
         gid_t gid;
         OrderedHashmap *seclabel_list;
         OrderedHashmap *run_list;
-        usec_t exec_delay_usec;
         usec_t birth_usec;
-        sd_netlink *rtnl;
         unsigned builtin_run;
         unsigned builtin_ret;
         UdevRuleEscapeType esc:8;
@@ -47,14 +49,8 @@ typedef struct UdevEvent {
         int default_log_level;
 } UdevEvent;
 
-UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl, int log_level);
+UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker);
 UdevEvent *udev_event_free(UdevEvent *event);
 DEFINE_TRIVIAL_CLEANUP_FUNC(UdevEvent*, udev_event_free);
 
-int udev_event_execute_rules(
-                UdevEvent *event,
-                int inotify_fd,
-                usec_t timeout_usec,
-                int timeout_signal,
-                Hashmap *properties_list,
-                UdevRules *rules);
+int udev_event_execute_rules(UdevEvent *event, UdevRules *rules);
index 05ed9fdff79482f8b9814176b9d7a91f9cd6e451..bd74ab0a3a2234817c3667466e54b6ad796873d0 100644 (file)
@@ -156,7 +156,6 @@ static ssize_t udev_event_subst_format(
                 const char *attr,
                 char *dest,
                 size_t l,
-                Hashmap *global_props,
                 bool *ret_truncated) {
 
         sd_device *parent, *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
@@ -349,7 +348,7 @@ static ssize_t udev_event_subst_format(
         case FORMAT_SUBST_ENV:
                 if (isempty(attr))
                         return -EINVAL;
-                r = device_get_property_value_with_fallback(dev, attr, global_props, &val);
+                r = device_get_property_value_with_fallback(dev, attr, event->worker ? event->worker->properties : NULL, &val);
                 if (r == -ENOENT)
                         goto null_terminate;
                 if (r < 0)
@@ -379,7 +378,6 @@ size_t udev_event_apply_format(
                 char *dest,
                 size_t size,
                 bool replace_whitespace,
-                Hashmap *global_props,
                 bool *ret_truncated) {
 
         bool truncated = false;
@@ -412,7 +410,7 @@ size_t udev_event_apply_format(
                         continue;
                 }
 
-                subst_len = udev_event_subst_format(event, type, attr, dest, size, global_props, &t);
+                subst_len = udev_event_subst_format(event, type, attr, dest, size, &t);
                 if (subst_len < 0) {
                         log_device_warning_errno(event->dev, subst_len,
                                                  "Failed to substitute variable '$%s' or apply format '%%%c', ignoring: %m",
index 92fef9baca764fdd2b7806421e50cef072b9414d..9914dc03b2d133a524ba5f80e4764fef8e54f084 100644 (file)
@@ -14,7 +14,6 @@ size_t udev_event_apply_format(
                 char *dest,
                 size_t size,
                 bool replace_whitespace,
-                Hashmap *global_props,
                 bool *ret_truncated);
 int udev_check_format(const char *value, size_t *offset, const char **hint);
 
index 1cf215ed46cb6e2782b3cc93566208451cb5c748..5bf00bb26255abb1e53c89d12b719d9335453bc8 100644 (file)
@@ -332,6 +332,28 @@ static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *use
         return 1;
 }
 
+static usec_t extra_timeout_usec(void) {
+        static usec_t saved = 10 * USEC_PER_SEC;
+        static bool parsed = false;
+        const char *e;
+        int r;
+
+        if (parsed)
+                return saved;
+
+        parsed = true;
+
+        e = getenv("SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC");
+        if (!e)
+                return saved;
+
+        r = parse_sec(e, &saved);
+        if (r < 0)
+                log_debug_errno(r, "Failed to parse $SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC=%s, ignoring: %m", e);
+
+        return saved;
+}
+
 static void worker_attach_event(Worker *worker, Event *event) {
         Manager *manager = ASSERT_PTR(ASSERT_PTR(worker)->manager);
         sd_event *e = ASSERT_PTR(manager->event);
@@ -349,8 +371,12 @@ static void worker_attach_event(Worker *worker, Event *event) {
                                           udev_warn_timeout(manager->timeout_usec), USEC_PER_SEC,
                                           on_event_timeout_warning, event);
 
+        /* Manager.timeout_usec is also used as the timeout for running programs specified in
+         * IMPORT{program}=, PROGRAM=, or RUN=. Here, let's add an extra time before the manager
+         * kills a worker, to make it possible that the worker detects timed out of spawned programs,
+         * kills them, and finalizes the event. */
         (void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
-                                          manager->timeout_usec, USEC_PER_SEC,
+                                          usec_add(manager->timeout_usec, extra_timeout_usec()), USEC_PER_SEC,
                                           on_event_timeout, event);
 }
 
index e12c26ce5a6c9b95a5aff49486d9527c8b5c1f7b..633fb2a31d7d7b8c66178679e5d867c2512e7128 100644 (file)
@@ -510,22 +510,17 @@ static int link_update(sd_device *dev, const char *slink, bool add) {
 }
 
 static int device_get_devpath_by_devnum(sd_device *dev, char **ret) {
-        const char *subsystem;
         dev_t devnum;
         int r;
 
         assert(dev);
         assert(ret);
 
-        r = sd_device_get_subsystem(dev, &subsystem);
-        if (r < 0)
-                return r;
-
         r = sd_device_get_devnum(dev, &devnum);
         if (r < 0)
                 return r;
 
-        return device_path_make_major_minor(streq(subsystem, "block") ? S_IFBLK : S_IFCHR, devnum, ret);
+        return device_path_make_major_minor(device_in_subsystem(dev, "block") ? S_IFBLK : S_IFCHR, devnum, ret);
 }
 
 int udev_node_update(sd_device *dev, sd_device *dev_old) {
index 5f120023948d07f04b6475007b2b08c30bb49c32..9d01e5866c41b0aff16eca49f083b0d5189f10a6 100644 (file)
@@ -1761,7 +1761,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
 
         switch (token->attr_subst_type) {
         case SUBST_TYPE_FORMAT:
-                (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, NULL, &truncated);
+                (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "sysfs attribute name", name,
                                             token->type == TK_M_ATTR ? "ATTR" : "ATTRS", /* is_match = */ true);
@@ -1928,10 +1928,7 @@ static size_t udev_replace_ifname(char *str) {
 static int udev_rule_apply_token_to_event(
                 UdevRuleToken *token,
                 sd_device *dev,
-                UdevEvent *event,
-                usec_t timeout_usec,
-                int timeout_signal,
-                Hashmap *properties_list) {
+                UdevEvent *event) {
 
         int r;
 
@@ -1983,7 +1980,7 @@ static int udev_rule_apply_token_to_event(
         case TK_M_ENV: {
                 const char *val = NULL;
 
-                (void) device_get_property_value_with_fallback(dev, token->data, properties_list, &val);
+                (void) device_get_property_value_with_fallback(dev, token->data, event->worker ? event->worker->properties : NULL, &val);
 
                 return token_match_string(token, val);
         }
@@ -2038,7 +2035,7 @@ static int udev_rule_apply_token_to_event(
                 char buf[UDEV_PATH_SIZE];
                 bool truncated;
 
-                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ true);
                         return false;
@@ -2056,7 +2053,7 @@ static int udev_rule_apply_token_to_event(
                 struct stat statbuf;
                 bool match, truncated;
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "file name", token->value, "TEST", /* is_match = */ true);
                         return false;
@@ -2099,7 +2096,7 @@ static int udev_rule_apply_token_to_event(
                 size_t count;
 
                 event->program_result = mfree(event->program_result);
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "command", token->value, "PROGRAM", /* is_match = */ true);
                         return false;
@@ -2107,7 +2104,7 @@ static int udev_rule_apply_token_to_event(
 
                 log_event_debug(dev, token, "Running PROGRAM '%s'", buf);
 
-                r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result), NULL);
+                r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof(result), NULL);
                 if (r != 0) {
                         if (r < 0)
                                 log_event_warning_errno(dev, token, r, "Failed to execute \"%s\": %m", buf);
@@ -2131,7 +2128,7 @@ static int udev_rule_apply_token_to_event(
                 char buf[UDEV_PATH_SIZE];
                 bool truncated;
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "file name to be imported", token->value, "IMPORT", /* is_match = */ true);
                         return false;
@@ -2182,7 +2179,7 @@ static int udev_rule_apply_token_to_event(
                 char buf[UDEV_LINE_SIZE], result[UDEV_LINE_SIZE];
                 bool truncated;
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "command", token->value, "IMPORT", /* is_match = */ true);
                         return false;
@@ -2190,7 +2187,7 @@ static int udev_rule_apply_token_to_event(
 
                 log_event_debug(dev, token, "Importing properties from results of '%s'", buf);
 
-                r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result, &truncated);
+                r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof result, &truncated);
                 if (r != 0) {
                         if (r < 0)
                                 log_event_warning_errno(dev, token, r, "Failed to execute '%s', ignoring: %m", buf);
@@ -2261,7 +2258,7 @@ static int udev_rule_apply_token_to_event(
                         event->builtin_run |= mask;
                 }
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "builtin command", token->value, "IMPORT", /* is_match = */ true);
                         return false;
@@ -2317,7 +2314,7 @@ static int udev_rule_apply_token_to_event(
                 char buf[UDEV_PATH_SIZE];
                 bool truncated;
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "property name", token->value, "IMPORT", /* is_match = */ true);
                         return false;
@@ -2378,7 +2375,7 @@ static int udev_rule_apply_token_to_event(
                 if (token->op == OP_ASSIGN_FINAL)
                         event->owner_final = true;
 
-                (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "user name", token->value, "OWNER", /* is_match = */ false);
                         break;
@@ -2401,7 +2398,7 @@ static int udev_rule_apply_token_to_event(
                 if (token->op == OP_ASSIGN_FINAL)
                         event->group_final = true;
 
-                (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "group name", token->value, "GROUP", /* is_match = */ false);
                         break;
@@ -2423,7 +2420,7 @@ static int udev_rule_apply_token_to_event(
                 if (token->op == OP_ASSIGN_FINAL)
                         event->mode_final = true;
 
-                (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "mode", token->value, "MODE", /* is_match = */ false);
                         break;
@@ -2475,7 +2472,7 @@ static int udev_rule_apply_token_to_event(
                 if (!name)
                         return log_oom();
 
-                (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "security label", token->value, "SECLABEL", /* is_match = */ false);
                         break;
@@ -2519,7 +2516,7 @@ static int udev_rule_apply_token_to_event(
                 }
 
                 if (token->op == OP_ADD &&
-                    device_get_property_value_with_fallback(dev, name, properties_list, &val) >= 0) {
+                    device_get_property_value_with_fallback(dev, name, event->worker ? event->worker->properties : NULL, &val) >= 0) {
                         l = strpcpyl_full(&p, l, &truncated, val, " ", NULL);
                         if (truncated) {
                                 log_event_warning(dev, token,
@@ -2529,7 +2526,7 @@ static int udev_rule_apply_token_to_event(
                         }
                 }
 
-                (void) udev_event_apply_format(event, token->value, p, l, false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, p, l, false, &truncated);
                 if (truncated) {
                         _cleanup_free_ char *key_with_name = strjoin("ENV{", name, "}");
                         log_event_truncated(dev, token, "property value", token->value,
@@ -2554,7 +2551,7 @@ static int udev_rule_apply_token_to_event(
                 char buf[UDEV_PATH_SIZE];
                 bool truncated;
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "tag name", token->value, "TAG", /* is_match = */ false);
                         break;
@@ -2591,7 +2588,7 @@ static int udev_rule_apply_token_to_event(
                         break;
                 }
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "network interface name", token->value, "NAME", /* is_match = */ false);
                         break;
@@ -2629,7 +2626,7 @@ static int udev_rule_apply_token_to_event(
                         device_cleanup_devlinks(dev);
 
                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf),
-                                               /* replace_whitespace = */ event->esc != ESCAPE_NONE, properties_list, &truncated);
+                                               /* replace_whitespace = */ event->esc != ESCAPE_NONE, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "symbolic link path", token->value, "SYMLINK", /* is_match = */ false);
                         break;
@@ -2701,7 +2698,7 @@ static int udev_rule_apply_token_to_event(
                         log_event_error_errno(dev, token, r, "Could not find file matches '%s', ignoring: %m", buf);
                         break;
                 }
-                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "attribute value", token->value, "ATTR", /* is_match = */ false);
                         break;
@@ -2721,13 +2718,13 @@ static int udev_rule_apply_token_to_event(
                 char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
                 bool truncated;
 
-                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ false);
                         break;
                 }
 
-                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
                 if (truncated) {
                         _cleanup_free_ char *key_with_name = strjoin("SYSCTL{", buf, "}");
                         log_event_truncated(dev, token, "sysctl value", token->value,
@@ -2756,7 +2753,7 @@ static int udev_rule_apply_token_to_event(
                 if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
                         ordered_hashmap_clear_free_key(event->run_list);
 
-                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+                (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
                 if (truncated) {
                         log_event_truncated(dev, token, "command", token->value,
                                             token->type == TK_A_RUN_BUILTIN ? "RUN{builtin}" : "RUN{program}",
@@ -2793,11 +2790,7 @@ static bool token_is_for_parents(UdevRuleToken *token) {
         return token->type >= TK_M_PARENTS_KERNEL && token->type <= TK_M_PARENTS_TAG;
 }
 
-static int udev_rule_apply_parent_token_to_event(
-                UdevRuleToken *head_token,
-                UdevEvent *event,
-                int timeout_signal) {
-
+static int udev_rule_apply_parent_token_to_event(UdevRuleToken *head_token, UdevEvent *event) {
         int r;
 
         assert(head_token);
@@ -2810,7 +2803,7 @@ static int udev_rule_apply_parent_token_to_event(
                         if (!token_is_for_parents(token))
                                 return true; /* All parent tokens match. */
 
-                        r = udev_rule_apply_token_to_event(token, event->dev_parent, event, 0, timeout_signal, NULL);
+                        r = udev_rule_apply_token_to_event(token, event->dev_parent, event);
                         if (r < 0)
                                 return r;
                         if (r == 0)
@@ -2830,9 +2823,6 @@ static int udev_rule_apply_parent_token_to_event(
 static int udev_rule_apply_line_to_event(
                 UdevRuleLine *line,
                 UdevEvent *event,
-                usec_t timeout_usec,
-                int timeout_signal,
-                Hashmap *properties_list,
                 UdevRuleLine **next_line) {
 
         UdevRuleLineType mask = LINE_HAS_GOTO | LINE_UPDATE_SOMETHING;
@@ -2868,7 +2858,7 @@ static int udev_rule_apply_line_to_event(
                         if (parents_done)
                                 continue;
 
-                        r = udev_rule_apply_parent_token_to_event(token, event, timeout_signal);
+                        r = udev_rule_apply_parent_token_to_event(token, event);
                         if (r <= 0)
                                 return r;
 
@@ -2876,7 +2866,7 @@ static int udev_rule_apply_line_to_event(
                         continue;
                 }
 
-                r = udev_rule_apply_token_to_event(token, event->dev, event, timeout_usec, timeout_signal, properties_list);
+                r = udev_rule_apply_token_to_event(token, event->dev, event);
                 if (r <= 0)
                         return r;
         }
@@ -2887,13 +2877,7 @@ static int udev_rule_apply_line_to_event(
         return 0;
 }
 
-int udev_rules_apply_to_event(
-                UdevRules *rules,
-                UdevEvent *event,
-                usec_t timeout_usec,
-                int timeout_signal,
-                Hashmap *properties_list) {
-
+int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event) {
         int r;
 
         assert(rules);
@@ -2901,7 +2885,7 @@ int udev_rules_apply_to_event(
 
         LIST_FOREACH(rule_files, file, rules->rule_files)
                 LIST_FOREACH_WITH_NEXT(rule_lines, line, next_line, file->rule_lines) {
-                        r = udev_rule_apply_line_to_event(line, event, timeout_usec, timeout_signal, properties_list, &next_line);
+                        r = udev_rule_apply_line_to_event(line, event, &next_line);
                         if (r < 0)
                                 return r;
                 }
index 4352312a9a5797c8e5015700266b25fd6e62497a..9d19c8099c5bb4aa20fdf8f3e0eddee298ab3947 100644 (file)
@@ -39,10 +39,7 @@ 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_should_reload(UdevRules *rules);
-int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event,
-                              usec_t timeout_usec,
-                              int timeout_signal,
-                              Hashmap *properties_list);
+int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event);
 int udev_rules_apply_static_dev_perms(UdevRules *rules);
 
 ResolveNameTiming resolve_name_timing_from_string(const char *s) _pure_;
index 9d6993a242955a9c9d318e3335f4ec94bb2d655f..8f7c9fec42c9eed1c0388e20c6510d47d6b3d0fc 100644 (file)
@@ -23,6 +23,7 @@ typedef struct Spawn {
         usec_t timeout_usec;
         int timeout_signal;
         usec_t event_birth_usec;
+        usec_t cmd_birth_usec;
         bool accept_failure;
         int fd_stdout;
         int fd_stderr;
@@ -163,31 +164,20 @@ static int spawn_wait(Spawn *spawn) {
         if (r < 0)
                 return log_device_debug_errno(spawn->device, r, "Failed to allocate sd-event object: %m");
 
-        if (spawn->timeout_usec > 0) {
-                usec_t usec, age_usec;
-
-                usec = now(CLOCK_MONOTONIC);
-                age_usec = usec - spawn->event_birth_usec;
-                if (age_usec < spawn->timeout_usec) {
-                        if (spawn->timeout_warn_usec > 0 &&
-                            spawn->timeout_warn_usec < spawn->timeout_usec &&
-                            spawn->timeout_warn_usec > age_usec) {
-                                spawn->timeout_warn_usec -= age_usec;
-
-                                r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
-                                                      usec + spawn->timeout_warn_usec, USEC_PER_SEC,
-                                                      on_spawn_timeout_warning, spawn);
-                                if (r < 0)
-                                        return log_device_debug_errno(spawn->device, r, "Failed to create timeout warning event source: %m");
-                        }
-
-                        spawn->timeout_usec -= age_usec;
-
+        if (spawn->timeout_usec != USEC_INFINITY) {
+                if (spawn->timeout_warn_usec < spawn->timeout_usec) {
                         r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
-                                              usec + spawn->timeout_usec, USEC_PER_SEC, on_spawn_timeout, spawn);
+                                              usec_add(spawn->cmd_birth_usec, spawn->timeout_warn_usec), USEC_PER_SEC,
+                                              on_spawn_timeout_warning, spawn);
                         if (r < 0)
-                                return log_device_debug_errno(spawn->device, r, "Failed to create timeout event source: %m");
+                                return log_device_debug_errno(spawn->device, r, "Failed to create timeout warning event source: %m");
                 }
+
+                r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
+                                      usec_add(spawn->cmd_birth_usec, spawn->timeout_usec), USEC_PER_SEC,
+                                      on_spawn_timeout, spawn);
+                if (r < 0)
+                        return log_device_debug_errno(spawn->device, r, "Failed to create timeout event source: %m");
         }
 
         if (spawn->fd_stdout >= 0) {
@@ -221,8 +211,6 @@ static int spawn_wait(Spawn *spawn) {
 
 int udev_event_spawn(
                 UdevEvent *event,
-                usec_t timeout_usec,
-                int timeout_signal,
                 bool accept_failure,
                 const char *cmd,
                 char *result,
@@ -240,6 +228,16 @@ int udev_event_spawn(
         assert(event->dev);
         assert(result || result_size == 0);
 
+        int timeout_signal = event->worker ? event->worker->timeout_signal : SIGKILL;
+        usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
+        usec_t now_usec = now(CLOCK_MONOTONIC);
+        usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
+        usec_t cmd_timeout_usec = usec_sub_unsigned(timeout_usec, age_usec);
+        if (cmd_timeout_usec <= 0)
+                return log_device_warning_errno(event->dev, SYNTHETIC_ERRNO(ETIME),
+                                                "The event already takes longer (%s) than the timeout (%s), skipping execution of '%s'.",
+                                                FORMAT_TIMESPAN(age_usec, 1), FORMAT_TIMESPAN(timeout_usec, 1), cmd);
+
         /* pipes from child to parent */
         if (result || log_get_max_level() >= LOG_INFO)
                 if (pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) != 0)
@@ -299,10 +297,11 @@ int udev_event_spawn(
                 .cmd = cmd,
                 .pid = pid,
                 .accept_failure = accept_failure,
-                .timeout_warn_usec = udev_warn_timeout(timeout_usec),
-                .timeout_usec = timeout_usec,
+                .timeout_warn_usec = udev_warn_timeout(cmd_timeout_usec),
+                .timeout_usec = cmd_timeout_usec,
                 .timeout_signal = timeout_signal,
                 .event_birth_usec = event->birth_usec,
+                .cmd_birth_usec = now_usec,
                 .fd_stdout = outpipe[READ_END],
                 .fd_stderr = errpipe[READ_END],
                 .result = result,
@@ -322,11 +321,13 @@ int udev_event_spawn(
         return r; /* 0 for success, and positive if the program failed */
 }
 
-void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal) {
+void udev_event_execute_run(UdevEvent *event) {
         const char *command;
         void *val;
         int r;
 
+        assert(event);
+
         ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list) {
                 UdevBuiltinCommand builtin_cmd = PTR_TO_UDEV_BUILTIN_CMD(val);
 
@@ -336,15 +337,26 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_s
                         if (r < 0)
                                 log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command);
                 } else {
-                        if (event->exec_delay_usec > 0) {
+                        if (event->worker && event->worker->exec_delay_usec > 0) {
+                                usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
+                                usec_t now_usec = now(CLOCK_MONOTONIC);
+                                usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
+
+                                if (event->worker->exec_delay_usec >= usec_sub_unsigned(timeout_usec, age_usec)) {
+                                        log_device_warning(event->dev,
+                                                           "Cannot delaying execution of \"%s\" for %s, skipping.",
+                                                           command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC));
+                                        continue;
+                                }
+
                                 log_device_debug(event->dev, "Delaying execution of \"%s\" for %s.",
-                                                 command, FORMAT_TIMESPAN(event->exec_delay_usec, USEC_PER_SEC));
-                                (void) usleep_safe(event->exec_delay_usec);
+                                                 command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC));
+                                (void) usleep_safe(event->worker->exec_delay_usec);
                         }
 
                         log_device_debug(event->dev, "Running command \"%s\"", command);
 
-                        r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0, NULL);
+                        r = udev_event_spawn(event, /* accept_failure = */ false, command, NULL, 0, NULL);
                         if (r < 0)
                                 log_device_warning_errno(event->dev, r, "Failed to execute '%s', ignoring: %m", command);
                         else if (r > 0) /* returned value is positive when program fails */
index ba6f1ae872ed081aba685fb4d8705f4ba924bcd0..6b22b68b24f4f4d3b63e6138cd193b3405b481f0 100644 (file)
@@ -14,14 +14,12 @@ typedef struct UdevEvent UdevEvent;
 
 int udev_event_spawn(
                 UdevEvent *event,
-                usec_t timeout_usec,
-                int timeout_signal,
                 bool accept_failure,
                 const char *cmd,
                 char *result,
                 size_t ressize,
                 bool *ret_truncated);
-void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal);
+void udev_event_execute_run(UdevEvent *event);
 
 static inline usec_t udev_warn_timeout(usec_t timeout_usec) {
         if (timeout_usec == USEC_INFINITY)
index 58c82794f0f8384ab1c984fd5e6803f4d9819965..258eb26a65feae5713b4d87459be6c08ddc9614c 100644 (file)
@@ -232,12 +232,9 @@ int udev_watch_end(int inotify_fd, sd_device *dev) {
         _cleanup_close_ int dirfd = -EBADF;
         int wd, r;
 
+        assert(inotify_fd >= 0);
         assert(dev);
 
-        /* This may be called by 'udevadm test'. In that case, inotify_fd is not initialized. */
-        if (inotify_fd < 0)
-                return 0;
-
         if (sd_device_get_devname(dev, NULL) < 0)
                 return 0;
 
index 2dc7bd082001db2d9e5a6b4dd9eb418cc3988964..63a93e53dc6d96890c60dcacc431c08420d266b6 100644 (file)
@@ -138,11 +138,7 @@ static int worker_mark_block_device_read_only(sd_device *dev) {
         if (!device_for_action(dev, SD_DEVICE_ADD))
                 return 0;
 
-        r = sd_device_get_subsystem(dev, &val);
-        if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
-
-        if (!streq(val, "block"))
+        if (!device_in_subsystem(dev, "block"))
                 return 0;
 
         r = sd_device_get_sysname(dev, &val);
@@ -176,7 +172,7 @@ static int worker_process_device(UdevWorker *worker, sd_device *dev) {
 
         log_device_uevent(dev, "Processing device");
 
-        udev_event = udev_event_new(dev, worker->exec_delay_usec, worker->rtnl, worker->log_level);
+        udev_event = udev_event_new(dev, worker);
         if (!udev_event)
                 return -ENOMEM;
 
@@ -194,18 +190,17 @@ static int worker_process_device(UdevWorker *worker, sd_device *dev) {
         if (worker->blockdev_read_only)
                 (void) worker_mark_block_device_read_only(dev);
 
+        /* Disable watch during event processing. */
+        r = udev_watch_end(worker->inotify_fd, dev);
+        if (r < 0)
+                log_device_warning_errno(dev, r, "Failed to remove inotify watch, ignoring: %m");
+
         /* apply rules, create node, symlinks */
-        r = udev_event_execute_rules(
-                          udev_event,
-                          worker->inotify_fd,
-                          worker->timeout_usec,
-                          worker->timeout_signal,
-                          worker->properties,
-                          worker->rules);
+        r = udev_event_execute_rules(udev_event, worker->rules);
         if (r < 0)
                 return r;
 
-        udev_event_execute_run(udev_event, worker->timeout_usec, worker->timeout_signal);
+        udev_event_execute_run(udev_event);
 
         if (!worker->rtnl)
                 /* in case rtnl was initialized */
index 5d1fafbd0390a56bc6bc627d4fd71f79b1f0a5bc..b6632968070e006d3eca81381a80f30b97d1469a 100644 (file)
@@ -97,16 +97,19 @@ int builtin_main(int argc, char *argv[], void *userdata) {
                 goto finish;
         }
 
-        event = udev_event_new(dev, 0, NULL, LOG_DEBUG);
+        event = udev_event_new(dev, NULL);
         if (!event) {
                 r = log_oom();
                 goto finish;
         }
 
         r = udev_builtin_run(event, cmd, arg_command, true);
-        if (r < 0)
+        if (r < 0) {
                 log_debug_errno(r, "Builtin command '%s' fails: %m", arg_command);
+                goto finish;
+        }
 
+        r = 0;
 finish:
         udev_builtin_exit();
         return r;
index 809143ede0bc584ad15ff079307a8fa6444386fb..813638fd90cfad4b8a0a216da0172c3bdcae0397 100644 (file)
@@ -124,12 +124,12 @@ int test_main(int argc, char *argv[], void *userdata) {
         /* don't read info from the db */
         device_seal(dev);
 
-        event = udev_event_new(dev, 0, NULL, LOG_DEBUG);
+        event = udev_event_new(dev, NULL);
 
         assert_se(sigfillset(&mask) >= 0);
         assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
 
-        udev_event_execute_rules(event, -1, 60 * USEC_PER_SEC, SIGKILL, NULL, rules);
+        udev_event_execute_rules(event, rules);
 
         FOREACH_DEVICE_PROPERTY(dev, key, value)
                 printf("%s=%s\n", key, value);
@@ -138,7 +138,7 @@ int test_main(int argc, char *argv[], void *userdata) {
                 char program[UDEV_PATH_SIZE];
                 bool truncated;
 
-                (void) udev_event_apply_format(event, cmd, program, sizeof(program), false, NULL, &truncated);
+                (void) udev_event_apply_format(event, cmd, program, sizeof(program), false, &truncated);
                 if (truncated)
                         log_warning("The command '%s' is truncated while substituting into '%s'.", program, cmd);
                 printf("run: '%s'\n", program);
index 51dc041a29c39133c865e0f4fcf9051ef7ac37e3..687b927f721d1cff95c4d8e4830465e3aba0e4fc 100644 (file)
@@ -137,4 +137,4 @@ static int run(int argc, char *argv[]) {
         return udevadm_main(argc, argv);
 }
 
-DEFINE_MAIN_FUNCTION(run);
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/vpick/meson.build b/src/vpick/meson.build
new file mode 100644 (file)
index 0000000..a8c14cb
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+executables += [
+        executable_template + {
+                'name' : 'systemd-vpick',
+                'public' : true,
+                'sources' : files('vpick-tool.c'),
+        },
+]
diff --git a/src/vpick/vpick-tool.c b/src/vpick/vpick-tool.c
new file mode 100644 (file)
index 0000000..0ae8fe3
--- /dev/null
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+
+#include "architecture.h"
+#include "build.h"
+#include "format-table.h"
+#include "fs-util.h"
+#include "main-func.h"
+#include "path-util.h"
+#include "parse-util.h"
+#include "pretty-print.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "vpick.h"
+
+static char *arg_filter_basename = NULL;
+static char *arg_filter_version = NULL;
+static Architecture arg_filter_architecture = _ARCHITECTURE_INVALID;
+static char *arg_filter_suffix = NULL;
+static uint32_t arg_filter_type_mask = 0;
+static enum {
+        PRINT_PATH,
+        PRINT_FILENAME,
+        PRINT_VERSION,
+        PRINT_TYPE,
+        PRINT_ARCHITECTURE,
+        PRINT_TRIES,
+        PRINT_ALL,
+        _PRINT_INVALID = -EINVAL,
+} arg_print = _PRINT_INVALID;
+static PickFlags arg_flags = PICK_ARCHITECTURE|PICK_TRIES;
+
+STATIC_DESTRUCTOR_REGISTER(arg_filter_basename, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_filter_version, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_filter_suffix, freep);
+
+static int help(void) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man("systemd-vpick", "1", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%1$s [OPTIONS...] PATH...\n"
+               "\n%5$sPick entry from versioned directory.%6$s\n\n"
+               "  -h --help            Show this help\n"
+               "     --version         Show package version\n"
+               "\n%3$sLookup Keys:%4$s\n"
+               "  -B --basename=BASENAME\n"
+               "                       Look for specified basename\n"
+               "  -V VERSION           Look for specified version\n"
+               "  -A ARCH              Look for specified architecture\n"
+               "  -S --suffix=SUFFIX   Look for specified suffix\n"
+               "  -t --type=TYPE       Look for specified inode type\n"
+               "\n%3$sOutput:%4$s\n"
+               "  -p --print=filename  Print selected filename rather than path\n"
+               "  -p --print=version   Print selected version rather than path\n"
+               "  -p --print=type      Print selected inode type rather than path\n"
+               "  -p --print=arch      Print selected architecture rather than path\n"
+               "  -p --print=tries     Print selected tries left/tries done rather than path\n"
+               "  -p --print=all       Print all of the above\n"
+               "     --resolve=yes     Canonicalize the result path\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_RESOLVE,
+        };
+
+        static const struct option options[] = {
+                { "help",         no_argument,       NULL, 'h'          },
+                { "version",      no_argument,       NULL, ARG_VERSION  },
+                { "basename",     required_argument, NULL, 'B'          },
+                { "suffix",       required_argument, NULL, 'S'          },
+                { "type",         required_argument, NULL, 't'          },
+                { "print",        required_argument, NULL, 'p'          },
+                { "resolve",      required_argument, NULL, ARG_RESOLVE  },
+                {}
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "hB:V:A:S:t:p:", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        return help();
+
+                case ARG_VERSION:
+                        return version();
+
+                case 'B':
+                        if (!filename_part_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid basename string: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_filter_basename, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case 'V':
+                        if (!version_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version string: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_filter_version, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case 'A':
+                        if (streq(optarg, "native"))
+                                arg_filter_architecture = native_architecture();
+                        else if (streq(optarg, "secondary")) {
+#ifdef ARCHITECTURE_SECONDARY
+                                arg_filter_architecture = ARCHITECTURE_SECONDARY;
+#else
+                                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Local architecture has no secondary architecture.");
+#endif
+                        } else if (streq(optarg, "uname"))
+                                arg_filter_architecture = uname_architecture();
+                        else if (streq(optarg, "auto"))
+                                arg_filter_architecture = _ARCHITECTURE_INVALID;
+                        else {
+                                arg_filter_architecture = architecture_from_string(optarg);
+                                if (arg_filter_architecture < 0)
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown architecture: %s", optarg);
+                        }
+                        break;
+
+                case 'S':
+                        if (!filename_part_is_valid(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid suffix string: %s", optarg);
+
+                        r = free_and_strdup_warn(&arg_filter_suffix, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case 't':
+                        if (isempty(optarg))
+                                arg_filter_type_mask = 0;
+                        else {
+                                mode_t m;
+
+                                m = inode_type_from_string(optarg);
+                                if (m == MODE_INVALID)
+                                        return log_error_errno(m, "Unknown inode type: %s", optarg);
+
+                                arg_filter_type_mask |= UINT32_C(1) << IFTODT(m);
+                        }
+
+                        break;
+
+                case 'p':
+                        if (streq(optarg, "path"))
+                                arg_print = PRINT_PATH;
+                        else if (streq(optarg, "filename"))
+                                arg_print = PRINT_FILENAME;
+                        else if (streq(optarg, "version"))
+                                arg_print = PRINT_VERSION;
+                        else if (streq(optarg, "type"))
+                                arg_print = PRINT_TYPE;
+                        else if (STR_IN_SET(optarg, "arch", "architecture"))
+                                arg_print = PRINT_ARCHITECTURE;
+                        else if (streq(optarg, "tries"))
+                                arg_print = PRINT_TRIES;
+                        else if (streq(optarg, "all"))
+                                arg_print = PRINT_ALL;
+                        else
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown --print= argument: %s", optarg);
+
+                        break;
+
+                case ARG_RESOLVE:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --resolve= value: %m");
+
+                        SET_FLAG(arg_flags, PICK_RESOLVE, r);
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        if (arg_print < 0)
+                arg_print = PRINT_PATH;
+
+        return 1;
+}
+
+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;
+
+        if (optind >= argc)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path to resolve must be specified.");
+
+        for (int i = optind; i < argc; i++) {
+                _cleanup_free_ char *p = NULL;
+                r = path_make_absolute_cwd(argv[i], &p);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make path '%s' absolute: %m", argv[i]);
+
+                _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+                r = path_pick(/* toplevel_path= */ NULL,
+                              /* toplevel_fd= */ AT_FDCWD,
+                              p,
+                              &(PickFilter) {
+                                      .basename = arg_filter_basename,
+                                      .version = arg_filter_version,
+                                      .architecture = arg_filter_architecture,
+                                      .suffix = arg_filter_suffix,
+                                      .type_mask = arg_filter_type_mask,
+                              },
+                              arg_flags,
+                              &result);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to pick version for '%s': %m", p);
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No matching version for '%s' found.", p);
+
+                switch (arg_print) {
+
+                case PRINT_PATH:
+                        fputs(result.path, stdout);
+                        if (result.st.st_mode != MODE_INVALID && S_ISDIR(result.st.st_mode) && !endswith(result.path, "/"))
+                                fputc('/', stdout);
+                        fputc('\n', stdout);
+                        break;
+
+                case PRINT_FILENAME: {
+                        _cleanup_free_ char *fname = NULL;
+
+                        r = path_extract_filename(result.path, &fname);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to extract filename from path '%s': %m", result.path);
+
+                        puts(fname);
+                        break;
+                }
+
+                case PRINT_VERSION:
+                        if (!result.version)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No version information discovered.");
+
+                        puts(result.version);
+                        break;
+
+                case PRINT_TYPE:
+                        if (result.st.st_mode == MODE_INVALID)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No inode type information discovered.");
+
+                        puts(inode_type_to_string(result.st.st_mode));
+                        break;
+
+                case PRINT_ARCHITECTURE:
+                        if (result.architecture < 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No architecture information discovered.");
+
+                        puts(architecture_to_string(result.architecture));
+                        break;
+
+                case PRINT_TRIES:
+                        if (result.tries_left == UINT_MAX)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No tries left/tries done information discovered.");
+
+                        printf("+%u-%u", result.tries_left, result.tries_done);
+                        break;
+
+                case PRINT_ALL: {
+                        _cleanup_(table_unrefp) Table *t = NULL;
+
+                        t = table_new_vertical();
+                        if (!t)
+                                return log_oom();
+
+                        table_set_ersatz_string(t, TABLE_ERSATZ_NA);
+
+                        r = table_add_many(
+                                        t,
+                                        TABLE_FIELD, "Path",
+                                        TABLE_PATH, result.path,
+                                        TABLE_FIELD, "Version",
+                                        TABLE_STRING, result.version,
+                                        TABLE_FIELD, "Type",
+                                        TABLE_STRING, result.st.st_mode == MODE_INVALID ? NULL : inode_type_to_string(result.st.st_mode),
+                                        TABLE_FIELD, "Architecture",
+                                        TABLE_STRING, result.architecture < 0 ? NULL : architecture_to_string(result.architecture));
+                        if (r < 0)
+                                return table_log_add_error(r);
+
+                        if (result.tries_left != UINT_MAX) {
+                                r = table_add_many(
+                                                t,
+                                                TABLE_FIELD, "Tries left",
+                                                TABLE_UINT, result.tries_left,
+                                                TABLE_FIELD, "Tries done",
+                                                TABLE_UINT, result.tries_done);
+                                if (r < 0)
+                                        return table_log_add_error(r);
+                        }
+
+                        r = table_print(t, stdout);
+                        if (r < 0)
+                                return table_log_print_error(r);
+
+                        break;
+                }
+
+                default:
+                        assert_not_reached();
+                }
+        }
+
+        return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
index 7940600612d3694b0341f70dbc9213eed04b961a..e3eb62f198cb7ee7d1655d57fd43c9190f41a32b 100755 (executable)
@@ -8,6 +8,9 @@ NSPAWN_ARGUMENTS="--private-network"
 # shellcheck source=test/test-functions
 . "${TEST_BASE_DIR:?}/test-functions"
 
+# (Hopefully) a temporary workaround for https://github.com/systemd/systemd/issues/30573
+KERNEL_APPEND="${KERNEL_APPEND:-} SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST=100"
+
 test_append_files() {
     local workspace="${1:?}"
 
diff --git a/test/test-exec-deserialization.py b/test/test-exec-deserialization.py
deleted file mode 100755 (executable)
index f8f3a6d..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-#  Copyright © 2017 Michal Sekletar <msekleta@redhat.com>
-
-# ATTENTION: This uses the *installed* systemd, not the one from the built
-# source tree.
-
-import os
-import subprocess
-import sys
-import time
-import unittest
-import uuid
-from enum import Enum
-
-class InstallChange(Enum):
-    NO_CHANGE = 0
-    LINES_SWAPPED = 1
-    COMMAND_ADDED_BEFORE = 2
-    COMMAND_ADDED_AFTER = 3
-    COMMAND_INTERLEAVED = 4
-    REMOVAL = 5
-
-class ExecutionResumeTest(unittest.TestCase):
-    def setUp(self):
-        self.unit = 'test-issue-518.service'
-        self.unitfile_path = f'/run/systemd/system/{self.unit}'
-        self.output_file = f"/tmp/test-issue-518-{uuid.uuid4()}"
-        self.unit_files = {}
-
-        unit_file_content = f'''
-        [Service]
-        Type=oneshot
-        ExecStart=/bin/sleep 3
-        ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
-        '''
-        self.unit_files[InstallChange.NO_CHANGE] = unit_file_content
-
-        unit_file_content = f'''
-        [Service]
-        Type=oneshot
-        ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
-        ExecStart=/bin/sleep 3
-        '''
-        self.unit_files[InstallChange.LINES_SWAPPED] = unit_file_content
-
-        unit_file_content = f'''
-        [Service]
-        Type=oneshot
-        ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
-        ExecStart=/bin/sleep 3
-        ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
-        '''
-        self.unit_files[InstallChange.COMMAND_ADDED_BEFORE] = unit_file_content
-
-        unit_file_content = f'''
-        [Service]
-        Type=oneshot
-        ExecStart=/bin/sleep 3
-        ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
-        ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
-        '''
-        self.unit_files[InstallChange.COMMAND_ADDED_AFTER] = unit_file_content
-
-        unit_file_content = f'''
-        [Service]
-        Type=oneshot
-        ExecStart=/bin/bash -c "echo baz >>{self.output_file}"
-        ExecStart=/bin/sleep 3
-        ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
-        ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
-        '''
-        self.unit_files[InstallChange.COMMAND_INTERLEAVED] = unit_file_content
-
-        unit_file_content = f'''
-        [Service]
-        Type=oneshot
-        ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
-        ExecStart=/bin/bash -c "echo baz >>{self.output_file}"
-        '''
-        self.unit_files[InstallChange.REMOVAL] = unit_file_content
-
-    def reload(self):
-        subprocess.check_call(['systemctl', 'daemon-reload'])
-
-    def write_unit_file(self, unit_file_change):
-        if not isinstance(unit_file_change, InstallChange):
-            raise ValueError('Unknown unit file change')
-
-        content = self.unit_files[unit_file_change]
-
-        with open(self.unitfile_path, 'w', encoding='utf-8') as f:
-            f.write(content)
-
-        self.reload()
-
-    def check_output(self, expected_output):
-        for _ in range(15):
-            # Wait until the unit finishes so we don't check an incomplete log
-            if subprocess.call(['systemctl', '-q', 'is-active', self.unit]) == 0:
-                continue
-
-            os.sync()
-
-            try:
-                with open(self.output_file, 'r', encoding='utf-8') as log:
-                    output = log.read()
-                    self.assertEqual(output, expected_output)
-                    return
-            except IOError:
-                pass
-
-            time.sleep(1)
-
-        self.fail(f'Timed out while waiting for the output file {self.output_file} to appear')
-
-    def setup_unit(self):
-        self.write_unit_file(InstallChange.NO_CHANGE)
-        subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', self.unit])
-        time.sleep(1)
-
-    def test_no_change(self):
-        expected_output = 'foo\n'
-
-        self.setup_unit()
-        self.reload()
-
-        self.check_output(expected_output)
-
-    def test_swapped(self):
-        self.setup_unit()
-        self.write_unit_file(InstallChange.LINES_SWAPPED)
-        self.reload()
-
-        self.assertTrue(not os.path.exists(self.output_file))
-
-    def test_added_before(self):
-        expected_output = 'foo\n'
-
-        self.setup_unit()
-        self.write_unit_file(InstallChange.COMMAND_ADDED_BEFORE)
-        self.reload()
-
-        self.check_output(expected_output)
-
-    def test_added_after(self):
-        expected_output = 'foo\nbar\n'
-
-        self.setup_unit()
-        self.write_unit_file(InstallChange.COMMAND_ADDED_AFTER)
-        self.reload()
-
-        self.check_output(expected_output)
-
-    def test_interleaved(self):
-        expected_output = 'foo\nbar\n'
-
-        self.setup_unit()
-        self.write_unit_file(InstallChange.COMMAND_INTERLEAVED)
-        self.reload()
-
-        self.check_output(expected_output)
-
-    def test_removal(self):
-        self.setup_unit()
-        self.write_unit_file(InstallChange.REMOVAL)
-        self.reload()
-
-        self.assertTrue(not os.path.exists(self.output_file))
-
-    def test_issue_6533(self):
-        unit = "test-issue-6533.service"
-        unitfile_path = f"/run/systemd/system/{unit}"
-
-        content = '''
-        [Service]
-        ExecStart=/bin/sleep 5
-        '''
-
-        with open(unitfile_path, 'w', encoding='utf-8') as f:
-            f.write(content)
-
-        self.reload()
-
-        subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', unit])
-        time.sleep(2)
-
-        content = '''
-        [Service]
-        ExecStart=/bin/sleep 5
-        ExecStart=/bin/true
-        '''
-
-        with open(unitfile_path, 'w', encoding='utf-8') as f:
-            f.write(content)
-
-        self.reload()
-        time.sleep(5)
-
-        self.assertNotEqual(subprocess.call("journalctl -b _PID=1 | grep -q 'Freezing execution'", shell=True), 0)
-
-    def tearDown(self):
-        for f in [self.output_file, self.unitfile_path]:
-            try:
-                os.remove(f)
-            except OSError:
-                # ignore error if log file doesn't exist
-                pass
-
-        self.reload()
-
-if __name__ == '__main__':
-    unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=3))
index 5af7b8cd91debadf07bfb162f9ae360507002069..86a76a8d469a0d70d3db728b810b93fccd172071 100644 (file)
@@ -1151,7 +1151,9 @@ install_multipath() {
     image_install kpartx /lib/udev/kpartx_id lsmod mpathpersist multipath multipathd partx
     image_install "${ROOTLIBDIR:?}"/system/multipathd.{service,socket}
     if get_bool "$LOOKS_LIKE_DEBIAN"; then
-        inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules
+        # Note: try both 60-kpartx.rules (as seen on Debian Sid with 0.9.4-7) and 90-kpartx.rules (as seen on
+        # Ubuntu Jammy with 0.8.8-1ubuntu1.22.04.4)
+        inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-kpartx.rules 60-multipath.rules 68-del-part-nodes.rules 90-kpartx.rules
     else
         inst_rules 11-dm-mpath.rules 11-dm-parts.rules 62-multipath.rules 66-kpartx.rules 68-del-part-nodes.rules
     fi
@@ -1840,7 +1842,8 @@ save_journal() {
     # Show messages from the testsuite-XX.service or messages with priority "warning" and higher
     echo " --- $source_dir ---"
     "$JOURNALCTL" --no-pager --no-hostname -o short-monotonic -D "$source_dir" \
-        _SYSTEMD_UNIT="testsuite-${TESTID:?}.service" + PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0
+        _SYSTEMD_UNIT="testsuite-${TESTID:?}.service" + SYSLOG_IDENTIFIER="testsuite-$TESTID.sh" + \
+        PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0
 
     if get_bool "$save"; then
         # If we don't have systemd-journal-remote copy all journals from /var/log/journal/
diff --git a/test/units/testsuite-05.effective-limit.sh b/test/units/testsuite-05.effective-limit.sh
new file mode 100755 (executable)
index 0000000..3ff8e83
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+pre=test05
+cat >/run/systemd/system/"$pre"alpha.slice <<EOF
+[Slice]
+MemoryMax=40M
+MemoryHigh=40M
+TasksMax=400
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta.slice <<EOF
+[Slice]
+MemoryMax=10M
+MemoryHigh=10M
+TasksMax=100
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta-gamma.slice <<EOF
+[Slice]
+MemoryMax=20M
+MemoryHigh=20M
+TasksMax=200
+EOF
+
+systemctl daemon-reload
+
+srv=probe.service
+slc0="$pre"alpha.slice
+slc="$pre"alpha-beta-gamma.slice
+
+systemd-run --unit "$srv" --slice "$slc"  \
+    -p MemoryMax=5M \
+    -p MemoryHigh=5M \
+    -p TasksMax=50 \
+    sleep inf
+
+# Compare with inequality because test can run in a constrained container
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+systemd-run --unit "$srv" --slice "$slc"  \
+    sleep inf
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "100"
+
+systemctl set-property "$slc0" \
+    MemoryMax=5M \
+    MemoryHigh=5M \
+    TasksMax=50
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+rm -f /run/systemd/system/"$pre"* || :
diff --git a/test/units/testsuite-05.rlimit.sh b/test/units/testsuite-05.rlimit.sh
new file mode 100755 (executable)
index 0000000..bbf3adb
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+P=/run/systemd/system.conf.d
+mkdir $P
+
+cat >$P/rlimits.conf <<EOF
+[Manager]
+DefaultLimitNOFILE=10000:16384
+EOF
+
+systemctl daemon-reload
+
+[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
+[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
+
+[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
+[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
+
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
index ab72d8fe27ad35f43c2ae9e35b9ddfc69dd300dc..cf32accb8c555b460d6d0c6f3fa9984d21006c8d 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 [Unit]
-Description=TEST-05-RLIMITS
+Description=TEST-05-LIMITS
 
 [Service]
 ExecStartPre=rm -f /failed /testok
index 870845d14bf42cd3105f16451826010161dee803..9c2a033aa98d17ce1517bf42cf30c31c187ab0ea 100755 (executable)
@@ -3,25 +3,9 @@
 set -eux
 set -o pipefail
 
-P=/run/systemd/system.conf.d
-mkdir $P
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
 
-cat >$P/rlimits.conf <<EOF
-[Manager]
-DefaultLimitNOFILE=10000:16384
-EOF
-
-systemctl daemon-reload
-
-[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
-[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
-
-[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
-[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
-
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
+run_subtests
 
 touch /testok
diff --git a/test/units/testsuite-07.aux-scope.sh b/test/units/testsuite-07.aux-scope.sh
new file mode 100755 (executable)
index 0000000..4e46e1b
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+export SYSTEMD_PAGER=cat
+
+if ! grep -q pidfd_open /proc/kallsyms; then
+    echo "pidfds not available, skipping the test..."
+    exit 0
+fi
+
+systemd-run --unit test-aux-scope.service \
+            -p Slice=aux.slice -p Type=exec -p TasksMax=99 -p CPUWeight=199 -p IPAccounting=yes \
+            /usr/lib/systemd/tests/unit-tests/manual/test-aux-scope
+kill -s USR1 "$(systemctl show --value --property MainPID test-aux-scope.service)"
+
+sleep 1
+
+systemctl status test-aux-scope.service
+# shellcheck disable=SC2009
+test "$(ps -eo pid,unit | grep -c test-aux-scope.service)" = 1
+
+systemctl status test-aux-scope.scope
+# shellcheck disable=SC2009
+test "$(ps -eo pid,unit | grep -c test-aux-scope.scope)" = 10
+
+test "$(systemctl show -p Slice --value test-aux-scope.scope)" = aux.slice
+test "$(systemctl show -p TasksMax --value test-aux-scope.scope)" = 99
+test "$(systemctl show -p CPUWeight --value test-aux-scope.scope)" = 199
+test "$(systemctl show -p IPAccounting --value test-aux-scope.scope)" = yes
+
+systemctl stop test-aux-scope.scope
+systemctl stop test-aux-scope.service
diff --git a/test/units/testsuite-07.exec-deserialization.sh b/test/units/testsuite-07.exec-deserialization.sh
new file mode 100755 (executable)
index 0000000..a0aa580
--- /dev/null
@@ -0,0 +1,221 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+setup_base_unit() {
+    local unit_path="${1:?}"
+    local log_file="${2:?}"
+    local unit_name="${unit_path##*/}"
+
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    systemctl --job-mode=replace --no-block start "$unit_name"
+    # Wait until the unit leaves the "inactive" state
+    timeout 5s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" == inactive ]]; do sleep .1; done"
+    # Sleep for 1 second from the unit start to get well "into" the first (or second) ExecStart= directive
+    sleep 1
+}
+
+check_output() {
+    local unit_name="${1:?}"
+    local log_file="${2:?}"
+    local expected="${3?}"
+    local unit_name="${unit_path##*/}"
+
+    # Wait until the unit becomes inactive before checking the log
+    timeout 10s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" != inactive ]]; do sleep .5; done"
+
+    diff "$log_file" <(echo -ne "$expected")
+}
+
+testcase_no_change() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-no-change-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Simple sanity test without any reordering shenanignans, to check if the base unit works as expected.
+    check_output "$unit_path" "$log_file" "foo\n"
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_swapped() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-swapped-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Swap the two ExecStart= lines.
+    #
+    # Since we should be in the first "sleep" of the base unit, after replacing the unit with the following
+    # one we should continue running from the respective "ExecStart=sleep 3" line, which is now the last
+    # one, resulting no output in the final log file.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=sleep 3
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" ""
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_added_before() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-added-before-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Add one new ExecStart= before the existing ones.
+    #
+    # Since, after reload, we should continue running from the "sleep 3" statement, the newly added "echo
+    # bar" one will have no efect and we should end up with the same output as in the previous case.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo bar >>$log_file"
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" "foo\n"
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_added_after() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-added-after-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Add an ExecStart= line after the existing ones.
+    #
+    # Same case as above, except the newly added ExecStart= should get executed, as it was added after the
+    # "sleep 3" statement.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=bash -c "echo bar >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" "foo\nbar\n"
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_interleaved() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-interleaved-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Combination of the two previous cases.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo baz >>$log_file"
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=bash -c "echo bar >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" "foo\nbar\n"
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_removal() {
+    local unit_path log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-removal-XXX.service)"
+    log_file="$(mktemp)"
+
+    setup_base_unit "$unit_path" "$log_file"
+
+    # Remove the currently executed ExecStart= line.
+    #
+    # In this case we completely drop the currently excuted "sleep 3" statement, so after reload systemd
+    # should complain that the currently executed command vanished and simply finish executing the unit,
+    # resulting in an empty log.
+    cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo bar >>$log_file"
+ExecStart=bash -c "echo baz >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" ""
+
+    rm -f "$unit_path" "$log_file"
+}
+
+testcase_issue_6533() {
+    local unit_path unit_name log_file
+
+    unit_path="$(mktemp /run/systemd/system/test-deserialization-issue-6533-XXX.service)"
+    unit_name="${unit_path##*/}"
+    log_file="$(mktemp)"
+
+    cat >"$unit_path" <<EOF
+[Service]
+Type=simple
+ExecStart=/bin/sleep 5
+EOF
+    systemctl daemon-reload
+
+    systemctl --job-mode=replace --no-block start "$unit_name"
+    sleep 2
+
+    # Make sure we try to execute the next command only for oneshot services, as for other types we allow
+    # only one ExecStart= directive.
+    #
+    # See: https://github.com/systemd/systemd/issues/6533
+    cat >"$unit_path" <<EOF
+[Service]
+Type=simple
+ExecStart=/bin/sleep 5
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+    systemctl daemon-reload
+
+    check_output "$unit_path" "$log_file" ""
+    (! journalctl -b --grep "Freezing execution" _PID=1)
+}
+
+mkdir -p /run/systemd/system/
+run_testcases
+systemctl daemon-reload
index 9af706fae165664a926b7d0227be320f946a68c6..d19d85074f51e1bae00825fb9d241b5f72df4b1f 100755 (executable)
@@ -1,7 +1,8 @@
 #!/usr/bin/env bash
 # SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
+set -eux
 
+TMPDIR=
 TEST_RULE="/run/udev/rules.d/49-test.rules"
 KILL_PID=
 
@@ -10,8 +11,15 @@ setup() {
     [[ -e /etc/udev/udev.conf ]] && cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bak
 
     cat >"${TEST_RULE}" <<EOF
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", PROGRAM=="/bin/sleep 60"
+ACTION!="add", GOTO="test_end"
+SUBSYSTEM!="mem", GOTO="test_end"
+KERNEL!="null", GOTO="test_end"
+
+OPTIONS="log_level=debug"
+PROGRAM=="/bin/touch /tmp/test-udev-marker"
+PROGRAM!="/bin/sleep 60", ENV{PROGRAM_RESULT}="KILLED"
+
+LABEL="test_end"
 EOF
     cat >/etc/udev/udev.conf <<EOF
 event_timeout=10
@@ -35,19 +43,51 @@ teardown() {
     systemctl restart systemd-udevd.service
 }
 
-run_test() {
-    local since
+run_test_timeout() {
+    TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+    udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
+    KILL_PID="$!"
+
+    SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
+
+    for _ in {1..40}; do
+        if grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt; then
+            sleep .5
+            kill "$KILL_PID"
+            KILL_PID=
+
+            cat "$TMPDIR"/monitor.txt
+            (! grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt)
+            (! grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt)
+            (! grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt)
+            grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt
+            rm -rf "$TMPDIR"
+            return 0
+        fi
+        sleep .5
+    done
+
+    return 1
+}
 
-    since="$(date '+%F %T')"
+run_test_killed() {
+    local killed=
 
     TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
     udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
     KILL_PID="$!"
 
+    rm -f /tmp/test-udev-marker
     SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
 
     for _ in {1..40}; do
-        if coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then
+        if [[ -z "$killed" ]]; then
+            if [[ -e /tmp/test-udev-marker ]]; then
+                killall --signal ABRT --regexp udev-worker
+                killed=1
+            fi
+        elif grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt; then
+            sleep .5
             kill "$KILL_PID"
             KILL_PID=
 
@@ -55,6 +95,8 @@ run_test() {
             grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
             grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt
             grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt
+            (! grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt)
+            rm -rf "$TMPDIR"
             return 0
         fi
         sleep .5
@@ -66,6 +108,7 @@ run_test() {
 trap teardown EXIT
 
 setup
-run_test
+run_test_timeout
+run_test_killed
 
 exit 0
index 20f120a93055133503c73eb8965c8e6815dd8139..f229dcf25f080bbdad323efb47a566d727830025 100755 (executable)
@@ -238,4 +238,17 @@ assert_rc 124 timeout 5 udevadm wait --removed /sys/class/net/$netdev
 udevadm wait --settle /sys/class/net/$netdev
 udevadm wait -h
 
+udevadm lock --help
+udevadm lock --version
+for i in /dev/block/*; do
+    udevadm lock --device "$i" --print
+    udevadm lock --device "$i" true
+    (! udevadm lock --device "$i" false)
+done
+for i in / /usr; do
+    udevadm lock --backing "$i" --print
+    udevadm lock --backing "$i" true
+    (! udevadm lock --backing "$i" false)
+done
+
 exit 0
index 0b0dfc6c8314c8f02f9440e5de0c65d62c450beb..7b2013fb51c484a783ae968cf05dd88f0d0abf47 100755 (executable)
@@ -21,16 +21,32 @@ at_exit() {
     fi
 }
 
+add_suppression() {
+    local interface="${1:?}"
+    local suppression="${2:?}"
+
+    sed -i "\%\[$interface\]%a$suppression" /etc/dfuzzer.conf
+}
+
 trap at_exit EXIT
 
 systemctl log-level info
 
 # FIXME: systemd-run doesn't play well with daemon-reexec
 # See: https://github.com/systemd/systemd/issues/27204
-sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:Reexecute FIXME' /etc/dfuzzer.conf
+add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:Reexecute FIXME"
+
+add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:SoftReboot destructive"
+add_suppression "org.freedesktop.login1" "Sleep destructive"
+
+# Skip calling start and stop methods on unit objects, as doing that is not only time consuming, but it also
+# starts/stops units that interfere with the machine state. The actual code paths should be covered (to some
+# degree) by the respective method counterparts on the manager object.
+for method in Start Stop Restart ReloadOrRestart ReloadOrTryRestart Kill; do
+    add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Unit:$method"
+done
 
-sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:SoftReboot destructive' /etc/dfuzzer.conf
-sed -i '/\[org.freedesktop.login1\]/aSleep destructive' /etc/dfuzzer.conf
+cat /etc/dfuzzer.conf
 
 # TODO
 #   * check for possibly newly introduced buses?
index 28218ab6d7456ede0fa6a3c5854912d56c95e532..b5d01bfc8e8b336e12ce84432144b7f45e4fae80 100755 (executable)
@@ -493,6 +493,20 @@ systemd-sysext unmerge
 rm -rf /run/extensions/app-reject
 rm /var/lib/extensions/app-nodistro.raw
 
+# Some super basic test that RootImage= works with .v/ dirs
+VBASE="vtest$RANDOM"
+VDIR="/tmp/${VBASE}.v"
+mkdir "$VDIR"
+
+ln -s "${image}.raw" "$VDIR/${VBASE}_33.raw"
+ln -s "${image}.raw" "$VDIR/${VBASE}_34.raw"
+ln -s "${image}.raw" "$VDIR/${VBASE}_35.raw"
+
+systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+
+rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
+rmdir "$VDIR"
+
 mkdir -p /run/machines /run/portables /run/extensions
 touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
 
index 81edb0ab7a154bf8c50f2a0b3a8f020407d70fb8..1123a1d33b1f22fc3e531595f7c92d5f92350fdb 100755 (executable)
@@ -148,7 +148,7 @@ check_device_units() {(
         if ! check_device_unit "$log_level" "$path"; then
            return 1
         fi
-    done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ { print $1 }' | sed -e 's/\.device$//')
+    done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ && $4 == "plugged" { print $1 }' | sed -e 's/\.device$//')
 
     return 0
 )}
@@ -396,6 +396,11 @@ EOF
 
     udevadm control --reload
 
+    # initialize partition table
+    for disk in {0..9}; do
+        echo 'label: gpt' | udevadm lock --device="${devices[$disk]}" sfdisk -q "${devices[$disk]}"
+    done
+
     # Delete the partitions, immediately recreate them, wait for udev to settle
     # down, and then check if we have any dangling symlinks in /dev/disk/. Rinse
     # and repeat.
diff --git a/test/units/testsuite-74.vpick.sh b/test/units/testsuite-74.vpick.sh
new file mode 100755 (executable)
index 0000000..400097f
--- /dev/null
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+at_exit() {
+    set +e
+    rm -rf /var/lib/machines/mymachine.raw.v
+    rm -rf /var/lib/machines/mytree.v
+    rm -rf /var/lib/machines/testroot.v
+    umount -l /tmp/dotvroot
+    rmdir /tmp/dotvroot
+}
+
+trap at_exit EXIT
+
+mkdir -p /var/lib/machines/mymachine.raw.v
+
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw
+
+mkdir -p /var/lib/machines/mytree.v
+
+mkdir /var/lib/machines/mytree.v/mytree_33.4
+mkdir /var/lib/machines/mytree.v/mytree_33.5
+mkdir /var/lib/machines/mytree.v/mytree_36.0+0-5
+mkdir /var/lib/machines/mytree.v/mytree_37.0_arm64+2-3
+mkdir /var/lib/machines/mytree.v/mytree_38.0_arm64+0-5
+
+ARCH="$(busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Architecture | cut -d\" -f 2)"
+
+export SYSTEMD_LOG_LEVEL=debug
+
+if [ "$ARCH" = "x86-64" ] ; then
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw"
+
+    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+elif [ "$ARCH" = "arm64" ] ; then
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+else
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+
+    test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+    (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+    systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+fi
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A x86-64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A ia64)
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p version)" = "7.6.0"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p type)" = "reg"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p filename)" = "mymachine_7.6.0_arm64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p arch)" = "arm64"
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t reg)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t dir)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t fifo)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t sock)
+
+if [ "$ARCH" != "arm64" ] ; then
+    test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+    test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+else
+    test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+    test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+fi
+
+(! systemd-vpick /var/lib/machines/mytree.v --type=reg)
+
+mkdir /tmp/dotvroot
+mount --bind / /tmp/dotvroot
+
+mkdir /var/lib/machines/testroot.v
+mkdir /var/lib/machines/testroot.v/testroot_32
+ln -s /tmp/dotvroot /var/lib/machines/testroot.v/testroot_33
+mkdir /var/lib/machines/testroot.v/testroot_34
+
+ls -l /var/lib/machines/testroot.v
+
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+find /var/lib/machines/testroot.v/testroot_34
+rm -rf /var/lib/machines/testroot.v/testroot_34
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_33/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /tmp/dotvroot/
+systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true
+
+rm /var/lib/machines/testroot.v/testroot_33
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+rm -rf /var/lib/machines/testroot.v/testroot_32
+(! systemd-vpick /var/lib/machines/testroot.v)
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
index a18c8980cf0ee7185077523c4e42758aaa8264e2..66f9c73ba6a6b9e174d6ad72de8023cb4fc6d83e 100755 (executable)
@@ -28,6 +28,15 @@ assert_eq() {(
     fi
 )}
 
+assert_le() {(
+    set +ex
+
+    if [[ "${1:?}" -gt "${2:?}" ]]; then
+        echo "FAIL: '$1' > '$2'" >&2
+        exit 1
+    fi
+)}
+
 assert_in() {(
     set +ex
 
index 9d3604951d56bf6a1b6d1e450520bdb25759ae6c..40cc3d10d35ff9f6621b77be051de44b1a008445 100644 (file)
@@ -700,6 +700,7 @@ units = [
           'file' : 'tmp.mount',
           'symlinks' : ['local-fs.target.wants/'],
         },
+        { 'file' : 'tpm2.target' },
         { 'file' : 'umount.target' },
         { 'file' : 'usb-gadget.target' },
         { 'file' : 'user-runtime-dir@.service.in' },
index 6d7b8ff84c6c766d1af654fcf0de1852ab1137c8..7d156c14483923331b070db98418a21df92da910 100644 (file)
@@ -11,6 +11,7 @@
 Description=TPM2 PCR Extension (Varlink)
 Documentation=man:systemd-pcrextend(8)
 DefaultDependencies=no
+After=tpm2.target
 Before=sockets.target
 ConditionSecurity=measured-uki
 
index 2305b1cd4c1d81df018d8b68d190640152200f03..6020a21c40c7d04882068e13bd9343b825e2fcbc 100644 (file)
@@ -11,6 +11,7 @@
 Description=TPM2 PCR Extension (Varlink)
 Documentation=man:systemd-pcrphase.service(8)
 DefaultDependencies=no
+After=tpm2.target
 Conflicts=shutdown.target initrd-switch-root.target
 Before=shutdown.target initrd-switch-root.target
 
index 11dc7471946b866c2f88c3880fc05714a61427c2..582e1d83e505e609d74a30401ebe5383fdc0c2cc 100644 (file)
@@ -12,7 +12,7 @@ Description=TPM2 PCR Root File System Measurement
 Documentation=man:systemd-pcrfs-root.service(8)
 DefaultDependencies=no
 Conflicts=shutdown.target
-After=systemd-pcrmachine.service
+After=tpm2.target systemd-pcrmachine.service
 Before=shutdown.target
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index fbaec4b999ea71c04bcd7e546bd4ca354c05663b..262a82fb04ce41134a9f5702f1fc63b5d90578db 100644 (file)
@@ -13,7 +13,7 @@ Documentation=man:systemd-pcrfs@.service(8)
 DefaultDependencies=no
 BindsTo=%i.mount
 Conflicts=shutdown.target
-After=%i.mount systemd-pcrfs-root.service
+After=%i.mount tpm2.target systemd-pcrfs-root.service
 Before=shutdown.target
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index fb7d3ce601fd5c7239d02544347e9843a9164ffb..cbd1005104f2f26bcfe24602c2769f30baf884a2 100644 (file)
@@ -12,6 +12,7 @@ Description=TPM2 PCR Machine ID Measurement
 Documentation=man:systemd-pcrmachine.service(8)
 DefaultDependencies=no
 Conflicts=shutdown.target
+After=tpm2.target
 Before=sysinit.target shutdown.target
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index b337d602bad01d592ea1b39da561fc8704b6efe4..3b18b4f29d60c39f81176dd780e9b88683cb5356 100644 (file)
@@ -12,6 +12,7 @@ Description=TPM2 PCR Barrier (initrd)
 Documentation=man:systemd-pcrphase-initrd.service(8)
 DefaultDependencies=no
 Conflicts=shutdown.target initrd-switch-root.target
+After=tpm2.target
 Before=sysinit.target cryptsetup-pre.target cryptsetup.target shutdown.target initrd-switch-root.target systemd-sysext.service
 ConditionPathExists=/etc/initrd-release
 ConditionSecurity=measured-uki
index 08f73973bee81b5351aa82f1693aa02f415d8661..d938e02401bb70ae457087547eb7ec9afc46e731 100644 (file)
@@ -12,7 +12,7 @@ Description=TPM2 PCR Barrier (Initialization)
 Documentation=man:systemd-pcrphase-sysinit.service(8)
 DefaultDependencies=no
 Conflicts=shutdown.target
-After=sysinit.target
+After=sysinit.target tpm2.target
 Before=basic.target shutdown.target
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index c94ad756d40008d284de239091d239ebfcea99b2..26b9e5c5389114cb26f1cadb20a448d90d1459c0 100644 (file)
@@ -10,7 +10,7 @@
 [Unit]
 Description=TPM2 PCR Barrier (User)
 Documentation=man:systemd-pcrphase.service(8)
-After=remote-fs.target remote-cryptsetup.target
+After=remote-fs.target remote-cryptsetup.target tpm2.target
 Before=systemd-user-sessions.service
 ConditionPathExists=!/etc/initrd-release
 ConditionSecurity=measured-uki
index 736f36848ca9412f432696dab6160ca5795671bf..820aecfef6cc9072b6b1fe53aff9dbc887f644b6 100644 (file)
@@ -15,7 +15,7 @@ Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-network-
 Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients
 
 DefaultDependencies=no
-After=systemd-sysusers.service
+After=systemd-sysctl.service systemd-sysusers.service
 Before=sysinit.target network.target nss-lookup.target shutdown.target initrd-switch-root.target
 Conflicts=shutdown.target initrd-switch-root.target
 Wants=nss-lookup.target
index c1597ea3f9fd6f730b9fbc5cf97546d805b7337c..628f8166782b8d0845979928a236c12d8b851a54 100644 (file)
@@ -15,6 +15,7 @@ Conflicts=shutdown.target
 Before=sysinit.target shutdown.target
 ConditionSecurity=measured-uki
 ConditionPathExists=!/run/systemd/tpm2-srk-public-key.pem
+After=tpm2.target
 
 [Service]
 Type=oneshot
index 6c99f3af0a6b258a3df6562cc57c22825df64b6d..4830afa24356c8e5300f1e9ec074ac87ed38d1a6 100644 (file)
@@ -17,6 +17,7 @@ Before=sysinit.target shutdown.target
 RequiresMountsFor=/var/lib/systemd/tpm2-srk-public-key.pem
 ConditionSecurity=measured-uki
 ConditionPathExists=!/etc/initrd-release
+After=tpm2.target
 
 [Service]
 Type=oneshot
diff --git a/units/tpm2.target b/units/tpm2.target
new file mode 100644 (file)
index 0000000..ba51d57
--- /dev/null
@@ -0,0 +1,16 @@
+#  SPDX-License-Identifier: LGPL-2.1-or-later
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Trusted Platform Module
+Documentation=man:systemd.special(7)
+
+# Make this a synchronization point on the first TPM device found
+After=dev-tpmrm0.device
+Wants=dev-tpmrm0.device