]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #21838 from lnussel/logind-refactor
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 31 Jan 2022 10:45:33 +0000 (19:45 +0900)
committerGitHub <noreply@github.com>
Mon, 31 Jan 2022 10:45:33 +0000 (19:45 +0900)
Logind shutdown refactor

254 files changed:
.clusterfuzzlite/Dockerfile [new file with mode: 0644]
.github/ISSUE_TEMPLATE/Bug_report.md
.github/dependabot.yml
.github/workflows/cflite_pr.yml [new file with mode: 0644]
.github/workflows/codeql-analysis.yml
.github/workflows/mkosi.yml
.github/workflows/unit_tests.sh
.packit.yml
NEWS
catalog/meson.build
docs/CGROUP_DELEGATION.md
docs/CODING_STYLE.md
docs/CONTAINER_INTERFACE.md
docs/ENVIRONMENT.md
docs/GVARIANT-SERIALIZATION.md
docs/INITRD_INTERFACE.md
docs/JOURNAL_FILE_FORMAT.md
docs/JOURNAL_NATIVE_PROTOCOL.md
docs/PASSWORD_AGENTS.md
docs/PORTABLE_SERVICES.md
docs/RESOLVED-VPNS.md
docs/UIDS-GIDS.md
docs/USER_GROUP_API.md
docs/USER_RECORD.md
hwdb.d/20-dmi-id.hwdb
hwdb.d/60-evdev.hwdb
hwdb.d/60-input-id.hwdb
hwdb.d/60-sensor.hwdb
hwdb.d/70-mouse.hwdb
hwdb.d/70-pda.hwdb [new file with mode: 0644]
hwdb.d/meson.build
hwdb.d/parse_hwdb.py
man/common-variables.xml
man/machine-info.xml
man/meson.build
man/org.freedesktop.hostname1.xml
man/org.freedesktop.portable1.xml
man/org.freedesktop.systemd1.xml
man/portablectl.xml
man/rules/meson.build
man/systemd-networkd-wait-online.service.xml
man/systemd-stdio-bridge.xml
man/systemd.exec.xml
man/systemd.link.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.unit.xml
meson.build
mkosi.default.d/debian/10-mkosi.debian
rules.d/50-udev-default.rules.in
src/basic/filesystems-gperf.gperf
src/basic/log.h
src/basic/missing_magic.h
src/basic/path-lookup.c
src/boot/bootctl.c
src/boot/efi/boot.c
src/boot/efi/console.c
src/boot/efi/console.h
src/boot/efi/meson.build
src/boot/efi/util.c
src/boot/efi/util.h
src/boot/efi/xbootldr.c
src/core/bpf-devices.c
src/core/bpf-firewall.c
src/core/bpf/meson.build
src/core/dbus-execute.c
src/core/dbus-unit.c
src/core/dbus.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.in
src/core/main.c
src/core/namespace.c
src/core/namespace.h
src/core/service.c
src/core/unit-printf.c
src/fstab-generator/fstab-generator.c
src/home/homed-home.c
src/home/homed-manager.c
src/hostname/hostnamed.c
src/hostname/org.freedesktop.hostname1.policy
src/journal-remote/fuzz-journal-remote.c
src/journal/journald-audit.c
src/journal/journald-file.c
src/journal/test-journal.c
src/kernel-install/50-depmod.install
src/kernel-install/90-loaderentry.install
src/kernel-install/kernel-install
src/libsystemd-network/dhcp-internal.h
src/libsystemd-network/dhcp-packet.c
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/fuzz-dhcp-client.c [new file with mode: 0644]
src/libsystemd-network/fuzz-dhcp-server-relay-message.c [new file with mode: 0644]
src/libsystemd-network/fuzz-dhcp-server.c
src/libsystemd-network/meson.build
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/test-dhcp-option.c
src/libsystemd-network/test-dhcp-server.c
src/libsystemd/sd-bus/bus-introspect.c
src/libsystemd/sd-bus/bus-introspect.h
src/libsystemd/sd-bus/bus-objects.c
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-journal/journal-authenticate.c
src/libsystemd/sd-journal/journal-file.c
src/libsystemd/sd-journal/journal-file.h
src/libsystemd/sd-journal/journal-verify.c
src/libsystemd/sd-journal/sd-journal.c
src/libsystemd/sd-network/network-util.c
src/libsystemd/sd-network/network-util.h
src/login/logind-dbus.c
src/login/logind.conf.in
src/login/user-runtime-dir.c
src/machine/image-dbus.c
src/machine/machine-dbus.c
src/machine/machined-dbus.c
src/network/netdev/l2tp-tunnel.c
src/network/netdev/l2tp-tunnel.h
src/network/netdev/macsec.c
src/network/netdev/macsec.h
src/network/netdev/wireguard.c
src/network/netdev/wireguard.h
src/network/networkd-address-label.c
src/network/networkd-address-label.h
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-bridge-fdb.c
src/network/networkd-bridge-fdb.h
src/network/networkd-bridge-mdb.c
src/network/networkd-bridge-mdb.h
src/network/networkd-dhcp-server-static-lease.c
src/network/networkd-dhcp-server-static-lease.h
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-neighbor.c
src/network/networkd-neighbor.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-nexthop.c
src/network/networkd-nexthop.h
src/network/networkd-radv.c
src/network/networkd-radv.h
src/network/networkd-route.c
src/network/networkd-route.h
src/network/networkd-routing-policy-rule.c
src/network/networkd-routing-policy-rule.h
src/network/networkd-sriov.c
src/network/networkd-sriov.h
src/network/networkd-util.c
src/network/networkd-util.h
src/network/tc/qdisc.c
src/network/tc/qdisc.h
src/network/tc/tclass.c
src/network/tc/tclass.h
src/network/wait-online/link.c
src/network/wait-online/manager.c
src/network/wait-online/manager.h
src/oom/oomd-manager.c
src/oom/oomd-util.c
src/portable/portable.c
src/portable/portable.h
src/portable/portablectl.c
src/portable/portabled-bus.c
src/portable/portabled-image-bus.c
src/resolve/fuzz-etc-hosts.c [new file with mode: 0644]
src/resolve/meson.build
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-stream.c
src/resolve/resolved-dns-synthesize.c
src/resolve/resolved-dnstls-gnutls.c
src/resolve/resolved-dnstls-openssl.c
src/resolve/resolved-dnstls.h
src/resolve/resolved-etc-hosts.c
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/test-resolved-stream.c [new file with mode: 0644]
src/run/run.c
src/shared/bootspec.c
src/shared/bootspec.h
src/shared/bpf-program.c
src/shared/bpf-program.h
src/shared/bus-object.c
src/shared/bus-unit-util.c
src/shared/cgroup-show.c
src/shared/conf-parser.c
src/shared/conf-parser.h
src/shared/copy.c
src/shared/copy.h
src/shared/dissect-image.c
src/shared/dns-domain.h
src/shared/install-printf.c
src/shared/main-func.h
src/shared/meson.build
src/shared/netif-sriov.c [new file with mode: 0644]
src/shared/netif-sriov.h [new file with mode: 0644]
src/shared/netif-util.c
src/shared/netif-util.h
src/shared/pager.c
src/shared/selinux-util.c
src/shared/smack-util.c
src/shared/specifier.c
src/shared/specifier.h
src/shared/udev-util.c
src/stdio-bridge/stdio-bridge.c
src/sysext/sysext.c
src/test/test-bpf-firewall.c
src/test/test-bpf-foreign-programs.c
src/test/test-copy.c
src/test/test-env-file.c
src/test/test-load-fragment.c
src/test/test-namespace.c
src/test/test-ns.c
src/test/test-specifier.c
src/udev/meson.build
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
src/udev/udevadm-info.c
test/TEST-64-UDEV-STORAGE/test.sh
test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50a [new file with mode: 0644]
test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 [new file with mode: 0644]
test/fuzz/fuzz-dhcp-server-relay-message/7d924e16295cd14e12a01a5631ea94a3d11d1b52 [new file with mode: 0644]
test/fuzz/fuzz-dhcp-server-relay-message/clusterfuzz-testcase-minimized-fuzz-dhcp-server-relay-message-4972399731277824 [new file with mode: 0644]
test/fuzz/fuzz-dhcp-server-relay-message/fe4344e65d495388540dc1bf8eae70c46f8b867c [new file with mode: 0644]
test/fuzz/fuzz-dhcp-server/clusterfuzz-testcase-minimized-fuzz-dhcp-server-4916534286352384 [new file with mode: 0644]
test/fuzz/fuzz-dhcp-server/crash-64c861ad981838e509920e8538640ef46feaae66 [new file with mode: 0644]
test/fuzz/fuzz-dhcp-server/crash-e13ccbc34d0fa5add0ee1eb69ed630dd2cdfb1ca [new file with mode: 0644]
test/fuzz/fuzz-link-parser/directives.link
test/fuzz/fuzz-unit-file/directives-all.service
test/fuzz/fuzz-unit-file/directives.mount
test/fuzz/fuzz-unit-file/directives.service
test/fuzz/fuzz-unit-file/directives.socket
test/fuzz/fuzz-unit-file/directives.swap
test/meson.build
test/splash.bmp [deleted file]
test/test-efi-create-disk.sh [deleted file]
test/test-functions
test/test-network/conf/25-sriov-udev.network [new file with mode: 0644]
test/test-network/conf/25-sriov.link [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py
test/test-resolve/selfsigned.cert [new file with mode: 0644]
test/test-resolve/selfsigned.key [new file with mode: 0644]
test/units/testsuite-17.03.sh
test/units/testsuite-29.sh
test/units/testsuite-46.sh
test/units/testsuite-50.sh
test/units/testsuite-51-repro-3.service [new file with mode: 0644]
test/units/testsuite-51.sh
tools/debug-sd-boot.sh
tools/oss-fuzz.sh
units/meson.build
units/systemd-networkd-wait-online@.service.in [new file with mode: 0644]

diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile
new file mode 100644 (file)
index 0000000..47f238c
--- /dev/null
@@ -0,0 +1,5 @@
+FROM gcr.io/oss-fuzz-base/base-builder@sha256:14b332de0e18683f37386eaedbf735bc6e8d81f9c0e1138d620f2178e20cd30a
+ENV MERGE_WITH_OSS_FUZZ_CORPORA=yes
+COPY . $SRC/systemd
+WORKDIR $SRC/systemd
+COPY tools/oss-fuzz.sh $SRC/build.sh
index 8eb40d4339ed2fab142a2cdce6a319f63abef10d..592833e24101f486b4e2b8fc98c4ba2269f7cbec 100644 (file)
@@ -10,7 +10,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later
 <!-- **NOTE:** Do not submit bug reports about anything but the two most recently released *major* systemd versions upstream! -->
 <!--           If there have been multiple stable releases for that major version, please consider updating to a recent one before reporting an issue. -->
 <!--           When using a distro package, please make sure that the version reported is meaningful for upstream. -->
-<!-- See https://github.com/systemd/systemd-stable/releases for the list of most recent releases. -->
+<!-- See https://github.com/systemd/systemd-stable/tags for the list of most recent releases. -->
 <!-- For older version please use distribution trackers (see https://systemd.io/CONTRIBUTING#filing-issues). -->
 
 **Used distribution**
index a23d6374c1fcd54d2085d993d3f6ffd237e0686d..3e067c176fd4f9258104336379c8b7b6cf16812a 100644 (file)
@@ -5,10 +5,15 @@ updates:
   - package-ecosystem: "github-actions"
     directory: "/"
     schedule:
-      interval: "weekly"
+      interval: "monthly"
     open-pull-requests-limit: 2
   - package-ecosystem: "pip"
     directory: "/.github/workflows"
     schedule:
       interval: "monthly"
     open-pull-requests-limit: 2
+  - package-ecosystem: "docker"
+    directory: "/.clusterfuzzlite"
+    schedule:
+      interval: "monthly"
+    open-pull-requests-limit: 2
diff --git a/.github/workflows/cflite_pr.yml b/.github/workflows/cflite_pr.yml
new file mode 100644 (file)
index 0000000..3fe2bac
--- /dev/null
@@ -0,0 +1,39 @@
+---
+# vi: ts=2 sw=2 et:
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+name: ClusterFuzzLite PR fuzzing
+on:
+  pull_request:
+    branches:
+      - main
+      - v[0-9]+-stable
+
+permissions: read-all
+
+jobs:
+  PR:
+    runs-on: ubuntu-latest
+    if: github.repository != 'systemd/systemd' || github.event.pull_request.user.login == 'dependabot[bot]'
+    concurrency:
+      group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }}
+      cancel-in-progress: true
+    strategy:
+      fail-fast: false
+      matrix:
+        sanitizer: [address, undefined, memory]
+    steps:
+    - name: Build Fuzzers (${{ matrix.sanitizer }})
+      id: build
+      uses: google/clusterfuzzlite/actions/build_fuzzers@41dccd0566905e2a7d1724e7883edbfa66d78877
+      with:
+        sanitizer: ${{ matrix.sanitizer }}
+        github-token: ${{ secrets.GITHUB_TOKEN }}
+    - name: Run Fuzzers (${{ matrix.sanitizer }})
+      id: run
+      uses: google/clusterfuzzlite/actions/run_fuzzers@41dccd0566905e2a7d1724e7883edbfa66d78877
+      with:
+        github-token: ${{ secrets.GITHUB_TOKEN }}
+        fuzz-seconds: 1200
+        mode: 'code-change'
+        sanitizer: ${{ matrix.sanitizer }}
index f2c6421ccdfebd571f4d9707dbd494da9b87d8e2..4ad9877661f2021551ab4061c525d39c59ee4d7c 100644 (file)
@@ -11,6 +11,8 @@ on:
       - .github/codeql-config.yml
       - .github/codeql-custom.qls
       - .github/workflows/codeql-analysis.yml
+      - .github/workflows/requirements.txt
+      - .github/workflows/unit_tests.sh
   # It takes the workflow approximately 30 minutes to analyze the code base
   # so it doesn't seem to make much sense to trigger it on every PR or commit.
   # It runs daily at 01:00 to avoid colliding with the Coverity workflow.
@@ -41,7 +43,7 @@ jobs:
       uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
 
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@cd783c8a29bdcf5a5c79c5137889e24651fa626c
+      uses: github/codeql-action/init@384cfc42b2131df01c009d3d2eed7b78d8e8556e
       with:
         languages: ${{ matrix.language }}
         config-file: ./.github/codeql-config.yml
@@ -49,7 +51,7 @@ jobs:
     - run: sudo -E .github/workflows/unit_tests.sh SETUP
 
     - name: Autobuild
-      uses: github/codeql-action/autobuild@cd783c8a29bdcf5a5c79c5137889e24651fa626c
+      uses: github/codeql-action/autobuild@384cfc42b2131df01c009d3d2eed7b78d8e8556e
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@cd783c8a29bdcf5a5c79c5137889e24651fa626c
+      uses: github/codeql-action/analyze@384cfc42b2131df01c009d3d2eed7b78d8e8556e
index e4b2d91ba6e85a0c9299dd3e776d745522877819..966e364f713c502d8d2e4c9a8bf9271c7c128a12 100644 (file)
@@ -40,7 +40,7 @@ jobs:
 
     steps:
     - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
-    - uses: systemd/mkosi@01ea953fd2af738f974b228991c768c12b50db95
+    - uses: systemd/mkosi@30288805db1a953ea31045933adb93194f91e3da
 
     - name: Install
       run: sudo apt-get update && sudo apt-get install --no-install-recommends python3-pexpect python3-jinja2
index 5d9e9da562e88cf8035d127c8a27f010b0ab4906..37594cb95e139fceb08d89d248e178dcc360e14a 100755 (executable)
@@ -17,6 +17,7 @@ ADDITIONAL_DEPS=(
     libqrencode-dev
     libssl-dev
     libtss2-dev
+    libxkbcommon-dev
     libzstd-dev
     perl
     python3-libevdev
index 962c77913ebb5c2d045429a3f0739c3e196a757b..0b1362e13a573ae99a6b55e51a85c388a3c98ff0 100644 (file)
@@ -37,7 +37,7 @@ jobs:
   trigger: pull_request
   metadata:
     targets:
-    - fedora-rawhide-aarch64
-    - fedora-rawhide-i386
-    - fedora-rawhide-ppc64le
-    - fedora-rawhide-x86_64
+    - fedora-35-aarch64
+    - fedora-35-i386
+    - fedora-35-ppc64le
+    - fedora-35-x86_64
diff --git a/NEWS b/NEWS
index ad8244095b169452475e00cfeb9b5373902e9c9f..606b4714c1316ef6adb3df170271acb17ec6d3d1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,30 @@
 systemd System and Service Manager
 
+CHANGES WITH 251:
+        * Incompatibility and Regression note:
+          In v250, the feature that automatically configures routes to addresses
+          specified in AllowedIPs= was added and enabled by default. However,
+          this feature causes network connectivity issues on many existing
+          setups. Hence, this is disabled by default since v250.3. The feature
+          can still be used by explicitly configuring RouteTable= setting in
+          .netdev files.
+
+        * Services with Restart=always and a failing ExecCondition= will no longer
+          be restarted, to bring ExecCondition= in line with Condition*= settings.
+
+        * In v250 systemd-homed started making use of UID mapped mounts for the
+          home areas if the kernel and used file system support it. Files are
+          now internally owned by the "nobody" user (i.e. the user typically
+          used for indicating "this ownership is not mapped"), and dynamically
+          mapped to the UID used locally on the system via the UID mapping
+          mount logic of recent kernels.
+          In the current implementation systemd-homed only maps a limited
+          number of UIDs and GIDs making it impossible to run unprivileged
+          containers that want to map a full POSIX compliant UID and GID range
+          with their rootfs located within the systemd-homed managed home area.
+          This will be fixed in subsequent releases. See
+          https://github.com/systemd/systemd/pull/22239 for a proposal.
+
 CHANGES WITH 250:
 
         * Support for encrypted and authenticated credentials has been added.
index 7139c2e0531397711f4bfcfda7501d247be109e7..a20189d31491bbc62a2ce07623b3895c93adf272 100644 (file)
@@ -19,7 +19,7 @@ support_url = get_option('support-url')
 support_sed = 's~%SUPPORT_URL%~@0@~'.format(support_url)
 
 foreach file : in_files
-        custom_target(
+        catalogs += custom_target(
                 file,
                 input : file + '.in',
                 output: file,
index aeb2be97b3f55eee885fc22bf43dc21790830aac..03340e7e8f7fe5399d401416230d0e6452085092 100644 (file)
@@ -356,7 +356,7 @@ but of course that's between you and those other tenants, and systemd won't
 care. Replicating the cgroup hierarchies in those unsupported controllers would
 mean replicating the full cgroup paths in them, and hence the prefixing
 `.slice` components too, otherwise the hierarchies will start being orthogonal
-after all, and that's not really desirable. On more thing: systemd will clean
+after all, and that's not really desirable. One more thing: systemd will clean
 up after you in the hierarchies it manages: if your daemon goes down, its
 cgroups will be removed too. You basically get the guarantee that you start
 with a pristine cgroup sub-tree for your service or scope whenever it is
index eba1e1444e60a144dc21fc1a3cb7144e78dfaa4e..cd4486973e2a231014f2e6e563b987684bf66b45 100644 (file)
@@ -358,7 +358,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later
 
 - For every function you add, think about whether it is a "logging" function or
   a "non-logging" function. "Logging" functions do (non-debug) logging on their
-  own, "non-logging" function never log on their own (except at debug level)
+  own, "non-logging" functions never log on their own (except at debug level)
   and expect their callers to log. All functions in "library" code, i.e. in
   `src/shared/` and suchlike must be "non-logging". Every time a "logging"
   function calls a "non-logging" function, it should log about the resulting
index 6607f5220039cdda9c00b42f507f340def2f5bfc..757207ce94a7e59d15441a1fa62a219a61ef62d7 100644 (file)
@@ -277,7 +277,7 @@ care should be taken to avoid naming conflicts. `systemd` (and in particular
 1. Do not drop `CAP_MKNOD` from the container. `PrivateDevices=` is a commonly
    used service setting that provides a service with its own, private, minimal
    version of `/dev/`. To set this up systemd in the container needs this
-   capability. If you take away the capability than all services that set this
+   capability. If you take away the capability, then all services that set this
    flag will cease to work. Use `BPF_PROG_TYPE_CGROUP_DEVICE` BPF programs — on
    cgroupv2 — or the `devices` controller — on cgroupv1 — to restrict what
    device nodes the container can create instead of taking away the capability
index 71d6c55010dba43bd5196de761e4cc061d197351..e8a2ce394a11f7935aad0ab5337ad085e0b55027 100644 (file)
@@ -325,7 +325,7 @@ fuzzers:
 * `$SYSTEMD_FUZZ_RUNS` — The number of times execution should be repeated in
   manual invocations.
 
-Note that is may be also useful to set `$SYSTEMD_LOG_LEVEL`, since all logging
+Note that it may be also useful to set `$SYSTEMD_LOG_LEVEL`, since all logging
 is suppressed by default.
 
 `systemd-importd`:
index c999fdd58ab9a56576f3468965a76051d519eae1..3dca54ebe3decc14ac5da939fa9d6f29cd9d3950 100644 (file)
@@ -7,7 +7,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later
 
 # GVariant D-Bus Message Serialization
 
-We stay close to the original dbus1 framing as possible, but make
+We stay as close to the original dbus1 framing as possible, but make
 certain changes to adapt for GVariant. dbus1 has the following
 framing:
 
index 2d1d0ac607d484706cc11d9ac1525c84980dd492..3898840c56b70db65e5b9129bf9f29411dae53fc 100644 (file)
@@ -12,7 +12,7 @@ The Linux initrd mechanism (short for "initial RAM disk") refers to a small
 file system archive that is unpacked by the kernel and contains the first
 userspace code that runs. It typically finds and transitions into the actual
 root file system to use. systemd supports both initrd and initrd-less boots. If
-an initrd is used it is a good idea to pass a few bits of runtime information
+an initrd is used, it is a good idea to pass a few bits of runtime information
 from the initrd to systemd in order to avoid duplicate work and to provide
 performance data to the administrator. In this page we attempt to roughly
 describe the interfaces that exist between the initrd and systemd. These
index f9c9fcb314cd220fc8e57beb8e5245daddd40853..0036ee45d68ee593c56a75f591c0b36713e137c4 100644 (file)
@@ -297,7 +297,7 @@ STATE_ARCHIVED. If a writer is asked to write to a file that is not in
 STATE_OFFLINE it should immediately rotate the file and start a new one,
 without changing the file.
 
-After and before the state field is changed `fdatasync()` should be executed on
+After and before the state field is changed, `fdatasync()` should be executed on
 the file to ensure the dirty state hits disk.
 
 
index 855f6acf30fd619fbf208ea8d5d659fa620b756b..d3fa9b7f73c1301a176298913aa8c1479fd361f9 100644 (file)
@@ -17,7 +17,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later
 
 The latter is what this document is about: if you are developing a program and
 want to pass structured log data to `journald`, it's the Journal's native
-protocol what you want to use. The systemd project provides the
+protocol that you want to use. The systemd project provides the
 [`sd_journal_print(3)`](https://www.freedesktop.org/software/systemd/man/sd_journal_print.html)
 API that implements the client side of this protocol. This document explains
 what this interface does behind the scenes, in case you'd like to implement a
index 7d810fbbd9fc3ab047b69039aebad2207ab7886d..ed204911bc09a1ed391ddf7afdd7ec255f154136 100644 (file)
@@ -34,7 +34,7 @@ It is easy to write additional agents. The basic algorithm to follow looks like
 
 Again, it is essential that you stop showing the password box/notification/status icon if the `ask.xxx` file is removed or when `NotAfter=` elapses (if it is set `!= 0`)!
 
-It may happen that multiple password entries are pending at the same time. Your agent needs to be able to deal with that. Depending on your environment you may either choose to show all outstanding passwords at the same time or instead only one and as soon as the user replied to that one go on to the next one.
+It may happen that multiple password entries are pending at the same time. Your agent needs to be able to deal with that. Depending on your environment you may either choose to show all outstanding passwords at the same time or instead only one and as soon as the user has replied to that one go on to the next one.
 
 You may test this all with manually invoking the "`systemd-ask-password`" tool on the command line. Pass `--no-tty` to ensure the password is asked via the agent system. Note that only privileged users may use this tool (after all this is intended purely for system-level passwords).
 
index dd9164126fd3eda68790b2f5e003fefcde24990b..6fb61d5e873d2ef978a5935df4d9dc708e316633 100644 (file)
@@ -284,9 +284,12 @@ following must be also be observed:
 4. The upper extension(s) image(s) must at least contain one matching unit file each,
    with the right name prefix and suffix (see above).
 
+5. As with the base/OS image, the upper extension(s) image(s) must be a plain
+   sub-directory, a btrfs subvolume or a raw disk image.
+
 ```
 # portablectl attach --extension foobar_0.7.23.raw debian-runtime_11.1.raw foobar
-# portablectl attach --extension barbaz_7.0.23.raw debian-runtime_11.1.raw barbaz
+# portablectl attach --extension barbaz_7.0.23/ debian-runtime_11.1.raw barbaz
 ```
 
 ## Execution Environment
index 89a5cdfacf2dcf85e4d2abc4581e477bb7fb0dc8..e06fbc3d2f7ccc043752e81d428ca4cf007a17fb 100644 (file)
@@ -66,7 +66,7 @@ a network interface may configure.
    differentiate them. i.e. `~foo.com` is a configured routing domain, while
    `foo.com` would be a configured search domain.
 
-   One routing domain is particular interesting: `~.` — the catch-all routing
+   One routing domain is particularly interesting: `~.` — the catch-all routing
    domain. (The *dot* domain `.` is how DNS denotes the "root" domain, i.e. the
    parent domain of all domains, but itself.) When used on an interface any DNS
    traffic is preferably routed to its DNS servers. (A search domain – i.e. `.`
index d3b7c2fe9ed7dd1a45e0504ff49b02b681c4fad6..5c05bf11cb4c85339fa9fe72be409e97f291fd9c 100644 (file)
@@ -176,7 +176,7 @@ Systemd has compile-time default for these boundaries. Using those defaults is
 recommended. It will nevertheless query `/etc/login.defs` at runtime, when
 compiled with `-Dcompat-mutable-uid-boundaries=true` and that file is present.
 Support for this is considered only a compatibility feature and should not be
-used except when upgrading systems which were creating with different defaults.
+used except when upgrading systems which were created with different defaults.
 
 ## Considerations for container managers
 
@@ -255,7 +255,7 @@ the artifacts the container manager persistently leaves in the system.
 | 2147483648…4294967294 | HIC SVNT LEONES       |               |                               |
 |            4294967295 | 32bit `(uid_t) -1`    | Linux         |                               |
 
-Note that "Unused" in the table above doesn't meant that these ranges are
+Note that "Unused" in the table above doesn't mean that these ranges are
 really unused. It just means that these ranges have no well-established
 pre-defined purposes between Linux, generic low-level distributions and
 `systemd`. There might very well be other packages that allocate from these
index cefe6d3dceef31d2dd71e373844249503d474019..ccd9f9b3d887dacfa2509d462fde820f0103e0d9 100644 (file)
@@ -241,7 +241,7 @@ about existence or non-existence of a record can be returned nor any user
 record at all. (The `service` field is defined in order to allow implementation
 of daemons that provide multiple distinct user/group services over the same
 `AF_UNIX` socket: in order to correctly determine which service a client wants
-to talk to the client needs to provide the name in each request.)
+to talk to, the client needs to provide the name in each request.)
 
 The `GetGroupRecord` method call works analogously but for groups.
 
@@ -257,7 +257,7 @@ with `more` set, so that multiple replies can be returned (since typically
 there are multiple members per group and also multiple groups a user is
 member of). As with `GetUserRecord` and `GetGroupRecord` the `service`
 parameter needs to contain the name of the service being talked to, in order to
-allow implementation of multiple service within the same IPC socket. In case no
+allow implementation of multiple services within the same IPC socket. In case no
 matching membership is known `NoRecordFound` is returned. The other two errors
 are also generated in the same cases as for `GetUserRecord` and
 `GetGroupRecord`.
@@ -270,7 +270,7 @@ before the complete list is acquired.
 Note that only the `GetMemberships` call is authoritative about memberships of
 users in groups. i.e. it should not be considered sufficient to check the
 `memberOf` field of user records and the `members` field of group records to
-acquire the full list of memberships. The full list can only bet determined by
+acquire the full list of memberships. The full list can only be determined by
 `GetMemberships`, and as mentioned requires merging of these lists of all local
 services. Result of this is that it can be one service that defines a user A,
 and another service that defines a group B, and a third service that declares
index bac0ce1721dd8e6e0969214b360559c1bec62e32..da911d5e73a5af1e248241e2f9a3cbf2b1f292f3 100644 (file)
@@ -333,7 +333,7 @@ values, which is then inherited by all the user's processes, see
 [`setrlimit()`](http://man7.org/linux/man-pages/man2/setrlimit.2.html) for more
 information.
 
-`locked` → A boolean value. If true the user account is locked, the user may
+`locked` → A boolean value. If true, the user account is locked, the user may
 not log in. If this field is missing it should be assumed to be false,
 i.e. logins are permitted. This field corresponds to the `sp_expire` field of
 `struct spwd` (i.e. the `/etc/shadow` data for a user) being set to zero or
@@ -359,11 +359,11 @@ directory, also containing the `~/.identity` user record; `luks` is a per-user
 LUKS volume that is mounted as home directory, and `cifs` a home directory
 mounted from a Windows File Share. The five latter types are primarily used by
 `systemd-homed` when managing home directories, but may be used if other
-managers are used too. If this is not set `classic` is the implied default.
+managers are used too. If this is not set, `classic` is the implied default.
 
 `diskSize` → An unsigned 64bit integer, indicating the intended home directory
 disk space in bytes to assign to the user. Depending on the selected storage
-type this might be implement differently: for `luks` this is the intended size
+type this might be implemented differently: for `luks` this is the intended size
 of the file system and LUKS volume, while for the others this likely translates
 to classic file system quota settings.
 
@@ -425,7 +425,7 @@ the top-level directory of the CIFS share is used.
 
 `imagePath` → A string with an absolute file system path to the file, directory
 or block device to use for storage backing the home directory. If the `luks`
-storage is used this refers to the loopback file or block device node to store
+storage is used, this refers to the loopback file or block device node to store
 the LUKS volume on. For `fscrypt`, `directory`, `subvolume` this refers to the
 directory to bind mount as home directory on login. Not defined for `classic`
 or `cifs`.
@@ -465,7 +465,7 @@ relevant when the storage mechanism used is `luks`.
 referencing the file system UUID the home directory is located in. This is
 primarily relevant when the storage mechanism used is `luks`.
 
-`luksDiscard` → A boolean. If true and `luks` storage is used controls whether
+`luksDiscard` → A boolean. If true and `luks` storage is used, controls whether
 the loopback block devices, LUKS and the file system on top shall be used in
 `discard` mode, i.e. erased sectors should always be returned to the underlying
 storage. If false and `luks` storage is used turns this behavior off. In
@@ -579,7 +579,7 @@ against all plugged in security tokens and if there's exactly one matching
 private key found with it it is used.
 
 `fido2HmacCredential` → An array of strings, each with a Base64-encoded FIDO2
-credential ID that shell be used for authentication with FIDO2 devices that
+credential ID that shall be used for authentication with FIDO2 devices that
 implement the `hmac-secret` extension. The salt to pass to the FIDO2 device is
 found in `fido2HmacSalt`.
 
index a614473bd907683b28846b341fd286cc83e2ed3f..c7bf6cfab10b4cfb0b3af95c9b217b17f1beca67 100644 (file)
@@ -4,3 +4,7 @@
 dmi:bvnLENOVO*
     ID_SYSFS_ATTRIBUTE_MODEL=product_version
     ID_VENDOR_FROM_DATABASE=Lenovo
+
+# Microsoft Surface 1's chassis type
+dmi:bvnMicrosoft Corporation*:pvrSurface with Windows 8 Pro*
+    ID_CHASSIS=tablet
index f896dde357f306739b24d8e641c5ec44ed3a2ee3..9fcb4a3ddf112b6f5aaedd121f69821fd3baa1af 100644 (file)
@@ -591,6 +591,24 @@ evdev:name:MSFT0001:02 04F3:304B Touchpad:dmi:*svnLENOVO:*pvrLenovoLegionY9000X2
  EVDEV_ABS_35=::31
  EVDEV_ABS_36=::30
 
+#########################################
+# Microsoft
+#########################################
+
+# Surface Laptop 2 (13")
+evdev:name:Microsoft Surface 045E:0933 Touchpad:dmi:*svnMicrosoftCorporation:*pnSurfaceLaptop2**
+ EVDEV_ABS_00=::38
+ EVDEV_ABS_01=::38
+ EVDEV_ABS_35=::38
+ EVDEV_ABS_36=::38
+
+# Surface Laptop 3 (15")
+evdev:name:Microsoft Surface 045E:09AF Touchpad:dmi:*svnMicrosoftCorporation:*pnSurfaceLaptop3**
+ EVDEV_ABS_00=::39
+ EVDEV_ABS_01=::37
+ EVDEV_ABS_35=::39
+ EVDEV_ABS_36=::37
+
 #########################################
 # NEWYES
 #########################################
index c4101cc2a528d7329669faab5373af3df595fd74..2d5681dea694946654f0ffdb077b584f551b97d7 100644 (file)
@@ -70,4 +70,13 @@ id-input:modalias:input:b0005v046DpB00De0700*
 
 # Logitech MX Keys
 id-input:modalias:input:b0003v046Dp408Ae0111*
- ID_INPUT_MOUSE=0 
+ ID_INPUT_MOUSE=0
+
+# Logitech Craft Keyboard
+id-input:modalias:input:b0003v046Dp4066e0111*
+ ID_INPUT_MOUSE=0
+
+# CH Products Pro Pedals
+id-input:modalias:input:b0003v068Ep00F2e0100*
+ ID_INPUT_ACCELEROMETER=0
+ ID_INPUT_JOYSTICK=1
index ab7b1adacd51f69d9a171a8baf41298b162e48d8..c4ee7e0c7b6791b7438248c19b38d66a471bd08f 100644 (file)
@@ -426,6 +426,10 @@ sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd03/20/201
 sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/25/2017:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
  ACCEL_LOCATION=base
 
+# GPD Pocket 3
+sensor:modalias:acpi:MXC6655*:dmi:*:svnGPD:pnG1621-02:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+
 #########################################
 # Hometech
 ########################################
index 6302312456258643b4ba9552cd6c1663b4fd5bd9..66e74f667cf8485067137fe9f682a5099ff461d9 100644 (file)
@@ -198,6 +198,17 @@ mouse:usb:v0461p4d46:name:USB Optical Mouse:*
 mouse:usb:v056ep010d:name:ELECOM TrackBall Mouse HUGE TrackBall:*
  MOUSE_DPI=500@125 *1000@125 1500@125
 
+# Elecom DEFT Pro TrackBall (M-DPT1MR)
+mouse:usb:v056ep0131:name:ELECOM TrackBall Mouse DEFT Pro TrackBall Mouse:*
+ MOUSE_DPI=*500 1000 1500
+ MOUSE_WHEEL_CLICK_ANGLE=10
+
+# Elecom Relacon (M-RT1DR)
+mouse:usb:v056ep0155:name:ELECOM ELECOM Relacon:*
+ ID_INPUT_TRACKBALL=1
+ MOUSE_DPI=*500 1000 1500
+ MOUSE_WHEEL_CLICK_ANGLE=30
+
 ##########################################
 # Fujitsu Siemens
 ##########################################
diff --git a/hwdb.d/70-pda.hwdb b/hwdb.d/70-pda.hwdb
new file mode 100644 (file)
index 0000000..e122acc
--- /dev/null
@@ -0,0 +1,39 @@
+# This file is part of systemd.
+#
+# Database for handhelds (PDAs, calculators, etc.) that should be accessible
+# the seat owner.
+#
+# Permitted keys:
+#   Specify if a device is a signal analyzer
+#   ID_PDA=1|0
+
+###########################################################
+# Texas Instruments
+###########################################################
+# SilverLink
+usb:v0451pE001*
+ ID_PDA=1
+
+# TI-84 Plus DirectLink
+usb:v0451pE003*
+ ID_PDA=1
+
+# TI-89 Titanium DirectLink
+usb:v0451pE004*
+ ID_PDA=1
+
+# TI-84 Plus Silver Edition DirectLink
+usb:v0451pE008*
+ ID_PDA=1
+
+# TI-Nspire DirectLink
+usb:v0451pE012*
+ ID_PDA=1
+
+# TI-Nspire Lab Cradle
+usb:v0451pE01C*
+ ID_PDA=1
+
+# TI-Nspire CX II DirectLink
+usb:v0451pE022*
+ ID_PDA=1
index 8ff044131c029b754c34326ce01d9d8c52d81ad6..fc72ebb2bd13139acb566d8e92e73b9748ba5d21 100644 (file)
@@ -31,6 +31,7 @@ hwdb_files_test = files('''
         70-cameras.hwdb
         70-joystick.hwdb
         70-mouse.hwdb
+        70-pda.hwdb
         70-pointingstick.hwdb
         70-touchpad.hwdb
         80-ieee1394-unit-function.hwdb
index 0268bf9580d7b55e37e0f9cdb213da46e27be055..194a71ac08ed8a9644781084eb9932cfe30b7697 100755 (executable)
@@ -121,7 +121,7 @@ def hwdb_grammar():
 def property_grammar():
     ParserElement.setDefaultWhitespaceChars(' ')
 
-    dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ'))('SETTINGS*')
+    dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Optional(Suppress('@') + INTEGER('HZ')))('SETTINGS*')
     mount_matrix_row = SIGNED_REAL + ',' + SIGNED_REAL + ',' + SIGNED_REAL
     mount_matrix = Group(mount_matrix_row + ';' + mount_matrix_row + ';' + mount_matrix_row)('MOUNT_MATRIX')
     xkb_setting = Optional(Word(alphanums + '+-/@._'))
@@ -136,6 +136,7 @@ def property_grammar():
              ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER),
              ('ID_AUTOSUSPEND', Or((Literal('0'), Literal('1')))),
              ('ID_PERSIST', Or((Literal('0'), Literal('1')))),
+             ('ID_PDA', Or((Literal('0'), Literal('1')))),
              ('ID_INPUT', Or((Literal('0'), Literal('1')))),
              ('ID_INPUT_ACCELEROMETER', Or((Literal('0'), Literal('1')))),
              ('ID_INPUT_JOYSTICK', Or((Literal('0'), Literal('1')))),
index 2f478cb453452f7873d7e15669d8e34ee038ef4c..0e220b3f9ebb93e757d7d533170ac52664cc7d42 100644 (file)
       <citerefentry project='man-pages'><refentrytitle>less</refentrytitle><manvolnum>1</manvolnum></citerefentry> and
       <citerefentry project='man-pages'><refentrytitle>more</refentrytitle><manvolnum>1</manvolnum></citerefentry>, until one is found. If
       no pager implementation is discovered no pager is invoked. Setting this environment variable to an empty string
-      or the value <literal>cat</literal> is equivalent to passing <option>--no-pager</option>.</para></listitem>
+      or the value <literal>cat</literal> is equivalent to passing <option>--no-pager</option>.</para>
+
+      <para>Note: if <varname>$SYSTEMD_PAGERSECURE</varname> is not set, <varname>$SYSTEMD_PAGER</varname>
+      (as well as <varname>$PAGER</varname>) will be silently ignored.</para></listitem>
     </varlistentry>
 
     <varlistentry id='less'>
index 0f57cbdb88ad850e9123408e2dcf57d039c58fb5..4467c59c94c8735c03af68b190888f34106cf70c 100644 (file)
         <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         will be used.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>VENDOR=</varname></term>
+
+        <listitem><para>Specifies the hardware vendor. If unspecified, the hardware vendor set in DMI
+        or hwdb will be used.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MODEL=</varname></term>
+
+        <listitem><para>Specifies the hardware model. If unspecified, the hardware model set in DMI or
+        hwdb will be used.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 710b4ca0088542d47a97e0707b2f7fe5b3aea0a4..069772467eb782caadd8963c7da7c3f2b2abe158 100644 (file)
@@ -226,7 +226,8 @@ update_dbus_docs = custom_target(
 if conf.get('BUILD_MODE_DEVELOPER') == 1
         test('dbus-docs-fresh',
              update_dbus_docs_py,
-             args : ['--build-dir', project_build_root, '--test', dbus_docs])
+             args : ['--build-dir', project_build_root, '--test', dbus_docs],
+             depends : dbus_programs)
 endif
 
 update_man_rules = custom_target(
index 4f51cd5e806e831c332daed973b62df2db69d785..5e3bb7807dc9278e09da3655b221a3693a861505 100644 (file)
@@ -58,6 +58,8 @@ node /org/freedesktop/hostname1 {
                   in  b interactive);
       GetProductUUID(in  b interactive,
                      out ay uuid);
+      GetHardwareSerial(in  b interactive,
+                        out s serial);
       Describe(out s json);
     properties:
       readonly s Hostname = '...';
@@ -93,6 +95,8 @@ node /org/freedesktop/hostname1 {
 };
     </programlisting>
 
+    <!--method GetHardwareSerial is not documented!-->
+
     <!--property HardwareVendor is not documented!-->
 
     <!--property HardwareModel is not documented!-->
@@ -119,6 +123,8 @@ node /org/freedesktop/hostname1 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="GetProductUUID()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="GetHardwareSerial()"/>
+
     <variablelist class="dbus-method" generated="True" extra-ref="Describe()"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="Hostname"/>
index 53c960206ed22b34d394aa1f38e79940b73ae1c8..4842885c0cc50f079b540ad3be99e5252bfc2cf2 100644 (file)
@@ -57,6 +57,10 @@ node /org/freedesktop/portable1 {
                                      out a{say} units);
       GetImageState(in  s image,
                     out s state);
+      GetImageStateWithExtensions(in  s image,
+                                  in  as extensions,
+                                  in  t flags,
+                                  out s state);
       AttachImage(in  s image,
                   in  as matches,
                   in  s profile,
@@ -132,6 +136,8 @@ node /org/freedesktop/portable1 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="GetImageState()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="GetImageStateWithExtensions()"/>
+
     <variablelist class="dbus-method" generated="True" extra-ref="AttachImage()"/>
 
     <variablelist class="dbus-method" generated="True" extra-ref="AttachImageWithExtensions()"/>
@@ -187,7 +193,15 @@ node /org/freedesktop/portable1 {
       This method is a superset of <function>GetImageMetadata()</function> with the addition of
       a list of extensions as input parameter, which were overlaid on top of the main
       image via <function>AttachImageWithExtensions()</function>.
-      The <varname>flag</varname> parameter is currently unused and reserved for future purposes.</para>
+      The <varname>flag</varname> parameter can be used to request that, before the units, the path of
+      each extension and an array of bytes with the content of the respective extension-release file
+      are sent. One such structure will be sent for each extension named in the input arguments. The
+      flag value to enable this functionality is defined as follows:</para>
+
+      <programlisting>
+#define PORTABLE_INSPECT_EXTENSION_RELEASES  (UINT64_C(1) &lt;&lt; 1)
+      </programlisting>
+
 
       <para><function>GetImageState()</function> retrieves the image state as one of the following
       strings:
@@ -207,6 +221,12 @@ node /org/freedesktop/portable1 {
         <listitem><para>running-runtime</para></listitem>
       </itemizedlist></para>
 
+      <para><function>GetImageStateWithExtensions()</function> is a superset of
+      <function>GetImageState()</function>, with additional support for a list of extensions
+      as input parameters, which is necessary to query the state in case the image was attached
+      in that particular way. The <varname>flag</varname> parameter is currently unused and
+      reserved for future purposes.</para>
+
       <para><function>AttachImage()</function> attaches a portable image to the system.
       This method takes an image path or name, a list of strings that will be used to search for
       unit files inside the image (partial or complete matches), a string indicating which
@@ -334,6 +354,9 @@ node /org/freedesktop/portable1 {
                                 out ay os_release,
                                 out a{say} units);
       GetState(out s state);
+      GetStateWithExtensions(in  as extensions,
+                             in  t flags,
+                             out s state);
       Attach(in  as matches,
              in  s profile,
              in  b runtime,
@@ -402,6 +425,8 @@ node /org/freedesktop/portable1 {
 
     <!--method GetState is not documented!-->
 
+    <!--method GetStateWithExtensions is not documented!-->
+
     <!--method Attach is not documented!-->
 
     <!--method AttachWithExtensions is not documented!-->
@@ -434,6 +459,8 @@ node /org/freedesktop/portable1 {
 
     <variablelist class="dbus-method" generated="True" extra-ref="GetState()"/>
 
+    <variablelist class="dbus-method" generated="True" extra-ref="GetStateWithExtensions()"/>
+
     <variablelist class="dbus-method" generated="True" extra-ref="Attach()"/>
 
     <variablelist class="dbus-method" generated="True" extra-ref="AttachWithExtensions()"/>
index bd69a00b57cabb7db30e01027c3f411a7b36fde5..97a8c98b39e0d6d6a8637f8a36106440e855e809 100644 (file)
@@ -2682,6 +2682,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s RootVerity = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ExtensionDirectories = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(sba(ss)) ExtensionImages = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(ssba(ss)) MountImages = [...];
@@ -3827,6 +3829,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="RootVerity"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="MountImages"/>
@@ -4185,6 +4189,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       <varname>RootHashSignature</varname>
       <varname>MountImages</varname>
       <varname>ExtensionImages</varname>
+      <varname>ExtensionDirectories</varname>
       see systemd.exec(5) for their meaning.</para>
 
       <para><varname>MemoryAvailable</varname> indicates how much unused memory is available to the unit before
@@ -4559,6 +4564,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s RootVerity = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ExtensionDirectories = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(sba(ss)) ExtensionImages = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(ssba(ss)) MountImages = [...];
@@ -5722,6 +5729,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="RootVerity"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="MountImages"/>
@@ -6344,6 +6353,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s RootVerity = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ExtensionDirectories = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(sba(ss)) ExtensionImages = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(ssba(ss)) MountImages = [...];
@@ -7353,6 +7364,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="RootVerity"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="MountImages"/>
@@ -8102,6 +8115,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly s RootVerity = '...';
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ExtensionDirectories = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(sba(ss)) ExtensionImages = [...];
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly a(ssba(ss)) MountImages = [...];
@@ -9083,6 +9098,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="RootVerity"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="MountImages"/>
index c5404db0bae59d6de07b4a47b978433beb69bca6..719ea81aee820777dc869992b5689345f43e62fe 100644 (file)
@@ -47,7 +47,8 @@
     <para>Portable service images are an efficient way to bundle multiple related services and other units together,
     and transfer them as a whole between systems. When these images are attached the local system the contained units
     may run in most ways like regular system-provided units, either with full privileges or inside strict sandboxing,
-    depending on the selected configuration.</para>
+    depending on the selected configuration. For more details, see
+    <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>.</para>
 
     <para>Specifically portable service images may be of the following kind:</para>
 
         top of <replaceable>IMAGE</replaceable> when attaching/detaching. This argument can be specified
         multiple times, in which case the order in which images are laid down follows the rules specified in
         <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        for the <varname>ExtensionImages=</varname> directive. The image(s) must contain an
-        <filename>extension-release</filename> file with metadata that matches what is defined in the
-        <filename>os-release</filename> of <replaceable>IMAGE</replaceable>. See:
+        for the <varname>ExtensionImages=</varname> directive and for the
+        <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> tool.
+        The image(s) must contain an <filename>extension-release</filename> file with metadata that matches
+        what is defined in the <filename>os-release</filename> of <replaceable>IMAGE</replaceable>. See:
         <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+        Images can be block images, btrfs subvolumes or directories. For more information on portable
+        services with extensions, see the <literal>Extension Images</literal> paragraph on
+        <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>.
         </para>
 
         <para>Note that the same extensions have to be specified, in the same order, when attaching
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>org.freedesktop.portable1</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-portabled.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
index b689b1c1af698eef0db173aac11a131a68c1dd30..7200ecf6810e4799e45326c916bcf9c00ad1e79d 100644 (file)
@@ -932,7 +932,7 @@ manpages = [
  ['systemd-network-generator.service', '8', ['systemd-network-generator'], ''],
  ['systemd-networkd-wait-online.service',
   '8',
-  ['systemd-networkd-wait-online'],
+  ['systemd-networkd-wait-online', 'systemd-networkd-wait-online@.service'],
   'ENABLE_NETWORKD'],
  ['systemd-networkd.service', '8', ['systemd-networkd'], 'ENABLE_NETWORKD'],
  ['systemd-notify', '1', [], ''],
index 9c6b02ac1c80fe5d29b3a96563022ef89fec4352..a3a70db20912e607ba27d14fcaaa4830e820ac80 100644 (file)
 
   <refnamediv>
     <refname>systemd-networkd-wait-online.service</refname>
+    <refname>systemd-networkd-wait-online@.service</refname>
     <refname>systemd-networkd-wait-online</refname>
     <refpurpose>Wait for network to come online</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <para><filename>systemd-networkd-wait-online.service</filename></para>
+    <para><filename>systemd-networkd-wait-online@.service</filename></para>
     <para><filename>/usr/lib/systemd/systemd-networkd-wait-online</filename></para>
   </refsynopsisdiv>
 
     to be fully configured or failed, and for at least one link to be online. Here, online means that
     the link's operational state is equal or higher than <literal>degraded</literal>. The threshold
     can be configured by <option>--operational-state=</option> option.</para>
+
+    <para>The service <filename>systemd-networkd-wait-online.service</filename> invokes
+    <command>systemd-networkd-wait-online</command> without any options. Thus, it waits for all managed
+    interfaces to be configured or failed, and for at least one to be online.</para>
+
+    <para>The service <filename>systemd-networkd-wait-online@.service</filename> takes an interface
+    name, and invokes <command>systemd-networkd-wait-online</command> with <option>-i</option> and the
+    specified interface name. Thus, wait for the specified interface to be configured and online. For
+    example, <filename>systemd-networkd-wait-online@eth0.service</filename> waits for
+    <filename>eth0</filename> to be configured by <command>systemd-networkd</command> and online.
+    </para>
   </refsect1>
 
   <refsect1>
index bef61cb7cfe370b3d7ac5de23440785ea13276ab..002a91b12998c270cf6015c326bbff9af348de4d 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para><command>systemd-stdio-bridge</command> may be used as a STDIO or socket-activatable
-    proxy to a given D-Bus endpoint.</para>
-
+    <para><command>systemd-stdio-bridge</command> implements a proxy for a D-Bus endpoint. It expects to
+    receive an open connection to a bus when started, and will also connect to a (different) bus as a
+    client. It will then act as a server on the first connection, and forward messages between the two
+    busses. This program is suitable for socket activation: the first connection may be a pipe or a socket
+    and must be passed as either standard input, or as an open file descriptor according to the protocol
+    described in
+    <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>. The
+    second connection will be made by default to the local system bus, but this can be influenced by the
+    <option>--user</option>, <option>--system</option>, <option>--machine=</option>, and
+    <option>--bus-path=</option> options described below.</para>
+
+    <para><citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry> uses
+    <command>systemd-stdio-bridge</command> to forward D-Bus connections over
+    <citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+    or to connect to the bus of a different user, see
+    <citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
   </refsect1>
 
   <refsect1>
     <para>The following options are understood:</para>
 
     <variablelist>
+      <xi:include href="user-system-options.xml" xpointer="user" />
+      <xi:include href="user-system-options.xml" xpointer="system" />
+      <xi:include href="user-system-options.xml" xpointer="machine" />
+
       <varlistentry>
           <term><option>-p <replaceable>PATH</replaceable></option></term>
           <term><option>--bus-path=<replaceable>PATH</replaceable></option></term>
@@ -52,9 +70,6 @@
 
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
-      <xi:include href="user-system-options.xml" xpointer="user" />
-      <xi:include href="user-system-options.xml" xpointer="system" />
-      <xi:include href="user-system-options.xml" xpointer="machine" />
     </variablelist>
   </refsect1>
 
@@ -70,7 +85,6 @@
       <citerefentry project='dbus'><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry project='dbus'><refentrytitle>dbus-broker</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <ulink url="https://www.freedesktop.org/wiki/Software/dbus">D-Bus</ulink>,
-      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     </para>
   </refsect1>
index 079ff14aeaffc6357922b0e9130b828dce13fedf..2c00c08affbec1c427c7de8589de8b78f6af05a0 100644 (file)
 
         <xi:include href="system-only.xml" xpointer="singular"/></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>ExtensionDirectories=</varname></term>
+
+        <listitem><para>This setting is similar to <varname>BindReadOnlyPaths=</varname> in that it mounts a file
+        system hierarchy from a directory, but instead of providing a destination path, an overlay will be set
+        up. This option expects a whitespace separated list of source directories.</para>
+
+        <para>A read-only OverlayFS will be set up on top of <filename>/usr/</filename> and
+        <filename>/opt/</filename> hierarchies. The order in which the directories are listed will determine
+        the order in which the overlay is laid down: directories specified first to last will result in overlayfs
+        layers bottom to top.</para>
+
+        <para>Each directory listed in <varname>ExtensionDirectories=</varname> may be prefixed with <literal>-</literal>,
+        in which case it will be ignored when its source path does not exist. Any mounts created with this option are
+        specific to the unit, and are not visible in the host's mount table.</para>
+
+        <para>These settings may be used more than once, each usage appends to the unit's list of directories
+        paths. If the empty string is assigned, the entire list of mount paths defined prior to this is
+        reset.</para>
+
+        <para>Each directory must contain a <filename>/usr/lib/extension-release.d/extension-release.IMAGE</filename>
+        file, with the appropriate metadata which matches <varname>RootImage=</varname>/<varname>RootDirectory=</varname>
+        or the host. See:
+        <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+        <xi:include href="system-only.xml" xpointer="singular"/></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
@@ -2485,18 +2513,39 @@ SystemCallErrorNumber=EPERM</programlisting>
       <varlistentry>
         <term><varname>EnvironmentFile=</varname></term>
 
-        <listitem><para>Similar to <varname>Environment=</varname> but reads the environment variables from a text
-        file. The text file should contain new-line-separated variable assignments.  Empty lines, lines without an
-        <literal>=</literal> separator, or lines starting with ; or # will be ignored, which may be used for
-        commenting. A line ending with a backslash will be concatenated with the following one, allowing multiline
-        variable definitions. The parser strips leading and trailing whitespace from the values of assignments, unless
-        you use double quotes (").</para>
-
-        <para><ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C escapes</ulink>
-        are supported, but not
-        <ulink url="https://en.wikipedia.org/wiki/Control_character#In_ASCII">most control characters</ulink>.
-        <literal>\t</literal> and <literal>\n</literal> can be used to insert tabs and newlines within
-        <varname>EnvironmentFile=</varname>.</para>
+        <listitem><para>Similar to <varname>Environment=</varname> but reads the environment variables from a text file.
+        The text file should contain newline-separated variable assignments.  Empty lines, lines without an
+        <literal>=</literal> separator, or lines starting with <literal>;</literal> or <literal>#</literal> will be
+        ignored, which may be used for commenting. The file must be UTF-8 encoded. Valid characters are <ulink
+        url="https://www.unicode.org/glossary/#unicode_scalar_value">unicode scalar values</ulink> other than <ulink
+        url="https://www.unicode.org/glossary/#noncharacter">noncharacters</ulink>, U+0000 NUL, and U+FEFF <ulink
+        url="https://www.unicode.org/glossary/#byte_order_mark">byte order mark</ulink>. Control codes other than NUL
+        are allowed.</para>
+
+        <para>In the file, an unquoted value after the <literal>=</literal> is parsed with the same backslash-escape
+        rules as <ulink
+        url="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01">unquoted
+        text</ulink> in a POSIX shell, but unlike in a shell, interior whitespace is preserved and quotes after the
+        first non-whitespace character are preserved. Leading and trailing whitespace (space, tab, carriage return) is
+        discarded, but interior whitespace within the line is preserved verbatim. A line ending with a backslash will be
+        continued to the following one, with the newline itself discarded. A backslash
+        <literal>\</literal> followed by any character other than newline will preserve the following character, so that
+        <literal>\\</literal> will become the value <literal>\</literal>.</para>
+
+        <para>In the file, a <literal>'</literal>-quoted value after the <literal>=</literal> can span multiple lines
+        and contain any character verbatim other than single quote, like <ulink
+        url="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_02">single-quoted
+        text</ulink> in a POSIX shell. No backslash-escape sequences are recognized. Leading and trailing whitespace
+        outside of the single quotes is discarded.</para>
+
+        <para>In the file, a <literal>"</literal>-quoted value after the <literal>=</literal> can span multiple lines,
+        and the same escape sequences are recognized as in <ulink
+        url="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_03">double-quoted
+        text</ulink> of a POSIX shell. Backslash (<literal>\</literal>) followed by any of <literal>"\`$</literal> will
+        preserve that character. A backslash followed by newline is a line continuation, and the newline itself is
+        discarded. A backslash followed by any other character is ignored; both the backslash and the following
+        character are preserved verbatim. Leading and trailing whitespace outside of the double quotes is
+        discarded.</para>
 
         <para>The argument passed should be an absolute filename or wildcard expression, optionally prefixed with
         <literal>-</literal>, which indicates that if the file does not exist, it will not be read and no error or
@@ -2529,12 +2578,6 @@ SystemCallErrorNumber=EPERM</programlisting>
         <para>Variables set for invoked processes due to this setting are subject to being overridden by those
         configured with <varname>Environment=</varname> or <varname>EnvironmentFile=</varname>.</para>
 
-        <para><ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C escapes</ulink>
-        are supported, but not
-        <ulink url="https://en.wikipedia.org/wiki/Control_character#In_ASCII">most control characters</ulink>.
-        <literal>\t</literal> and <literal>\n</literal> can be used to insert tabs and newlines within
-        <varname>EnvironmentFile=</varname>.</para>
-
         <para>Example:
         <programlisting>PassEnvironment=VAR1 VAR2 VAR3</programlisting>
         passes three variables <literal>VAR1</literal>,
index 45cabbccf70c1c0d5aa8a2843a500ca2cb5edfbd..700defeda6b86045e9f4daf7a302d5c1a0c41f65 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>SR-IOVVirtualFunctions=</varname></term>
+        <listitem>
+          <para>Specifies the number of SR-IOV virtual functions. Takes an integer in the range
+          0…2147483647. Defaults to unset, and automatically determined from the values specified in
+          the <varname>VirtualFunction=</varname> settings in the [SR-IOV] sections.</para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='sr-iov'>
+    <title>[SR-IOV] Section Options</title>
+    <para>The [SR-IOV] section accepts the following keys. Specify several [SR-IOV] sections to
+    configure several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource
+    into virtual PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV
+    improves north-south network performance (that is, traffic with endpoints outside the host machine)
+    by allowing traffic to bypass the host machine’s network stack.</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>VirtualFunction=</varname></term>
+        <listitem>
+          <para>Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move
+          data in and out. Takes an integer in the range 0…2147483646. This option is compulsory.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>VLANId=</varname></term>
+        <listitem>
+          <para>Specifies VLAN ID of the virtual function. Takes an integer in the range 1…4095.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>QualityOfService=</varname></term>
+        <listitem>
+          <para>Specifies quality of service of the virtual function. Takes an integer in the range
+          1…4294967294.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>VLANProtocol=</varname></term>
+        <listitem>
+          <para>Specifies VLAN protocol of the virtual function. Takes <literal>802.1Q</literal> or
+          <literal>802.1ad</literal>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MACSpoofCheck=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will
+          be used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>QueryReceiveSideScaling=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Toggle the ability of querying the receive side scaling (RSS)
+          configuration of the virtual function (VF). The VF RSS information like RSS hash key may be
+          considered sensitive on some devices where this information is shared between VF and the
+          physical function (PF). When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Trust=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Allows one to set trust mode of the virtual function (VF). When set,
+          VF users can set a specific feature which may impact security and/or performance. When unset,
+          the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>LinkState=</varname></term>
+        <listitem>
+          <para>Allows one to set the link state of the virtual function (VF). Takes a boolean or a
+          special value <literal>auto</literal>. Setting to <literal>auto</literal> means a
+          reflection of the physical function (PF) link state, <literal>yes</literal> lets the VF to
+          communicate with other VFs on this host even if the PF link state is down,
+          <literal>no</literal> causes the hardware to drop any packets sent by the VF. When unset,
+          the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MACAddress=</varname></term>
+        <listitem>
+          <para>Specifies the MAC address for the virtual function.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index eeca0a4eb52285dd17dbbf8b4871848ee13024a5..449b23d5ac3343010e49dec1eda491105e0395a7 100644 (file)
           <entry>A dummy device drops all packets sent to it.</entry></row>
 
           <row><entry><varname>gre</varname></entry>
-          <entry>A Level 3 GRE tunnel over IPv4. See <ulink url="https://tools.ietf.org/html/rfc2784">RFC 2784</ulink> for details.</entry></row>
+          <entry>A Level 3 GRE tunnel over IPv4. See <ulink url="https://tools.ietf.org/html/rfc2784">RFC 2784</ulink> for details. Name <literal>gre0</literal> should not be used, as the kernel creates a device with this name when the corresponding kernel module is loaded.</entry></row>
 
           <row><entry><varname>gretap</varname></entry>
-          <entry>A Level 2 GRE tunnel over IPv4.</entry></row>
+          <entry>A Level 2 GRE tunnel over IPv4. Name <literal>gretap0</literal> should not be used, as the kernel creates a device with this name when the corresponding kernel module is loaded.</entry></row>
 
           <row><entry><varname>erspan</varname></entry>
-          <entry>ERSPAN mirrors traffic on one or more source ports and delivers the mirrored traffic to one or more destination ports on another switch. The traffic is encapsulated in generic routing encapsulation (GRE) and is therefore routable across a layer 3 network between the source switch and the destination switch.</entry></row>
+          <entry>ERSPAN mirrors traffic on one or more source ports and delivers the mirrored traffic to one or more destination ports on another switch. The traffic is encapsulated in generic routing encapsulation (GRE) and is therefore routable across a layer 3 network between the source switch and the destination switch. Name <literal>erspan0</literal> should not be used, as the kernel creates a device with this name when the corresponding kernel module is loaded.</entry></row>
 
           <row><entry><varname>ip6gre</varname></entry>
           <entry>A Level 3 GRE tunnel over IPv6.</entry></row>
index 574cf599ec8f59385a8e70a1cfd44ba4a79caaed..44be11de1966c35199e6294cb8e9cfcf7e3ccd8a 100644 (file)
     </variablelist>
   </refsect1>
 
-  <refsect1>
-    <title>[SR-IOV] Section Options</title>
-    <para>The [SR-IOV] section accepts the following keys. Specify several [SR-IOV] sections to
-    configure several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource
-    into virtual PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV
-    improves north-south network performance (that is, traffic with endpoints outside the host machine)
-    by allowing traffic to bypass the host machine’s network stack.</para>
-
-    <variablelist class='network-directives'>
-      <varlistentry>
-        <term><varname>VirtualFunction=</varname></term>
-        <listitem>
-          <para>Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move
-          data in and out. Takes an integer in the range 0…2147483646. This option is compulsory.
-          </para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>VLANId=</varname></term>
-        <listitem>
-          <para>Specifies VLAN ID of the virtual function. Takes an integer in the range 1…4095.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>QualityOfService=</varname></term>
-        <listitem>
-          <para>Specifies quality of service of the virtual function. Takes an integer in the range
-          1…4294967294.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>VLANProtocol=</varname></term>
-        <listitem>
-          <para>Specifies VLAN protocol of the virtual function. Takes <literal>802.1Q</literal> or
-          <literal>802.1ad</literal>.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>MACSpoofCheck=</varname></term>
-        <listitem>
-          <para>Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will
-          be used.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>QueryReceiveSideScaling=</varname></term>
-        <listitem>
-          <para>Takes a boolean. Toggle the ability of querying the receive side scaling (RSS)
-          configuration of the virtual function (VF). The VF RSS information like RSS hash key may be
-          considered sensitive on some devices where this information is shared between VF and the
-          physical function (PF). When unset, the kernel's default will be used.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>Trust=</varname></term>
-        <listitem>
-          <para>Takes a boolean. Allows one to set trust mode of the virtual function (VF). When set, VF
-          users can set a specific feature which may impact security and/or performance. When unset,
-          the kernel's default will be used.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>LinkState=</varname></term>
-        <listitem>
-          <para>Allows one to set the link state of the virtual function (VF). Takes a boolean or a
-          special value <literal>auto</literal>. Setting to <literal>auto</literal> means a
-          reflection of the physical function (PF) link state, <literal>yes</literal> lets the VF to
-          communicate with other VFs on this host even if the PF link state is down,
-          <literal>no</literal> causes the hardware to drop any packets sent by the VF. When unset,
-          the kernel's default will be used.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>MACAddress=</varname></term>
-        <listitem>
-          <para>Specifies the MAC address for the virtual function.</para>
-        </listitem>
-      </varlistentry>
-    </variablelist>
-  </refsect1>
+  <xi:include href="systemd.link.xml" xpointer="sr-iov" />
 
   <refsect1>
     <title>[Network] Section Options</title>
@@ -4497,22 +4410,48 @@ DHCP=yes</programlisting>
     </example>
 
     <example>
-      <title>IPv6 Prefix Delegation</title>
+      <title>IPv6 Prefix Delegation (DHCPv6 PD)</title>
 
-      <programlisting># /etc/systemd/network/55-ipv6-pd-upstream.network
+      <programlisting># /etc/systemd/network/55-dhcpv6-pd-upstream.network
 [Match]
 Name=enp1s0
 
 [Network]
-DHCP=ipv6</programlisting>
+DHCP=ipv6
+
+# The below setting is optional, to also assign an address in the delegated prefix
+# to the upstream interface. If not necessary, then comment out the line below and
+# the [DHCPPrefixDelegation] section.
+DHCPPrefixDelegation=yes
+
+# If the upstream network provides Router Advertisement with Managed bit set,
+# then comment out the line below and WithoutRA= setting in the [DHCPv6] section.
+IPv6AcceptRA=no
 
-      <programlisting># /etc/systemd/network/56-ipv6-pd-downstream.network
+[DHCPv6]
+WithoutRA=solicit
+
+[DHCPPrefixDelegation]
+UplinkInterface=:self
+SubnetId=0
+Announce=no</programlisting>
+
+      <programlisting># /etc/systemd/network/55-dhcpv6-pd-downstream.network
 [Match]
 Name=enp2s0
 
 [Network]
+DHCPPrefixDelegation=yes
 IPv6SendRA=yes
-DHCPPrefixDelegation=yes</programlisting>
+
+# It is expected that the host is acting as a router. So, usually it is not
+# necessary to receive Router Advertisement from other hosts in the downstream network.
+IPv6AcceptRA=no
+
+[DHCPPrefixDelegation]
+UplinkInterface=enp1s0
+SubnetId=1
+Announce=yes</programlisting>
 
       <para>This will enable DHCPv6-PD on the interface enp1s0 as an upstream interface where the
       DHCPv6 client is running and enp2s0 as a downstream interface where the prefix is delegated to.
@@ -4520,6 +4459,46 @@ DHCPPrefixDelegation=yes</programlisting>
       </para>
     </example>
 
+    <example>
+      <title>IPv6 Prefix Delegation (DHCPv4 6RD)</title>
+
+      <programlisting># /etc/systemd/network/55-dhcpv4-6rd-upstream.network
+[Match]
+Name=enp1s0
+
+[Network]
+DHCP=ipv4
+
+# When DHCPv4-6RD is used, the upstream network does not support IPv6.
+# Hence, it is not necessary to wait for Router Advertisement, which is enabled by default.
+IPv6AcceptRA=no
+
+[DHCPv4]
+Use6RD=yes</programlisting>
+
+      <programlisting># /etc/systemd/network/55-dhcpv4-6rd-downstream.network
+[Match]
+Name=enp2s0
+
+[Network]
+DHCPPrefixDelegation=yes
+IPv6SendRA=yes
+
+# It is expected that the host is acting as a router. So, usually it is not
+# necessary to receive Router Advertisement from other hosts in the downstream network.
+IPv6AcceptRA=no
+
+[DHCPPrefixDelegation]
+UplinkInterface=enp1s0
+SubnetId=1
+Announce=yes</programlisting>
+
+      <para>This will enable DHCPv4-6RD on the interface enp1s0 as an upstream interface where the
+      DHCPv4 client is running and enp2s0 as a downstream interface where the prefix is delegated to.
+      The delegated prefixes are distributed by IPv6 Router Advertisement on the downstream network.
+      </para>
+    </example>
+
     <example>
       <title>A bridge with two enslaved links</title>
 
index 2a44b8cfd8ebd0fd4c8b43fe597dcab6309e8e60..0b0a66713f519085c594875d6d7ff3c6be9682c6 100644 (file)
             <entry><filename>$XDG_RUNTIME_DIR/systemd/user.control</filename></entry>
           </row>
           <row>
-            <entry><filename>/run/systemd/transient</filename></entry>
+            <entry><filename>$XDG_RUNTIME_DIR/systemd/transient</filename></entry>
             <entry>Dynamic configuration for transient units</entry>
           </row>
           <row>
-            <entry><filename>/run/systemd/generator.early</filename></entry>
+            <entry><filename>$XDG_RUNTIME_DIR/systemd/generator.early</filename></entry>
             <entry>Generated units with high priority (see <replaceable>early-dir</replaceable> in <citerefentry
             ><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>)</entry>
           </row>
@@ -2098,6 +2098,16 @@ Note that this setting is <emphasis>not</emphasis> influenced by the <varname>Us
           <xi:include href="standard-specifiers.xml" xpointer="V"/>
           <xi:include href="standard-specifiers.xml" xpointer="w"/>
           <xi:include href="standard-specifiers.xml" xpointer="W"/>
+          <row>
+            <entry><literal>%y</literal></entry>
+            <entry>The path to the fragment</entry>
+            <entry>This is the path where the main part of the unit file is located. For linked unit files, the real path outside of the unit search directories is used. For units that don't have a fragment file, this specifier will raise an error.</entry>
+          </row>
+          <row>
+            <entry><literal>%Y</literal></entry>
+            <entry>The directory of the fragment</entry>
+            <entry>This is the directory part of <literal>%y</literal>.</entry>
+          </row>
           <xi:include href="standard-specifiers.xml" xpointer="percent"/>
         </tbody>
       </tgroup>
index 910862ed367bcc337a97966ab0daa0480b622bbd..b8b01e0deb7a4b2c62e7270177c464f17ce34b61 100644 (file)
@@ -250,7 +250,6 @@ conf.set_quoted('SYSTEMD_LANGUAGE_FALLBACK_MAP',              pkgdatadir / 'lang
 conf.set_quoted('SYSTEMD_MAKEFS_PATH',                        rootlibexecdir / 'systemd-makefs')
 conf.set_quoted('SYSTEMD_PULL_PATH',                          rootlibexecdir / 'systemd-pull')
 conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH',               rootlibexecdir / 'systemd-shutdown')
-conf.set_quoted('SYSTEMD_STDIO_BRIDGE_BINARY_PATH',           bindir / 'systemd-stdio-bridge')
 conf.set_quoted('SYSTEMD_TEST_DATA',                          testsdir / 'testdata')
 conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', rootbindir / 'systemd-tty-ask-password-agent')
 conf.set_quoted('SYSTEMD_UPDATE_HELPER_PATH',                 rootlibexecdir / 'systemd-update-helper')
@@ -600,10 +599,8 @@ env = find_program('env')
 perl = find_program('perl', required : false)
 rsync = find_program('rsync', required : false)
 meson_make_symlink = project_source_root + '/tools/meson-make-symlink.sh'
-test_efi_create_disk_sh = find_program('test/test-efi-create-disk.sh')
 
 mkdir_p = 'mkdir -p $DESTDIR/@0@'
-splash_bmp = files('test/splash.bmp')
 
 # If -Dxxx-path option is found, use that. Otherwise, check in $PATH,
 # /usr/sbin, /sbin, and fall back to the default from middle column.
@@ -985,29 +982,66 @@ bpf_framework_required = want_bpf_framework == 'true'
 libbpf = dependency('libbpf', required : bpf_framework_required, version : '>= 0.2')
 conf.set10('HAVE_LIBBPF', libbpf.found())
 
-if want_bpf_framework == 'false'
+if want_bpf_framework == 'false' or not libbpf.found()
         conf.set10('BPF_FRAMEWORK', 0)
 else
         # Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu
         # (like clang-10/llvm-strip-10)
-        clang_bin = cc.get_id() == 'clang' ? cc.cmd_array()[0] : 'clang'
-        if meson.is_cross_build() or clang_bin.contains('afl-clang') or clang_bin.contains('hfuzz-clang')
-                clang_bin = 'clang'
+        if meson.is_cross_build() or cc.get_id() != 'clang' or cc.cmd_array()[0].contains('afl-clang') or cc.cmd_array()[0].contains('hfuzz-clang')
+                r = find_program('clang', required : bpf_framework_required, version : '>= 10.0.0')
+                clang_found = r.found()
+                if clang_found
+                        if meson.version().version_compare('>= 0.55')
+                                clang = [r.full_path()]
+                        else
+                                clang = [r.path()]
+                        endif
+                endif
+                # Assume that the required flags are supported by the found clang.
+                clang_supports_flags = clang_found
+        else
+                clang_found = true
+                clang = cc.cmd_array()
+                clang_supports_flags = cc.has_argument('-Wno-compare-distinct-pointer-types')
         endif
-        clang = find_program(clang_bin, required : bpf_framework_required)
-        if not meson.is_cross_build() and clang.found()
-                llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
-                                             check : true).stdout().strip()
+
+        if clang_found
+                # Check if 'clang -target bpf' is supported.
+                clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus', check : false).returncode() == 0
         else
-                llvm_strip_bin = 'llvm-strip'
+                clang_supports_bpf = false
         endif
-        llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required)
 
         # Debian installs this in /usr/sbin/ which is not in $PATH.
         # We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
-        bpftool = find_program('bpftool', '/usr/sbin/bpftool', required : bpf_framework_required)
+        # We use 'bpftool gen' subcommand, it was added by 985ead416df39d6fe8e89580cc1db6aa273e0175 (v5.6).
+        bpftool = find_program('bpftool',
+                               '/usr/sbin/bpftool',
+                               required : false,
+                               version : '>= 5.13.0')
+
+        if bpftool.found()
+                bpftool_strip = true
+        else
+                bpftool_strip = false
+                bpftool = find_program('bpftool',
+                                       '/usr/sbin/bpftool',
+                                       required : bpf_framework_required,
+                                       version : '>= 5.6.0')
+        endif
+
+        if not bpftool_strip
+                if not meson.is_cross_build() and clang_found
+                        llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
+                                                     check : true).stdout().strip()
+                else
+                        llvm_strip_bin = 'llvm-strip'
+                endif
+                llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required, version : '>= 10.0.0')
+        endif
+
+        deps_found = clang_found and clang_supports_bpf and clang_supports_flags and (bpftool_strip or llvm_strip.found()) and bpftool.found()
 
-        deps_found = libbpf.found() and clang.found() and llvm_strip.found() and bpftool.found()
         # Can build BPF program from source code in restricted C
         conf.set10('BPF_FRAMEWORK', deps_found)
 endif
@@ -1741,6 +1775,7 @@ conf.set10('SYSTEMD_SLOW_TESTS_DEFAULT', slow_tests)
 
 tests = []
 fuzzers = []
+catalogs = []
 
 ############################################################
 
@@ -2048,7 +2083,8 @@ foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'],
                         test('dlopen-nss_' + module,
                              test_dlopen,
                              # path to dlopen must include a slash
-                             args : nss.full_path())
+                             args : nss.full_path(),
+                             depends : nss)
                 endif
         endif
 endforeach
@@ -2162,7 +2198,8 @@ if want_tests != 'false'
         test('test-fstab-generator',
              test_fstab_generator_sh,
              # https://github.com/mesonbuild/meson/issues/2681
-             args : exe.full_path())
+             args : exe.full_path(),
+             depends : exe)
 endif
 
 if conf.get('ENABLE_ENVIRONMENT_D') == 1
@@ -2313,7 +2350,8 @@ if conf.get('ENABLE_LOGIND') == 1
                         test('dlopen-pam_systemd',
                              test_dlopen,
                              # path to dlopen must include a slash
-                             args : pam_systemd.full_path())
+                             args : pam_systemd.full_path(),
+                             depends : pam_systemd)
                 endif
         endif
 
@@ -2652,7 +2690,7 @@ if conf.get('HAVE_LIBCRYPTSETUP') == 1
 endif
 
 if conf.get('HAVE_SYSV_COMPAT') == 1
-        executable(
+        exe = executable(
                 'systemd-sysv-generator',
                 'src/sysv-generator/sysv-generator.c',
                 include_directories : includes,
@@ -2661,6 +2699,13 @@ if conf.get('HAVE_SYSV_COMPAT') == 1
                 install : true,
                 install_dir : systemgeneratordir)
 
+        sysv_generator_test_py = find_program('test/sysv-generator-test.py')
+        if want_tests != 'false'
+                test('sysv-generator-test',
+                     sysv_generator_test_py,
+                     depends : exe)
+        endif
+
         executable(
                 'systemd-rc-local-generator',
                 'src/rc-local-generator/rc-local-generator.c',
@@ -3544,7 +3589,8 @@ if want_tests != 'false'
         test('test-network-generator-conversion',
              test_network_generator_conversion_sh,
              # https://github.com/mesonbuild/meson/issues/2681
-             args : exe.full_path())
+             args : exe.full_path(),
+             depends : exe)
 endif
 
 executable(
@@ -3558,13 +3604,14 @@ executable(
 
 ############################################################
 
-custom_target(
+runtest_env = custom_target(
         'systemd-runtest.env',
         output : 'systemd-runtest.env',
         command : [sh, '-c',
                    '{ echo SYSTEMD_TEST_DATA=@0@; echo SYSTEMD_CATALOG_DIR=@1@; } >@OUTPUT@'.format(
                            project_source_root / 'test',
                            project_build_root / 'catalog')],
+        depends : catalogs,
         build_by_default : true)
 
 test_cflags = ['-DTEST_CODE=1']
@@ -3607,7 +3654,8 @@ foreach tuple : tests
                         build_by_default : want_tests != 'false',
                         install_rpath : rootlibexecdir,
                         install : install_tests,
-                        install_dir : testsdir / type)
+                        install_dir : testsdir / type,
+                        link_depends : runtest_env)
 
                 if type == 'manual'
                         message('@0@ is a manual test'.format(name))
@@ -3800,7 +3848,8 @@ foreach exec : public_programs
         if want_tests != 'false'
                 test('check-help-' + name,
                      check_help,
-                     args : exec.full_path())
+                     args : exec.full_path(),
+                     depends: exec)
         endif
 endforeach
 
@@ -3877,7 +3926,7 @@ if git.found()
                         command : [env, 'etags', '-o', '@0@/TAGS'.format(project_source_root)] + all_files)
                 run_target(
                         'ctags',
-                        command : [env, 'ctags', '-o', '@0@/tags'.format(project_source_root)] + all_files)
+                        command : [env, 'ctags', '--tag-relative=never', '-o', '@0@/tags'.format(project_source_root)] + all_files)
         endif
 endif
 
index 2be71f3c7f6daf17a08e0a784ff2cba319998111..d35b1d72a64265d0a51868ac0407117242028e03 100644 (file)
@@ -5,7 +5,7 @@
 
 [Distribution]
 Distribution=debian
-Release=unstable
+Release=testing
 
 [Packages]
 BuildPackages=
index 685b59a78aea2461615d4dc0740654003ebd92b6..9cdaadb4cd5f4be9e3475fbd9f358ef725bd05e4 100644 (file)
@@ -105,7 +105,13 @@ SUBSYSTEM=="ptp", ATTR{clock_name}=="KVM virtual PTP", SYMLINK += "ptp_kvm"
 
 SUBSYSTEM=="ptp", ATTR{clock_name}=="hyperv", SYMLINK += "ptp_hyperv"
 
-SUBSYSTEM=="dmi", ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="", ENV{ID_VENDOR}="$attr{sys_vendor}", ENV{ID_MODEL}="$attr{product_name}"
-SUBSYSTEM=="dmi", ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="product_version", ENV{ID_VENDOR}="$attr{sys_vendor}", ENV{ID_MODEL}="$attr{product_version}"
+SUBSYSTEM!="dmi", GOTO="dmi_end"
+ENV{ID_VENDOR}="$attr{sys_vendor}"
+ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="|product_name", ENV{ID_MODEL}="$attr{product_name}"
+ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="product_version", ENV{ID_MODEL}="$attr{product_version}"
+# fallback to board information
+ENV{ID_VENDOR}=="", ENV{ID_VENDOR}="$attr{board_vendor}"
+ENV{ID_MODEL}=="", ENV{ID_MODEL}="$attr{board_name}"
+LABEL="dmi_end"
 
 LABEL="default_end"
index 08c8c445105a1cb9edd0cf10e38c06f57d7250d9..e8c5357f9146117aeb38c1c96e7f0c717d52bd37 100644 (file)
@@ -40,7 +40,7 @@ ceph,            {CEPH_SUPER_MAGIC}
 cgroup2,         {CGROUP2_SUPER_MAGIC}
 # note that the cgroupfs magic got reassigned from cpuset
 cgroup,          {CGROUP_SUPER_MAGIC}
-cifs,            {CIFS_MAGIC_NUMBER}
+cifs,            {CIFS_SUPER_MAGIC, SMB2_SUPER_MAGIC}
 coda,            {CODA_SUPER_MAGIC}
 configfs,        {CONFIGFS_MAGIC}
 cramfs,          {CRAMFS_MAGIC}
@@ -109,7 +109,7 @@ selinuxfs,       {SELINUX_MAGIC}
 shiftfs,         {SHIFTFS_MAGIC}
 smackfs,         {SMACK_MAGIC}
 # smb3 is an alias for cifs
-smb3,            {CIFS_MAGIC_NUMBER}
+smb3,            {CIFS_SUPER_MAGIC}
 # smbfs was removed from the kernel in 2010, the magic remains
 smbfs,           {SMB_SUPER_MAGIC}
 sockfs,          {SOCKFS_MAGIC}
index 1e2bec16468cbc3614b9bb0955cfdeb7c903c293..0d927bfce976a8e122d5bfa35b08b92b0d4938a2 100644 (file)
@@ -273,9 +273,11 @@ int log_emergency_level(void);
         })
 
 #if LOG_TRACE
-#  define log_trace(...) log_debug(__VA_ARGS__)
+#  define log_trace(...)          log_debug(__VA_ARGS__)
+#  define log_trace_errno(...)    log_debug_errno(__VA_ARGS__)
 #else
-#  define log_trace(...) do {} while (0)
+#  define log_trace(...)          do {} while (0)
+#  define log_trace_errno(e, ...) (-ERRNO_VALUE(e))
 #endif
 
 /* Structured logging */
index 7d9320bb6dc9bff0faf131b3b786d907b4e25075..c104fcfba3151a528f05514b091100c9c0a3f4bc 100644 (file)
 #define XFS_SB_MAGIC 0x58465342
 #endif
 
-/* Not exposed yet. Defined at fs/cifs/cifsglob.h */
-#ifndef CIFS_MAGIC_NUMBER
-#define CIFS_MAGIC_NUMBER 0xFF534D42
+/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
+#ifndef CIFS_SUPER_MAGIC
+#define CIFS_SUPER_MAGIC 0xFF534D42
+#endif
+
+/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
+#ifndef SMB2_SUPER_MAGIC
+#define SMB2_SUPER_MAGIC 0xFE534D42
 #endif
 
 /* 257f871993474e2bde6c497b54022c362cf398e1 (4.5) */
index 6fb8c40e7a57eef683d4003d1785bda9920c319e..921a30cef78be2fc961e9458773b148f54f87a3a 100644 (file)
@@ -238,7 +238,7 @@ static int acquire_generator_dirs(
                 char **generator_early,
                 char **generator_late) {
 
-        _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
+        _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *p = NULL;
         const char *prefix;
 
         assert(generator);
@@ -261,7 +261,11 @@ static int acquire_generator_dirs(
                 if (!e)
                         return -ENXIO;
 
-                prefix = strjoina(e, "/systemd");
+                p = path_join(e, "/systemd");
+                if (!p)
+                        return -ENOMEM;
+
+                prefix = p;
         }
 
         x = path_join(prefix, "generator");
index 1bcb4d16899b0e090874e344e8820723574e56dd..86391b8def633fde65d5cc489765ad898155cf73 100644 (file)
@@ -1603,7 +1603,7 @@ static int verb_list(int argc, char *argv[], void *userdata) {
         else if (r < 0)
                 log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
         else
-                (void) boot_entries_augment_from_loader(&config, efi_entries, false);
+                (void) boot_entries_augment_from_loader(&config, efi_entries);
 
         if (config.n_entries == 0)
                 log_info("No boot loader entries found.");
index f49bd9e5f856ef8404d331ca693cf126561b9616..eed5bcc9610add3cbcb77e121a95537d10e26ddd 100644 (file)
@@ -61,7 +61,6 @@ typedef struct {
         CHAR16 *options;
         CHAR16 key;
         EFI_STATUS (*call)(void);
-        BOOLEAN non_unique;
         UINTN tries_done;
         UINTN tries_left;
         CHAR16 *path;
@@ -439,6 +438,7 @@ static void ps_bool(const CHAR16 *fmt, BOOLEAN value) {
 static void print_status(Config *config, CHAR16 *loaded_image_path) {
         UINT64 key;
         UINTN x_max, y_max;
+        UINT32 screen_width = 0, screen_height = 0;
         SecureBootMode secure;
         _cleanup_freepool_ CHAR16 *device_part_uuid = NULL;
 
@@ -447,6 +447,7 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) {
 
         clear_screen(COLOR_NORMAL);
         console_query_mode(&x_max, &y_max);
+        query_screen_resolution(&screen_width, &screen_height);
 
         secure = secure_boot_mode();
         (void) efivar_get(LOADER_GUID, L"LoaderDevicePartUUID", &device_part_uuid);
@@ -464,7 +465,7 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) {
             Print(L"           secure boot: %s (%s)\n", yes_no(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)), secure_boot_mode_to_string(secure));
           ps_bool(L"                  shim: %s\n",      shim_loaded());
           ps_bool(L"                   TPM: %s\n",      tpm_present());
-            Print(L"          console mode: %d/%d (%lu x %lu)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - 1LL, x_max, y_max);
+            Print(L"          console mode: %d/%d (%lux%lu @%ux%u)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - 1LL, x_max, y_max, screen_width, screen_height);
 
         Print(L"\n--- Press any key to continue. ---\n\n");
         console_key_read(&key, UINT64_MAX);
@@ -1717,88 +1718,84 @@ static void config_default_entry_select(Config *config) {
                 config->timeout_sec = 10;
 }
 
-static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
-        BOOLEAN non_unique = FALSE;
+static BOOLEAN entries_unique(ConfigEntry **entries, BOOLEAN *unique, UINTN entry_count) {
+        BOOLEAN is_unique = TRUE;
 
         assert(entries);
+        assert(unique);
 
         for (UINTN i = 0; i < entry_count; i++)
-                entries[i]->non_unique = FALSE;
-
-        for (UINTN i = 0; i < entry_count; i++)
-                for (UINTN k = 0; k < entry_count; k++) {
-                        if (i == k)
-                                continue;
+                for (UINTN k = i + 1; k < entry_count; k++) {
                         if (StrCmp(entries[i]->title_show, entries[k]->title_show) != 0)
                                 continue;
 
-                        non_unique = entries[i]->non_unique = entries[k]->non_unique = TRUE;
+                        is_unique = unique[i] = unique[k] = FALSE;
                 }
 
-        return non_unique;
+        return is_unique;
 }
 
 /* generate a unique title, avoiding non-distinguishable menu entries */
 static void config_title_generate(Config *config) {
         assert(config);
 
+        BOOLEAN unique[config->entry_count];
+
         /* set title */
         for (UINTN i = 0; i < config->entry_count; i++) {
-                FreePool(config->entries[i]->title_show);
-                config->entries[i]->title_show = xstrdup(
-                                config->entries[i]->title ?: config->entries[i]->id);
+                assert(!config->entries[i]->title_show);
+                unique[i] = TRUE;
+                config->entries[i]->title_show = xstrdup(config->entries[i]->title ?: config->entries[i]->id);
         }
 
-        if (!find_nonunique(config->entries, config->entry_count))
+        if (entries_unique(config->entries, unique, config->entry_count))
                 return;
 
         /* add version to non-unique titles */
         for (UINTN i = 0; i < config->entry_count; i++) {
-                CHAR16 *s;
-
-                if (!config->entries[i]->non_unique)
+                if (unique[i])
                         continue;
+
+                unique[i] = TRUE;
+
                 if (!config->entries[i]->version)
                         continue;
 
-                s = xpool_print(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version);
-                FreePool(config->entries[i]->title_show);
-                config->entries[i]->title_show = s;
+                _cleanup_freepool_ CHAR16 *t = config->entries[i]->title_show;
+                config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->version);
         }
 
-        if (!find_nonunique(config->entries, config->entry_count))
+        if (entries_unique(config->entries, unique, config->entry_count))
                 return;
 
         /* add machine-id to non-unique titles */
         for (UINTN i = 0; i < config->entry_count; i++) {
-                CHAR16 *s;
-                _cleanup_freepool_ CHAR16 *m = NULL;
-
-                if (!config->entries[i]->non_unique)
+                if (unique[i])
                         continue;
+
+                unique[i] = TRUE;
+
                 if (!config->entries[i]->machine_id)
                         continue;
 
-                m = xstrdup(config->entries[i]->machine_id);
-                m[8] = '\0';
-                s = xpool_print(L"%s (%s)", config->entries[i]->title_show, m);
-                FreePool(config->entries[i]->title_show);
-                config->entries[i]->title_show = s;
+                _cleanup_freepool_ CHAR16 *t = config->entries[i]->title_show;
+                config->entries[i]->title_show = xpool_print(
+                        L"%s (%.*s)",
+                        t,
+                        StrnLen(config->entries[i]->machine_id, 8),
+                        config->entries[i]->machine_id);
         }
 
-        if (!find_nonunique(config->entries, config->entry_count))
+        if (entries_unique(config->entries, unique, config->entry_count))
                 return;
 
         /* add file name to non-unique titles */
         for (UINTN i = 0; i < config->entry_count; i++) {
-                CHAR16 *s;
-
-                if (!config->entries[i]->non_unique)
+                if (unique[i])
                         continue;
-                s = xpool_print(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->id);
-                FreePool(config->entries[i]->title_show);
-                config->entries[i]->title_show = s;
-                config->entries[i]->non_unique = FALSE;
+
+                _cleanup_freepool_ CHAR16 *t = config->entries[i]->title_show;
+                config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->id);
         }
 }
 
index b8142c38b3f65e0adc20adad99358599f98c1743..3a0cf535e15c995e878b626057628febb545ca2d 100644 (file)
@@ -183,19 +183,32 @@ static EFI_STATUS change_mode(INT64 mode) {
         return err;
 }
 
-static INT64 get_auto_mode(void) {
-        EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
+EFI_STATUS query_screen_resolution(UINT32 *ret_w, UINT32 *ret_h) {
         EFI_STATUS err;
+        EFI_GRAPHICS_OUTPUT_PROTOCOL *go;
+
+        err = LibLocateProtocol(&GraphicsOutputProtocol, (void **) &go);
+        if (EFI_ERROR(err))
+                return err;
+
+        if (!go->Mode || !go->Mode->Info)
+                return EFI_DEVICE_ERROR;
+
+        *ret_w = go->Mode->Info->HorizontalResolution;
+        *ret_h = go->Mode->Info->VerticalResolution;
+        return EFI_SUCCESS;
+}
+
+static INT64 get_auto_mode(void) {
+        UINT32 screen_width, screen_height;
 
-        err = LibLocateProtocol(&GraphicsOutputProtocol, (void **)&GraphicsOutput);
-        if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
-                EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info = GraphicsOutput->Mode->Info;
+        if (!EFI_ERROR(query_screen_resolution(&screen_width, &screen_height))) {
                 BOOLEAN keep = FALSE;
 
                 /* Start verifying if we are in a resolution larger than Full HD
                  * (1920x1080). If we're not, assume we're in a good mode and do not
                  * try to change it. */
-                if (Info->HorizontalResolution <= HORIZONTAL_MAX_OK && Info->VerticalResolution <= VERTICAL_MAX_OK)
+                if (screen_width <= HORIZONTAL_MAX_OK && screen_height <= VERTICAL_MAX_OK)
                         keep = TRUE;
                 /* For larger resolutions, calculate the ratio of the total screen
                  * area to the text viewport area. If it's less than 10 times bigger,
@@ -203,7 +216,7 @@ static INT64 get_auto_mode(void) {
                 else {
                         UINT64 text_area;
                         UINTN x_max, y_max;
-                        UINT64 screen_area = (UINT64)Info->HorizontalResolution * (UINT64)Info->VerticalResolution;
+                        UINT64 screen_area = (UINT64)screen_width * (UINT64)screen_height;
 
                         console_query_mode(&x_max, &y_max);
                         text_area = SYSTEM_FONT_WIDTH * SYSTEM_FONT_HEIGHT * (UINT64)x_max * (UINT64)y_max;
index 90086028c04142255822e13901048f66a08b8f00..c27c19b39fb53c983fb889b94bc440694f0f3cf2 100644 (file)
@@ -29,3 +29,4 @@ enum {
 EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec);
 EFI_STATUS console_set_mode(INT64 mode);
 EFI_STATUS console_query_mode(UINTN *x_max, UINTN *y_max);
+EFI_STATUS query_screen_resolution(UINT32 *ret_width, UINT32 *ret_height);
index fad92f09a1cf7e43f1e9f9de860b516eb9a4e288..d23352d48e54d73787b8df833f90cf9eb277dfea 100644 (file)
@@ -195,7 +195,7 @@ efi_cflags += cc.get_supported_arguments({
 if get_option('werror')
         efi_cflags += ['-Werror']
 endif
-if get_option('debug')
+if get_option('debug') and get_option('mode') == 'developer'
         efi_cflags += ['-ggdb', '-DEFI_DEBUG']
 endif
 if get_option('optimization') != '0'
@@ -210,7 +210,19 @@ if get_option('b_lto')
 endif
 
 foreach arg : get_option('c_args')
-        if arg in ['-Werror', '-g', '-ggdb', '-O1', '-O2', '-O3', '-Og', '-Os', '-DNDEBUG', '-flto', '-fno-lto']
+        if arg in [
+                '-DNDEBUG',
+                '-fno-lto',
+                '-O1', '-O2', '-O3', '-Og', '-Os',
+                '-Werror',
+           ] or arg.split('=')[0] in [
+                '-ffile-prefix-map',
+                '-flto',
+           ] or (get_option('mode') == 'developer' and arg in [
+                '-DEFI_DEBUG',
+                '-g', '-ggdb',
+           ])
+
                 message('Using "@0@" from c_args for EFI compiler'.format(arg))
                 efi_cflags += arg
         endif
@@ -244,17 +256,11 @@ else
 endif
 
 if efi_arch[1] == 'arm'
-        # On arm, the compiler (correctly) gives us the following warning:
-        #    libgcc.a(_popcountsi2.o) uses 4-byte wchar_t yet the output is to
-        #    use 2-byte wchar_t; use of wchar_t values across objects may fail
-        #
-        # libgcc does not have any occurrences of wchar_t in its sources or the
-        # documentation, so it's safe to assume that we can ignore this warning.
-        #
-        # So far, this only happens with arm due to popcount even though x86 and
-        # x86_64 also have to rely on libgcc's popcount. Therefore, we only disable
-        # this for arm to make sure this doesn't mask other issues in the future.
-        efi_ldflags += ['-Wl,--no-warn-mismatch']
+        # On arm, the compiler (correctly) warns about wchar_t size mismatch. This
+        # is because libgcc is not compiled with -fshort-wchar, but it does not
+        # have any occurrences of wchar_t in its sources or the documentation, so
+        # it is safe to assume that we can ignore this warning.
+        efi_ldflags += ['-Wl,--no-wchar-size-warning']
 endif
 
 if run_command('grep', '-q', '__CTOR_LIST__', efi_lds, check: false).returncode() == 0
@@ -307,11 +313,13 @@ efi_headers = files(
         'disk.h',
         'drivers.h',
         'graphics.h',
+        'initrd.h',
         'linux.h',
         'measure.h',
         'missing_efi.h',
         'pe.h',
         'random-seed.h',
+        'secure-boot.h',
         'shim.h',
         'splash.h',
         'util.h',
@@ -383,25 +391,27 @@ foreach file : fundamental_source_paths + common_sources + systemd_boot_sources
         endif
 endforeach
 
-systemd_boot_efi_name = 'systemd-boot@0@.efi'.format(efi_arch[0])
-stub_elf_name = 'linux@0@.elf.stub'.format(efi_arch[0])
-stub_efi_name = 'linux@0@.efi.stub'.format(efi_arch[0])
-
-efi_stubs = []
-foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects, false],
-                 [stub_elf_name, stub_efi_name, stub_objects, true]]
-        so = custom_target(
-                tuple[0],
-                input : tuple[2],
-                output : tuple[0],
-                command : [cc.cmd_array(), '-o', '@OUTPUT@', efi_ldflags, efi_cflags, tuple[2], '-lefi', '-lgnuefi', '-lgcc'],
-                install : tuple[3],
+foreach tuple : [['systemd-boot@0@.@1@', systemd_boot_objects, false],
+                 ['linux@0@.@1@.stub', stub_objects, true]]
+        elf = custom_target(
+                tuple[0].format(efi_arch[0], 'elf'),
+                input : tuple[1],
+                output : tuple[0].format(efi_arch[0], 'elf'),
+                command : [cc.cmd_array(),
+                           '-o', '@OUTPUT@',
+                           efi_cflags,
+                           efi_ldflags,
+                           '@INPUT@',
+                           '-lefi',
+                           '-lgnuefi',
+                           '-lgcc'],
+                install : tuple[2],
                 install_dir : bootlibdir)
 
-        stub = custom_target(
-                tuple[1],
-                input : so,
-                output : tuple[1],
+        custom_target(
+                tuple[0].format(efi_arch[0], 'efi'),
+                input : elf,
+                output : tuple[0].format(efi_arch[0], 'efi'),
                 command : [objcopy,
                            '-j', '.bss*',
                            '-j', '.data',
@@ -418,14 +428,4 @@ foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects
                            '@INPUT@', '@OUTPUT@'],
                 install : true,
                 install_dir : bootlibdir)
-
-        efi_stubs += [[so, stub]]
 endforeach
-
-############################################################
-
-test_efi_disk_img = custom_target(
-        'test-efi-disk.img',
-        input : [efi_stubs[0][0], efi_stubs[1][1]],
-        output : 'test-efi-disk.img',
-        command : [test_efi_create_disk_sh, '@OUTPUT@','@INPUT@', splash_bmp])
index e023b97d2f0915b436b83d5a1f0f688a606afda0..257646551c52d52fe45fd0ac90d27808a2a2b22d 100644 (file)
@@ -455,7 +455,7 @@ EFI_STATUS file_read(EFI_FILE *dir, const CHAR16 *name, UINTN off, UINTN size, C
                 if (EFI_ERROR(err))
                         return err;
 
-                size = info->FileSize+1;
+                size = info->FileSize;
         }
 
         if (off > 0) {
@@ -464,12 +464,16 @@ EFI_STATUS file_read(EFI_FILE *dir, const CHAR16 *name, UINTN off, UINTN size, C
                         return err;
         }
 
-        buf = xallocate_pool(size + 1);
+        /* Allocate some extra bytes to guarantee the result is NUL-terminated for CHAR8 and CHAR16 strings. */
+        UINTN extra = size % sizeof(CHAR16) + sizeof(CHAR16);
+
+        buf = xallocate_pool(size + extra);
         err = handle->Read(handle, &size, buf);
         if (EFI_ERROR(err))
                 return err;
 
-        buf[size] = '\0';
+        /* Note that handle->Read() changes size to reflect the actually bytes read. */
+        ZeroMem(buf + size, extra);
 
         *ret = TAKE_PTR(buf);
         if (ret_size)
index b60814b4919e87789a17b020ee9c7d569d3baa40..40ce98c6110893150a7da32056732f27283f5dce 100644 (file)
 #define UINT64_MAX ((UINT64) -1)
 #endif
 
-#define assert_alloc_ret(p)     \
-        ({                      \
-                void *_p = (p); \
-                assert(_p);     \
-                _p;             \
-        })
-
 #define xnew_alloc(type, n, alloc)                                           \
         ({                                                                   \
                 UINTN _alloc_size;                                           \
                 (type *) alloc(_alloc_size);                                 \
         })
 
-#define xallocate_pool(size) assert_alloc_ret(AllocatePool(size))
-#define xallocate_zero_pool(size) assert_alloc_ret(AllocateZeroPool(size))
-#define xreallocate_pool(p, old_size, new_size) assert_alloc_ret(ReallocatePool((p), (old_size), (new_size)))
-#define xpool_print(fmt, ...) ((CHAR16 *) assert_alloc_ret(PoolPrint((fmt), ##__VA_ARGS__)))
-#define xstrdup(str) ((CHAR16 *) assert_alloc_ret(StrDuplicate(str)))
+#define xallocate_pool(size) ASSERT_PTR(AllocatePool(size))
+#define xallocate_zero_pool(size) ASSERT_PTR(AllocateZeroPool(size))
+#define xreallocate_pool(p, old_size, new_size) ASSERT_PTR(ReallocatePool((p), (old_size), (new_size)))
+#define xpool_print(fmt, ...) ((CHAR16 *) ASSERT_PTR(PoolPrint((fmt), ##__VA_ARGS__)))
+#define xstrdup(str) ((CHAR16 *) ASSERT_PTR(StrDuplicate(str)))
 #define xnew(type, n) xnew_alloc(type, (n), xallocate_pool)
 #define xnew0(type, n) xnew_alloc(type, (n), xallocate_zero_pool)
 
index 4972877d209728a989fdf7f32696c1fcf0427f72..49d5707e02f40ebee8366ab7fac87c6d0c309489 100644 (file)
@@ -12,20 +12,17 @@ union GptHeaderBuffer {
         uint8_t space[CONST_ALIGN_TO(sizeof(EFI_PARTITION_TABLE_HEADER), 512)];
 };
 
-static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
-        EFI_DEVICE_PATH *parent;
-        UINTN len;
-
+static EFI_DEVICE_PATH *path_chop(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) {
         assert(path);
         assert(node);
 
-        len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
-        parent = (EFI_DEVICE_PATH*) xallocate_pool(len + sizeof(EFI_DEVICE_PATH));
+        UINTN len = (UINT8 *) node - (UINT8 *) path;
+        EFI_DEVICE_PATH *chopped = xallocate_pool(len + END_DEVICE_PATH_LENGTH);
 
-        CopyMem(parent, path, len);
-        CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH));
+        CopyMem(chopped, path, len);
+        SetDevicePathEndNode((EFI_DEVICE_PATH *) ((UINT8 *) chopped + len));
 
-        return parent;
+        return chopped;
 }
 
 static BOOLEAN verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_expected) {
@@ -75,10 +72,7 @@ static EFI_STATUS try_gpt(
                 EFI_BLOCK_IO *block_io,
                 EFI_LBA lba,
                 EFI_LBA *ret_backup_lba, /* May be changed even on error! */
-                UINT32 *ret_part_number,
-                UINT64 *ret_part_start,
-                UINT64 *ret_part_size,
-                EFI_GUID *ret_part_uuid) {
+                HARDDRIVE_DEVICE_PATH *ret_hd) {
 
         _cleanup_freepool_ EFI_PARTITION_ENTRY *entries = NULL;
         union GptHeaderBuffer gpt;
@@ -87,10 +81,7 @@ static EFI_STATUS try_gpt(
         UINTN size;
 
         assert(block_io);
-        assert(ret_part_number);
-        assert(ret_part_start);
-        assert(ret_part_size);
-        assert(ret_part_uuid);
+        assert(ret_hd);
 
         /* Read the GPT header */
         err = block_io->ReadBlocks(
@@ -142,10 +133,12 @@ static EFI_STATUS try_gpt(
                 if (end < start) /* Bogus? */
                         continue;
 
-                *ret_part_number = i + 1;
-                *ret_part_start = start;
-                *ret_part_size = end - start + 1;
-                CopyMem(ret_part_uuid, &entry->UniquePartitionGUID, sizeof(*ret_part_uuid));
+                ret_hd->PartitionNumber = i + 1;
+                ret_hd->PartitionStart = start;
+                ret_hd->PartitionSize = end - start + 1;
+                ret_hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
+                ret_hd->SignatureType = SIGNATURE_TYPE_GUID;
+                CopyMem(ret_hd->Signature, &entry->UniquePartitionGUID, sizeof(ret_hd->Signature));
 
                 return EFI_SUCCESS;
         }
@@ -155,95 +148,88 @@ static EFI_STATUS try_gpt(
         return EFI_NOT_FOUND;
 }
 
-static EFI_STATUS find_device(
-                EFI_HANDLE *device,
-                EFI_DEVICE_PATH **ret_device_path,
-                UINT32 *ret_part_number,
-                UINT64 *ret_part_start,
-                UINT64 *ret_part_size,
-                EFI_GUID *ret_part_uuid) {
-
-        EFI_DEVICE_PATH *partition_path;
+static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_path) {
         EFI_STATUS err;
 
         assert(device);
         assert(ret_device_path);
-        assert(ret_part_number);
-        assert(ret_part_start);
-        assert(ret_part_size);
-        assert(ret_part_uuid);
 
-        partition_path = DevicePathFromHandle(device);
+        EFI_DEVICE_PATH *partition_path = DevicePathFromHandle(device);
         if (!partition_path)
                 return EFI_NOT_FOUND;
 
+        /* Find the (last) partition node itself. */
+        EFI_DEVICE_PATH *part_node = NULL;
         for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
-                _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL;
-                EFI_HANDLE disk_handle;
-                EFI_BLOCK_IO *block_io;
-                EFI_DEVICE_PATH *p;
-
-                /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media
-                 * devices */
-                if (DevicePathType(node) != MESSAGING_DEVICE_PATH)
+                if (DevicePathType(node) != MEDIA_DEVICE_PATH)
                         continue;
 
-                /* Determine the device path one level up */
-                disk_path = p = path_parent(partition_path, node);
-                if (!disk_path)
+                if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)
                         continue;
 
-                err = BS->LocateDevicePath(&BlockIoProtocol, &p, &disk_handle);
-                if (EFI_ERROR(err))
-                        continue;
+                part_node = node;
+        }
 
-                err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io);
-                if (EFI_ERROR(err))
-                        continue;
+        if (!part_node)
+                return EFI_NOT_FOUND;
 
-                /* Filter out some block devices early. (We only care about block devices that aren't
-                 * partitions themselves — we look for GPT partition tables to parse after all —, and only
-                 * those which contain a medium and have at least 2 blocks.) */
-                if (block_io->Media->LogicalPartition ||
-                    !block_io->Media->MediaPresent ||
-                    block_io->Media->LastBlock <= 1)
-                        continue;
+        /* Chop off the partition part, leaving us with the full path to the disk itself. */
+        _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL;
+        EFI_DEVICE_PATH *p = disk_path = path_chop(partition_path, part_node);
+
+        EFI_HANDLE disk_handle;
+        EFI_BLOCK_IO *block_io;
+        err = BS->LocateDevicePath(&BlockIoProtocol, &p, &disk_handle);
+        if (EFI_ERROR(err))
+                return err;
 
-                /* Try several copies of the GPT header, in case one is corrupted */
-                EFI_LBA backup_lba = 0;
-                for (UINTN nr = 0; nr < 3; nr++) {
-                        EFI_LBA lba;
-
-                        /* Read the first copy at LBA 1 and then try the backup GPT header pointed
-                         * to by the first header if that one was corrupted. As a last resort,
-                         * try the very last LBA of this block device. */
-                        if (nr == 0)
-                                lba = 1;
-                        else if (nr == 1 && backup_lba != 0)
-                                lba = backup_lba;
-                        else if (nr == 2 && backup_lba != block_io->Media->LastBlock)
-                                lba = block_io->Media->LastBlock;
-                        else
-                                continue;
-
-                        err = try_gpt(
-                                block_io, lba,
-                                nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */
-                                ret_part_number,
-                                ret_part_start,
-                                ret_part_size,
-                                ret_part_uuid);
-                        if (!EFI_ERROR(err)) {
-                                *ret_device_path = DuplicateDevicePath(partition_path);
-                                if (!*ret_device_path)
-                                        return EFI_OUT_OF_RESOURCES;
-                                return EFI_SUCCESS;
-                        }
+        err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io);
+        if (EFI_ERROR(err))
+                return err;
+
+        /* Filter out some block devices early. (We only care about block devices that aren't
+         * partitions themselves — we look for GPT partition tables to parse after all —, and only
+         * those which contain a medium and have at least 2 blocks.) */
+        if (block_io->Media->LogicalPartition ||
+            !block_io->Media->MediaPresent ||
+            block_io->Media->LastBlock <= 1)
+                return EFI_NOT_FOUND;
 
+        /* Try several copies of the GPT header, in case one is corrupted */
+        EFI_LBA backup_lba = 0;
+        HARDDRIVE_DEVICE_PATH hd = *((HARDDRIVE_DEVICE_PATH *) part_node);
+        for (UINTN nr = 0; nr < 3; nr++) {
+                EFI_LBA lba;
+
+                /* Read the first copy at LBA 1 and then try the backup GPT header pointed
+                 * to by the first header if that one was corrupted. As a last resort,
+                 * try the very last LBA of this block device. */
+                if (nr == 0)
+                        lba = 1;
+                else if (nr == 1 && backup_lba != 0)
+                        lba = backup_lba;
+                else if (nr == 2 && backup_lba != block_io->Media->LastBlock)
+                        lba = block_io->Media->LastBlock;
+                else
+                        continue;
+
+                err = try_gpt(
+                        block_io, lba,
+                        nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */
+                        &hd);
+                if (EFI_ERROR(err)) {
                         /* GPT was valid but no XBOOT loader partition found. */
                         if (err == EFI_NOT_FOUND)
                                 break;
+                        /* Bad GPT, try next one. */
+                        continue;
                 }
+
+                /* Patch in the data we found */
+                EFI_DEVICE_PATH *xboot_path = ASSERT_PTR(DuplicateDevicePath(partition_path));
+                CopyMem((UINT8 *) xboot_path + ((UINT8 *) part_node - (UINT8 *) partition_path), &hd, sizeof(hd));
+                *ret_device_path = xboot_path;
+                return EFI_SUCCESS;
         }
 
         /* No xbootloader partition found */
@@ -252,40 +238,18 @@ static EFI_STATUS find_device(
 
 EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir) {
         _cleanup_freepool_ EFI_DEVICE_PATH *partition_path = NULL;
-        UINT32 part_number = UINT32_MAX;
-        UINT64 part_start = UINT64_MAX, part_size = UINT64_MAX;
         EFI_HANDLE new_device;
         EFI_FILE *root_dir;
-        EFI_GUID part_uuid;
         EFI_STATUS err;
 
         assert(device);
         assert(ret_device);
         assert(ret_root_dir);
 
-        err = find_device(device, &partition_path, &part_number, &part_start, &part_size, &part_uuid);
+        err = find_device(device, &partition_path);
         if (EFI_ERROR(err))
                 return err;
 
-        /* Patch in the data we found */
-        for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) {
-                HARDDRIVE_DEVICE_PATH *hd;
-
-                if (DevicePathType(node) != MEDIA_DEVICE_PATH)
-                        continue;
-
-                if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP)
-                        continue;
-
-                hd = (HARDDRIVE_DEVICE_PATH*) node;
-                hd->PartitionNumber = part_number;
-                hd->PartitionStart = part_start;
-                hd->PartitionSize = part_size;
-                CopyMem(hd->Signature, &part_uuid, sizeof(hd->Signature));
-                hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
-                hd->SignatureType = SIGNATURE_TYPE_GUID;
-        }
-
         EFI_DEVICE_PATH *dp = partition_path;
         err = BS->LocateDevicePath(&BlockIoProtocol, &dp, &new_device);
         if (EFI_ERROR(err))
index 4d86e8665f9061ca9c74969fb554dee2329cea85..f62c6f193120a0cd23a575eff26872f40375f035 100644 (file)
@@ -192,7 +192,7 @@ int bpf_devices_cgroup_init(
         if (policy == CGROUP_DEVICE_POLICY_AUTO && !allow_list)
                 return 0;
 
-        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog);
+        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, "sd_devices", &prog);
         if (r < 0)
                 return log_error_errno(r, "Loading device control BPF program failed: %m");
 
@@ -306,7 +306,7 @@ int bpf_devices_supported(void) {
                 return supported = 0;
         }
 
-        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &program);
+        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, "sd_devices", &program);
         if (r < 0) {
                 log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
                 return supported = 0;
index 3c1c02e4449649cd48afdcbb858b87112edd7d96..0297053add595efec4559afc1815407dbdf8812a 100644 (file)
@@ -145,6 +145,7 @@ static int add_instructions_for_ip_any(
 
 static int bpf_firewall_compile_bpf(
                 Unit *u,
+                const char *prog_name,
                 bool is_ingress,
                 BPFProgram **ret,
                 bool ip_allow_any,
@@ -216,7 +217,7 @@ static int bpf_firewall_compile_bpf(
                 return 0;
         }
 
-        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p);
+        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, prog_name, &p);
         if (r < 0)
                 return r;
 
@@ -526,9 +527,10 @@ static int bpf_firewall_prepare_accounting_maps(Unit *u, bool enabled, int *fd_i
 }
 
 int bpf_firewall_compile(Unit *u) {
+        const char *ingress_name = NULL, *egress_name = NULL;
+        bool ip_allow_any = false, ip_deny_any = false;
         CGroupContext *cc;
         int r, supported;
-        bool ip_allow_any = false, ip_deny_any = false;
 
         assert(u);
 
@@ -551,6 +553,13 @@ int bpf_firewall_compile(Unit *u) {
                 return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
                                             "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
 
+        /* If BPF_F_ALLOW_MULTI flag is supported program name is also supported (both were added to v4.15
+         * kernel). */
+        if (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
+                ingress_name = "sd_fw_ingress";
+                egress_name = "sd_fw_egress";
+        }
+
         /* Note that when we compile a new firewall we first flush out the access maps and the BPF programs themselves,
          * but we reuse the accounting maps. That way the firewall in effect always maps to the actual
          * configuration, but we don't flush out the accounting unnecessarily */
@@ -584,11 +593,11 @@ int bpf_firewall_compile(Unit *u) {
         if (r < 0)
                 return log_unit_error_errno(u, r, "Preparation of eBPF accounting maps failed: %m");
 
-        r = bpf_firewall_compile_bpf(u, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
+        r = bpf_firewall_compile_bpf(u, ingress_name, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
         if (r < 0)
                 return log_unit_error_errno(u, r, "Compilation for ingress BPF program failed: %m");
 
-        r = bpf_firewall_compile_bpf(u, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
+        r = bpf_firewall_compile_bpf(u, egress_name, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
         if (r < 0)
                 return log_unit_error_errno(u, r, "Compilation for egress BPF program failed: %m");
 
@@ -604,7 +613,7 @@ static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set
                 _cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
                 int r;
 
-                r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &prog);
+                r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &prog);
                 if (r < 0)
                         return log_unit_error_errno(u, r, "Can't allocate CGROUP SKB BPF program: %m");
 
@@ -825,7 +834,8 @@ int bpf_firewall_supported(void) {
                 return supported = BPF_FIREWALL_UNSUPPORTED;
         }
 
-        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &program);
+        /* prog_name is NULL since it is supported only starting from v4.15 kernel. */
+        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &program);
         if (r < 0) {
                 bpf_firewall_unsupported_reason =
                         log_debug_errno(r, "Can't allocate CGROUP SKB BPF program, BPF firewalling is not supported: %m");
@@ -882,7 +892,9 @@ int bpf_firewall_supported(void) {
         /* So now we know that the BPF program is generally available, let's see if BPF_F_ALLOW_MULTI is also supported
          * (which was added in kernel 4.15). We use a similar logic as before, but this time we use the BPF_PROG_ATTACH
          * bpf() call and the BPF_F_ALLOW_MULTI flags value. Since the flags are checked early in the system call we'll
-         * get EINVAL if it's not supported, and EBADF as before if it is available. */
+         * get EINVAL if it's not supported, and EBADF as before if it is available.
+         * Use probe result as the indicator that program name is also supported since they both were
+         * added in kernel 4.15. */
 
         zero(attr);
         attr.attach_type = BPF_CGROUP_INET_EGRESS;
index c2465a845f44c7a64ab183c9a95cecb667545f04..57a4c5393c90737bbb7888473f1dde789db4e050 100644 (file)
@@ -65,13 +65,23 @@ bpf_o_unstripped_cmd += [
         '@OUTPUT@'
 ]
 
-bpf_o_cmd = [
-        llvm_strip,
-        '-g',
-        '@INPUT@',
-        '-o',
-        '@OUTPUT@'
-]
+if bpftool_strip
+        bpf_o_cmd = [
+                bpftool,
+                'g',
+                'o',
+                '@OUTPUT@',
+                '@INPUT@'
+        ]
+else
+        bpf_o_cmd = [
+                llvm_strip,
+                '-g',
+                '@INPUT@',
+                '-o',
+                '@OUTPUT@'
+        ]
+endif
 
 skel_h_cmd = [
         bpftool,
index 5c499e5d060866bcf6f213713f638def7424e214..8a8d9c9b2e248ccd0c30e05f5b2017cbb5a50dea 100644 (file)
@@ -1204,6 +1204,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RootHashSignaturePath", "s", NULL, offsetof(ExecContext, root_hash_sig_path), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("ExtensionDirectories", "as", NULL, offsetof(ExecContext, extension_directories), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ExtensionImages", "a(sba(ss))", property_get_extension_images, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("MountImages", "a(ssba(ss))", property_get_mount_images, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -3261,7 +3262,8 @@ int bus_exec_context_set_transient_property(
                 return 1;
 
         } else if (STR_IN_SET(name, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
-                              "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths", "ExecPaths", "NoExecPaths")) {
+                              "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths", "ExecPaths", "NoExecPaths",
+                              "ExtensionDirectories")) {
                 _cleanup_strv_free_ char **l = NULL;
                 char ***dirs;
                 char **p;
@@ -3291,6 +3293,8 @@ int bus_exec_context_set_transient_property(
                                 dirs = &c->exec_paths;
                         else if (streq(name, "NoExecPaths"))
                                 dirs = &c->no_exec_paths;
+                        else if (streq(name, "ExtensionDirectories"))
+                                dirs = &c->extension_directories;
                         else /* "InaccessiblePaths" */
                                 dirs = &c->inaccessible_paths;
 
index eef491740cf8c9666802f94bb0560b28e6b442e4..1128c42ad94028c14a6787beaf009b04b3c828ba 100644 (file)
@@ -1314,11 +1314,15 @@ static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) {
         for (;;) {
                 pid_t pid;
 
+                /* libvirt / qemu uses threaded mode and cgroup.procs cannot be read at the lower levels.
+                 * From https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#threads,
+                 * “cgroup.procs” in a threaded domain cgroup contains the PIDs of all processes in
+                 * the subtree and is not readable in the subtree proper. */
                 r = cg_read_pid(f, &pid);
+                if (IN_SET(r, 0, -EOPNOTSUPP))
+                        break;
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        break;
 
                 if (is_kernel_thread(pid) > 0)
                         continue;
index 2c5bda58f98816c8060eba13fa7bceb4c65fcef1..073675ceefa446f074cc78a9554445f3d6474215 100644 (file)
@@ -925,14 +925,18 @@ int bus_init_private(Manager *m) {
 
                 r = sockaddr_un_set_path(&sa.un, "/run/systemd/private");
         } else {
-                const char *e, *joined;
+                _cleanup_free_ char *joined = NULL;
+                const char *e;
 
                 e = secure_getenv("XDG_RUNTIME_DIR");
                 if (!e)
                         return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN),
                                                "XDG_RUNTIME_DIR is not set, refusing.");
 
-                joined = strjoina(e, "/systemd/private");
+                joined = path_join(e, "/systemd/private");
+                if (!joined)
+                        return log_oom();
+
                 r = sockaddr_un_set_path(&sa.un, joined);
         }
         if (r < 0)
index eb25c98925685ae2ad301770500942a0cc395ad5..d3266a9ab53ac296942cd13bb10aa8a0b7099563 100644 (file)
@@ -2065,6 +2065,9 @@ bool exec_needs_mount_namespace(
         if (context->n_extension_images > 0)
                 return true;
 
+        if (!strv_isempty(context->extension_directories))
+                return true;
+
         if (!IN_SET(context->mount_flags, 0, MS_SHARED))
                 return true;
 
@@ -3370,7 +3373,7 @@ static int compile_symlinks(
                                         return r;
                         }
 
-                        if (!exec_directory_is_private(context, dt))
+                        if (!exec_directory_is_private(context, dt) || exec_context_with_rootfs(context))
                                 continue;
 
                         private_path = path_join(params->prefix[dt], "private", context->directories[dt].items[i].path);
@@ -3566,6 +3569,7 @@ static int apply_mount_namespace(
                             context->root_verity,
                             context->extension_images,
                             context->n_extension_images,
+                            context->extension_directories,
                             propagate_dir,
                             incoming_dir,
                             root_dir || root_image ? params->notify_socket : NULL,
@@ -5244,6 +5248,7 @@ void exec_context_done(ExecContext *c) {
         c->root_hash_sig_path = mfree(c->root_hash_sig_path);
         c->root_verity = mfree(c->root_verity);
         c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images);
+        c->extension_directories = strv_free(c->extension_directories);
         c->tty_path = mfree(c->tty_path);
         c->syslog_identifier = mfree(c->syslog_identifier);
         c->user = mfree(c->user);
@@ -5496,20 +5501,18 @@ static int exec_context_named_iofds(
         return targets == 0 ? 0 : -ENOENT;
 }
 
-static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l) {
-        char **i, **r = NULL;
+static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
+        _cleanup_strv_free_ char **v = NULL;
+        char **i;
+        int r;
 
         assert(c);
-        assert(l);
+        assert(ret);
 
         STRV_FOREACH(i, c->environment_files) {
-                char *fn;
-                int k;
-                bool ignore = false;
-                char **p;
                 _cleanup_globfree_ glob_t pglob = {};
-
-                fn = *i;
+                bool ignore = false;
+                char *fn = *i;
 
                 if (fn[0] == '-') {
                         ignore = true;
@@ -5519,33 +5522,30 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c,
                 if (!path_is_absolute(fn)) {
                         if (ignore)
                                 continue;
-
-                        strv_free(r);
                         return -EINVAL;
                 }
 
                 /* Filename supports globbing, take all matching files */
-                k = safe_glob(fn, 0, &pglob);
-                if (k < 0) {
+                r = safe_glob(fn, 0, &pglob);
+                if (r < 0) {
                         if (ignore)
                                 continue;
-
-                        strv_free(r);
-                        return k;
+                        return r;
                 }
 
                 /* When we don't match anything, -ENOENT should be returned */
                 assert(pglob.gl_pathc > 0);
 
                 for (unsigned n = 0; n < pglob.gl_pathc; n++) {
-                        k = load_env_file(NULL, pglob.gl_pathv[n], &p);
-                        if (k < 0) {
+                        _cleanup_strv_free_ char **p = NULL;
+
+                        r = load_env_file(NULL, pglob.gl_pathv[n], &p);
+                        if (r < 0) {
                                 if (ignore)
                                         continue;
-
-                                strv_free(r);
-                                return k;
+                                return r;
                         }
+
                         /* Log invalid environment variables with filename */
                         if (p) {
                                 InvalidEnvInfo info = {
@@ -5556,23 +5556,19 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c,
                                 p = strv_env_clean_with_callback(p, invalid_env, &info);
                         }
 
-                        if (!r)
-                                r = p;
+                        if (!v)
+                                v = TAKE_PTR(p);
                         else {
-                                char **m;
-
-                                m = strv_env_merge(r, p);
-                                strv_free(r);
-                                strv_free(p);
+                                char **m = strv_env_merge(v, p);
                                 if (!m)
                                         return -ENOMEM;
 
-                                r = m;
+                                strv_free_and_replace(v, m);
                         }
                 }
         }
 
-        *l = r;
+        *ret = TAKE_PTR(v);
 
         return 0;
 }
@@ -6120,6 +6116,8 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                                 strempty(o->options));
                 fprintf(f, "\n");
         }
+
+        strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories);
 }
 
 bool exec_context_maintains_privileges(const ExecContext *c) {
index 805e9b476537a2c30a35b09155048098fe390379..4aff50b4424bf05de1fa13a3e1f49e50d2145818 100644 (file)
@@ -273,6 +273,7 @@ struct ExecContext {
         size_t n_mount_images;
         MountImage *extension_images;
         size_t n_extension_images;
+        char **extension_directories;
 
         uint64_t capability_bounding_set;
         uint64_t capability_ambient_set;
index deea540e1047382d0235ac94bb1b86eda3d8c92e..cc04393c1e8b4c20d26b17438d1b23cfef086e0e 100644 (file)
@@ -9,6 +9,7 @@
 {{type}}.RootHash,                         config_parse_exec_root_hash,                 0,                                  offsetof({{type}}, exec_context)
 {{type}}.RootHashSignature,                config_parse_exec_root_hash_sig,             0,                                  offsetof({{type}}, exec_context)
 {{type}}.RootVerity,                       config_parse_unit_path_printf,               true,                               offsetof({{type}}, exec_context.root_verity)
+{{type}}.ExtensionDirectories,             config_parse_namespace_path_strv,            0,                                  offsetof({{type}}, exec_context.extension_directories)
 {{type}}.ExtensionImages,                  config_parse_extension_images,               0,                                  offsetof({{type}}, exec_context)
 {{type}}.MountImages,                      config_parse_mount_images,                   0,                                  offsetof({{type}}, exec_context)
 {{type}}.User,                             config_parse_user_group_compat,              0,                                  offsetof({{type}}, exec_context.user)
index 57aedb9b93b459cc5ef95df9c1002d3e6ebfd6a2..fba9a4a8ec3caed7370f0435dac2e80c3c91989e 100644 (file)
@@ -2726,6 +2726,8 @@ int main(int argc, char *argv[]) {
         Manager *m = NULL;
         FDSet *fds = NULL;
 
+        assert(argc > 0 && !isempty(argv[0]));
+
         /* SysV compatibility: redirect init → telinit */
         redirect_telinit(argc, argv);
 
index c549dcc96be4d19d0df6e9541da160dace34b401..c8dc6c0503a890ab681862ca5313722f61f82117 100644 (file)
@@ -63,6 +63,7 @@ typedef enum MountMode {
         EXEC,
         TMPFS,
         RUN,
+        EXTENSION_DIRECTORIES, /* Bind-mounted outside the root directory, and used by subsequent mounts */
         EXTENSION_IMAGES, /* Mounted outside the root directory, and used by subsequent mounts */
         MQUEUEFS,
         READWRITE_IMPLICIT, /* Should have the lowest priority. */
@@ -408,22 +409,23 @@ static int append_mount_images(MountEntry **p, const MountImage *mount_images, s
         return 0;
 }
 
-static int append_extension_images(
+static int append_extensions(
                 MountEntry **p,
                 const char *root,
                 const char *extension_dir,
                 char **hierarchies,
                 const MountImage *mount_images,
-                size_t n) {
+                size_t n,
+                char **extension_directories) {
 
         _cleanup_strv_free_ char **overlays = NULL;
-        char **hierarchy;
+        char **hierarchy, **extension_directory;
         int r;
 
         assert(p);
         assert(extension_dir);
 
-        if (n == 0)
+        if (n == 0 && strv_isempty(extension_directories))
                 return 0;
 
         /* Prepare a list of overlays, that will have as each element a string suitable for being
@@ -482,6 +484,62 @@ static int append_extension_images(
                 };
         }
 
+        /* Secondly, extend the lowerdir= parameters with each ExtensionDirectory.
+         * Bind mount them in the same location as the ExtensionImages, so that we
+         * can check that they are valid trees (extension-release.d). */
+        STRV_FOREACH(extension_directory, extension_directories) {
+                _cleanup_free_ char *mount_point = NULL, *source = NULL;
+                const char *e = *extension_directory;
+                bool ignore_enoent = false;
+
+                /* Pick up the counter where the ExtensionImages left it. */
+                r = asprintf(&mount_point, "%s/%zu", extension_dir, n++);
+                if (r < 0)
+                        return -ENOMEM;
+
+                /* Look for any prefixes */
+                if (startswith(e, "-")) {
+                        e++;
+                        ignore_enoent = true;
+                }
+                /* Ignore this for now */
+                if (startswith(e, "+"))
+                        e++;
+
+                source = strdup(e);
+                if (!source)
+                        return -ENOMEM;
+
+                for (size_t j = 0; hierarchies && hierarchies[j]; ++j) {
+                        _cleanup_free_ char *prefixed_hierarchy = NULL, *escaped = NULL, *lowerdir = NULL;
+
+                        prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
+                        if (!prefixed_hierarchy)
+                                return -ENOMEM;
+
+                        escaped = shell_escape(prefixed_hierarchy, ",:");
+                        if (!escaped)
+                                return -ENOMEM;
+
+                        /* Note that lowerdir= parameters are in 'reverse' order, so the
+                         * top-most directory in the overlay comes first in the list. */
+                        lowerdir = strjoin(escaped, ":", overlays[j]);
+                        if (!lowerdir)
+                                return -ENOMEM;
+
+                        free_and_replace(overlays[j], lowerdir);
+                }
+
+                *((*p)++) = (MountEntry) {
+                        .path_malloc = TAKE_PTR(mount_point),
+                        .source_const = TAKE_PTR(source),
+                        .mode = EXTENSION_DIRECTORIES,
+                        .ignore = ignore_enoent,
+                        .has_prefix = true,
+                        .read_only = true,
+                };
+        }
+
         /* Then, for each hierarchy, prepare an overlay with the list of lowerdir= strings
          * set up earlier. */
         for (size_t i = 0; hierarchies && hierarchies[i]; ++i) {
@@ -605,9 +663,12 @@ static int append_protect_system(MountEntry **p, ProtectSystem protect_system, b
 static int mount_path_compare(const MountEntry *a, const MountEntry *b) {
         int d;
 
-        /* EXTENSION_IMAGES will be used by other mounts as a base, so sort them first
+        /* ExtensionImages/Directories will be used by other mounts as a base, so sort them first
          * regardless of the prefix - they are set up in the propagate directory anyway */
         d = -CMP(a->mode == EXTENSION_IMAGES, b->mode == EXTENSION_IMAGES);
+        if (d != 0)
+                return d;
+        d = -CMP(a->mode == EXTENSION_DIRECTORIES, b->mode == EXTENSION_DIRECTORIES);
         if (d != 0)
                 return d;
 
@@ -757,8 +818,8 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, size_t
 
         for (f = m, t = m; f < m + *n; f++) {
 
-                /* ExtensionImages bases are opened in /run/systemd/unit-extensions on the host */
-                if (f->mode != EXTENSION_IMAGES && !path_startswith(mount_entry_path(f), root_directory)) {
+                /* ExtensionImages/Directories bases are opened in /run/systemd/unit-extensions on the host */
+                if (!IN_SET(f->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) && !path_startswith(mount_entry_path(f), root_directory)) {
                         log_debug("%s is outside of root directory.", mount_entry_path(f));
                         mount_entry_done(f);
                         continue;
@@ -1151,6 +1212,8 @@ static int mount_image(const MountEntry *m, const char *root_directory) {
                                 NULL);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
+                if (isempty(host_os_release_id))
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
         }
 
         r = verity_dissect_and_mount(
@@ -1294,6 +1357,47 @@ static int apply_one_mount(
                 what = mount_entry_path(m);
                 break;
 
+        case EXTENSION_DIRECTORIES: {
+                _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL,
+                                *host_os_release_sysext_level = NULL, *extension_name = NULL;
+                _cleanup_strv_free_ char **extension_release = NULL;
+
+                r = path_extract_filename(mount_entry_source(m), &extension_name);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to extract extension name from %s: %m", mount_entry_source(m));
+
+                r = parse_os_release(
+                                empty_to_root(root_directory),
+                                "ID", &host_os_release_id,
+                                "VERSION_ID", &host_os_release_version_id,
+                                "SYSEXT_LEVEL", &host_os_release_sysext_level,
+                                NULL);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
+                if (isempty(host_os_release_id))
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory));
+
+                r = load_extension_release_pairs(mount_entry_source(m), extension_name, &extension_release);
+                if (r == -ENOENT && m->ignore)
+                        return 0;
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to parse directory %s extension-release metadata: %m", extension_name);
+
+                r = extension_release_validate(
+                                extension_name,
+                                host_os_release_id,
+                                host_os_release_version_id,
+                                host_os_release_sysext_level,
+                                /* host_sysext_scope */ NULL, /* Leave empty, we need to accept both system and portable */
+                                extension_release);
+                if (r == 0)
+                        return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Directory %s extension-release metadata does not match the root's", extension_name);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to compare directory %s extension-release metadata with the root's os-release: %m", extension_name);
+
+                _fallthrough_;
+        }
+
         case BIND_MOUNT:
                 rbind = false;
 
@@ -1523,6 +1627,7 @@ static size_t namespace_calculate_mounts(
                 size_t n_temporary_filesystems,
                 size_t n_mount_images,
                 size_t n_extension_images,
+                size_t n_extension_directories,
                 size_t n_hierarchies,
                 const char* tmp_dir,
                 const char* var_tmp_dir,
@@ -1557,7 +1662,8 @@ static size_t namespace_calculate_mounts(
                 strv_length(empty_directories) +
                 n_bind_mounts +
                 n_mount_images +
-                (n_extension_images > 0 ? n_hierarchies + n_extension_images : 0) + /* Mount each image plus an overlay per hierarchy */
+                (n_extension_images > 0 || n_extension_directories > 0 ? /* Mount each image and directory plus an overlay per hierarchy */
+                        n_hierarchies + n_extension_images + n_extension_directories: 0) +
                 n_temporary_filesystems +
                 ns_info->private_dev +
                 (ns_info->protect_kernel_tunables ?
@@ -1576,7 +1682,14 @@ static size_t namespace_calculate_mounts(
                 ns_info->private_ipc; /* /dev/mqueue */
 }
 
-static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
+/* Walk all mount entries and dropping any unused mounts. This affects all
+ * mounts:
+ * - that are implicitly protected by a path that has been rendered inaccessible
+ * - whose immediate parent requests the same protection mode as the mount itself
+ * - that are outside of the relevant root directory
+ * - which are duplicates
+ */
+static void drop_unused_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
         assert(root_directory);
         assert(n_mounts);
         assert(mounts || *n_mounts == 0);
@@ -1653,8 +1766,8 @@ static int apply_mounts(
                         if (m->applied)
                                 continue;
 
-                        /* ExtensionImages are first opened in the propagate directory, not in the root_directory */
-                        r = follow_symlink(m->mode != EXTENSION_IMAGES ? root : NULL, m);
+                        /* ExtensionImages/Directories are first opened in the propagate directory, not in the root_directory */
+                        r = follow_symlink(!IN_SET(m->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) ? root : NULL, m);
                         if (r < 0) {
                                 if (error_path && mount_entry_path(m))
                                         *error_path = strdup(mount_entry_path(m));
@@ -1682,7 +1795,7 @@ static int apply_mounts(
                 if (!again)
                         break;
 
-                normalize_mounts(root, mounts, n_mounts);
+                drop_unused_mounts(root, mounts, n_mounts);
         }
 
         /* Now that all filesystems have been set up, but before the
@@ -1691,7 +1804,7 @@ static int apply_mounts(
          * exist, which means this will be a no-op. */
         r = create_symlinks_from_tuples(root, exec_dir_symlinks);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "Failed to set up ExecDirectories symlinks inside mount namespace: %m");
 
         /* Create a deny list we can pass to bind_mount_recursive() */
         deny_list = new(char*, (*n_mounts)+1);
@@ -1877,6 +1990,7 @@ int setup_namespace(
                 const char *verity_data_path,
                 const MountImage *extension_images,
                 size_t n_extension_images,
+                char **extension_directories,
                 const char *propagate_dir,
                 const char *incoming_dir,
                 const char *notify_socket,
@@ -1990,7 +2104,7 @@ int setup_namespace(
                 require_prefix = true;
         }
 
-        if (n_extension_images > 0) {
+        if (n_extension_images > 0 || !strv_isempty(extension_directories)) {
                 r = parse_env_extension_hierarchies(&hierarchies);
                 if (r < 0)
                         return r;
@@ -2008,6 +2122,7 @@ int setup_namespace(
                         n_temporary_filesystems,
                         n_mount_images,
                         n_extension_images,
+                        strv_length(extension_directories),
                         strv_length(hierarchies),
                         tmp_dir, var_tmp_dir,
                         creds_path,
@@ -2076,7 +2191,7 @@ int setup_namespace(
                 if (r < 0)
                         goto finish;
 
-                r = append_extension_images(&m, root, extension_dir, hierarchies, extension_images, n_extension_images);
+                r = append_extensions(&m, root, extension_dir, hierarchies, extension_images, n_extension_images, extension_directories);
                 if (r < 0)
                         goto finish;
 
@@ -2155,14 +2270,19 @@ int setup_namespace(
                                 goto finish;
                 }
 
+                /* Note, if proc is mounted with subset=pid then neither of the
+                 * two paths will exist, i.e. they are implicitly protected by
+                 * the mount option. */
                 if (ns_info->protect_hostname) {
                         *(m++) = (MountEntry) {
                                 .path_const = "/proc/sys/kernel/hostname",
                                 .mode = READONLY,
+                                .ignore = ignore_protect_proc,
                         };
                         *(m++) = (MountEntry) {
                                 .path_const = "/proc/sys/kernel/domainname",
                                 .mode = READONLY,
+                                .ignore = ignore_protect_proc,
                         };
                 }
 
@@ -2243,7 +2363,7 @@ int setup_namespace(
                 if (r < 0)
                         goto finish;
 
-                normalize_mounts(root, mounts, &n_mounts);
+                drop_unused_mounts(root, mounts, &n_mounts);
         }
 
         /* All above is just preparation, figuring out what to do. Let's now actually start doing something. */
index 62f05d7585fb9b508b16fc170a6467c73843a2d6..ae84d2b03b101b71e8012e66ba607bdf7d9650ee 100644 (file)
@@ -142,6 +142,7 @@ int setup_namespace(
                 const char *root_verity,
                 const MountImage *extension_images,
                 size_t n_extension_images,
+                char **extension_directories,
                 const char *propagate_dir,
                 const char *incoming_dir,
                 const char *notify_socket,
index 88307fa1a5dea776ee59c1efff1fc54da1c468b5..905558af57c1522c25928f84a3d301f254cf36e8 100644 (file)
@@ -1798,7 +1798,7 @@ static bool service_shall_restart(Service *s, const char **reason) {
                 return false;
 
         case SERVICE_RESTART_ALWAYS:
-                return true;
+                return s->result != SERVICE_SKIP_CONDITION;
 
         case SERVICE_RESTART_ON_SUCCESS:
                 return s->result == SERVICE_SUCCESS;
index 46c383b84190a36a51b9bb068587a7c24aa9adb6..4818feef5e0e395af7692ff773c5ba15366a9d43 100644 (file)
 #include "user-util.h"
 
 static int specifier_prefix_and_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const Unit *u = userdata;
-
-        assert(u);
+        const Unit *u = ASSERT_PTR(userdata);
 
         return unit_name_to_prefix_and_instance(u->id, ret);
 }
 
 static int specifier_prefix(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const Unit *u = userdata;
-
-        assert(u);
+        const Unit *u = ASSERT_PTR(userdata);
 
         return unit_name_to_prefix(u->id, ret);
 }
 
 static int specifier_prefix_unescaped(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         _cleanup_free_ char *p = NULL;
-        const Unit *u = userdata;
+        const Unit *u = ASSERT_PTR(userdata);
         int r;
 
-        assert(u);
-
         r = unit_name_to_prefix(u->id, &p);
         if (r < 0)
                 return r;
@@ -43,21 +37,17 @@ static int specifier_prefix_unescaped(char specifier, const void *data, const ch
 }
 
 static int specifier_instance_unescaped(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const Unit *u = userdata;
-
-        assert(u);
+        const Unit *u = ASSERT_PTR(userdata);
 
         return unit_name_unescape(strempty(u->instance), ret);
 }
 
 static int specifier_last_component(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const Unit *u = userdata;
+        const Unit *u = ASSERT_PTR(userdata);
         _cleanup_free_ char *prefix = NULL;
         char *dash;
         int r;
 
-        assert(u);
-
         r = unit_name_to_prefix(u->id, &prefix);
         if (r < 0)
                 return r;
@@ -82,9 +72,7 @@ static int specifier_last_component_unescaped(char specifier, const void *data,
 }
 
 static int specifier_filename(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const Unit *u = userdata;
-
-        assert(u);
+        const Unit *u = ASSERT_PTR(userdata);
 
         if (u->instance)
                 return unit_name_path_unescape(u->instance, ret);
@@ -97,11 +85,9 @@ static void bad_specifier(const Unit *u, char specifier) {
 }
 
 static int specifier_cgroup(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const Unit *u = userdata;
+        const Unit *u = ASSERT_PTR(userdata);
         char *n;
 
-        assert(u);
-
         bad_specifier(u, specifier);
 
         if (u->cgroup_path)
@@ -116,11 +102,9 @@ static int specifier_cgroup(char specifier, const void *data, const char *root,
 }
 
 static int specifier_cgroup_root(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const Unit *u = userdata;
+        const Unit *u = ASSERT_PTR(userdata);
         char *n;
 
-        assert(u);
-
         bad_specifier(u, specifier);
 
         n = strdup(u->manager->cgroup_root);
@@ -132,11 +116,9 @@ static int specifier_cgroup_root(char specifier, const void *data, const char *r
 }
 
 static int specifier_cgroup_slice(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const Unit *u = userdata, *slice;
+        const Unit *u = ASSERT_PTR(userdata), *slice;
         char *n;
 
-        assert(u);
-
         bad_specifier(u, specifier);
 
         slice = UNIT_GET_SLICE(u);
@@ -155,11 +137,9 @@ static int specifier_cgroup_slice(char specifier, const void *data, const char *
 }
 
 static int specifier_special_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const Unit *u = userdata;
+        const Unit *u = ASSERT_PTR(userdata);
         char *n = NULL;
 
-        assert(u);
-
         n = strdup(u->manager->prefix[PTR_TO_UINT(data)]);
         if (!n)
                 return -ENOMEM;
@@ -169,7 +149,6 @@ static int specifier_special_directory(char specifier, const void *data, const c
 }
 
 int unit_name_printf(const Unit *u, const char* format, char **ret) {
-
         /*
          * This will use the passed string as format string and replace the following specifiers (which should all be
          * safe for inclusion in unit names):
@@ -240,6 +219,8 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length,
                 { 'P', specifier_prefix_unescaped,         NULL },
 
                 { 'f', specifier_filename,                 NULL },
+                { 'y', specifier_real_path,                u->fragment_path },
+                { 'Y', specifier_real_directory,           u->fragment_path },
 
                 { 'c', specifier_cgroup,                   NULL },
                 { 'r', specifier_cgroup_slice,             NULL },
index 9b32383a76637bd5f237cdbd481a2e791580d5f3..ca9b045e85952d8a5af9d7598b66494b2631e426 100644 (file)
@@ -721,7 +721,7 @@ static int sysroot_is_nfsroot(void) {
                 if (!sep)
                         return -EINVAL;
 
-                a = strndupa(arg_root_what + 1, sep - arg_root_what - 1);
+                a = strndupa_safe(arg_root_what + 1, sep - arg_root_what - 1);
 
                 r = in_addr_from_string(AF_INET6, a, &u);
                 if (r < 0)
@@ -733,7 +733,7 @@ static int sysroot_is_nfsroot(void) {
         /* IPv4 address */
         sep = strchr(arg_root_what, ':');
         if (sep) {
-                a = strndupa(arg_root_what, sep - arg_root_what);
+                a = strndupa_safe(arg_root_what, sep - arg_root_what);
 
                 if (in_addr_from_string(AF_INET, a, &u) >= 0)
                         return true;
index 470c7f07f6209f6734799282c232754326f84544..1340cf30d334fbb26e15af5d9810901bdba334c4 100644 (file)
@@ -1185,14 +1185,18 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
         if (r < 0)
                 return r;
         if (r == 0) {
+                _cleanup_free_ char *joined = NULL;
                 const char *homework, *suffix, *unix_path;
 
                 /* Child */
 
                 suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
-                if (suffix)
-                        unix_path = strjoina("/run/systemd/home/notify.", suffix);
-                else
+                if (suffix) {
+                        joined = strjoin("/run/systemd/home/notify.", suffix);
+                        if (!joined)
+                                return log_oom();
+                        unix_path = joined;
+                } else
                         unix_path = "/run/systemd/home/notify";
 
                 if (setenv("NOTIFY_SOCKET", unix_path, 1) < 0) {
index 6c178b8a0e0ec4a363366cb847f485bcb683cd94..c1ec555cacf313b747120f6d838ebda1a475e6b6 100644 (file)
@@ -936,6 +936,7 @@ int manager_enumerate_images(Manager *m) {
 }
 
 static int manager_connect_bus(Manager *m) {
+        _cleanup_free_ char *b = NULL;
         const char *suffix, *busname;
         int r;
 
@@ -955,9 +956,12 @@ static int manager_connect_bus(Manager *m) {
                 return r;
 
         suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
-        if (suffix)
-                busname = strjoina("org.freedesktop.home1.", suffix);
-        else
+        if (suffix) {
+                b = strjoin("org.freedesktop.home1.", suffix);
+                if (!b)
+                        return log_oom();
+                busname = b;
+        } else
                 busname = "org.freedesktop.home1";
 
         r = sd_bus_request_name_async(m->bus, NULL, busname, 0, NULL, NULL);
@@ -974,6 +978,7 @@ static int manager_connect_bus(Manager *m) {
 }
 
 static int manager_bind_varlink(Manager *m) {
+        _cleanup_free_ char *p = NULL;
         const char *suffix, *socket_path;
         int r;
 
@@ -999,9 +1004,12 @@ static int manager_bind_varlink(Manager *m) {
         /* To make things easier to debug, when working from a homed managed home directory, let's optionally
          * use a different varlink socket name */
         suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
-        if (suffix)
-                socket_path = strjoina("/run/systemd/userdb/io.systemd.Home.", suffix);
-        else
+        if (suffix) {
+                p = strjoin("/run/systemd/userdb/io.systemd.Home.", suffix);
+                if (!p)
+                        return log_oom();
+                socket_path = p;
+        } else
                 socket_path = "/run/systemd/userdb/io.systemd.Home";
 
         r = varlink_server_listen_address(m->varlink_server, socket_path, 0666);
@@ -1159,9 +1167,11 @@ static int manager_listen_notify(Manager *m) {
 
         suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
         if (suffix) {
-                const char *unix_path;
+                _cleanup_free_ char *unix_path = NULL;
 
-                unix_path = strjoina("/run/systemd/home/notify.", suffix);
+                unix_path = strjoin("/run/systemd/home/notify.", suffix);
+                if (!unix_path)
+                        return log_oom();
                 r = sockaddr_un_set_path(&sa.un, unix_path);
                 if (r < 0)
                         return log_error_errno(r, "Socket path %s does not fit in sockaddr_un: %m", unix_path);
index b20a93ad81c8d109c0e4d9fe4e94d071bccefd52..4637ac8817838293f602f6f539d4292e51569414 100644 (file)
@@ -43,7 +43,7 @@
 
 /* Properties we cache are indexed by an enum, to make invalidation easy and systematic (as we can iterate
  * through them all, and they are uniformly strings). */
-enum {
+typedef enum {
         /* Read from /etc/hostname */
         PROP_STATIC_HOSTNAME,
 
@@ -53,6 +53,8 @@ enum {
         PROP_CHASSIS,
         PROP_DEPLOYMENT,
         PROP_LOCATION,
+        PROP_VENDOR,
+        PROP_MODEL,
 
         /* Read from /etc/os-release (or /usr/lib/os-release) */
         PROP_OS_PRETTY_NAME,
@@ -60,7 +62,7 @@ enum {
         PROP_OS_HOME_URL,
         _PROP_MAX,
         _PROP_INVALID = -EINVAL,
-};
+} HostProperty;
 
 typedef struct Context {
         char *data[_PROP_MAX];
@@ -133,7 +135,9 @@ static void context_read_machine_info(Context *c) {
                            "ICON_NAME", &c->data[PROP_ICON_NAME],
                            "CHASSIS", &c->data[PROP_CHASSIS],
                            "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
-                           "LOCATION", &c->data[PROP_LOCATION]);
+                           "LOCATION", &c->data[PROP_LOCATION],
+                           "VENDOR", &c->data[PROP_VENDOR],
+                           "MODEL", &c->data[PROP_MODEL]);
         if (r < 0 && r != -ENOENT)
                 log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
 
@@ -166,6 +170,68 @@ static void context_read_os_release(Context *c) {
         c->etc_os_release_stat = current_stat;
 }
 
+static int get_dmi_data(const char *database_key, const char *regular_key, char **ret) {
+        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+        _cleanup_free_ char *b = NULL;
+        const char *s = NULL;
+        int r;
+
+        r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id");
+        if (r < 0)
+                return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m");
+
+        if (database_key)
+                (void) sd_device_get_property_value(device, database_key, &s);
+        if (!s && regular_key)
+                (void) sd_device_get_property_value(device, regular_key, &s);
+
+        if (s) {
+                b = strdup(s);
+                if (!b)
+                        return -ENOMEM;
+        }
+
+        if (ret)
+                *ret = TAKE_PTR(b);
+
+        return !!s;
+}
+
+static int get_hardware_vendor(char **ret) {
+        return get_dmi_data("ID_VENDOR_FROM_DATABASE", "ID_VENDOR", ret);
+}
+
+static int get_hardware_model(char **ret) {
+        return get_dmi_data("ID_MODEL_FROM_DATABASE", "ID_MODEL", ret);
+}
+
+static int get_hardware_serial(char **ret) {
+        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+        _cleanup_free_ char *b = NULL;
+        const char *s = NULL;
+        int r;
+
+        r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id");
+        if (r < 0)
+                return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m");
+
+        (void) sd_device_get_sysattr_value(device, "product_serial", &s);
+        if (isempty(s))
+                /* Fallback to board serial */
+                (void) sd_device_get_sysattr_value(device, "board_serial", &s);
+
+        if (!isempty(s)) {
+                b = strdup(s);
+                if (!b)
+                        return -ENOMEM;
+        }
+
+        if (ret)
+                *ret = TAKE_PTR(b);
+
+        return !isempty(s);
+}
+
 static const char* valid_chassis(const char *chassis) {
         assert(chassis);
 
@@ -318,15 +384,31 @@ try_devicetree:
         return chassis;
 }
 
-static char* context_fallback_icon_name(Context *c) {
-        const char *chassis;
+static char* context_get_chassis(Context *c) {
+        const char *fallback;
+        char *dmi;
 
         assert(c);
 
         if (!isempty(c->data[PROP_CHASSIS]))
-                return strjoin("computer-", c->data[PROP_CHASSIS]);
+                return strdup(c->data[PROP_CHASSIS]);
+
+        if (get_dmi_data("ID_CHASSIS", NULL, &dmi) >= 0)
+                return dmi;
+
+        fallback = fallback_chassis();
+        if (fallback)
+                return strdup(fallback);
+
+        return NULL;
+}
+
+static char* context_fallback_icon_name(Context *c) {
+        _cleanup_free_ char *chassis = NULL;
 
-        chassis = fallback_chassis();
+        assert(c);
+
+        chassis = context_get_chassis(c);
         if (chassis)
                 return strjoin("computer-", chassis);
 
@@ -464,39 +546,25 @@ static int context_write_data_machine_info(Context *c) {
         return 0;
 }
 
-static int get_dmi_data(const char *database_key, const char *regular_key, char **ret) {
-        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
-        _cleanup_free_ char *b = NULL;
-        const char *s = NULL;
-        int r;
-
-        r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id");
-        if (r < 0)
-                return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m");
-
-        if (database_key)
-                (void) sd_device_get_property_value(device, database_key, &s);
-        if (!s && regular_key)
-                (void) sd_device_get_property_value(device, regular_key, &s);
+static int property_get_hardware_property(
+                sd_bus_message *reply,
+                Context *c,
+                HostProperty prop,
+                int (*getter)(char **)) {
 
-        if (s) {
-                b = strdup(s);
-                if (!b)
-                        return -ENOMEM;
-        }
+        _cleanup_free_ char *from_dmi = NULL;
 
-        if (ret)
-                *ret = TAKE_PTR(b);
+        assert(reply);
+        assert(c);
+        assert(IN_SET(prop, PROP_VENDOR, PROP_MODEL));
+        assert(getter);
 
-        return !!s;
-}
+        context_read_machine_info(c);
 
-static int get_hardware_vendor(char **ret) {
-        return get_dmi_data("ID_VENDOR_FROM_DATABASE", "ID_VENDOR", ret);
-}
+        if (isempty(c->data[prop]))
+                (void) getter(&from_dmi);
 
-static int get_hardware_model(char **ret) {
-        return get_dmi_data("ID_MODEL_FROM_DATABASE", "ID_MODEL", ret);
+        return sd_bus_message_append(reply, "s", from_dmi ?: c->data[prop]);
 }
 
 static int property_get_hardware_vendor(
@@ -508,10 +576,7 @@ static int property_get_hardware_vendor(
                 void *userdata,
                 sd_bus_error *error) {
 
-        _cleanup_free_ char *vendor = NULL;
-
-        (void) get_hardware_vendor(&vendor);
-        return sd_bus_message_append(reply, "s", vendor);
+        return property_get_hardware_property(reply, userdata, PROP_VENDOR, get_hardware_vendor);
 }
 
 static int property_get_hardware_model(
@@ -523,10 +588,7 @@ static int property_get_hardware_model(
                 void *userdata,
                 sd_bus_error *error) {
 
-        _cleanup_free_ char *model = NULL;
-
-        (void) get_hardware_model(&model);
-        return sd_bus_message_append(reply, "s", model);
+        return property_get_hardware_property(reply, userdata, PROP_MODEL, get_hardware_model);
 }
 
 static int property_get_hostname(
@@ -724,17 +786,14 @@ static int property_get_chassis(
                 void *userdata,
                 sd_bus_error *error) {
 
+        _cleanup_free_ char *chassis = NULL;
         Context *c = userdata;
-        const char *name;
 
         context_read_machine_info(c);
 
-        if (isempty(c->data[PROP_CHASSIS]))
-                name = fallback_chassis();
-        else
-                name = c->data[PROP_CHASSIS];
+        chassis = context_get_chassis(c);
 
-        return sd_bus_message_append(reply, "s", name);
+        return sd_bus_message_append(reply, "s", chassis);
 }
 
 static int property_get_uname_field(
@@ -1019,12 +1078,54 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err
         return sd_bus_send(NULL, reply, NULL);
 }
 
+static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_free_ char *serial = NULL;
+        Context *c = userdata;
+        int interactive, r;
+
+        assert(m);
+        assert(c);
+
+        r = sd_bus_message_read(m, "b", &interactive);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_polkit_async(
+                        m,
+                        CAP_SYS_ADMIN,
+                        "org.freedesktop.hostname1.get-hardware-serial",
+                        NULL,
+                        interactive,
+                        UID_INVALID,
+                        &c->polkit_registry,
+                        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 = get_hardware_serial(&serial);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_new_method_return(m, &reply);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "s", serial);
+        if (r < 0)
+                return r;
+
+        return sd_bus_send(NULL, reply, NULL);
+}
+
 static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) {
-        _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL, *vendor = NULL, *model = NULL;
+        _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL,
+                *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         sd_id128_t product_uuid = SD_ID128_NULL;
-        const char *chassis = NULL;
         Context *c = userdata;
         bool privileged;
         struct utsname u;
@@ -1036,7 +1137,7 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
         r = bus_verify_polkit_async(
                         m,
                         CAP_SYS_ADMIN,
-                        "org.freedesktop.hostname1.get-product-uuid",
+                        "org.freedesktop.hostname1.get-description",
                         NULL,
                         false,
                         UID_INVALID,
@@ -1071,16 +1172,20 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
         if (isempty(c->data[PROP_ICON_NAME]))
                 in = context_fallback_icon_name(c);
 
-        if (isempty(c->data[PROP_CHASSIS]))
-                chassis = fallback_chassis();
+        chassis = context_get_chassis(c);
 
         assert_se(uname(&u) >= 0);
 
-        (void) get_hardware_vendor(&vendor);
-        (void) get_hardware_model(&model);
+        if (isempty(c->data[PROP_VENDOR]))
+                (void) get_hardware_vendor(&vendor);
+        if (isempty(c->data[PROP_MODEL]))
+                (void) get_hardware_model(&model);
 
-        if (privileged) /* The product UUID is only available to privileged clients */
-                id128_get_product(&product_uuid);
+        if (privileged) {
+                /* The product UUID and hardware serial is only available to privileged clients */
+                (void) id128_get_product(&product_uuid);
+                (void) get_hardware_serial(&serial);
+        }
 
         r = json_build(&v, JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)),
@@ -1089,7 +1194,7 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
                                        JSON_BUILD_PAIR("DefaultHostname", JSON_BUILD_STRING(dhn)),
                                        JSON_BUILD_PAIR("HostnameSource", JSON_BUILD_STRING(hostname_source_to_string(c->hostname_source))),
                                        JSON_BUILD_PAIR("IconName", JSON_BUILD_STRING(in ?: c->data[PROP_ICON_NAME])),
-                                       JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis ?: c->data[PROP_CHASSIS])),
+                                       JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis)),
                                        JSON_BUILD_PAIR("Deployment", JSON_BUILD_STRING(c->data[PROP_DEPLOYMENT])),
                                        JSON_BUILD_PAIR("Location", JSON_BUILD_STRING(c->data[PROP_LOCATION])),
                                        JSON_BUILD_PAIR("KernelName", JSON_BUILD_STRING(u.sysname)),
@@ -1098,8 +1203,9 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro
                                        JSON_BUILD_PAIR("OperatingSystemPrettyName", JSON_BUILD_STRING(c->data[PROP_OS_PRETTY_NAME])),
                                        JSON_BUILD_PAIR("OperatingSystemCPEName", JSON_BUILD_STRING(c->data[PROP_OS_CPE_NAME])),
                                        JSON_BUILD_PAIR("OperatingSystemHomeURL", JSON_BUILD_STRING(c->data[PROP_OS_HOME_URL])),
-                                       JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor)),
-                                       JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model)),
+                                       JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor ?: c->data[PROP_VENDOR])),
+                                       JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model ?: c->data[PROP_MODEL])),
+                                       JSON_BUILD_PAIR("HardwareSerial", JSON_BUILD_STRING(serial)),
                                        JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)),
                                        JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL)));
 
@@ -1197,6 +1303,13 @@ static const sd_bus_vtable hostname_vtable[] = {
                                  SD_BUS_PARAM(uuid),
                                  method_get_product_uuid,
                                  SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetHardwareSerial",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 "s",
+                                 SD_BUS_PARAM(serial),
+                                 method_get_hardware_serial,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD_WITH_ARGS("Describe",
                                 SD_BUS_NO_ARGS,
                                 SD_BUS_RESULT("s", json),
index dacea0ff0acd762c2fb7cc66bd99ac586d82ee1e..a86cead53ed126cd55ccbe0e982033e22fb9b4d0 100644 (file)
                 </defaults>
         </action>
 
+        <action id="org.freedesktop.hostname1.get-hardware-serial">
+                <description gettext-domain="systemd">Get hardware serial number</description>
+                <message gettext-domain="systemd">Authentication is required to get hardware serial number.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
+        <action id="org.freedesktop.hostname1.get-description">
+                <description gettext-domain="systemd">Get system description</description>
+                <message gettext-domain="systemd">Authentication is required to get system description.</message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
 </policyconfig>
index 32c4e1819ca6313a65f1e1f94fe5b5697a3af30e..1731b94bb8b2714717ab76b0ce282ee2ac9affd4 100644 (file)
@@ -21,7 +21,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-journal-remote.XXXXXX.journal";
         _cleanup_close_ int fdout = -1;
         _cleanup_(sd_journal_closep) sd_journal *j = NULL;
-        RemoteServer s = {};
+        _cleanup_(journal_remote_server_destroy) RemoteServer s = {};
         int r;
 
         if (size <= 2)
@@ -59,7 +59,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         while (s.active)
                 assert_se(journal_remote_handle_raw_source(NULL, fdin, 0, &s) >= 0);
 
-        journal_remote_server_destroy(&s);
         assert_se(close(fdin) < 0 && errno == EBADF); /* Check that the fd is closed already */
 
         /* Out */
index a8e3b175ac4906e00bfc470abf1f1b29c2bfc36b..ea535a27af7fef0cdbdd79b4aaee0b1a9dda43bd 100644 (file)
@@ -399,7 +399,7 @@ void process_audit_string(Server *s, int type, const char *data, size_t size) {
 
         z = n;
 
-        map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, iovec, &n, ELEMENTSOF(iovec));
+        map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, iovec, &n, n + N_IOVEC_AUDIT_FIELDS);
 
         server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, LOG_NOTICE, 0);
 
index 35ca305384ecbf99f99ef95f2ec36b7f76cbf36e..4e095acc934fe6923f85e4b0a6aa62047ac2c0d3 100644 (file)
@@ -31,9 +31,9 @@ static int journald_file_truncate(JournalFile *f) {
         f->header->arena_size = htole64(p - le64toh(f->header->header_size));
 
         if (ftruncate(f->fd, p) < 0)
-                log_debug_errno(errno, "Failed to truncate %s: %m", f->path);
+                return log_debug_errno(errno, "Failed to truncate %s: %m", f->path);
 
-        return 0;
+        return journal_file_fstat(f);
 }
 
 static int journald_file_entry_array_punch_hole(JournalFile *f, uint64_t p, uint64_t n_entries) {
@@ -72,6 +72,19 @@ static int journald_file_entry_array_punch_hole(JournalFile *f, uint64_t p, uint
         if (sz < MINIMUM_HOLE_SIZE)
                 return 0;
 
+        if (p == le64toh(f->header->tail_object_offset) && !f->seal) {
+                o.object.size = htole64(offset - p);
+                if (pwrite(f->fd, &o, sizeof(EntryArrayObject), p) < 0)
+                        return log_debug_errno(errno, "Failed to modify entry array object size: %m");
+
+                f->header->arena_size = htole64(ALIGN64(offset) - le64toh(f->header->header_size));
+
+                if (ftruncate(f->fd, ALIGN64(offset)) < 0)
+                        return log_debug_errno(errno, "Failed to truncate %s: %m", f->path);
+
+                return 0;
+        }
+
         if (fallocate(f->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, sz) < 0)
                 return log_debug_errno(errno, "Failed to punch hole in entry array of %s: %m", f->path);
 
@@ -179,7 +192,7 @@ static void journald_file_set_offline_internal(JournaldFile *f) {
 
                                 log_debug_errno(r, "Failed to re-enable copy-on-write for %s: %m, rewriting file", f->file->path);
 
-                                r = copy_file_atomic(f->file->path, f->file->path, f->file->mode, 0, FS_NOCOW_FL, COPY_REPLACE | COPY_FSYNC);
+                                r = copy_file_atomic(f->file->path, f->file->path, f->file->mode, 0, FS_NOCOW_FL, COPY_REPLACE | COPY_FSYNC | COPY_HOLES);
                                 if (r < 0) {
                                         log_debug_errno(r, "Failed to rewrite %s: %m", f->file->path);
                                         continue;
index 3afe66db894db41888ccf02c304546944930a894..fbe4f0360ec87c8a9b9ba2b00ab6b056e8572a14 100644 (file)
@@ -29,7 +29,7 @@ static void test_non_empty(void) {
         JournaldFile *f;
         struct iovec iovec;
         static const char test[] = "TEST1=1", test2[] = "TEST2=2";
-        Object *o;
+        Object *o, *d;
         uint64_t p;
         sd_id128_t fake_boot_id;
         char t[] = "/var/tmp/journal-XXXXXX";
@@ -75,21 +75,21 @@ static void test_non_empty(void) {
         assert_se(journal_file_next_entry(f->file, 0, DIRECTION_DOWN, &o, &p) == 1);
         assert_se(le64toh(o->entry.seqnum) == 1);
 
-        assert_se(journal_file_find_data_object(f->file, test, strlen(test), NULL, &p) == 1);
-        assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_DOWN, &o, NULL) == 1);
+        assert_se(journal_file_find_data_object(f->file, test, strlen(test), &d, NULL) == 1);
+        assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_DOWN, &o, NULL) == 1);
         assert_se(le64toh(o->entry.seqnum) == 1);
 
-        assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_UP, &o, NULL) == 1);
+        assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_UP, &o, NULL) == 1);
         assert_se(le64toh(o->entry.seqnum) == 3);
 
-        assert_se(journal_file_find_data_object(f->file, test2, strlen(test2), NULL, &p) == 1);
-        assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_UP, &o, NULL) == 1);
+        assert_se(journal_file_find_data_object(f->file, test2, strlen(test2), &d, NULL) == 1);
+        assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_UP, &o, NULL) == 1);
         assert_se(le64toh(o->entry.seqnum) == 2);
 
-        assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_DOWN, &o, NULL) == 1);
+        assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_DOWN, &o, NULL) == 1);
         assert_se(le64toh(o->entry.seqnum) == 2);
 
-        assert_se(journal_file_find_data_object(f->file, "quux", 4, NULL, &p) == 0);
+        assert_se(journal_file_find_data_object(f->file, "quux", 4, &d, NULL) == 0);
 
         assert_se(journal_file_move_to_entry_by_seqnum(f->file, 1, DIRECTION_DOWN, &o, NULL) == 1);
         assert_se(le64toh(o->entry.seqnum) == 1);
index fd00c436322a3a992ab093a46e406db33fc6394a..61afdd4e9cd5f6175fd4daca38770a14bf32447a 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/sh
 # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
 # ex: ts=8 sw=4 sts=4 et filetype=sh
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
 COMMAND="$1"
 KERNEL_VERSION="$2"
-ENTRY_DIR_ABS="$3"
-KERNEL_IMAGE="$4"
-INITRD_OPTIONS_START="5"
-
-[[ $KERNEL_VERSION ]] || exit 1
 
 case "$COMMAND" in
     add)
-        [[ -d "/lib/modules/${KERNEL_VERSION}/kernel" ]] || exit 0
-        [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
-            echo "Running depmod -a ${KERNEL_VERSION}"
-        exec depmod -a "${KERNEL_VERSION}"
+        [ -d "/lib/modules/$KERNEL_VERSION/kernel" ] || exit 0
+        [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+depmod -a $KERNEL_VERSION"
+        exec depmod -a "$KERNEL_VERSION"
         ;;
     remove)
         [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
             echo "Removing /lib/modules/${KERNEL_VERSION}/modules.dep and associated files"
-        exec rm -f /lib/modules/"${KERNEL_VERSION}"/modules.{alias{,.bin},builtin{,.alias}.bin,dep{,.bin},devname,softdep,symbols{,.bin}}
+        exec rm -f \
+            "/lib/modules/$KERNEL_VERSION/modules.alias" \
+            "/lib/modules/$KERNEL_VERSION/modules.alias.bin" \
+            "/lib/modules/$KERNEL_VERSION/modules.builtin.bin" \
+            "/lib/modules/$KERNEL_VERSION/modules.builtin.alias.bin" \
+            "/lib/modules/$KERNEL_VERSION/modules.dep" \
+            "/lib/modules/$KERNEL_VERSION/modules.dep.bin" \
+            "/lib/modules/$KERNEL_VERSION/modules.devname" \
+            "/lib/modules/$KERNEL_VERSION/modules.softdep" \
+            "/lib/modules/$KERNEL_VERSION/modules.symbols" \
+            "/lib/modules/$KERNEL_VERSION/modules.symbols.bin"
         ;;
     *)
         exit 0
index 044eced3f018d07852251f5c970478aa013f2ff0..e588e72bf9c4a51c46feae42dbe1cf4640febf60 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/sh
 # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
 # ex: ts=8 sw=4 sts=4 et filetype=sh
 # SPDX-License-Identifier: LGPL-2.1-or-later
@@ -22,68 +22,53 @@ COMMAND="$1"
 KERNEL_VERSION="$2"
 ENTRY_DIR_ABS="$3"
 KERNEL_IMAGE="$4"
-INITRD_OPTIONS_START="5"
+INITRD_OPTIONS_SHIFT=4
 
-if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then
-    exit 0
-fi
-
-if [ "$KERNEL_INSTALL_LAYOUT" != "bls" ]; then
-    exit 0
-fi
+[ "$KERNEL_INSTALL_LAYOUT" = "bls" ] || exit 0
 
 MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
 BOOT_ROOT="$KERNEL_INSTALL_BOOT_ROOT"
 
 BOOT_MNT="$(stat -c %m "$BOOT_ROOT")"
-if [[ "$BOOT_MNT" == '/' ]]; then
+if [ "$BOOT_MNT" = '/' ]; then
     ENTRY_DIR="$ENTRY_DIR_ABS"
 else
     ENTRY_DIR="${ENTRY_DIR_ABS#$BOOT_MNT}"
 fi
 
-if [[ $COMMAND == remove ]]; then
-    rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
-    rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+"*".conf"
-    exit 0
-fi
-
-if ! [[ $COMMAND == add ]]; then
-    exit 1
-fi
-
-if ! [[ $KERNEL_IMAGE ]]; then
-    exit 1
-fi
+case "$COMMAND" in
+    remove)
+        exec rm -f \
+            "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" \
+            "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+"*".conf"
+        ;;
+    add)
+        ;;
+    *)
+        exit 1
+        ;;
+esac
 
-if [[ -f /etc/os-release ]]; then
+if [ -r /etc/os-release ]; then
     . /etc/os-release
-elif [[ -f /usr/lib/os-release ]]; then
+elif [ -r /usr/lib/os-release ]; then
     . /usr/lib/os-release
 fi
 
-if ! [[ $PRETTY_NAME ]]; then
-    PRETTY_NAME="Linux $KERNEL_VERSION"
-fi
+[ -n "$PRETTY_NAME" ] || PRETTY_NAME="Linux $KERNEL_VERSION"
 
-if [[ -f /etc/kernel/cmdline ]]; then
-    read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline
-elif [[ -f /usr/lib/kernel/cmdline ]]; then
-    read -r -d '' -a BOOT_OPTIONS < /usr/lib/kernel/cmdline
+if [ -r /etc/kernel/cmdline ]; then
+    BOOT_OPTIONS="$(tr -s "$IFS" ' ' </etc/kernel/cmdline)"
+elif [ -r /usr/lib/kernel/cmdline ]; then
+    BOOT_OPTIONS="$(tr -s "$IFS" ' ' </usr/lib/kernel/cmdline)"
 else
-    declare -a BOOT_OPTIONS
-
-    read -r -d '' -a line < /proc/cmdline
-    for i in "${line[@]}"; do
-        [[ "${i#initrd=*}" != "$i" ]] && continue
-        [[ "${i#BOOT_IMAGE=*}" != "$i" ]] && continue
-        BOOT_OPTIONS+=("$i")
-    done
+    BOOT_OPTIONS="$(tr -s "$IFS" '\n' </proc/cmdline | grep -ve '^BOOT_IMAGE=' -e '^initrd=' | tr '\n' ' ')"
 fi
+BOOT_OPTIONS="${BOOT_OPTIONS% }"
 
-if [[ -f /etc/kernel/tries ]]; then
+if [ -r /etc/kernel/tries ]; then
     read -r TRIES </etc/kernel/tries
-    if ! [[ "$TRIES" =~ ^[0-9]+$ ]] ; then
+    if ! echo "$TRIES" | grep -q '^[0-9][0-9]*$'; then
         echo "/etc/kernel/tries does not contain an integer." >&2
         exit 1
     fi
@@ -106,43 +91,40 @@ install -g root -o root -m 0644 "$KERNEL_IMAGE" "$ENTRY_DIR_ABS/linux" || {
     exit 1
 }
 
-INITRD_OPTIONS=( "${@:${INITRD_OPTIONS_START}}" )
+shift "$INITRD_OPTIONS_SHIFT"
+for initrd; do
+    [ -f "$initrd" ] || {
+        echo "Initrd '$initrd' not a file." >&2
+        exit 1
+    }
 
-for initrd in "${INITRD_OPTIONS[@]}"; do
-    if [[ -f "${initrd}" ]]; then
-        initrd_basename="$(basename ${initrd})"
-        [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
-            echo "Installing $ENTRY_DIR_ABS/${initrd_basename}"
-        install -g root -o root -m 0644 "${initrd}" "$ENTRY_DIR_ABS/${initrd_basename}" || {
-            echo "Could not copy '${initrd}' to '$ENTRY_DIR_ABS/${initrd_basename}'." >&2
-            exit 1
-        }
-    fi
+    initrd_basename="${initrd##*/}"
+    [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Installing $ENTRY_DIR_ABS/$initrd_basename"
+    install -g root -o root -m 0644 "$initrd" "$ENTRY_DIR_ABS/$initrd_basename" || {
+        echo "Could not copy '$initrd' to '$ENTRY_DIR_ABS/$initrd_basename'." >&2
+        exit 1
+    }
 done
 
-# If no initrd option is supplied, fall back to "initrd" which is
-# the name used by dracut when generating it in its kernel-install hook
-[[ ${#INITRD_OPTIONS[@]} == 0 ]] && INITRD_OPTIONS=( initrd )
-
 mkdir -p "${LOADER_ENTRY%/*}" || {
     echo "Could not create loader entry directory '${LOADER_ENTRY%/*}'." >&2
     exit 1
 }
 
-[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
-    echo "Creating $LOADER_ENTRY"
+[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Creating $LOADER_ENTRY"
 {
     echo "title      $PRETTY_NAME"
     echo "version    $KERNEL_VERSION"
     echo "machine-id $MACHINE_ID"
-    echo "options    ${BOOT_OPTIONS[*]}"
+    echo "options    $BOOT_OPTIONS"
     echo "linux      $ENTRY_DIR/linux"
-    for initrd in "${INITRD_OPTIONS[@]}"; do
-        [[ -f $ENTRY_DIR_ABS/$(basename ${initrd}) ]] && \
-            echo "initrd     $ENTRY_DIR/$(basename ${initrd})"
+    for initrd; do
+        echo "initrd     $ENTRY_DIR/${initrd##*/}"
     done
+    # Try "initrd", generated by dracut in its kernel-install hook, if no initrds were supplied
+    [ $# -eq 0 ] && [ -f "$ENTRY_DIR_ABS/initrd" ] && echo "initrd     $ENTRY_DIR/initrd"
     :
-} > "$LOADER_ENTRY" || {
+} >"$LOADER_ENTRY" || {
     echo "Could not create loader entry '$LOADER_ENTRY'." >&2
     exit 1
 }
index d85852532b634864272fb69ac883a34f048f8e9b..e56483ef9609e8cb669b6c621d99b14167f95c04 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/sh
 # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
 # ex: ts=8 sw=4 sts=4 et filetype=sh
 # SPDX-License-Identifier: LGPL-2.1-or-later
@@ -18,7 +18,7 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with systemd; If not, see <http://www.gnu.org/licenses/>.
 
-SKIP_REMAINING=77
+skip_remaining=77
 
 usage()
 {
@@ -26,30 +26,23 @@ usage()
     echo "  $0 [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE ...]"
     echo "  $0 [OPTIONS...] remove KERNEL-VERSION"
     echo "Options:"
-    echo "  -h,--help     Print this help"
-    echo "  -v,--verbose  Increase verbosity"
+    echo "  -h, --help     Print this help"
+    echo "  -v, --verbose  Increase verbosity"
 }
 
 dropindirs_sort()
 {
-    local suffix=$1; shift
-    local -a files
-    local f d i
-
-    readarray -t files <<<"$(
-        for d in "$@"; do
-            for i in "$d/"*"$suffix"; do
-                if [[ -e "$i" ]]; then
-                    echo "${i##*/}"
-                fi
-            done
-        done | sort -Vu
-    )"
-
-    for f in "${files[@]}"; do
-        for d in "$@"; do
-            if [[ -e "$d/$f" ]]; then
-                echo "$d/$f"
+    suffix="$1"
+    shift
+
+    for d; do
+        for i in "$d/"*"$suffix"; do
+            [ -e "$i" ] && echo "${i##*/}"
+        done
+    done | sort -Vu | while read -r f; do
+        for d; do
+            if [ -e "$d/$f" ]; then
+                [ -x "$d/$f" ] && echo "$d/$f"
                 continue 2
             fi
         done
@@ -58,38 +51,35 @@ dropindirs_sort()
 
 export LC_COLLATE=C
 
-for i in "$@"; do
-    if [ "$i" == "--help" -o "$i" == "-h" ]; then
+for i; do
+    if [ "$i" = "--help" ] || [ "$i" = "-h" ]; then
         usage
         exit 0
     fi
 done
 
-KERNEL_INSTALL_VERBOSE=0
-if [ "$1" == "--verbose" -o "$1" == "-v" ]; then
+export KERNEL_INSTALL_VERBOSE=0
+if [ "$1" = "--verbose" ] || [ "$1" = "-v" ]; then
     shift
     KERNEL_INSTALL_VERBOSE=1
 fi
-export KERNEL_INSTALL_VERBOSE
 
-if [[ "${0##*/}" == 'installkernel' ]]; then
-    COMMAND='add'
-    # make install doesn't pass any parameter wrt initrd handling
-    INITRD_OPTIONS=()
+if [ "${0##*/}" = "installkernel" ]; then
+    COMMAND=add
+    # make install doesn't pass any initrds
 else
     COMMAND="$1"
-    shift
-    INITRD_OPTIONS=( "${@:3}" )
+    [ $# -ge 1 ] && shift
 fi
 
-KERNEL_VERSION="$1"
-KERNEL_IMAGE="$2"
-
-if [[ ! $COMMAND ]] || [[ ! $KERNEL_VERSION ]]; then
+if [ $# -lt 1 ]; then
     echo "Not enough arguments" >&2
     exit 1
 fi
 
+KERNEL_VERSION="$1"
+shift
+
 if [ -r "/etc/kernel/install.conf" ]; then
     . /etc/kernel/install.conf
 elif [ -r "/usr/lib/kernel/install.conf" ]; then
@@ -99,12 +89,11 @@ fi
 # Prefer to use an existing machine ID from /etc/machine-info or /etc/machine-id. If we're using the machine
 # ID /etc/machine-id, try to persist it in /etc/machine-info. If no machine ID is found, try to generate
 # a new machine ID in /etc/machine-info. If that fails, use "Default".
-
-[ -z "$MACHINE_ID" ] && [ -f /etc/machine-info ] && source /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
-[ -z "$MACHINE_ID" ] && [ -f /etc/machine-id ] && read -r MACHINE_ID </etc/machine-id
-[ -n "$MACHINE_ID" ] && [ -z "$KERNEL_INSTALL_MACHINE_ID" ] && echo "KERNEL_INSTALL_MACHINE_ID=$MACHINE_ID" >>/etc/machine-info
+[ -z "$MACHINE_ID" ] && [ -r /etc/machine-info ]              && . /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
+[ -z "$MACHINE_ID" ] && [ -r /etc/machine-id ]                && read -r MACHINE_ID </etc/machine-id
+[ -n "$MACHINE_ID" ] && [ -z "$KERNEL_INSTALL_MACHINE_ID" ]   && echo "KERNEL_INSTALL_MACHINE_ID=$MACHINE_ID" >>/etc/machine-info
 [ -z "$MACHINE_ID" ] && NEW_MACHINE_ID="$(systemd-id128 new)" && echo "KERNEL_INSTALL_MACHINE_ID=$NEW_MACHINE_ID" >>/etc/machine-info
-[ -z "$MACHINE_ID" ] && [ -f /etc/machine-info ] && source /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
+[ -z "$MACHINE_ID" ] && [ -r /etc/machine-info ]              && . /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID"
 [ -z "$MACHINE_ID" ] && MACHINE_ID="Default"
 
 [ -z "$BOOT_ROOT" ] && for suff in "$MACHINE_ID" "loader/entries"; do
@@ -125,11 +114,6 @@ done
 [ -z "$BOOT_ROOT" ] && BOOT_ROOT="/boot"
 
 
-ENTRY_DIR_ABS="$BOOT_ROOT/$MACHINE_ID/$KERNEL_VERSION"
-
-export KERNEL_INSTALL_MACHINE_ID="$MACHINE_ID"
-export KERNEL_INSTALL_BOOT_ROOT="$BOOT_ROOT"
-
 if [ -z "$layout" ]; then
     # Administrative decision: if not present, some scripts generate into /boot.
     if [ -d "$BOOT_ROOT/$MACHINE_ID" ]; then
@@ -152,21 +136,23 @@ MAKE_ENTRY_DIR_ABS=$?
 
 ret=0
 
-readarray -t PLUGINS <<<"$(
+PLUGINS="$(
     dropindirs_sort ".install" \
         "/etc/kernel/install.d" \
         "/usr/lib/kernel/install.d"
 )"
+IFS="
+"
 
-case $COMMAND in
+case "$COMMAND" in
     add)
-        if [[ ! "$KERNEL_IMAGE" ]]; then
-            echo "Command 'add' requires an argument" >&2
+        if [ $# -lt 1 ]; then
+            echo "Command 'add' requires a kernel image" >&2
             exit 1
         fi
 
-        if [[ ! -f "$KERNEL_IMAGE" ]]; then
-            echo "Kernel image argument ${KERNEL_IMAGE} not a file" >&2
+        if ! [ -f "$1" ]; then
+            echo "Kernel image argument $1 not a file" >&2
             exit 1
         fi
 
@@ -182,32 +168,22 @@ case $COMMAND in
             fi
         fi
 
-        for f in "${PLUGINS[@]}"; do
-            if [[ -x $f ]]; then
-                [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
-                    echo "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS $KERNEL_IMAGE ${INITRD_OPTIONS[@]}"
-                "$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$KERNEL_IMAGE" "${INITRD_OPTIONS[@]}"
-                x=$?
-                if [[ $x == $SKIP_REMAINING ]]; then
-                    break
-                fi
-                ((ret+=$x))
-            fi
+        for f in $PLUGINS; do
+            [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS $*"
+            "$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$@"
+            err=$?
+            [ $err -eq $skip_remaining ] && break
+            ret=$(( ret + err ))
         done
         ;;
 
     remove)
-        for f in "${PLUGINS[@]}"; do
-            if [[ -x $f ]]; then
-                [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \
-                    echo "+$f remove $KERNEL_VERSION $ENTRY_DIR_ABS"
-                "$f" remove "$KERNEL_VERSION" "$ENTRY_DIR_ABS"
-                x=$?
-                if [[ $x == $SKIP_REMAINING ]]; then
-                    break
-                fi
-                ((ret+=$x))
-            fi
+        for f in $PLUGINS; do
+            [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+$f remove $KERNEL_VERSION $ENTRY_DIR_ABS"
+            "$f" remove "$KERNEL_VERSION" "$ENTRY_DIR_ABS"
+            err=$?
+            [ $err -eq $skip_remaining ] && break
+            ret=$(( ret + err ))
         done
 
         if [ "$MAKE_ENTRY_DIR_ABS" -eq 0 ]; then
@@ -222,4 +198,4 @@ case $COMMAND in
         ;;
 esac
 
-exit $ret
+exit "$ret"
index 6538f05bdb44b559473139b9176e2b50b28a607d..466d8e4b3f1403f4889ef1f3a5a7d77802d9ab65 100644 (file)
@@ -53,8 +53,8 @@ typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
 int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
 
 int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
-                      uint8_t type, uint16_t arp_type, size_t optlen,
-                      size_t *optoffset);
+                      uint8_t type, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr,
+                      size_t optlen, size_t *optoffset);
 
 uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);
 
index cace916f4413b6beca58589ac0b3b1e642d7fa3a..d1a1cf57f3d9b1b2e627cdb168587d0740ac14ad 100644 (file)
 
 #include "dhcp-internal.h"
 #include "dhcp-protocol.h"
+#include "memory-util.h"
 
 #define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
 
-int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
-                      uint8_t type, uint16_t arp_type, size_t optlen,
-                      size_t *optoffset) {
+int dhcp_message_init(
+                DHCPMessage *message,
+                uint8_t op,
+                uint32_t xid,
+                uint8_t type,
+                uint16_t arp_type,
+                uint8_t hlen,
+                const uint8_t *chaddr,
+                size_t optlen,
+                size_t *optoffset) {
+
         size_t offset = 0;
         int r;
 
         assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
-        assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
+        assert(chaddr || hlen == 0);
 
         message->op = op;
         message->htype = arp_type;
-        message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
+
+        /* RFC2131 section 4.1.1:
+           The client MUST include its hardware address in the ’chaddr’ field, if
+           necessary for delivery of DHCP reply messages.
+
+           RFC 4390 section 2.1:
+           A DHCP client, when working over an IPoIB interface, MUST follow the
+           following rules:
+           "htype" (hardware address type) MUST be 32 [ARPPARAM].
+           "hlen" (hardware address length) MUST be 0.
+           "chaddr" (client hardware address) field MUST be zeroed.
+         */
+        message->hlen = (arp_type == ARPHRD_INFINIBAND) ? 0 : hlen;
+        memcpy_safe(message->chaddr, chaddr, message->hlen);
+
         message->xid = htobe32(xid);
         message->magic = htobe32(DHCP_MAGIC_COOKIE);
 
index 13d8cd77b44ebd005c1d41859e0d31bb90ef5fcf..51510b9d22e61964b5c8395f3742ea95c9576776 100644 (file)
@@ -26,7 +26,7 @@ typedef enum DHCPRawOption {
 
 typedef struct DHCPClientId {
         size_t length;
-        void *data;
+        uint8_t *data;
 } DHCPClientId;
 
 typedef struct DHCPLease {
@@ -34,6 +34,8 @@ typedef struct DHCPLease {
 
         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];
diff --git a/src/libsystemd-network/fuzz-dhcp-client.c b/src/libsystemd-network/fuzz-dhcp-client.c
new file mode 100644 (file)
index 0000000..1812a61
--- /dev/null
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fuzz.h"
+#include "sd-event.h"
+
+#include "sd-dhcp-client.c"
+
+int dhcp_network_bind_raw_socket(
+                int ifindex,
+                union sockaddr_union *link,
+                uint32_t id,
+                const uint8_t *addr, size_t addr_len,
+                const uint8_t *bcaddr, size_t bcaddr_len,
+                uint16_t arp_type, uint16_t port) {
+
+        int fd;
+        fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) {
+        return len;
+}
+
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
+        int fd;
+
+        fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
+int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) {
+        return len;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+        uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'};
+        uint8_t bcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+        _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+        int res, r;
+
+        if (!getenv("SYSTEMD_LOG_LEVEL"))
+                log_set_max_level(LOG_CRIT);
+
+        r = sd_dhcp_client_new(&client, false);
+        assert_se(r >= 0);
+        assert_se(client);
+
+        assert_se(sd_event_new(&e) >= 0);
+
+        r = sd_dhcp_client_attach_event(client, e, 0);
+        assert_se(r >= 0);
+
+        assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
+        assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0);
+        dhcp_client_set_test_mode(client, true);
+
+        res = sd_dhcp_client_start(client);
+        assert_se(IN_SET(res, 0, -EINPROGRESS));
+        client->xid = 2;
+
+        (void) client_handle_offer(client, (DHCPMessage*) data, size);
+
+        assert_se(sd_dhcp_client_stop(client) >= 0);
+
+        return 0;
+}
diff --git a/src/libsystemd-network/fuzz-dhcp-server-relay-message.c b/src/libsystemd-network/fuzz-dhcp-server-relay-message.c
new file mode 100644 (file)
index 0000000..a53e1c2
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "fuzz.h"
+
+#include "sd-dhcp-server.c"
+
+ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) {
+        return len;
+}
+
+ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
+        return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+        _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
+        struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))};
+        union in_addr_union relay_address;
+        _cleanup_free_ uint8_t *message = NULL;
+
+        if (size < sizeof(DHCPMessage))
+                return 0;
+
+        assert_se(sd_dhcp_server_new(&server, 1) >= 0);
+        assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
+        assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0);
+        assert_se(in_addr_from_string(AF_INET, "192.168.5.1", &relay_address) >= 0);
+        assert_se(sd_dhcp_server_set_relay_target(server, &relay_address.in) >= 0);
+        assert_se(sd_dhcp_server_set_bind_to_interface(server, false) >= 0);
+        assert_se(sd_dhcp_server_set_relay_agent_information(server, "string:sample_circuit_id", "string:sample_remote_id") >= 0);
+
+        size_t buflen = size;
+        buflen += relay_agent_information_length(server->agent_circuit_id, server->agent_remote_id) + 2;
+        assert_se(message = malloc(buflen));
+        memcpy(message, data, size);
+
+        server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY);
+        assert_se(server->fd >= 0);
+
+        (void) dhcp_server_relay_message(server, (DHCPMessage *) message, size - sizeof(DHCPMessage), buflen);
+        return 0;
+}
index e90284f6f2e37c80bcf8e5b655a7c8fef95ad145..6a57850992c2d4cafa9b30931f82b1911ee52a39 100644 (file)
@@ -21,13 +21,17 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
         struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))};
         static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
+        _cleanup_free_ uint8_t *duped = NULL;
         uint8_t *client_id;
         DHCPLease *lease;
 
         if (size < sizeof(DHCPMessage))
                 return 0;
 
+        assert_se(duped = memdup(data, size));
+
         assert_se(sd_dhcp_server_new(&server, 1) >= 0);
+        assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
         server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY);
         assert_se(server->fd >= 0);
         assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0);
@@ -44,12 +48,14 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         lease->address = htobe32(UINT32_C(10) << 24 | UINT32_C(2));
         lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1));
         lease->expiration = UINT64_MAX;
-        memcpy(lease->chaddr, chaddr, 16);
+        lease->htype = ARPHRD_ETHER;
+        lease->hlen = ETH_ALEN;
+        memcpy(lease->chaddr, chaddr, ETH_ALEN);
         assert_se(hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease) >= 0);
         assert_se(hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease) >= 0);
         lease->server = server;
 
-        (void) dhcp_server_handle_message(server, (DHCPMessage*)data, size);
+        (void) dhcp_server_handle_message(server, (DHCPMessage*) duped, size);
 
         return 0;
 }
index 6be409d8adbbb38e46c82e82b38e93f2a62e2377..853401d5be832758c214aa6e9de63ab3195df59a 100644 (file)
@@ -105,6 +105,10 @@ tests += [
 ]
 
 fuzzers += [
+        [files('fuzz-dhcp-client.c'),
+         [libshared,
+          libsystemd_network]],
+
         [files('fuzz-dhcp6-client.c'),
          [libshared,
           libsystemd_network]],
@@ -113,6 +117,10 @@ fuzzers += [
          [libsystemd_network,
           libshared]],
 
+        [files('fuzz-dhcp-server-relay-message.c'),
+         [libsystemd_network,
+          libshared]],
+
         [files('fuzz-lldp-rx.c'),
          [libshared,
           libsystemd_network]],
index c33be947b749f37620dff812bdf5ee66de12e7a7..7b296ae4a0402951df329bafd56232ad6d01003c 100644 (file)
@@ -832,7 +832,8 @@ static int client_message_init(
                 return -ENOMEM;
 
         r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
-                              client->arp_type, optlen, &optoffset);
+                              client->arp_type, client->mac_addr_len, client->mac_addr,
+                              optlen, &optoffset);
         if (r < 0)
                 return r;
 
@@ -848,7 +849,7 @@ static int client_message_init(
         secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
         packet->dhcp.secs = htobe16(secs);
 
-        /* RFC2132 section 4.1
+        /* RFC2131 section 4.1
            A client that cannot receive unicast IP datagrams until its protocol
            software has been configured with an IP address SHOULD set the
            BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
@@ -862,15 +863,6 @@ static int client_message_init(
         if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
                 packet->dhcp.flags = htobe16(0x8000);
 
-        /* RFC2132 section 4.1.1:
-           The client MUST include its hardware address in the ’chaddr’ field, if
-           necessary for delivery of DHCP reply messages.  Non-Ethernet
-           interfaces will leave 'chaddr' empty and use the client identifier
-           instead (eg, RFC 4390 section 2.1).
-         */
-        if (client->arp_type == ARPHRD_ETHER)
-                memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
-
         /* If no client identifier exists, construct an RFC 4361-compliant one */
         if (client->client_id_len == 0) {
                 size_t duid_len;
index ab131701fbc163e494323c10a425e793b0a68eec..5a40eb94d327cdbf37ca3fdb0f0edff81637c6c8 100644 (file)
@@ -492,10 +492,8 @@ static int lease_parse_routes(
 
                 route->option = SD_DHCP_OPTION_STATIC_ROUTE;
                 r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
-                if (r < 0) {
-                        log_debug("Failed to determine destination prefix length from class based IP, ignoring");
-                        continue;
-                }
+                if (r < 0)
+                        return -EINVAL;
 
                 assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
                 route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
@@ -907,7 +905,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d
                       pos = next_chunk;
         }
 
-        *domains = TAKE_PTR(names);
+        strv_free_and_replace(*domains, names);
 
         return cnt;
 }
index ea98060ea2026e6bb0ba17589a2193e8c9ece67e..1d27d28959b918399e8a71afb7ca3fa3246e7a6a 100644 (file)
@@ -15,6 +15,7 @@
 #include "fd-util.h"
 #include "in-addr-util.h"
 #include "io-util.h"
+#include "memory-util.h"
 #include "network-common.h"
 #include "ordered-set.h"
 #include "siphash24.h"
@@ -294,22 +295,32 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) {
         return 0;
 }
 
-static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
-                                        DHCPPacket *packet, size_t len) {
+static int dhcp_server_send_unicast_raw(
+                sd_dhcp_server *server,
+                uint8_t hlen,
+                const uint8_t *chaddr,
+                DHCPPacket *packet,
+                size_t len) {
+
         union sockaddr_union link = {
                 .ll.sll_family = AF_PACKET,
                 .ll.sll_protocol = htobe16(ETH_P_IP),
                 .ll.sll_ifindex = server->ifindex,
-                .ll.sll_halen = ETH_ALEN,
+                .ll.sll_halen = hlen,
         };
 
         assert(server);
         assert(server->ifindex > 0);
         assert(server->address);
+        assert(hlen > 0);
+        assert(chaddr);
         assert(packet);
         assert(len > sizeof(DHCPPacket));
 
-        memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN);
+        memcpy(link.ll.sll_addr, chaddr, hlen);
+
+        if (len > UINT16_MAX)
+                return -EOVERFLOW;
 
         dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
                                       packet->dhcp.yiaddr,
@@ -378,8 +389,16 @@ static bool requested_broadcast(DHCPMessage *message) {
         return message->flags & htobe16(0x8000);
 }
 
-static int dhcp_server_send(sd_dhcp_server *server, be32_t destination, uint16_t destination_port,
-                            DHCPPacket *packet, size_t optoffset, bool l2_broadcast) {
+static int dhcp_server_send(
+                sd_dhcp_server *server,
+                uint8_t hlen,
+                const uint8_t *chaddr,
+                be32_t destination,
+                uint16_t destination_port,
+                DHCPPacket *packet,
+                size_t optoffset,
+                bool l2_broadcast) {
+
         if (destination != INADDR_ANY)
                 return dhcp_server_send_udp(server, destination,
                                             destination_port, &packet->dhcp,
@@ -392,7 +411,7 @@ static int dhcp_server_send(sd_dhcp_server *server, be32_t destination, uint16_t
                 /* we cannot send UDP packet to specific MAC address when the
                    address is not yet configured, so must fall back to raw
                    packets */
-                return dhcp_server_send_unicast_raw(server, packet,
+                return dhcp_server_send_unicast_raw(server, hlen, chaddr, packet,
                                                     sizeof(DHCPPacket) + optoffset);
 }
 
@@ -462,7 +481,8 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
                 destination = req->message->ciaddr;
 
         bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK;
-        return dhcp_server_send(server, destination, destination_port, packet, optoffset, l2_broadcast);
+        return dhcp_server_send(server, req->message->hlen, req->message->chaddr,
+                                destination, destination_port, packet, optoffset, l2_broadcast);
 }
 
 static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
@@ -482,14 +502,14 @@ static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
                 return -ENOMEM;
 
         r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
-                              be32toh(req->message->xid), type, ARPHRD_ETHER,
+                              be32toh(req->message->xid), type,
+                              req->message->htype, req->message->hlen, req->message->chaddr,
                               req->max_optlen, &optoffset);
         if (r < 0)
                 return r;
 
         packet->dhcp.flags = req->message->flags;
         packet->dhcp.giaddr = req->message->giaddr;
-        memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN);
 
         *_optoffset = optoffset;
         *ret = TAKE_PTR(packet);
@@ -591,11 +611,7 @@ static int server_send_offer_or_ack(
                         return r;
         }
 
-        r = dhcp_server_send_packet(server, req, packet, type, offset);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return dhcp_server_send_packet(server, req, packet, type, offset);
 }
 
 static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
@@ -610,8 +626,14 @@ static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
         return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
 }
 
-static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
-                                  be32_t gateway, const uint8_t chaddr[]) {
+static int server_send_forcerenew(
+                sd_dhcp_server *server,
+                be32_t address,
+                be32_t gateway,
+                uint8_t htype,
+                uint8_t hlen,
+                const uint8_t *chaddr) {
+
         _cleanup_free_ DHCPPacket *packet = NULL;
         size_t optoffset = 0;
         int r;
@@ -625,7 +647,7 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
                 return -ENOMEM;
 
         r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
-                              DHCP_FORCERENEW, ARPHRD_ETHER,
+                              DHCP_FORCERENEW, htype, hlen, chaddr,
                               DHCP_MIN_OPTIONS_SIZE, &optoffset);
         if (r < 0)
                 return r;
@@ -635,15 +657,9 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
         if (r < 0)
                 return r;
 
-        memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
-
-        r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
-                                 &packet->dhcp,
-                                 sizeof(DHCPMessage) + optoffset);
-        if (r < 0)
-                return r;
-
-        return 0;
+        return dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
+                                    &packet->dhcp,
+                                    sizeof(DHCPMessage) + optoffset);
 }
 
 static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
@@ -675,8 +691,7 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
                         if (!data)
                                 return -ENOMEM;
 
-                        free(req->client_id.data);
-                        req->client_id.data = data;
+                        free_and_replace(req->client_id.data, data);
                         req->client_id.length = len;
                 }
 
@@ -712,22 +727,45 @@ static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMes
 
         req->message = message;
 
-        /* set client id based on MAC address if client did not send an explicit
-           one */
+        if (message->hlen > sizeof(message->chaddr))
+                return -EBADMSG;
+
+        /* set client id based on MAC address if client did not send an explicit one */
         if (!req->client_id.data) {
-                void *data;
+                uint8_t *data;
 
-                data = malloc0(ETH_ALEN + 1);
+                if (message->hlen == 0)
+                        return -EBADMSG;
+
+                data = new0(uint8_t, message->hlen + 1);
                 if (!data)
                         return -ENOMEM;
 
-                ((uint8_t*) data)[0] = 0x01;
-                memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN);
+                data[0] = 0x01;
+                memcpy(data + 1, message->chaddr, message->hlen);
 
-                req->client_id.length = ETH_ALEN + 1;
+                req->client_id.length = message->hlen + 1;
                 req->client_id.data = data;
         }
 
+        if (message->hlen == 0 || memeqzero(message->chaddr, message->hlen)) {
+                /* 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)
+                        return -EBADMSG;
+
+                if (req->client_id.length <= 1 || req->client_id.length > sizeof(message->chaddr) + 1)
+                        return -EBADMSG;
+
+                if (req->client_id.data[0] != 0x01)
+                        return -EBADMSG;
+
+                message->hlen = req->client_id.length - 1;
+                memcpy(message->chaddr, req->client_id.data + 1, message->hlen);
+        }
+
         if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
                 req->max_optlen = DHCP_MIN_OPTIONS_SIZE;
 
@@ -782,6 +820,10 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag
         assert(message);
         assert(sd_dhcp_server_is_in_relay_mode(server));
 
+        if (message->hlen == 0 || message->hlen > sizeof(message->chaddr) || memeqzero(message->chaddr, message->hlen))
+                return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG),
+                                             "(relay agent) received message without/invalid hardware address, discarding.");
+
         if (message->op == BOOTREQUEST) {
                 log_dhcp_server(server, "(relay agent) BOOTREQUEST (0x%x)", be32toh(message->xid));
                 if (message->hops >= 16)
@@ -821,7 +863,7 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag
 
                 bool l2_broadcast = requested_broadcast(message) || message_type == DHCP_NAK;
                 const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message->ciaddr;
-                return dhcp_server_send(server, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast);
+                return dhcp_server_send(server, message->hlen, message->chaddr, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast);
         }
         return -EBADMSG;
 }
@@ -830,7 +872,9 @@ static int prepare_new_lease(
                 DHCPLease **ret_lease,
                 be32_t address,
                 const DHCPClientId *client_id,
-                const uint8_t chaddr[static ETH_ALEN],
+                uint8_t htype,
+                uint8_t hlen,
+                const uint8_t *chaddr,
                 be32_t gateway,
                 usec_t expiration) {
 
@@ -843,6 +887,8 @@ static int prepare_new_lease(
         *lease = (DHCPLease) {
                 .address = address,
                 .client_id.length = client_id->length,
+                .htype = htype,
+                .hlen = hlen,
                 .gateway = gateway,
                 .expiration = expiration,
         };
@@ -850,7 +896,7 @@ static int prepare_new_lease(
         if (!lease->client_id.data)
                 return -ENOMEM;
 
-        memcpy(&lease->chaddr, chaddr, ETH_ALEN);
+        memcpy(lease->chaddr, chaddr, hlen);
 
         *ret_lease = TAKE_PTR(lease);
 
@@ -868,12 +914,11 @@ static int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) {
         if (r < 0)
                 return r;
 
-        HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) {
+        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;
 }
@@ -900,9 +945,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
         assert(server);
         assert(message);
 
-        if (message->op != BOOTREQUEST ||
-            message->htype != ARPHRD_ETHER ||
-            message->hlen != ETHER_ADDR_LEN)
+        if (message->op != BOOTREQUEST)
                 return 0;
 
         req = new0(DHCPRequest, 1);
@@ -915,7 +958,6 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
 
         r = ensure_sane_request(server, req, message);
         if (r < 0)
-                /* this only fails on critical errors */
                 return r;
 
         r = dhcp_server_cleanup_expired_leases(server);
@@ -1054,6 +1096,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
                         expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now);
 
                         r = prepare_new_lease(&lease, static_lease->address, &req->client_id,
+                                              req->message->htype, req->message->hlen,
                                               req->message->chaddr, req->message->giaddr, expiration);
                         if (r < 0)
                                 return r;
@@ -1097,6 +1140,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
 
                         if (!existing_lease) {
                                 r = prepare_new_lease(&new_lease, address, &req->client_id,
+                                                      req->message->htype, req->message->hlen,
                                                       req->message->chaddr, req->message->giaddr, expiration);
                                 if (r < 0)
                                         return r;
@@ -1222,7 +1266,7 @@ static int server_receive_message(sd_event_source *s, int fd,
         if ((size_t) len < sizeof(DHCPMessage))
                 return 0;
 
-        CMSG_FOREACH(cmsg, &msg) {
+        CMSG_FOREACH(cmsg, &msg)
                 if (cmsg->cmsg_level == IPPROTO_IP &&
                     cmsg->cmsg_type == IP_PKTINFO &&
                     cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
@@ -1235,7 +1279,6 @@ static int server_receive_message(sd_event_source *s, int fd,
 
                         break;
                 }
-        }
 
         if (sd_dhcp_server_is_in_relay_mode(server)) {
                 r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage), buflen);
@@ -1326,7 +1369,8 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
         log_dhcp_server(server, "FORCERENEW");
 
         HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) {
-                k = server_send_forcerenew(server, lease->address, lease->gateway, lease->chaddr);
+                k = server_send_forcerenew(server, lease->address, lease->gateway,
+                                           lease->htype, lease->hlen, lease->chaddr);
                 if (k < 0)
                         r = k;
         }
@@ -1532,22 +1576,16 @@ int sd_dhcp_server_set_static_lease(
 
         assert_return(server, -EINVAL);
         assert_return(client_id, -EINVAL);
-        assert_return(client_id_size == ETH_ALEN + 1, -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. */
+         * the server removes any static lease with the specified mac address. */
         if (!address || address->s_addr == 0) {
-                _cleanup_free_ void *data = NULL;
                 DHCPClientId c;
 
-                data = memdup(client_id, client_id_size);
-                if (!data)
-                        return -ENOMEM;
-
                 c = (DHCPClientId) {
                         .length = client_id_size,
-                        .data = data,
+                        .data = client_id,
                 };
 
                 dhcp_lease_free(hashmap_remove(server->static_leases_by_client_id, &c));
index 800a6641e357bc43c3e326891f694733407440d7..b01b1f57a3ad4bc2c02b89c1209688398c15f11b 100644 (file)
@@ -89,7 +89,8 @@ static void test_message_init(void) {
         message = malloc0(len);
 
         assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678,
-                  DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0);
+                                    DHCP_DISCOVER, ARPHRD_ETHER, ETH_ALEN, (uint8_t[16]){},
+                                    optlen, &optoffset) >= 0);
 
         assert_se(message->xid == htobe32(0x12345678));
         assert_se(message->op == BOOTREQUEST);
index a5c9bfeafbe40dfb9525ac8f325423095060d3fa..b0e37352c35f16eaae7123f4aa4781724439c9bc 100644 (file)
@@ -137,12 +137,12 @@ static void test_message_handler(void) {
         assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
 
         test.message.htype = 0;
-        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
+        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
         test.message.htype = ARPHRD_ETHER;
         assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
 
         test.message.hlen = 0;
-        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
+        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == -EBADMSG);
         test.message.hlen = ETHER_ADDR_LEN;
         assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
 
index b9ef6af631cb7298983b9c9a6a8c30581453edeb..eed0dae82fa1e6d841c33d08cd6d24e1f5f9c892 100644 (file)
@@ -110,7 +110,7 @@ static int set_interface_name(struct introspect *intro, const char *interface_na
         return free_and_strdup(&intro->interface_name, interface_name);
 }
 
-int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) {
+int introspect_write_child_nodes(struct introspect *i, OrderedSet *s, const char *prefix) {
         char *node;
 
         assert(i);
@@ -118,7 +118,7 @@ int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefi
 
         assert_se(set_interface_name(i, NULL) >= 0);
 
-        while ((node = set_steal_first(s))) {
+        while ((node = ordered_set_steal_first(s))) {
                 const char *e;
 
                 e = object_path_startswith(node, prefix);
index 34f32a4cf9f0da1efa928f0f78a0d545f0f4dea9..19e3ef09e24fdbd0575df896da0e486376014052 100644 (file)
@@ -5,7 +5,7 @@
 
 #include "sd-bus.h"
 
-#include "set.h"
+#include "ordered-set.h"
 
 struct introspect {
         FILE *f;
@@ -17,7 +17,7 @@ struct introspect {
 
 int introspect_begin(struct introspect *i, bool trusted);
 int introspect_write_default_interfaces(struct introspect *i, bool object_manager);
-int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix);
+int introspect_write_child_nodes(struct introspect *i, OrderedSet *s, const char *prefix);
 int introspect_write_interface(
                 struct introspect *i,
                 const char *interface_name,
index bf69539062dfd1ef44274d08d39c3442cfce65b4..40158a732601cacaa56369113ee36449bbcbc3ba 100644 (file)
@@ -9,7 +9,6 @@
 #include "bus-slot.h"
 #include "bus-type.h"
 #include "missing_capability.h"
-#include "set.h"
 #include "string-util.h"
 #include "strv.h"
 
@@ -99,7 +98,7 @@ static int add_enumerated_to_set(
                 sd_bus *bus,
                 const char *prefix,
                 struct node_enumerator *first,
-                Set *s,
+                OrderedSet *s,
                 sd_bus_error *error) {
 
         struct node_enumerator *c;
@@ -146,7 +145,7 @@ static int add_enumerated_to_set(
                                 continue;
                         }
 
-                        r = set_consume(s, *k);
+                        r = ordered_set_consume(s, *k);
                         if (r == -EEXIST)
                                 r = 0;
                 }
@@ -171,7 +170,7 @@ static int add_subtree_to_set(
                 const char *prefix,
                 struct node *n,
                 unsigned flags,
-                Set *s,
+                OrderedSet *s,
                 sd_bus_error *error) {
 
         struct node *i;
@@ -198,7 +197,7 @@ static int add_subtree_to_set(
                 if (!t)
                         return -ENOMEM;
 
-                r = set_consume(s, t);
+                r = ordered_set_consume(s, t);
                 if (r < 0 && r != -EEXIST)
                         return r;
 
@@ -220,10 +219,10 @@ static int get_child_nodes(
                 const char *prefix,
                 struct node *n,
                 unsigned flags,
-                Set **_s,
+                OrderedSet **_s,
                 sd_bus_error *error) {
 
-        Set *s = NULL;
+        OrderedSet *s = NULL;
         int r;
 
         assert(bus);
@@ -231,13 +230,13 @@ static int get_child_nodes(
         assert(n);
         assert(_s);
 
-        s = set_new(&string_hash_ops);
+        s = ordered_set_new(&string_hash_ops);
         if (!s)
                 return -ENOMEM;
 
         r = add_subtree_to_set(bus, prefix, n, flags, s, error);
         if (r < 0) {
-                set_free_free(s);
+                ordered_set_free_free(s);
                 return r;
         }
 
@@ -937,7 +936,7 @@ int introspect_path(
                 char **ret,
                 sd_bus_error *error) {
 
-        _cleanup_set_free_free_ Set *s = NULL;
+        _cleanup_ordered_set_free_ OrderedSet *s = NULL;
         _cleanup_(introspect_free) struct introspect intro = {};
         struct node_vtable *c;
         bool empty;
@@ -963,7 +962,7 @@ int introspect_path(
         if (r < 0)
                 return r;
 
-        empty = set_isempty(s);
+        empty = ordered_set_isempty(s);
 
         LIST_FOREACH(vtables, c, n->vtables) {
                 if (require_fallback && !c->is_fallback)
@@ -1233,7 +1232,7 @@ static int process_get_managed_objects(
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_set_free_free_ Set *s = NULL;
+        _cleanup_ordered_set_free_free_ OrderedSet *s = NULL;
         char *path;
         int r;
 
@@ -1263,7 +1262,7 @@ static int process_get_managed_objects(
         if (r < 0)
                 return r;
 
-        SET_FOREACH(path, s) {
+        ORDERED_SET_FOREACH(path, s) {
                 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
                 if (r < 0)
                         return bus_maybe_reply_error(m, r, &error);
@@ -2352,7 +2351,7 @@ _public_ int sd_bus_emit_properties_changed(
 static int object_added_append_all_prefix(
                 sd_bus *bus,
                 sd_bus_message *m,
-                Set *s,
+                OrderedSet *s,
                 const char *prefix,
                 const char *path,
                 bool require_fallback) {
@@ -2392,10 +2391,10 @@ static int object_added_append_all_prefix(
                          * skip it on any of its parents. The child vtables
                          * always fully override any conflicting vtables of
                          * any parent node. */
-                        if (set_get(s, c->interface))
+                        if (ordered_set_get(s, c->interface))
                                 continue;
 
-                        r = set_put(s, c->interface);
+                        r = ordered_set_put(s, c->interface);
                         if (r < 0)
                                 return r;
 
@@ -2441,7 +2440,7 @@ static int object_added_append_all_prefix(
 }
 
 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
-        _cleanup_set_free_ Set *s = NULL;
+        _cleanup_ordered_set_free_ OrderedSet *s = NULL;
         _cleanup_free_ char *prefix = NULL;
         size_t pl;
         int r;
@@ -2465,7 +2464,7 @@ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *p
          * a parent that were overwritten by a child.
          */
 
-        s = set_new(&string_hash_ops);
+        s = ordered_set_new(&string_hash_ops);
         if (!s)
                 return -ENOMEM;
 
@@ -2572,7 +2571,7 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
 static int object_removed_append_all_prefix(
                 sd_bus *bus,
                 sd_bus_message *m,
-                Set *s,
+                OrderedSet *s,
                 const char *prefix,
                 const char *path,
                 bool require_fallback) {
@@ -2605,7 +2604,7 @@ static int object_removed_append_all_prefix(
                  * skip it on any of its parents. The child vtables
                  * always fully override any conflicting vtables of
                  * any parent node. */
-                if (set_get(s, c->interface))
+                if (ordered_set_get(s, c->interface))
                         continue;
 
                 r = node_vtable_get_userdata(bus, path, c, &u, &error);
@@ -2616,7 +2615,7 @@ static int object_removed_append_all_prefix(
                 if (r == 0)
                         continue;
 
-                r = set_put(s, c->interface);
+                r = ordered_set_put(s, c->interface);
                 if (r < 0)
                         return r;
 
@@ -2631,7 +2630,7 @@ static int object_removed_append_all_prefix(
 }
 
 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
-        _cleanup_set_free_ Set *s = NULL;
+        _cleanup_ordered_set_free_ OrderedSet *s = NULL;
         _cleanup_free_ char *prefix = NULL;
         size_t pl;
         int r;
@@ -2642,7 +2641,7 @@ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char
 
         /* see sd_bus_emit_object_added() for details */
 
-        s = set_new(&string_hash_ops);
+        s = ordered_set_new(&string_hash_ops);
         if (!s)
                 return -ENOMEM;
 
index ece01e35f73c609907fb228c7cfbeb8f5a23b87d..27c91ea724f93a453997dd96d12f6bcca2c8aa85 100644 (file)
@@ -152,7 +152,9 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
         if (verify) {
                 r = chase_symlinks(_syspath, NULL, 0, &syspath, NULL);
                 if (r == -ENOENT)
-                        return -ENODEV; /* the device does not exist (any more?) */
+                         /* the device does not exist (any more?) */
+                        return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                               "sd-device: Failed to chase symlinks in \"%s\".", _syspath);
                 if (r < 0)
                         return log_debug_errno(r, "sd-device: Failed to get target of '%s': %m", _syspath);
 
@@ -173,7 +175,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
 
                         new_syspath = path_join("/sys", p);
                         if (!new_syspath)
-                                return -ENOMEM;
+                                return log_oom_debug();
 
                         free_and_replace(syspath, new_syspath);
                         path_simplify(syspath);
@@ -186,31 +188,35 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
                         path = strjoina(syspath, "/uevent");
                         if (access(path, F_OK) < 0) {
                                 if (errno == ENOENT)
-                                        /* this is not a valid device */
-                                        return -ENODEV;
+                                        /* This is not a valid device.
+                                         * Note, this condition is quite often satisfied when
+                                         * enumerating devices or finding a parent device.
+                                         * Hence, use log_trace_errno() here. */
+                                        return log_trace_errno(SYNTHETIC_ERRNO(ENODEV),
+                                                               "sd-device: the uevent file \"%s\" does not exist.", path);
 
                                 return log_debug_errno(errno, "sd-device: cannot access uevent file for %s: %m", syspath);
                         }
                 } else {
                         /* everything else just needs to be a directory */
                         if (!is_dir(syspath, false))
-                                return -ENODEV;
+                                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                                       "sd-device: the syspath \"%s\" is not a directory.", syspath);
                 }
         } else {
                 syspath = strdup(_syspath);
                 if (!syspath)
-                        return -ENOMEM;
+                        return log_oom_debug();
         }
 
         devpath = syspath + STRLEN("/sys");
 
         if (devpath[0] != '/')
-                /* '/sys' alone is not a valid device path */
-                return -ENODEV;
+                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "sd-device: \"/sys\" alone is not a valid device path.");
 
         r = device_add_property_internal(device, "DEVPATH", devpath);
         if (r < 0)
-                return r;
+                return log_debug_errno(r, "sd-device: Failed to add \"DEVPATH\" property for device \"%s\": %m", syspath);
 
         free_and_replace(device->syspath, syspath);
         device->devpath = devpath;
index dd257eadfe800931638ed218b7943ae4777ac582..82056998bd64cce6e17c84cd61ee6d1368060321 100644 (file)
@@ -2095,7 +2095,7 @@ _public_ int sd_event_add_inotify(
                 sd_event_inotify_handler_t callback,
                 void *userdata) {
 
-        sd_event_source *s;
+        sd_event_source *s = NULL; /* avoid false maybe-uninitialized warning */
         int fd, r;
 
         assert_return(path, -EINVAL);
index 0ff25c1f4729bf6462e7252f93e799501427e72b..83cbf4128e3177dd0e6d434aa0138a34160c1e24 100644 (file)
@@ -248,18 +248,18 @@ int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uin
         case OBJECT_DATA:
                 /* All but hash and payload are mutable */
                 gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
-                gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload));
+                gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
                 break;
 
         case OBJECT_FIELD:
                 /* Same here */
                 gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
-                gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload));
+                gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
                 break;
 
         case OBJECT_ENTRY:
                 /* All */
-                gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum));
+                gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
                 break;
 
         case OBJECT_FIELD_HASH_TABLE:
index ef4c261096fbde9a18190b093ccbc3f3cc9fdca4..dc866b89052fa6d70ac83e1831608b71607e871c 100644 (file)
@@ -618,10 +618,10 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                                                le64toh(o->data.n_entries),
                                                offset);
 
-                if (le64toh(o->object.size) <= offsetof(DataObject, payload))
+                if (le64toh(o->object.size) <= offsetof(Object, data.payload))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64,
-                                               offsetof(DataObject, payload),
+                                               offsetof(Object, data.payload),
                                                le64toh(o->object.size),
                                                offset);
 
@@ -640,10 +640,10 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                 break;
 
         case OBJECT_FIELD:
-                if (le64toh(o->object.size) <= offsetof(FieldObject, payload))
+                if (le64toh(o->object.size) <= offsetof(Object, field.payload))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Bad field size (<= %zu): %" PRIu64 ": %" PRIu64,
-                                               offsetof(FieldObject, payload),
+                                               offsetof(Object, field.payload),
                                                le64toh(o->object.size),
                                                offset);
 
@@ -660,18 +660,18 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                 uint64_t sz;
 
                 sz = le64toh(READ_NOW(o->object.size));
-                if (sz < offsetof(EntryObject, items) ||
-                    (sz - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
+                if (sz < offsetof(Object, entry.items) ||
+                    (sz - offsetof(Object, entry.items)) % sizeof(EntryItem) != 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64,
-                                               offsetof(EntryObject, items),
+                                               offsetof(Object, entry.items),
                                                sz,
                                                offset);
 
-                if ((sz - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
+                if ((sz - offsetof(Object, entry.items)) / sizeof(EntryItem) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid number items in entry: %" PRIu64 ": %" PRIu64,
-                                               (sz - offsetof(EntryObject, items)) / sizeof(EntryItem),
+                                               (sz - offsetof(Object, entry.items)) / sizeof(EntryItem),
                                                offset);
 
                 if (le64toh(o->entry.seqnum) <= 0)
@@ -700,9 +700,9 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                 uint64_t sz;
 
                 sz = le64toh(READ_NOW(o->object.size));
-                if (sz < offsetof(HashTableObject, items) ||
-                    (sz - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
-                    (sz - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
+                if (sz < offsetof(Object, hash_table.items) ||
+                    (sz - offsetof(Object, hash_table.items)) % sizeof(HashItem) != 0 ||
+                    (sz - offsetof(Object, hash_table.items)) / sizeof(HashItem) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid %s hash table size: %" PRIu64 ": %" PRIu64,
                                                o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
@@ -716,9 +716,9 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                 uint64_t sz;
 
                 sz = le64toh(READ_NOW(o->object.size));
-                if (sz < offsetof(EntryArrayObject, items) ||
-                    (sz - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
-                    (sz - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
+                if (sz < offsetof(Object, entry_array.items) ||
+                    (sz - offsetof(Object, entry_array.items)) % sizeof(le64_t) != 0 ||
+                    (sz - offsetof(Object, entry_array.items)) / sizeof(le64_t) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid object entry array size: %" PRIu64 ": %" PRIu64,
                                                sz,
@@ -758,7 +758,6 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
         uint64_t s;
 
         assert(f);
-        assert(ret);
 
         /* Objects may only be located at multiple of 64 bit */
         if (!VALID64(offset))
@@ -813,7 +812,9 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
         if (r < 0)
                 return r;
 
-        *ret = o;
+        if (ret)
+                *ret = o;
+
         return 0;
 }
 
@@ -823,7 +824,6 @@ int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, O
         uint64_t s;
 
         assert(f);
-        assert(ret);
 
         /* Objects may only be located at multiple of 64 bit */
         if (!VALID64(offset))
@@ -872,7 +872,9 @@ int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, O
         if (r < 0)
                 return r;
 
-        *ret = o;
+        if (ret)
+                *ret = o;
+
         return 0;
 }
 
@@ -1453,19 +1455,11 @@ static int journal_file_append_field(
 
         hash = journal_file_hash_data(f, field, size);
 
-        r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p);
+        r = journal_file_find_field_object_with_hash(f, field, size, hash, ret, ret_offset);
         if (r < 0)
                 return r;
-        if (r > 0) {
-
-                if (ret)
-                        *ret = o;
-
-                if (ret_offset)
-                        *ret_offset = p;
-
+        if (r > 0)
                 return 0;
-        }
 
         osize = offsetof(Object, field.payload) + size;
         r = journal_file_append_object(f, OBJECT_FIELD, osize, &o, &p);
@@ -1479,20 +1473,20 @@ static int journal_file_append_field(
         if (r < 0)
                 return r;
 
-        /* The linking might have altered the window, so let's
-         * refresh our pointer */
-        r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o);
-        if (r < 0)
-                return r;
+        /* The linking might have altered the window, so let's only pass the offset to hmac which will
+         * move to the object again if needed. */
 
 #if HAVE_GCRYPT
-        r = journal_file_hmac_put_object(f, OBJECT_FIELD, o, p);
+        r = journal_file_hmac_put_object(f, OBJECT_FIELD, NULL, p);
         if (r < 0)
                 return r;
 #endif
 
-        if (ret)
-                *ret = o;
+        if (ret) {
+                r = journal_file_move_to_object(f, OBJECT_FIELD, p, ret);
+                if (r < 0)
+                        return r;
+        }
 
         if (ret_offset)
                 *ret_offset = p;
@@ -1517,19 +1511,11 @@ static int journal_file_append_data(
 
         hash = journal_file_hash_data(f, data, size);
 
-        r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
+        r = journal_file_find_data_object_with_hash(f, data, size, hash, ret, ret_offset);
         if (r < 0)
                 return r;
-        if (r > 0) {
-
-                if (ret)
-                        *ret = o;
-
-                if (ret_offset)
-                        *ret_offset = p;
-
+        if (r > 0)
                 return 0;
-        }
 
         eq = memchr(data, '=', size);
         if (!eq)
@@ -1567,17 +1553,16 @@ static int journal_file_append_data(
         if (r < 0)
                 return r;
 
-#if HAVE_GCRYPT
-        r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
+        /* The linking might have altered the window, so let's refresh our pointer. */
+        r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
         if (r < 0)
                 return r;
-#endif
 
-        /* The linking might have altered the window, so let's
-         * refresh our pointer */
-        r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
+#if HAVE_GCRYPT
+        r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
         if (r < 0)
                 return r;
+#endif
 
         /* Create field object ... */
         r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, &fp);
@@ -1801,12 +1786,20 @@ static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
         /* Link up the items */
         n = journal_file_entry_n_items(o);
         for (uint64_t i = 0; i < n; i++) {
-                r = journal_file_link_entry_item(f, o, offset, i);
-                if (r < 0)
-                        return r;
+                int k;
+
+                /* If we fail to link an entry item because we can't allocate a new entry array, don't fail
+                 * immediately but try to link the other entry items since it might still be possible to link
+                 * those if they don't require a new entry array to be allocated. */
+
+                k = journal_file_link_entry_item(f, o, offset, i);
+                if (k == -E2BIG)
+                        r = k;
+                else if (k < 0)
+                        return k;
         }
 
-        return 0;
+        return r;
 }
 
 static int journal_file_append_entry_internal(
@@ -2126,7 +2119,7 @@ static int generic_array_get(
                 direction_t direction,
                 Object **ret, uint64_t *ret_offset) {
 
-        Object *o, *e;
+        Object *o;
         uint64_t p = 0, a, t = 0, k;
         int r;
         ChainCacheItem *ci;
@@ -2178,9 +2171,16 @@ static int generic_array_get(
                 do {
                         p = le64toh(o->entry_array.items[i]);
 
-                        r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &e);
-                        if (r >= 0)
-                                goto found;
+                        r = journal_file_move_to_object(f, OBJECT_ENTRY, p, ret);
+                        if (r >= 0) {
+                                /* Let's cache this item for the next invocation */
+                                chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i);
+
+                                if (ret_offset)
+                                        *ret_offset = p;
+
+                                return 1;
+                        }
                         if (!IN_SET(r, -EADDRNOTAVAIL, -EBADMSG))
                                 return r;
 
@@ -2195,18 +2195,6 @@ static int generic_array_get(
         }
 
         return 0;
-
-found:
-        /* Let's cache this item for the next invocation */
-        chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i);
-
-        if (ret)
-                *ret = e;
-
-        if (ret_offset)
-                *ret_offset = p;
-
-        return 1;
 }
 
 static int generic_array_get_plus_one(
@@ -2217,21 +2205,17 @@ static int generic_array_get_plus_one(
                 direction_t direction,
                 Object **ret, uint64_t *ret_offset) {
 
-        Object *o;
         int r;
 
         assert(f);
 
         if (i == 0) {
-                r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
+                r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, ret);
                 if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG))
                         return generic_array_get(f, first, 0, direction, ret, ret_offset);
                 if (r < 0)
                         return r;
 
-                if (ret)
-                        *ret = o;
-
                 if (ret_offset)
                         *ret_offset = extra;
 
@@ -2260,7 +2244,7 @@ static int generic_array_bisect(
 
         uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = UINT64_MAX;
         bool subtract_one = false;
-        Object *o, *array = NULL;
+        Object *array = NULL;
         int r;
         ChainCacheItem *ci;
 
@@ -2448,12 +2432,11 @@ found:
         else
                 p = le64toh(array->entry_array.items[i]);
 
-        r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
-        if (r < 0)
-                return r;
-
-        if (ret)
-                *ret = o;
+        if (ret) {
+                r = journal_file_move_to_object(f, OBJECT_ENTRY, p, ret);
+                if (r < 0)
+                        return r;
+        }
 
         if (ret_offset)
                 *ret_offset = p;
@@ -2478,7 +2461,6 @@ static int generic_array_bisect_plus_one(
 
         int r;
         bool step_back = false;
-        Object *o;
 
         assert(f);
         assert(test_object);
@@ -2521,12 +2503,11 @@ static int generic_array_bisect_plus_one(
         return r;
 
 found:
-        r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
-        if (r < 0)
-                return r;
-
-        if (ret)
-                *ret = o;
+        if (ret) {
+                r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, ret);
+                if (r < 0)
+                        return r;
+        }
 
         if (ret_offset)
                 *ret_offset = extra;
@@ -2549,6 +2530,26 @@ _pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle
                 return TEST_RIGHT;
 }
 
+int journal_file_move_to_entry_by_offset(
+                JournalFile *f,
+                uint64_t p,
+                direction_t direction,
+                Object **ret,
+                uint64_t *ret_offset) {
+
+        assert(f);
+        assert(f->header);
+
+        return generic_array_bisect(
+                        f,
+                        le64toh(f->header->entry_array_offset),
+                        le64toh(f->header->n_entries),
+                        p,
+                        test_object_offset,
+                        direction,
+                        ret, ret_offset, NULL);
+}
+
 static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
         uint64_t sq;
         Object *o;
@@ -2828,19 +2829,16 @@ int journal_file_next_entry(
 
 int journal_file_next_entry_for_data(
                 JournalFile *f,
-                uint64_t data_offset,
+                Object *d,
                 direction_t direction,
                 Object **ret, uint64_t *ret_offset) {
 
         uint64_t i, n, ofs;
-        Object *d;
         int r;
 
         assert(f);
-
-        r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
-        if (r < 0)
-                return r;
+        assert(d);
+        assert(d->object.type == OBJECT_DATA);
 
         n = le64toh(READ_NOW(d->data.n_entries));
         if (n <= 0)
@@ -2865,19 +2863,14 @@ int journal_file_next_entry_for_data(
 
 int journal_file_move_to_entry_by_offset_for_data(
                 JournalFile *f,
-                uint64_t data_offset,
+                Object *d,
                 uint64_t p,
                 direction_t direction,
                 Object **ret, uint64_t *ret_offset) {
 
-        int r;
-        Object *d;
-
         assert(f);
-
-        r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
-        if (r < 0)
-                return r;
+        assert(d);
+        assert(d->object.type == OBJECT_DATA);
 
         return generic_array_bisect_plus_one(
                         f,
@@ -2892,17 +2885,24 @@ int journal_file_move_to_entry_by_offset_for_data(
 
 int journal_file_move_to_entry_by_monotonic_for_data(
                 JournalFile *f,
-                uint64_t data_offset,
+                Object *d,
                 sd_id128_t boot_id,
                 uint64_t monotonic,
                 direction_t direction,
                 Object **ret, uint64_t *ret_offset) {
 
-        Object *o, *d;
+        Object *o;
         int r;
-        uint64_t b, z;
+        uint64_t b, z, entry_offset, entry_array_offset, n_entries;
 
         assert(f);
+        assert(d);
+        assert(d->object.type == OBJECT_DATA);
+
+        /* Save all the required data before the data object gets invalidated. */
+        entry_offset = le64toh(READ_NOW(d->data.entry_offset));
+        entry_array_offset = le64toh(READ_NOW(d->data.entry_array_offset));
+        n_entries = le64toh(READ_NOW(d->data.n_entries));
 
         /* First, seek by time */
         r = find_data_object_by_boot_id(f, boot_id, &o, &b);
@@ -2925,18 +2925,17 @@ int journal_file_move_to_entry_by_monotonic_for_data(
         /* And now, continue seeking until we find an entry that
          * exists in both bisection arrays */
 
+        r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
+        if (r < 0)
+                return r;
+
         for (;;) {
-                Object *qo;
                 uint64_t p, q;
 
-                r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
-                if (r < 0)
-                        return r;
-
                 r = generic_array_bisect_plus_one(f,
-                                                  le64toh(d->data.entry_offset),
-                                                  le64toh(d->data.entry_array_offset),
-                                                  le64toh(d->data.n_entries),
+                                                  entry_offset,
+                                                  entry_array_offset,
+                                                  n_entries,
                                                   z,
                                                   test_object_offset,
                                                   direction,
@@ -2944,10 +2943,6 @@ int journal_file_move_to_entry_by_monotonic_for_data(
                 if (r <= 0)
                         return r;
 
-                r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
-                if (r < 0)
-                        return r;
-
                 r = generic_array_bisect_plus_one(f,
                                                   le64toh(o->data.entry_offset),
                                                   le64toh(o->data.entry_array_offset),
@@ -2955,14 +2950,18 @@ int journal_file_move_to_entry_by_monotonic_for_data(
                                                   p,
                                                   test_object_offset,
                                                   direction,
-                                                  &qo, &q, NULL);
+                                                  NULL, &q, NULL);
 
                 if (r <= 0)
                         return r;
 
                 if (p == q) {
-                        if (ret)
-                                *ret = qo;
+                        if (ret) {
+                                r = journal_file_move_to_object(f, OBJECT_ENTRY, q, ret);
+                                if (r < 0)
+                                        return r;
+                        }
+
                         if (ret_offset)
                                 *ret_offset = q;
 
@@ -2975,19 +2974,14 @@ int journal_file_move_to_entry_by_monotonic_for_data(
 
 int journal_file_move_to_entry_by_seqnum_for_data(
                 JournalFile *f,
-                uint64_t data_offset,
+                Object *d,
                 uint64_t seqnum,
                 direction_t direction,
                 Object **ret, uint64_t *ret_offset) {
 
-        Object *d;
-        int r;
-
         assert(f);
-
-        r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
-        if (r < 0)
-                return r;
+        assert(d);
+        assert(d->object.type == OBJECT_DATA);
 
         return generic_array_bisect_plus_one(
                         f,
@@ -3002,19 +2996,14 @@ int journal_file_move_to_entry_by_seqnum_for_data(
 
 int journal_file_move_to_entry_by_realtime_for_data(
                 JournalFile *f,
-                uint64_t data_offset,
+                Object *d,
                 uint64_t realtime,
                 direction_t direction,
                 Object **ret, uint64_t *ret_offset) {
 
-        Object *d;
-        int r;
-
         assert(f);
-
-        r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
-        if (r < 0)
-                return r;
+        assert(d);
+        assert(d->object.type == OBJECT_DATA);
 
         return generic_array_bisect_plus_one(
                         f,
@@ -3274,7 +3263,7 @@ int journal_file_open(
         r = getenv_bool("SYSTEMD_JOURNAL_KEYED_HASH");
         if (r < 0) {
                 if (r != -ENXIO)
-                        log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring.");
+                        log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring: %m");
                 f->keyed_hash = true;
         } else
                 f->keyed_hash = r;
@@ -3576,21 +3565,16 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
 
         for (uint64_t i = 0; i < n; i++) {
                 uint64_t l, h;
-                le64_t le_hash;
                 size_t t;
                 void *data;
                 Object *u;
 
                 q = le64toh(o->entry.items[i].object_offset);
-                le_hash = o->entry.items[i].hash;
 
                 r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
                 if (r < 0)
                         return r;
 
-                if (le_hash != o->data.hash)
-                        return -EBADMSG;
-
                 l = le64toh(READ_NOW(o->object.size));
                 if (l < offsetof(Object, data.payload))
                         return -EBADMSG;
index 39e91d71c45db1741b956693a49030c5f993b71c..f673e05e72baecd15f0203ea1931e9ed5005a212 100644 (file)
@@ -193,7 +193,7 @@ uint64_t journal_file_entry_n_items(Object *o) _pure_;
 uint64_t journal_file_entry_array_n_items(Object *o) _pure_;
 uint64_t journal_file_hash_table_n_items(Object *o) _pure_;
 
-int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset);
+int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *ret_offset);
 int journal_file_append_entry(
                 JournalFile *f,
                 const dual_timestamp *ts,
@@ -201,29 +201,30 @@ int journal_file_append_entry(
                 const struct iovec iovec[], unsigned n_iovec,
                 uint64_t *seqno,
                 Object **ret,
-                uint64_t *offset);
+                uint64_t *ret_offset);
 
-int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset);
-int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset);
+int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *ret_offset);
+int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *ret_offset);
 
-int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *offset);
-int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset);
+int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *ret_offset);
+int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *ret_offset);
 
 void journal_file_reset_location(JournalFile *f);
 void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset);
 int journal_file_compare_locations(JournalFile *af, JournalFile *bf);
-int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *offset);
+int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset);
 
-int journal_file_next_entry_for_data(JournalFile *f, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset);
+int journal_file_next_entry_for_data(JournalFile *f, Object *d, direction_t direction, Object **ret, uint64_t *ret_offset);
 
-int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset);
+int journal_file_move_to_entry_by_offset(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *ret_offset);
 
-int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, uint64_t data_offset, uint64_t p, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, uint64_t data_offset, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, uint64_t data_offset, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset);
-int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, uint64_t data_offset, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset);
+int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, Object *d, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, Object *d, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, Object *d, uint64_t realtime, direction_t direction, Object **ret, uint64_t *ret_offset);
+int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, Object *d, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *ret_offset);
 
 int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p);
 
index 8288ebcd6e91341a9c9d5329ab029cb85c0fe149..56eaecb1014049fad55ff33c6d985b6b3e3fbe82 100644 (file)
@@ -137,8 +137,6 @@ static int hash_payload(JournalFile *f, Object *o, uint64_t offset, const uint8_
 }
 
 static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
-        uint64_t i;
-
         assert(f);
         assert(offset);
         assert(o);
@@ -169,9 +167,9 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         return -EBADMSG;
                 }
 
-                if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
+                if (le64toh(o->object.size) - offsetof(Object, data.payload) <= 0) {
                         error(offset, "Bad object size (<= %zu): %"PRIu64,
-                              offsetof(DataObject, payload),
+                              offsetof(Object, data.payload),
                               le64toh(o->object.size));
                         return -EBADMSG;
                 }
@@ -207,10 +205,10 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                 uint64_t h1, h2;
                 int r;
 
-                if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
+                if (le64toh(o->object.size) - offsetof(Object, field.payload) <= 0) {
                         error(offset,
                               "Bad field size (<= %zu): %"PRIu64,
-                              offsetof(FieldObject, payload),
+                              offsetof(Object, field.payload),
                               le64toh(o->object.size));
                         return -EBADMSG;
                 }
@@ -239,18 +237,18 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
         }
 
         case OBJECT_ENTRY:
-                if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
+                if ((le64toh(o->object.size) - offsetof(Object, entry.items)) % sizeof(EntryItem) != 0) {
                         error(offset,
                               "Bad entry size (<= %zu): %"PRIu64,
-                              offsetof(EntryObject, items),
+                              offsetof(Object, entry.items),
                               le64toh(o->object.size));
                         return -EBADMSG;
                 }
 
-                if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
+                if ((le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem) <= 0) {
                         error(offset,
                               "Invalid number items in entry: %"PRIu64,
-                              (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
+                              (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem));
                         return -EBADMSG;
                 }
 
@@ -275,7 +273,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         return -EBADMSG;
                 }
 
-                for (i = 0; i < journal_file_entry_n_items(o); i++) {
+                for (uint64_t i = 0; i < journal_file_entry_n_items(o); i++) {
                         if (le64toh(o->entry.items[i].object_offset) == 0 ||
                             !VALID64(le64toh(o->entry.items[i].object_offset))) {
                                 error(offset,
@@ -290,8 +288,8 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
 
         case OBJECT_DATA_HASH_TABLE:
         case OBJECT_FIELD_HASH_TABLE:
-                if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
-                    (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
+                if ((le64toh(o->object.size) - offsetof(Object, hash_table.items)) % sizeof(HashItem) != 0 ||
+                    (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem) <= 0) {
                         error(offset,
                               "Invalid %s size: %"PRIu64,
                               journal_object_type_to_string(o->object.type),
@@ -299,7 +297,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         return -EBADMSG;
                 }
 
-                for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
+                for (uint64_t i = 0; i < journal_file_hash_table_n_items(o); i++) {
                         if (o->hash_table.items[i].head_hash_offset != 0 &&
                             !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
                                 error(offset,
@@ -334,8 +332,8 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                 break;
 
         case OBJECT_ENTRY_ARRAY:
-                if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
-                    (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
+                if ((le64toh(o->object.size) - offsetof(Object, entry_array.items)) % sizeof(le64_t) != 0 ||
+                    (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(le64_t) <= 0) {
                         error(offset,
                               "Invalid object entry array size: %"PRIu64,
                               le64toh(o->object.size));
@@ -349,7 +347,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                         return -EBADMSG;
                 }
 
-                for (i = 0; i < journal_file_entry_array_n_items(o); i++)
+                for (uint64_t i = 0; i < journal_file_entry_array_n_items(o); i++)
                         if (le64toh(o->entry_array.items[i]) != 0 &&
                             !VALID64(le64toh(o->entry_array.items[i]))) {
                                 error(offset,
@@ -422,92 +420,6 @@ static int contains_uint64(MMapFileDescriptor *f, uint64_t n, uint64_t p) {
         return 0;
 }
 
-static int entry_points_to_data(
-                JournalFile *f,
-                MMapFileDescriptor *cache_entry_fd,
-                uint64_t n_entries,
-                uint64_t entry_p,
-                uint64_t data_p) {
-
-        int r;
-        uint64_t i, n, a;
-        Object *o;
-        bool found = false;
-
-        assert(f);
-        assert(cache_entry_fd);
-
-        if (!contains_uint64(cache_entry_fd, n_entries, entry_p)) {
-                error(data_p, "Data object references invalid entry at "OFSfmt, entry_p);
-                return -EBADMSG;
-        }
-
-        r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
-        if (r < 0)
-                return r;
-
-        n = journal_file_entry_n_items(o);
-        for (i = 0; i < n; i++)
-                if (le64toh(o->entry.items[i].object_offset) == data_p) {
-                        found = true;
-                        break;
-                }
-
-        if (!found) {
-                error(entry_p, "Data object at "OFSfmt" not referenced by linked entry", data_p);
-                return -EBADMSG;
-        }
-
-        /* Check if this entry is also in main entry array. Since the
-         * main entry array has already been verified we can rely on
-         * its consistency. */
-
-        i = 0;
-        n = le64toh(f->header->n_entries);
-        a = le64toh(f->header->entry_array_offset);
-
-        while (i < n) {
-                uint64_t m, u;
-
-                r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
-                if (r < 0)
-                        return r;
-
-                m = journal_file_entry_array_n_items(o);
-                u = MIN(n - i, m);
-
-                if (entry_p <= le64toh(o->entry_array.items[u-1])) {
-                        uint64_t x, y, z;
-
-                        x = 0;
-                        y = u;
-
-                        while (x < y) {
-                                z = (x + y) / 2;
-
-                                if (le64toh(o->entry_array.items[z]) == entry_p)
-                                        return 0;
-
-                                if (x + 1 >= y)
-                                        break;
-
-                                if (entry_p < le64toh(o->entry_array.items[z]))
-                                        y = z;
-                                else
-                                        x = z;
-                        }
-
-                        error(entry_p, "Entry object doesn't exist in main entry array");
-                        return -EBADMSG;
-                }
-
-                i += u;
-                a = le64toh(o->entry_array.next_entry_array_offset);
-        }
-
-        return 0;
-}
-
 static int verify_data(
                 JournalFile *f,
                 Object *o, uint64_t p,
@@ -538,9 +450,18 @@ static int verify_data(
         assert(o->data.entry_offset);
 
         last = q = le64toh(o->data.entry_offset);
-        r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p);
+        if (!contains_uint64(cache_entry_fd, n_entries, q)) {
+                error(p, "Data object references invalid entry at "OFSfmt, q);
+                return -EBADMSG;
+        }
+
+        r = journal_file_move_to_entry_by_offset(f, q, DIRECTION_DOWN, NULL, NULL);
         if (r < 0)
                 return r;
+        if (r == 0) {
+                error(q, "Entry object doesn't exist in the main entry array");
+                return -EBADMSG;
+        }
 
         i = 1;
         while (i < n) {
@@ -576,9 +497,18 @@ static int verify_data(
                         }
                         last = q;
 
-                        r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p);
+                        if (!contains_uint64(cache_entry_fd, n_entries, q)) {
+                                error(p, "Data object references invalid entry at "OFSfmt, q);
+                                return -EBADMSG;
+                        }
+
+                        r = journal_file_move_to_entry_by_offset(f, q, DIRECTION_DOWN, NULL, NULL);
                         if (r < 0)
                                 return r;
+                        if (r == 0) {
+                                error(q, "Entry object doesn't exist in the main entry array");
+                                return -EBADMSG;
+                        }
 
                         /* Pointer might have moved, reposition */
                         r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
@@ -703,7 +633,8 @@ static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p)
 static int verify_entry(
                 JournalFile *f,
                 Object *o, uint64_t p,
-                MMapFileDescriptor *cache_data_fd, uint64_t n_data) {
+                MMapFileDescriptor *cache_data_fd, uint64_t n_data,
+                bool last) {
 
         uint64_t i, n;
         int r;
@@ -714,11 +645,10 @@ static int verify_entry(
 
         n = journal_file_entry_n_items(o);
         for (i = 0; i < n; i++) {
-                uint64_t q, h;
+                uint64_t q;
                 Object *u;
 
                 q = le64toh(o->entry.items[i].object_offset);
-                h = le64toh(o->entry.items[i].hash);
 
                 if (!contains_uint64(cache_data_fd, n_data, q)) {
                         error(p, "Invalid data object of entry");
@@ -729,16 +659,23 @@ static int verify_entry(
                 if (r < 0)
                         return r;
 
-                if (le64toh(u->data.hash) != h) {
-                        error(p, "Hash mismatch for data object of entry");
+                r = data_object_in_hash_table(f, le64toh(u->data.hash), q);
+                if (r < 0)
+                        return r;
+                if (r == 0) {
+                        error(p, "Data object missing from hash table");
                         return -EBADMSG;
                 }
 
-                r = data_object_in_hash_table(f, h, q);
+                r = journal_file_move_to_entry_by_offset_for_data(f, u, p, DIRECTION_DOWN, NULL, NULL);
                 if (r < 0)
                         return r;
-                if (r == 0) {
-                        error(p, "Data object missing from hash table");
+
+                /* The last entry object has a very high chance of not being referenced as journal files
+                 * almost always run out of space during linking of entry items when trying to add a new
+                 * entry array so let's not error in that scenario. */
+                if (r == 0 && !last) {
+                        error(p, "Entry object not referenced by linked data object at "OFSfmt, q);
                         return -EBADMSG;
                 }
         }
@@ -812,7 +749,7 @@ static int verify_entry_array(
                         if (r < 0)
                                 return r;
 
-                        r = verify_entry(f, o, p, cache_data_fd, n_data);
+                        r = verify_entry(f, o, p, cache_data_fd, n_data, /*last=*/ i + 1 == n);
                         if (r < 0)
                                 return r;
 
@@ -842,21 +779,21 @@ static int verify_hash_table(
                 return -EBADMSG;
         }
 
-        if (header_offset != p + offsetof(HashTableObject, items)) {
+        if (header_offset != p + offsetof(Object, hash_table.items)) {
                 error(p,
                       "Header offset for %s invalid (%" PRIu64 " != %" PRIu64 ")",
                       journal_object_type_to_string(o->object.type),
                       header_offset,
-                      p + offsetof(HashTableObject, items));
+                      p + offsetof(Object, hash_table.items));
                 return -EBADMSG;
         }
 
-        if (header_size != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
+        if (header_size != le64toh(o->object.size) - offsetof(Object, hash_table.items)) {
                 error(p,
                       "Header size for %s invalid (%" PRIu64 " != %" PRIu64 ")",
                       journal_object_type_to_string(o->object.type),
                       header_size,
-                      le64toh(o->object.size) - offsetof(HashTableObject, items));
+                      le64toh(o->object.size) - offsetof(Object, hash_table.items));
                 return -EBADMSG;
         }
 
index 7a6cc4aca35491925c3860e8142b3ccaa57933f8..644b9957b0e01d638014457f7c8d8b894a94054e 100644 (file)
@@ -501,7 +501,8 @@ static int next_for_match(
         assert(f);
 
         if (m->type == MATCH_DISCRETE) {
-                uint64_t dp, hash;
+                Object *d;
+                uint64_t hash;
 
                 /* If the keyed hash logic is used, we need to calculate the hash fresh per file. Otherwise
                  * we can use what we pre-calculated. */
@@ -510,11 +511,11 @@ static int next_for_match(
                 else
                         hash = m->hash;
 
-                r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp);
+                r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, &d, NULL);
                 if (r <= 0)
                         return r;
 
-                return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset);
+                return journal_file_move_to_entry_by_offset_for_data(f, d, after_offset, direction, ret, offset);
 
         } else if (m->type == MATCH_OR_TERM) {
                 Match *i;
@@ -597,6 +598,7 @@ static int find_location_for_match(
         assert(f);
 
         if (m->type == MATCH_DISCRETE) {
+                Object *d;
                 uint64_t dp, hash;
 
                 if (JOURNAL_HEADER_KEYED_HASH(f->header))
@@ -604,27 +606,32 @@ static int find_location_for_match(
                 else
                         hash = m->hash;
 
-                r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp);
+                r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, &d, &dp);
                 if (r <= 0)
                         return r;
 
                 /* FIXME: missing: find by monotonic */
 
                 if (j->current_location.type == LOCATION_HEAD)
-                        return journal_file_next_entry_for_data(f, dp, DIRECTION_DOWN, ret, offset);
+                        return journal_file_next_entry_for_data(f, d, DIRECTION_DOWN, ret, offset);
                 if (j->current_location.type == LOCATION_TAIL)
-                        return journal_file_next_entry_for_data(f, dp, DIRECTION_UP, ret, offset);
+                        return journal_file_next_entry_for_data(f, d, DIRECTION_UP, ret, offset);
                 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
-                        return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset);
+                        return journal_file_move_to_entry_by_seqnum_for_data(f, d, j->current_location.seqnum, direction, ret, offset);
                 if (j->current_location.monotonic_set) {
-                        r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
+                        r = journal_file_move_to_entry_by_monotonic_for_data(f, d, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
                         if (r != -ENOENT)
                                 return r;
+
+                        /* The data object might have been invalidated. */
+                        r = journal_file_move_to_object(f, OBJECT_DATA, dp, &d);
+                        if (r < 0)
+                                return r;
                 }
                 if (j->current_location.realtime_set)
-                        return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset);
+                        return journal_file_move_to_entry_by_realtime_for_data(f, d, j->current_location.realtime, direction, ret, offset);
 
-                return journal_file_next_entry_for_data(f, dp, direction, ret, offset);
+                return journal_file_next_entry_for_data(f, d, direction, ret, offset);
 
         } else if (m->type == MATCH_OR_TERM) {
                 uint64_t np = 0;
@@ -2296,12 +2303,10 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **
         for (i = 0; i < n; i++) {
                 Object *d;
                 uint64_t p, l;
-                le64_t le_hash;
                 size_t t;
                 int compression;
 
                 p = le64toh(o->entry.items[i].object_offset);
-                le_hash = o->entry.items[i].hash;
                 r = journal_file_move_to_object(f, OBJECT_DATA, p, &d);
                 if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) {
                         log_debug_errno(r, "Entry item %"PRIu64" data object is bad, skipping over it: %m", i);
@@ -2310,11 +2315,6 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **
                 if (r < 0)
                         return r;
 
-                if (le_hash != d->data.hash) {
-                        log_debug("Entry item %"PRIu64" hash is bad, skipping over it.", i);
-                        continue;
-                }
-
                 l = le64toh(d->object.size) - offsetof(Object, data.payload);
 
                 compression = d->object.flags & OBJECT_COMPRESSION_MASK;
@@ -2443,10 +2443,8 @@ _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t
 
         for (uint64_t n = journal_file_entry_n_items(o); j->current_field < n; j->current_field++) {
                 uint64_t p;
-                le64_t le_hash;
 
                 p = le64toh(o->entry.items[j->current_field].object_offset);
-                le_hash = o->entry.items[j->current_field].hash;
                 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
                 if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) {
                         log_debug_errno(r, "Entry item %"PRIu64" data object is bad, skipping over it: %m", j->current_field);
@@ -2455,11 +2453,6 @@ _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t
                 if (r < 0)
                         return r;
 
-                if (le_hash != o->data.hash) {
-                        log_debug("Entry item %"PRIu64" hash is bad, skipping over it.", j->current_field);
-                        continue;
-                }
-
                 r = return_data(j, f, o, data, size);
                 if (r == -EBADMSG) {
                         log_debug("Entry item %"PRIu64" data payload is bad, skipping over it.", j->current_field);
index 971247714ac5bc6192b155fd0d2d3e32c06afdf7..2059567ef894f031c33437ef3f4cd685ed31a37a 100644 (file)
@@ -135,3 +135,23 @@ int parse_operational_state_range(const char *str, LinkOperationalStateRange *ou
 
         return 0;
 }
+
+int network_link_get_operational_state(int ifindex, LinkOperationalState *ret) {
+        _cleanup_free_ char *str = NULL;
+        LinkOperationalState s;
+        int r;
+
+        assert(ifindex > 0);
+        assert(ret);
+
+        r = sd_network_link_get_operational_state(ifindex, &str);
+        if (r < 0)
+                return r;
+
+        s = link_operstate_from_string(str);
+        if (s < 0)
+                return s;
+
+        *ret = s;
+        return 0;
+}
index 6e2ad28426302de4cad6f4afcccce1122df41a1a..c47e271a768fd29ab04438b0dc98fc173f150cb1 100644 (file)
@@ -83,3 +83,4 @@ typedef struct LinkOperationalStateRange {
                                                                    LINK_OPERSTATE_ROUTABLE }
 
 int parse_operational_state_range(const char *str, LinkOperationalStateRange *out);
+int network_link_get_operational_state(int ifindex, LinkOperationalState *ret);
index cffe445919ec82bf3835f4fe23d6a92ac6c17f72..5c4341df2bf4c24bace0a179b1bc2a036e645b10 100644 (file)
@@ -2923,7 +2923,7 @@ static int boot_loader_entry_exists(Manager *m, const char *id) {
 
         r = manager_read_efi_boot_loader_entries(m);
         if (r >= 0)
-                (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true);
+                (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries);
 
         return boot_config_has_entry(&config, id);
 }
@@ -3081,7 +3081,7 @@ static int property_get_boot_loader_entries(
 
         r = manager_read_efi_boot_loader_entries(m);
         if (r >= 0)
-                (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true);
+                (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries);
 
         r = sd_bus_message_open_container(reply, 'a', "s");
         if (r < 0)
index 2d084e134df9681d2e1f54c64d1d1a6b38dc490c..926bd6cfe9812d755418e45d05c305d2b12c5163 100644 (file)
@@ -39,7 +39,7 @@
 #IdleAction=ignore
 #IdleActionSec=30min
 #RuntimeDirectorySize=10%
-#RuntimeDirectoryInodes=400k
+#RuntimeDirectoryInodesMax=400k
 #RemoveIPC=yes
 #InhibitorsMax=8192
 #SessionsMax=8192
index 5ce5b35e178681801cce0c7394be84857ce7060f..f96a2d8662350cd9c95a61146c97457555283b9f 100644 (file)
@@ -80,7 +80,9 @@ static int user_mkdir_runtime_path(
                          uid, gid, runtime_dir_size, runtime_dir_inodes,
                          mac_smack_use() ? ",smackfsroot=*" : "");
 
-                (void) mkdir_label(runtime_path, 0700);
+                r = mkdir_label(runtime_path, 0700);
+                if (r < 0 && r != -EEXIST)
+                        return log_error_errno(r, "Failed to create %s: %m", runtime_path);
 
                 r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options);
                 if (r < 0) {
index e6ffb52924c0f845f641e5f68ee3419e06400b1b..6d145c762299b5f664a6c6d742d1c4140e673b37 100644 (file)
@@ -43,11 +43,17 @@ int bus_image_method_remove(
         if (m->n_operations >= OPERATIONS_MAX)
                 return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
 
+        const char *details[] = {
+                "image", image->name,
+                "verb", "remove",
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->polkit_registry,
@@ -108,11 +114,18 @@ int bus_image_method_rename(
         if (!image_name_is_valid(new_name))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
 
+        const char *details[] = {
+                "image", image->name,
+                "verb", "rename",
+                "new_name", new_name,
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->polkit_registry,
@@ -155,11 +168,18 @@ int bus_image_method_clone(
         if (!image_name_is_valid(new_name))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
 
+        const char *details[] = {
+                "image", image->name,
+                "verb", "clone",
+                "new_name", new_name,
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->polkit_registry,
@@ -207,7 +227,8 @@ int bus_image_method_mark_read_only(
 
         Image *image = userdata;
         Manager *m = image->userdata;
-        int r, read_only;
+        bool read_only;
+        int r;
 
         assert(message);
 
@@ -215,11 +236,18 @@ int bus_image_method_mark_read_only(
         if (r < 0)
                 return r;
 
+        const char *details[] = {
+                "image", image->name,
+                "verb", "mark_read_only",
+                "read_only", (read_only?"1":"0"),
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->polkit_registry,
@@ -254,11 +282,17 @@ int bus_image_method_set_limit(
         if (!FILE_SIZE_VALID_OR_INFINITY(limit))
                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
 
+        const char *details[] = {
+                "machine", image->name,
+                "verb", "set_limit",
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-images",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->polkit_registry,
index 8f11afd65bf5280ae561c341108f170643dc1c6a..7baca67f1b4de21d9f43f484a9d58a796cf237b0 100644 (file)
@@ -73,11 +73,17 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu
         assert(message);
         assert(m);
 
+        const char *details[] = {
+                "machine", m->name,
+                "verb", "unregister",
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_KILL,
                         "org.freedesktop.machine1.manage-machines",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->manager->polkit_registry,
@@ -101,11 +107,17 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus
         assert(message);
         assert(m);
 
+        const char *details[] = {
+                "machine", m->name,
+                "verb", "terminate",
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_KILL,
                         "org.freedesktop.machine1.manage-machines",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->manager->polkit_registry,
@@ -147,11 +159,17 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
         if (!SIGNAL_VALID(signo))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
 
+        const char *details[] = {
+                "machine", m->name,
+                "verb", "kill",
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_KILL,
                         "org.freedesktop.machine1.manage-machines",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->manager->polkit_registry,
@@ -439,11 +457,16 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
         assert(message);
         assert(m);
 
+        const char *details[] = {
+                "machine", m->name,
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->manager->polkit_registry,
@@ -526,11 +549,17 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
         assert(message);
         assert(m);
 
+        const char *details[] = {
+                "machine", m->name,
+                "verb", "login",
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->manager->polkit_registry,
@@ -835,11 +864,19 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
         else if (!path_is_absolute(dest) || !path_is_normalized(dest))
                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
 
+        const char *details[] = {
+                "machine", m->name,
+                "verb", "bind",
+                "src", src,
+                "dest", dest,
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->manager->polkit_registry,
@@ -899,11 +936,19 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
         else if (!path_is_absolute(dest))
                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
 
+        const char *details[] = {
+                "machine", m->name,
+                "verb", "copy",
+                "src", src,
+                "dest", dest,
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->manager->polkit_registry,
@@ -1013,11 +1058,17 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
         assert(message);
         assert(m);
 
+        const char *details[] = {
+                "machine", m->name,
+                "verb", "open_root_directory",
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->manager->polkit_registry,
index 342b18a8df925622e8f3a15f6507f4d12e8791af..ee9ad99255342655fb2546fdf35bc44ac2f844bb 100644 (file)
@@ -714,11 +714,17 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
         else
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm);
 
+        const char *details[] = {
+                "verb", "clean_pool",
+                "mode", mm,
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->polkit_registry,
@@ -844,11 +850,16 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus
         if (!FILE_SIZE_VALID_OR_INFINITY(limit))
                 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
 
+        const char *details[] = {
+                "verb", "set_pool_limit",
+                NULL
+        };
+
         r = bus_verify_polkit_async(
                         message,
                         CAP_SYS_ADMIN,
                         "org.freedesktop.machine1.manage-machines",
-                        NULL,
+                        details,
                         false,
                         UID_INVALID,
                         &m->polkit_registry,
index d870a11e008d82c6a0e47e788d36bf91978fa9c6..419541ebdf3ddda5108abf391211bb91bf43960f 100644 (file)
@@ -46,15 +46,15 @@ static L2tpSession* l2tp_session_free(L2tpSession *s) {
         if (s->tunnel && s->section)
                 ordered_hashmap_remove(s->tunnel->sessions_by_section, s->section);
 
-        network_config_section_free(s->section);
+        config_section_free(s->section);
         free(s->name);
         return mfree(s);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(L2tpSession, l2tp_session_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(L2tpSession, l2tp_session_free);
 
 static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned section_line, L2tpSession **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(l2tp_session_freep) L2tpSession *s = NULL;
         int r;
 
@@ -63,7 +63,7 @@ static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -83,7 +83,7 @@ static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned
                 .section = TAKE_PTR(n),
         };
 
-        r = ordered_hashmap_ensure_put(&t->sessions_by_section, &network_config_hash_ops, s->section, s);
+        r = ordered_hashmap_ensure_put(&t->sessions_by_section, &config_section_hash_ops, s->section, s);
         if (r < 0)
                 return r;
 
index a884d2100f076e5b4ef0a4c9f08189c194423269..236b78ce4b9678f75d79c8ace0b272480c6f6104 100644 (file)
@@ -34,7 +34,7 @@ typedef struct L2tpTunnel L2tpTunnel;
 
 typedef struct L2tpSession {
         L2tpTunnel *tunnel;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         char *name;
 
index ebb8c9c7dcb8bb499293fde775819e50fc572374..a049ade6ba9e68ef924e2f4965c83e4e890972ef 100644 (file)
@@ -43,16 +43,16 @@ static ReceiveAssociation* macsec_receive_association_free(ReceiveAssociation *c
         if (c->macsec && c->section)
                 ordered_hashmap_remove(c->macsec->receive_associations_by_section, c->section);
 
-        network_config_section_free(c->section);
+        config_section_free(c->section);
         security_association_clear(&c->sa);
 
         return mfree(c);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free);
 
 static int macsec_receive_association_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveAssociation **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(macsec_receive_association_freep) ReceiveAssociation *c = NULL;
         int r;
 
@@ -61,7 +61,7 @@ static int macsec_receive_association_new_static(MACsec *s, const char *filename
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -82,7 +82,7 @@ static int macsec_receive_association_new_static(MACsec *s, const char *filename
 
         security_association_init(&c->sa);
 
-        r = ordered_hashmap_ensure_put(&s->receive_associations_by_section, &network_config_hash_ops, c->section, c);
+        r = ordered_hashmap_ensure_put(&s->receive_associations_by_section, &config_section_hash_ops, c->section, c);
         if (r < 0)
                 return r;
 
@@ -103,12 +103,12 @@ static ReceiveChannel* macsec_receive_channel_free(ReceiveChannel *c) {
                         ordered_hashmap_remove(c->macsec->receive_channels_by_section, c->section);
         }
 
-        network_config_section_free(c->section);
+        config_section_free(c->section);
 
         return mfree(c);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free);
 
 static int macsec_receive_channel_new(MACsec *s, uint64_t sci, ReceiveChannel **ret) {
         ReceiveChannel *c;
@@ -129,7 +129,7 @@ static int macsec_receive_channel_new(MACsec *s, uint64_t sci, ReceiveChannel **
 }
 
 static int macsec_receive_channel_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveChannel **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(macsec_receive_channel_freep) ReceiveChannel *c = NULL;
         int r;
 
@@ -138,7 +138,7 @@ static int macsec_receive_channel_new_static(MACsec *s, const char *filename, un
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -154,7 +154,7 @@ static int macsec_receive_channel_new_static(MACsec *s, const char *filename, un
 
         c->section = TAKE_PTR(n);
 
-        r = ordered_hashmap_ensure_put(&s->receive_channels_by_section, &network_config_hash_ops, c->section, c);
+        r = ordered_hashmap_ensure_put(&s->receive_channels_by_section, &config_section_hash_ops, c->section, c);
         if (r < 0)
                 return r;
 
@@ -170,16 +170,16 @@ static TransmitAssociation* macsec_transmit_association_free(TransmitAssociation
         if (a->macsec && a->section)
                 ordered_hashmap_remove(a->macsec->transmit_associations_by_section, a->section);
 
-        network_config_section_free(a->section);
+        config_section_free(a->section);
         security_association_clear(&a->sa);
 
         return mfree(a);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free);
 
 static int macsec_transmit_association_new_static(MACsec *s, const char *filename, unsigned section_line, TransmitAssociation **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(macsec_transmit_association_freep) TransmitAssociation *a = NULL;
         int r;
 
@@ -188,7 +188,7 @@ static int macsec_transmit_association_new_static(MACsec *s, const char *filenam
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -209,7 +209,7 @@ static int macsec_transmit_association_new_static(MACsec *s, const char *filenam
 
         security_association_init(&a->sa);
 
-        r = ordered_hashmap_ensure_put(&s->transmit_associations_by_section, &network_config_hash_ops, a->section, a);
+        r = ordered_hashmap_ensure_put(&s->transmit_associations_by_section, &config_section_hash_ops, a->section, a);
         if (r < 0)
                 return r;
 
index 4d88e49514657f9573e37158552d7a533031802e..17bb1ca3fbe5799a1bc82fbb3247548b55d577ef 100644 (file)
@@ -39,14 +39,14 @@ typedef struct SecurityAssociation {
 
 typedef struct TransmitAssociation {
         MACsec *macsec;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         SecurityAssociation sa;
 } TransmitAssociation;
 
 typedef struct ReceiveAssociation {
         MACsec *macsec;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         MACsecSCI sci;
         SecurityAssociation sa;
@@ -54,7 +54,7 @@ typedef struct ReceiveAssociation {
 
 typedef struct ReceiveChannel {
         MACsec *macsec;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         MACsecSCI sci;
         ReceiveAssociation *rxsa[MACSEC_MAX_ASSOCIATION_NUMBER];
index 88f668753a50b9d6978e46d842422ed787374455..431b5ec045437279d49ed4bf587bfeb5162526c6 100644 (file)
@@ -47,7 +47,7 @@ static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) {
                         hashmap_remove(peer->wireguard->peers_by_section, peer->section);
         }
 
-        network_config_section_free(peer->section);
+        config_section_free(peer->section);
 
         while ((mask = peer->ipmasks)) {
                 LIST_REMOVE(ipmasks, peer->ipmasks, mask);
@@ -65,10 +65,10 @@ static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) {
         return mfree(peer);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(WireguardPeer, wireguard_peer_free);
 
 static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL;
         int r;
 
@@ -77,7 +77,7 @@ static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigne
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -99,7 +99,7 @@ static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigne
 
         LIST_PREPEND(peers, w->peers, peer);
 
-        r = hashmap_ensure_put(&w->peers_by_section, &network_config_hash_ops, peer->section, peer);
+        r = hashmap_ensure_put(&w->peers_by_section, &config_section_hash_ops, peer->section, peer);
         if (r < 0)
                 return r;
 
index 29bacdc77109e170cd22b1f3bdb1630d14917625..09dca88bbf0803bf29ccfed5fd57b931b6ceac59 100644 (file)
@@ -24,7 +24,7 @@ typedef struct WireguardIPmask {
 
 typedef struct WireguardPeer {
         Wireguard *wireguard;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         uint8_t public_key[WG_KEY_LEN];
         uint8_t preshared_key[WG_KEY_LEN];
index 6f911b805f0841f27993960b2ecdda785bb9119c..506238f3919036eac1aeb093e89c950ddc1d862b 100644 (file)
@@ -21,14 +21,14 @@ AddressLabel *address_label_free(AddressLabel *label) {
                 hashmap_remove(label->network->address_labels_by_section, label->section);
         }
 
-        network_config_section_free(label->section);
+        config_section_free(label->section);
         return mfree(label);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(AddressLabel, address_label_free);
 
 static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(address_label_freep) AddressLabel *label = NULL;
         int r;
 
@@ -37,7 +37,7 @@ static int address_label_new_static(Network *network, const char *filename, unsi
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -57,7 +57,7 @@ static int address_label_new_static(Network *network, const char *filename, unsi
                 .label = UINT32_MAX,
         };
 
-        r = hashmap_ensure_put(&network->address_labels_by_section, &network_config_hash_ops, label->section, label);
+        r = hashmap_ensure_put(&network->address_labels_by_section, &config_section_hash_ops, label->section, label);
         if (r < 0)
                 return r;
 
index 0f975454d9939208d22b742e0e69951227b9596f..582dd05b8809705f080ba34ae279eb267c1dee78 100644 (file)
@@ -13,7 +13,7 @@ typedef struct Request Request;
 
 typedef struct AddressLabel {
         Network *network;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         uint32_t label;
         struct in6_addr prefix;
index 7df743efb55768e40c127542388fc3a013698e97..ff80f185ceac50dd25d377414e90bbe5a34e77b6 100644 (file)
@@ -77,7 +77,7 @@ int address_new(Address **ret) {
 }
 
 static int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(address_freep) Address *address = NULL;
         int r;
 
@@ -86,7 +86,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -107,7 +107,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s
         address->section = TAKE_PTR(n);
         address->source = NETWORK_CONFIG_SOURCE_STATIC;
 
-        r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address);
+        r = ordered_hashmap_ensure_put(&network->addresses_by_section, &config_section_hash_ops, address->section, address);
         if (r < 0)
                 return r;
 
@@ -134,7 +134,7 @@ Address *address_free(Address *address) {
 
         sd_ipv4acd_unref(address->acd);
 
-        network_config_section_free(address->section);
+        config_section_free(address->section);
         free(address->label);
         return mfree(address);
 }
index 41c4ce6fa4d59f02812deb906857c412c5cca8af..c1e5b3ce3a762a846e2231f60cc124a86f37830b 100644 (file)
@@ -22,7 +22,7 @@ typedef int (*address_ready_callback_t)(Address *address);
 struct Address {
         Link *link;
         Network *network;
-        NetworkConfigSection *section;
+        ConfigSection *section;
         NetworkConfigSource source;
         NetworkConfigState state;
         union in_addr_union provider; /* DHCP server or router address */
@@ -72,7 +72,7 @@ int address_dup(const Address *src, Address **ret);
 bool address_is_ready(const Address *a);
 void address_set_broadcast(Address *a);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_free);
 
 int link_drop_addresses(Link *link);
 int link_drop_foreign_addresses(Link *link);
index a0024f62d48ae823ef03b9e411cd78a903f0ec09..c1e2977d4dfb32e679a09fa7509b88d2d727a11a 100644 (file)
@@ -32,13 +32,13 @@ BridgeFDB *bridge_fdb_free(BridgeFDB *fdb) {
                 hashmap_remove(fdb->network->bridge_fdb_entries_by_section, fdb->section);
         }
 
-        network_config_section_free(fdb->section);
+        config_section_free(fdb->section);
 
         free(fdb->outgoing_ifname);
         return mfree(fdb);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(BridgeFDB, bridge_fdb_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeFDB, bridge_fdb_free);
 
 /* create a new FDB entry or get an existing one. */
 static int bridge_fdb_new_static(
@@ -47,7 +47,7 @@ static int bridge_fdb_new_static(
                 unsigned section_line,
                 BridgeFDB **ret) {
 
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(bridge_fdb_freep) BridgeFDB *fdb = NULL;
         int r;
 
@@ -56,7 +56,7 @@ static int bridge_fdb_new_static(
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -83,7 +83,7 @@ static int bridge_fdb_new_static(
                 .ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
         };
 
-        r = hashmap_ensure_put(&network->bridge_fdb_entries_by_section, &network_config_hash_ops, fdb->section, fdb);
+        r = hashmap_ensure_put(&network->bridge_fdb_entries_by_section, &config_section_hash_ops, fdb->section, fdb);
         if (r < 0)
                 return r;
 
index fae7da5bbb2c72bf66f1b742ab17bc2e50d4ae11..b1098760d2d9e3ad522b3f90ab7ca44597896959 100644 (file)
@@ -27,7 +27,7 @@ typedef enum NeighborCacheEntryFlags {
 
 typedef struct BridgeFDB {
         Network *network;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         uint32_t vni;
 
index 10025a97aeb85169b26e84ca44d205b13d29ab24..d155090ad1012eaa02980bc85b95c0eda5eb6dce 100644 (file)
@@ -24,12 +24,12 @@ BridgeMDB *bridge_mdb_free(BridgeMDB *mdb) {
                 hashmap_remove(mdb->network->bridge_mdb_entries_by_section, mdb->section);
         }
 
-        network_config_section_free(mdb->section);
+        config_section_free(mdb->section);
 
         return mfree(mdb);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(BridgeMDB, bridge_mdb_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeMDB, bridge_mdb_free);
 
 /* create a new MDB entry or get an existing one. */
 static int bridge_mdb_new_static(
@@ -38,7 +38,7 @@ static int bridge_mdb_new_static(
                 unsigned section_line,
                 BridgeMDB **ret) {
 
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(bridge_mdb_freep) BridgeMDB *mdb = NULL;
         int r;
 
@@ -47,7 +47,7 @@ static int bridge_mdb_new_static(
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -72,7 +72,7 @@ static int bridge_mdb_new_static(
                 .section = TAKE_PTR(n),
         };
 
-        r = hashmap_ensure_put(&network->bridge_mdb_entries_by_section, &network_config_hash_ops, mdb->section, mdb);
+        r = hashmap_ensure_put(&network->bridge_mdb_entries_by_section, &config_section_hash_ops, mdb->section, mdb);
         if (r < 0)
                 return r;
 
index 9ca262e0ce9acaa664649d34328efc71066d671c..ce91e3f5725d691a5a5505318bcf37e6921de459 100644 (file)
@@ -13,7 +13,7 @@ typedef struct Request Request;
 
 typedef struct BridgeMDB {
         Network *network;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         int family;
         union in_addr_union group_addr;
index 6acd838e2b790f24e54aca8fc5ec09dc4f4ea832..38e8c7e889b86d4bcd788321177f208a2f82e845 100644 (file)
@@ -7,7 +7,7 @@
 #include "networkd-network.h"
 #include "networkd-util.h"
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(DHCPStaticLease, dhcp_static_lease_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(DHCPStaticLease, dhcp_static_lease_free);
 
 DHCPStaticLease *dhcp_static_lease_free(DHCPStaticLease *static_lease) {
         if (!static_lease)
@@ -16,7 +16,7 @@ DHCPStaticLease *dhcp_static_lease_free(DHCPStaticLease *static_lease) {
         if (static_lease->network && static_lease->section)
                 hashmap_remove(static_lease->network->dhcp_static_leases_by_section, static_lease->section);
 
-        network_config_section_free(static_lease->section);
+        config_section_free(static_lease->section);
         free(static_lease->client_id);
         return mfree(static_lease);
 }
@@ -35,7 +35,7 @@ static int dhcp_static_lease_new(DHCPStaticLease **ret) {
 }
 
 static int lease_new_static(Network *network, const char *filename, unsigned section_line, DHCPStaticLease **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(dhcp_static_lease_freep) DHCPStaticLease *static_lease = NULL;
         int r;
 
@@ -44,7 +44,7 @@ static int lease_new_static(Network *network, const char *filename, unsigned sec
         assert(section_line > 0);
         assert(ret);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -60,7 +60,7 @@ static int lease_new_static(Network *network, const char *filename, unsigned sec
 
         static_lease->network = network;
         static_lease->section = TAKE_PTR(n);
-        r = hashmap_ensure_put(&network->dhcp_static_leases_by_section, &network_config_hash_ops, static_lease->section, static_lease);
+        r = hashmap_ensure_put(&network->dhcp_static_leases_by_section, &config_section_hash_ops, static_lease->section, static_lease);
         if (r < 0)
                 return r;
 
index c2a9ad6bb12aa2e418b639368a4906465a94b906..9b8e78b90d817cc27482e404d91760fa220c92a5 100644 (file)
@@ -8,11 +8,11 @@
 #include "in-addr-util.h"
 
 typedef struct Network Network;
-typedef struct NetworkConfigSection NetworkConfigSection;
+typedef struct ConfigSection ConfigSection;
 
 typedef struct DHCPStaticLease {
         Network *network;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         struct in_addr address;
         uint8_t *client_id;
index cea700bbb8449203740a9457a358d6d881cba105..ab4f321b0064ddcfeeeeacb97cd2c5cc3e748f76 100644 (file)
@@ -1026,8 +1026,7 @@ static Link *link_drop(Link *link) {
         hashmap_remove(link->manager->links_by_name, link->ifname);
 
         /* bonding master and its slaves have the same hardware address. */
-        if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link)
-                hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr);
+        hashmap_remove_value(link->manager->links_by_hw_addr, &link->hw_addr, link);
 
         /* The following must be called at last. */
         assert_se(hashmap_remove(link->manager->links_by_index, INT_TO_PTR(link->ifindex)) == link);
@@ -1775,7 +1774,7 @@ static int link_admin_state_up(Link *link) {
                 return 0;
 
         if (link->activated && link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN) {
-                log_link_info(link, "ActivationPolicy is \"always-off\", forcing link down.");
+                log_link_info(link, "Activation policy is \"always-down\", forcing link down.");
                 return link_request_to_bring_up_or_down(link, /* up = */ false);
         }
 
@@ -1795,28 +1794,13 @@ static int link_admin_state_down(Link *link) {
                 return 0;
 
         if (link->activated && link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) {
-                log_link_info(link, "ActivationPolicy is \"always-on\", forcing link up.");
+                log_link_info(link, "Activation policy is \"always-up\", forcing link up.");
                 return link_request_to_bring_up_or_down(link, /* up = */ true);
         }
 
         return 0;
 }
 
-bool link_has_carrier(Link *link) {
-        /* see Documentation/networking/operstates.txt in the kernel sources */
-
-        if (link->kernel_operstate == IF_OPER_UP)
-                return true;
-
-        if (link->kernel_operstate == IF_OPER_UNKNOWN)
-                /* operstate may not be implemented, so fall back to flags */
-                if (FLAGS_SET(link->flags, IFF_LOWER_UP | IFF_RUNNING) &&
-                    !FLAGS_SET(link->flags, IFF_DORMANT))
-                        return true;
-
-        return false;
-}
-
 static bool link_is_enslaved(Link *link) {
         if (link->flags & IFF_SLAVE)
                 return true;
@@ -2163,8 +2147,7 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message)
                 log_link_debug(link, "Hardware address is changed: %s → %s",
                                HW_ADDR_TO_STR(&link->hw_addr), HW_ADDR_TO_STR(&addr));
 
-                if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link)
-                        hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr);
+                hashmap_remove_value(link->manager->links_by_hw_addr, &link->hw_addr, link);
         }
 
         link->hw_addr = addr;
@@ -2549,6 +2532,8 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
         r = ethtool_get_driver(&manager->ethtool_fd, link->ifname, &link->driver);
         if (r < 0)
                 log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m");
+        else
+                log_link_debug(link, "Found driver: %s", strna(link->driver));
 
         if (streq_ptr(link->driver, "dsa")) {
                 uint32_t dsa_master_ifindex;
index 8bf1cc67f7188a1195432db0fbfb493408ec3bab..7ccb31df79199250c7d153b5432bb29ae16c6032 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "ether-addr-util.h"
 #include "log-link.h"
+#include "netif-util.h"
 #include "network-util.h"
 #include "networkd-util.h"
 #include "ordered-set.h"
@@ -212,7 +213,10 @@ void link_check_ready(Link *link);
 
 void link_update_operstate(Link *link, bool also_update_bond_master);
 
-bool link_has_carrier(Link *link);
+static inline bool link_has_carrier(Link *link) {
+        assert(link);
+        return netif_has_carrier(link->kernel_operstate, link->flags);
+}
 
 bool link_ipv6_enabled(Link *link);
 bool link_ipv6ll_enabled(Link *link);
index 26b7385497087045d8729121c953b438e6da386b..4aab290bf803e7c2a575260ea342c280015c2696 100644 (file)
@@ -19,7 +19,7 @@ Neighbor *neighbor_free(Neighbor *neighbor) {
                 hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
         }
 
-        network_config_section_free(neighbor->section);
+        config_section_free(neighbor->section);
 
         if (neighbor->link)
                 set_remove(neighbor->link->neighbors, neighbor);
@@ -27,10 +27,10 @@ Neighbor *neighbor_free(Neighbor *neighbor) {
         return mfree(neighbor);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Neighbor, neighbor_free);
 
 static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
         int r;
 
@@ -39,7 +39,7 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -60,7 +60,7 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned
                 .source = NETWORK_CONFIG_SOURCE_STATIC,
         };
 
-        r = hashmap_ensure_put(&network->neighbors_by_section, &network_config_hash_ops, neighbor->section, neighbor);
+        r = hashmap_ensure_put(&network->neighbors_by_section, &config_section_hash_ops, neighbor->section, neighbor);
         if (r < 0)
                 return r;
 
index e9e18541107ada2ce6710f2f043ebda1fe669f34..f31f58a4d4ea4e6059c14257dbfe1059e3fcf610 100644 (file)
@@ -18,7 +18,7 @@ typedef struct Request Request;
 typedef struct Neighbor {
         Network *network;
         Link *link;
-        NetworkConfigSection *section;
+        ConfigSection *section;
         NetworkConfigSource source;
         NetworkConfigState state;
 
index 4c3bf97311d44979033e55c0ed9de021e498a8fd..08e3f13f5a5d10958cbc9ac058ce4c9e4cc85bad 100644 (file)
@@ -73,15 +73,15 @@ Link.Unmanaged,                              config_parse_bool,
 Link.ActivationPolicy,                       config_parse_activation_policy,                           0,                             offsetof(Network, activation_policy)
 Link.RequiredForOnline,                      config_parse_required_for_online,                         0,                             0
 Link.RequiredFamilyForOnline,                config_parse_required_family_for_online,                  0,                             offsetof(Network, required_family_for_online)
-SR-IOV.VirtualFunction,                      config_parse_sr_iov_uint32,                               0,                             0
-SR-IOV.VLANId,                               config_parse_sr_iov_uint32,                               0,                             0
-SR-IOV.QualityOfService,                     config_parse_sr_iov_uint32,                               0,                             0
-SR-IOV.VLANProtocol,                         config_parse_sr_iov_vlan_proto,                           0,                             0
-SR-IOV.MACSpoofCheck,                        config_parse_sr_iov_boolean,                              0,                             0
-SR-IOV.QueryReceiveSideScaling,              config_parse_sr_iov_boolean,                              0,                             0
-SR-IOV.Trust,                                config_parse_sr_iov_boolean,                              0,                             0
-SR-IOV.LinkState,                            config_parse_sr_iov_link_state,                           0,                             0
-SR-IOV.MACAddress,                           config_parse_sr_iov_mac,                                  0,                             0
+SR-IOV.VirtualFunction,                      config_parse_sr_iov_uint32,                               0,                             offsetof(Network, sr_iov_by_section)
+SR-IOV.VLANId,                               config_parse_sr_iov_uint32,                               0,                             offsetof(Network, sr_iov_by_section)
+SR-IOV.QualityOfService,                     config_parse_sr_iov_uint32,                               0,                             offsetof(Network, sr_iov_by_section)
+SR-IOV.VLANProtocol,                         config_parse_sr_iov_vlan_proto,                           0,                             offsetof(Network, sr_iov_by_section)
+SR-IOV.MACSpoofCheck,                        config_parse_sr_iov_boolean,                              0,                             offsetof(Network, sr_iov_by_section)
+SR-IOV.QueryReceiveSideScaling,              config_parse_sr_iov_boolean,                              0,                             offsetof(Network, sr_iov_by_section)
+SR-IOV.Trust,                                config_parse_sr_iov_boolean,                              0,                             offsetof(Network, sr_iov_by_section)
+SR-IOV.LinkState,                            config_parse_sr_iov_link_state,                           0,                             offsetof(Network, sr_iov_by_section)
+SR-IOV.MACAddress,                           config_parse_sr_iov_mac,                                  0,                             offsetof(Network, sr_iov_by_section)
 Network.Description,                         config_parse_string,                                      0,                             offsetof(Network, description)
 Network.KeepMaster,                          config_parse_bool,                                        0,                             offsetof(Network, keep_master)
 Network.BatmanAdvanced,                      config_parse_ifname,                                      0,                             offsetof(Network, batadv_name)
index 873ad2e70341a3fc5f47656b7ac5341ab1f58a43..3142be471f8b8f29608c8cfae98ff8bf50c00500 100644 (file)
@@ -321,7 +321,9 @@ int network_verify(Network *network) {
         network_drop_invalid_route_prefixes(network);
         network_drop_invalid_routing_policy_rules(network);
         network_drop_invalid_traffic_control(network);
-        network_drop_invalid_sr_iov(network);
+        r = sr_iov_drop_invalid_sections(UINT32_MAX, network->sr_iov_by_section);
+        if (r < 0)
+                return r;
         network_drop_invalid_static_leases(network);
 
         network_adjust_dhcp_server(network);
index b829aaab90ade59b6c2f0e2a6f13153363db09c3..e9e5d08557bd6a7047d73b90d7d4b0f2f4f15a81 100644 (file)
@@ -27,7 +27,7 @@ NextHop *nexthop_free(NextHop *nexthop) {
                 hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
         }
 
-        network_config_section_free(nexthop->section);
+        config_section_free(nexthop->section);
 
         if (nexthop->link) {
                 set_remove(nexthop->link->nexthops, nexthop);
@@ -48,7 +48,7 @@ NextHop *nexthop_free(NextHop *nexthop) {
         return mfree(nexthop);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop, nexthop_free);
 
 static int nexthop_new(NextHop **ret) {
         _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
@@ -68,7 +68,7 @@ static int nexthop_new(NextHop **ret) {
 }
 
 static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
         int r;
 
@@ -77,7 +77,7 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -96,7 +96,7 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s
         nexthop->section = TAKE_PTR(n);
         nexthop->source = NETWORK_CONFIG_SOURCE_STATIC;
 
-        r = hashmap_ensure_put(&network->nexthops_by_section, &network_config_hash_ops, nexthop->section, nexthop);
+        r = hashmap_ensure_put(&network->nexthops_by_section, &config_section_hash_ops, nexthop->section, nexthop);
         if (r < 0)
                 return r;
 
index 7a8920238c0bc64556097571072506b24719d809..01b29ae560294454c87b175ea7e41ef8d6c2f036 100644 (file)
@@ -22,7 +22,7 @@ typedef struct NextHop {
         Network *network;
         Manager *manager;
         Link *link;
-        NetworkConfigSection *section;
+        ConfigSection *section;
         NetworkConfigSource source;
         NetworkConfigState state;
 
index 59b0922a4c4e4a169bcce0f907d27cf7509e334c..0c5eebc8157a68de93e783aed372b8a3040e81bc 100644 (file)
@@ -69,16 +69,16 @@ Prefix *prefix_free(Prefix *prefix) {
                 hashmap_remove(prefix->network->prefixes_by_section, prefix->section);
         }
 
-        network_config_section_free(prefix->section);
+        config_section_free(prefix->section);
         set_free(prefix->tokens);
 
         return mfree(prefix);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Prefix, prefix_free);
 
 static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(prefix_freep) Prefix *prefix = NULL;
         int r;
 
@@ -87,7 +87,7 @@ static int prefix_new_static(Network *network, const char *filename, unsigned se
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -111,7 +111,7 @@ static int prefix_new_static(Network *network, const char *filename, unsigned se
                 .address_auto_configuration = true,
         };
 
-        r = hashmap_ensure_put(&network->prefixes_by_section, &network_config_hash_ops, prefix->section, prefix);
+        r = hashmap_ensure_put(&network->prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
         if (r < 0)
                 return r;
 
@@ -128,15 +128,15 @@ RoutePrefix *route_prefix_free(RoutePrefix *prefix) {
                 hashmap_remove(prefix->network->route_prefixes_by_section, prefix->section);
         }
 
-        network_config_section_free(prefix->section);
+        config_section_free(prefix->section);
 
         return mfree(prefix);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutePrefix, route_prefix_free);
 
 static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL;
         int r;
 
@@ -145,7 +145,7 @@ static int route_prefix_new_static(Network *network, const char *filename, unsig
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -166,7 +166,7 @@ static int route_prefix_new_static(Network *network, const char *filename, unsig
                 .lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC,
         };
 
-        r = hashmap_ensure_put(&network->route_prefixes_by_section, &network_config_hash_ops, prefix->section, prefix);
+        r = hashmap_ensure_put(&network->route_prefixes_by_section, &config_section_hash_ops, prefix->section, prefix);
         if (r < 0)
                 return r;
 
index 392c00b37d16d0410a94ccdf8b7370b9716b5bb4..952fa8ae58e173c4a02ebadd9fa53a0c245e90c1 100644 (file)
@@ -29,7 +29,7 @@ typedef enum RADVPrefixDelegation {
 
 typedef struct Prefix {
         Network *network;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         struct in6_addr prefix;
         uint8_t prefixlen;
@@ -46,7 +46,7 @@ typedef struct Prefix {
 
 typedef struct RoutePrefix {
         Network *network;
-        NetworkConfigSection *section;
+        ConfigSection *section;
 
         struct in6_addr prefix;
         uint8_t prefixlen;
index 00e64978d45ba7ba36b21b0ec5ce91c023a4e776..03021362b6cb7851d8a95e406bdfc8bd041db904 100644 (file)
@@ -47,7 +47,7 @@ int route_new(Route **ret) {
 }
 
 static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(route_freep) Route *route = NULL;
         int r;
 
@@ -56,7 +56,7 @@ static int route_new_static(Network *network, const char *filename, unsigned sec
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -78,7 +78,7 @@ static int route_new_static(Network *network, const char *filename, unsigned sec
         route->section = TAKE_PTR(n);
         route->source = NETWORK_CONFIG_SOURCE_STATIC;
 
-        r = hashmap_ensure_put(&network->routes_by_section, &network_config_hash_ops, route->section, route);
+        r = hashmap_ensure_put(&network->routes_by_section, &config_section_hash_ops, route->section, route);
         if (r < 0)
                 return r;
 
@@ -95,7 +95,7 @@ Route *route_free(Route *route) {
                 hashmap_remove(route->network->routes_by_section, route->section);
         }
 
-        network_config_section_free(route->section);
+        config_section_free(route->section);
 
         if (route->link)
                 set_remove(route->link->routes, route);
index e3e22a59853ce95e5c5c7800318dd3b7f81f8905..3471008fee7145bb565c74cfa14f0ea57d8dd7e7 100644 (file)
@@ -19,7 +19,7 @@ typedef struct Route {
         Link *link;
         Manager *manager;
         Network *network;
-        NetworkConfigSection *section;
+        ConfigSection *section;
         NetworkConfigSource source;
         NetworkConfigState state;
         union in_addr_union provider; /* DHCP server or router address */
@@ -74,7 +74,7 @@ extern const struct hash_ops route_hash_ops;
 
 int route_new(Route **ret);
 Route *route_free(Route *route);
-DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(Route, route_free);
 int route_dup(const Route *src, Route **ret);
 
 int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
index a2e72a7d7c7b3a4cdc6cb4c4874f9ee17f8f0a40..1f3dfa53b0ccdc8ef516f5a29f3f74b2491c302b 100644 (file)
@@ -54,14 +54,14 @@ RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule) {
         if (rule->manager)
                 set_remove(rule->manager->rules, rule);
 
-        network_config_section_free(rule->section);
+        config_section_free(rule->section);
         free(rule->iif);
         free(rule->oif);
 
         return mfree(rule);
 }
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free);
 
 static int routing_policy_rule_new(RoutingPolicyRule **ret) {
         RoutingPolicyRule *rule;
@@ -86,7 +86,7 @@ static int routing_policy_rule_new(RoutingPolicyRule **ret) {
 
 static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) {
         _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         int r;
 
         assert(network);
@@ -94,7 +94,7 @@ static int routing_policy_rule_new_static(Network *network, const char *filename
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -113,7 +113,7 @@ static int routing_policy_rule_new_static(Network *network, const char *filename
         rule->source = NETWORK_CONFIG_SOURCE_STATIC;
         rule->protocol = RTPROT_STATIC;
 
-        r = hashmap_ensure_put(&network->rules_by_section, &network_config_hash_ops, rule->section, rule);
+        r = hashmap_ensure_put(&network->rules_by_section, &config_section_hash_ops, rule->section, rule);
         if (r < 0)
                 return r;
 
index f52943bd2e6d11434b7dfad113c953c32e3b2eee..1ab147caae7b37b99ce35600c759465d832f6c71 100644 (file)
@@ -17,7 +17,7 @@ typedef struct Request Request;
 typedef struct RoutingPolicyRule {
         Manager *manager;
         Network *network;
-        NetworkConfigSection *section;
+        ConfigSection *section;
         NetworkConfigSource source;
         NetworkConfigState state;
 
index 6da0f83521cb538b275f6a69f03fa300d080da12..cf138c737021b3825dbfdfe20daec14b2f6ec8aa 100644 (file)
@@ -1,82 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later
  * Copyright © 2020 VMware, Inc. */
 
-#include "alloc-util.h"
 #include "netlink-util.h"
+#include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-sriov.h"
-#include "parse-util.h"
-#include "set.h"
-#include "string-util.h"
-
-static int sr_iov_new(SRIOV **ret) {
-        SRIOV *sr_iov;
-
-        sr_iov = new(SRIOV, 1);
-        if (!sr_iov)
-                return -ENOMEM;
-
-        *sr_iov = (SRIOV) {
-                  .vf = UINT32_MAX,
-                  .vlan_proto = ETH_P_8021Q,
-                  .vf_spoof_check_setting = -1,
-                  .trust = -1,
-                  .query_rss = -1,
-                  .link_state = _SR_IOV_LINK_STATE_INVALID,
-        };
-
-        *ret = TAKE_PTR(sr_iov);
-
-        return 0;
-}
-
-static int sr_iov_new_static(Network *network, const char *filename, unsigned section_line, SRIOV **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
-        _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL;
-        SRIOV *existing = NULL;
-        int r;
-
-        assert(network);
-        assert(ret);
-        assert(filename);
-        assert(section_line > 0);
-
-        r = network_config_section_new(filename, section_line, &n);
-        if (r < 0)
-                return r;
-
-        existing = ordered_hashmap_get(network->sr_iov_by_section, n);
-        if (existing) {
-                *ret = existing;
-                return 0;
-        }
-
-        r = sr_iov_new(&sr_iov);
-        if (r < 0)
-                return r;
-
-        sr_iov->network = network;
-        sr_iov->section = TAKE_PTR(n);
-
-        r = ordered_hashmap_ensure_put(&network->sr_iov_by_section, &network_config_hash_ops, sr_iov->section, sr_iov);
-        if (r < 0)
-                return r;
-
-        *ret = TAKE_PTR(sr_iov);
-        return 0;
-}
-
-SRIOV *sr_iov_free(SRIOV *sr_iov) {
-        if (!sr_iov)
-                return NULL;
-
-        if (sr_iov->network && sr_iov->section)
-                ordered_hashmap_remove(sr_iov->network->sr_iov_by_section, sr_iov->section);
-
-        network_config_section_free(sr_iov->section);
-
-        return mfree(sr_iov);
-}
 
 static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
@@ -117,104 +45,16 @@ static int sr_iov_configure(Link *link, SRIOV *sr_iov) {
 
         r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
-
-        r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not open IFLA_VFINFO_LIST container: %m");
-
-        r = sd_netlink_message_open_container(req, IFLA_VF_INFO);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not open IFLA_VF_INFO container: %m");
-
-        if (!ether_addr_is_null(&sr_iov->mac)) {
-                struct ifla_vf_mac ivm = {
-                        .vf = sr_iov->vf,
-                };
-
-                memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN);
-                r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac));
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append IFLA_VF_MAC: %m");
-        }
-
-        if (sr_iov->vf_spoof_check_setting >= 0) {
-                struct ifla_vf_spoofchk ivs = {
-                        .vf = sr_iov->vf,
-                        .setting = sr_iov->vf_spoof_check_setting,
-                };
-
-                r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk));
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append IFLA_VF_SPOOFCHK: %m");
-        }
-
-        if (sr_iov->query_rss >= 0) {
-                struct ifla_vf_rss_query_en ivs = {
-                        .vf = sr_iov->vf,
-                        .setting = sr_iov->query_rss,
-                };
-
-                r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en));
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append IFLA_VF_RSS_QUERY_EN: %m");
-        }
-
-        if (sr_iov->trust >= 0) {
-                struct ifla_vf_trust ivt = {
-                        .vf = sr_iov->vf,
-                        .setting = sr_iov->trust,
-                };
-
-                r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust));
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append IFLA_VF_TRUST: %m");
-        }
-
-        if (sr_iov->link_state >= 0) {
-                struct ifla_vf_link_state ivl = {
-                        .vf = sr_iov->vf,
-                        .link_state = sr_iov->link_state,
-                };
-
-                r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state));
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append IFLA_VF_LINK_STATE: %m");
-        }
-
-        if (sr_iov->vlan > 0) {
-                /* Because of padding, first the buffer must be initialized with 0. */
-                struct ifla_vf_vlan_info ivvi = {};
-                ivvi.vf = sr_iov->vf;
-                ivvi.vlan = sr_iov->vlan;
-                ivvi.qos = sr_iov->qos;
-                ivvi.vlan_proto = htobe16(sr_iov->vlan_proto);
-
-                r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not open IFLA_VF_VLAN_LIST container: %m");
-
-                r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info));
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not append IFLA_VF_VLAN_INFO: %m");
-
-                r = sd_netlink_message_close_container(req);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not close IFLA_VF_VLAN_LIST container: %m");
-        }
-
-        r = sd_netlink_message_close_container(req);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not close IFLA_VF_INFO container: %m");
+                return r;
 
-        r = sd_netlink_message_close_container(req);
+        r = sr_iov_set_netlink_message(sr_iov, req);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not close IFLA_VFINFO_LIST container: %m");
+                return r;
 
         r = netlink_call_async(link->manager->rtnl, NULL, req, sr_iov_handler,
                                link_netlink_destroy_callback, link);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+                return r;
 
         link_ref(link);
         link->sr_iov_messages++;
@@ -239,7 +79,9 @@ int link_configure_sr_iov(Link *link) {
         ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) {
                 r = sr_iov_configure(link, sr_iov);
                 if (r < 0)
-                        return r;
+                        return log_link_warning_errno(link, r,
+                                                      "Failed to configure SR-IOV virtual function %"PRIu32": %m",
+                                                      sr_iov->vf);
         }
 
         if (link->sr_iov_messages == 0)
@@ -249,287 +91,3 @@ int link_configure_sr_iov(Link *link) {
 
         return 0;
 }
-
-static int sr_iov_section_verify(SRIOV *sr_iov) {
-        assert(sr_iov);
-
-        if (section_is_invalid(sr_iov->section))
-                return -EINVAL;
-
-        if (sr_iov->vf == UINT32_MAX)
-                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
-                                         "%s: [SRIOV] section without VirtualFunction= field configured. "
-                                         "Ignoring [SRIOV] section from line %u.",
-                                         sr_iov->section->filename, sr_iov->section->line);
-
-        return 0;
-}
-
-void network_drop_invalid_sr_iov(Network *network) {
-        SRIOV *sr_iov;
-
-        assert(network);
-
-        ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section)
-                if (sr_iov_section_verify(sr_iov) < 0)
-                        sr_iov_free(sr_iov);
-}
-
-int config_parse_sr_iov_uint32(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
-        Network *network = data;
-        uint32_t k;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
-        if (r < 0)
-                return r;
-
-        if (isempty(rvalue)) {
-                if (streq(lvalue, "VirtualFunction"))
-                        sr_iov->vf = UINT32_MAX;
-                else if (streq(lvalue, "VLANId"))
-                        sr_iov->vlan = 0;
-                else if (streq(lvalue, "QualityOfService"))
-                        sr_iov->qos = 0;
-                else
-                        assert_not_reached();
-
-                TAKE_PTR(sr_iov);
-                return 0;
-        }
-
-        r = safe_atou32(rvalue, &k);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
-                return 0;
-        }
-
-        if (streq(lvalue, "VLANId")) {
-                if (k == 0 || k > 4095) {
-                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k);
-                        return 0;
-                }
-                sr_iov->vlan = k;
-        } else if (streq(lvalue, "VirtualFunction")) {
-                if (k >= INT_MAX) {
-                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k);
-                        return 0;
-                }
-                sr_iov->vf = k;
-        } else if (streq(lvalue, "QualityOfService"))
-                sr_iov->qos = k;
-        else
-                assert_not_reached();
-
-        TAKE_PTR(sr_iov);
-        return 0;
-}
-
-int config_parse_sr_iov_vlan_proto(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
-        Network *network = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
-        if (r < 0)
-                return r;
-
-        if (isempty(rvalue) || streq(rvalue, "802.1Q"))
-                sr_iov->vlan_proto = ETH_P_8021Q;
-        else if (streq(rvalue, "802.1ad"))
-                sr_iov->vlan_proto = ETH_P_8021AD;
-        else {
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
-                return 0;
-        }
-
-        TAKE_PTR(sr_iov);
-        return 0;
-}
-
-int config_parse_sr_iov_link_state(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
-        Network *network = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
-        if (r < 0)
-                return r;
-
-        /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
-         * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
-
-        if (isempty(rvalue)) {
-                sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
-                TAKE_PTR(sr_iov);
-                return 0;
-        }
-
-        if (streq(rvalue, "auto")) {
-                sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
-                TAKE_PTR(sr_iov);
-                return 0;
-        }
-
-        r = parse_boolean(rvalue);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
-                return 0;
-        }
-
-        sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE;
-        TAKE_PTR(sr_iov);
-        return 0;
-}
-
-int config_parse_sr_iov_boolean(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
-        Network *network = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
-        if (r < 0)
-                return r;
-
-        if (isempty(rvalue)) {
-                if (streq(lvalue, "MACSpoofCheck"))
-                        sr_iov->vf_spoof_check_setting = -1;
-                else if (streq(lvalue, "QueryReceiveSideScaling"))
-                        sr_iov->query_rss = -1;
-                else if (streq(lvalue, "Trust"))
-                        sr_iov->trust = -1;
-                else
-                        assert_not_reached();
-
-                TAKE_PTR(sr_iov);
-                return 0;
-        }
-
-        r = parse_boolean(rvalue);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
-                return 0;
-        }
-
-        if (streq(lvalue, "MACSpoofCheck"))
-                sr_iov->vf_spoof_check_setting = r;
-        else if (streq(lvalue, "QueryReceiveSideScaling"))
-                sr_iov->query_rss = r;
-        else if (streq(lvalue, "Trust"))
-                sr_iov->trust = r;
-        else
-                assert_not_reached();
-
-        TAKE_PTR(sr_iov);
-        return 0;
-}
-
-int config_parse_sr_iov_mac(
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
-        Network *network = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
-        if (r < 0)
-                return r;
-
-        if (isempty(rvalue)) {
-                sr_iov->mac = ETHER_ADDR_NULL;
-                TAKE_PTR(sr_iov);
-                return 0;
-        }
-
-        r = parse_ether_addr(rvalue, &sr_iov->mac);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
-                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
-                return 0;
-        }
-
-        TAKE_PTR(sr_iov);
-        return 0;
-}
index 950d1f9c59827ffb6a214b97e64f28740f760710..4251fddf88b69914be3c810f5d4850a1e0cf1727 100644 (file)
@@ -2,45 +2,8 @@
  * Copyright © 2020 VMware, Inc. */
 #pragma once
 
-#include <linux/if_link.h>
+#include "netif-sriov.h"
 
-#include "conf-parser.h"
-#include "ether-addr-util.h"
-#include "networkd-link.h"
-#include "networkd-network.h"
-#include "networkd-util.h"
+typedef struct Link Link;
 
-typedef enum SRIOVLinkState {
-        SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO,
-        SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE,
-        SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE,
-        _SR_IOV_LINK_STATE_MAX,
-        _SR_IOV_LINK_STATE_INVALID = -EINVAL,
-} SRIOVLinkState;
-
-typedef struct SRIOV {
-        NetworkConfigSection *section;
-        Network *network;
-
-        uint32_t vf;   /* 0 - 2147483646 */
-        uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */
-        uint32_t qos;
-        uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */
-        int vf_spoof_check_setting;
-        int query_rss;
-        int trust;
-        SRIOVLinkState link_state;
-        struct ether_addr mac;
-} SRIOV;
-
-SRIOV *sr_iov_free(SRIOV *sr_iov);
 int link_configure_sr_iov(Link *link);
-void network_drop_invalid_sr_iov(Network *network);
-
-DEFINE_NETWORK_SECTION_FUNCTIONS(SRIOV, sr_iov_free);
-
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32);
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean);
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state);
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto);
-CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac);
index ac6f1680ca4b1fecd5de9ccf43878e1cb32b2537..1c0987bc3370c1049359e039171c2984face2681 100644 (file)
@@ -243,50 +243,6 @@ int config_parse_mud_url(
         return free_and_replace(*url, unescaped);
 }
 
-static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) {
-        siphash24_compress_string(c->filename, state);
-        siphash24_compress(&c->line, sizeof(c->line), state);
-}
-
-static int network_config_compare_func(const NetworkConfigSection *x, const NetworkConfigSection *y) {
-        int r;
-
-        r = strcmp(x->filename, y->filename);
-        if (r != 0)
-                return r;
-
-        return CMP(x->line, y->line);
-}
-
-DEFINE_HASH_OPS(network_config_hash_ops, NetworkConfigSection, network_config_hash_func, network_config_compare_func);
-
-int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) {
-        NetworkConfigSection *cs;
-
-        cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1);
-        if (!cs)
-                return -ENOMEM;
-
-        strcpy(cs->filename, filename);
-        cs->line = line;
-
-        *s = TAKE_PTR(cs);
-
-        return 0;
-}
-
-unsigned hashmap_find_free_section_line(Hashmap *hashmap) {
-        NetworkConfigSection *cs;
-        unsigned n = 0;
-        void *entry;
-
-        HASHMAP_FOREACH_KEY(entry, cs, hashmap)
-                if (n < cs->line)
-                        n = cs->line;
-
-        return n + 1;
-}
-
 int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg) {
         const char *err_msg = NULL;
 
index d94243855e712515321cdcdd6faa4290ae62212e..0a627588aa2e5c617dcad43f846098ab4fd23c1c 100644 (file)
 
 typedef struct Link Link;
 
-typedef struct NetworkConfigSection {
-        unsigned line;
-        bool invalid;
-        char filename[];
-} NetworkConfigSection;
-
 typedef enum NetworkConfigSource {
         NETWORK_CONFIG_SOURCE_FOREIGN, /* configured by kernel */
         NETWORK_CONFIG_SOURCE_STATIC,
@@ -141,37 +135,6 @@ AddressFamily dhcp_deprecated_address_family_from_string(const char *s) _pure_;
 const char *dhcp_lease_server_type_to_string(sd_dhcp_lease_server_type_t t) _const_;
 sd_dhcp_lease_server_type_t dhcp_lease_server_type_from_string(const char *s) _pure_;
 
-static inline NetworkConfigSection* network_config_section_free(NetworkConfigSection *cs) {
-        return mfree(cs);
-}
-DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free);
-
-int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s);
-extern const struct hash_ops network_config_hash_ops;
-unsigned hashmap_find_free_section_line(Hashmap *hashmap);
-
-static inline bool section_is_invalid(NetworkConfigSection *section) {
-        /* If this returns false, then it does _not_ mean the section is valid. */
-
-        if (!section)
-                return false;
-
-        return section->invalid;
-}
-
-#define DEFINE_NETWORK_SECTION_FUNCTIONS(type, free_func)               \
-        static inline type* free_func##_or_set_invalid(type *p) {       \
-                assert(p);                                              \
-                                                                        \
-                if (p->section)                                         \
-                        p->section->invalid = true;                     \
-                else                                                    \
-                        free_func(p);                                   \
-                return NULL;                                            \
-        }                                                               \
-        DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func);                  \
-        DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);
-
 int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg);
 #define log_link_message_error_errno(link, m, err, msg)   log_link_message_full_errno(link, m, LOG_ERR, err, msg)
 #define log_link_message_warning_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_WARNING, err, msg)
index 995df2fc866a048cb29c42790d3593195b3c0932..803c999855b106d4e711d582cf35edfe316e60de 100644 (file)
@@ -77,7 +77,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
 }
 
 int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(qdisc_freep) QDisc *qdisc = NULL;
         TrafficControl *existing;
         QDisc *q = NULL;
@@ -88,7 +88,7 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -126,7 +126,7 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns
         qdisc->network = network;
         qdisc->section = TAKE_PTR(n);
 
-        r = ordered_hashmap_ensure_put(&network->tc_by_section, &network_config_hash_ops, qdisc->section, TC(qdisc));
+        r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, qdisc->section, TC(qdisc));
         if (r < 0)
                 return r;
 
@@ -141,7 +141,7 @@ QDisc* qdisc_free(QDisc *qdisc) {
         if (qdisc->network && qdisc->section)
                 ordered_hashmap_remove(qdisc->network->tc_by_section, qdisc->section);
 
-        network_config_section_free(qdisc->section);
+        config_section_free(qdisc->section);
 
         free(qdisc->tca_kind);
         return mfree(qdisc);
index bf2df146a7ea0c465c631a10d123298a2c440f13..e37c4806e32cb2ea31b0c65089e42342bea68b68 100644 (file)
@@ -37,7 +37,7 @@ typedef enum QDiscKind {
 typedef struct QDisc {
         TrafficControl meta;
 
-        NetworkConfigSection *section;
+        ConfigSection *section;
         Network *network;
 
         int family;
@@ -80,7 +80,7 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns
 int qdisc_configure(Link *link, QDisc *qdisc);
 int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(QDisc, qdisc_free);
 
 DEFINE_TC_CAST(QDISC, QDisc);
 
index 10bffd65c6262014c4e98d295079552f1691e895..6a36ac1c1cd6c1d8ce3a92e5e46f1f72a9f1e106 100644 (file)
@@ -45,7 +45,7 @@ static int tclass_new(TClassKind kind, TClass **ret) {
 }
 
 int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
-        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(tclass_freep) TClass *tclass = NULL;
         TrafficControl *existing;
         int r;
@@ -55,7 +55,7 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u
         assert(filename);
         assert(section_line > 0);
 
-        r = network_config_section_new(filename, section_line, &n);
+        r = config_section_new(filename, section_line, &n);
         if (r < 0)
                 return r;
 
@@ -82,7 +82,7 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u
         tclass->network = network;
         tclass->section = TAKE_PTR(n);
 
-        r = ordered_hashmap_ensure_put(&network->tc_by_section, &network_config_hash_ops, tclass->section, tclass);
+        r = ordered_hashmap_ensure_put(&network->tc_by_section, &config_section_hash_ops, tclass->section, tclass);
         if (r < 0)
                 return r;
 
@@ -97,7 +97,7 @@ TClass* tclass_free(TClass *tclass) {
         if (tclass->network && tclass->section)
                 ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section);
 
-        network_config_section_free(tclass->section);
+        config_section_free(tclass->section);
 
         return mfree(tclass);
 }
index fc91789f30007b15140d56ff9eda577e0fb1f6fe..5f8016c187258c868f2db2c9310c8e770378b126 100644 (file)
@@ -19,7 +19,7 @@ typedef enum TClassKind {
 typedef struct TClass {
         TrafficControl meta;
 
-        NetworkConfigSection *section;
+        ConfigSection *section;
         Network *network;
 
         uint32_t classid;
@@ -59,7 +59,7 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u
 int tclass_configure(Link *link, TClass *tclass);
 int tclass_section_verify(TClass *tclass);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(TClass, tclass_free);
+DEFINE_SECTION_CLEANUP_FUNCTIONS(TClass, tclass_free);
 
 DEFINE_TC_CAST(TCLASS, TClass);
 
index 5a33d563c240537eac7acf7ee760320fe7077a77..ce6c8b181c2abac2ca27b3674e4b8f6b79fab60b 100644 (file)
@@ -32,7 +32,7 @@ int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) {
                 .required_operstate = LINK_OPERSTATE_RANGE_DEFAULT,
         };
 
-        r = hashmap_ensure_put(&m->links, NULL, INT_TO_PTR(ifindex), l);
+        r = hashmap_ensure_put(&m->links_by_index, NULL, INT_TO_PTR(ifindex), l);
         if (r < 0)
                 return r;
 
@@ -53,7 +53,7 @@ Link *link_free(Link *l) {
                 return NULL;
 
         if (l->manager) {
-                hashmap_remove(l->manager->links, INT_TO_PTR(l->ifindex));
+                hashmap_remove(l->manager->links_by_index, INT_TO_PTR(l->ifindex));
                 hashmap_remove(l->manager->links_by_name, l->ifname);
         }
 
@@ -97,7 +97,7 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
 }
 
 int link_update_monitor(Link *l) {
-        _cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *required_family = NULL,
+        _cleanup_free_ char *required_operstate = NULL, *required_family = NULL,
                 *ipv4_address_state = NULL, *ipv6_address_state = NULL, *state = NULL;
         int r, ret = 0;
 
@@ -123,18 +123,9 @@ int link_update_monitor(Link *l) {
                                                    "Failed to parse required operational state, ignoring: %m");
         }
 
-        r = sd_network_link_get_operational_state(l->ifindex, &operstate);
+        r = network_link_get_operational_state(l->ifindex, &l->operational_state);
         if (r < 0)
                 ret = log_link_debug_errno(l, r, "Failed to get operational state, ignoring: %m");
-        else {
-                LinkOperationalState s;
-
-                s = link_operstate_from_string(operstate);
-                if (s < 0)
-                        ret = log_link_debug_errno(l, s, "Failed to parse operational state, ignoring: %m");
-                else
-                        l->operational_state = s;
-        }
 
         r = sd_network_link_get_required_family_for_online(l->ifindex, &required_family);
         if (r < 0)
index 3890e92b61c7138e6142979da30c8574c98fdc16..6656813f39b2212395aea0b3d01b9a1d8f99511b 100644 (file)
@@ -21,14 +21,15 @@ static bool manager_ignore_link(Manager *m, Link *link) {
                 return true;
 
         /* if interfaces are given on the command line, ignore all others */
-        if (m->interfaces && !hashmap_contains(m->interfaces, link->ifname))
+        if (m->command_line_interfaces_by_name &&
+            !hashmap_contains(m->command_line_interfaces_by_name, link->ifname))
                 return true;
 
         if (!link->required_for_online)
                 return true;
 
         /* ignore interfaces we explicitly are asked to ignore */
-        return strv_fnmatch(m->ignore, link->ifname);
+        return strv_fnmatch(m->ignored_interfaces, link->ifname);
 }
 
 static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange s) {
@@ -44,13 +45,29 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange
          *       0: operstate is not enough
          *       1: online */
 
-        if (!l->state)
+        if (!l->state || streq(l->state, "pending"))
+                /* If no state string exists, networkd (and possibly also udevd) has not detected the
+                 * interface yet, that mean we cannot determine whether the interface is managed or
+                 * not. Hence, return negative value.
+                 * If the link is in pending state, then udevd has not processed the link, and networkd
+                 * has not tried to find .network file for the link. Hence, return negative value. */
                 return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
-                                            "link has not yet been processed by udev");
+                                            "link has not yet been processed by udev: setup state is %s.",
+                                            strna(l->state));
+
+        if (streq(l->state, "unmanaged")) {
+                /* If the link is in unmanaged state, then ignore the interface unless the interface is
+                 * specified in '--interface/-i' option. */
+                if (!hashmap_contains(m->command_line_interfaces_by_name, l->ifname)) {
+                        log_link_debug(l, "link is not managed by networkd (yet?).");
+                        return 0;
+                }
 
-        if (STR_IN_SET(l->state, "configuring", "pending"))
+        } else if (!streq(l->state, "configured"))
+                /* If the link is in non-configured state, return negative value here. */
                 return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN),
-                                            "link is being processed by networkd");
+                                            "link is being processed by networkd: setup state is %s.",
+                                            l->state);
 
         if (s.min < 0)
                 s.min = m->required_operstate.min >= 0 ? m->required_operstate.min
@@ -93,20 +110,21 @@ static int manager_link_is_online(Manager *m, Link *l, LinkOperationalStateRange
                 }
         }
 
+        log_link_debug(l, "link is confiured by networkd and online.");
         return 1;
 }
 
 bool manager_configured(Manager *m) {
         bool one_ready = false;
         const char *ifname;
-        void *p;
         Link *l;
         int r;
 
-        if (!hashmap_isempty(m->interfaces)) {
+        if (!hashmap_isempty(m->command_line_interfaces_by_name)) {
+                LinkOperationalStateRange *range;
+
                 /* wait for all the links given on the command line to appear */
-                HASHMAP_FOREACH_KEY(p, ifname, m->interfaces) {
-                        LinkOperationalStateRange *range = p;
+                HASHMAP_FOREACH_KEY(range, ifname, m->command_line_interfaces_by_name) {
 
                         l = hashmap_get(m->links_by_name, ifname);
                         if (!l && range->min == LINK_OPERSTATE_MISSING) {
@@ -137,7 +155,7 @@ bool manager_configured(Manager *m) {
 
         /* wait for all links networkd manages to be in admin state 'configured'
          * and at least one link to gain a carrier */
-        HASHMAP_FOREACH(l, m->links) {
+        HASHMAP_FOREACH(l, m->links_by_index) {
                 if (manager_ignore_link(m, l)) {
                         log_link_debug(l, "link is ignored");
                         continue;
@@ -189,7 +207,7 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *
                 return 0;
         }
 
-        l = hashmap_get(m->links, INT_TO_PTR(ifindex));
+        l = hashmap_get(m->links_by_index, INT_TO_PTR(ifindex));
 
         switch (type) {
 
@@ -292,7 +310,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *
 
         sd_network_monitor_flush(m->network_monitor);
 
-        HASHMAP_FOREACH(l, m->links) {
+        HASHMAP_FOREACH(l, m->links_by_index) {
                 r = link_update_monitor(l);
                 if (r < 0 && r != -ENODATA)
                         log_link_warning_errno(l, r, "Failed to update link state, ignoring: %m");
@@ -329,10 +347,14 @@ static int manager_network_monitor_listen(Manager *m) {
         return 0;
 }
 
-int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
+int manager_new(Manager **ret,
+                Hashmap *command_line_interfaces_by_name,
+                char **ignored_interfaces,
                 LinkOperationalStateRange required_operstate,
                 AddressFamily required_family,
-                bool any, usec_t timeout) {
+                bool any,
+                usec_t timeout) {
+
         _cleanup_(manager_freep) Manager *m = NULL;
         int r;
 
@@ -343,8 +365,8 @@ int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
                 return -ENOMEM;
 
         *m = (Manager) {
-                .interfaces = interfaces,
-                .ignore = ignore,
+                .command_line_interfaces_by_name = command_line_interfaces_by_name,
+                .ignored_interfaces = ignored_interfaces,
                 .required_operstate = required_operstate,
                 .required_family = required_family,
                 .any = any,
@@ -382,7 +404,7 @@ Manager* manager_free(Manager *m) {
         if (!m)
                 return NULL;
 
-        hashmap_free_with_destructor(m->links, link_free);
+        hashmap_free_with_destructor(m->links_by_index, link_free);
         hashmap_free(m->links_by_name);
 
         sd_event_source_unref(m->network_monitor_event_source);
index f2e091638c42f85b42d9b725c01cacc8a606a475..01ad18f8f626d57b4f58be7375bcef7efd1bedbc 100644 (file)
@@ -13,12 +13,12 @@ typedef struct Manager Manager;
 typedef struct Link Link;
 
 struct Manager {
-        Hashmap *links;
+        Hashmap *links_by_index;
         Hashmap *links_by_name;
 
         /* Do not free the two members below. */
-        Hashmap *interfaces;
-        char **ignore;
+        Hashmap *command_line_interfaces_by_name;
+        char **ignored_interfaces;
 
         LinkOperationalStateRange required_operstate;
         AddressFamily required_family;
@@ -34,7 +34,7 @@ struct Manager {
 };
 
 Manager* manager_free(Manager *m);
-int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
+int manager_new(Manager **ret, Hashmap *command_line_interfaces_by_name, char **ignored_interfaces,
                 LinkOperationalStateRange required_operstate,
                 AddressFamily required_family,
                 bool any, usec_t timeout);
index 9f4f083ab9ac9c0ef0821bf60e9e9668056b56a0..b0a81474ccf961311ae64e69e249aba36916da58 100644 (file)
@@ -410,7 +410,7 @@ static int monitor_swap_contexts_handler(sd_event_source *s, uint64_t usec, void
                 if (r < 0)
                         log_notice_errno(r, "Failed to kill any cgroup(s) based on swap: %m");
                 else {
-                        if (selected)
+                        if (selected && r > 0)
                                 log_notice("Killed %s due to memory used (%"PRIu64") / total (%"PRIu64") and "
                                            "swap used (%"PRIu64") / total (%"PRIu64") being more than "
                                            PERMYRIAD_AS_PERCENT_FORMAT_STR,
@@ -518,9 +518,13 @@ static int monitor_memory_pressure_contexts_handler(sd_event_source *s, uint64_t
                         if (r < 0)
                                 log_notice_errno(r, "Failed to kill any cgroup(s) under %s based on pressure: %m", t->path);
                         else {
-                                /* Don't act on all the high pressure cgroups at once; return as soon as we kill one */
+                                /* Don't act on all the high pressure cgroups at once; return as soon as we kill one.
+                                 * If r == 0 then it means there were not eligible candidates, the candidate cgroup
+                                 * disappeared, or the candidate cgroup has no processes by the time we tried to kill
+                                 * it. In either case, go through the event loop again and select a new candidate if
+                                 * pressure is still high. */
                                 m->mem_pressure_post_action_delay_start = usec_now;
-                                if (selected)
+                                if (selected && r > 0)
                                         log_notice("Killed %s due to memory pressure for %s being %lu.%02lu%% > %lu.%02lu%%"
                                                    " for > %s with reclaim activity",
                                                    selected, t->path,
index 64ea8cf7e43201c2203273ac321459b39c4774c4..cef7519a74bee3ee3eed687f30922851940757d7 100644 (file)
@@ -196,9 +196,19 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) {
                 r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL);
         else
                 r = cg_kill(SYSTEMD_CGROUP_CONTROLLER, path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL);
-        if (r < 0)
+
+        /* The cgroup could have been cleaned up after we have sent SIGKILL to all of the processes, but before
+         * we could do one last iteration of cgroup.procs to check. Or the service unit could have exited and
+         * was removed between picking candidates and coming into this function. In either case, let's log
+         * about it let the caller decide what to do once they know how many PIDs were killed. */
+        if (IN_SET(r, -ENOENT, -ENODEV))
+                log_debug_errno(r, "Error when sending SIGKILL to processes in cgroup path %s, ignoring: %m", path);
+        else if (r < 0)
                 return r;
 
+        if (set_isempty(pids_killed))
+                log_debug("Nothing killed when attempting to kill %s", path);
+
         r = increment_oomd_xattr(path, "user.oomd_kill", set_size(pids_killed));
         if (r < 0)
                 log_debug_errno(r, "Failed to set user.oomd_kill on kill: %m");
@@ -224,8 +234,6 @@ int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char
                         continue;
 
                 r = oomd_cgroup_kill(sorted[i]->path, true, dry_run);
-                if (r == 0)
-                        continue; /* We didn't find anything to kill */
                 if (r == -ENOMEM)
                         return r; /* Treat oom as a hard error */
                 if (r < 0) {
@@ -238,7 +246,7 @@ int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char
                 if (!selected)
                         return -ENOMEM;
                 *ret_selected = selected;
-                return 1;
+                return r;
         }
 
         return ret;
@@ -264,8 +272,6 @@ int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run,
                         continue;
 
                 r = oomd_cgroup_kill(sorted[i]->path, true, dry_run);
-                if (r == 0)
-                        continue; /* We didn't find anything to kill */
                 if (r == -ENOMEM)
                         return r; /* Treat oom as a hard error */
                 if (r < 0) {
@@ -278,7 +284,7 @@ int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run,
                 if (!selected)
                         return -ENOMEM;
                 *ret_selected = selected;
-                return 1;
+                return r;
         }
 
         return ret;
index be311f94c4a30806a58bfd2bcde45881c47cd241..325cb702c51c24b1d3866803f00a4444a3b0c921 100644 (file)
@@ -253,6 +253,7 @@ static int extract_now(
 
                 FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to read directory: %m")) {
                         _cleanup_(portable_metadata_unrefp) PortableMetadata *m = NULL;
+                        _cleanup_(mac_selinux_freep) char *con = NULL;
                         _cleanup_close_ int fd = -1;
 
                         if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
@@ -274,16 +275,16 @@ static int extract_now(
                                 continue;
                         }
 
-                        if (socket_fd >= 0) {
-                                _cleanup_(mac_selinux_freep) char *con = NULL;
 #if HAVE_SELINUX
-                                /* The units will be copied on the host's filesystem, so if they had a SELinux label
-                                 * we have to preserve it. Copy it out so that it can be applied later. */
+                        /* The units will be copied on the host's filesystem, so if they had a SELinux label
+                         * we have to preserve it. Copy it out so that it can be applied later. */
 
-                                r = fgetfilecon_raw(fd, &con);
-                                if (r < 0 && errno != ENODATA)
-                                        log_debug_errno(errno, "Failed to get SELinux file context from '%s', ignoring: %m", de->d_name);
+                        r = fgetfilecon_raw(fd, &con);
+                        if (r < 0 && errno != ENODATA)
+                                log_debug_errno(errno, "Failed to get SELinux file context from '%s', ignoring: %m", de->d_name);
 #endif
+
+                        if (socket_fd >= 0) {
                                 struct iovec iov[] = {
                                         IOVEC_MAKE_STRING(de->d_name),
                                         IOVEC_MAKE((char *)"\0", sizeof(char)),
@@ -295,7 +296,7 @@ static int extract_now(
                                         return log_debug_errno(r, "Failed to send unit metadata to parent: %m");
                         }
 
-                        m = portable_metadata_new(de->d_name, NULL, NULL, fd);
+                        m = portable_metadata_new(de->d_name, where, con, fd);
                         if (!m)
                                 return -ENOMEM;
                         fd = -1;
@@ -336,10 +337,16 @@ static int portable_extract_by_path(
 
         r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, &d);
         if (r == -EISDIR) {
+                _cleanup_free_ char *image_name = NULL;
+
                 /* We can't turn this into a loop-back block device, and this returns EISDIR? Then this is a directory
                  * tree and not a raw device. It's easy then. */
 
-                r = extract_now(path, matches, NULL, path_is_extension, -1, &os_release, &unit_files);
+                r = path_extract_filename(path, &image_name);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to extract image name from path '%s': %m", path);
+
+                r = extract_now(path, matches, image_name, path_is_extension, -1, &os_release, &unit_files);
                 if (r < 0)
                         return r;
 
@@ -505,6 +512,7 @@ static int extract_image_and_extensions(
                 bool validate_sysext,
                 Image **ret_image,
                 OrderedHashmap **ret_extension_images,
+                OrderedHashmap **ret_extension_releases,
                 PortableMetadata **ret_os_release,
                 Hashmap **ret_unit_files,
                 char ***ret_valid_prefixes,
@@ -512,7 +520,7 @@ static int extract_image_and_extensions(
 
         _cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL;
         _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
-        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
+        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
         _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
         _cleanup_strv_free_ char **valid_prefixes = NULL;
         _cleanup_(image_unrefp) Image *image = NULL;
@@ -533,6 +541,12 @@ static int extract_image_and_extensions(
                 if (!extension_images)
                         return -ENOMEM;
 
+                if (ret_extension_releases) {
+                        extension_releases = ordered_hashmap_new(&portable_metadata_hash_ops);
+                        if (!extension_releases)
+                                return -ENOMEM;
+                }
+
                 STRV_FOREACH(p, extension_image_paths) {
                         _cleanup_(image_unrefp) Image *new = NULL;
 
@@ -581,6 +595,7 @@ static int extract_image_and_extensions(
                 _cleanup_(portable_metadata_unrefp) PortableMetadata *extension_release_meta = NULL;
                 _cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
                 _cleanup_strv_free_ char **extension_release = NULL;
+                _cleanup_close_ int extension_release_fd = -1;
                 _cleanup_fclose_ FILE *f = NULL;
                 const char *e;
 
@@ -592,10 +607,15 @@ static int extract_image_and_extensions(
                 if (r < 0)
                         return r;
 
-                if (!validate_sysext && !ret_valid_prefixes)
+                if (!validate_sysext && !ret_valid_prefixes && !ret_extension_releases)
                         continue;
 
-                r = take_fdopen_unlocked(&extension_release_meta->fd, "r", &f);
+                /* We need to keep the fd valid, to return the PortableMetadata to the caller. */
+                extension_release_fd = fd_reopen(extension_release_meta->fd, O_CLOEXEC);
+                if (extension_release_fd < 0)
+                        return extension_release_fd;
+
+                r = take_fdopen_unlocked(&extension_release_fd, "r", &f);
                 if (r < 0)
                         return r;
 
@@ -623,6 +643,13 @@ static int extract_image_and_extensions(
                         if (r < 0)
                                 return r;
                 }
+
+                if (ret_extension_releases) {
+                        r = ordered_hashmap_put(extension_releases, ext->name, extension_release_meta);
+                        if (r < 0)
+                                return r;
+                        TAKE_PTR(extension_release_meta);
+                }
         }
 
         strv_sort(valid_prefixes);
@@ -631,6 +658,8 @@ static int extract_image_and_extensions(
                 *ret_image = TAKE_PTR(image);
         if (ret_extension_images)
                 *ret_extension_images = TAKE_PTR(extension_images);
+        if (ret_extension_releases)
+                *ret_extension_releases = TAKE_PTR(extension_releases);
         if (ret_os_release)
                 *ret_os_release = TAKE_PTR(os_release);
         if (ret_unit_files)
@@ -646,12 +675,13 @@ int portable_extract(
                 char **matches,
                 char **extension_image_paths,
                 PortableMetadata **ret_os_release,
+                OrderedHashmap **ret_extension_releases,
                 Hashmap **ret_unit_files,
                 char ***ret_valid_prefixes,
                 sd_bus_error *error) {
 
         _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
-        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
+        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
         _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
         _cleanup_(strv_freep) char **valid_prefixes = NULL;
         _cleanup_(image_unrefp) Image *image = NULL;
@@ -666,6 +696,7 @@ int portable_extract(
                         /* validate_sysext= */ false,
                         &image,
                         &extension_images,
+                        &extension_releases,
                         &os_release,
                         &unit_files,
                         ret_valid_prefixes ? &valid_prefixes : NULL,
@@ -688,6 +719,8 @@ int portable_extract(
 
         if (ret_os_release)
                 *ret_os_release = TAKE_PTR(os_release);
+        if (ret_extension_releases)
+                *ret_extension_releases = TAKE_PTR(extension_releases);
         if (ret_unit_files)
                 *ret_unit_files = TAKE_PTR(unit_files);
         if (ret_valid_prefixes)
@@ -872,6 +905,10 @@ static const char *root_setting_from_image(ImageType type) {
         return IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "RootDirectory=" : "RootImage=";
 }
 
+static const char *extension_setting_from_image(ImageType type) {
+        return IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "ExtensionDirectories=" : "ExtensionImages=";
+}
+
 static int make_marker_text(const char *image_path, OrderedHashmap *extension_images, char **ret_text) {
         _cleanup_free_ char *text = NULL, *escaped_image_path = NULL;
         Image *ext;
@@ -918,7 +955,6 @@ static int install_chroot_dropin(
                 size_t *n_changes) {
 
         _cleanup_free_ char *text = NULL, *dropin = NULL;
-        Image *ext;
         int r;
 
         assert(image_path);
@@ -936,6 +972,7 @@ static int install_chroot_dropin(
         if (endswith(m->name, ".service")) {
                 const char *os_release_source, *root_type;
                 _cleanup_free_ char *base_name = NULL;
+                Image *ext;
 
                 root_type = root_setting_from_image(type);
 
@@ -962,7 +999,7 @@ static int install_chroot_dropin(
 
                 if (m->image_path && !path_equal(m->image_path, image_path))
                         ORDERED_HASHMAP_FOREACH(ext, extension_images)
-                                if (!strextend(&text, "ExtensionImages=", ext->path, "\n"))
+                                if (!strextend(&text, extension_setting_from_image(ext->type), ext->path, "\n"))
                                         return -ENOMEM;
         }
 
@@ -1261,6 +1298,7 @@ int portable_attach(
                         /* validate_sysext= */ true,
                         &image,
                         &extension_images,
+                        /* extension_releases= */ NULL,
                         /* os_release= */ NULL,
                         &unit_files,
                         &valid_prefixes,
@@ -1651,6 +1689,7 @@ not_found:
 static int portable_get_state_internal(
                 sd_bus *bus,
                 const char *name_or_path,
+                char **extension_image_paths,
                 PortableFlags flags,
                 PortableState *ret,
                 sd_bus_error *error) {
@@ -1695,7 +1734,7 @@ static int portable_get_state_internal(
                 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
                         continue;
 
-                r = test_chroot_dropin(d, where, de->d_name, name_or_path, NULL, NULL);
+                r = test_chroot_dropin(d, where, de->d_name, name_or_path, extension_image_paths, NULL);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -1728,6 +1767,7 @@ static int portable_get_state_internal(
 int portable_get_state(
                 sd_bus *bus,
                 const char *name_or_path,
+                char **extension_image_paths,
                 PortableFlags flags,
                 PortableState *ret,
                 sd_bus_error *error) {
@@ -1741,12 +1781,12 @@ int portable_get_state(
         /* We look for matching units twice: once in the regular directories, and once in the runtime directories — but
          * the latter only if we didn't find anything in the former. */
 
-        r = portable_get_state_internal(bus, name_or_path, flags & ~PORTABLE_RUNTIME, &state, error);
+        r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags & ~PORTABLE_RUNTIME, &state, error);
         if (r < 0)
                 return r;
 
         if (state == PORTABLE_DETACHED) {
-                r = portable_get_state_internal(bus, name_or_path, flags | PORTABLE_RUNTIME, &state, error);
+                r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags | PORTABLE_RUNTIME, &state, error);
                 if (r < 0)
                         return r;
         }
index 2837e8b2869ed1af1c8b9fa2a4f72301d63bffbc..62e1cb70328aa2cc785ae30cbb964f4f9e6dd4c1 100644 (file)
@@ -21,13 +21,14 @@ typedef struct PortableMetadata {
 #define PORTABLE_METADATA_IS_UNIT(m) (!IN_SET((m)->name[0], 0, '/'))
 
 typedef enum PortableFlags {
-        PORTABLE_RUNTIME        = 1 << 0, /* Public API via DBUS, do not change */
-        PORTABLE_PREFER_COPY    = 1 << 1,
-        PORTABLE_PREFER_SYMLINK = 1 << 2,
-        PORTABLE_REATTACH       = 1 << 3,
-        _PORTABLE_MASK_PUBLIC   = PORTABLE_RUNTIME,
+        PORTABLE_RUNTIME                    = 1 << 0,
+        PORTABLE_INSPECT_EXTENSION_RELEASES = 1 << 1, /* Public API via DBUS, do not change */
+        PORTABLE_PREFER_COPY                = 1 << 2,
+        PORTABLE_PREFER_SYMLINK             = 1 << 3,
+        PORTABLE_REATTACH                   = 1 << 4,
+        _PORTABLE_MASK_PUBLIC               = PORTABLE_RUNTIME | PORTABLE_INSPECT_EXTENSION_RELEASES,
         _PORTABLE_TYPE_MAX,
-        _PORTABLE_TYPE_INVALID  = -EINVAL,
+        _PORTABLE_TYPE_INVALID              = -EINVAL,
 } PortableFlags;
 
 /* This enum is anonymous, since we usually store it in an 'int', as we overload it with negative errno
@@ -65,12 +66,12 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref);
 
 int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
 
-int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
+int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
 
 int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
 int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
 
-int portable_get_state(sd_bus *bus, const char *name_or_path, PortableFlags flags, PortableState *ret, sd_bus_error *error);
+int portable_get_state(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error);
 
 int portable_get_profiles(char ***ret);
 
index 60feac6f5dba3acc53190e06ff85a4e234f9105b..eee26d00331d369a3615b46d97f09521b3f24655 100644 (file)
@@ -260,8 +260,8 @@ static int maybe_reload(sd_bus **bus) {
 static int get_image_metadata(sd_bus *bus, const char *image, char **matches, sd_bus_message **reply) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        PortableFlags flags = PORTABLE_INSPECT_EXTENSION_RELEASES;
         const char *method;
-        uint64_t flags = 0;
         int r;
 
         assert(bus);
@@ -366,6 +366,74 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
+        /* If we specified any extensions, we'll first get back exactly the
+         * paths (and extension-release content) for each one of the arguments. */
+        for (size_t i = 0; i < strv_length(arg_extension_images); ++i) {
+                const char *name;
+
+                r = sd_bus_message_enter_container(reply, 'e', "say");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+                if (r == 0)
+                        break;
+
+                r = sd_bus_message_read(reply, "s", &name);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_read_array(reply, 'y', &data, &sz);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                if (arg_cat) {
+                        if (nl)
+                                fputc('\n', stdout);
+
+                        printf("%s-- Extension Release: %s --%s\n", ansi_highlight(), name, ansi_normal());
+                        fwrite(data, sz, 1, stdout);
+                        fflush(stdout);
+                        nl = true;
+                } else {
+                        _cleanup_free_ char *pretty_portable = NULL, *pretty_os = NULL, *sysext_level = NULL,
+                                *id = NULL, *version_id = NULL, *sysext_scope = NULL, *portable_prefixes = NULL;
+                        _cleanup_fclose_ FILE *f = NULL;
+
+                        f = fmemopen_unlocked((void*) data, sz, "re");
+                        if (!f)
+                                return log_error_errno(errno, "Failed to open extension-release buffer: %m");
+
+                        r = parse_env_file(f, name,
+                                           "ID", &id,
+                                           "VERSION_ID", &version_id,
+                                           "SYSEXT_SCOPE", &sysext_scope,
+                                           "SYSEXT_LEVEL", &sysext_level,
+                                           "PORTABLE_PRETTY_NAME", &pretty_portable,
+                                           "PORTABLE_PREFIXES", &portable_prefixes,
+                                           "PRETTY_NAME", &pretty_os);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse extension release from '%s': %m", name);
+
+                        printf("Extension:\n\t%s\n"
+                                "\tExtension Scope:\n\t\t%s\n"
+                                "\tExtension Compatibility Level:\n\t\t%s\n"
+                                "\tPortable Service:\n\t\t%s\n"
+                                "\tPortable Prefixes:\n\t\t%s\n"
+                                "\tOperating System:\n\t\t%s (%s %s)\n",
+                                name,
+                                strna(sysext_scope),
+                                strna(sysext_level),
+                                strna(pretty_portable),
+                                strna(portable_prefixes),
+                                strna(pretty_os),
+                                strna(id),
+                                strna(version_id));
+                }
+
+                r = sd_bus_message_exit_container(reply);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+        }
+
         for (;;) {
                 const char *name;
 
@@ -700,6 +768,14 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
         if (r < 0)
                 return bus_log_parse_error(r);
 
+        /* If we specified any extensions, we'll first get back exactly the
+         * paths (and extension-release content) for each one of the arguments. */
+        for (size_t i = 0; i < strv_length(arg_extension_images); ++i) {
+                r = sd_bus_message_skip(reply, "{say}");
+                if (r < 0)
+                        return bus_log_parse_error(r);
+        }
+
         for (;;) {
                 const char *name;
 
@@ -1034,11 +1110,11 @@ static int set_limit(int argc, char *argv[], void *userdata) {
 }
 
 static int is_image_attached(int argc, char *argv[], void *userdata) {
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_free_ char *image = NULL;
-        const char *state;
+        const char *state, *method;
         int r;
 
         r = determine_image(argv[1], true, &image);
@@ -1049,9 +1125,29 @@ static int is_image_attached(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = bus_call_method(bus, bus_portable_mgr, "GetImageState", &error, &reply, "s", image);
+        method = strv_isempty(arg_extension_images) ? "GetImageState" : "GetImageStateWithExtensions";
+
+        r = bus_message_new_method_call(bus, &m, bus_portable_mgr, method);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "s", image);
         if (r < 0)
-                return log_error_errno(r, "Failed to get image state: %s", bus_error_message(&error, r));
+                return bus_log_create_error(r);
+
+        r = attach_extensions_to_message(m, arg_extension_images);
+        if (r < 0)
+                return r;
+
+        if (!strv_isempty(arg_extension_images)) {
+                r = sd_bus_message_append(m, "t", 0);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
+        if (r < 0)
+                return log_error_errno(r, "%s failed: %s", method, bus_error_message(&error, r));
 
         r = sd_bus_message_read(reply, "s", &state);
         if (r < 0)
index 5b992d9df83937b0777076700be2f8cedfbbeb6a..214fdb5b30abe3c1d3c756fb6b5ffe8a97f034ac 100644 (file)
@@ -169,6 +169,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er
                 r = portable_get_state(
                                 sd_bus_message_get_bus(message),
                                 image->path,
+                                NULL,
                                 0,
                                 &state,
                                 &error_state);
@@ -225,6 +226,7 @@ static int method_get_image_metadata(sd_bus_message *message, void *userdata, sd
 }
 
 static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_strv_free_ char **extension_images = NULL;
         const char *name_or_path;
         PortableState state;
         int r;
@@ -235,9 +237,28 @@ static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bu
         if (r < 0)
                 return r;
 
+        if (sd_bus_message_is_method_call(message, NULL, "GetImageStateWithExtensions")) {
+                uint64_t input_flags = 0;
+
+                r = sd_bus_message_read_strv(message, &extension_images);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_read(message, "t", &input_flags);
+                if (r < 0)
+                        return r;
+
+                /* No flags are supported by this method for now. */
+                if (input_flags != 0)
+                        return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
+                                                          "Invalid 'flags' parameter '%" PRIu64 "'",
+                                                          input_flags);
+        }
+
         r = portable_get_state(
                         sd_bus_message_get_bus(message),
                         name_or_path,
+                        extension_images,
                         0,
                         &state,
                         error);
@@ -428,6 +449,13 @@ const sd_bus_vtable manager_vtable[] = {
                                 SD_BUS_RESULT("s", state),
                                 method_get_image_state,
                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("GetImageStateWithExtensions",
+                                SD_BUS_ARGS("s", image,
+                                            "as", extensions,
+                                            "t", flags),
+                                SD_BUS_RESULT("s", state),
+                                method_get_image_state,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD_WITH_ARGS("AttachImage",
                                 SD_BUS_ARGS("s", image,
                                             "as", matches,
index ede062dbfb4e5de274cbaf0c1a92843dd367499c..6660498e51fbffdd12b685484091c5787e05ffdc 100644 (file)
@@ -102,13 +102,13 @@ int bus_image_common_get_metadata(
                 Image *image,
                 sd_bus_error *error) {
 
+        _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_releases = NULL;
         _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
         _cleanup_strv_free_ char **matches = NULL, **extension_images = NULL;
         _cleanup_hashmap_free_ Hashmap *unit_files = NULL;
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_free_ PortableMetadata **sorted = NULL;
-        /* Unused for now, but added to the DBUS methods for future-proofing */
-        uint64_t input_flags = 0;
+        PortableFlags flags = 0;
         size_t i;
         int r;
 
@@ -133,14 +133,17 @@ int bus_image_common_get_metadata(
 
         if (sd_bus_message_is_method_call(message, NULL, "GetImageMetadataWithExtensions") ||
             sd_bus_message_is_method_call(message, NULL, "GetMetadataWithExtensions")) {
+                uint64_t input_flags = 0;
+
                 r = sd_bus_message_read(message, "t", &input_flags);
                 if (r < 0)
                         return r;
-                /* Let clients know that this version doesn't support any flags */
-                if (input_flags != 0)
+
+                if ((input_flags & ~_PORTABLE_MASK_PUBLIC) != 0)
                         return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
                                                           "Invalid 'flags' parameter '%" PRIu64 "'",
                                                           input_flags);
+                flags |= input_flags;
         }
 
         r = bus_image_acquire(m,
@@ -161,6 +164,7 @@ int bus_image_common_get_metadata(
                         matches,
                         extension_images,
                         &os_release,
+                        &extension_releases,
                         &unit_files,
                         NULL,
                         error);
@@ -187,6 +191,32 @@ int bus_image_common_get_metadata(
         if (r < 0)
                 return r;
 
+        /* If it was requested, also send back the extension path and the content
+         * of each extension-release file. Behind a flag, as it's an incompatible
+         * change. */
+        if (FLAGS_SET(flags, PORTABLE_INSPECT_EXTENSION_RELEASES)) {
+                PortableMetadata *extension_release;
+
+                ORDERED_HASHMAP_FOREACH(extension_release, extension_releases) {
+
+                        r = sd_bus_message_open_container(reply, 'e', "say");
+                        if (r < 0)
+                                return r;
+
+                        r = sd_bus_message_append(reply, "s", extension_release->image_path);
+                        if (r < 0)
+                                return r;
+
+                        r = append_fd(reply, extension_release);
+                        if (r < 0)
+                                return r;
+
+                        r = sd_bus_message_close_container(reply);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
         for (i = 0; i < hashmap_size(unit_files); i++) {
 
                 r = sd_bus_message_open_container(reply, 'e', "say");
@@ -222,6 +252,7 @@ static int bus_image_method_get_state(
                 void *userdata,
                 sd_bus_error *error) {
 
+        _cleanup_strv_free_ char **extension_images = NULL;
         Image *image = userdata;
         PortableState state;
         int r;
@@ -229,9 +260,28 @@ static int bus_image_method_get_state(
         assert(message);
         assert(image);
 
+        if (sd_bus_message_is_method_call(message, NULL, "GetStateWithExtensions")) {
+                uint64_t input_flags = 0;
+
+                r = sd_bus_message_read_strv(message, &extension_images);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_read(message, "t", &input_flags);
+                if (r < 0)
+                        return r;
+
+                /* No flags are supported by this method for now. */
+                if (input_flags != 0)
+                        return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS,
+                                                          "Invalid 'flags' parameter '%" PRIu64 "'",
+                                                          input_flags);
+        }
+
         r = portable_get_state(
                         sd_bus_message_get_bus(message),
                         image->path,
+                        extension_images,
                         0,
                         &state,
                         error);
@@ -462,6 +512,7 @@ int bus_image_common_remove(
         r = portable_get_state(
                         sd_bus_message_get_bus(message),
                         image->path,
+                        NULL,
                         0,
                         &state,
                         error);
@@ -846,6 +897,12 @@ const sd_bus_vtable image_vtable[] = {
                                 SD_BUS_RESULT("s", state),
                                 bus_image_method_get_state,
                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("GetStateWithExtensions",
+                                SD_BUS_ARGS("as", extensions,
+                                            "t", flags),
+                                SD_BUS_RESULT("s", state),
+                                bus_image_method_get_state,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD_WITH_ARGS("Attach",
                                 SD_BUS_ARGS("as", matches,
                                             "s", profile,
diff --git a/src/resolve/fuzz-etc-hosts.c b/src/resolve/fuzz-etc-hosts.c
new file mode 100644 (file)
index 0000000..050c85d
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "fuzz.h"
+#include "resolved-etc-hosts.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_(etc_hosts_free) EtcHosts h = {};
+
+        if (!getenv("SYSTEMD_LOG_LEVEL"))
+                log_set_max_level(LOG_CRIT);
+
+        f = data_to_file(data, size);
+        assert_se(f);
+
+        (void) etc_hosts_parse(&h, f);
+
+        return 0;
+}
index 1fee993d0a25551198cdeecdb82144ebb776552f..24f94f8c0e1618eb4fd9652595facc2a7b652318 100644 (file)
@@ -70,7 +70,6 @@ systemd_resolved_sources = files('''
         resolved-socket-graveyard.h
         resolved-varlink.c
         resolved-varlink.h
-        resolved.c
 '''.split())
 
 resolvectl_sources = files('''
@@ -200,6 +199,14 @@ tests += [
          [lib_openssl_or_gcrypt,
           libm]],
 
+        [files('test-resolved-stream.c')
+         + basic_dns_sources + systemd_resolved_sources,
+         [libshared],
+         [lib_openssl_or_gcrypt,
+          libm]
+         + systemd_resolved_dependencies,
+         resolve_includes],
+
         [files('test-dnssec.c'),
          [libsystemd_resolve_core,
           libshared],
@@ -221,4 +228,13 @@ fuzzers += [
           libshared],
          [lib_openssl_or_gcrypt,
           libm]],
+        [files('fuzz-etc-hosts.c',
+          'resolved-etc-hosts.c',
+          'resolved-etc-hosts.h'),
+         [libsystemd_resolve_core,
+          libshared],
+         [lib_openssl_or_gcrypt,
+          libm]],
 ]
+
+systemd_resolved_sources += files('resolved.c')
index c0f6df6447c69fa9a53104514b04aa98ed4ff9ed..f0d0ca4bba31a63252d4ff2aa808638d2465f37e 100644 (file)
@@ -613,6 +613,10 @@ DnsScopeMatch dns_scope_good_domain(
         if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family, false, false) & flags) == 0)
                 return DNS_SCOPE_NO;
 
+        /* Never resolve empty name. */
+        if (dns_name_is_empty(domain))
+                return DNS_SCOPE_NO;
+
         /* Never resolve any loopback hostname or IP address via DNS, LLMNR or mDNS. Instead, always rely on
          * synthesized RRs for these. */
         if (is_localhost(domain) ||
index f48e2a8029850bfa69fb5477a8eff70be557260f..51ffa6b4b056e2804a2c779820e18d620fd11c0f 100644 (file)
@@ -6,6 +6,7 @@
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "io-util.h"
+#include "macro.h"
 #include "missing_network.h"
 #include "resolved-dns-stream.h"
 #include "resolved-manager.h"
@@ -280,13 +281,15 @@ static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
         return dns_stream_complete(s, ETIMEDOUT);
 }
 
-static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
-        _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */
+static int on_stream_io_impl(DnsStream *s, uint32_t revents) {
         bool progressed = false;
         int r;
 
         assert(s);
 
+        /* This returns 1 when possible remaining stream exists, 0 on completed
+           stream or recoverable error, and negative errno on failure. */
+
 #if ENABLE_DNS_OVER_TLS
         if (s->encrypted) {
                 r = dnstls_stream_on_io(s, revents);
@@ -441,6 +444,44 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
                         log_warning_errno(errno, "Couldn't restart TCP connection timeout, ignoring: %m");
         }
 
+        return 1;
+}
+
+static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+        _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */
+        int r;
+
+        assert(s);
+
+        r = on_stream_io_impl(s, revents);
+        if (r <= 0)
+                return r;
+
+#if ENABLE_DNS_OVER_TLS
+        if (!s->encrypted)
+                return 0;
+
+        /* When using DNS-over-TLS, the underlying TLS library may read the entire TLS record
+           and buffer it internally. If this happens, we will not receive further EPOLLIN events,
+           and unless there's some unrelated activity on the socket, we will hang until time out.
+           To avoid this, if there's buffered TLS data, generate a "fake" EPOLLIN event.
+           This is hacky, but it makes this case transparent to the rest of the IO code. */
+        while (dnstls_stream_has_buffered_data(s)) {
+                uint32_t events;
+
+                /* Make sure the stream still wants to process more data... */
+                r = sd_event_source_get_io_events(s->io_event_source, &events);
+                if (r < 0)
+                        return r;
+                if (!FLAGS_SET(events, EPOLLIN))
+                        break;
+
+                r = on_stream_io_impl(s, EPOLLIN);
+                if (r <= 0)
+                        return r;
+        }
+#endif
+
         return 0;
 }
 
index ef1423f44169521856dec0dd9d27f716f8ab0267..0914515fdfb29a8da00fe42e56f053596ab99174 100644 (file)
@@ -394,7 +394,18 @@ int dns_synthesize_answer(
 
                 name = dns_resource_key_name(key);
 
-                if (is_localhost(name)) {
+                if (dns_name_is_empty(name)) {
+                        /* Do nothing. */
+
+                } else if (dns_name_endswith(name, "0.in-addr.arpa") > 0 ||
+                           dns_name_equal(name, "255.255.255.255.in-addr.arpa") > 0 ||
+                           dns_name_equal(name, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0 ||
+                           dns_name_endswith(name, "invalid") > 0) {
+
+                        nxdomain = true;
+                        continue;
+
+                } else if (is_localhost(name)) {
 
                         r = synthesize_localhost_rr(m, key, ifindex, &answer);
                         if (r < 0)
index e7ccba934e5204b136af00dd457c2b83a74f0d4d..8610cacab67cad01c1177432a807854b5444899a 100644 (file)
@@ -211,6 +211,14 @@ ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
         return ss;
 }
 
+bool dnstls_stream_has_buffered_data(DnsStream *stream) {
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.session);
+
+        return gnutls_record_check_pending(stream->dnstls_data.session) > 0;
+}
+
 void dnstls_server_free(DnsServer *server) {
         assert(server);
 
index cba3f14f2d93aca800f352f75807f4fe3f011c78..7d264dd367365272f72d0920acd4dc0ff7b28ed7 100644 (file)
@@ -367,6 +367,14 @@ ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
         return ss;
 }
 
+bool dnstls_stream_has_buffered_data(DnsStream *stream) {
+        assert(stream);
+        assert(stream->encrypted);
+        assert(stream->dnstls_data.ssl);
+
+        return SSL_has_pending(stream->dnstls_data.ssl) > 0;
+}
+
 void dnstls_server_free(DnsServer *server) {
         assert(server);
 
index b638d61ec7a4d1efd763736909635891ae66c824..ed214dc6c46cea00197dbf513b6434597a0d07fc 100644 (file)
@@ -3,6 +3,7 @@
 
 #if ENABLE_DNS_OVER_TLS
 
+#include <stdbool.h>
 #include <stdint.h>
 
 typedef struct DnsServer DnsServer;
@@ -28,6 +29,7 @@ int dnstls_stream_on_io(DnsStream *stream, uint32_t revents);
 int dnstls_stream_shutdown(DnsStream *stream, int error);
 ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count);
 ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count);
+bool dnstls_stream_has_buffered_data(DnsStream *stream);
 
 void dnstls_server_free(DnsServer *server);
 
index 9af3a27bb10b0961e2422ad818f90e76544e1cf1..a8da6c3d8810c36efbe45f8ca435d44278044bda 100644 (file)
@@ -109,7 +109,10 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
 
                 r = dns_name_is_valid_ldh(name);
                 if (r <= 0) {
-                        log_warning_errno(r, "/etc/hosts:%u: hostname \"%s\" is not valid, ignoring.", nr, name);
+                        if (r < 0)
+                                log_warning_errno(r, "/etc/hosts:%u: Failed to check the validity of hostname \"%s\", ignoring: %m", nr, name);
+                        else
+                                log_warning("/etc/hosts:%u: hostname \"%s\" is not valid, ignoring.", nr, name);
                         continue;
                 }
 
index 6c910498a2579bb29725f9aa0b61d65f434f2bdc..9eb0ccefd121ce3c7f6ac9c23cbd193a3fdad2fa 100644 (file)
@@ -11,6 +11,7 @@
 #include "fileio.h"
 #include "log-link.h"
 #include "mkdir.h"
+#include "netif-util.h"
 #include "parse-util.h"
 #include "resolved-link.h"
 #include "resolved-llmnr.h"
@@ -133,7 +134,7 @@ void link_allocate_scopes(Link *l) {
 
                         r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to allocate DNS scope: %m");
+                                log_link_warning_errno(l, r, "Failed to allocate DNS scope, ignoring: %m");
                 }
         } else
                 l->unicast_scope = dns_scope_free(l->unicast_scope);
@@ -144,7 +145,7 @@ void link_allocate_scopes(Link *l) {
                 if (!l->llmnr_ipv4_scope) {
                         r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m");
+                                log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv4 scope, ignoring: %m");
                 }
         } else
                 l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope);
@@ -156,7 +157,7 @@ void link_allocate_scopes(Link *l) {
                 if (!l->llmnr_ipv6_scope) {
                         r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m");
+                                log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv6 scope, ignoring: %m");
                 }
         } else
                 l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope);
@@ -167,7 +168,7 @@ void link_allocate_scopes(Link *l) {
                 if (!l->mdns_ipv4_scope) {
                         r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to allocate mDNS IPv4 scope: %m");
+                                log_link_warning_errno(l, r, "Failed to allocate mDNS IPv4 scope, ignoring: %m");
                 }
         } else
                 l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
@@ -178,7 +179,7 @@ void link_allocate_scopes(Link *l) {
                 if (!l->mdns_ipv6_scope) {
                         r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to allocate mDNS IPv6 scope: %m");
+                                log_link_warning_errno(l, r, "Failed to allocate mDNS IPv6 scope, ignoring: %m");
                 }
         } else
                 l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope);
@@ -198,13 +199,13 @@ void link_add_rrs(Link *l, bool force_remove) {
                 if (l->mdns_ipv4_scope) {
                         r = dns_scope_add_dnssd_services(l->mdns_ipv4_scope);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add IPv4 DNS-SD services: %m");
+                                log_link_warning_errno(l, r, "Failed to add IPv4 DNS-SD services, ignoring: %m");
                 }
 
                 if (l->mdns_ipv6_scope) {
                         r = dns_scope_add_dnssd_services(l->mdns_ipv6_scope);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add IPv6 DNS-SD services: %m");
+                                log_link_warning_errno(l, r, "Failed to add IPv6 DNS-SD services, ignoring: %m");
                 }
 
         } else {
@@ -212,13 +213,13 @@ void link_add_rrs(Link *l, bool force_remove) {
                 if (l->mdns_ipv4_scope) {
                         r = dns_scope_remove_dnssd_services(l->mdns_ipv4_scope);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to remove IPv4 DNS-SD services: %m");
+                                log_link_warning_errno(l, r, "Failed to remove IPv4 DNS-SD services, ignoring: %m");
                 }
 
                 if (l->mdns_ipv6_scope) {
                         r = dns_scope_remove_dnssd_services(l->mdns_ipv6_scope);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to remove IPv6 DNS-SD services: %m");
+                                log_link_warning_errno(l, r, "Failed to remove IPv6 DNS-SD services, ignoring: %m");
                 }
         }
 }
@@ -237,15 +238,16 @@ int link_process_rtnl(Link *l, sd_netlink_message *m) {
         (void) sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
         (void) sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &l->operstate);
 
-        if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
+        if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0 &&
+            !streq_ptr(l->ifname, n)) {
+                if (l->ifname)
+                        log_link_debug(l, "Interface name change detected: %s -> %s", l->ifname, n);
+
                 r = free_and_strdup(&l->ifname, n);
                 if (r < 0)
                         return r;
         }
 
-        link_allocate_scopes(l);
-        link_add_rrs(l, false);
-
         return 0;
 }
 
@@ -382,7 +384,10 @@ void link_set_dns_over_tls_mode(Link *l, DnsOverTlsMode mode) {
 
 #if ! ENABLE_DNS_OVER_TLS
         if (mode != DNS_OVER_TLS_NO)
-                log_warning("DNS-over-TLS option for the link cannot be enabled or set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support.");
+                log_link_warning(l,
+                                 "DNS-over-TLS option for the link cannot be enabled or set to opportunistic "
+                                 "when systemd-resolved is built without DNS-over-TLS support. "
+                                 "Turning off DNS-over-TLS support.");
         return;
 #endif
 
@@ -417,7 +422,10 @@ void link_set_dnssec_mode(Link *l, DnssecMode mode) {
 
 #if !HAVE_OPENSSL_OR_GCRYPT
         if (IN_SET(mode, DNSSEC_YES, DNSSEC_ALLOW_DOWNGRADE))
-                log_warning("DNSSEC option for the link cannot be enabled or set to allow-downgrade when systemd-resolved is built without a cryptographic library. Turning off DNSSEC support.");
+                log_link_warning(l,
+                                 "DNSSEC option for the link cannot be enabled or set to allow-downgrade "
+                                 "when systemd-resolved is built without a cryptographic library. "
+                                 "Turning off DNSSEC support.");
         return;
 #endif
 
@@ -608,6 +616,10 @@ static void link_read_settings(Link *l) {
 
         l->is_managed = true;
 
+        r = network_link_get_operational_state(l->ifindex, &l->networkd_operstate);
+        if (r < 0)
+                log_link_warning_errno(l, r, "Failed to read networkd's link operational state, ignoring: %m");
+
         r = link_update_dns_servers(l);
         if (r < 0)
                 log_link_warning_errno(l, r, "Failed to read DNS servers for the interface, ignoring: %m");
@@ -670,7 +682,6 @@ int link_update(Link *l) {
 }
 
 bool link_relevant(Link *l, int family, bool local_multicast) {
-        _cleanup_free_ char *state = NULL;
         LinkAddress *a;
 
         assert(l);
@@ -681,24 +692,21 @@ bool link_relevant(Link *l, int family, bool local_multicast) {
          * A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at
          * least one routable address. */
 
-        if (l->flags & (IFF_LOOPBACK|IFF_DORMANT))
+        if ((l->flags & (IFF_LOOPBACK | IFF_DORMANT)) != 0)
                 return false;
 
-        if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP))
+        if (!FLAGS_SET(l->flags, IFF_UP | IFF_LOWER_UP))
                 return false;
 
-        if (local_multicast) {
-                if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST)
-                        return false;
-        }
+        if (local_multicast &&
+            !FLAGS_SET(l->flags, IFF_MULTICAST))
+                return false;
 
-        /* Check kernel operstate
-         * https://www.kernel.org/doc/Documentation/networking/operstates.txt */
-        if (!IN_SET(l->operstate, IF_OPER_UNKNOWN, IF_OPER_UP))
+        if (!netif_has_carrier(l->operstate, l->flags))
                 return false;
 
-        (void) sd_network_link_get_operational_state(l->ifindex, &state);
-        if (state && !STR_IN_SET(state, "unknown", "degraded", "degraded-carrier", "routable"))
+        if (l->is_managed &&
+            !IN_SET(l->networkd_operstate, LINK_OPERSTATE_DEGRADED_CARRIER, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_ROUTABLE))
                 return false;
 
         LIST_FOREACH(addresses, a, l->addresses)
@@ -733,7 +741,7 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
                 return s;
 
         if (s)
-                log_debug("Switching to DNS server %s for interface %s.", strna(dns_server_string_full(s)), l->ifname);
+                log_link_debug(l, "Switching to DNS server %s.", strna(dns_server_string_full(s)));
 
         dns_server_unref(l->current_dns_server);
         l->current_dns_server = dns_server_ref(s);
@@ -925,11 +933,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
 
                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add A record to LLMNR zone: %m");
+                                log_link_warning_errno(a->link, r, "Failed to add A record to LLMNR zone, ignoring: %m");
 
                         r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add IPv4 PTR record to LLMNR zone: %m");
+                                log_link_warning_errno(a->link, r, "Failed to add IPv4 PTR record to LLMNR zone, ignoring: %m");
                 } else {
                         if (a->llmnr_address_rr) {
                                 if (a->link->llmnr_ipv4_scope)
@@ -978,11 +986,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
 
                         r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_address_rr, true);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add A record to MDNS zone: %m");
+                                log_link_warning_errno(a->link, r, "Failed to add A record to MDNS zone, ignoring: %m");
 
                         r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_ptr_rr, false);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add IPv4 PTR record to MDNS zone: %m");
+                                log_link_warning_errno(a->link, r, "Failed to add IPv4 PTR record to MDNS zone, ignoring: %m");
                 } else {
                         if (a->mdns_address_rr) {
                                 if (a->link->mdns_ipv4_scope)
@@ -1035,11 +1043,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
 
                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m");
+                                log_link_warning_errno(a->link, r, "Failed to add AAAA record to LLMNR zone, ignoring: %m");
 
                         r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m");
+                                log_link_warning_errno(a->link, r, "Failed to add IPv6 PTR record to LLMNR zone, ignoring: %m");
                 } else {
                         if (a->llmnr_address_rr) {
                                 if (a->link->llmnr_ipv6_scope)
@@ -1089,11 +1097,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
 
                         r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_address_rr, true);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add AAAA record to MDNS zone: %m");
+                                log_link_warning_errno(a->link, r, "Failed to add AAAA record to MDNS zone, ignoring: %m");
 
                         r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_ptr_rr, false);
                         if (r < 0)
-                                log_warning_errno(r, "Failed to add IPv6 PTR record to MDNS zone: %m");
+                                log_link_warning_errno(a->link, r, "Failed to add IPv6 PTR record to MDNS zone, ignoring: %m");
                 } else {
                         if (a->mdns_address_rr) {
                                 if (a->link->mdns_ipv6_scope)
@@ -1112,7 +1120,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
         return;
 
 fail:
-        log_debug_errno(r, "Failed to update address RRs: %m");
+        log_link_debug_errno(a->link, r, "Failed to update address RRs, ignoring: %m");
 }
 
 int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
@@ -1284,7 +1292,7 @@ fail:
         if (temp_path)
                 (void) unlink(temp_path);
 
-        return log_error_errno(r, "Failed to save link data %s: %m", l->state_file);
+        return log_link_error_errno(l, r, "Failed to save link data %s: %m", l->state_file);
 }
 
 int link_load_user(Link *l) {
@@ -1354,7 +1362,7 @@ int link_load_user(Link *l) {
 
                 r = link_update_dns_server_one(l, word);
                 if (r < 0) {
-                        log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word);
+                        log_link_debug_errno(l, r, "Failed to load DNS server '%s', ignoring: %m", word);
                         continue;
                 }
         }
@@ -1375,7 +1383,7 @@ int link_load_user(Link *l) {
 
                 r = link_update_search_domain_one(l, n, is_route);
                 if (r < 0) {
-                        log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word);
+                        log_link_debug_errno(l, r, "Failed to load search domain '%s', ignoring: %m", word);
                         continue;
                 }
         }
@@ -1399,7 +1407,7 @@ int link_load_user(Link *l) {
         return 0;
 
 fail:
-        return log_error_errno(r, "Failed to load link data %s: %m", l->state_file);
+        return log_link_error_errno(l, r, "Failed to load link data %s: %m", l->state_file);
 }
 
 void link_remove_user(Link *l) {
index f65718ce310ca6d50a81147d4734ec4d158210e5..b5299e0b5b87ae5577e88cf26b644c7675451ace 100644 (file)
@@ -6,6 +6,7 @@
 #include "sd-netlink.h"
 
 #include "in-addr-util.h"
+#include "network-util.h"
 #include "ratelimit.h"
 #include "resolve-util.h"
 
@@ -68,6 +69,7 @@ struct Link {
         DnsScope *mdns_ipv6_scope;
 
         struct stat networkd_state_file_stat;
+        LinkOperationalState networkd_operstate;
         bool is_managed;
 
         char *ifname;
diff --git a/src/resolve/test-resolved-stream.c b/src/resolve/test-resolved-stream.c
new file mode 100644 (file)
index 0000000..5017338
--- /dev/null
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "log.h"
+#include "process-util.h"
+#include "resolved-dns-packet.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-rr.h"
+#if ENABLE_DNS_OVER_TLS
+#include "resolved-dnstls.h"
+#endif
+#include "resolved-dns-server.h"
+#include "resolved-dns-stream.h"
+#include "resolved-manager.h"
+#include "sd-event.h"
+#include "sparse-endian.h"
+#include "tests.h"
+
+static struct sockaddr_in SERVER_ADDRESS;
+
+/* Bytes of the questions & answers used in the test, including TCP DNS 2-byte length prefix */
+static const uint8_t QUESTION_A[] =  {
+        0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
+        'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01
+};
+static const uint8_t QUESTION_AAAA[] =  {
+        0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
+        'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01
+};
+static const uint8_t ANSWER_A[] =  {
+        0x00, 0x2D, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
+        'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0,
+        0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x52, 0x8D, 0x00, 0x04, 0x5D, 0xB8, 0xD8, 0x22,
+};
+static const uint8_t ANSWER_AAAA[] =  {
+        0x00, 0x39, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e',
+        'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01, 0xC0,
+        0x0C, 0x00, 0x1C, 0x00, 0x01, 0x00, 0x00, 0x54, 0x4B, 0x00, 0x10, 0x26, 0x06, 0x28, 0x00, 0x02,
+        0x20, 0x00, 0x01, 0x02, 0x48, 0x18, 0x93, 0x25, 0xC8, 0x19, 0x46,
+};
+
+/**
+ * A mock TCP DNS server that asserts certain questions are received
+ * and replies with the same answer every time.
+ */
+static void receive_and_check_question(int fd, const uint8_t *expected_question,
+                                       size_t question_size) {
+        uint8_t *actual_question;
+        size_t n_read = 0;
+
+        actual_question = newa(uint8_t, question_size);
+        while (n_read < question_size) {
+                ssize_t r = read(fd, actual_question + n_read, question_size - n_read);
+                assert_se(r >= 0);
+                n_read += (size_t)r;
+        }
+        assert_se(n_read == question_size);
+
+        assert_se(memcmp(expected_question, actual_question, question_size) == 0);
+}
+
+static void send_answer(int fd, const uint8_t *answer, size_t answer_size) {
+        assert_se(write(fd, answer, answer_size) == (ssize_t)answer_size);
+}
+
+/* Sends two answers together in a single write operation,
+ * so they hopefully end up in a single TCP packet / TLS record */
+static void send_answers_together(int fd,
+                                  const uint8_t *answer1, size_t answer1_size,
+                                  const uint8_t *answer2, size_t answer2_size) {
+        uint8_t *answer;
+        size_t answer_size = answer1_size + answer2_size;
+
+        answer = newa(uint8_t, answer_size);
+        memcpy(answer, answer1, answer1_size);
+        memcpy(answer + answer1_size, answer2, answer2_size);
+        assert_se(write(fd, answer, answer_size) == (ssize_t)answer_size);
+}
+
+static void server_handle(int fd) {
+        receive_and_check_question(fd, QUESTION_A, sizeof(QUESTION_A));
+        send_answer(fd, ANSWER_A, sizeof(ANSWER_A));
+
+        receive_and_check_question(fd, QUESTION_AAAA, sizeof(QUESTION_AAAA));
+        send_answer(fd, ANSWER_AAAA, sizeof(ANSWER_AAAA));
+
+        receive_and_check_question(fd, QUESTION_A, sizeof(QUESTION_A));
+        receive_and_check_question(fd, QUESTION_AAAA, sizeof(QUESTION_AAAA));
+        send_answers_together(fd, ANSWER_A, sizeof(ANSWER_A),
+                                  ANSWER_AAAA, sizeof(ANSWER_AAAA));
+}
+
+static void *tcp_dns_server(void *p) {
+        _cleanup_close_ int bindfd = -1, acceptfd = -1;
+
+        assert_se((bindfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0);
+        assert_se(setsockopt(bindfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) >= 0);
+        assert_se(bind(bindfd, &SERVER_ADDRESS, sizeof(SERVER_ADDRESS)) >= 0);
+        assert_se(listen(bindfd, 1) >= 0);
+        assert_se((acceptfd = accept(bindfd, NULL, NULL)) >= 0);
+        server_handle(acceptfd);
+        return NULL;
+}
+
+#if ENABLE_DNS_OVER_TLS
+/*
+ * Spawns a DNS TLS server using the command line "openssl s_server" tool.
+ */
+static void *tls_dns_server(void *p) {
+        pid_t openssl_pid;
+        int r;
+        _cleanup_close_ int fd_server = -1, fd_tls = -1;
+        _cleanup_free_ char *cert_path = NULL, *key_path = NULL;
+        _cleanup_free_ char *ip_str = NULL, *bind_str = NULL;
+
+        assert_se(get_testdata_dir("test-resolve/selfsigned.cert", &cert_path) >= 0);
+        assert_se(get_testdata_dir("test-resolve/selfsigned.key", &key_path) >= 0);
+
+        assert_se(in_addr_to_string(SERVER_ADDRESS.sin_family,
+                                    &(union in_addr_union){.in = SERVER_ADDRESS.sin_addr},
+                                    &ip_str) >= 0);
+        assert_se(asprintf(&bind_str, "%s:%d", ip_str, be16toh(SERVER_ADDRESS.sin_port)) >= 0);
+
+        /* We will hook one of the socketpair ends to OpenSSL's TLS server
+         * stdin/stdout, so we will be able to read and write plaintext
+         * from the other end's file descriptor like an usual TCP server */
+        {
+                int fd[2];
+                assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) >= 0);
+                fd_server = fd[0];
+                fd_tls = fd[1];
+        }
+
+        r = safe_fork_full("(test-resolved-stream-tls-openssl)", (int[]) { fd_server, fd_tls }, 2,
+                FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_REOPEN_LOG, &openssl_pid);
+        assert(r >= 0);
+        if (r == 0) {
+                /* Child */
+                assert_se(dup2(fd_tls, STDIN_FILENO) >= 0);
+                assert_se(dup2(fd_tls, STDOUT_FILENO) >= 0);
+                close(TAKE_FD(fd_server));
+                close(TAKE_FD(fd_tls));
+
+                execlp("openssl", "openssl", "s_server", "-accept", bind_str,
+                       "-key", key_path, "-cert", cert_path,
+                       "-quiet", "-naccept", "1", NULL);
+                log_error("exec failed, is something wrong with the 'openssl' command?");
+                _exit(EXIT_FAILURE);
+        } else {
+                pthread_mutex_t *server_lock = (pthread_mutex_t *)p;
+
+                server_handle(fd_server);
+
+                /* Once the test is done kill the TLS server to release the port */
+                assert_se(pthread_mutex_lock(server_lock) == 0);
+                assert_se(kill(openssl_pid, SIGTERM) >= 0);
+                assert_se(waitpid(openssl_pid, NULL, 0) >= 0);
+                assert_se(pthread_mutex_unlock(server_lock) == 0);
+        }
+
+        return NULL;
+}
+#endif
+
+static const char *TEST_DOMAIN = "example.com";
+static const uint64_t EVENT_TIMEOUT_USEC = 5 * 1000 * 1000;
+
+static void send_simple_question(DnsStream *stream, uint16_t type) {
+        _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+        _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+
+        assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 0);
+        assert_se(question = dns_question_new(1));
+        assert_se(key = dns_resource_key_new(DNS_CLASS_IN, type, TEST_DOMAIN));
+        assert_se(dns_question_add(question, key, 0) >= 0);
+        assert_se(dns_packet_append_question(p, question) >= 0);
+        DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(question));
+        assert_se(dns_stream_write_packet(stream, p) >= 0);
+}
+
+static const size_t MAX_RECEIVED_PACKETS = 2;
+static DnsPacket *received_packets[2] = {};
+static size_t n_received_packets = 0;
+
+static int on_stream_packet(DnsStream *stream) {
+        assert_se(n_received_packets < MAX_RECEIVED_PACKETS);
+        assert_se(received_packets[n_received_packets++] = dns_stream_take_read_packet(stream));
+        return 0;
+}
+
+static void test_dns_stream(bool tls) {
+        Manager manager = {};
+         _cleanup_(dns_stream_unrefp) DnsStream *stream = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+        _cleanup_close_ int clientfd = -1;
+        int r;
+
+        void *(*server_entrypoint)(void *);
+        pthread_t server_thread;
+        pthread_mutex_t server_lock;
+
+        log_info("test-resolved-stream: Started %s test", tls ? "TLS" : "TCP");
+
+#if ENABLE_DNS_OVER_TLS
+        if (tls) {
+                /* For TLS mode, use DNS_OVER_TLS_OPPORTUNISTIC instead of
+                 * DNS_OVER_TLS_YES, just to make certificate validation more
+                 * lenient, allowing us to use self-signed certificates.
+                 * We never downgrade, everything we test always goes over TLS */
+                manager.dns_over_tls_mode = DNS_OVER_TLS_OPPORTUNISTIC;
+        }
+#endif
+
+        assert_se(sd_event_new(&event) >= 0);
+        manager.event = event;
+
+        /* Set up a mock DNS (over TCP or TLS) server */
+        server_entrypoint = tcp_dns_server;
+#if ENABLE_DNS_OVER_TLS
+        if (tls)
+                server_entrypoint = tls_dns_server;
+#endif
+        assert_se(pthread_mutex_init(&server_lock, NULL) == 0);
+        assert_se(pthread_mutex_lock(&server_lock) == 0);
+        assert_se(pthread_create(&server_thread, NULL, server_entrypoint, &server_lock) == 0);
+
+        /* Create a socket client and connect to the TCP or TLS server
+         * The server may not be up immediately, so try to connect a few times before failing */
+        assert_se((clientfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0);
+
+        for (int i = 0; i < 100; i++) {
+                r = connect(clientfd, &SERVER_ADDRESS, sizeof(SERVER_ADDRESS));
+                if (r >= 0)
+                        break;
+                usleep(EVENT_TIMEOUT_USEC / 100);
+        }
+        assert_se(r >= 0);
+
+        /* systemd-resolved uses (and requires) the socket to be in nonblocking mode */
+        assert_se(fcntl(clientfd, F_SETFL, O_NONBLOCK) >= 0);
+
+        /* Initialize DNS stream */
+        assert_se(dns_stream_new(&manager, &stream, DNS_STREAM_LOOKUP, DNS_PROTOCOL_DNS,
+                                 TAKE_FD(clientfd), NULL, DNS_STREAM_DEFAULT_TIMEOUT_USEC) >= 0);
+        stream->on_packet = on_stream_packet;
+#if ENABLE_DNS_OVER_TLS
+        if (tls) {
+                DnsServer server = {
+                        .manager = &manager,
+                        .family = SERVER_ADDRESS.sin_family,
+                        .address.in = SERVER_ADDRESS.sin_addr
+                };
+
+                assert_se(dnstls_manager_init(&manager) >= 0);
+                assert_se(dnstls_stream_connect_tls(stream, &server) >= 0);
+        }
+#endif
+
+        /* Test: Question of type A and associated answer */
+        log_info("test-resolved-stream: A record");
+        send_simple_question(stream, DNS_TYPE_A);
+        while (n_received_packets != 1)
+                assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1);
+        assert_se(DNS_PACKET_DATA(received_packets[0]));
+        assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]),
+                         ANSWER_A + 2, sizeof(ANSWER_A) - 2) == 0);
+        dns_packet_unref(TAKE_PTR(received_packets[0]));
+        n_received_packets = 0;
+
+        /* Test: Question of type AAAA and associated answer */
+        log_info("test-resolved-stream: AAAA record");
+        send_simple_question(stream, DNS_TYPE_AAAA);
+        while (n_received_packets != 1)
+                assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1);
+        assert_se(DNS_PACKET_DATA(received_packets[0]));
+        assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]),
+                         ANSWER_AAAA + 2, sizeof(ANSWER_AAAA) - 2) == 0);
+        dns_packet_unref(TAKE_PTR(received_packets[0]));
+        n_received_packets = 0;
+
+        /* Test: Question of type A and AAAA and associated answers
+         * Both answers are sent back in a single packet or TLS record
+         * (tests the fix of PR #22132: "Fix DoT timeout on multiple answer records") */
+        log_info("test-resolved-stream: A + AAAA record");
+        send_simple_question(stream, DNS_TYPE_A);
+        send_simple_question(stream, DNS_TYPE_AAAA);
+
+        while (n_received_packets != 2)
+                assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1);
+        assert_se(DNS_PACKET_DATA(received_packets[0]));
+        assert_se(DNS_PACKET_DATA(received_packets[1]));
+        assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]),
+                         ANSWER_A + 2, sizeof(ANSWER_A) - 2) == 0);
+        assert_se(memcmp(DNS_PACKET_DATA(received_packets[1]),
+                         ANSWER_AAAA + 2, sizeof(ANSWER_AAAA) - 2) == 0);
+        dns_packet_unref(TAKE_PTR(received_packets[0]));
+        dns_packet_unref(TAKE_PTR(received_packets[1]));
+        n_received_packets = 0;
+
+#if ENABLE_DNS_OVER_TLS
+        if (tls)
+                dnstls_manager_free(&manager);
+#endif
+
+        /* Stop the DNS server */
+        assert_se(pthread_mutex_unlock(&server_lock) == 0);
+        assert_se(pthread_join(server_thread, NULL) == 0);
+        assert_se(pthread_mutex_destroy(&server_lock) == 0);
+
+        log_info("test-resolved-stream: Finished %s test", tls ? "TLS" : "TCP");
+}
+
+int main(int argc, char **argv) {
+        SERVER_ADDRESS = (struct sockaddr_in) {
+                .sin_family = AF_INET,
+                .sin_port = htobe16(12345),
+                .sin_addr.s_addr = htobe32(INADDR_LOOPBACK)
+        };
+
+        test_setup_logging(LOG_DEBUG);
+
+        test_dns_stream(false);
+#if ENABLE_DNS_OVER_TLS
+        if (system("openssl version >/dev/null 2>&1") != 0)
+                return log_tests_skipped("Skipping TLS test since the 'openssl' command does not seem to be available");
+        test_dns_stream(true);
+#endif
+
+        return 0;
+}
index ff24373847c1f5053f10b6b3c2f77dc11787b06e..e75b027542f18e3fc0df73df0c8881f17c10c369 100644 (file)
@@ -794,9 +794,12 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
 
                 e = getenv("TERM");
                 if (e) {
-                        char *n;
+                        _cleanup_free_ char *n = NULL;
+
+                        n = strjoin("TERM=", e);
+                        if (!n)
+                                return log_oom();
 
-                        n = strjoina("TERM=", e);
                         r = sd_bus_message_append(m,
                                                   "(sv)",
                                                   "Environment", "as", 1, n);
index 0076092c2ab4b1115f9c61531e0d5fbeeb445beb..52268f60415bb73910ce424aa348939b3fcdbd89 100644 (file)
@@ -759,8 +759,7 @@ int boot_entries_load_config_auto(
 
 int boot_entries_augment_from_loader(
                 BootConfig *config,
-                char **found_by_loader,
-                bool only_auto) {
+                char **found_by_loader) {
 
         static const char *const title_table[] = {
                 /* Pretty names for a few well-known automatically discovered entries. */
@@ -785,7 +784,12 @@ int boot_entries_augment_from_loader(
                 if (boot_config_has_entry(config, *i))
                         continue;
 
-                if (only_auto && !startswith(*i, "auto-"))
+                /*
+                 * consider the 'auto-' entries only, because the others
+                 * ones are detected scanning the 'esp' and 'xbootldr'
+                 * directories by boot_entries_load_config()
+                 */
+                if (!startswith(*i, "auto-"))
                         continue;
 
                 c = strdup(*i);
index 81845f47e37c749c650564a02e113bd738bbc13f..4a95e24e27966853f889f2a915702d4f859aae47 100644 (file)
@@ -76,7 +76,7 @@ static inline BootEntry* boot_config_default_entry(BootConfig *config) {
 void boot_config_free(BootConfig *config);
 int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config);
 int boot_entries_load_config_auto(const char *override_esp_path, const char *override_xbootldr_path, BootConfig *config);
-int boot_entries_augment_from_loader(BootConfig *config, char **list, bool only_auto);
+int boot_entries_augment_from_loader(BootConfig *config, char **list);
 
 static inline const char* boot_entry_title(const BootEntry *entry) {
         return entry->show_title ?: entry->title ?: entry->id;
index b8ca32a1f01d830cc53c32362a70fe3770a5b4b4..31fa4448b03e1bf6b76adce41c29be119ea92a3a 100644 (file)
@@ -55,6 +55,7 @@ BPFProgram *bpf_program_free(BPFProgram *p) {
         (void) bpf_program_cgroup_detach(p);
 
         safe_close(p->kernel_fd);
+        free(p->prog_name);
         free(p->instructions);
         free(p->attached_path);
 
@@ -78,8 +79,18 @@ static int bpf_program_get_info_by_fd(int prog_fd, struct bpf_prog_info *info, u
         return RET_NERRNO(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)));
 }
 
-int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
+int bpf_program_new(uint32_t prog_type, const char *prog_name, BPFProgram **ret) {
         _cleanup_(bpf_program_freep) BPFProgram *p = NULL;
+        _cleanup_free_ char *name = NULL;
+
+        if (prog_name) {
+                if (strlen(prog_name) >= BPF_OBJ_NAME_LEN)
+                        return -ENAMETOOLONG;
+
+                name = strdup(prog_name);
+                if (!name)
+                        return -ENOMEM;
+        }
 
         p = new(BPFProgram, 1);
         if (!p)
@@ -88,6 +99,7 @@ int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
         *p = (BPFProgram) {
                 .prog_type = prog_type,
                 .kernel_fd = -1,
+                .prog_name = TAKE_PTR(name),
         };
 
         *ret = TAKE_PTR(p);
@@ -165,6 +177,8 @@ int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size) {
         attr.log_buf = PTR_TO_UINT64(log_buf);
         attr.log_level = !!log_buf;
         attr.log_size = log_size;
+        if (p->prog_name)
+                strncpy(attr.prog_name, p->prog_name, BPF_OBJ_NAME_LEN - 1);
 
         p->kernel_fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
         if (p->kernel_fd < 0)
index e54900fa2fe8bf3228254cf4479b42e2c4f8a937..b640fb9d9f655fa5c9f3c30aa179a3461604f773 100644 (file)
@@ -20,6 +20,7 @@ struct BPFProgram {
         /* The loaded BPF program, if loaded */
         int kernel_fd;
         uint32_t prog_type;
+        char *prog_name;
 
         /* The code of it BPF program, if known */
         size_t n_instructions;
@@ -32,7 +33,7 @@ struct BPFProgram {
         uint32_t attached_flags;
 };
 
-int bpf_program_new(uint32_t prog_type, BPFProgram **ret);
+int bpf_program_new(uint32_t prog_type, const char *prog_name, BPFProgram **ret);
 int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret);
 BPFProgram *bpf_program_free(BPFProgram *p);
 
index f2e53913fbfcaf96547b187562570330c44cd48f..4ed5215e3d1ad1c3ffbe826e294ddb1444ed8814 100644 (file)
@@ -156,10 +156,10 @@ int bus_introspect_implementations(
         if (impl != main_impl)
                 bus_introspect_implementation(&intro, impl);
 
-        _cleanup_set_free_ Set *nodes = NULL;
+        _cleanup_ordered_set_free_ OrderedSet *nodes = NULL;
 
         for (size_t i = 0; impl->children && impl->children[i]; i++) {
-                r = set_put_strdup(&nodes, impl->children[i]->path);
+                r = ordered_set_put_strdup(&nodes, impl->children[i]->path);
                 if (r < 0)
                         return log_oom();
         }
index dcce530c999a5a0d1bd4b2d19b5cfafdb0866e1c..c35dd286e60b6e9b589fe963c200c270f46c6ec9 100644 (file)
@@ -973,6 +973,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                               "ExecPaths",
                               "NoExecPaths",
                               "ExecSearchPath",
+                              "ExtensionDirectories",
                               "ConfigurationDirectory",
                               "SupplementaryGroups",
                               "SystemCallArchitectures"))
index 40bc2bff0515114a94934a3f17463c7328e83afd..48dd4d800137f0c4b9e8f5685e1f11a0ea64c2fd 100644 (file)
@@ -89,7 +89,6 @@ static int show_cgroup_one_by_path(
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char *p = NULL;
         size_t n = 0;
-        pid_t pid;
         char *fn;
         int r;
 
@@ -102,7 +101,18 @@ static int show_cgroup_one_by_path(
         if (!f)
                 return -errno;
 
-        while ((r = cg_read_pid(f, &pid)) > 0) {
+        for (;;) {
+                pid_t pid;
+
+                /* libvirt / qemu uses threaded mode and cgroup.procs cannot be read at the lower levels.
+                 * From https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#threads,
+                 * “cgroup.procs” in a threaded domain cgroup contains the PIDs of all processes in
+                 * the subtree and is not readable in the subtree proper. */
+                r = cg_read_pid(f, &pid);
+                if (IN_SET(r, 0, -EOPNOTSUPP))
+                        break;
+                if (r < 0)
+                        return r;
 
                 if (!(flags & OUTPUT_KERNEL_THREADS) && is_kernel_thread(pid) > 0)
                         continue;
@@ -113,9 +123,6 @@ static int show_cgroup_one_by_path(
                 pids[n++] = pid;
         }
 
-        if (r < 0)
-                return r;
-
         show_pid_array(pids, n, prefix, n_columns, false, more, flags);
 
         return 0;
index 1e1967d7ea232dd0ef270d60618af728992d1adb..64e67fb6f5754a7f58c91d7fdc1a95e8d4da003f 100644 (file)
@@ -573,6 +573,50 @@ int config_parse_many(
         return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
 }
 
+static void config_section_hash_func(const ConfigSection *c, struct siphash *state) {
+        siphash24_compress_string(c->filename, state);
+        siphash24_compress(&c->line, sizeof(c->line), state);
+}
+
+static int config_section_compare_func(const ConfigSection *x, const ConfigSection *y) {
+        int r;
+
+        r = strcmp(x->filename, y->filename);
+        if (r != 0)
+                return r;
+
+        return CMP(x->line, y->line);
+}
+
+DEFINE_HASH_OPS(config_section_hash_ops, ConfigSection, config_section_hash_func, config_section_compare_func);
+
+int config_section_new(const char *filename, unsigned line, ConfigSection **s) {
+        ConfigSection *cs;
+
+        cs = malloc0(offsetof(ConfigSection, filename) + strlen(filename) + 1);
+        if (!cs)
+                return -ENOMEM;
+
+        strcpy(cs->filename, filename);
+        cs->line = line;
+
+        *s = TAKE_PTR(cs);
+
+        return 0;
+}
+
+unsigned hashmap_find_free_section_line(Hashmap *hashmap) {
+        ConfigSection *cs;
+        unsigned n = 0;
+        void *entry;
+
+        HASHMAP_FOREACH_KEY(entry, cs, hashmap)
+                if (n < cs->line)
+                        n = cs->line;
+
+        return n + 1;
+}
+
 #define DEFINE_PARSER(type, vartype, conv_func)                         \
         DEFINE_CONFIG_PARSE_PTR(config_parse_##type, conv_func, vartype, "Failed to parse " #type " value")
 
index d6866655327612bf66acc71caf1796b9a3f955d3..1e03f93bce14aacfd77b028c778e3682afb75337 100644 (file)
@@ -114,6 +114,43 @@ int config_parse_many(
                 void *userdata,
                 Hashmap **ret_stats_by_path);   /* possibly NULL */
 
+typedef struct ConfigSection {
+        unsigned line;
+        bool invalid;
+        char filename[];
+} ConfigSection;
+
+static inline ConfigSection* config_section_free(ConfigSection *cs) {
+        return mfree(cs);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free);
+
+int config_section_new(const char *filename, unsigned line, ConfigSection **s);
+extern const struct hash_ops config_section_hash_ops;
+unsigned hashmap_find_free_section_line(Hashmap *hashmap);
+
+static inline bool section_is_invalid(ConfigSection *section) {
+        /* If this returns false, then it does _not_ mean the section is valid. */
+
+        if (!section)
+                return false;
+
+        return section->invalid;
+}
+
+#define DEFINE_SECTION_CLEANUP_FUNCTIONS(type, free_func)               \
+        static inline type* free_func##_or_set_invalid(type *p) {       \
+                assert(p);                                              \
+                                                                        \
+                if (p->section)                                         \
+                        p->section->invalid = true;                     \
+                else                                                    \
+                        free_func(p);                                   \
+                return NULL;                                            \
+        }                                                               \
+        DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func);                  \
+        DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid);
+
 CONFIG_PARSER_PROTOTYPE(config_parse_int);
 CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
 CONFIG_PARSER_PROTOTYPE(config_parse_long);
index 1ace40424e5ef3153054d10517008d3af4cdf65f..09417063562131c4245c91fd052a316d93defc45 100644 (file)
@@ -107,6 +107,47 @@ static int look_for_signals(CopyFlags copy_flags) {
         return 0;
 }
 
+static int create_hole(int fd, off_t size) {
+        off_t offset;
+        off_t end;
+
+        offset = lseek(fd, 0, SEEK_CUR);
+        if (offset < 0)
+                return -errno;
+
+        end = lseek(fd, 0, SEEK_END);
+        if (end < 0)
+                return -errno;
+
+        /* If we're not at the end of the target file, try to punch a hole in the existing space using fallocate(). */
+
+        if (offset < end &&
+            fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, MIN(size, end - offset)) < 0 &&
+            !ERRNO_IS_NOT_SUPPORTED(errno))
+                return -errno;
+
+        if (end - offset >= size) {
+                /* If we've created the full hole, set the file pointer to the end of the hole we created and exit. */
+                if (lseek(fd, offset + size, SEEK_SET) < 0)
+                        return -errno;
+
+                return 0;
+        }
+
+        /* If we haven't created the full hole, use ftruncate() to grow the file (and the hole) to the
+         * required size and move the file pointer to the end of the file. */
+
+        size -= end - offset;
+
+        if (ftruncate(fd, end + size) < 0)
+                return -errno;
+
+        if (lseek(fd, 0, SEEK_END) < 0)
+                return -errno;
+
+        return 0;
+}
+
 int copy_bytes_full(
                 int fdf, int fdt,
                 uint64_t max_bytes,
@@ -202,6 +243,49 @@ int copy_bytes_full(
                 if (max_bytes != UINT64_MAX && m > max_bytes)
                         m = max_bytes;
 
+                if (copy_flags & COPY_HOLES) {
+                        off_t c, e;
+
+                        c = lseek(fdf, 0, SEEK_CUR);
+                        if (c < 0)
+                                return -errno;
+
+                        /* To see if we're in a hole, we search for the next data offset. */
+                        e = lseek(fdf, c, SEEK_DATA);
+                        if (e < 0 && errno == ENXIO)
+                                /* If errno == ENXIO, that means we've reached the final hole of the file and
+                                * that hole isn't followed by more data. */
+                                e = lseek(fdf, 0, SEEK_END);
+                        if (e < 0)
+                                return -errno;
+
+                        /* If we're in a hole (current offset is not a data offset), create a hole of the
+                         * same size in the target file. */
+                        if (e > c) {
+                                r = create_hole(fdt, e - c);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        c = e; /* Set c to the start of the data segment. */
+
+                        /* After copying a potential hole, find the end of the data segment by looking for
+                         * the next hole. If we get ENXIO, we're at EOF. */
+                        e = lseek(fdf, c, SEEK_HOLE);
+                        if (e < 0) {
+                                if (errno == ENXIO)
+                                        break;
+                                return -errno;
+                        }
+
+                        /* SEEK_HOLE modifies the file offset so we need to move back to the initial offset. */
+                        if (lseek(fdf, c, SEEK_SET) < 0)
+                                return -errno;
+
+                        /* Make sure we're not copying more than the current data segment. */
+                        m = MIN(m, (size_t) e - c);
+                }
+
                 /* First try copy_file_range(), unless we already tried */
                 if (try_cfr) {
                         n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u);
index a7b45b4fbf4369135670f125fe7daa7a554945d2..d755916bd98dea3a89f419010e8721165bdbc1bb 100644 (file)
@@ -24,6 +24,7 @@ typedef enum CopyFlags {
         COPY_FSYNC_FULL  = 1 << 11, /* fsync_full() after we are done */
         COPY_SYNCFS      = 1 << 12, /* syncfs() the *top-level* dir after we are done */
         COPY_ALL_XATTRS  = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
+        COPY_HOLES       = 1 << 14, /* Copy holes */
 } CopyFlags;
 
 typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
index 39a7f4c3f2863339e543615d0e319ff160a0e15a..14519ead703aeb962ed94cc656044eeb3fcccf00 100644 (file)
@@ -3534,9 +3534,9 @@ int verity_dissect_and_mount(
         /* If we got os-release values from the caller, then we need to match them with the image's
          * extension-release.d/ content. Return -EINVAL if there's any mismatch.
          * First, check the distro ID. If that matches, then check the new SYSEXT_LEVEL value if
-         * available, or else fallback to VERSION_ID. */
-        if (required_host_os_release_id &&
-            (required_host_os_release_version_id || required_host_os_release_sysext_level)) {
+         * available, or else fallback to VERSION_ID. If neither is present (eg: rolling release),
+         * then a simple match on the ID will be performed. */
+        if (required_host_os_release_id) {
                 _cleanup_strv_free_ char **extension_release = NULL;
 
                 r = load_extension_release_pairs(dest, dissected_image->image_name, &extension_release);
index c25fcaacc2a74cb6ac077f3935d1742fb39fa002..24bf00bd58b79c076ad8694cd6ede2ed0f3c4eec 100644 (file)
@@ -60,6 +60,10 @@ static inline int dns_name_is_valid_ldh(const char *s) {
         return 1;
 }
 
+static inline bool dns_name_is_empty(const char *s) {
+        return isempty(s) || streq(s, ".");
+}
+
 void dns_name_hash_func(const char *s, struct siphash *state);
 int dns_name_compare_func(const char *a, const char *b);
 extern const struct hash_ops dns_name_hash_ops;
index 403d6013c1fb4faebcc7885fa7ea149c973a3bc1..14553d8c5541830c24a96455242f215929840394 100644 (file)
 #include "user-util.h"
 
 static int specifier_prefix_and_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const UnitFileInstallInfo *i = userdata;
+        const UnitFileInstallInfo *i = ASSERT_PTR(userdata);
         _cleanup_free_ char *prefix = NULL;
         int r;
 
-        assert(i);
-
         r = unit_name_to_prefix_and_instance(i->name, &prefix);
         if (r < 0)
                 return r;
@@ -38,11 +36,9 @@ static int specifier_prefix_and_instance(char specifier, const void *data, const
 }
 
 static int specifier_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const UnitFileInstallInfo *i = userdata;
+        const UnitFileInstallInfo *i = ASSERT_PTR(userdata);
         char *ans;
 
-        assert(i);
-
         if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE) && i->default_instance)
                 return unit_name_replace_instance(i->name, i->default_instance, ret);
 
@@ -54,20 +50,16 @@ static int specifier_name(char specifier, const void *data, const char *root, co
 }
 
 static int specifier_prefix(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const UnitFileInstallInfo *i = userdata;
-
-        assert(i);
+        const UnitFileInstallInfo *i = ASSERT_PTR(userdata);
 
         return unit_name_to_prefix(i->name, ret);
 }
 
 static int specifier_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        const UnitFileInstallInfo *i = userdata;
+        const UnitFileInstallInfo *i = ASSERT_PTR(userdata);
         char *instance;
         int r;
 
-        assert(i);
-
         r = unit_name_to_instance(i->name, &instance);
         if (r < 0)
                 return r;
@@ -87,6 +79,8 @@ static int specifier_last_component(char specifier, const void *data, const char
         char *dash;
         int r;
 
+        assert(ret);
+
         r = specifier_prefix(specifier, data, root, userdata, &prefix);
         if (r < 0)
                 return r;
index 05cdffeec00da3073368e5e03e688cdae303239a..09103bace96e2a492f596a6e1f3095310b174876 100644 (file)
@@ -15,6 +15,7 @@
 #define _DEFINE_MAIN_FUNCTION(intro, impl, ret)                         \
         int main(int argc, char *argv[]) {                              \
                 int r;                                                  \
+                assert(argc > 0 && !isempty(argv[0]));                  \
                 save_argc_argv(argc, argv);                             \
                 intro;                                                  \
                 r = impl;                                               \
index f58d623f4a11184bd11088a4dcb083ee0157f12a..66715e00c3cc3df714ef923b08f5531f6e62531d 100644 (file)
@@ -224,6 +224,8 @@ shared_sources = files('''
         net-condition.h
         netif-naming-scheme.c
         netif-naming-scheme.h
+        netif-sriov.c
+        netif-sriov.h
         netif-util.c
         netif-util.h
         nscd-flush.h
diff --git a/src/shared/netif-sriov.c b/src/shared/netif-sriov.c
new file mode 100644 (file)
index 0000000..720aa65
--- /dev/null
@@ -0,0 +1,631 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "device-util.h"
+#include "netlink-util.h"
+#include "netif-sriov.h"
+#include "parse-util.h"
+#include "set.h"
+#include "stdio-util.h"
+#include "string-util.h"
+
+static int sr_iov_new(SRIOV **ret) {
+        SRIOV *sr_iov;
+
+        assert(ret);
+
+        sr_iov = new(SRIOV, 1);
+        if (!sr_iov)
+                return -ENOMEM;
+
+        *sr_iov = (SRIOV) {
+                  .vf = UINT32_MAX,
+                  .vlan_proto = ETH_P_8021Q,
+                  .vf_spoof_check_setting = -1,
+                  .trust = -1,
+                  .query_rss = -1,
+                  .link_state = _SR_IOV_LINK_STATE_INVALID,
+        };
+
+        *ret = TAKE_PTR(sr_iov);
+
+        return 0;
+}
+
+static int sr_iov_new_static(OrderedHashmap **sr_iov_by_section, const char *filename, unsigned section_line, SRIOV **ret) {
+        _cleanup_(config_section_freep) ConfigSection *n = NULL;
+        _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL;
+        SRIOV *existing = NULL;
+        int r;
+
+        assert(sr_iov_by_section);
+        assert(filename);
+        assert(section_line > 0);
+        assert(ret);
+
+        r = config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        existing = ordered_hashmap_get(*sr_iov_by_section, n);
+        if (existing) {
+                *ret = existing;
+                return 0;
+        }
+
+        r = sr_iov_new(&sr_iov);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_ensure_put(sr_iov_by_section, &config_section_hash_ops, n, sr_iov);
+        if (r < 0)
+                return r;
+
+        sr_iov->section = TAKE_PTR(n);
+        sr_iov->sr_iov_by_section = *sr_iov_by_section;
+
+        *ret = TAKE_PTR(sr_iov);
+        return 0;
+}
+
+SRIOV *sr_iov_free(SRIOV *sr_iov) {
+        if (!sr_iov)
+                return NULL;
+
+        if (sr_iov->sr_iov_by_section && sr_iov->section)
+                ordered_hashmap_remove(sr_iov->sr_iov_by_section, sr_iov->section);
+
+        config_section_free(sr_iov->section);
+
+        return mfree(sr_iov);
+}
+
+int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req) {
+        int r;
+
+        assert(sr_iov);
+        assert(req);
+
+        r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_open_container(req, IFLA_VF_INFO);
+        if (r < 0)
+                return r;
+
+        if (!ether_addr_is_null(&sr_iov->mac)) {
+                struct ifla_vf_mac ivm = {
+                        .vf = sr_iov->vf,
+                };
+
+                memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN);
+                r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac));
+                if (r < 0)
+                        return r;
+        }
+
+        if (sr_iov->vf_spoof_check_setting >= 0) {
+                struct ifla_vf_spoofchk ivs = {
+                        .vf = sr_iov->vf,
+                        .setting = sr_iov->vf_spoof_check_setting,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk));
+                if (r < 0)
+                        return r;
+        }
+
+        if (sr_iov->query_rss >= 0) {
+                struct ifla_vf_rss_query_en ivs = {
+                        .vf = sr_iov->vf,
+                        .setting = sr_iov->query_rss,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en));
+                if (r < 0)
+                        return r;
+        }
+
+        if (sr_iov->trust >= 0) {
+                struct ifla_vf_trust ivt = {
+                        .vf = sr_iov->vf,
+                        .setting = sr_iov->trust,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust));
+                if (r < 0)
+                        return r;
+        }
+
+        if (sr_iov->link_state >= 0) {
+                struct ifla_vf_link_state ivl = {
+                        .vf = sr_iov->vf,
+                        .link_state = sr_iov->link_state,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state));
+                if (r < 0)
+                        return r;
+        }
+
+        if (sr_iov->vlan > 0) {
+                /* Because of padding, first the buffer must be initialized with 0. */
+                struct ifla_vf_vlan_info ivvi = {};
+                ivvi.vf = sr_iov->vf;
+                ivvi.vlan = sr_iov->vlan;
+                ivvi.qos = sr_iov->qos;
+                ivvi.vlan_proto = htobe16(sr_iov->vlan_proto);
+
+                r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST);
+                if (r < 0)
+                        return r;
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info));
+                if (r < 0)
+                        return r;
+
+                r = sd_netlink_message_close_container(req);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int sr_iov_get_num_vfs(sd_device *device, uint32_t *ret) {
+        const char *str;
+        uint32_t n;
+        int r;
+
+        assert(device);
+        assert(ret);
+
+        r = sd_device_get_sysattr_value(device, "device/sriov_numvfs", &str);
+        if (r < 0)
+                return r;
+
+        r = safe_atou32(str, &n);
+        if (r < 0)
+                return r;
+
+        *ret = n;
+        return 0;
+}
+
+int sr_iov_set_num_vfs(sd_device *device, uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) {
+        char val[DECIMAL_STR_MAX(uint32_t)];
+        const char *str;
+        int r;
+
+        assert(device);
+
+        if (num_vfs == UINT32_MAX) {
+                uint32_t current_num_vfs;
+                SRIOV *sr_iov;
+
+                /* If the number of virtual functions is not specified, then use the maximum number of VF + 1. */
+
+                num_vfs = 0;
+                ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section)
+                        num_vfs = MAX(num_vfs, sr_iov->vf + 1);
+
+                if (num_vfs == 0) /* No VF is configured. */
+                        return 0;
+
+                r = sr_iov_get_num_vfs(device, &current_num_vfs);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "Failed to get the current number of SR-IOV virtual functions: %m");
+
+                /* Enough VFs already exist. */
+                if (num_vfs <= current_num_vfs)
+                        return 0;
+
+        } else if (num_vfs == 0) {
+                r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0");
+                if (r < 0)
+                        log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute, ignoring: %m");
+
+                /* Gracefully handle the error in disabling VFs when the interface does not support SR-IOV. */
+                return r == -ENOENT ? 0 : r;
+        }
+
+        /* So, the interface does not have enough VFs. Before increasing the number of VFs, check the
+         * maximum allowed number of VFs from the sriov_totalvfs sysattr. Note that the sysattr
+         * currently exists only for PCI drivers. Hence, ignore -ENOENT.
+         * TODO: netdevsim provides the information in debugfs. */
+        r = sd_device_get_sysattr_value(device, "device/sriov_totalvfs", &str);
+        if (r >= 0) {
+                uint32_t max_num_vfs;
+
+                r = safe_atou32(str, &max_num_vfs);
+                if (r < 0)
+                        return log_device_debug_errno(device, r, "Failed to parse device/sriov_totalvfs sysfs attribute '%s': %m", str);
+
+                if (num_vfs > max_num_vfs)
+                        return log_device_debug_errno(device, SYNTHETIC_ERRNO(ERANGE),
+                                                      "Specified number of virtual functions is out of range. "
+                                                      "The maximum allowed value is %"PRIu32".",
+                                                      max_num_vfs);
+
+        } else if (r != -ENOENT) /* Currently, only PCI driver has the attribute. */
+                return log_device_debug_errno(device, r, "Failed to read device/sriov_totalvfs sysfs attribute: %m");
+
+        xsprintf(val, "%"PRIu32, num_vfs);
+        r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val);
+        if (r == -EBUSY) {
+                /* Some devices e.g. netdevsim refuse to set sriov_numvfs if it has non-zero value. */
+                r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0");
+                if (r >= 0)
+                        r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val);
+        }
+        if (r < 0)
+                return log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute: %m");
+
+        log_device_debug(device, "device/sriov_numvfs sysfs attribute set to '%s'.", val);
+        return 0;
+}
+
+static int sr_iov_section_verify(uint32_t num_vfs, SRIOV *sr_iov) {
+        assert(sr_iov);
+
+        if (section_is_invalid(sr_iov->section))
+                return -EINVAL;
+
+        if (sr_iov->vf == UINT32_MAX)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: [SR-IOV] section without VirtualFunction= field configured. "
+                                         "Ignoring [SR-IOV] section from line %u.",
+                                         sr_iov->section->filename, sr_iov->section->line);
+
+        if (sr_iov->vf >= num_vfs)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: VirtualFunction= must be smaller than the value specified in SR-IOVVirtualFunctions=. "
+                                         "Ignoring [SR-IOV] section from line %u.",
+                                         sr_iov->section->filename, sr_iov->section->line);
+
+        return 0;
+}
+
+int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) {
+        _cleanup_hashmap_free_ Hashmap *hashmap = NULL;
+        SRIOV *sr_iov;
+        int r;
+
+        ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section) {
+                SRIOV *dup;
+
+                if (sr_iov_section_verify(num_vfs, sr_iov) < 0) {
+                        sr_iov_free(sr_iov);
+                        continue;
+                }
+
+                assert(sr_iov->vf < INT_MAX);
+
+                dup = hashmap_remove(hashmap, UINT32_TO_PTR(sr_iov->vf + 1));
+                if (dup) {
+                        log_warning("%s: Conflicting [SR-IOV] section is specified at line %u and %u, "
+                                    "dropping the [SR-IOV] section specified at line %u.",
+                                    dup->section->filename, sr_iov->section->line,
+                                    dup->section->line, dup->section->line);
+                        sr_iov_free(dup);
+                }
+
+                r = hashmap_ensure_put(&hashmap, NULL, UINT32_TO_PTR(sr_iov->vf + 1), sr_iov);
+                if (r < 0)
+                        return log_oom();
+                assert(r > 0);
+        }
+
+        return 0;
+}
+
+int config_parse_sr_iov_uint32(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        OrderedHashmap **sr_iov_by_section = data;
+        uint32_t k;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "VirtualFunction"))
+                        sr_iov->vf = UINT32_MAX;
+                else if (streq(lvalue, "VLANId"))
+                        sr_iov->vlan = 0;
+                else if (streq(lvalue, "QualityOfService"))
+                        sr_iov->qos = 0;
+                else
+                        assert_not_reached();
+
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "VLANId")) {
+                if (k == 0 || k > 4095) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k);
+                        return 0;
+                }
+                sr_iov->vlan = k;
+        } else if (streq(lvalue, "VirtualFunction")) {
+                if (k >= INT_MAX) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k);
+                        return 0;
+                }
+                sr_iov->vf = k;
+        } else if (streq(lvalue, "QualityOfService"))
+                sr_iov->qos = k;
+        else
+                assert_not_reached();
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_vlan_proto(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        OrderedHashmap **sr_iov_by_section = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue) || streq(rvalue, "802.1Q"))
+                sr_iov->vlan_proto = ETH_P_8021Q;
+        else if (streq(rvalue, "802.1ad"))
+                sr_iov->vlan_proto = ETH_P_8021AD;
+        else {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_link_state(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        OrderedHashmap **sr_iov_by_section = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
+         * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
+
+        if (isempty(rvalue)) {
+                sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        if (streq(rvalue, "auto")) {
+                sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE;
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_boolean(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        OrderedHashmap **sr_iov_by_section = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "MACSpoofCheck"))
+                        sr_iov->vf_spoof_check_setting = -1;
+                else if (streq(lvalue, "QueryReceiveSideScaling"))
+                        sr_iov->query_rss = -1;
+                else if (streq(lvalue, "Trust"))
+                        sr_iov->trust = -1;
+                else
+                        assert_not_reached();
+
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "MACSpoofCheck"))
+                sr_iov->vf_spoof_check_setting = r;
+        else if (streq(lvalue, "QueryReceiveSideScaling"))
+                sr_iov->query_rss = r;
+        else if (streq(lvalue, "Trust"))
+                sr_iov->trust = r;
+        else
+                assert_not_reached();
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_mac(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        OrderedHashmap **sr_iov_by_section = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                sr_iov->mac = ETHER_ADDR_NULL;
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = parse_ether_addr(rvalue, &sr_iov->mac);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_num_vfs(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        uint32_t n, *num_vfs = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *num_vfs = UINT32_MAX;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &n);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (n > INT_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "The number of SR-IOV virtual functions is too large. It must be equal to "
+                           "or smaller than 2147483647. Ignoring assignment: %"PRIu32, n);
+                return 0;
+        }
+
+        *num_vfs = n;
+        return 0;
+}
diff --git a/src/shared/netif-sriov.h b/src/shared/netif-sriov.h
new file mode 100644 (file)
index 0000000..4c85f10
--- /dev/null
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <linux/if_link.h>
+
+#include "sd-device.h"
+
+#include "conf-parser.h"
+#include "ether-addr-util.h"
+#include "hashmap.h"
+
+typedef enum SRIOVLinkState {
+        SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO,
+        SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE,
+        SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE,
+        _SR_IOV_LINK_STATE_MAX,
+        _SR_IOV_LINK_STATE_INVALID = -EINVAL,
+} SRIOVLinkState;
+
+typedef struct SRIOV {
+        ConfigSection *section;
+        OrderedHashmap *sr_iov_by_section;
+
+        uint32_t vf;   /* 0 - 2147483646 */
+        uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */
+        uint32_t qos;
+        uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */
+        int vf_spoof_check_setting;
+        int query_rss;
+        int trust;
+        SRIOVLinkState link_state;
+        struct ether_addr mac;
+} SRIOV;
+
+SRIOV *sr_iov_free(SRIOV *sr_iov);
+int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req);
+int sr_iov_get_num_vfs(sd_device *device, uint32_t *ret);
+int sr_iov_set_num_vfs(sd_device *device, uint32_t num_vfs, OrderedHashmap *sr_iov_by_section);
+int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_section);
+
+DEFINE_SECTION_CLEANUP_FUNCTIONS(SRIOV, sr_iov_free);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_num_vfs);
index 603d4de109ff7fb8d558f8b81205cd93d9dd6468..69d7cc2b10dd38bc5fee9382900c3c6f22004573 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <linux/if.h>
 #include <linux/if_arp.h>
 
 #include "arphrd-util.h"
 #include "sparse-endian.h"
 #include "strv.h"
 
+bool netif_has_carrier(uint8_t operstate, unsigned flags) {
+        /* see Documentation/networking/operstates.txt in the kernel sources */
+
+        if (operstate == IF_OPER_UP)
+                return true;
+
+        if (operstate != IF_OPER_UNKNOWN)
+                return false;
+
+        /* operstate may not be implemented, so fall back to flags */
+        return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) &&
+                !FLAGS_SET(flags, IFF_DORMANT);
+}
+
 int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) {
         const char *t;
         char *p;
index c26dab63cf3018cc64068d7896f8c596728ad209..fb6a27cce151783f0244dc276a67294fbd56ae34 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "ether-addr-util.h"
 
+bool netif_has_carrier(uint8_t operstate, unsigned flags);
 int net_get_type_string(sd_device *device, uint16_t iftype, char **ret);
 const char *net_get_persistent_name(sd_device *device);
 int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret);
index f75ef62d2d0520fcebf959f4f3b994b48be7eb17..9426d3ef988ef7fb6f2c1c33c66787e997fa3a94 100644 (file)
@@ -86,6 +86,7 @@ static int no_quit_on_interrupt(int exe_name_fd, const char *less_opts) {
 void pager_open(PagerFlags flags) {
         _cleanup_close_pair_ int fd[2] = { -1, -1 }, exe_name_pipe[2] = { -1, -1 };
         _cleanup_strv_free_ char **pager_args = NULL;
+        _cleanup_free_ char *l = NULL;
         const char *pager, *less_opts;
         int r;
 
@@ -131,8 +132,12 @@ void pager_open(PagerFlags flags) {
         less_opts = getenv("SYSTEMD_LESS");
         if (!less_opts)
                 less_opts = "FRSXMK";
-        if (flags & PAGER_JUMP_TO_END)
-                less_opts = strjoina(less_opts, " +G");
+        if (flags & PAGER_JUMP_TO_END) {
+                l = strjoin(less_opts, " +G");
+                if (!l)
+                        return (void) log_oom();
+                less_opts = l;
+        }
 
         /* We set SIGINT as PR_DEATHSIG signal here, to match the "K" parameter we set in $LESS, which enables SIGINT behaviour. */
         r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGINT|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pager_pid);
index a1359a5bfd3fab09a3aaaf5b0a375477d61d7027..67ea8581422a1a4e37d9b946d4411656a4ff6fd6 100644 (file)
@@ -346,7 +346,7 @@ int mac_selinux_apply_fd(int fd, const char *path, const char *label) {
 
         assert(label);
 
-        if (fsetfilecon(fd, label) < 0)
+        if (setfilecon(FORMAT_PROC_FD_PATH(fd), label) < 0)
                 return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, strna(path));
 #endif
         return 0;
index b8434b068ca9af79187571e8e0ecbcc80422d846..0df1778cb2dcb8c4b4c71a220631852663f9008d 100644 (file)
@@ -95,9 +95,9 @@ int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) {
                 return 0;
 
         if (label)
-                r = fsetxattr(fd, smack_attr_to_string(attr), label, strlen(label), 0);
+                r = setxattr(FORMAT_PROC_FD_PATH(fd), smack_attr_to_string(attr), label, strlen(label), 0);
         else
-                r = fremovexattr(fd, smack_attr_to_string(attr));
+                r = removexattr(FORMAT_PROC_FD_PATH(fd), smack_attr_to_string(attr));
         if (r < 0)
                 return -errno;
 
index 1fd76b1d15f18295c7b5d1a169ae6dce113d73c5..aef5b9c94d3ee7e0c27313e3a79093640dcac0c4 100644 (file)
@@ -18,6 +18,7 @@
 #include "id128-util.h"
 #include "macro.h"
 #include "os-util.h"
+#include "path-util.h"
 #include "specifier.h"
 #include "string-util.h"
 #include "strv.h"
@@ -35,7 +36,6 @@
 int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret) {
         _cleanup_free_ char *result = NULL;
         bool percent = false;
-        const char *f;
         size_t l;
         char *t;
         int r;
@@ -48,8 +48,10 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[
                 return -ENOMEM;
         t = result;
 
-        for (f = text; *f != '\0'; f++, l--) {
+        for (const char *f = text; *f != '\0'; f++, l--) {
                 if (percent) {
+                        percent = false;
+
                         if (*f == '%')
                                 *(t++) = '%';
                         else {
@@ -66,6 +68,8 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[
                                         r = i->lookup(i->specifier, i->data, root, userdata, &w);
                                         if (r < 0)
                                                 return r;
+                                        if (isempty(w))
+                                                continue;
 
                                         j = t - result;
                                         k = strlen(w);
@@ -82,8 +86,6 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[
                                         *(t++) = *f;
                                 }
                         }
-
-                        percent = false;
                 } else if (*f == '%')
                         percent = true;
                 else
@@ -108,16 +110,39 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[
 /* Generic handler for simple string replacements */
 
 int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        char *n;
+        char *n = NULL;
 
-        n = strdup(strempty(data));
-        if (!n)
-                return -ENOMEM;
+        if (!isempty(data)) {
+                n = strdup(data);
+                if (!n)
+                        return -ENOMEM;
+        }
 
         *ret = n;
         return 0;
 }
 
+int specifier_real_path(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        const char *path = data;
+
+        if (!path)
+                return -ENOENT;
+
+        return chase_symlinks(path, root, 0, ret, NULL);
+}
+
+int specifier_real_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        _cleanup_free_ char *path = NULL;
+        int r;
+
+        r = specifier_real_path(specifier, data, root, userdata, &path);
+        if (r < 0)
+                return r;
+
+        assert(path);
+        return path_extract_directory(path, ret);
+}
+
 int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         sd_id128_t id;
         char *n;
@@ -186,10 +211,8 @@ int specifier_short_host_name(char specifier, const void *data, const char *root
 int specifier_kernel_release(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         struct utsname uts;
         char *n;
-        int r;
 
-        r = uname(&uts);
-        if (r < 0)
+        if (uname(&uts) < 0)
                 return -errno;
 
         n = strdup(uts.release);
@@ -211,47 +234,31 @@ int specifier_architecture(char specifier, const void *data, const char *root, c
         return 0;
 }
 
-static int specifier_os_release_common(const char *field, const char *root, char **ret) {
-        char *t = NULL;
-        int r;
-
-        r = parse_os_release(root, field, &t);
-        if (r < 0)
-                return r;
-        if (!t) {
-                /* fields in /etc/os-release might quite possibly be missing, even if everything is entirely
-                 * valid otherwise. Let's hence return "" in that case. */
-                t = strdup("");
-                if (!t)
-                        return -ENOMEM;
-        }
-
-        *ret = t;
-        return 0;
-}
+/* Note: fields in /etc/os-release might quite possibly be missing, even if everything is entirely valid
+ * otherwise. We'll return an empty value or NULL in that case from the functions below. */
 
 int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        return specifier_os_release_common("ID", root, ret);
+        return parse_os_release(root, "ID", ret);
 }
 
 int specifier_os_version_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        return specifier_os_release_common("VERSION_ID", root, ret);
+        return parse_os_release(root, "VERSION_ID", ret);
 }
 
 int specifier_os_build_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        return specifier_os_release_common("BUILD_ID", root, ret);
+        return parse_os_release(root, "BUILD_ID", ret);
 }
 
 int specifier_os_variant_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        return specifier_os_release_common("VARIANT_ID", root, ret);
+        return parse_os_release(root, "VARIANT_ID", ret);
 }
 
 int specifier_os_image_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        return specifier_os_release_common("IMAGE_ID", root, ret);
+        return parse_os_release(root, "IMAGE_ID", ret);
 }
 
 int specifier_os_image_version(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-        return specifier_os_release_common("IMAGE_VERSION", root, ret);
+        return parse_os_release(root, "IMAGE_VERSION", ret);
 }
 
 int specifier_group_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
@@ -291,7 +298,6 @@ int specifier_user_name(char specifier, const void *data, const char *root, cons
 }
 
 int specifier_user_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
-
         if (asprintf(ret, UID_FMT, getuid()) < 0)
                 return -ENOMEM;
 
index c433ee2d63eb240a08213d4223d332c4b19c6380..eae5f12ad7660ae4f6f72f1c9bb26fe8d2dac707 100644 (file)
@@ -14,6 +14,8 @@ typedef struct Specifier {
 int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret);
 
 int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_real_path(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_real_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret);
 
 int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
 int specifier_boot_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
index 56c28773ceda270bc5f82409536619fea8385263..32dfcdafc47bcfbe4833040fa4c59cc96aa26149 100644 (file)
@@ -13,6 +13,7 @@
 #include "errno-util.h"
 #include "escape.h"
 #include "fd-util.h"
+#include "id128-util.h"
 #include "log.h"
 #include "macro.h"
 #include "parse-util.h"
@@ -337,6 +338,7 @@ bool device_for_action(sd_device *dev, sd_device_action_t a) {
 
 void log_device_uevent(sd_device *device, const char *str) {
         sd_device_action_t action = _SD_DEVICE_ACTION_INVALID;
+        sd_id128_t event_id = SD_ID128_NULL;
         uint64_t seqnum = 0;
 
         if (!DEBUG_LOGGING)
@@ -344,9 +346,12 @@ void log_device_uevent(sd_device *device, const char *str) {
 
         (void) sd_device_get_seqnum(device, &seqnum);
         (void) sd_device_get_action(device, &action);
-        log_device_debug(device, "%s%s(SEQNUM=%"PRIu64", ACTION=%s)",
+        (void) sd_device_get_trigger_uuid(device, &event_id);
+        log_device_debug(device, "%s%s(SEQNUM=%"PRIu64", ACTION=%s%s%s)",
                          strempty(str), isempty(str) ? "" : " ",
-                         seqnum, strna(device_action_to_string(action)));
+                         seqnum, strna(device_action_to_string(action)),
+                         sd_id128_is_null(event_id) ? "" : ", UUID=",
+                         sd_id128_is_null(event_id) ? "" : id128_to_uuid_string(event_id, (char[ID128_UUID_STRING_MAX]){}));
 }
 
 int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
index b45f7912cbedf90442d818e3e0bb5b15da4a18cb..33701e775eadcdb26794f6ac3bf67383aa16684b 100644 (file)
@@ -26,9 +26,8 @@ static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static bool arg_user = false;
 
 static int help(void) {
-
         printf("%s [OPTIONS...]\n\n"
-               "STDIO or socket-activatable proxy to a given DBus endpoint.\n\n"
+               "Forward messages between two D-Bus busses via a pipe or socket.\n\n"
                "  -h --help              Show this help\n"
                "     --version           Show package version\n"
                "  -p --bus-path=PATH     Path to the bus address (default: %s)\n"
@@ -41,7 +40,6 @@ static int help(void) {
 }
 
 static int parse_argv(int argc, char *argv[]) {
-
         enum {
                 ARG_VERSION = 0x100,
                 ARG_MACHINE,
@@ -64,7 +62,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "hp:M:", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "hp:M:", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -98,7 +96,6 @@ static int parse_argv(int argc, char *argv[]) {
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "Unknown option code %c", c);
                 }
-        }
 
         return 1;
 }
@@ -125,7 +122,7 @@ static int run(int argc, char *argv[]) {
                 in_fd = SD_LISTEN_FDS_START;
                 out_fd = SD_LISTEN_FDS_START;
         } else
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Illegal number of file descriptors passed.");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "More than one file descriptor was passed.");
 
         is_unix =
                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
index 5abf1bb418389223c82ab5e05730cae5e8cb1511..60789e0f2c19d725061be923477ea71efa4a6709 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <fcntl.h>
 #include <getopt.h>
+#include <linux/loop.h>
 #include <sys/mount.h>
 #include <unistd.h>
 
@@ -529,7 +530,11 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
                         if (verity_settings.data_path)
                                 flags |= DISSECT_IMAGE_NO_PARTITION_TABLE;
 
-                        r = loop_device_make_by_path(img->path, O_RDONLY, 0, &d);
+                        r = loop_device_make_by_path(
+                                        img->path,
+                                        O_RDONLY,
+                                        FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
+                                        &d);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to set up loopback device for %s: %m", img->path);
 
index 2e19db600e1bfdc79c1f3b4bb4fa37e9347aef6b..cbcb525f52de37ecfa723083ddc20e6770defa20 100644 (file)
@@ -55,7 +55,7 @@ int main(int argc, char *argv[]) {
         assert_se(set_unit_path(unit_dir) >= 0);
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
-        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p);
+        r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, "sd_trivial", &p);
         assert_se(r == 0);
 
         r = bpf_program_add_instructions(p, exit_insn, ELEMENTSOF(exit_insn));
index 8c3f76e9ec8bfa31e4fae8bc6746ecbdb8bda760..d73f487ff65dca1225331061715f379b11a62a80 100644 (file)
@@ -162,7 +162,7 @@ static int pin_programs(Unit *u, CGroupContext *cc, const Test *test_suite, size
                 if (r < 0)
                         return log_error_errno(r, "Failed to convert program to string");
 
-                r = bpf_program_new(test_suite[i].prog_type, &prog);
+                r = bpf_program_new(test_suite[i].prog_type, "sd_trivial", &prog);
                 if (r < 0)
                         return log_error_errno(r, "Failed to create program '%s'", str);
 
index c7ed054207efc0efe043e7e08cd79dd9685fa343..2c17c9d83daf4537cd008016a0515675db8bf640 100644 (file)
@@ -323,4 +323,59 @@ TEST(copy_proc) {
         assert_se(!isempty(a));
 }
 
+TEST_RET(copy_holes) {
+        char fn[] = "/var/tmp/test-copy-hole-fd-XXXXXX";
+        char fn_copy[] = "/var/tmp/test-copy-hole-fd-XXXXXX";
+        struct stat stat;
+        off_t blksz;
+        int r, fd, fd_copy;
+        char *buf;
+
+        fd = mkostemp_safe(fn);
+        assert_se(fd >= 0);
+
+        fd_copy = mkostemp_safe(fn_copy);
+        assert_se(fd_copy >= 0);
+
+        r = RET_NERRNO(fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 1));
+        if (ERRNO_IS_NOT_SUPPORTED(r))
+                return log_tests_skipped("Filesystem doesn't support hole punching");
+        assert_se(r >= 0);
+
+        assert_se(fstat(fd, &stat) >= 0);
+        blksz = stat.st_blksize;
+        buf = alloca0(blksz);
+
+        /* We need to make sure to create hole in multiples of the block size, otherwise filesystems (btrfs)
+         * might silently truncate/extend the holes. */
+
+        assert_se(lseek(fd, blksz, SEEK_CUR) >= 0);
+        assert_se(write(fd, buf, blksz) >= 0);
+        assert_se(lseek(fd, 0, SEEK_END) == 2 * blksz);
+        /* Only ftruncate() can create holes at the end of a file. */
+        assert_se(ftruncate(fd, 3 * blksz) >= 0);
+        assert_se(lseek(fd, 0, SEEK_SET) >= 0);
+
+        assert_se(copy_bytes(fd, fd_copy, UINT64_MAX, COPY_HOLES) >= 0);
+
+        /* Test that the hole starts at the beginning of the file. */
+        assert_se(lseek(fd_copy, 0, SEEK_HOLE) == 0);
+        /* Test that the hole has the expected size. */
+        assert_se(lseek(fd_copy, 0, SEEK_DATA) == blksz);
+        assert_se(lseek(fd_copy, blksz, SEEK_HOLE) == 2 * blksz);
+        assert_se(lseek(fd_copy, 2 * blksz, SEEK_DATA) < 0 && errno == ENXIO);
+
+        /* Test that the copied file has the correct size. */
+        assert_se(fstat(fd_copy, &stat) >= 0);
+        assert_se(stat.st_size == 3 * blksz);
+
+        close(fd);
+        close(fd_copy);
+
+        unlink(fn);
+        unlink(fn_copy);
+
+        return 0;
+}
+
 DEFINE_TEST_MAIN(LOG_DEBUG);
index 6cc2455c1f2d147aaae8adc4c66c9bab0c902990..7b132447bf36b4cad6c8a69b0f1e24509cd65ef9 100644 (file)
         "a=a\n"                                 \
         "b=b\\\n"                               \
         "c\n"                                   \
-        "d=d\\\n"                               \
-        "e\\\n"                                 \
-        "f\n"                                   \
+        "d= d\\\n"                              \
+        "e  \\\n"                               \
+        "f  \n"                                 \
         "g=g\\ \n"                              \
-        "h=h\n"                                 \
+        "h= ąęół\\ śćńźżµ \n"                   \
         "i=i\\"
 
 #define env_file_2                              \
 #define env_file_3 \
         "#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
         "#--nouser-config                                     \\\n" \
-        "normal=line"
-
-#define env_file_4 \
-       "# Generated\n" \
-       "\n" \
-       "HWMON_MODULES=\"coretemp f71882fg\"\n" \
-       "\n" \
-       "# For compatibility reasons\n" \
-       "\n" \
-       "MODULE_0=coretemp\n" \
-       "MODULE_1=f71882fg"
+        "normal=line                                          \\\n" \
+        ";normal=ignored                                      \\\n" \
+        "normal_ignored                                       \\\n" \
+        "normal ignored                                       \\\n"
+
+#define env_file_4                              \
+        "# Generated\n"                         \
+        "\n"                                    \
+        "HWMON_MODULES=\"coretemp f71882fg\"\n" \
+        "\n"                                    \
+        "# For compatibility reasons\n"         \
+        "\n"                                    \
+        "MODULE_0=coretemp\n"                   \
+        "MODULE_1=f71882fg"
 
 #define env_file_5                              \
-        "a=\n"                                 \
+        "a=\n"                                  \
         "b="
 
+#define env_file_6                              \
+        "a=\\ \\n \\t \\x \\y \\' \n"           \
+        "b= \\$'                  \n"           \
+        "c= ' \\n\\t\\$\\`\\\\\n"               \
+        "'   \n"                                \
+        "d= \" \\n\\t\\$\\`\\\\\n"              \
+        "\"   \n"
+
+
 TEST(load_env_file_1) {
         _cleanup_strv_free_ char **data = NULL;
         int r;
@@ -57,9 +69,9 @@ TEST(load_env_file_1) {
         assert_se(r == 0);
         assert_se(streq(data[0], "a=a"));
         assert_se(streq(data[1], "b=bc"));
-        assert_se(streq(data[2], "d=def"));
+        assert_se(streq(data[2], "d=de  f"));
         assert_se(streq(data[3], "g=g "));
-        assert_se(streq(data[4], "h=h"));
+        assert_se(streq(data[4], "h=ąęół śćńźżµ"));
         assert_se(streq(data[5], "i=i"));
         assert_se(data[6] == NULL);
 }
@@ -133,6 +145,26 @@ TEST(load_env_file_5) {
         assert_se(data[2] == NULL);
 }
 
+TEST(load_env_file_6) {
+        _cleanup_strv_free_ char **data = NULL;
+        int r;
+
+        _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX";
+        _cleanup_close_ int fd;
+
+        fd = mkostemp_safe(name);
+        assert_se(fd >= 0);
+        assert_se(write(fd, env_file_6, strlen(env_file_6)) == strlen(env_file_6));
+
+        r = load_env_file(NULL, name, &data);
+        assert_se(r == 0);
+        assert_se(streq(data[0], "a= n t x y '"));
+        assert_se(streq(data[1], "b=$'"));
+        assert_se(streq(data[2], "c= \\n\\t\\$\\`\\\\\n"));
+        assert_se(streq(data[3], "d= \\n\\t$`\\\n"));
+        assert_se(data[4] == NULL);
+}
+
 TEST(write_and_load_env_file) {
         const char *v;
 
index e878979a8952c4e1076275f1e43bf0b1eacd2f7a..9e5ae855976e4986ddb321fa612284ee69a135e3 100644 (file)
@@ -769,6 +769,70 @@ TEST(config_parse_pass_environ) {
         assert_se(streq(passenv[0], "normal_name"));
 }
 
+TEST(config_parse_unit_env_file) {
+        /* int config_parse_unit_env_file(
+                 const char *unit,
+                 const char *filename,
+                 unsigned line,
+                 const char *section,
+                 unsigned section_line,
+                 const char *lvalue,
+                 int ltype,
+                 const char *rvalue,
+                 void *data,
+                 void *userdata) */
+
+        _cleanup_(manager_freep) Manager *m = NULL;
+        Unit *u;
+        _cleanup_strv_free_ char **files = NULL;
+        int r;
+
+        r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
+        if (manager_errno_skip_test(r)) {
+                log_notice_errno(r, "Skipping test: manager_new: %m");
+                return;
+        }
+
+        assert_se(r >= 0);
+        assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+
+        assert_se(u = unit_new(m, sizeof(Service)));
+        assert_se(unit_add_name(u, "foobar.service") == 0);
+
+        r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+                                      "EnvironmentFile", 0, "not-absolute",
+                                       &files, u);
+        assert_se(r == 0);
+        assert_se(strv_isempty(files));
+
+        r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+                                      "EnvironmentFile", 0, "/absolute1",
+                                       &files, u);
+        assert_se(r == 0);
+        assert_se(strv_length(files) == 1);
+
+        r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+                                      "EnvironmentFile", 0, "/absolute2",
+                                       &files, u);
+        assert_se(r == 0);
+        assert_se(strv_length(files) == 2);
+        assert_se(streq(files[0], "/absolute1"));
+        assert_se(streq(files[1], "/absolute2"));
+
+        r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+                                       "EnvironmentFile", 0, "",
+                                       &files, u);
+        assert_se(r == 0);
+        assert_se(strv_isempty(files));
+
+        r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
+                                       "EnvironmentFile", 0, "/path/%n.conf",
+                                       &files, u);
+        assert_se(r == 0);
+        assert_se(strv_length(files) == 1);
+        assert_se(streq(files[0], "/path/foobar.service.conf"));
+}
+
 TEST(unit_dump_config_items) {
         unit_dump_config_items(stdout);
 }
index 8df5533d6e3684e8a17b465c6f22723f8f7a74cb..09c3091641fe13a45c4d23b9a919ef2e3a920da8 100644 (file)
@@ -207,6 +207,7 @@ TEST(protect_kernel_logs) {
                                     NULL,
                                     NULL,
                                     NULL,
+                                    NULL,
                                     NULL);
                 assert_se(r == 0);
 
index b03eabb59bbc60adb9beafd26e39ca0c1ec8e485..cd455a3a5b79a3de0dd298029e97bdd5c239198b 100644 (file)
@@ -108,6 +108,7 @@ int main(int argc, char *argv[]) {
                             NULL,
                             NULL,
                             NULL,
+                            NULL,
                             NULL);
         if (r < 0) {
                 log_error_errno(r, "Failed to set up namespace: %m");
index 40957eeb5916fd5f7fcb79303d5d32b069f04f83..ded2dcd55a0c27756c010598cd4ff1f0e3c05667 100644 (file)
@@ -3,6 +3,7 @@
 #include "alloc-util.h"
 #include "log.h"
 #include "specifier.h"
+#include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -56,6 +57,7 @@ TEST(specifier_printf) {
         static const Specifier table[] = {
                 { 'X', specifier_string,         (char*) "AAAA" },
                 { 'Y', specifier_string,         (char*) "BBBB" },
+                { 'e', specifier_string,         NULL           },
                 COMMON_SYSTEM_SPECIFIERS,
                 {}
         };
@@ -63,25 +65,66 @@ TEST(specifier_printf) {
         _cleanup_free_ char *w = NULL;
         int r;
 
-        r = specifier_printf("xxx a=%X b=%Y yyy", SIZE_MAX, table, NULL, NULL, &w);
+        r = specifier_printf("xxx a=%X b=%Y e=%e yyy", SIZE_MAX, table, NULL, NULL, &w);
         assert_se(r >= 0);
         assert_se(w);
 
         puts(w);
-        assert_se(streq(w, "xxx a=AAAA b=BBBB yyy"));
+        assert_se(streq(w, "xxx a=AAAA b=BBBB e= yyy"));
 
         free(w);
-        r = specifier_printf("machine=%m, boot=%b, host=%H, version=%v, arch=%a", SIZE_MAX, table, NULL, NULL, &w);
+        r = specifier_printf("machine=%m, boot=%b, host=%H, version=%v, arch=%a, empty=%e", SIZE_MAX, table, NULL, NULL, &w);
         assert_se(r >= 0);
         assert_se(w);
         puts(w);
 
         w = mfree(w);
-        specifier_printf("os=%o, os-version=%w, build=%B, variant=%W", SIZE_MAX, table, NULL, NULL, &w);
+        specifier_printf("os=%o, os-version=%w, build=%B, variant=%W, empty=%e%e%e", SIZE_MAX, table, NULL, NULL, &w);
         if (w)
                 puts(w);
 }
 
+TEST(specifier_real_path) {
+        static const Specifier table[] = {
+                { 'p', specifier_string,         "/dev/initctl" },
+                { 'y', specifier_real_path,      "/dev/initctl" },
+                { 'Y', specifier_real_directory, "/dev/initctl" },
+                { 'w', specifier_real_path,      "/dev/tty" },
+                { 'W', specifier_real_directory, "/dev/tty" },
+                {}
+        };
+
+        _cleanup_free_ char *w = NULL;
+        int r;
+
+        r = specifier_printf("p=%p y=%y Y=%Y w=%w W=%W", SIZE_MAX, table, NULL, NULL, &w);
+        assert_se(r >= 0 || r == -ENOENT);
+        assert_se(w || r == -ENOENT);
+        puts(strnull(w));
+
+        /* /dev/initctl should normally be a symlink to /run/initctl */
+        if (files_same("/dev/initctl", "/run/initctl", 0) > 0)
+                assert_se(streq(w, "p=/dev/initctl y=/run/initctl Y=/run w=/dev/tty W=/dev"));
+}
+
+TEST(specifier_real_path_missing_file) {
+        static const Specifier table[] = {
+                { 'p', specifier_string,         "/dev/-no-such-file--" },
+                { 'y', specifier_real_path,      "/dev/-no-such-file--" },
+                { 'Y', specifier_real_directory, "/dev/-no-such-file--" },
+                {}
+        };
+
+        _cleanup_free_ char *w = NULL;
+        int r;
+
+        r = specifier_printf("p=%p y=%y", SIZE_MAX, table, NULL, NULL, &w);
+        assert_se(r == -ENOENT);
+
+        r = specifier_printf("p=%p Y=%Y", SIZE_MAX, table, NULL, NULL, &w);
+        assert_se(r == -ENOENT);
+}
+
 TEST(specifiers) {
         for (const Specifier *s = specifier_table; s->specifier; s++) {
                 char spec[3];
index 9fd0bcdd0efa76d76f70360709698f6b060bcb50..7d1e6d06bf523cf31c154a6cb785616cc6a58049 100644 (file)
@@ -152,7 +152,7 @@ foreach prog : udev_progs
                 install : true,
                 install_dir : udevlibexecdir)
 
-        udev_prog_paths += {name : exe.full_path()}
+        udev_prog_paths += {name : exe}
 endforeach
 
 if install_sysconfdir_samples
index ac7825b00f27978fd3d3c5715a5eda40ce12be4e..7dde4ed59f5287b8a4ffc34e8b6aaf3b8c8d58fb 100644 (file)
@@ -8,6 +8,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "ethtool-util.h"
 #include "link-config.h"
 #include "net-condition.h"
+#include "netif-sriov.h"
 #include "socket-util.h"
 %}
 struct ConfigPerfItem;
@@ -101,3 +102,13 @@ Link.RxMaxCoalescedHighFrames,             config_parse_coalesce_u32,
 Link.TxCoalesceHighSec,                    config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.tx_coalesce_usecs_high)
 Link.TxMaxCoalescedHighFrames,             config_parse_coalesce_u32,             0,                             offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_high)
 Link.CoalescePacketRateSampleIntervalSec,  config_parse_coalesce_sec,             0,                             offsetof(LinkConfig, coalesce.rate_sample_interval)
+Link.SR-IOVVirtualFunctions,               config_parse_sr_iov_num_vfs,           0,                             offsetof(LinkConfig, sr_iov_num_vfs)
+SR-IOV.VirtualFunction,                    config_parse_sr_iov_uint32,            0,                             offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.VLANId,                             config_parse_sr_iov_uint32,            0,                             offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.QualityOfService,                   config_parse_sr_iov_uint32,            0,                             offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.VLANProtocol,                       config_parse_sr_iov_vlan_proto,        0,                             offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.MACSpoofCheck,                      config_parse_sr_iov_boolean,           0,                             offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.QueryReceiveSideScaling,            config_parse_sr_iov_boolean,           0,                             offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.Trust,                              config_parse_sr_iov_boolean,           0,                             offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.LinkState,                          config_parse_sr_iov_link_state,        0,                             offsetof(LinkConfig, sr_iov_by_section)
+SR-IOV.MACAddress,                         config_parse_sr_iov_mac,               0,                             offsetof(LinkConfig, sr_iov_by_section)
index 05f0f2e0a61b029a0436300a8ff641b8d72df091..7e2da6088c4196d4cb7a0d57f564ea13f20f4d77 100644 (file)
@@ -22,6 +22,7 @@
 #include "log-link.h"
 #include "memory-util.h"
 #include "net-condition.h"
+#include "netif-sriov.h"
 #include "netif-util.h"
 #include "netlink-util.h"
 #include "parse-util.h"
@@ -60,6 +61,8 @@ static LinkConfig* link_config_free(LinkConfig *config) {
         free(config->wol_password_file);
         erase_and_free(config->wol_password);
 
+        ordered_hashmap_free_with_destructor(config->sr_iov_by_section, sr_iov_free);
+
         return mfree(config);
 }
 
@@ -247,6 +250,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
                 .txqueuelen = UINT32_MAX,
                 .coalesce.use_adaptive_rx_coalesce = -1,
                 .coalesce.use_adaptive_tx_coalesce = -1,
+                .sr_iov_num_vfs = UINT32_MAX,
         };
 
         for (i = 0; i < ELEMENTSOF(config->features); i++)
@@ -257,7 +261,9 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
                         STRV_MAKE_CONST(filename),
                         (const char* const*) CONF_PATHS_STRV("systemd/network"),
                         dropin_dirname,
-                        "Match\0Link\0",
+                        "Match\0"
+                        "Link\0"
+                        "SR-IOV\0",
                         config_item_perf_lookup, link_config_gperf_lookup,
                         CONFIG_PARSE_WARN, config, NULL);
         if (r < 0)
@@ -285,6 +291,10 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
         if (r < 0)
                 return r;
 
+        r = sr_iov_drop_invalid_sections(config->sr_iov_num_vfs, config->sr_iov_by_section);
+        if (r < 0)
+                return r;
+
         log_debug("Parsed configuration file %s", filename);
 
         LIST_PREPEND(configs, ctx->configs, TAKE_PTR(config));
@@ -830,6 +840,77 @@ static int link_apply_alternative_names(Link *link, sd_netlink **rtnl) {
         return 0;
 }
 
+static int sr_iov_configure(Link *link, sd_netlink **rtnl, SRIOV *sr_iov) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(rtnl);
+        assert(link->ifindex > 0);
+
+        if (!*rtnl) {
+                r = sd_netlink_open(rtnl);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_rtnl_message_new_link(*rtnl, &req, RTM_SETLINK, link->ifindex);
+        if (r < 0)
+                return r;
+
+        r = sr_iov_set_netlink_message(sr_iov, req);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call(*rtnl, req, 0, NULL);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) {
+        SRIOV *sr_iov;
+        uint32_t n;
+        int r;
+
+        assert(link);
+        assert(link->config);
+        assert(link->device);
+
+        r = sr_iov_set_num_vfs(link->device, link->config->sr_iov_num_vfs, link->config->sr_iov_by_section);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Failed to set the number of SR-IOV virtual functions, ignoring: %m");
+
+        if (ordered_hashmap_isempty(link->config->sr_iov_by_section))
+                return 0;
+
+        r = sr_iov_get_num_vfs(link->device, &n);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "Failed to get the number of SR-IOV virtual functions, ignoring [SR-IOV] sections: %m");
+                return 0;
+        }
+        if (n == 0) {
+                log_link_warning(link, "No SR-IOV virtual function exists, ignoring [SR-IOV] sections: %m");
+                return 0;
+        }
+
+        ORDERED_HASHMAP_FOREACH(sr_iov, link->config->sr_iov_by_section) {
+                if (sr_iov->vf >= n) {
+                        log_link_warning(link, "SR-IOV virtual function %"PRIu32" does not exist, ignoring.", sr_iov->vf);
+                        continue;
+                }
+
+                r = sr_iov_configure(link, rtnl, sr_iov);
+                if (r < 0)
+                        log_link_warning_errno(link, r,
+                                               "Failed to configure SR-IOV virtual function %"PRIu32", ignoring: %m",
+                                               sr_iov->vf);
+        }
+
+        return 0;
+}
+
 int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) {
         int r;
 
@@ -861,6 +942,10 @@ int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) {
         if (r < 0)
                 return r;
 
+        r = link_apply_sr_iov_config(link, rtnl);
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
index 3b2cd696201ffb80a3e5c3aa22e0dab93066b584..0d1d117f2e1a2ab5b590115de21278285d54d6cb 100644 (file)
@@ -7,6 +7,7 @@
 #include "condition.h"
 #include "conf-parser.h"
 #include "ethtool-util.h"
+#include "hashmap.h"
 #include "list.h"
 #include "net-condition.h"
 #include "netif-naming-scheme.h"
@@ -76,6 +77,9 @@ struct LinkConfig {
         int autoneg_flow_control;
         netdev_coalesce_param coalesce;
 
+        uint32_t sr_iov_num_vfs;
+        OrderedHashmap *sr_iov_by_section;
+
         LIST_FIELDS(LinkConfig, configs);
 };
 
index 740434bb419bfe8e3be146f12406ad76e9842a7e..5ae33e101ae17d27089028d38edc6f6618a3c30c 100644 (file)
@@ -47,6 +47,8 @@ static const char *arg_export_prefix = NULL;
 static usec_t arg_wait_for_initialization_timeout = 0;
 
 static bool skip_attribute(const char *name) {
+        assert(name);
+
         /* Those are either displayed separately or should not be shown at all. */
         return STR_IN_SET(name,
                           "uevent",
@@ -66,6 +68,9 @@ typedef struct SysAttr {
 STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
 
 static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
+        assert(a);
+        assert(b);
+
         return strcmp(a->name, b->name);
 }
 
@@ -75,6 +80,8 @@ static int print_all_attributes(sd_device *device, bool is_parent) {
         size_t n_items = 0;
         int r;
 
+        assert(device);
+
         value = NULL;
         (void) sd_device_get_devpath(device, &value);
         printf("  looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
@@ -139,6 +146,8 @@ static int print_device_chain(sd_device *device) {
         sd_device *child, *parent;
         int r;
 
+        assert(device);
+
         printf("\n"
                "Udevadm info starts with the device specified by the devpath and then\n"
                "walks up the chain of parent devices. It prints for every device\n"
@@ -164,6 +173,8 @@ static int print_record(sd_device *device) {
         const char *str, *val;
         int i;
 
+        assert(device);
+
         (void) sd_device_get_devpath(device, &str);
         printf("P: %s\n", str);
 
@@ -190,6 +201,8 @@ static int print_record(sd_device *device) {
 static int stat_device(const char *name, bool export, const char *prefix) {
         struct stat statbuf;
 
+        assert(name);
+
         if (stat(name, &statbuf) != 0)
                 return -errno;
 
@@ -229,15 +242,17 @@ static int export_devices(void) {
 }
 
 static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
+        assert(dir);
+
         if (depth <= 0)
                 return;
 
         FOREACH_DIRENT_ALL(dent, dir, break) {
                 struct stat stats;
 
-                if (dent->d_name[0] == '.')
+                if (dot_or_dot_dot(dent->d_name))
                         continue;
-                if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
+                if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
                         continue;
                 if ((stats.st_mode & mask) != 0)
                         continue;
@@ -254,8 +269,55 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) {
         }
 }
 
+/*
+ * Assume that dir is a directory with file names matching udev data base
+ * entries for devices in /run/udev/data (such as "b8:16"), and removes
+ * all files except those that haven't been deleted in /run/udev/data
+ * (i.e. they were skipped during db cleanup because of the db_persist flag).
+ */
+static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) {
+        assert(dir);
+        assert(datadir);
+
+        FOREACH_DIRENT_ALL(dent, dir, break) {
+                if (dot_or_dot_dot(dent->d_name))
+                        continue;
+
+                if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
+                        /* The corresponding udev database file still exists.
+                         * Assuming the parsistent flag is set for the database. */
+                        continue;
+
+                (void) unlinkat(dirfd(dir), dent->d_name, 0);
+        }
+}
+
+static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) {
+        assert(dir);
+        assert(datadir);
+
+        FOREACH_DIRENT_ALL(dent, dir, break) {
+                struct stat stats;
+
+                if (dot_or_dot_dot(dent->d_name))
+                        continue;
+                if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
+                        continue;
+                if (S_ISDIR(stats.st_mode)) {
+                        _cleanup_closedir_ DIR *dir2 = NULL;
+
+                        dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+                        if (dir2)
+                                cleanup_dir_after_db_cleanup(dir2, datadir);
+
+                        (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
+                } else
+                        (void) unlinkat(dirfd(dir), dent->d_name, 0);
+        }
+}
+
 static void cleanup_db(void) {
-        _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL;
+        _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL;
 
         dir1 = opendir("/run/udev/data");
         if (dir1)
@@ -263,19 +325,18 @@ static void cleanup_db(void) {
 
         dir2 = opendir("/run/udev/links");
         if (dir2)
-                cleanup_dir(dir2, 0, 2);
+                cleanup_dirs_after_db_cleanup(dir2, dir1);
 
         dir3 = opendir("/run/udev/tags");
         if (dir3)
-                cleanup_dir(dir3, 0, 2);
+                cleanup_dirs_after_db_cleanup(dir3, dir1);
 
         dir4 = opendir("/run/udev/static_node-tags");
         if (dir4)
                 cleanup_dir(dir4, 0, 2);
 
-        dir5 = opendir("/run/udev/watch");
-        if (dir5)
-                cleanup_dir(dir5, 0, 1);
+        /* Do not remove /run/udev/watch. It will be handled by udevd well on restart.
+         * And should not be removed by external program when udevd is running. */
 }
 
 static int query_device(QueryType query, sd_device* device) {
@@ -341,10 +402,10 @@ static int query_device(QueryType query, sd_device* device) {
 
         case QUERY_ALL:
                 return print_record(device);
-        }
 
-        assert_not_reached();
-        return 0;
+        default:
+                assert_not_reached();
+        }
 }
 
 static int help(void) {
index 0f26eaafebaa8be196bbddef4595885009fcaf04..c360c8b6610f6dd9844ce9eb65857469d6deee1d 100755 (executable)
@@ -119,7 +119,6 @@ test_run() {
 
     # Execute each currently defined function starting with "testcase_"
     for testcase in "${TESTCASES[@]}"; do
-        _image_cleanup
         echo "------ $testcase: BEGIN ------"
         # Note for my future frustrated self: `fun && xxx` (as well as ||, if, while,
         # until, etc.) _DISABLES_ the `set -e` behavior in _ALL_ nested function
@@ -130,8 +129,14 @@ test_run() {
         # So, be careful when adding clean up snippets in the testcase_*() functions -
         # if the `test_run_one()` function isn't the last command, you have propagate
         # the exit code correctly (e.g. `test_run_one() || return $?`, see below).
-        ec=0
-        "$testcase" "$test_id" || ec=$?
+
+        # FIXME: temporary workaround for intermittent fails in certain tests
+        # See: https://github.com/systemd/systemd/issues/21819
+        for ((_i = 0; _i < 3; _i++)); do
+            _image_cleanup
+            ec=0
+            "$testcase" "$test_id" && break || ec=$?
+        done
         case $ec in
             0)
                 passed+=("$testcase")
@@ -166,6 +171,7 @@ testcase_megasas2_basic() {
         return 77
     fi
 
+    local i
     local qemu_opts=(
         "-device megasas-gen2,id=scsi0"
         "-device megasas-gen2,id=scsi1"
@@ -192,6 +198,9 @@ testcase_nvme_basic() {
         return 77
     fi
 
+    local i
+    local qemu_opts=()
+
     for i in {0..27}; do
         qemu_opts+=(
             "-device nvme,drive=nvme$i,serial=deadbeef$i,num_queues=8"
@@ -215,7 +224,7 @@ testcase_virtio_scsi_identically_named_partitions() {
     # and attach them to a virtio-scsi controller
     local qemu_opts=("-device virtio-scsi-pci,id=scsi0,num_queues=4")
     local diskpath="${TESTDIR:?}/namedpart0.img"
-    local lodev qemu_timeout
+    local lodev qemu_timeout
 
     dd if=/dev/zero of="$diskpath" bs=1M count=18
     lodev="$(losetup --show -f -P "$diskpath")"
@@ -325,7 +334,7 @@ testcase_lvm_basic() {
     fi
 
     local qemu_opts=("-device ahci,id=ahci0")
-    local diskpath
+    local diskpath i
 
     # Attach 4 SATA disks to the VM (and set their model and serial fields
     # to something predictable, so we can refer to them later)
diff --git a/test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50a b/test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50a
new file mode 100644 (file)
index 0000000..87345bf
Binary files /dev/null and b/test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50a differ
diff --git a/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 b/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980
new file mode 100644 (file)
index 0000000..a51cf70
Binary files /dev/null and b/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 differ
diff --git a/test/fuzz/fuzz-dhcp-server-relay-message/7d924e16295cd14e12a01a5631ea94a3d11d1b52 b/test/fuzz/fuzz-dhcp-server-relay-message/7d924e16295cd14e12a01a5631ea94a3d11d1b52
new file mode 100644 (file)
index 0000000..117fbe0
Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server-relay-message/7d924e16295cd14e12a01a5631ea94a3d11d1b52 differ
diff --git a/test/fuzz/fuzz-dhcp-server-relay-message/clusterfuzz-testcase-minimized-fuzz-dhcp-server-relay-message-4972399731277824 b/test/fuzz/fuzz-dhcp-server-relay-message/clusterfuzz-testcase-minimized-fuzz-dhcp-server-relay-message-4972399731277824
new file mode 100644 (file)
index 0000000..e902b69
Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server-relay-message/clusterfuzz-testcase-minimized-fuzz-dhcp-server-relay-message-4972399731277824 differ
diff --git a/test/fuzz/fuzz-dhcp-server-relay-message/fe4344e65d495388540dc1bf8eae70c46f8b867c b/test/fuzz/fuzz-dhcp-server-relay-message/fe4344e65d495388540dc1bf8eae70c46f8b867c
new file mode 100644 (file)
index 0000000..0d2b0c8
Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server-relay-message/fe4344e65d495388540dc1bf8eae70c46f8b867c differ
diff --git a/test/fuzz/fuzz-dhcp-server/clusterfuzz-testcase-minimized-fuzz-dhcp-server-4916534286352384 b/test/fuzz/fuzz-dhcp-server/clusterfuzz-testcase-minimized-fuzz-dhcp-server-4916534286352384
new file mode 100644 (file)
index 0000000..3d13705
Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server/clusterfuzz-testcase-minimized-fuzz-dhcp-server-4916534286352384 differ
diff --git a/test/fuzz/fuzz-dhcp-server/crash-64c861ad981838e509920e8538640ef46feaae66 b/test/fuzz/fuzz-dhcp-server/crash-64c861ad981838e509920e8538640ef46feaae66
new file mode 100644 (file)
index 0000000..a61ee0b
Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server/crash-64c861ad981838e509920e8538640ef46feaae66 differ
diff --git a/test/fuzz/fuzz-dhcp-server/crash-e13ccbc34d0fa5add0ee1eb69ed630dd2cdfb1ca b/test/fuzz/fuzz-dhcp-server/crash-e13ccbc34d0fa5add0ee1eb69ed630dd2cdfb1ca
new file mode 100644 (file)
index 0000000..1bd216c
Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server/crash-e13ccbc34d0fa5add0ee1eb69ed630dd2cdfb1ca differ
index 7586e35b4930e8e90c9c5a1fabf7f75c121ad32f..a5773826c583f320b59eebcece7c166e372d2ca6 100644 (file)
@@ -80,3 +80,14 @@ RxMaxCoalescedHighFrames=
 TxCoalesceHighSec=
 TxMaxCoalescedHighFrames=
 CoalescePacketRateSampleIntervalSec=
+SR-IOVVirtualFunctions=
+[SR-IOV]
+VirtualFunction=
+MACSpoofCheck=
+VLANId=
+VLANProtocol=
+QualityOfService=
+QueryReceiveSideScaling=
+Trust=
+LinkState=
+MACAddress=
index 78ddaf5ec8a54b5e3dd4f015453162e1d26fd33e..186557f8a5431ddd747d662c8818cf362c958e8d 100644 (file)
@@ -217,6 +217,7 @@ RootImage=
 RootHash=
 RootHashSignature=
 RootVerity=
+ExtensionDirectories=
 ExtensionImages=
 RuntimeMaxSec=
 SELinuxContextFromNet=
index 67421444cca0b3e080369c074adfb80f264c4759..0a44328e5c687869a2caac7abe57eda9153384cd 100644 (file)
@@ -40,6 +40,7 @@ DynamicUser=
 Environment=
 EnvironmentFile=
 ExecPaths=
+ExtensionDirectories=
 ExtensionImages=
 FinalKillSignal=
 ForceUnmount=
index ca9959538b899336c8d6809a2d2f980d83dd3038..6be65062d3ec22a179f1a735b9d93b5b10b8d2d4 100644 (file)
@@ -168,6 +168,7 @@ ExecStartPre=
 ExecStop=
 ExecStopPost=
 ExitType=
+ExtensionDirectories=
 ExtensionImages=
 FailureAction=
 FileDescriptorStoreMax=
index 865fd83adc50c0da88aff8714c2b4dd7e2756edf..90358fc11aa84e4642a5afaf1c5f04974d6358dd 100644 (file)
@@ -50,6 +50,7 @@ ExecStartPost=
 ExecStartPre=
 ExecStopPost=
 ExecStopPre=
+ExtensionDirectories=
 ExtensionImages=
 FileDescriptorName=
 FinalKillSignal=
index f538ba8b6095b4281df2f62268512e16fa246422..5d057fa63060c2c791d25e6b10fa378d60b46b10 100644 (file)
@@ -39,6 +39,7 @@ DynamicUser=
 Environment=
 EnvironmentFile=
 ExecPaths=
+ExtensionDirectories=
 ExtensionImages=
 FinalKillSignal=
 Group=
index 6f6bba699466e5fb1201392ae36e0c067b4c1a4f..546ef74d4ac88ba8a1bf79dd6ae91afc31069053 100644 (file)
@@ -116,16 +116,6 @@ endif
 
 ############################################################
 
-if conf.get('HAVE_SYSV_COMPAT') == 1
-        sysv_generator_test_py = find_program('sysv-generator-test.py')
-        if want_tests != 'false'
-                test('sysv-generator-test',
-                     sysv_generator_test_py)
-        endif
-endif
-
-############################################################
-
 if install_tests
         install_data('run-unit-tests.py',
                      install_mode : 'rwxr-xr-x',
@@ -184,9 +174,10 @@ if want_tests != 'false' and dmi_arches.contains(host_machine.cpu_family())
 
                 test(name,
                      udev_dmi_memory_id_test,
-                     args : [udev_prog_paths['dmi_memory_id'],
+                     args : [udev_prog_paths['dmi_memory_id'].full_path(),
                              source,
-                             source + '.txt'])
+                             source + '.txt'],
+                     depends : udev_prog_paths['dmi_memory_id'])
         endforeach
 endif
 
diff --git a/test/splash.bmp b/test/splash.bmp
deleted file mode 100644 (file)
index 27247f7..0000000
Binary files a/test/splash.bmp and /dev/null differ
diff --git a/test/test-efi-create-disk.sh b/test/test-efi-create-disk.sh
deleted file mode 100755 (executable)
index 46062e4..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-set -eo pipefail
-
-out="${1:?}"
-systemd_efi="${2:?}"
-boot_stub="${3:?}"
-splash_bmp="${4:?}"
-
-efi_dir="$(bootctl -p)"
-entry_dir="${efi_dir}/$(cat /etc/machine-id)/$(uname -r)"
-
-# create GPT table with EFI System Partition
-rm -f "$out"
-dd if=/dev/null of="$out" bs=1M seek=512 count=1 status=none
-parted --script "$out" "mklabel gpt" "mkpart ESP fat32 1MiB 511MiB" "set 1 boot on"
-
-# create FAT32 file system
-LOOP="$(losetup --show -f -P "$out")"
-mkfs.vfat -F32 "${LOOP}p1"
-mkdir -p mnt
-mount "${LOOP}p1" mnt
-
-mkdir -p mnt/EFI/{BOOT,systemd}
-cp "$systemd_efi" mnt/EFI/BOOT/BOOTX64.efi
-
-if [ -e /boot/shellx64.efi ]; then
-    cp /boot/shellx64.efi mnt/
-fi
-
-mkdir mnt/EFI/Linux
-echo -n "foo=yes bar=no root=/dev/fakeroot debug rd.break=initqueue" >mnt/cmdline.txt
-objcopy \
-    --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
-    --add-section .cmdline=mnt/cmdline.txt --change-section-vma .cmdline=0x30000 \
-    --add-section .splash="$splash_bmp" --change-section-vma .splash=0x40000 \
-    --add-section .linux="${entry_dir}/linux" --change-section-vma .linux=0x2000000 \
-    --add-section .initrd="${entry_dir}/initrd" --change-section-vma .initrd=0x3000000 \
-    "$boot_stub" mnt/EFI/Linux/linux-test.efi
-
-# install entries
-mkdir -p mnt/loader/entries
-echo -e "timeout 3\n" > mnt/loader/loader.conf
-echo -e "title Test\nefi /test\n" > mnt/loader/entries/test.conf
-echo -e "title Test2\nlinux /test2\noptions option=yes word number=1000 more\n" > mnt/loader/entries/test2.conf
-echo -e "title Test3\nlinux /test3\n" > mnt/loader/entries/test3.conf
-echo -e "title Test4\nlinux /test4\n" > mnt/loader/entries/test4.conf
-echo -e "title Test5\nefi /test5\n" > mnt/loader/entries/test5.conf
-echo -e "title Test6\nlinux /test6\n" > mnt/loader/entries/test6.conf
-
-sync
-umount mnt
-rmdir mnt
-losetup -d "$LOOP"
index 2258b8b1fe7bcdc485bed6351851e0d41c6321aa..ac8bf8883b9ebe3c45a3e1bb6a80fc6ce90966ed 100644 (file)
@@ -576,7 +576,8 @@ install_verity_minimal() {
         oldinitdir="$initdir"
         rm -rfv "$TESTDIR/minimal"
         export initdir="$TESTDIR/minimal"
-        mkdir -p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt"
+        # app0 will use TemporaryFileSystem=/var/lib, app1 will need the mount point in the base image
+        mkdir -p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt" "$initdir/var/lib/app1"
         setup_basic_dirs
         install_basic_tools
         # Shellcheck treats [[ -v VAR ]] as an assignment to avoid a different
@@ -598,19 +599,23 @@ install_verity_minimal() {
         touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf"
         touch "$initdir/opt/some_file"
         echo MARKER=1 >>"$initdir/usr/lib/os-release"
-        echo "PORTABLE_PREFIXES=app0 minimal" >>"$initdir/usr/lib/os-release"
-        echo -e "[Service]\nExecStartPre=cat /usr/lib/os-release\nExecStart=sleep 120" >"$initdir/usr/lib/systemd/system/app0.service"
-        cp "$initdir/usr/lib/systemd/system/app0.service" "$initdir/usr/lib/systemd/system/app0-foo.service"
+        echo "PORTABLE_PREFIXES=app0 minimal minimal-app0" >>"$initdir/usr/lib/os-release"
+        cat >"$initdir/usr/lib/systemd/system/minimal-app0.service" <<EOF
+[Service]
+ExecStartPre=cat /usr/lib/os-release
+ExecStart=sleep 120
+EOF
+        cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
 
-        mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_0.raw"
+        mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_0.raw" -noappend
         veritysetup format "$oldinitdir/usr/share/minimal_0.raw" "$oldinitdir/usr/share/minimal_0.verity" | \
             grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_0.roothash"
 
         sed -i "s/MARKER=1/MARKER=2/g" "$initdir/usr/lib/os-release"
-        rm "$initdir/usr/lib/systemd/system/app0-foo.service"
-        cp "$initdir/usr/lib/systemd/system/app0.service" "$initdir/usr/lib/systemd/system/app0-bar.service"
+        rm "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
+        cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-bar.service"
 
-        mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw"
+        mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend
         veritysetup format "$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \
             grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash"
 
@@ -629,16 +634,20 @@ install_verity_minimal() {
 Type=oneshot
 RemainAfterExit=yes
 ExecStart=/opt/script0.sh
+TemporaryFileSystem=/var/lib
+StateDirectory=app0
+RuntimeDirectory=app0
 EOF
         cat >"$initdir/opt/script0.sh" <<EOF
 #!/bin/bash
 set -e
 test -e /usr/lib/os-release
+echo bar > \${STATE_DIRECTORY}/foo
 cat /usr/lib/extension-release.d/extension-release.app0
 EOF
         chmod +x "$initdir/opt/script0.sh"
         echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
-        mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw"
+        mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw" -noappend
 
         export initdir="$TESTDIR/app1"
         mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
@@ -652,16 +661,19 @@ EOF
 Type=oneshot
 RemainAfterExit=yes
 ExecStart=/opt/script1.sh
+StateDirectory=app1
+RuntimeDirectory=app1
 EOF
         cat >"$initdir/opt/script1.sh" <<EOF
 #!/bin/bash
 set -e
 test -e /usr/lib/os-release
+echo baz > \${STATE_DIRECTORY}/foo
 cat /usr/lib/extension-release.d/extension-release.app2
 EOF
         chmod +x "$initdir/opt/script1.sh"
         echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file"
-        mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw"
+        mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw" -noappend
     )
 }
 
diff --git a/test/test-network/conf/25-sriov-udev.network b/test/test-network/conf/25-sriov-udev.network
new file mode 100644 (file)
index 0000000..e914131
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=eni99np1
+
+[Network]
+Address=192.168.100.100/24
+IPv6AcceptRA=no
diff --git a/test/test-network/conf/25-sriov.link b/test/test-network/conf/25-sriov.link
new file mode 100644 (file)
index 0000000..cc19561
--- /dev/null
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Driver=netdevsim
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+AlternativeNamesPolicy=database onboard slot path mac
+MACAddressPolicy=persistent
+
+[SR-IOV]
+VirtualFunction=0
+VLANId=5
+VLANProtocol=802.1ad
+QualityOfService=1
+MACSpoofCheck=yes
+QueryReceiveSideScaling=yes
+Trust=yes
+LinkState=yes
+MACAddress=00:11:22:33:44:55
+
+[SR-IOV]
+VirtualFunction=1
+VLANId=6
+VLANProtocol=802.1Q
+QualityOfService=2
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=no
+MACAddress=00:11:22:33:44:56
+
+[SR-IOV]
+VirtualFunction=2
+VLANId=7
+QualityOfService=3
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=auto
+MACAddress=00:11:22:33:44:57
index 2207f36245a2e962246789a6654cf9fa28e8950e..ba1617039357492482661fe9595ae6c537f826cb 100755 (executable)
@@ -176,7 +176,7 @@ def expectedFailureIfAlternativeNameIsNotAvailable():
         call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
         rc = call('ip link prop add dev dummy98 altname hogehogehogehogehoge', stderr=subprocess.DEVNULL)
         if rc == 0:
-            rc = call('ip link show dev hogehogehogehogehoge', stderr=subprocess.DEVNULL)
+            rc = call('ip link show dev hogehogehogehogehoge', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
             if rc == 0:
                 supported = True
 
@@ -594,6 +594,21 @@ class Utilities():
     def check_link_attr(self, *args):
         self.assertEqual(read_link_attr(*args[:-1]), args[-1]);
 
+    def wait_activated(self, link, state='down', timeout=20, fail_assert=True):
+        # wait for the interface is activated.
+        invocation_id = check_output('systemctl show systemd-networkd -p InvocationID --value')
+        needle = f'{link}: Bringing link {state}'
+        flag = state.upper()
+        for iteration in range(timeout+1):
+            output = check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id)
+            if needle in output and flag in check_output(f'ip link show {link}'):
+                return True
+            if iteration < timeout:
+                time.sleep(1)
+        if fail_assert:
+            self.fail(f'Timed out waiting for {link} activated.')
+        return False
+
     def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
         """Wait for the link to reach the specified operstate and/or setup state.
 
@@ -2065,7 +2080,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '25-route-vrf.network',
         '25-gateway-static.network',
         '25-gateway-next-static.network',
-        '25-sriov.network',
         '25-sysctl-disable-ipv6.network',
         '25-sysctl.network',
         '25-test1.network',
@@ -3054,7 +3068,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.wait_operstate('test1', 'routable')
 
     def _test_activation_policy(self, test):
-        self.setUp()
         conffile = '25-activation-policy.network'
         if test:
             conffile = f'{conffile}.d/{test}.conf'
@@ -3062,13 +3075,13 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         start_networkd()
 
         always = test.startswith('always')
-        if test == 'manual':
-            initial_up = 'UP' in check_output('ip link show test1')
-        else:
-            initial_up = not test.endswith('down') # note: default is up
+        initial_up = test != 'manual' and not test.endswith('down') # note: default is up
         expect_up = initial_up
         next_up = not expect_up
 
+        if test.endswith('down'):
+            self.wait_activated('test1')
+
         for iteration in range(4):
             with self.subTest(iteration=iteration, expect_up=expect_up):
                 operstate = 'routable' if expect_up else 'off'
@@ -3088,16 +3101,17 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
                 check_output('ip link set dev test1 down')
             expect_up = initial_up if always else next_up
             next_up = not next_up
-
-        self.tearDown()
+            if always:
+                time.sleep(1)
 
     def test_activation_policy(self):
         for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
             with self.subTest(test=test):
+                self.setUp()
                 self._test_activation_policy(test)
+                self.tearDown()
 
     def _test_activation_policy_required_for_online(self, policy, required):
-        self.setUp()
         conffile = '25-activation-policy.network'
         units = ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile]
         if policy:
@@ -3107,6 +3121,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         copy_unit_to_networkd_unit_path(*units, dropins=False)
         start_networkd()
 
+        if policy.endswith('down'):
+            self.wait_activated('test1')
+
         if policy.endswith('down') or policy == 'manual':
             self.wait_operstate('test1', 'off', setup_state='configuring')
         else:
@@ -3132,13 +3149,13 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         yesno = 'yes' if expected else 'no'
         self.assertRegex(output, f'Required For Online: {yesno}')
 
-        self.tearDown()
-
     def test_activation_policy_required_for_online(self):
         for policy in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
             for required in ['yes', 'no', '']:
                 with self.subTest(policy=policy, required=required):
+                    self.setUp()
                     self._test_activation_policy_required_for_online(policy, required)
+                    self.tearDown()
 
     def test_domain(self):
         copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network')
@@ -3450,32 +3467,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'qdisc fq_pie 3a: root')
         self.assertRegex(output, 'limit 200000p')
 
-    @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
-    def test_sriov(self):
-        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
-        call('modprobe netdevsim', stderr=subprocess.DEVNULL)
-        with open('/sys/bus/netdevsim/new_device', mode='w') as f:
-            f.write('99 1')
-
-        call('udevadm settle')
-        call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
-        with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
-            f.write('3')
-
-        copy_unit_to_networkd_unit_path('25-sriov.network')
-        start_networkd()
-        self.wait_online(['eni99np1:routable'])
-
-        output = check_output('ip link show dev eni99np1')
-        print(output)
-        self.assertRegex(output,
-                         'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
-                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
-                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
-        )
-
-        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
-
     def test_wait_online_ipv4(self):
         copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-with-ipv6-prefix.network', 'dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network')
         start_networkd()
@@ -3999,6 +3990,133 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
         print(output)
         self.assertIn('from all to 8.8.8.8 lookup 100', output)
 
+class NetworkdSRIOVTests(unittest.TestCase, Utilities):
+    units = [
+        '25-sriov-udev.network',
+        '25-sriov.link',
+        '25-sriov.network',
+    ]
+
+    def setUp(self):
+        stop_networkd(show_logs=False)
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+
+    def tearDown(self):
+        remove_unit_from_networkd_path(self.units)
+        stop_networkd(show_logs=True)
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+
+    @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
+    def test_sriov(self):
+        call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+
+        with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+            f.write('99 1')
+
+        call('udevadm settle')
+        call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
+        with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
+            f.write('3')
+
+        copy_unit_to_networkd_unit_path('25-sriov.network')
+        start_networkd()
+        self.wait_online(['eni99np1:routable'])
+
+        output = check_output('ip link show dev eni99np1')
+        print(output)
+        self.assertRegex(output,
+                         'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
+        )
+
+    @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
+    def test_sriov_udev(self):
+        call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+
+        copy_unit_to_networkd_unit_path('25-sriov.link', '25-sriov-udev.network')
+        call('udevadm control --reload')
+
+        with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+            f.write('99 1')
+
+        start_networkd()
+        self.wait_online(['eni99np1:routable'])
+
+        output = check_output('ip link show dev eni99np1')
+        print(output)
+        self.assertRegex(output,
+                         'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
+        )
+        self.assertNotIn('vf 3', output)
+        self.assertNotIn('vf 4', output)
+
+        with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
+            f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
+
+        call('udevadm control --reload')
+        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+
+        output = check_output('ip link show dev eni99np1')
+        print(output)
+        self.assertRegex(output,
+                         'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
+                         'vf 3'
+        )
+        self.assertNotIn('vf 4', output)
+
+        with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
+            f.write('[Link]\nSR-IOVVirtualFunctions=\n')
+
+        call('udevadm control --reload')
+        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+
+        output = check_output('ip link show dev eni99np1')
+        print(output)
+        self.assertRegex(output,
+                         'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off\n *'
+                         'vf 3'
+        )
+        self.assertNotIn('vf 4', output)
+
+        with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
+            f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
+
+        call('udevadm control --reload')
+        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+
+        output = check_output('ip link show dev eni99np1')
+        print(output)
+        self.assertRegex(output,
+                         'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off'
+        )
+        self.assertNotIn('vf 2', output)
+        self.assertNotIn('vf 3', output)
+        self.assertNotIn('vf 4', output)
+
+        with open(os.path.join(network_unit_file_path, '25-sriov.link'), mode='a') as f:
+            f.write('[Link]\nSR-IOVVirtualFunctions=\n')
+
+        call('udevadm control --reload')
+        call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+
+        output = check_output('ip link show dev eni99np1')
+        print(output)
+        self.assertRegex(output,
+                         'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
+        )
+        self.assertNotIn('vf 3', output)
+        self.assertNotIn('vf 4', output)
+
 class NetworkdLLDPTests(unittest.TestCase, Utilities):
     links = ['veth99']
 
diff --git a/test/test-resolve/selfsigned.cert b/test/test-resolve/selfsigned.cert
new file mode 100644 (file)
index 0000000..456862c
--- /dev/null
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFmzCCA4OgAwIBAgIUZlvbV3+2YGjHJDTW+u0XL/ypvsowDQYJKoZIhvcNAQEL
+BQAwXDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkRlbmlhbDEUMBIGA1UEBwwLU3By
+aW5nZmllbGQxDDAKBgNVBAoMA0RpczEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29t
+MCAXDTIyMDEyMzE0MjYyMloYDzMwMjEwNTI2MTQyNjIyWjBcMQswCQYDVQQGEwJV
+UzEPMA0GA1UECAwGRGVuaWFsMRQwEgYDVQQHDAtTcHJpbmdmaWVsZDEMMAoGA1UE
+CgwDRGlzMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQC14826tEEHKICM/AKOKsyBUDaa6Z6KqS927ifb43LJ
+fxtg8vW+vX9OGtje2qVAoI1UMSu4yttItSd9cjrnAFLPFvQGC8dFhn436ehLWiKP
+AP5KvhIQ3equ5fTicn+Hxdm7C3Um2SEEE347I/vArfzuNE7PIJ57sh7KeBHGCrU3
+6iPl1DkkUilbqJAcgFoepozx1SbPq4h8LsdqJDKg+XUtvtuUS850D5Hb5ErTc8NL
+VrA+urSIr+yIX4jAeLXbktNLGuAc3+cJTjwuWdDvP51yS0qpj459dFaRWQE9gu0b
+DkRwYLF7Mpel66B8TBkHAWBhSs+oLNnv//Zv945wbUkTK29N2VtSEI4pd/47nTX9
+MwGn4q/ZAjhI7JUN3LcRDsrLdVAUabbK/U+xkL/lOlRRBK/1iuLELkaJlMUiuqZh
+q3DvNjqeT5yY8GTU5iXoBcvY0lac3+zYaemTgD5cZfF4gpTflGfc5Gf+he6U3Dol
+TT+4JfMrw0YdbqsH4oyEtmLBfMvvp+PQysiOELSFbSAphZOOcy8QSzoRrniNynPd
+kM7kIM+0t2XUaz0lKtNuZSo9DnhTMvTLPnnbk5aJt5nPxPprcdqhcJhrHw7gVhBo
+EceYJmXGiJJMLYuBNymZ4u7YrBg0e0qO+Fi9a4Kfh/QNMq/6VrpWvycb9LtCLhU+
+qQIDAQABo1MwUTAdBgNVHQ4EFgQU3ugK1HtfPaq90JC5Qf5ekrn4uUcwHwYDVR0j
+BBgwFoAU3ugK1HtfPaq90JC5Qf5ekrn4uUcwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEATzNvQP+VNLY0qK5b/8j8C5PHB0WKsTHSVNOhITr0Bq67
+AeOq5Mh2qZvJd/QAPTb/Cg60802RLQxp6uhCcfMdxTXkz1mxq6dKEeDAu/zAZzXk
+WSpJl/VORnibjvXf2OS6ucb4KPOxfkYiD328CdSYBJapmQbnUmwZph2SO0bpY7K3
+EbTY9fIyabGMrjbXL5EGRvqA0NSnJHVUYx1h3b32PYKHrQKu6syCE4OrMY0yjdLH
+1WnHeC3iB02AFy7TTfmeUiMTxaiPXAPjBDDIQtv1GHt8GR7WHSD3seIhAu6Lzbyr
+0zrxk52C9v0YP1lgOwnvmQfbUSpWc29yhrXFkqkZToqbmYjNO55gPN8JA/2GrWan
+s8gQwQ8z+yWAqNJQA5S+9+hNlBlcq659gCjIxoyCmkol4EepwR1WWdZjs2I00FHk
+mQL1ig6H81rg/Bh2SraKR1tGdmjCNFi4RfWHsxCBcd1cGFeUIN+ygNmjXmzXJDFP
+5vUXL9J5iu+WD1rnwB2gPRSvZUrmKUZnOGk0/kt1RpgbcFdOza+6vZmB51fXZYpD
+YyvXHTbuHVOyXA160/Fmg6BNy5BfrTuXaZ3YVeZmvDf+ywVl2BFDQZDoLLQMIHzl
+L2DdMuhVmgITqx8ZtrSxqBxW0DQXFZiMT+sv81+o2SO5nDzSYjoXfQv/Xkgpx44=
+-----END CERTIFICATE-----
diff --git a/test/test-resolve/selfsigned.key b/test/test-resolve/selfsigned.key
new file mode 100644 (file)
index 0000000..44a0982
--- /dev/null
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC14826tEEHKICM
+/AKOKsyBUDaa6Z6KqS927ifb43LJfxtg8vW+vX9OGtje2qVAoI1UMSu4yttItSd9
+cjrnAFLPFvQGC8dFhn436ehLWiKPAP5KvhIQ3equ5fTicn+Hxdm7C3Um2SEEE347
+I/vArfzuNE7PIJ57sh7KeBHGCrU36iPl1DkkUilbqJAcgFoepozx1SbPq4h8Lsdq
+JDKg+XUtvtuUS850D5Hb5ErTc8NLVrA+urSIr+yIX4jAeLXbktNLGuAc3+cJTjwu
+WdDvP51yS0qpj459dFaRWQE9gu0bDkRwYLF7Mpel66B8TBkHAWBhSs+oLNnv//Zv
+945wbUkTK29N2VtSEI4pd/47nTX9MwGn4q/ZAjhI7JUN3LcRDsrLdVAUabbK/U+x
+kL/lOlRRBK/1iuLELkaJlMUiuqZhq3DvNjqeT5yY8GTU5iXoBcvY0lac3+zYaemT
+gD5cZfF4gpTflGfc5Gf+he6U3DolTT+4JfMrw0YdbqsH4oyEtmLBfMvvp+PQysiO
+ELSFbSAphZOOcy8QSzoRrniNynPdkM7kIM+0t2XUaz0lKtNuZSo9DnhTMvTLPnnb
+k5aJt5nPxPprcdqhcJhrHw7gVhBoEceYJmXGiJJMLYuBNymZ4u7YrBg0e0qO+Fi9
+a4Kfh/QNMq/6VrpWvycb9LtCLhU+qQIDAQABAoICAEbiyfm6aCFnCnpneIN5cIvw
+++bxpyT4/JOICyaqBME8dSoaZeV5KpUA54Yqhf6i05F9PEHfZQh3+TTtgMEoIh2t
+H1r/2iBhYu1djndXYGKFC5WLb7T9F4oj+oUKBGOgmtNHiteiBTj2c9qOkn2sEQew
+gQo99yXT7CYSFzMsVyW8bVMTm1VpY87h6ZACAZ0yYXmaDW8ftahX/sWB5+1Oavly
+CVdJF+OpcbnVxceUtQa2eSdpUhR3I2KegMgqAw3YsdnyVmdKZ1r8D34s6L1k+HJj
+n2xnkyuXXGl224HidY92xvtY47JUrD8wjjIC4joVsj8YjcdH+4OKKLvIKc3s+W4C
++fF6Uhxe7V0kg6bw+UE5eGC0CYeQww4Ruwd7Y8MHpF+6QAK2QsVWGCrhvCgXf9ix
+s4iJBjaGG3fEMVyTrMeaJ/IhI4x6awjN8f/viqXfpwU5a06/JpILghgNTigHTCi2
++Bpv4pXNmdn5AdLbGU+H+XVannY1obM2TV3XK5xMDpEblHG/Y2uNuOGa1YvSUlnl
+AkYCV9pk39Hpnp0TWZ0MQmV4gG0VqyS7t6rh+3xehK+dxD5O9T+BSeB2H5hRrN5V
+qImdsRvJMjj1WMQcREfC+9DTY/YOBlMdymtm7cix0JhnlK4UlvInQYvrnPasYvUA
+wUVINpxGc87HxFEWeqSBAoIBAQDv8f5vS2w1yZNnz1cQwCPvUmLEevruCeO0IR71
+zNGRncoseMr8la4QOzAFCdosAqrfMlIuHK6Y1LVGgyEK4RYsU3zpOWHMzBalhChJ
+dXeZoP/5a7p/Hxrh1YoZCCenTQne/KHIzhyOUzkYn2YltibkOR+sVUeYx8ZgDXxV
+WwJmeMRV9Y19sE3fGYEQAo4gd1/8kObWlT49VwqDuUAjTtwwFQKs2b6UF77cAOgt
+U1rclYg9LyB+liGof1TMA1S0z63keOYBX2S3154rB/91vXJdWPx0q1kCjyFfBlAp
+ckxa7pygcWluGlUUqQlKglEEzQrA32OXRBa1loy2bzeU+px5AoIBAQDCD2HfzCzE
+gl91ZgPnmTK7vDz3Dc+7nGP9ZVFBJory5zC/aCasaQ2FndpSQiC1kzVKdrW1h+U/
+yY2Bg8KrxyV6xP/yR2l0dM4tdjhZRJtCHIua2/K9w8aC713ak1ZbjVQtQPdX0tNy
+zQADb/WaI3cLoXBul9vtEqdILqsMe3RsaTsAtswheCuT8n4ySbgdepavNOufTvh8
+TJVZq4+V0Vas+jYt4+yODHqfswGG1Ud2kZ4rucv8XsIOkA24eDsAgzBQormbeSXJ
+KS37LRT3Swf+jA7WajfwrKV6cO7Y9mwOPMWZzFz3ES/qFYbK9s1KftkcudiYRc25
+KJZslS5xVcexAoIBAHjAKtAtf65t2/2xDVrDpxHoPwYr8Z3bYjkjNeZzBcAnTTgm
+LdkBJpDKiHbwp1fgm8cpFsxX6NHGsddjZDyKW9NAzKq+Euayim8PXArjz6WDrW4C
+9d7Fc4zVHuNMBFCgZ2hNcMmSWDKT1Tb7+LbfvSC7UqIyZI6Rctah0sFNxJ53Bi9Q
+HL10/StaNWYuMwJJsQd0kIbKooDSDduOXaWnKQ4VdLwx9EOo04b5+d3dheteYSqR
+TeQGf7fBJJZq0rUPkq5Y3T8xl4khPFrhcoD5LtWlU58PIAM2ro+YqLzC5YQZcr8X
+c/xRyiFUk/VoMYed/Fxlz0Ovo1INCpFA1RLnL9kCggEAX/0923Zh+n3GfAqPCeME
+bkpJGacSRumvp+qSy5gmCMqEmVkKMCPylVIkaKXfChGbvY6EiRuEMQ4gWZz0EQX7
+qwOA2rWqGvmf9mrQqo8+APCfuWTsaCNLsP53vSM+ByEcLxpAfoeBIfr287xQjwLV
+4sHjHEEvfs/IQPMclpsGVo2iqtLAnBmV7KN4+qTuVl6J5HZXykBEty8mfOlYp7GZ
+nwxQ+lgQbZ8MlKv1qF0c8TBMPbK0jMvOT2e/8aw++xzpLCmhh57gKuWcoe6FvWC2
+vplGyZZWv0yWub7c1iLmBhDXaSDmJyuwOKiXORPlLeEawZPH6GI2xUynQ2RzSYo1
+sQKCAQAbVhs1HcP5PAOTF5jAUdMbx+LeLUgKjO3Nx+YUeQCKVOgypd8w7N9k7WPi
+BvTu7nkMtiK5UCix+UGthUFYyMClD2wnQ1h6nhVVz/D98cukksr1awNu6ms9M2ol
+u6U7tfViEJhPxL+1pdAnFmqAoQx8fGpiyZQbb9DAcVBrIqQEjCRr4yZ8XaHOcTL0
+OeQO6ZCgxYOO5ac4snc1PDnRrlLs++b6tyaunLsFRSBkuzkMugXgUc+y3xgzBUQf
+LOb/QIZvtqyF6s/YJZtLjLC8vdoe0ZqINh5Dq1xoGvtI1/QMgWraom999w9liFWs
+VULYeUwXocBKk6rBSgDlsFF5LW22
+-----END PRIVATE KEY-----
index 91f0211bca17e1b473de1c4405287f259b379b79..4c2a0410dac5280a4f712767cdc4c873815889da 100755 (executable)
@@ -11,8 +11,10 @@ setup() {
 ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
 ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", PROGRAM=="/bin/sleep 60"
 EOF
-    echo "event_timeout=10" >>/etc/udev/udev.conf
-    echo "timeout_signal=SIGABRT" >>/etc/udev/udev.conf
+    cat >>/etc/udev/udev.conf <<EOF
+event_timeout=10
+timeout_signal=SIGABRT
+EOF
 
     systemctl restart systemd-udevd.service
 }
index 0e0c8cf41d98bbfac235b7db2e313d54fe39c96f..1b927a83052dbee7e960cfcf189d11f72c7a6b75 100755 (executable)
@@ -6,10 +6,13 @@ set -eux
 set -o pipefail
 
 ARGS=()
+state_directory=/var/lib/private/
 if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
     # If we're running under sanitizers, we need to use a less restrictive
     # profile, otherwise LSan syscall would get blocked by seccomp
     ARGS+=(--profile=trusted)
+    # With the trusted profile DynamicUser is disabled, so the storage is not in private/
+    state_directory=/var/lib/
 fi
 
 systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service'
@@ -24,29 +27,29 @@ cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
 Environment=SYSTEMD_LOG_LEVEL=debug
 EOF
 
-portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw app0
+portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0
 
-systemctl is-active app0.service
-systemctl is-active app0-foo.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
 set +o pipefail
 set +e
-systemctl is-active app0-bar.service && exit 1
+systemctl is-active minimal-app0-bar.service && exit 1
 set -e
 set -o pipefail
 
-portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw app0
+portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
 
-systemctl is-active app0.service
-systemctl is-active app0-bar.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
 set +o pipefail
 set +e
-systemctl is-active app0-foo.service && exit 1
+systemctl is-active minimal-app0-foo.service && exit 1
 set -e
 set -o pipefail
 
 portablectl list | grep -q -F "minimal_1"
 
-portablectl detach --now --runtime /usr/share/minimal_1.raw app0
+portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0
 
 portablectl list | grep -q -F "No images."
 
@@ -55,55 +58,70 @@ portablectl list | grep -q -F "No images."
 unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw
 unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw
 
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 app0
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
 
-systemctl is-active app0.service
-systemctl is-active app0-foo.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
 set +o pipefail
 set +e
-systemctl is-active app0-bar.service && exit 1
+systemctl is-active minimal-app0-bar.service && exit 1
 set -e
 set -o pipefail
 
-portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 app0
+portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
 
-systemctl is-active app0.service
-systemctl is-active app0-bar.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
 set +o pipefail
 set +e
-systemctl is-active app0-foo.service && exit 1
+systemctl is-active minimal-app0-foo.service && exit 1
 set -e
 set -o pipefail
 
 portablectl list | grep -q -F "minimal_1"
 
-portablectl detach --now --enable --runtime /tmp/minimal_1 app0
+portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
 
 portablectl list | grep -q -F "No images."
 
 portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0
 
 systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
 
 portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
 
 systemctl is-active app0.service
+status="$(portablectl is-attached --extension app0 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
 
 portablectl detach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0
 
 portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
 
 systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_0)"
+[[ "${status}" == "running-runtime" ]]
 
 portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
 
 systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1 minimal_1)"
+[[ "${status}" == "running-runtime" ]]
 
 portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
 
+# Ensure that the combination of read-only images, state directory and dynamic user works, and that
+# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
+# after the service is attached before the file appears.
+grep -q -F bar "${state_directory}/app0/foo"
+grep -q -F baz "${state_directory}/app1/foo"
+
 # portablectl also works with directory paths rather than images
 
-mkdir /tmp/rootdir /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
+mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
+mount /usr/share/app0.raw /tmp/app0
 mount /usr/share/app1.raw /tmp/app1
 mount /usr/share/minimal_0.raw /tmp/rootdir
 
@@ -124,7 +142,22 @@ systemctl is-active app1.service
 portablectl detach --now --runtime overlay app1
 
 umount /tmp/overlay
+
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
+systemctl is-active app0.service
+systemctl is-active app1.service
+
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service
+portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service
+
+portablectl detach --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
+
 umount /tmp/rootdir
+umount /tmp/app0
 umount /tmp/app1
 
 echo OK >/testok
index c3e57cec95cac4dc4d034acf067f2aac69183d47..d0bedc63d5e2c6737ca494859bb79ca7448f1652 100755 (executable)
@@ -153,20 +153,13 @@ if ! systemd-detect-virt -cq ; then
     inspect test-user2
 fi
 
-wait_for_state test-user inactive
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-wait_for_state test-user inactive
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \
     && { echo 'unexpected success'; exit 1; }
-wait_for_state test-user inactive
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
-wait_for_state test-user inactive
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
-wait_for_state test-user inactive
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
-wait_for_state test-user inactive
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-wait_for_state test-user inactive
 PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \
     && { echo 'unexpected success'; exit 1; }
 
index b35527f76147298528099183e0aceaae2fa1da78..6aabbd139c39da6ea797824f5a6cceca04f8c402 100755 (executable)
@@ -308,7 +308,8 @@ systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.r
 cat >/run/systemd/system/testservice-50e.service <<EOF
 [Service]
 MountAPIVFS=yes
-TemporaryFileSystem=/run
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
 RootImage=${image}.raw
 ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
 # Relevant only for sanitizer runs
@@ -321,6 +322,37 @@ EOF
 systemctl start testservice-50e.service
 systemctl is-active testservice-50e.service
 
+# ExtensionDirectories will set up an overlay
+mkdir -p "${image_dir}/app0" "${image_dir}/app1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; }
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; }
+systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0"
+systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+cat >/run/systemd/system/testservice-50f.service <<EOF
+[Service]
+MountAPIVFS=yes
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
+RootImage=${image}.raw
+ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1
+# Relevant only for sanitizer runs
+UnsetEnvironment=LD_PRELOAD
+ExecStart=/bin/bash -c '/opt/script0.sh | grep ID'
+ExecStart=/bin/bash -c '/opt/script1.sh | grep ID'
+Type=oneshot
+RemainAfterExit=yes
+EOF
+systemctl start testservice-50f.service
+systemctl is-active testservice-50f.service
+umount "${image_dir}/app0"
+umount "${image_dir}/app1"
+
 echo OK >/testok
 
 exit 0
diff --git a/test/units/testsuite-51-repro-3.service b/test/units/testsuite-51-repro-3.service
new file mode 100644 (file)
index 0000000..c68f93d
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Issue 22257 Repro with Restart=always
+
+[Service]
+Type=simple
+Restart=always
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
index fbe9693f3a5ec660ca69dda0c949750999de3062..e603d953a4fd3e618767620bf3d5890f9fc6ef16 100755 (executable)
@@ -5,9 +5,11 @@ set -o pipefail
 
 systemctl start testsuite-51-repro-1
 systemctl start testsuite-51-repro-2
+systemctl start testsuite-51-repro-3
 sleep 5 # wait a bit in case there are restarts so we can count them below
 
 [[ "$(systemctl show testsuite-51-repro-1 -P NRestarts)" == "0" ]]
 [[ "$(systemctl show testsuite-51-repro-2 -P NRestarts)" == "0" ]]
+[[ "$(systemctl show testsuite-51-repro-3 -P NRestarts)" == "0" ]]
 
 touch /testok
index 816eead22df8b0e9ca92f5afa38193d696fc37ec..1bd2cc406db168226f217992c91861ed3e4bcf14 100755 (executable)
@@ -32,10 +32,10 @@ fi
 binary=$(realpath "${1}")
 if [[ "${1}" =~ systemd-boot([[:alnum:]]+).efi ]]; then
     target="systemd-boot"
-    symbols=$(realpath "$(dirname "${1}")/systemd_boot.so")
+    symbols=$(realpath "${1%efi}elf")
 elif [[ "${1}" =~ linux([[:alnum:]]+).efi.stub ]]; then
     target="systemd-stub"
-    symbols=$(realpath "$(dirname "${1}")/linux${BASH_REMATCH[1]}.elf.stub")
+    symbols=$(realpath "${1%efi.stub}elf.stub")
 else
     echo "Cannot detect EFI binary '${1}'."
     exit 1
index cff90145ce0a70be1a314c78f20e0890b8ca09e6..8ff3abefb741d2ec3822f39b5476ffa78e57ece5 100755 (executable)
@@ -35,7 +35,7 @@ else
     apt-get update
     apt-get install -y gperf m4 gettext python3-pip \
         libcap-dev libmount-dev libkmod-dev \
-        pkg-config wget python3-jinja2
+        pkg-config wget python3-jinja2 zipmerge
 
     # gnu-efi is installed here to enable -Dgnu-efi behind which fuzz-bcd
     # is hidden. It isn't linked against efi. It doesn't
@@ -73,9 +73,14 @@ done
 zip -jqr "$OUT/fuzz-bcd_seed_corpus.zip" "$bcd"
 rm -rf "$bcd"
 
+hosts=$(mktemp)
+wget -O "$hosts" https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
+zip -jq "$OUT/fuzz-etc-hosts_seed_corpus.zip" "$hosts"
+rm -rf "$hosts"
+
 # The seed corpus is a separate flat archive for each fuzzer,
 # with a fixed name ${fuzzer}_seed_corpus.zip.
-for d in "$(dirname "$0")/../test/fuzz/fuzz-"*; do
+for d in test/fuzz/fuzz-*; do
     zip -jqr "$OUT/$(basename "$d")_seed_corpus.zip" "$d"
 done
 
@@ -93,3 +98,15 @@ wget -O "$OUT/fuzz-json.dict" https://raw.githubusercontent.com/rc0r/afl-fuzz/ma
 find "$build" -maxdepth 1 -type f -executable -name "fuzz-*" -exec mv {} "$OUT" \;
 find src -type f -name "fuzz-*.dict" -exec cp {} "$OUT" \;
 cp src/fuzz/*.options "$OUT"
+
+if [[ "$MERGE_WITH_OSS_FUZZ_CORPORA" == "yes" ]]; then
+    for f in "$OUT/"fuzz-*; do
+        [[ -x "$f" ]] || continue
+        fuzzer=$(basename "$f")
+        t=$(mktemp)
+        if wget -O "$t" "https://storage.googleapis.com/systemd-backup.clusterfuzz-external.appspot.com/corpus/libFuzzer/systemd_${fuzzer}/public.zip"; then
+            zipmerge "$OUT/${fuzzer}_seed_corpus.zip" "$t"
+        fi
+        rm -rf "$t"
+    done
+fi
index 3184433b8f547198befc71ecf67f794fd1e2d2f5..2bb0a8e845de8310418f30495f0bfbd63a40129e 100644 (file)
@@ -220,6 +220,7 @@ in_units = [
         ['systemd-network-generator.service',    ''],
         ['systemd-networkd.service',             'ENABLE_NETWORKD'],
         ['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD'],
+        ['systemd-networkd-wait-online@.service','ENABLE_NETWORKD'],
         ['systemd-nspawn@.service',              ''],
         ['systemd-oomd.service',                 'ENABLE_OOMD'],
         ['systemd-portabled.service',            'ENABLE_PORTABLED',
diff --git a/units/systemd-networkd-wait-online@.service.in b/units/systemd-networkd-wait-online@.service.in
new file mode 100644 (file)
index 0000000..949695f
--- /dev/null
@@ -0,0 +1,25 @@
+#  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=Wait for Network Interface %i to be Configured
+Documentation=man:systemd-networkd-wait-online.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Requires=systemd-networkd.service
+After=systemd-networkd.service
+Before=network-online.target shutdown.target
+
+[Service]
+Type=oneshot
+ExecStart={{ROOTLIBEXECDIR}}/systemd-networkd-wait-online -i %i
+RemainAfterExit=yes
+
+[Install]
+WantedBy=network-online.target