summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/dependabot.yml6
-rw-r--r--.github/workflows/build_test.yml5
-rw-r--r--.github/workflows/cifuzz.yml61
-rw-r--r--.github/workflows/codeql-analysis.yml42
-rw-r--r--.github/workflows/labeler.yml2
-rw-r--r--.github/workflows/linter.yml5
-rw-r--r--.github/workflows/mkosi.yml6
-rw-r--r--.github/workflows/unit_tests.yml10
-rw-r--r--TODO40
-rw-r--r--docs/CODING_STYLE.md2
-rw-r--r--docs/DISCOVERABLE_PARTITIONS.md8
-rw-r--r--docs/PORTING_TO_NEW_ARCHITECTURES.md61
-rw-r--r--docs/PREDICTABLE_INTERFACE_NAMES.md2
-rw-r--r--docs/TRANSIENT-SETTINGS.md1
-rw-r--r--docs/sysvinit/meson.build3
-rw-r--r--man/meson.build3
-rw-r--r--man/org.freedesktop.systemd1.xml8
-rw-r--r--man/os-release.xml30
-rw-r--r--man/repart.d.xml2
-rw-r--r--man/resolved.conf.xml2
-rw-r--r--man/rules/meson.build4
-rw-r--r--man/sd_bus_add_object.xml2
-rw-r--r--man/sd_event_add_inotify.xml29
-rw-r--r--man/sd_event_source_set_ratelimit.xml22
-rw-r--r--man/systemctl.xml4
-rw-r--r--man/systemd-analyze.xml24
-rw-r--r--man/systemd-fsck@.service.xml15
-rw-r--r--man/systemd.exec.xml13
-rw-r--r--man/systemd.journal-fields.xml2
-rw-r--r--man/systemd.netdev.xml81
-rw-r--r--man/systemd.network.xml273
-rw-r--r--man/systemd.nspawn.xml2
-rw-r--r--man/systemd.service.xml25
-rw-r--r--man/userdbctl.xml43
-rw-r--r--meson.build63
-rw-r--r--rules.d/meson.build3
-rw-r--r--shell-completion/bash/meson.build3
-rw-r--r--shell-completion/bash/systemd-run2
-rw-r--r--shell-completion/zsh/_systemd-run2
-rw-r--r--shell-completion/zsh/meson.build3
-rw-r--r--src/analyze/analyze.c64
-rw-r--r--src/basic/architecture.c3
-rw-r--r--src/basic/architecture.h3
-rw-r--r--src/basic/arphrd-list.c25
-rw-r--r--src/basic/arphrd-util.c45
-rw-r--r--src/basic/arphrd-util.h (renamed from src/basic/arphrd-list.h)5
-rw-r--r--src/basic/escape.c6
-rw-r--r--src/basic/escape.h2
-rw-r--r--src/basic/ether-addr-util.c210
-rw-r--r--src/basic/ether-addr-util.h56
-rw-r--r--src/basic/inotify-util.c18
-rw-r--r--src/basic/meson.build4
-rw-r--r--src/basic/process-util.c13
-rw-r--r--src/basic/umask-util.h3
-rw-r--r--src/boot/efi/boot.c139
-rw-r--r--src/boot/efi/meson.build557
-rw-r--r--src/core/automount.c6
-rw-r--r--src/core/bpf-foreign.c20
-rw-r--r--src/core/bpf-foreign.h5
-rw-r--r--src/core/cgroup.c5
-rw-r--r--src/core/dbus-service.c6
-rw-r--r--src/core/dbus-unit.c2
-rw-r--r--src/core/execute.c29
-rw-r--r--src/core/job.c8
-rw-r--r--src/core/load-fragment-gperf.gperf.in1
-rw-r--r--src/core/load-fragment.c4
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/manager.c12
-rw-r--r--src/core/manager.h4
-rw-r--r--src/core/meson.build6
-rw-r--r--src/core/mount.c27
-rw-r--r--src/core/namespace.c49
-rw-r--r--src/core/path.c6
-rw-r--r--src/core/service.c138
-rw-r--r--src/core/service.h11
-rw-r--r--src/core/socket.c6
-rw-r--r--src/core/swap.c6
-rw-r--r--src/core/timer.c6
-rw-r--r--src/core/unit-serialize.c1
-rw-r--r--src/core/unit.c16
-rw-r--r--src/core/unit.h9
-rw-r--r--src/fsck/fsck.c7
-rw-r--r--src/fundamental/bootspec-fundamental.c56
-rw-r--r--src/fundamental/bootspec-fundamental.h16
-rw-r--r--src/fundamental/macro-fundamental.h10
-rw-r--r--src/fundamental/meson.build6
-rw-r--r--src/fundamental/sha256.h2
-rw-r--r--src/fundamental/type.h21
-rw-r--r--src/fundamental/types-fundamental.h39
-rw-r--r--src/home/homework-luks.c4
-rw-r--r--src/hostname/hostnamectl.c2
-rw-r--r--src/hostname/hostnamed.c2
-rw-r--r--src/journal-remote/journal-remote.c4
-rw-r--r--src/journal-remote/meson.build3
-rw-r--r--src/journal/journald-stream.c7
-rw-r--r--src/libsystemd-network/arp-util.c1
-rw-r--r--src/libsystemd-network/dhcp-identifier.c4
-rw-r--r--src/libsystemd/libsystemd.sym2
-rw-r--r--src/libsystemd/meson.build3
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c6
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c39
-rw-r--r--src/libsystemd/sd-event/event-source.h6
-rw-r--r--src/libsystemd/sd-event/sd-event.c180
-rw-r--r--src/libsystemd/sd-event/test-event.c48
-rw-r--r--src/libsystemd/sd-id128/id128-util.c2
-rw-r--r--src/libsystemd/sd-netlink/netlink-types-rtnl.c22
-rw-r--r--src/libsystemd/sd-network/network-util.c83
-rw-r--r--src/libsystemd/sd-network/network-util.h9
-rw-r--r--src/libsystemd/sd-resolve/sd-resolve.c6
-rw-r--r--src/libudev/meson.build3
-rw-r--r--src/locale/test-keymap-util.c2
-rw-r--r--src/login/logind-core.c4
-rw-r--r--src/login/meson.build3
-rw-r--r--src/network/generator/network-generator.c4
-rw-r--r--src/network/netdev/bareudp.c64
-rw-r--r--src/network/netdev/bond.c2
-rw-r--r--src/network/netdev/bridge.c13
-rw-r--r--src/network/netdev/bridge.h11
-rw-r--r--src/network/netdev/geneve.c76
-rw-r--r--src/network/netdev/macsec.c2
-rw-r--r--src/network/netdev/macvlan.c2
-rw-r--r--src/network/netdev/netdev-gperf.gperf8
-rw-r--r--src/network/netdev/netdev.c255
-rw-r--r--src/network/netdev/netdev.h60
-rw-r--r--src/network/netdev/vrf.c2
-rw-r--r--src/network/networkctl.c7
-rw-r--r--src/network/networkd-bridge-fdb.c2
-rw-r--r--src/network/networkd-bridge-mdb.c1
-rw-r--r--src/network/networkd-dhcp-server-static-lease.c2
-rw-r--r--src/network/networkd-ipv4ll.c2
-rw-r--r--src/network/networkd-json.c4
-rw-r--r--src/network/networkd-link.c2
-rw-r--r--src/network/networkd-ndisc.c172
-rw-r--r--src/network/networkd-neighbor.c105
-rw-r--r--src/network/networkd-neighbor.h9
-rw-r--r--src/network/networkd-network-gperf.gperf22
-rw-r--r--src/network/networkd-network.c26
-rw-r--r--src/network/networkd-network.h2
-rw-r--r--src/network/networkd-setlink.c1
-rw-r--r--src/network/networkd-sriov.c2
-rw-r--r--src/network/tc/cake.c466
-rw-r--r--src/network/tc/cake.h61
-rw-r--r--src/network/test-networkd-conf.c124
-rw-r--r--src/nspawn/nspawn-gperf.gperf6
-rw-r--r--src/nspawn/nspawn-settings.c28
-rw-r--r--src/nspawn/nspawn-settings.h8
-rw-r--r--src/nspawn/nspawn.c58
-rw-r--r--src/partition/repart.c50
-rw-r--r--src/resolve/meson.build3
-rw-r--r--src/rpm/meson.build3
-rw-r--r--src/shared/base-filesystem.c21
-rw-r--r--src/shared/bootspec.c49
-rw-r--r--src/shared/bootspec.h2
-rw-r--r--src/shared/bus-unit-util.c1
-rw-r--r--src/shared/condition.c9
-rw-r--r--src/shared/conf-parser.c108
-rw-r--r--src/shared/conf-parser.h6
-rw-r--r--src/shared/dev-setup.c3
-rw-r--r--src/shared/gpt.c10
-rw-r--r--src/shared/meson.build2
-rw-r--r--src/shared/net-condition.c9
-rw-r--r--src/shared/netif-util.c101
-rw-r--r--src/shared/netif-util.h13
-rw-r--r--src/shared/varlink.c10
-rw-r--r--src/systemd/sd-event.h2
-rw-r--r--src/test/meson.build2
-rw-r--r--src/test/test-arphrd-util.c (renamed from src/test/test-arphrd-list.c)3
-rw-r--r--src/test/test-engine.c31
-rw-r--r--src/test/test-escape.c2
-rw-r--r--src/test/test-ether-addr-util.c133
-rw-r--r--src/test/test-execute.c5
-rw-r--r--src/test/test-fs-util.c3
-rw-r--r--src/timesync/meson.build3
-rw-r--r--src/udev/dmi_memory_id/dmi_memory_id.c2
-rw-r--r--src/udev/meson.build3
-rw-r--r--src/udev/net/link-config-gperf.gperf6
-rw-r--r--src/udev/net/link-config.c6
-rw-r--r--src/udev/test-udev-netlink.c2
-rw-r--r--src/udev/udev-builtin-net_id.c2
-rw-r--r--src/userdb/userdbctl.c322
-rw-r--r--src/userdb/userwork.c20
-rw-r--r--src/vconsole/meson.build3
-rw-r--r--src/xdg-autostart-generator/xdg-autostart-service.c1
-rw-r--r--sysctl.d/meson.build3
-rw-r--r--sysusers.d/meson.build3
l---------test/TEST-56-EXIT-TYPE/Makefile1
-rwxr-xr-xtest/TEST-56-EXIT-TYPE/test.sh9
-rw-r--r--test/fuzz/fuzz-network-parser/directives.network12
-rw-r--r--test/fuzz/fuzz-unit-file/directives.service1
-rw-r--r--test/test-execute/exec-umask-namespace.service12
-rw-r--r--test/test-network/conf/25-qdisc-cake.network12
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py16
-rwxr-xr-xtest/units/testsuite-20.sh17
-rw-r--r--test/units/testsuite-56.service6
-rwxr-xr-xtest/units/testsuite-56.sh79
-rwxr-xr-xtest/units/testsuite-58.sh32
-rwxr-xr-xtest/units/testsuite-65.sh8
-rw-r--r--tmpfiles.d/meson.build3
-rwxr-xr-xtools/meson-render-jinja2.py7
-rw-r--r--units/meson.build3
-rw-r--r--units/systemd-fsck-root.service.in2
201 files changed, 3967 insertions, 1872 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..123014908b
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,6 @@
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml
index 763a9b8025..5f2959871b 100644
--- a/.github/workflows/build_test.yml
+++ b/.github/workflows/build_test.yml
@@ -15,6 +15,9 @@ on:
jobs:
build:
runs-on: ubuntu-20.04
+ concurrency:
+ group: ${{ github.workflow }}-${{ matrix.env.COMPILER }}-${{ matrix.env.COMPILER_VERSION }}-${{ github.ref }}
+ cancel-in-progress: true
strategy:
fail-fast: false
matrix:
@@ -27,6 +30,6 @@ jobs:
env: ${{ matrix.env }}
steps:
- name: Repository checkout
- uses: actions/checkout@v1
+ uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Build check (${{ env.COMPILER }}-${{ env.COMPILER_VERSION }})
run: sudo -E .github/workflows/build_test.sh
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index 562f296399..2b5dba1757 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -17,32 +17,35 @@ on:
branches:
- main
jobs:
- Fuzzing:
- runs-on: ubuntu-latest
- if: github.repository == 'systemd/systemd'
- strategy:
- fail-fast: false
- matrix:
- sanitizer: [address, undefined, memory]
- steps:
- - name: Build Fuzzers (${{ matrix.sanitizer }})
- id: build
- uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
- with:
- oss-fuzz-project-name: 'systemd'
- dry-run: false
- allowed-broken-targets-percentage: 0
- sanitizer: ${{ matrix.sanitizer }}
- - name: Run Fuzzers (${{ matrix.sanitizer }})
- uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
- with:
- oss-fuzz-project-name: 'systemd'
- fuzz-seconds: 600
- dry-run: false
- sanitizer: ${{ matrix.sanitizer }}
- - name: Upload Crash
- uses: actions/upload-artifact@v1
- if: failure() && steps.build.outcome == 'success'
- with:
- name: ${{ matrix.sanitizer }}-artifacts
- path: ./out/artifacts
+ Fuzzing:
+ runs-on: ubuntu-latest
+ if: github.repository == 'systemd/systemd'
+ 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/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'systemd'
+ dry-run: false
+ allowed-broken-targets-percentage: 0
+ sanitizer: ${{ matrix.sanitizer }}
+ - name: Run Fuzzers (${{ matrix.sanitizer }})
+ uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'systemd'
+ fuzz-seconds: 600
+ dry-run: false
+ sanitizer: ${{ matrix.sanitizer }}
+ - name: Upload Crash
+ uses: actions/upload-artifact@27121b0bdffd731efa15d66772be8dc71245d074
+ if: failure() && steps.build.outcome == 'success'
+ with:
+ name: ${{ matrix.sanitizer }}-artifacts
+ path: ./out/artifacts
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000000..0f4a04ab53
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,42 @@
+name: "CodeQL"
+
+on:
+ # 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.
+ schedule:
+ - cron: '0 1 * * *'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ concurrency:
+ group: ${{ github.workflow }}-${{ matrix.language }}-${{ github.ref }}
+ cancel-in-progress: true
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'cpp', 'python' ]
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+
+ - run: sudo -E .github/workflows/unit_tests.sh SETUP
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index aad5d2157f..ee238c2fa7 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -11,7 +11,7 @@ jobs:
triage:
runs-on: ubuntu-latest
steps:
- - uses: actions/labeler@main
+ - uses: actions/labeler@69da01b8e0929f147b8943611bee75ee4175a49e
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/labeler.yml
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
index 1136c0523b..cb12b0a5d1 100644
--- a/.github/workflows/linter.yml
+++ b/.github/workflows/linter.yml
@@ -13,6 +13,9 @@ jobs:
build:
name: Lint Code Base
runs-on: ubuntu-latest
+ concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
steps:
- name: Repo checkout
@@ -22,7 +25,7 @@ jobs:
fetch-depth: 0
- name: Lint Code Base
- uses: github/super-linter@v3
+ uses: github/super-linter@fd9c4286d3de3fdd9258a395570cae287f13f974
env:
DEFAULT_BRANCH: main
# Excludes:
diff --git a/.github/workflows/mkosi.yml b/.github/workflows/mkosi.yml
index c70dc2ae10..11cb3f63b5 100644
--- a/.github/workflows/mkosi.yml
+++ b/.github/workflows/mkosi.yml
@@ -15,6 +15,9 @@ on:
jobs:
ci:
runs-on: ubuntu-20.04
+ concurrency:
+ group: ${{ github.workflow }}-${{ matrix.distro }}-${{ github.ref }}
+ cancel-in-progress: true
strategy:
fail-fast: false
matrix:
@@ -23,8 +26,7 @@ jobs:
- debian
- ubuntu
- fedora
- # Disabled until https://github.com/systemd/systemd/issues/21019 is fixed.
- # - opensuse
+ - opensuse
steps:
- uses: actions/checkout@v2
diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml
index 0b8c1782ea..2bb8400fdf 100644
--- a/.github/workflows/unit_tests.yml
+++ b/.github/workflows/unit_tests.yml
@@ -4,10 +4,6 @@
#
name: Unit tests
on:
- # On push/merge to main we only run the GCC job, to get coverage data uploaded to coveralls.io
- push:
- branches:
- - main
pull_request:
branches:
- main
@@ -15,6 +11,9 @@ on:
jobs:
build:
runs-on: ubuntu-20.04
+ concurrency:
+ group: ${{ github.workflow }}-${{ matrix.run_phase }}-${{ github.ref }}
+ cancel-in-progress: true
strategy:
fail-fast: false
matrix:
@@ -22,10 +21,7 @@ jobs:
steps:
- name: Repository checkout
uses: actions/checkout@v2
- if: github.event_name == 'pull_request' || matrix.run_phase == 'GCC'
- name: Install build dependencies
run: sudo -E .github/workflows/unit_tests.sh SETUP
- if: github.event_name == 'pull_request' || matrix.run_phase == 'GCC'
- name: Build & test (${{ matrix.run_phase }})
run: sudo -E .github/workflows/unit_tests.sh RUN_${{ matrix.run_phase }}
- if: github.event_name == 'pull_request' || matrix.run_phase == 'GCC'
diff --git a/TODO b/TODO
index 9c8bdfdaa5..bec042ab9c 100644
--- a/TODO
+++ b/TODO
@@ -81,6 +81,46 @@ Janitorial Clean-ups:
Features:
+* userdb: when synthesizing NSS records, pick "best" password from defined
+ passwords, not just the first. i.e. if there are multiple defined, prefer
+ unlocked over locked and prefer non-empty over empty.
+
+* maybe add a tool inspired by the GPT auto discovery spec that runs in the
+ initrd and rearranges the rootfs hierarchy via bind mounts, if
+ enabled. Specifically in some top-level dir /@auto/ it will look for
+ dirs/symlinks/subvolumes that are named after their purpose, and optionally
+ encode a version as well as assessment counters, and then mount them into the
+ file system tree to boot into, similar to how we do that for the gpt auto
+ logic. Maybe then bind mount the original root into /.superior or something
+ like that (so that update tools can look there). Further discussion in this
+ thread:
+ https://lists.freedesktop.org/archives/systemd-devel/2021-November/047059.html
+ The GPT dissection logic should automatically enable this tool whenever we
+ detect a specially marked root fs (i.e introduce a new generic root gpt type
+ for this, that is arch independent). The also implement this in the image
+ dissection logic, so that nspawn/RootImage= and so on grok it. Maybe make
+ generic enough so that it can also work for ostrees arrangements.
+
+* if a path ending in ".auto.d/" is set for RootDirectory=/RootImage= then do a
+ strverscmp() of everything inside that dir and use that. i.e. implement very
+ simple version control. Also use this in systemd-nspawn --image= and so on.
+
+* homed: while a home dir is not activated generate slightly different NSS
+ records for it, that reports the home dir as "/" and the shell as some binary
+ provided by us. Then, when an SSH login happens and SSH permits it our binary
+ is invoked. This binary can then talk to homed and activate the homedir if
+ it's not around yet, prompting the user for a password. Once that succeeded
+ we'll switch to the real user record, i.e. home dir and shell, and our tool
+ exec()s the latter. Net effect: ssh'ing into a homed account will just work:
+ we'll neatly prompt for the homedir's password if its needed. –– Building on
+ this we could take this even further: since this tool will potentially have
+ access to the client's ssh-agent (if ssh-agent forwarding is enabled) we
+ could implement SSH unlocking of a homedir with that: when enrolling a new
+ ssh pubkey in a user record we'd ask the ssh-agent to sign some random value
+ with the privkey, then use that as luks key to unlock the home dir. Will not
+ work for ECDSA keys since their signatures contain a random component, but
+ will work for RSA and Ed25519 keys.
+
* add tiny service that decrypts encrypted user records passed via initrd
credential logic and drops them into /run where nss-systemd can pick them up,
similar to /run/host/userdb/. Usecase: drop a root user JSON record there,
diff --git a/docs/CODING_STYLE.md b/docs/CODING_STYLE.md
index 4792f270c5..34e04ed735 100644
--- a/docs/CODING_STYLE.md
+++ b/docs/CODING_STYLE.md
@@ -297,7 +297,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later
with a more brutal `assert()`. We are more forgiving to public users than for
ourselves! Note that `assert()` and `assert_return()` really only should be
used for detecting programming errors, not for runtime errors. `assert()` and
- `assert_return()` by usage of `_likely_()` inform the compiler that he should
+ `assert_return()` by usage of `_likely_()` inform the compiler that it should
not expect these checks to fail, and they inform fellow programmers about the
expected validity and range of parameters.
diff --git a/docs/DISCOVERABLE_PARTITIONS.md b/docs/DISCOVERABLE_PARTITIONS.md
index 02ff1f6de3..af19b33a32 100644
--- a/docs/DISCOVERABLE_PARTITIONS.md
+++ b/docs/DISCOVERABLE_PARTITIONS.md
@@ -306,11 +306,9 @@ installation processes of Linux a bit more robust and self-descriptive.
### Why did you only define the root partition for these listed architectures?
-The automatic discovery of the root partition is defined to operate on the disk
-containing the current EFI System Partition (ESP). Since EFI only exists on
-x86, x86-64, ia64, ARM, LoongArch and RISC-V so far, we only defined root
-partition UUIDs for these architectures. Should EFI become more common on
-other architectures, we can define additional UUIDs for them.
+Please submit a patch that adds appropriate partition type UUIDs for the
+architecture of your choice should they be missing so far. The only reason they
+aren't defined yet is that nobody submitted them yet.
### Why define distinct root partition UUIDs for the various architectures?
diff --git a/docs/PORTING_TO_NEW_ARCHITECTURES.md b/docs/PORTING_TO_NEW_ARCHITECTURES.md
new file mode 100644
index 0000000000..964eccb854
--- /dev/null
+++ b/docs/PORTING_TO_NEW_ARCHITECTURES.md
@@ -0,0 +1,61 @@
+---
+title: Porting to New Architectures
+category: Contributing
+layout: default
+SPDX-License-Identifier: LGPL-2.1-or-later
+---
+
+# Porting systemd to New Architectures
+
+Here's a brief checklist of things to implement when porting systemd to a new
+architecture.
+
+1. Patch
+ [src/basic/architecture.h](https://github.com/systemd/systemd/blob/main/src/basic/architecture.h)
+ and
+ [src/basic/architecture.c](https://github.com/systemd/systemd/blob/main/src/basic/architecture.c)
+ to make your architecture known to systemd. Besides an `ARCHITECTURE_XYZ`
+ enumeration entry you need to provide an implementation of
+ `native_architecture()` and `uname_architecture()`.
+
+2. Patch
+ [src/shared/gpt.h](https://github.com/systemd/systemd/blob/main/src/shared/gpt.h)
+ and
+ [src/shared/gpt.c](https://github.com/systemd/systemd/blob/main/src/shared/gpt.c)
+ and define a new set of GPT partition type UUIDs for the root file system,
+ `/usr/` file system, and the matching Verity and Verity signature
+ partitions. Use `systemd-id128 new -p` to generate new suitable UUIDs you
+ can use for this. Make sure to register your new types in the various
+ functions in `gpt.c`. Also make sure to update the tables in
+ `docs/DISCOVERABLE_PARTITIONS.md` and `man/systemd-gpt-auto-generator.xml`
+ accordingly.
+
+3. If your architecture supports UEFI, make sure to update the `efi_arch`
+ variable logic in `meson.build` to be set to the right architecture string
+ as defined by the UEFI specification. (This ensures that `systemd-boot` will
+ be built as the appropriately named `BOOT<arch>.EFI` binary.) Also, if your
+ architecture uses a special boot protocol for the Linux kernel make sure to
+ implement it in `src/boot/efi/linux*.c`, so that the `systemd-stub` EFI stub
+ can work.
+
+4. Make sure to register the right system call numbers for your architecture in
+ `src/basic/missing_syscall_def.h`. systemd uses various system calls the
+ Linux kernel provides that are currently not wrapped by glibc (or are only
+ in very new glibc), and we need to know the right numbers for them. It might
+ also be necessary to tweak `src/basic/raw-clone.h`.
+
+5. Make sure the code in `src/shared/seccomp-util.c` properly understands the
+ local architecture and its system call quirks.
+
+6. If your architecture uses a `/lib64/` library directory, then make sure that
+ the `BaseFilesystem` table in `src/shared/base-filesystem.c` has an entry
+ for it so that it can be set up automatically if missing. This is useful to
+ support booting into OS trees that have an empty root directory with only
+ `/usr/` mounted in.
+
+7. If your architecture has a CPU opcode similar to x86' RDRAND consider adding
+ native support for it to `src/basic/random-util.c`'s `rdrand()` function.
+
+8. If your architecture supports VM virtualization and provides CPU opcodes
+ similar to x86' CPUID consider adding native support for detecting VMs this
+ way to `src/basic/virt.c`.
diff --git a/docs/PREDICTABLE_INTERFACE_NAMES.md b/docs/PREDICTABLE_INTERFACE_NAMES.md
index ddd7d29643..ae16d7211d 100644
--- a/docs/PREDICTABLE_INTERFACE_NAMES.md
+++ b/docs/PREDICTABLE_INTERFACE_NAMES.md
@@ -54,7 +54,7 @@ With this new scheme you now get:
* The same on all distributions that adopted systemd/udev
* It's easy to opt out of the scheme (see below)
-Does this have any drawbacks? Yes, it does. Previously it was practically guaranteed that hosts equipped with a single ethernet card only had a single `eth0` interface. With this new scheme in place, an administrator now has to check first what the local interface name is before he can invoke commands on it where previously he had a good chance that `eth0` was the right name.
+Does this have any drawbacks? Yes, it does. Previously it was practically guaranteed that hosts equipped with a single ethernet card only had a single `eth0` interface. With this new scheme in place, an administrator now has to check first what the local interface name is before they can invoke commands on it, where previously they had a good chance that `eth0` was the right name.
## I don't like this, how do I disable this?
diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md
index b90aaa702e..7bab5ec154 100644
--- a/docs/TRANSIENT-SETTINGS.md
+++ b/docs/TRANSIENT-SETTINGS.md
@@ -309,6 +309,7 @@ Most service unit settings are available for transient units.
✓ ExecStartPre=
✓ ExecStop=
✓ ExecStopPost=
+✓ ExitType=
✓ FileDescriptorStoreMax=
✓ GuessMainPID=
✓ NonBlocking=
diff --git a/docs/sysvinit/meson.build b/docs/sysvinit/meson.build
index 0a1935388a..cd3015ca4b 100644
--- a/docs/sysvinit/meson.build
+++ b/docs/sysvinit/meson.build
@@ -4,7 +4,6 @@ custom_target(
'README',
input : 'README.in',
output : 'README',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : conf.get('HAVE_SYSV_COMPAT') == 1,
install_dir : sysvinit_path)
diff --git a/man/meson.build b/man/meson.build
index f259ab8eb4..a06a601767 100644
--- a/man/meson.build
+++ b/man/meson.build
@@ -30,8 +30,7 @@ custom_entities_ent = custom_target(
'custom-entities.ent',
input : 'custom-entities.ent.in',
output : 'custom-entities.ent',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true)
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'])
man_pages = []
html_pages = []
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 93f2c90280..86bf7ef108 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -2235,7 +2235,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<literal>needs-reload</literal>. Package scripts may use the first to mark units for later restart when
a new version of the package is installed. Configuration management scripts may use the second to mark
units for a later reload when the configuration is adjusted. Those flags are not set by the manager,
- except to unset as appropriate when when the unit is stopped, restarted, or reloaded.</para>
+ except to unset as appropriate when the unit is stopped, restarted, or reloaded.</para>
<para><varname>JobTimeoutUSec</varname> maps directly to the corresponding configuration setting in the
unit file.</para>
@@ -2304,6 +2304,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s Type = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s ExitType = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s Restart = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PIDFile = '...';
@@ -2898,6 +2900,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property Type is not documented!-->
+ <!--property ExitType is not documented!-->
+
<!--property Restart is not documented!-->
<!--property PIDFile is not documented!-->
@@ -3428,6 +3432,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="Type"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ExitType"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Restart"/>
<variablelist class="dbus-property" generated="True" extra-ref="PIDFile"/>
diff --git a/man/os-release.xml b/man/os-release.xml
index 15608c437a..ef5ef8b2e1 100644
--- a/man/os-release.xml
+++ b/man/os-release.xml
@@ -35,22 +35,17 @@
<filename>/usr/lib/os-release</filename> files contain operating
system identification data.</para>
- <para>The basic file format of <filename>os-release</filename> is
- a newline-separated list of environment-like shell-compatible
- variable assignments. It is possible to source the configuration
- from shell scripts, however, beyond mere variable assignments, no
- shell features are supported (this means variable expansion is
- explicitly not supported), allowing applications to read the file
- without implementing a shell compatible execution engine. Variable
- assignment values must be enclosed in double or single quotes if
- they include spaces, semicolons or other special characters
- outside of A–Z, a–z, 0–9. Shell special characters ("$", quotes,
- backslash, backtick) must be escaped with backslashes, following
- shell style. All strings should be in UTF-8 format, and
- non-printable characters should not be used. It is not supported
- to concatenate multiple individually quoted strings. Lines
- beginning with "#" shall be ignored as comments. Blank lines are
- permitted and ignored.</para>
+ <para>The basic file format of <filename>os-release</filename> is a newline-separated list of
+ environment-like shell-compatible variable assignments. It is possible to source the configuration from
+ Bourne shell scripts, however, beyond mere variable assignments, no shell features are supported (this
+ means variable expansion is explicitly not supported), allowing applications to read the file without
+ implementing a shell compatible execution engine. Variable assignment values must be enclosed in double
+ or single quotes if they include spaces, semicolons or other special characters outside of A–Z, a–z,
+ 0–9. (Assignments that do not include these special characters may be enclosed in quotes too, but this is
+ optional.) Shell special characters ("$", quotes, backslash, backtick) must be escaped with backslashes,
+ following shell style. All strings should be in UTF-8 encoding, and non-printable characters should not
+ be used. It is not supported to concatenate multiple individually quoted strings. Lines beginning with
+ "#" shall be ignored as comments. Blank lines are permitted and ignored.</para>
<para>The file <filename>/etc/os-release</filename> takes
precedence over <filename>/usr/lib/os-release</filename>.
@@ -153,7 +148,8 @@
<listitem><para>A lower-case string (no spaces or other characters outside of 0–9, a–z, ".", "_"
and "-") identifying the operating system, excluding any version information and suitable for
processing by scripts or usage in generated filenames. If not set, a default of
- <literal>ID=linux</literal> may be used.</para>
+ <literal>ID=linux</literal> may be used. Note that even though this string may not include
+ characters that require shell quoting, quoting may nevertheless be used.</para>
<para>Examples: <literal>ID=fedora</literal>, <literal>ID=debian</literal>.</para></listitem>
</varlistentry>
diff --git a/man/repart.d.xml b/man/repart.d.xml
index 63d6d82694..93c85b6138 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -416,7 +416,7 @@
… suffixes (to the base of 1024). If <varname>SizeMinBytes=</varname> is specified the partition is
created at or grown to at least the specified size. If <varname>SizeMaxBytes=</varname> is specified
the partition is created at or grown to at most the specified size. The precise size is determined
- through the weight value value configured with <varname>Weight=</varname>, see above. When
+ through the weight value configured with <varname>Weight=</varname>, see above. When
<varname>SizeMinBytes=</varname> is set equal to <varname>SizeMaxBytes=</varname> the configured
weight has no effect as the partition is explicitly sized to the specified fixed value. Note that
partitions are never created smaller than 4096 bytes, and since partitions are never shrunk the
diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml
index ef6fa24b1d..c1994604b9 100644
--- a/man/resolved.conf.xml
+++ b/man/resolved.conf.xml
@@ -210,7 +210,7 @@
true all connections to the server will be encrypted. Note that this
mode requires a DNS server that supports DNS-over-TLS and has a valid
certificate. If the hostname was specified in <varname>DNS=</varname>
- by using the format format <literal>address#server_name</literal> it
+ by using the format <literal>address#server_name</literal> it
is used to validate its certificate and also to enable Server Name
Indication (SNI) when opening a TLS connection. Otherwise
the certificate is checked against the server's IP.
diff --git a/man/rules/meson.build b/man/rules/meson.build
index ad7cc41c98..7bf93fc16b 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -518,7 +518,9 @@ manpages = [
''],
['sd_event_add_inotify',
'3',
- ['sd_event_inotify_handler_t', 'sd_event_source_get_inotify_mask'],
+ ['sd_event_add_inotify_fd',
+ 'sd_event_inotify_handler_t',
+ 'sd_event_source_get_inotify_mask'],
''],
['sd_event_add_io',
'3',
diff --git a/man/sd_bus_add_object.xml b/man/sd_bus_add_object.xml
index 31a3344bbd..54683e4f11 100644
--- a/man/sd_bus_add_object.xml
+++ b/man/sd_bus_add_object.xml
@@ -508,7 +508,7 @@
<varlistentry>
<term><constant>SD_BUS_VTABLE_METHOD_NO_REPLY</constant></term>
- <listitem><para>Mark his vtable entry as a method that will not return a reply using the
+ <listitem><para>Mark this vtable entry as a method that will not return a reply using the
<constant>org.freedesktop.DBus.Method.NoReply</constant> annotation in introspection data.
</para></listitem>
</varlistentry>
diff --git a/man/sd_event_add_inotify.xml b/man/sd_event_add_inotify.xml
index 1681143eb1..d632bf7282 100644
--- a/man/sd_event_add_inotify.xml
+++ b/man/sd_event_add_inotify.xml
@@ -17,6 +17,7 @@
<refnamediv>
<refname>sd_event_add_inotify</refname>
+ <refname>sd_event_add_inotify_fd</refname>
<refname>sd_event_source_get_inotify_mask</refname>
<refname>sd_event_inotify_handler_t</refname>
@@ -47,6 +48,16 @@
</funcprototype>
<funcprototype>
+ <funcdef>int <function>sd_event_add_inotify_fd</function></funcdef>
+ <paramdef>sd_event *<parameter>event</parameter></paramdef>
+ <paramdef>sd_event_source **<parameter>source</parameter></paramdef>
+ <paramdef>int <parameter>fd</parameter></paramdef>
+ <paramdef>uint32_t <parameter>mask</parameter></paramdef>
+ <paramdef>sd_event_inotify_handler_t <parameter>handler</parameter></paramdef>
+ <paramdef>void *<parameter>userdata</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
<funcdef>int <function>sd_event_source_get_inotify_mask</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
<paramdef>uint32_t *<parameter>mask</parameter></paramdef>
@@ -71,6 +82,11 @@
<citerefentry project='man-pages'><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
further information.</para>
+ <para><function>sd_event_add_inotify_fd()</function> is identical to
+ <function>sd_event_add_inotify()</function>, except that it takes a file descriptor to an inode (possibly
+ an <constant>O_PATH</constant> one, but any other will do too) instead of a path in the file
+ system.</para>
+
<para>If multiple event sources are installed for the same inode the backing inotify watch descriptor is
automatically shared. The mask parameter may contain any flag defined by the inotify API, with the exception of
<constant>IN_MASK_ADD</constant>.</para>
@@ -157,6 +173,19 @@
<listitem><para>The passed event source is not an inotify process event source.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><constant>-EBADF</constant></term>
+
+ <listitem><para>The passed file descriptor is not valid.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOSYS</constant></term>
+
+ <listitem><para><function>sd_event_add_inotify_fd()</function> was called without
+ <filename>/proc/</filename> mounted.</para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect2>
</refsect1>
diff --git a/man/sd_event_source_set_ratelimit.xml b/man/sd_event_source_set_ratelimit.xml
index ac8529074a..37354a09f6 100644
--- a/man/sd_event_source_set_ratelimit.xml
+++ b/man/sd_event_source_set_ratelimit.xml
@@ -19,6 +19,7 @@
<refname>sd_event_source_set_ratelimit</refname>
<refname>sd_event_source_get_ratelimit</refname>
<refname>sd_event_source_is_ratelimited</refname>
+ <refname>sd_event_source_set_ratelimit_expire_callback</refname>
<refpurpose>Configure rate limiting on event sources</refpurpose>
</refnamediv>
@@ -46,6 +47,12 @@
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
</funcprototype>
+ <funcprototype>
+ <funcdef>int <function>sd_event_source_set_ratelimit_expire_callback</function></funcdef>
+ <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+ <paramdef>sd_event_handler_t<parameter>callback</parameter></paramdef>
+ </funcprototype>
+
</funcsynopsis>
</refsynopsisdiv>
@@ -78,6 +85,10 @@
is currently affected by rate limiting, i.e. it has recently hit the rate limit and is currently
temporarily disabled due to that.</para>
+ <para><function>sd_event_source_set_ratelimit_expire_callback</function> may be used to set a callback
+ function that is invoked every time the event source leaves rate limited state. Note that function is
+ called in the same event loop iteration in which state transition occured.</para>
+
<para>Rate limiting is currently implemented for I/O, timer, signal, defer and inotify event
sources.</para>
</refsect1>
@@ -85,11 +96,12 @@
<refsect1>
<title>Return Value</title>
- <para>On success, <function>sd_event_source_set_ratelimit()</function> and
- <function>sd_event_source_get_ratelimit()</function> return a non-negative integer. On failure, they
- return a negative errno-style error code. <function>sd_event_source_is_ratelimited</function> returns
- zero if rate limiting is currently not in effect and greater than zero if it is in effect; it returns a
- negative errno-style error code on failure.</para>
+ <para>On success, <function>sd_event_source_set_ratelimit()</function>,
+ <function>sd_event_source_set_ratelimit_expire_callback</function> and
+ <function>sd_event_source_get_ratelimit()</function> return a non-negative integer. On failure, they return
+ a negative errno-style error code. <function>sd_event_source_is_ratelimited</function> returns zero if rate
+ limiting is currently not in effect and greater than zero if it is in effect; it returns a negative
+ errno-style error code on failure.</para>
<refsect2>
<title>Errors</title>
diff --git a/man/systemctl.xml b/man/systemctl.xml
index b6be21ed87..1c14909523 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -1660,8 +1660,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
completion is implemented for property names.</para>
<para>For the manager itself,
- <command>systemctl show</command> will show all available
- properties. Those properties are documented in
+ <command>systemctl show</command>
+ will show all available properties, most of which are derived or closely match the options described in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml
index e072d66131..4c3b6d173b 100644
--- a/man/systemd-analyze.xml
+++ b/man/systemd-analyze.xml
@@ -551,7 +551,8 @@ NAutoVTs=8
<title><command>systemd-analyze verify <replaceable>FILE</replaceable>...</command></title>
<para>This command will load unit files and print warnings if any errors are detected. Files specified
- on the command line will be loaded, but also any other units referenced by them. The full unit search
+ on the command line will be loaded, but also any other units referenced by them. A unit's name on disk
+ can be overridden by specifying an alias after a colon; see below for an example. The full unit search
path is formed by combining the directories for all command line arguments, and the usual unit load
paths. The variable <varname>$SYSTEMD_UNIT_PATH</varname> is supported, and may be used to replace or
augment the compiled in set of unit load paths; see
@@ -613,6 +614,27 @@ Service a.service not loaded, a.socket cannot be started.
Service b@0.service not loaded, b.socket cannot be started.
</programlisting>
</example>
+
+ <example>
+ <title>Aliasing a unit</title>
+
+ <programlisting>$ cat /tmp/source
+[Unit]
+Description=Hostname printer
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/echo %H
+MysteryKey=true
+
+$ systemd-analyze verify /tmp/source
+Failed to prepare filename /tmp/source: Invalid argument
+
+$ systemd-analyze verify /tmp/source:alias.service
+/tmp/systemd-analyze-XXXXXX/alias.service:7: Unknown key name 'MysteryKey' in section 'Service', ignoring.
+ </programlisting>
+ </example>
+
</refsect2>
<refsect2>
diff --git a/man/systemd-fsck@.service.xml b/man/systemd-fsck@.service.xml
index 035382913f..aa0d8b12ae 100644
--- a/man/systemd-fsck@.service.xml
+++ b/man/systemd-fsck@.service.xml
@@ -55,9 +55,18 @@
the filesystem should actually be checked based on the time since
last check, number of mounts, unclean unmount, etc.</para>
- <para>If a file system check fails for a service without
- <option>nofail</option>, emergency mode is activated, by isolating
- to <filename>emergency.target</filename>.</para>
+ <para><filename>systemd-fsck-root.service</filename> will activate
+ <filename>reboot.target</filename> if <filename>/sbin/fsck</filename>
+ returns the "System should reboot" condition, or
+ <filename>emergency.target</filename> if <filename>/sbin/fsck</filename>
+ returns the "Filesystem errors left uncorrected" condition.</para>
+
+ <para><filename>systemd-fsck@.service</filename> will fail if
+ <filename>/sbin/fsck</filename> returns with either "System should reboot"
+ or "Filesystem errors left uncorrected" conditions. For filesystems
+ listed in <filename>/etc/fstab</filename> without <literal>nofail</literal>
+ or <literal>noauto</literal> options, <literal>local-fs.target</literal>
+ will then activate <filename>emergency.target</filename>.</para>
</refsect1>
<refsect1>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index c9cae4b9d3..aea7116e29 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -730,10 +730,13 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<listitem><para>Set the SELinux security context of the executed process. If set, this will override the
automated domain transition. However, the policy still needs to authorize the transition. This directive is
- ignored if SELinux is disabled. If prefixed by <literal>-</literal>, all errors will be ignored. This does not
- affect commands prefixed with <literal>+</literal>. See <citerefentry
- project='die-net'><refentrytitle>setexeccon</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
- details.</para></listitem>
+ ignored if SELinux is disabled. If prefixed by <literal>-</literal>, failing to set the SELinux
+ security context will be ignored, but it's still possible that the subsequent
+ <function>execve()</function> may fail if the policy doesn't allow the transition for the
+ non-overridden context. This does not affect commands prefixed with <literal>+</literal>. See
+ <citerefentry
+ project='die-net'><refentrytitle>setexeccon</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ for details.</para></listitem>
</varlistentry>
<varlistentry>
@@ -3163,7 +3166,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
configuration, with just a few environment variables. The user manager inherits environment variables as
any other system service, but in addition may receive additional environment variables from PAM, and,
typically, additional imported variables when the user starts a graphical session. It is recommended to
- keep the environment blocks in both the system and user managers managers lean. Importing all variables
+ keep the environment blocks in both the system and user managers lean. Importing all variables
inherited by the graphical session or by one of the user shells is strongly discouraged.</para>
<para>Hint: <command>systemd-run -P env</command> and <command>systemd-run --user -P env</command> print
diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml
index 578e074f57..554b517235 100644
--- a/man/systemd.journal-fields.xml
+++ b/man/systemd.journal-fields.xml
@@ -128,7 +128,7 @@
datagram. This field is only included if the <varname>MESSAGE=</varname>
field was modified compared to the original payload or the timestamp could
not be located properly and is not included in
- <varname>SYSLOG_TIMESTAMP=</varname>. Message truncation occurs when when
+ <varname>SYSLOG_TIMESTAMP=</varname>. Message truncation occurs when
the message contains leading or trailing whitespace (trailing and leading
whitespace is stripped), or it contains an embedded
<constant>NUL</constant> byte (the <constant>NUL</constant> byte and
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 610f491b9a..9cbd99a0ef 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -3,7 +3,8 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd.netdev" conditional='ENABLE_NETWORKD'>
+<refentry id="systemd.netdev" conditional='ENABLE_NETWORKD'
+ xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd.network</title>
@@ -197,78 +198,16 @@
<refsect1>
<title>[Match] Section Options</title>
- <para>A virtual network device is only created if the
- [Match] section matches the current
- environment, or if the section is empty. The following keys are
- accepted:</para>
+ <para>A virtual network device is only created if the [Match] section matches the current
+ environment, or if the section is empty. The following keys are accepted:</para>
<variablelist class='network-directives'>
- <varlistentry>
- <term><varname>Host=</varname></term>
- <listitem>
- <para>Matches against the hostname or machine ID of the host. See
- <literal>ConditionHost=</literal> in
- <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Virtualization=</varname></term>
- <listitem>
- <para>Checks whether the system is executed in a virtualized environment and optionally test
- whether it is a specific implementation. See <literal>ConditionVirtualization=</literal> in
- <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>KernelCommandLine=</varname></term>
- <listitem>
- <para>Checks whether a specific kernel command line option is set. See
- <literal>ConditionKernelCommandLine=</literal> in
- <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>KernelVersion=</varname></term>
- <listitem>
- <para>Checks whether the kernel version (as reported by <command>uname -r</command>) matches a
- certain expression. See <literal>ConditionKernelVersion=</literal> in
- <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Architecture=</varname></term>
- <listitem>
- <para>Checks whether the system is running on a specific architecture. See
- <literal>ConditionArchitecture=</literal> in
- <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
- </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Firmware=</varname></term>
- <listitem>
- <para>Checks whether the system is running on a machine with the specified firmware. See
- <literal>ConditionFirmware=</literal> in
- <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
- </para>
- </listitem>
- </varlistentry>
+ <xi:include href="systemd.link.xml" xpointer="host" />
+ <xi:include href="systemd.link.xml" xpointer="virtualization" />
+ <xi:include href="systemd.link.xml" xpointer="kernel-command-line" />
+ <xi:include href="systemd.link.xml" xpointer="kernel-version" />
+ <xi:include href="systemd.link.xml" xpointer="architecture" />
+ <xi:include href="systemd.link.xml" xpointer="firmware" />
</variablelist>
</refsect1>
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 49814a05eb..ab08797a9e 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -132,6 +132,7 @@
<xi:include href="systemd.link.xml" xpointer="kernel-command-line" />
<xi:include href="systemd.link.xml" xpointer="kernel-version" />
<xi:include href="systemd.link.xml" xpointer="architecture" />
+ <xi:include href="systemd.link.xml" xpointer="firmware" />
</variablelist>
</refsect1>
@@ -1150,7 +1151,7 @@ Table=1234</programlisting></para>
<varlistentry>
<term><varname>Label=</varname></term>
<listitem>
- <para>The label for the prefix, an unsigned integer in the range 0–4294967294.
+ <para>The label for the prefix, an unsigned integer in the range 0…4294967294.
0xffffffff is reserved. This setting is mandatory.</para>
</listitem>
</varlistentry>
@@ -1552,7 +1553,7 @@ Table=1234</programlisting></para>
<term><varname>TCPAdvertisedMaximumSegmentSize=</varname></term>
<listitem>
<para>Specifies the Path MSS (in bytes) hints given on TCP layer. The usual suffixes K, M, G, are
- supported and are understood to the base of 1024. An unsigned integer in the range 1–4294967294.
+ supported and are understood to the base of 1024. An unsigned integer in the range 1…4294967294.
When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
@@ -2049,7 +2050,7 @@ Table=1234</programlisting></para>
<para>Takes an IPv6 address with prefix length in the same format as the
<varname>Address=</varname> in the [Network] section. The DHCPv6 client will include a prefix
hint in the DHCPv6 solicitation sent to the server. The prefix length must be in the range
- 1–128. Defaults to unset.</para>
+ 1…128. Defaults to unset.</para>
</listitem>
</varlistentry>
@@ -2296,6 +2297,22 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
</varlistentry>
<varlistentry>
+ <term><varname>UseGateway=</varname></term>
+ <listitem>
+ <para>When true (the default), the router address will be configured as the default gateway.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>UseRoutePrefix=</varname></term>
+ <listitem>
+ <para>When true (the default), the routes corresponding to the route prefixes received in
+ the Router Advertisement will be configured.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>UseAutonomousPrefix=</varname></term>
<listitem>
<para>When true (the default), the autonomous prefix received in the Router Advertisement will be used and take
@@ -2541,7 +2558,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<para>Takes a boolean value. When <literal>yes</literal>, DHCP server socket will be bound
to its network interface and all socket communication will be restricted to this interface.
Defaults to <literal>yes</literal>, except if <varname>RelayTarget=</varname> is used (see below),
- in which case it defaults defaults to <literal>no</literal>.</para>
+ in which case it defaults to <literal>no</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -3095,7 +3112,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<varlistentry>
<term><varname>Termination=</varname></term>
<listitem>
- <para>Takes a boolean or a termination resistor value in ohm in the range 0–65535. When
+ <para>Takes a boolean or a termination resistor value in ohm in the range 0…65535. When
<literal>yes</literal>, the termination resistor is set to 120 ohm. When
<literal>no</literal> or <literal>0</literal> is set, the termination resistor is disabled.
When unset, the kernel's default will be used.</para>
@@ -3209,7 +3226,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<term><varname>PacketLimit=</varname></term>
<listitem>
<para>Specifies the maximum number of packets the qdisc may hold queued at a time.
- An unsigned integer in the range 0–4294967294. Defaults to 1000.</para>
+ An unsigned integer in the range 0…4294967294. Defaults to 1000.</para>
</listitem>
</varlistentry>
@@ -3358,7 +3375,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<term><varname>PacketLimit=</varname></term>
<listitem>
<para>Specifies the hard limit on the queue size in number of packets. When this limit is reached,
- incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and
+ incoming packets are dropped. An unsigned integer in the range 0…4294967294. Defaults to unset and
kernel's default is used.</para>
</listitem>
</varlistentry>
@@ -3420,7 +3437,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<para>Specifies the hard limit on the number of packets in the FIFO queue. The size limit prevents
overflow in case the kernel is unable to dequeue packets as quickly as it receives them. When this
limit is reached, incoming packets are dropped. An unsigned integer in the range
- 0–4294967294. Defaults to unset and kernel's default is used.</para>
+ 0…4294967294. Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
</variablelist>
@@ -3464,21 +3481,237 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<xi:include href="tc.xml" xpointer="qdisc-handle" />
<varlistentry>
+ <term><varname>Bandwidth=</varname></term>
+ <listitem>
+ <para>Specifies the shaper bandwidth. When suffixed with K, M, or G, the specified size is
+ parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of 1000. Defaults to
+ unset and kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>AutoRateIngress=</varname></term>
+ <listitem>
+ <para>Takes a boolean value. Enables automatic capacity estimation based on traffic arriving
+ at this qdisc. This is most likely to be useful with cellular links, which tend to change
+ quality randomly. If this setting is enabled, the <varname>Bandwidth=</varname> setting is
+ used as an initial estimate. Defaults to unset, and the kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>OverheadBytes=</varname></term>
<listitem>
- <para>Specifies that bytes to be addeded to the size of each packet. Bytes may be negative. Takes
- an integer in the range from -64 to 256. Defaults to unset and kernel's default is used.</para>
+ <para>Specifies that bytes to be addeded to the size of each packet. Bytes may be negative.
+ Takes an integer in the range -64…256. Defaults to unset and kernel's default is used.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
- <term><varname>Bandwidth=</varname></term>
+ <term><varname>MPUBytes=</varname></term>
<listitem>
- <para>Specifies the shaper bandwidth. When suffixed with K, M, or G, the specified size is
- parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of 1000. Defaults to
- unset and kernel's default is used.</para>
+ <para>Rounds each packet (including overhead) up to the specified bytes. Takes an integer in
+ the range 1…256. Defaults to unset and kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>CompensationMode=</varname></term>
+ <listitem>
+ <para>Takes one of <literal>none</literal>, <literal>atm</literal>, or <literal>ptm</literal>.
+ Specifies the compensation mode for overhead calculation. When <literal>none</literal>, no
+ compensation is taken into account. When <literal>atm</literal>, enables the compensation for
+ ATM cell framing, which is normally found on ADSL links. When <literal>ptm</literal>, enables
+ the compensation for PTM encoding, which is normally found on VDSL2 links and uses a 64b/65b
+ encoding scheme. Defaults to unset and the kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>UseRawPacketSize=</varname></term>
+ <listitem>
+ <para>Takes a boolean value. When true, the packet size reported by the Linux kernel will be
+ used, instead of the underlying IP packet size. Defaults to unset, and the kernel's default
+ is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>FlowIsolationMode=</varname></term>
+ <listitem>
+ <para>CAKE places packets from different flows into different queues, then packets from each
+ queue are delivered fairly. This specifies whether the fairness is based on source address,
+ destination address, individual flows, or any combination of those. The available values are:
+ </para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>none</option></term>
+ <listitem><para>
+ The flow isolation is disabled, and all traffic passes through a single queue.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>src-host</option></term>
+ <listitem><para>
+ Flows are defined only by source address. Equivalnet to the <literal>srchost</literal>
+ option for <command>tc qdisc</command> command. See also
+ <citerefentry project='man-pages'><refentrytitle>tc-cake</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>dst-host</option></term>
+ <listitem><para>
+ Flows are defined only by destination address. Equivalnet to the
+ <literal>srchost</literal> option for <command>tc qdisc</command> command. See also
+ <citerefentry project='man-pages'><refentrytitle>tc-cake</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>hosts</option></term>
+ <listitem><para>
+ Flows are defined by source-destination host pairs. Equivalent to the same option for
+ <command>tc qdisc</command> command. See also
+ <citerefentry project='man-pages'><refentrytitle>tc-cake</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>flows</option></term>
+ <listitem><para>
+ Flows are defined by the entire 5-tuple of source address, destination address,
+ transport protocol, source port and destination port. Equivalent to the same option for
+ <command>tc qdisc</command> command. See also
+ <citerefentry project='man-pages'><refentrytitle>tc-cake</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>dual-src-host</option></term>
+ <listitem><para>
+ Flows are defined by the 5-tuple (see <literal>flows</literal> in the above), and
+ fairness is applied first over source addresses, then over individual flows. Equivalnet
+ to the <literal>dual-srchost</literal> option for <command>tc qdisc</command> command.
+ See also
+ <citerefentry project='man-pages'><refentrytitle>tc-cake</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>dual-dst-host</option></term>
+ <listitem><para>
+ Flows are defined by the 5-tuple (see <literal>flows</literal> in the above), and
+ fairness is applied first over destination addresses, then over individual flows.
+ Equivalnet to the <literal>dual-dsthost</literal> option for
+ <command>tc qdisc</command> command. See also
+ <citerefentry project='man-pages'><refentrytitle>tc-cake</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>triple</option></term>
+ <listitem><para>
+ Flows are defined by the 5-tuple (see <literal>flows</literal>), and fairness is
+ applied over source and destination addresses, and also over individual flows.
+ Equivalnet to the <literal>triple-isolate</literal> option for
+ <command>tc qdisc</command> command. See also
+ <citerefentry project='man-pages'><refentrytitle>tc-cake</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Defaults to unset and the kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>NAT=</varname></term>
+ <listitem>
+ <para>Takes a boolean value. When true, CAKE performs a NAT lookup before applying
+ flow-isolation rules, to determine the true addresses and port numbers of the packet, to
+ improve fairness between hosts inside the NAT. This has no practical effect when
+ <varname>FlowIsolationMode=</varname> is <literal>none</literal> or <literal>flows</literal>,
+ or if NAT is performed on a different host. Defaults to unset, and the kernel's default is
+ used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PriorityQueueingPreset=</varname></term>
+ <listitem>
+ <para>CAKE divides traffic into <literal>tins</literal>, and each tin has its own independent
+ set of flow-isolation queues, bandwidth threshold, and priority. This specifies the preset of
+ tin profiles. The available values are:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>besteffort</option></term>
+ <listitem><para>
+ Disables priority queueing by placing all traffic in one tin.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>precedence</option></term>
+ <listitem><para>
+ Enables priority queueing based on the legacy interpretation of TOS
+ <literal>Precedence</literal> field. Use of this preset on the modern Internet is
+ firmly discouraged.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>diffserv8</option></term>
+ <listitem><para>
+ Enables priority queueing based on the Differentiated Service
+ (<literal>DiffServ</literal>) field with eight tins: Background Traffic, High
+ Throughput, Best Effort, Video Streaming, Low Latency Transactions, Interactive Shell,
+ Minimum Latency, and Network Control.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>diffserv4</option></term>
+ <listitem><para>
+ Enables priority queueing based on the Differentiated Service
+ (<literal>DiffServ</literal>) field with four tins: Background Traffic, Best Effort,
+ Streaming Media, and Latency Sensitive.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>diffserv3</option></term>
+ <listitem><para>
+ Enables priority queueing based on the Differentiated Service
+ (<literal>DiffServ</literal>) field with three tins: Background Traffic, Best Effort,
+ and Latency Sensitive.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Defaults to unset, and the kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>FirewallMark=</varname></term>
+ <listitem>
+ <para>Takes an integer in the range 1…4294967295. When specified, firewall-mark-based
+ overriding of CAKE's tin selection is enabled. Defaults to unset, and the kernel's default is
+ used.</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>Wash=</varname></term>
+ <listitem>
+ <para>Takes a boolean value. When true, CAKE clears the DSCP fields, except for ECN bits, of
+ any packet passing through CAKE. Defaults to unset, and the kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>SplitGSO=</varname></term>
+ <listitem>
+ <para>Takes a boolean value. When true, CAKE will split General Segmentation Offload (GSO)
+ super-packets into their on-the-wire components and dequeue them individually. Defaults to
+ unset, and the kernel's default is used.</para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
@@ -3495,7 +3728,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<term><varname>PacketLimit=</varname></term>
<listitem>
<para>Specifies the hard limit on the queue size in number of packets. When this limit is reached,
- incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and
+ incoming packets are dropped. An unsigned integer in the range 0…4294967294. Defaults to unset and
kernel's default is used.</para>
</listitem>
</varlistentry>
@@ -3579,7 +3812,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<varlistentry>
<term><varname>Bands=</varname></term>
<listitem>
- <para>Specifies the number of bands. An unsigned integer in the range 1–16. This value has to be at
+ <para>Specifies the number of bands. An unsigned integer in the range 1…16. This value has to be at
least large enough to cover the strict bands specified through the <varname>StrictBands=</varname>
and bandwidth-sharing bands specified in <varname>QuantumBytes=</varname>.</para>
</listitem>
@@ -3589,7 +3822,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<term><varname>StrictBands=</varname></term>
<listitem>
<para>Specifies the number of bands that should be created in strict mode. An unsigned integer in
- the range 1–16.</para>
+ the range 1…16.</para>
</listitem>
</varlistentry>
@@ -3961,7 +4194,7 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<term><varname>PacketLimit=</varname></term>
<listitem>
<para>Specifies the hard limit on the queue size in number of packets. When this limit is reached,
- incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and
+ incoming packets are dropped. An unsigned integer in the range 0…4294967294. Defaults to unset and
kernel's default is used.</para>
</listitem>
</varlistentry>
@@ -4018,8 +4251,8 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
<varlistentry>
<term><varname>VLAN=</varname></term>
<listitem>
- <para>The VLAN ID allowed on the port. This can be either a single ID or a range M-N. VLAN IDs are valid
- from 1 to 4094.</para>
+ <para>The VLAN ID allowed on the port. This can be either a single ID or a range M-N. Takes
+ an integer in the range 1…4094.</para>
</listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml
index bb9bf4b5d9..15cfd4bc76 100644
--- a/man/systemd.nspawn.xml
+++ b/man/systemd.nspawn.xml
@@ -33,7 +33,7 @@
<title>Description</title>
<para>An nspawn container settings file (suffix <filename>.nspawn</filename>) contains runtime
- configuration for a local container, and is used used by
+ configuration for a local container, and is used by
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
Files of this type are named after the containers they define settings for. They are optional, and only
required for containers whose execution environment shall differ from the defaults. Files of this type
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index 5042066d0d..95cb0aca3d 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -256,6 +256,31 @@
</varlistentry>
<varlistentry>
+ <term><varname>ExitType=</varname></term>
+
+ <listitem>
+ <para>Specifies when the manager should consider the service to be finished. One of <option>main</option> or
+ <option>cgroup</option>:</para>
+
+ <itemizedlist>
+ <listitem><para>If set to <option>main</option> (the default), the service manager
+ will consider the unit stopped when the main process, which is determined according to the
+ <varname>Type=</varname>, exits. Consequently, it cannot be used with
+ <varname>Type=</varname><option>oneshot</option>.</para></listitem>
+
+ <listitem><para>If set to <option>cgroup</option>, the service will be considered running as long as at
+ least one process in the cgroup has not exited.</para></listitem>
+ </itemizedlist>
+
+ <para>It is generally recommended to use <varname>ExitType=</varname><option>main</option> when a service has
+ a known forking model and a main process can reliably be determined. <varname>ExitType=</varname>
+ <option>cgroup</option> is meant for applications whose forking model is not known ahead of time and which
+ might not have a specific main process. It is well suited for transient or automatically generated services,
+ such as graphical applications inside of a desktop environment.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>RemainAfterExit=</varname></term>
<listitem><para>Takes a boolean value that specifies whether
diff --git a/man/userdbctl.xml b/man/userdbctl.xml
index 611c425b79..6a01e9d179 100644
--- a/man/userdbctl.xml
+++ b/man/userdbctl.xml
@@ -72,6 +72,16 @@
</varlistentry>
<varlistentry>
+ <term><option>--json=</option><replaceable>FORMAT</replaceable></term>
+
+ <listitem><para>Selects JSON out mode (like <option>--output=json</option>) and selects the precise
+ display mode. Takes one of <literal>pretty</literal> or <literal>short</literal>. If
+ <literal>pretty</literal> human-friendly whitespace and newlines are inserted in the output to make
+ the JSON data more readable. If <literal>short</literal> all superfluous whitespace is
+ suppressed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>--service=</option><replaceable>SERVICE</replaceable><optional>:<replaceable>SERVICE…</replaceable></optional></term>
<term><option>-s</option> <replaceable>SERVICE</replaceable>:<replaceable>SERVICE…</replaceable></term>
@@ -128,6 +138,22 @@
off.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--multiplexer=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Controls whether to do lookups via the multiplexer service (if specified as true, the
+ default) or do lookups in the client (if specified as false). Using the multiplexer service is
+ typically preferable, since it runs in a locked down sandbox.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--chain</option></term>
+
+ <listitem><para>When used with the <command>ssh-authorized-keys</command> command, this will allow
+ passing an additional command line after the user name that is chain executed after the lookup
+ completed. This allows chaining multiple tools that show SSH authorized keys.</para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="help" />
@@ -183,8 +209,8 @@
<varlistentry>
<term><command>ssh-authorized-keys</command></term>
- <listitem><para>This operation is not a public, user-facing interface. It is used to allow the SSH daemon to pick
- up authorized keys from user records, see below.</para></listitem>
+ <listitem><para>Show SSH authorized keys for this account. This command is intended to be used to
+ allow the SSH daemon to pick up authorized keys from user records, see below.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
@@ -283,6 +309,19 @@
AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u
AuthorizedKeysCommandUser root
…</programlisting>
+
+ <para>Sometimes it's useful to allow chain invocation of another program to list SSH authorized keys. By
+ using the <option>--chain</option> such a tool may be chain executed by <command>userdbctl
+ ssh-authorized-keys</command> once a lookup completes (regardless if an SSH key was found or
+ not). Example:</para>
+
+ <programlisting>…
+AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u --chain /usr/bin/othertool %u
+AuthorizedKeysCommandUser root
+…</programlisting>
+
+ <para>The above will first query the userdb database for SSH keys, and then chain execute
+ <command>/usr/bin/othertool</command> to also be queried.</para>
</refsect1>
<refsect1>
diff --git a/meson.build b/meson.build
index f1be04e5ae..0c4e5a711b 100644
--- a/meson.build
+++ b/meson.build
@@ -387,11 +387,16 @@ endif
# --as-needed and --no-undefined are provided by meson by default,
# run 'meson configure' to see what is enabled
possible_link_flags = [
- '-Wl,-z,relro',
+ '-Wl,--fatal-warnings',
'-Wl,-z,now',
+ '-Wl,-z,relro',
'-fstack-protector',
]
+if get_option('b_sanitize') == 'none'
+ possible_link_flags += '-Wl,--warn-common'
+endif
+
if cc.get_id() == 'clang'
possible_common_cc_flags += [
'-Wno-typedef-redefinition',
@@ -1636,39 +1641,8 @@ conf.set10('SYSTEMD_SLOW_TESTS_DEFAULT', slow_tests)
#####################################################################
-if get_option('efi')
- efi_arch = host_machine.cpu_family()
-
- if efi_arch == 'x86'
- EFI_MACHINE_TYPE_NAME = 'ia32'
- gnu_efi_arch = 'ia32'
- elif efi_arch == 'x86_64'
- EFI_MACHINE_TYPE_NAME = 'x64'
- gnu_efi_arch = 'x86_64'
- elif efi_arch == 'arm'
- EFI_MACHINE_TYPE_NAME = 'arm'
- gnu_efi_arch = 'arm'
- elif efi_arch == 'aarch64'
- EFI_MACHINE_TYPE_NAME = 'aa64'
- gnu_efi_arch = 'aarch64'
- elif efi_arch == 'riscv64'
- EFI_MACHINE_TYPE_NAME = 'riscv64'
- gnu_efi_arch = 'riscv64'
- else
- EFI_MACHINE_TYPE_NAME = ''
- gnu_efi_arch = ''
- endif
-
- have = true
- conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME)
-else
- have = false
-endif
-conf.set10('ENABLE_EFI', have)
-
subdir('src/fundamental')
subdir('src/boot/efi')
-conf.set10('HAVE_GNU_EFI', have_gnu_efi)
############################################################
@@ -3882,19 +3856,14 @@ summary({
# CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS}
# LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS}
-if conf.get('ENABLE_EFI') == 1
- summary({'EFI arch' : efi_arch},
+if conf.get('ENABLE_EFI') == 1 and conf.get('HAVE_GNU_EFI') == 1
+ summary({
+ 'EFI machine type' : efi_arch[0],
+ 'EFI CC' : '@0@'.format(' '.join(efi_cc)),
+ 'EFI lds' : efi_lds,
+ 'EFI crt0' : efi_crt0,
+ 'EFI include directory' : efi_incdir},
section : 'Extensible Firmware Interface')
-
- if have_gnu_efi
- summary({
- 'EFI machine type' : EFI_MACHINE_TYPE_NAME,
- 'EFI CC' : '@0@'.format(' '.join(efi_cc)),
- 'EFI lds' : efi_lds,
- 'EFI crt0' : efi_crt0,
- 'EFI include directory' : efi_incdir},
- section : 'Extensible Firmware Interface')
- endif
endif
found = []
@@ -3942,11 +3911,11 @@ foreach tuple : [
# components
['backlight'],
['binfmt'],
- ['bpf-framework', conf.get('BPF_FRAMEWORK') == 1],
+ ['bpf-framework'],
['coredump'],
['environment.d'],
['efi'],
- ['gnu-efi', have_gnu_efi],
+ ['gnu-efi'],
['firstboot'],
['hibernate'],
['homed'],
@@ -4003,7 +3972,7 @@ foreach tuple : [
['debug hashmap'],
['debug mmap cache'],
['debug siphash'],
- ['valgrind', conf.get('VALGRIND') == 1],
+ ['valgrind'],
['trace logging', conf.get('LOG_TRACE') == 1],
['install tests', install_tests],
['link-udev-shared', get_option('link-udev-shared')],
diff --git a/rules.d/meson.build b/rules.d/meson.build
index 05c55a6b41..5cecddb34f 100644
--- a/rules.d/meson.build
+++ b/rules.d/meson.build
@@ -52,8 +52,7 @@ foreach file : rules_in
file,
input : file + '.in',
output: file,
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : true,
install_dir : udevrulesdir)
endforeach
diff --git a/shell-completion/bash/meson.build b/shell-completion/bash/meson.build
index 1196795814..c6668e5ea3 100644
--- a/shell-completion/bash/meson.build
+++ b/shell-completion/bash/meson.build
@@ -14,8 +14,7 @@ custom_target(
'systemctl',
input : 'systemctl.in',
output : 'systemctl',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : bashcompletiondir != 'no',
install_dir : bashcompletiondir)
diff --git a/shell-completion/bash/systemd-run b/shell-completion/bash/systemd-run
index 76b9700f79..c5db8b77bd 100644
--- a/shell-completion/bash/systemd-run
+++ b/shell-completion/bash/systemd-run
@@ -78,7 +78,7 @@ _systemd_run() {
-p|--property)
local comps='CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP=
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group=
- DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth=
+ DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth=
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment=
KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA=
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC=
diff --git a/shell-completion/zsh/_systemd-run b/shell-completion/zsh/_systemd-run
index 934834b94b..7568ed4840 100644
--- a/shell-completion/zsh/_systemd-run
+++ b/shell-completion/zsh/_systemd-run
@@ -45,7 +45,7 @@ _arguments \
{-p+,--property=}'[Set unit property]:NAME=VALUE:(( \
CPUAccounting= MemoryAccounting= BlockIOAccounting= SendSIGHUP= \
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \
- DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= \
+ DevicePolicy= KillMode= ExitType= DeviceAllow= BlockIOReadBandwidth= \
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \
KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \
diff --git a/shell-completion/zsh/meson.build b/shell-completion/zsh/meson.build
index 31d9a39ccf..a0615c4df9 100644
--- a/shell-completion/zsh/meson.build
+++ b/shell-completion/zsh/meson.build
@@ -9,8 +9,7 @@ custom_target(
'_systemctl',
input : '_systemctl.in',
output : '_systemctl',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : zshcompletiondir != 'no',
install_dir : zshcompletiondir)
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 82fdd3deee..d4c73ab86a 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -26,6 +26,7 @@
#include "copy.h"
#include "def.h"
#include "exit-status.h"
+#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
#include "filesystems.h"
@@ -42,6 +43,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "pretty-print.h"
+#include "rm-rf.h"
#if HAVE_SECCOMP
# include "seccomp-util.h"
#endif
@@ -53,6 +55,7 @@
#include "strxcpyx.h"
#include "terminal-util.h"
#include "time-util.h"
+#include "tmpfile-util.h"
#include "unit-name.h"
#include "util.h"
#include "verb-log-control.h"
@@ -230,6 +233,53 @@ static int compare_unit_start(const UnitTimes *a, const UnitTimes *b) {
return CMP(a->activating, b->activating);
}
+static int process_aliases(char *argv[], char *tempdir, char ***ret) {
+ _cleanup_strv_free_ char **filenames = NULL;
+ char **filename;
+ int r;
+
+ assert(argv);
+ assert(tempdir);
+ assert(ret);
+
+ STRV_FOREACH(filename, strv_skip(argv, 1)) {
+ _cleanup_free_ char *src = NULL, *dst = NULL, *base = NULL;
+ const char *parse_arg;
+
+ parse_arg = *filename;
+ r = extract_first_word(&parse_arg, &src, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE);
+ if (r < 0)
+ return r;
+
+ if (!parse_arg) {
+ r = strv_consume(&filenames, TAKE_PTR(src));
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ r = path_extract_filename(parse_arg, &base);
+ if (r < 0)
+ return r;
+
+ dst = path_join(tempdir, base);
+ if (!dst)
+ return -ENOMEM;
+
+ r = copy_file(src, dst, 0, 0644, 0, 0, COPY_REFLINK);
+ if (r < 0)
+ return r;
+
+ r = strv_consume(&filenames, TAKE_PTR(dst));
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(filenames);
+ return 0;
+}
+
static UnitTimes* unit_times_free_array(UnitTimes *t) {
for (UnitTimes *p = t; p && p->has_data; p++)
free(p->name);
@@ -2257,7 +2307,19 @@ static int do_condition(int argc, char *argv[], void *userdata) {
}
static int do_verify(int argc, char *argv[], void *userdata) {
- return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
+ _cleanup_strv_free_ char **filenames = NULL;
+ _cleanup_(rm_rf_physical_and_freep) char *tempdir = NULL;
+ int r;
+
+ r = mkdtemp_malloc("/tmp/systemd-analyze-XXXXXX", &tempdir);
+ if (r < 0)
+ return log_error_errno(r, "Failed to setup working directory: %m");
+
+ r = process_aliases(argv, tempdir, &filenames);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't process aliases: %m");
+
+ return verify_units(filenames, arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
}
static int do_security(int argc, char *argv[], void *userdata) {
diff --git a/src/basic/architecture.c b/src/basic/architecture.c
index e5dafb9814..4f5c5b86a6 100644
--- a/src/basic/architecture.c
+++ b/src/basic/architecture.c
@@ -106,8 +106,7 @@ int uname_architecture(void) {
{ "crisv32", ARCHITECTURE_CRIS },
#elif defined(__nios2__)
{ "nios2", ARCHITECTURE_NIOS2 },
-#elif defined(__riscv__) || defined(__riscv)
- /* __riscv__ is obsolete, remove in 2018 */
+#elif defined(__riscv)
{ "riscv32", ARCHITECTURE_RISCV32 },
{ "riscv64", ARCHITECTURE_RISCV64 },
# if __SIZEOF_POINTER__ == 4
diff --git a/src/basic/architecture.h b/src/basic/architecture.h
index 9abc1831da..2126c79898 100644
--- a/src/basic/architecture.h
+++ b/src/basic/architecture.h
@@ -210,8 +210,7 @@ int uname_architecture(void);
#elif defined(__nios2__)
# define native_architecture() ARCHITECTURE_NIOS2
# define LIB_ARCH_TUPLE "nios2-linux-gnu"
-#elif defined(__riscv__) || defined(__riscv)
- /* __riscv__ is obsolete, remove in 2018 */
+#elif defined(__riscv)
# if __SIZEOF_POINTER__ == 4
# define native_architecture() ARCHITECTURE_RISCV32
# define LIB_ARCH_TUPLE "riscv32-linux-gnu"
diff --git a/src/basic/arphrd-list.c b/src/basic/arphrd-list.c
deleted file mode 100644
index 99048d20c8..0000000000
--- a/src/basic/arphrd-list.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <errno.h>
-#include <linux/if_arp.h>
-#include <string.h>
-
-#include "arphrd-list.h"
-#include "macro.h"
-
-static const struct arphrd_name* lookup_arphrd(register const char *str, register GPERF_LEN_TYPE len);
-
-#include "arphrd-from-name.h"
-#include "arphrd-to-name.h"
-
-int arphrd_from_name(const char *name) {
- const struct arphrd_name *sc;
-
- assert(name);
-
- sc = lookup_arphrd(name, strlen(name));
- if (!sc)
- return -EINVAL;
-
- return sc->id;
-}
diff --git a/src/basic/arphrd-util.c b/src/basic/arphrd-util.c
new file mode 100644
index 0000000000..3ea2c9d09a
--- /dev/null
+++ b/src/basic/arphrd-util.c
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <linux/if_arp.h>
+#include <linux/if_infiniband.h>
+#include <string.h>
+
+#include "arphrd-util.h"
+#include "macro.h"
+
+static const struct arphrd_name* lookup_arphrd(register const char *str, register GPERF_LEN_TYPE len);
+
+#include "arphrd-from-name.h"
+#include "arphrd-to-name.h"
+
+int arphrd_from_name(const char *name) {
+ const struct arphrd_name *sc;
+
+ assert(name);
+
+ sc = lookup_arphrd(name, strlen(name));
+ if (!sc)
+ return -EINVAL;
+
+ return sc->id;
+}
+
+size_t arphrd_to_hw_addr_len(uint16_t arphrd) {
+ switch (arphrd) {
+ case ARPHRD_ETHER:
+ return ETH_ALEN;
+ case ARPHRD_INFINIBAND:
+ return INFINIBAND_ALEN;
+ case ARPHRD_TUNNEL:
+ case ARPHRD_SIT:
+ case ARPHRD_IPGRE:
+ return sizeof(struct in_addr);
+ case ARPHRD_TUNNEL6:
+ case ARPHRD_IP6GRE:
+ return sizeof(struct in6_addr);
+ default:
+ return 0;
+ }
+}
diff --git a/src/basic/arphrd-list.h b/src/basic/arphrd-util.h
index bc95b4507b..33f5694abd 100644
--- a/src/basic/arphrd-list.h
+++ b/src/basic/arphrd-util.h
@@ -1,5 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <inttypes.h>
+#include <stddef.h>
+
const char *arphrd_to_name(int id);
int arphrd_from_name(const char *name);
+
+size_t arphrd_to_hw_addr_len(uint16_t arphrd);
diff --git a/src/basic/escape.c b/src/basic/escape.c
index 4bb98f9342..ce57fcc762 100644
--- a/src/basic/escape.c
+++ b/src/basic/escape.c
@@ -544,7 +544,7 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
return str_realloc(buf);
}
-char* quote_command_line(char **argv) {
+char* quote_command_line(char **argv, ShellEscapeFlags flags) {
_cleanup_free_ char *result = NULL;
assert(argv);
@@ -553,7 +553,7 @@ char* quote_command_line(char **argv) {
STRV_FOREACH(a, argv) {
_cleanup_free_ char *t = NULL;
- t = shell_maybe_quote(*a, SHELL_ESCAPE_EMPTY);
+ t = shell_maybe_quote(*a, flags);
if (!t)
return NULL;
@@ -561,5 +561,5 @@ char* quote_command_line(char **argv) {
return NULL;
}
- return TAKE_PTR(result);
+ return str_realloc(TAKE_PTR(result));
}
diff --git a/src/basic/escape.h b/src/basic/escape.h
index d490510deb..318da6f220 100644
--- a/src/basic/escape.h
+++ b/src/basic/escape.h
@@ -69,4 +69,4 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
char* shell_escape(const char *s, const char *bad);
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags);
-char* quote_command_line(char **argv);
+char* quote_command_line(char **argv, ShellEscapeFlags flags);
diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c
index dc5b5b833d..c609ea6ce0 100644
--- a/src/basic/ether-addr-util.c
+++ b/src/basic/ether-addr-util.c
@@ -11,7 +11,11 @@
#include "macro.h"
#include "string-util.h"
-char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) {
+char *hw_addr_to_string_full(
+ const struct hw_addr_data *addr,
+ HardwareAddressToStringFlags flags,
+ char buffer[static HW_ADDR_TO_STRING_MAX]) {
+
assert(addr);
assert(buffer);
assert(addr->length <= HW_ADDR_MAX_SIZE);
@@ -19,10 +23,13 @@ char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_
for (size_t i = 0, j = 0; i < addr->length; i++) {
buffer[j++] = hexchar(addr->bytes[i] >> 4);
buffer[j++] = hexchar(addr->bytes[i] & 0x0f);
- buffer[j++] = ':';
+ if (!FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON))
+ buffer[j++] = ':';
}
- buffer[addr->length > 0 ? addr->length * 3 - 1 : 0] = '\0';
+ buffer[addr->length == 0 || FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON) ?
+ addr->length * 2 :
+ addr->length * 3 - 1] = '\0';
return buffer;
}
@@ -39,7 +46,7 @@ int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b)
return memcmp(a->bytes, b->bytes, a->length);
}
-static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
+void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
assert(p);
assert(state);
@@ -48,6 +55,7 @@ static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *stat
}
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free);
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
assert(addr);
@@ -93,75 +101,163 @@ static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *sta
}
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(ether_addr_hash_ops_free, struct ether_addr, ether_addr_hash_func, ether_addr_compare, free);
-int ether_addr_from_string(const char *s, struct ether_addr *ret) {
- size_t pos = 0, n, field;
- char sep = '\0';
- const char *hex = HEXDIGITS, *hexoff;
- size_t x;
- bool touched;
-
-#define parse_fields(v) \
- for (field = 0; field < ELEMENTSOF(v); field++) { \
- touched = false; \
- for (n = 0; n < (2 * sizeof(v[0])); n++) { \
- if (s[pos] == '\0') \
- break; \
- hexoff = strchr(hex, s[pos]); \
- if (!hexoff) \
- break; \
- assert(hexoff >= hex); \
- x = hexoff - hex; \
- if (x >= 16) \
- x -= 6; /* A-F */ \
- assert(x < 16); \
- touched = true; \
- v[field] <<= 4; \
- v[field] += x; \
- pos++; \
- } \
- if (!touched) \
- return -EINVAL; \
- if (field < (ELEMENTSOF(v)-1)) { \
- if (s[pos] != sep) \
- return -EINVAL; \
- else \
- pos++; \
- } \
- }
+static int parse_hw_addr_one_field(const char **s, char sep, size_t len, uint8_t *buf) {
+ const char *hex = HEXDIGITS, *p;
+ uint16_t data = 0;
+ bool cont;
assert(s);
- assert(ret);
+ assert(*s);
+ assert(IN_SET(len, 1, 2));
+ assert(buf);
- s += strspn(s, WHITESPACE);
- sep = s[strspn(s, hex)];
+ p = *s;
- if (sep == '.') {
- uint16_t shorts[3] = { 0 };
+ for (size_t i = 0; i < len * 2; i++) {
+ const char *hexoff;
+ size_t x;
- parse_fields(shorts);
+ if (*p == '\0' || *p == sep) {
+ if (i == 0)
+ return -EINVAL;
+ break;
+ }
- if (s[pos] != '\0')
+ hexoff = strchr(hex, *p);
+ if (!hexoff)
return -EINVAL;
- for (n = 0; n < ELEMENTSOF(shorts); n++) {
- ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8);
- ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff);
+ assert(hexoff >= hex);
+ x = hexoff - hex;
+ if (x >= 16)
+ x -= 6; /* A-F */
+
+ assert(x < 16);
+ data <<= 4;
+ data += x;
+
+ p++;
+ }
+
+ if (*p != '\0' && *p != sep)
+ return -EINVAL;
+
+ switch (len) {
+ case 1:
+ buf[0] = data;
+ break;
+ case 2:
+ buf[0] = (data & 0xff00) >> 8;
+ buf[1] = data & 0xff;
+ break;
+ default:
+ assert_not_reached();
+ }
+
+ cont = *p == sep;
+ *s = p + cont;
+ return cont;
+}
+
+int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret) {
+ size_t field_size, max_len, len = 0;
+ uint8_t bytes[HW_ADDR_MAX_SIZE];
+ char sep;
+ int r;
+
+ assert(s);
+ assert(expected_len <= HW_ADDR_MAX_SIZE || expected_len == SIZE_MAX);
+ assert(ret);
+
+ /* This accepts the following formats:
+ *
+ * Dot separated 2 bytes format: xxyy.zzaa.bbcc
+ * Colon separated 1 bytes format: xx:yy:zz:aa:bb:cc
+ * Hyphen separated 1 bytes format: xx-yy-zz-aa-bb-cc
+ *
+ * Moreover, if expected_len == 0, 4, or 16, this also accepts:
+ *
+ * IPv4 format: used by IPv4 tunnel, e.g. ipgre
+ * IPv6 format: used by IPv6 tunnel, e.g. ip6gre
+ *
+ * The expected_len argument controls the length of acceptable addresses:
+ *
+ * 0: accepts 4 (AF_INET), 16 (AF_INET6), 6 (ETH_ALEN), or 20 (INFINIBAND_ALEN).
+ * SIZE_MAX: accepts arbitrary length, but at least one separator must be included.
+ * Otherwise: accepts addresses with matching length.
+ */
+
+ if (IN_SET(expected_len, 0, sizeof(struct in_addr), sizeof(struct in6_addr))) {
+ union in_addr_union a;
+ int family;
+
+ if (expected_len == 0)
+ r = in_addr_from_string_auto(s, &family, &a);
+ else {
+ family = expected_len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
+ r = in_addr_from_string(family, s, &a);
+ }
+ if (r >= 0) {
+ ret->length = FAMILY_ADDRESS_SIZE(family);
+ memcpy(ret->bytes, a.bytes, ret->length);
+ return 0;
}
+ }
- } else if (IN_SET(sep, ':', '-')) {
- struct ether_addr out = ETHER_ADDR_NULL;
+ max_len =
+ expected_len == 0 ? INFINIBAND_ALEN :
+ expected_len == SIZE_MAX ? HW_ADDR_MAX_SIZE : expected_len;
+ sep = s[strspn(s, HEXDIGITS)];
- parse_fields(out.ether_addr_octet);
+ if (sep == '.')
+ field_size = 2;
+ else if (IN_SET(sep, ':', '-'))
+ field_size = 1;
+ else
+ return -EINVAL;
- if (s[pos] != '\0')
- return -EINVAL;
+ if (max_len % field_size != 0)
+ return -EINVAL;
- for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++)
- ret->ether_addr_octet[n] = out.ether_addr_octet[n];
+ for (size_t i = 0; i < max_len / field_size; i++) {
+ r = parse_hw_addr_one_field(&s, sep, field_size, bytes + i * field_size);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ len = (i + 1) * field_size;
+ break;
+ }
+ }
- } else
+ if (len == 0)
return -EINVAL;
+ if (expected_len == 0) {
+ if (!IN_SET(len, 4, 16, ETH_ALEN, INFINIBAND_ALEN))
+ return -EINVAL;
+ } else if (expected_len != SIZE_MAX) {
+ if (len != expected_len)
+ return -EINVAL;
+ }
+
+ ret->length = len;
+ memcpy(ret->bytes, bytes, ret->length);
+ return 0;
+}
+
+int parse_ether_addr(const char *s, struct ether_addr *ret) {
+ struct hw_addr_data a;
+ int r;
+
+ assert(s);
+ assert(ret);
+
+ r = parse_hw_addr_full(s, ETH_ALEN, &a);
+ if (r < 0)
+ return r;
+
+ *ret = a.ether;
return 0;
}
diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h
index 794fc55bb8..32f45fe813 100644
--- a/src/basic/ether-addr-util.h
+++ b/src/basic/ether-addr-util.h
@@ -6,6 +6,9 @@
#include <stdbool.h>
#include "hash-funcs.h"
+#include "in-addr-util.h"
+#include "macro.h"
+#include "memory-util.h"
/* This is MAX_ADDR_LEN as defined in linux/netdevice.h, but net/if_arp.h
* defines a macro of the same name with a much lower size. */
@@ -16,29 +19,51 @@ struct hw_addr_data {
union {
struct ether_addr ether;
uint8_t infiniband[INFINIBAND_ALEN];
+ struct in_addr in;
+ struct in6_addr in6;
uint8_t bytes[HW_ADDR_MAX_SIZE];
};
};
+int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret);
+static inline int parse_hw_addr(const char *s, struct hw_addr_data *ret) {
+ return parse_hw_addr_full(s, 0, ret);
+}
+int parse_ether_addr(const char *s, struct ether_addr *ret);
+
+typedef enum HardwareAddressToStringFlags {
+ HW_ADDR_TO_STRING_NO_COLON = 1 << 0,
+} HardwareAddressToStringFlags;
+
#define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE)
-char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]);
+char *hw_addr_to_string_full(
+ const struct hw_addr_data *addr,
+ HardwareAddressToStringFlags flags,
+ char buffer[static HW_ADDR_TO_STRING_MAX]);
+static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buffer[static HW_ADDR_TO_STRING_MAX]) {
+ return hw_addr_to_string_full(addr, 0, buffer);
+}
/* Note: the lifetime of the compound literal is the immediately surrounding block,
* see C11 §6.5.2.5, and
* https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
-#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){})
+#define HW_ADDR_TO_STR_FULL(hw_addr, flags) hw_addr_to_string_full((hw_addr), flags, (char[HW_ADDR_TO_STRING_MAX]){})
+#define HW_ADDR_TO_STR(hw_addr) HW_ADDR_TO_STR_FULL(hw_addr, 0)
#define HW_ADDR_NULL ((const struct hw_addr_data){})
+void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state);
int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b);
static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) {
return hw_addr_compare(a, b) == 0;
}
static inline bool hw_addr_is_null(const struct hw_addr_data *addr) {
- return hw_addr_equal(addr, &HW_ADDR_NULL);
+ assert(addr);
+ return addr->length == 0 || memeqzero(addr->bytes, addr->length);
}
extern const struct hash_ops hw_addr_hash_ops;
+extern const struct hash_ops hw_addr_hash_ops_free;
#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
@@ -60,6 +85,29 @@ static inline bool ether_addr_is_null(const struct ether_addr *addr) {
return ether_addr_equal(addr, &ETHER_ADDR_NULL);
}
-int ether_addr_from_string(const char *s, struct ether_addr *ret);
+static inline bool ether_addr_is_broadcast(const struct ether_addr *addr) {
+ assert(addr);
+ return memeqbyte(0xff, addr->ether_addr_octet, ETH_ALEN);
+}
+
+static inline bool ether_addr_is_multicast(const struct ether_addr *addr) {
+ assert(addr);
+ return FLAGS_SET(addr->ether_addr_octet[0], 0x01);
+}
+
+static inline bool ether_addr_is_unicast(const struct ether_addr *addr) {
+ return !ether_addr_is_multicast(addr);
+}
+
+static inline bool ether_addr_is_local(const struct ether_addr *addr) {
+ /* Determine if the Ethernet address is locally-assigned one (IEEE 802) */
+ assert(addr);
+ return FLAGS_SET(addr->ether_addr_octet[0], 0x02);
+}
+
+static inline bool ether_addr_is_global(const struct ether_addr *addr) {
+ return !ether_addr_is_local(addr);
+}
extern const struct hash_ops ether_addr_hash_ops;
+extern const struct hash_ops ether_addr_hash_ops_free;
diff --git a/src/basic/inotify-util.c b/src/basic/inotify-util.c
index 848f8590fa..6da974dec0 100644
--- a/src/basic/inotify-util.c
+++ b/src/basic/inotify-util.c
@@ -2,14 +2,26 @@
#include "fd-util.h"
#include "inotify-util.h"
+#include "stat-util.h"
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
- int wd;
+ int wd, r;
/* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
- if (wd < 0)
- return -errno;
+ if (wd < 0) {
+ if (errno != ENOENT)
+ return -errno;
+
+ /* Didn't work with ENOENT? If so, then either /proc/ isn't mounted, or the fd is bad */
+ r = proc_mounted();
+ if (r == 0)
+ return -ENOSYS;
+ if (r > 0)
+ return -EBADF;
+
+ return -ENOENT; /* OK, no clue, let's propagate the original error */
+ }
return wd;
}
diff --git a/src/basic/meson.build b/src/basic/meson.build
index cf97f6de1d..042ab86d5e 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -9,8 +9,8 @@ basic_sources = files('''
alloc-util.h
architecture.c
architecture.h
- arphrd-list.c
- arphrd-list.h
+ arphrd-util.c
+ arphrd-util.h
async.c
async.h
audit-util.c
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 82bbda895f..fe732c0322 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -219,20 +219,9 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
if (!args)
return -ENOMEM;
- for (size_t i = 0; args[i]; i++) {
- char *e;
-
- e = shell_maybe_quote(args[i], shflags);
- if (!e)
- return -ENOMEM;
-
- free_and_replace(args[i], e);
- }
-
- ans = strv_join(args, " ");
+ ans = quote_command_line(args, shflags);
if (!ans)
return -ENOMEM;
-
} else {
/* Arguments are separated by NULs. Let's replace those with spaces. */
for (size_t i = 0; i < k - 1; i++)
diff --git a/src/basic/umask-util.h b/src/basic/umask-util.h
index bd7c2bdb8c..90d18f70ba 100644
--- a/src/basic/umask-util.h
+++ b/src/basic/umask-util.h
@@ -24,3 +24,6 @@ assert_cc((S_IFMT & 0777) == 0);
for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \
FLAGS_SET(_saved_umask_, S_IFMT); \
_saved_umask_ &= 0777)
+
+#define BLOCK_WITH_UMASK(mask) \
+ _unused_ _cleanup_umask_ mode_t _saved_umask_ = umask(mask);
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 5992a28cdd..0bacfb4bbe 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -4,6 +4,7 @@
#include <efigpt.h>
#include <efilib.h>
+#include "bootspec-fundamental.h"
#include "console.h"
#include "devicetree.h"
#include "disk.h"
@@ -28,8 +29,9 @@
#define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111)
-/* magic string to find in the binary image */
-_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
+/* Magic string for recognizing our own binaries */
+_used_ _section_(".sdmagic") static const char magic[] =
+ "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
/* Makes systemd-boot available from \EFI\Linux\ for testing purposes. */
_used_ _section_(".osrel") static const char osrel[] =
@@ -44,10 +46,10 @@ enum loader_type {
};
typedef struct {
- CHAR16 *id; /* The unique identifier for this entry */
- CHAR16 *title_show;
- CHAR16 *title;
- CHAR16 *version;
+ CHAR16 *id; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
+ CHAR16 *title_show; /* The string to actually display (this is made unique before showing) */
+ CHAR16 *title; /* The raw (human readable) title string of the entry (not necessarily unique) */
+ CHAR16 *version; /* The raw (human readable) version string of the entry */
CHAR16 *machine_id;
EFI_HANDLE *device;
enum loader_type type;
@@ -1651,14 +1653,14 @@ static void config_sort_entries(Config *config) {
sort_pointer_array((void**) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare);
}
-static INTN config_entry_find(Config *config, CHAR16 *needle) {
+static INTN config_entry_find(Config *config, const CHAR16 *needle) {
assert(config);
if (!needle)
return -1;
for (UINTN i = 0; i < config->entry_count; i++)
- if (MetaiMatch(config->entries[i]->id, needle))
+ if (MetaiMatch(config->entries[i]->id, (CHAR16*) needle))
return (INTN) i;
return -1;
@@ -1732,13 +1734,9 @@ static void config_title_generate(Config *config) {
/* set title */
for (UINTN i = 0; i < config->entry_count; i++) {
- CHAR16 *title;
-
FreePool(config->entries[i]->title_show);
- title = config->entries[i]->title;
- if (!title)
- title = config->entries[i]->id;
- config->entries[i]->title_show = StrDuplicate(title);
+ config->entries[i]->title_show = StrDuplicate(
+ config->entries[i]->title ?: config->entries[i]->id);
}
if (!find_nonunique(config->entries, config->entry_count))
@@ -2044,8 +2042,10 @@ static void config_entry_add_linux(
NULL,
};
- _cleanup_freepool_ CHAR16 *os_name_pretty = NULL, *os_name = NULL, *os_id = NULL,
- *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL, *os_image_version = NULL;
+ _cleanup_freepool_ CHAR16 *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
+ *os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL,
+ *path = NULL;
+ const CHAR16 *good_name, *good_version;
_cleanup_freepool_ CHAR8 *content = NULL;
UINTN offs[_SECTION_MAX] = {};
UINTN szs[_SECTION_MAX] = {};
@@ -2077,82 +2077,101 @@ static void config_entry_add_linux(
/* read properties from the embedded os-release file */
while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
- if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
- FreePool(os_name_pretty);
- os_name_pretty = stra_to_str(value);
+ if (strcmpa((const CHAR8*) "PRETTY_NAME", key) == 0) {
+ FreePool(os_pretty_name);
+ os_pretty_name = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((const CHAR8*) "IMAGE_ID", key) == 0) {
+ FreePool(os_image_id);
+ os_image_id = stra_to_str(value);
continue;
}
- if (strcmpa((CHAR8 *)"NAME", key) == 0) {
+ if (strcmpa((const CHAR8*) "NAME", key) == 0) {
FreePool(os_name);
os_name = stra_to_str(value);
continue;
}
- if (strcmpa((CHAR8 *)"ID", key) == 0) {
+ if (strcmpa((const CHAR8*) "ID", key) == 0) {
FreePool(os_id);
os_id = stra_to_str(value);
continue;
}
- if (strcmpa((CHAR8 *)"VERSION", key) == 0) {
+ if (strcmpa((const CHAR8*) "IMAGE_VERSION", key) == 0) {
+ FreePool(os_image_version);
+ os_image_version = stra_to_str(value);
+ continue;
+ }
+
+ if (strcmpa((const CHAR8*) "VERSION", key) == 0) {
FreePool(os_version);
os_version = stra_to_str(value);
continue;
}
- if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) {
+ if (strcmpa((const CHAR8*) "VERSION_ID", key) == 0) {
FreePool(os_version_id);
os_version_id = stra_to_str(value);
continue;
}
- if (strcmpa((CHAR8 *)"BUILD_ID", key) == 0) {
+ if (strcmpa((const CHAR8*) "BUILD_ID", key) == 0) {
FreePool(os_build_id);
os_build_id = stra_to_str(value);
continue;
}
-
- if (strcmpa((const CHAR8*) "IMAGE_VERSION", key) == 0) {
- FreePool(os_image_version);
- os_image_version = stra_to_str(value);
- continue;
- }
}
- if ((os_name_pretty || os_name) && os_id && (os_image_version || os_version || os_version_id || os_build_id)) {
- _cleanup_freepool_ CHAR16 *path = NULL;
-
- path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
-
- entry = config_entry_add_loader(
- config,
- device,
- LOADER_LINUX,
- f->FileName,
- /* key= */ 'l',
- os_name_pretty ?: os_name,
- path,
- os_image_version ?: (os_version ?: (os_version_id ? : os_build_id)));
-
- config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
-
- if (szs[SECTION_CMDLINE] == 0)
- continue;
+ if (!bootspec_pick_name_version(
+ os_pretty_name,
+ os_image_id,
+ os_name,
+ os_id,
+ os_image_version,
+ os_version,
+ os_version_id,
+ os_build_id,
+ &good_name,
+ &good_version))
+ continue;
- FreePool(content);
- content = NULL;
+ path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
+ if (!path)
+ return (void) log_oom();
+
+ entry = config_entry_add_loader(
+ config,
+ device,
+ LOADER_LINUX,
+ f->FileName,
+ /* key= */ 'l',
+ good_name,
+ path,
+ good_version);
+ if (!entry)
+ return (void) log_oom();
+
+ config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
+
+ if (szs[SECTION_CMDLINE] == 0)
+ continue;
- /* read the embedded cmdline file */
- err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, NULL);
- if (!EFI_ERROR(err)) {
+ content = mfree(content);
- /* chomp the newline */
- if (content[szs[SECTION_CMDLINE] - 1] == '\n')
- content[szs[SECTION_CMDLINE] - 1] = '\0';
+ /* read the embedded cmdline file */
+ err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, NULL);
+ if (!EFI_ERROR(err)) {
+ /* chomp the newline */
+ if (content[szs[SECTION_CMDLINE] - 1] == '\n')
+ content[szs[SECTION_CMDLINE] - 1] = '\0';
- entry->options = stra_to_str(content);
- }
+ entry->options = stra_to_str(content);
+ if (!entry->options)
+ return (void) log_oom();
}
}
}
@@ -2480,7 +2499,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = image_start(root_dir, image, &config, entry);
if (EFI_ERROR(err)) {
graphics_mode(FALSE);
- log_error_stall(L"Failed to execute %s (%s): %r", entry->title, entry->loader, err);
+ log_error_stall(L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err);
goto out;
}
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index cdf31c7fd8..9ffc839289 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -1,5 +1,102 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
+conf.set10('ENABLE_EFI', get_option('efi'))
+conf.set10('HAVE_GNU_EFI', false)
+
+if not get_option('efi') or get_option('gnu-efi') == 'false'
+ if get_option('gnu-efi') == 'true'
+ error('gnu-efi support requested, but general efi support is disabled')
+ endif
+ subdir_done()
+endif
+
+efi_arch = {
+ # host_cc_arch: [efi_arch (see Table 3-2 in UEFI spec), gnu_efi_inc_arch]
+ 'x86': ['ia32', 'ia32'],
+ 'x86_64': ['x64', 'x86_64'],
+ 'arm': ['arm', 'arm'],
+ 'aarch64': ['aa64', 'aarch64'],
+ 'riscv64': ['riscv64', 'riscv64'],
+}.get(host_machine.cpu_family(), [])
+
+efi_incdir = get_option('efi-includedir')
+if efi_arch.length() > 0 and not cc.has_header('@0@/@1@/efibind.h'.format(efi_incdir, efi_arch[1]))
+ efi_arch = []
+endif
+
+if efi_arch.length() == 0
+ if get_option('gnu-efi') == 'true'
+ error('gnu-efi support requested, but headers not found or efi arch is unkown')
+ endif
+ warning('gnu-efi headers not found or efi arch is unkown, disabling gnu-efi support')
+ subdir_done()
+endif
+
+if not cc.has_header_symbol('efi.h', 'EFI_IMAGE_MACHINE_X64',
+ include_directories: include_directories(efi_incdir, efi_incdir / efi_arch[1]))
+
+ if get_option('gnu-efi') == 'true'
+ error('gnu-efi support requested, but found headers are too old (3.0.5+ required)')
+ endif
+ warning('gnu-efi headers are too old (3.0.5+ required), disabling gnu-efi support')
+ subdir_done()
+endif
+
+objcopy = find_program('objcopy')
+efi_cc = get_option('efi-cc')
+if efi_cc.length() == 0
+ efi_cc = cc.cmd_array()
+endif
+
+efi_ld = find_program(get_option('efi-ld'))
+efi_ld_name = efi_ld.path().split('/')[-1]
+if efi_ld_name == 'lld' or efi_ld_name == 'ld.lld'
+ # LLVM/LLD does not support PE/COFF relocations
+ # https://lists.llvm.org/pipermail/llvm-dev/2021-March/149234.html
+ error('LLVM/lld does not support PE/COFF relocations. Use different linker for EFI image.')
+endif
+
+efi_libdir = ''
+foreach dir : [get_option('efi-libdir'),
+ '/usr/lib/gnuefi' / efi_arch[0],
+ run_command('realpath', '-e',
+ '/usr/lib' / run_command(efi_cc, '-print-multi-os-directory').stdout().strip()).stdout().strip()]
+ if run_command(test, '-d', dir).returncode() == 0
+ efi_libdir = dir
+ break
+ endif
+endforeach
+if efi_libdir == ''
+ if get_option('gnu-efi') == 'true'
+ error('gnu-efi support requested, but efi-libdir was not found')
+ endif
+ warning('efi-libdir was not found, disabling gnu-efi support')
+ subdir_done()
+endif
+
+efi_lds = ''
+foreach location : [ # New locations first introduced with gnu-efi 3.0.11
+ [efi_libdir / 'efi.lds',
+ efi_libdir / 'crt0.o'],
+ # Older locations...
+ [efi_libdir / 'gnuefi' / 'elf_@0@_efi.lds'.format(efi_arch[1]),
+ efi_libdir / 'gnuefi' / 'crt0-efi-@0@.o'.format(efi_arch[1])],
+ [efi_libdir / 'elf_@0@_efi.lds'.format(efi_arch[1]),
+ efi_libdir / 'crt0-efi-@0@.o'.format(efi_arch[1])]]
+ if run_command(test, '-f', location[0], '-a', '-f', location[1]).returncode() == 0
+ efi_lds = location[0]
+ efi_crt0 = location[1]
+ break
+ endif
+endforeach
+if efi_lds == ''
+ if get_option('gnu-efi') == 'true'
+ error('gnu-efi support requested, but cannot find efi.lds')
+ endif
+ warning('efi.lds was not found, disabling gnu-efi support')
+ subdir_done()
+endif
+
efi_headers = files('''
console.h
cpio.h
@@ -39,312 +136,206 @@ systemd_boot_sources = '''
'''.split()
stub_sources = '''
+ cpio.c
initrd.c
splash.c
stub.c
- cpio.c
'''.split()
-if efi_arch in ['x86', 'x86_64']
+if efi_arch[1] in ['ia32', 'x86_64']
stub_sources += 'linux_x86.c'
else
stub_sources += 'linux.c'
endif
-if conf.get('ENABLE_EFI') == 1 and get_option('gnu-efi') != 'false'
- efi_cc = get_option('efi-cc')
- if efi_cc.length() == 0
- efi_cc = cc.cmd_array()
+conf.set10('HAVE_GNU_EFI', true)
+conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0])
+
+efi_conf = configuration_data()
+efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0])
+efi_conf.set10('ENABLE_TPM', get_option('tpm'))
+
+foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit']
+ c = get_option('efi-' + ctype).split(',')
+ efi_conf.set(ctype.underscorify().to_upper(), 'EFI_TEXT_ATTR(@0@, @1@)'.format(
+ 'EFI_' + c[0].strip().underscorify().to_upper(),
+ 'EFI_' + c[1].strip().underscorify().to_upper()))
+endforeach
+
+if get_option('sbat-distro') != ''
+ efi_conf.set_quoted('SBAT_PROJECT', meson.project_name())
+ efi_conf.set_quoted('PROJECT_VERSION', meson.project_version())
+ efi_conf.set('PROJECT_URL', conf.get('PROJECT_URL'))
+ if get_option('sbat-distro-generation') < 1
+ error('SBAT Distro Generation must be a positive integer')
endif
-
- efi_ld = find_program(get_option('efi-ld'), required: true)
- efi_ld_name = efi_ld.path().split('/')[-1]
- if efi_ld_name == 'lld' or efi_ld_name == 'ld.lld'
- # LLVM/LLD does not support PE/COFF relocations
- # https://lists.llvm.org/pipermail/llvm-dev/2021-March/149234.html
- error('LLVM/lld does not support PE/COFF relocations. Use different linker for EFI image.')
- endif
-
- efi_incdir = get_option('efi-includedir')
-
- gnu_efi_path_arch = ''
- foreach name : [gnu_efi_arch, EFI_MACHINE_TYPE_NAME]
- if (gnu_efi_path_arch == '' and name != '' and
- cc.has_header('@0@/@1@/efibind.h'.format(efi_incdir, name)))
- gnu_efi_path_arch = name
+ efi_conf.set('SBAT_DISTRO_GENERATION', get_option('sbat-distro-generation'))
+ foreach sbatvar : [['sbat-distro', 'ID'],
+ ['sbat-distro-summary', 'NAME'],
+ ['sbat-distro-url', 'BUG_REPORT_URL']]
+ value = get_option(sbatvar[0])
+ if (value == '' and not meson.is_cross_build()) or value == 'auto'
+ cmd = 'if [ -e /etc/os-release ]; then . /etc/os-release; else . /usr/lib/os-release; fi; echo $@0@'.format(sbatvar[1])
+ value = run_command(sh, '-c', cmd).stdout().strip()
+ message('@0@ (from @1@): @2@'.format(sbatvar[0], sbatvar[1], value))
endif
- endforeach
-
- if gnu_efi_path_arch != '' and EFI_MACHINE_TYPE_NAME == ''
- error('gnu-efi is available, but EFI_MACHINE_TYPE_NAME is unknown')
- endif
-
- efi_libdir = get_option('efi-libdir')
- if efi_libdir == ''
- # New location first introduced with gnu-efi 3.0.11
- efi_libdir = '/usr/lib/gnuefi' / EFI_MACHINE_TYPE_NAME
- cmd = run_command(test, '-e', efi_libdir)
-
- if cmd.returncode() != 0
- # Fall back to the old approach
- cmd = run_command(efi_cc + ['-print-multi-os-directory'])
- if cmd.returncode() == 0
- path = '/usr/lib' / cmd.stdout().strip()
- cmd = run_command(env, 'realpath', '-e', path)
- if cmd.returncode() == 0
- efi_libdir = cmd.stdout().strip()
- endif
- endif
- endif
- endif
-
- have_gnu_efi = gnu_efi_path_arch != '' and efi_libdir != ''
-
- if have_gnu_efi and not cc.has_header_symbol('efi.h', 'EFI_IMAGE_MACHINE_X64',
- include_directories: include_directories(efi_incdir, efi_incdir / gnu_efi_path_arch))
- have_gnu_efi = false
- if get_option('gnu-efi') == 'true'
- error('gnu-efi support requested, but found headers are too old (3.0.5+ required)')
+ if value == ''
+ error('Required @0@ option not set and autodetection failed'.format(sbatvar[0]))
endif
- endif
-else
- have_gnu_efi = false
-endif
-
-if get_option('gnu-efi') == 'true' and not have_gnu_efi
- error('gnu-efi support requested, but headers were not found')
-endif
-
-if have_gnu_efi
- efi_conf = configuration_data()
- efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME)
- efi_conf.set10('ENABLE_TPM', get_option('tpm'))
-
- foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit']
- c = get_option('efi-' + ctype).split(',')
- fg = 'EFI_' + c[0].strip().underscorify().to_upper()
- bg = 'EFI_BACKGROUND_' + c[1].strip().underscorify().to_upper()
- efi_conf.set(ctype.underscorify().to_upper(), '(' + fg + '|' + bg + ')')
+ efi_conf.set_quoted(sbatvar[0].underscorify().to_upper(), value)
endforeach
- if get_option('sbat-distro') != ''
- efi_conf.set_quoted('SBAT_PROJECT', meson.project_name())
- efi_conf.set_quoted('PROJECT_VERSION', meson.project_version())
- efi_conf.set('PROJECT_URL', conf.get('PROJECT_URL'))
- if get_option('sbat-distro-generation') < 1
- error('SBAT Distro Generation must be a positive integer')
- endif
- efi_conf.set('SBAT_DISTRO_GENERATION', get_option('sbat-distro-generation'))
- sbatvars = [['sbat-distro', 'ID'],
- ['sbat-distro-summary', 'NAME'],
- ['sbat-distro-url', 'BUG_REPORT_URL']]
- foreach sbatvar : sbatvars
- value = get_option(sbatvar[0])
- if (value == '' and not meson.is_cross_build()) or value == 'auto'
- cmd = 'if [ -e /etc/os-release ]; then . /etc/os-release; else . /usr/lib/os-release; fi; echo $@0@'.format(sbatvar[1])
- value = run_command(sh, '-c', cmd).stdout().strip()
- message('@0@ (from @1@): @2@'.format(sbatvar[0], sbatvar[1], value))
- endif
- if value == ''
- error('Required @0@ option not set and autodetection failed'.format(sbatvar[0]))
- endif
- efi_conf.set_quoted(sbatvar[0].underscorify().to_upper(), value)
- endforeach
-
- pkgname = get_option('sbat-distro-pkgname')
- if pkgname == ''
- pkgname = meson.project_name()
- endif
- efi_conf.set_quoted('SBAT_DISTRO_PKGNAME', pkgname)
-
- pkgver = get_option('sbat-distro-version')
- if pkgver == ''
- efi_conf.set('SBAT_DISTRO_VERSION', 'GIT_VERSION')
- else
- efi_conf.set_quoted('SBAT_DISTRO_VERSION', pkgver)
- endif
+ pkgname = get_option('sbat-distro-pkgname')
+ if pkgname == ''
+ pkgname = meson.project_name()
endif
+ efi_conf.set_quoted('SBAT_DISTRO_PKGNAME', pkgname)
- efi_config_h = configure_file(
- output : 'efi_config.h',
- configuration : efi_conf)
-
- objcopy = find_program('objcopy')
-
- efi_location_map = [
- # New locations first introduced with gnu-efi 3.0.11
- [efi_libdir / 'efi.lds',
- efi_libdir / 'crt0.o'],
- # Older locations...
- [efi_libdir / 'gnuefi' / 'elf_@0@_efi.lds'.format(gnu_efi_path_arch),
- efi_libdir / 'gnuefi' / 'crt0-efi-@0@.o'.format(gnu_efi_path_arch)],
- [efi_libdir / 'elf_@0@_efi.lds'.format(gnu_efi_path_arch),
- efi_libdir / 'crt0-efi-@0@.o'.format(gnu_efi_path_arch)]]
- efi_lds = ''
- foreach location : efi_location_map
- if efi_lds == ''
- cmd = run_command(test, '-f', location[0])
- if cmd.returncode() == 0
- efi_lds = location[0]
- efi_crt0 = location[1]
- endif
- endif
- endforeach
- if efi_lds == ''
- if get_option('gnu-efi') == 'true'
- error('gnu-efi support requested, but cannot find efi.lds')
- else
- have_gnu_efi = false
- endif
+ pkgver = get_option('sbat-distro-version')
+ if pkgver == ''
+ efi_conf.set('SBAT_DISTRO_VERSION', 'GIT_VERSION')
+ else
+ efi_conf.set_quoted('SBAT_DISTRO_VERSION', pkgver)
endif
endif
-if have_gnu_efi
- compile_args = cc.get_supported_arguments(
- basic_disabled_warnings +
- possible_common_cc_flags + [
- '-fno-stack-protector',
- '-fno-strict-aliasing',
- '-fpic',
- '-fwide-exec-charset=UCS2',
- '-Wall',
- '-Wextra',
- '-Wsign-compare',
- ]
- ) + [
- '-nostdlib',
- '-std=gnu99',
- '-ffreestanding',
- '-fshort-wchar',
- '-isystem', efi_incdir,
- '-isystem', efi_incdir / gnu_efi_path_arch,
- '-I', fundamental_path,
- '-DSD_BOOT',
- '-DGNU_EFI_USE_MS_ABI',
- '-include', efi_config_h,
- '-include', version_h,
+efi_config_h = configure_file(
+ output : 'efi_config.h',
+ configuration : efi_conf)
+
+compile_args = cc.get_supported_arguments(
+ basic_disabled_warnings +
+ possible_common_cc_flags + [
+ '-fno-stack-protector',
+ '-fno-strict-aliasing',
+ '-fpic',
+ '-fwide-exec-charset=UCS2',
+ '-Wall',
+ '-Wextra',
+ '-Wsign-compare',
]
+) + [
+ '-nostdlib',
+ '-std=gnu99',
+ '-ffreestanding',
+ '-fshort-wchar',
+ '-isystem', efi_incdir,
+ '-isystem', efi_incdir / efi_arch[1],
+ '-I', fundamental_path,
+ '-DSD_BOOT',
+ '-DGNU_EFI_USE_MS_ABI',
+ '-include', efi_config_h,
+ '-include', version_h,
+]
+
+compile_args += cc.get_supported_arguments({
+ 'ia32': ['-mno-sse', '-mno-mmx'],
+ 'x86_64': ['-mno-red-zone', '-mno-sse', '-mno-mmx'],
+ 'arm': ['-mgeneral-regs-only', '-mfpu=none'],
+}.get(efi_arch[1], []))
+
+# We are putting the efi_cc command line together ourselves, so make sure to pull any
+# relevant compiler flags from meson/CFLAGS as povided by the user or distro.
+
+if get_option('werror')
+ compile_args += ['-Werror']
+endif
+if get_option('debug')
+ compile_args += ['-ggdb', '-DEFI_DEBUG']
+endif
+if get_option('optimization') != '0'
+ compile_args += ['-O' + get_option('optimization')]
+endif
+if get_option('b_ndebug') == 'true' or (
+ get_option('b_ndebug') == 'if-release' and get_option('buildtype') in ['plain', 'release'])
+ compile_args += ['-DNDEBUG']
+endif
- if efi_arch == 'x86_64'
- compile_args += ['-mno-red-zone',
- '-mno-sse',
- '-mno-mmx']
- elif efi_arch == 'x86'
- compile_args += ['-mno-sse',
- '-mno-mmx']
- elif efi_arch == 'arm'
- compile_args += cc.get_supported_arguments([
- '-mgeneral-regs-only',
- '-mfpu=none'
- ])
+foreach arg : get_option('c_args')
+ if arg in ['-Werror', '-g', '-ggdb', '-O1', '-O2', '-O3', '-Og', '-Os', '-DNDEBUG']
+ message('Using "@0@" from c_args for EFI compiler'.format(arg))
+ compile_args += arg
endif
+endforeach
+
+efi_ldflags = ['-T', efi_lds,
+ '-shared',
+ '-Bsymbolic',
+ '-nostdlib',
+ '--no-undefined',
+ '--warn-common',
+ '--fatal-warnings',
+ '-znocombreloc',
+ '--build-id=sha1',
+ '-L', efi_libdir,
+ efi_crt0]
+if efi_arch[1] in ['aarch64', 'arm', 'riscv64']
+ # Aarch64, ARM32 and 64bit RISC-V don't have an EFI capable objcopy.
+ # Use 'binary' instead, and add required symbols manually.
+ efi_ldflags += ['--defsym=EFI_SUBSYSTEM=0xa']
+ efi_format = ['-O', 'binary']
+else
+ efi_format = ['--target=efi-app-@0@'.format(efi_arch[1])]
+endif
- # We are putting the efi_cc command line together ourselves, so make sure to pull any
- # relevant compiler flags from meson/CFLAGS as povided by the user or distro.
-
- if get_option('werror')
- compile_args += ['-Werror']
- endif
- if get_option('debug')
- compile_args += ['-ggdb', '-DEFI_DEBUG']
- endif
- if get_option('optimization') != '0'
- compile_args += ['-O' + get_option('optimization')]
- endif
- if get_option('b_ndebug') == 'true' or (
- get_option('b_ndebug') == 'if-release' and ['plain', 'release'].contains(get_option('buildtype')))
- compile_args += ['-DNDEBUG']
+systemd_boot_objects = []
+stub_objects = []
+foreach file : fundamental_source_paths + common_sources + systemd_boot_sources + stub_sources
+ o_file = custom_target(file.split('/')[-1] + '.o',
+ input : file,
+ output : file.split('/')[-1] + '.o',
+ command : [efi_cc, '-c', '@INPUT@', '-o', '@OUTPUT@', compile_args],
+ depend_files : efi_headers + fundamental_headers)
+ if (fundamental_source_paths + common_sources + systemd_boot_sources).contains(file)
+ systemd_boot_objects += o_file
endif
-
- foreach arg : get_option('c_args')
- if arg in ['-Werror', '-g', '-ggdb', '-O1', '-O2', '-O3', '-Og', '-Os', '-DNDEBUG']
- message('Using "@0@" from c_args for EFI compiler'.format(arg))
- compile_args += arg
- endif
- endforeach
-
- efi_ldflags = ['-T', efi_lds,
- '-shared',
- '-Bsymbolic',
- '-nostdlib',
- '--no-undefined',
- '--warn-common',
- '--fatal-warnings',
- '-znocombreloc',
- '--build-id=sha1',
- '-L', efi_libdir,
- efi_crt0]
- if ['aarch64', 'arm', 'riscv64'].contains(efi_arch)
- # Aarch64, ARM32 and 64bit RISC-V don't have an EFI capable objcopy.
- # Use 'binary' instead, and add required symbols manually.
- efi_ldflags += ['--defsym=EFI_SUBSYSTEM=0xa']
- efi_format = ['-O', 'binary']
- else
- efi_format = ['--target=efi-app-@0@'.format(gnu_efi_arch)]
+ if (fundamental_source_paths + common_sources + stub_sources).contains(file)
+ stub_objects += o_file
endif
-
- systemd_boot_objects = []
- stub_objects = []
- foreach file : fundamental_source_paths + common_sources + systemd_boot_sources + stub_sources
- o_file = custom_target(file.split('/')[-1] + '.o',
- input : file,
- output : file.split('/')[-1] + '.o',
- command : efi_cc + ['-c', '@INPUT@', '-o', '@OUTPUT@']
- + compile_args,
- depend_files : efi_headers + fundamental_headers)
- if (fundamental_source_paths + common_sources + systemd_boot_sources).contains(file)
- systemd_boot_objects += o_file
- endif
- if (fundamental_source_paths + common_sources + stub_sources).contains(file)
- stub_objects += o_file
- endif
- endforeach
-
- libgcc_file_name = run_command(efi_cc + ['-print-libgcc-file-name']).stdout().strip()
- systemd_boot_efi_name = 'systemd-boot@0@.efi'.format(EFI_MACHINE_TYPE_NAME)
- stub_elf_name = 'linux@0@.elf.stub'.format(EFI_MACHINE_TYPE_NAME)
- stub_efi_name = 'linux@0@.efi.stub'.format(EFI_MACHINE_TYPE_NAME)
-
- 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 : [efi_ld, '-o', '@OUTPUT@',
- efi_ldflags, tuple[2],
- '-lefi', '-lgnuefi', libgcc_file_name],
- install : tuple[3],
- install_dir : bootlibdir)
-
- stub = custom_target(
- tuple[1],
- input : so,
- output : tuple[1],
- command : [objcopy,
- '-j', '.data',
- '-j', '.dynamic',
- '-j', '.dynsym',
- '-j', '.osrel',
- '-j', '.rel*',
- '-j', '.sbat',
- '-j', '.sdata',
- '-j', '.sdmagic',
- '-j', '.text',
- efi_format,
- '@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])
-endif
+endforeach
+
+libgcc_file_name = run_command(efi_cc + ['-print-libgcc-file-name']).stdout().strip()
+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 : [efi_ld, '-o', '@OUTPUT@', efi_ldflags, tuple[2], '-lefi', '-lgnuefi', libgcc_file_name],
+ install : tuple[3],
+ install_dir : bootlibdir)
+
+ stub = custom_target(
+ tuple[1],
+ input : so,
+ output : tuple[1],
+ command : [objcopy,
+ '-j', '.data',
+ '-j', '.dynamic',
+ '-j', '.dynsym',
+ '-j', '.osrel',
+ '-j', '.rel*',
+ '-j', '.sbat',
+ '-j', '.sdata',
+ '-j', '.sdmagic',
+ '-j', '.text',
+ efi_format,
+ '@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])
diff --git a/src/core/automount.c b/src/core/automount.c
index de470935c7..1fc3fc0f82 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -1063,7 +1063,7 @@ static bool automount_supported(void) {
return supported;
}
-static int automount_test_start_limit(Unit *u) {
+static int automount_can_start(Unit *u) {
Automount *a = AUTOMOUNT(u);
int r;
@@ -1075,7 +1075,7 @@ static int automount_test_start_limit(Unit *u) {
return r;
}
- return 0;
+ return 1;
}
static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
@@ -1142,5 +1142,5 @@ const UnitVTable automount_vtable = {
},
},
- .test_start_limit = automount_test_start_limit,
+ .can_start = automount_can_start,
};
diff --git a/src/core/bpf-foreign.c b/src/core/bpf-foreign.c
index 6b93b9785f..8538792b60 100644
--- a/src/core/bpf-foreign.c
+++ b/src/core/bpf-foreign.c
@@ -4,8 +4,10 @@
#include "bpf-program.h"
#include "cgroup.h"
#include "memory-util.h"
+#include "missing_magic.h"
#include "mountpoint-util.h"
#include "set.h"
+#include "stat-util.h"
typedef struct BPFForeignKey BPFForeignKey;
struct BPFForeignKey {
@@ -84,6 +86,14 @@ static int bpf_foreign_prepare(
assert(u);
assert(bpffs_path);
+ r = path_is_fs_type(bpffs_path, BPF_FS_MAGIC);
+ if (r < 0)
+ return log_unit_error_errno(u, r,
+ "Failed to determine filesystem type of %s: %m", bpffs_path);
+ if (r == 0)
+ return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
+ "Path in BPF filesystem is expected.");
+
r = bpf_program_new_from_bpffs_path(bpffs_path, &prog);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to create foreign BPFProgram: %m");
@@ -111,16 +121,6 @@ static int bpf_foreign_prepare(
return 0;
}
-int bpf_foreign_supported(void) {
- int r;
-
- r = cg_all_unified();
- if (r <= 0)
- return r;
-
- return path_is_mount_point("/sys/fs/bpf", NULL, 0);
-}
-
int bpf_foreign_install(Unit *u) {
_cleanup_free_ char *cgroup_path = NULL;
CGroupBPFForeignProgram *p;
diff --git a/src/core/bpf-foreign.h b/src/core/bpf-foreign.h
index 9559cd7981..e387b1b1d3 100644
--- a/src/core/bpf-foreign.h
+++ b/src/core/bpf-foreign.h
@@ -4,7 +4,10 @@
#include "unit.h"
-int bpf_foreign_supported(void);
+static inline int bpf_foreign_supported(void) {
+ return cg_all_unified();
+}
+
/*
* Attach cgroup-bpf programs foreign to systemd, i.e. loaded to the kernel by an entity
* external to systemd.
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index abc30e3990..c942db8d05 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -2283,8 +2283,11 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) {
z = unit_attach_pid_to_cgroup_via_bus(u, pid, suffix_path);
if (z < 0)
log_unit_info_errno(u, z, "Couldn't move process "PID_FMT" to requested cgroup '%s' (directly or via the system bus): %m", pid, empty_to_root(p));
- else
+ else {
+ if (ret >= 0)
+ ret++; /* Count successful additions */
continue; /* When the bus thing worked via the bus we are fully done for this PID. */
+ }
}
if (ret >= 0)
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index f42d97afac..e90fe4f596 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -27,6 +27,7 @@
#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exit_type, service_exit_type, ServiceExitType);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, service_result, ServiceResult);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, ServiceRestart);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess);
@@ -192,6 +193,7 @@ int bus_service_method_mount_image(sd_bus_message *message, void *userdata, sd_b
const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ExitType", "s", property_get_exit_type, offsetof(Service, exit_type), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Restart", "s", property_get_restart, offsetof(Service, restart), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PIDFile", "s", NULL, offsetof(Service, pid_file), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NotifyAccess", "s", property_get_notify_access, offsetof(Service, notify_access), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -378,6 +380,7 @@ static int bus_set_transient_std_fd(
}
static BUS_DEFINE_SET_TRANSIENT_PARSE(notify_access, NotifyAccess, notify_access_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(service_type, ServiceType, service_type_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(service_exit_type, ServiceExitType, service_exit_type_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(service_restart, ServiceRestart, service_restart_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(oom_policy, OOMPolicy, oom_policy_from_string);
static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, sd_bus_service_name_is_valid);
@@ -415,6 +418,9 @@ static int bus_service_set_transient_property(
if (streq(name, "Type"))
return bus_set_transient_service_type(u, name, &s->type, message, flags, error);
+ if (streq(name, "ExitType"))
+ return bus_set_transient_service_exit_type(u, name, &s->exit_type, message, flags, error);
+
if (streq(name, "OOMPolicy"))
return bus_set_transient_oom_policy(u, name, &s->oom_policy, message, flags, error);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index fe320f1b05..d4ec789a7c 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -2273,7 +2273,7 @@ static int bus_unit_set_transient_property(
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- r = unit_set_slice(u, slice, UNIT_DEPENDENCY_FILE);
+ r = unit_set_slice(u, slice);
if (r < 0)
return r;
diff --git a/src/core/execute.c b/src/core/execute.c
index 425e3e5a37..4a57e40779 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -3999,16 +3999,15 @@ static int exec_child(
exec_context_tty_reset(context, params);
if (unit_shall_confirm_spawn(unit)) {
- const char *vc = params->confirm_spawn;
_cleanup_free_ char *cmdline = NULL;
- cmdline = quote_command_line(command->argv);
+ cmdline = quote_command_line(command->argv, SHELL_ESCAPE_EMPTY);
if (!cmdline) {
*exit_status = EXIT_MEMORY;
return log_oom();
}
- r = ask_for_confirmation(context, vc, unit, cmdline);
+ r = ask_for_confirmation(context, params->confirm_spawn, unit, cmdline);
if (r != CONFIRM_EXECUTE) {
if (r == CONFIRM_PRETEND_SUCCESS) {
*exit_status = EXIT_SUCCESS;
@@ -4580,9 +4579,12 @@ static int exec_child(
if (fd >= 0) {
r = mac_selinux_get_child_mls_label(fd, executable, context->selinux_context, &mac_selinux_context_net);
- if (r < 0 && !context->selinux_context_ignore) {
- *exit_status = EXIT_SELINUX_CONTEXT;
- return log_unit_error_errno(unit, r, "Failed to determine SELinux context: %m");
+ if (r < 0) {
+ if (!context->selinux_context_ignore) {
+ *exit_status = EXIT_SELINUX_CONTEXT;
+ return log_unit_error_errno(unit, r, "Failed to determine SELinux context: %m");
+ }
+ log_unit_debug_errno(unit, r, "Failed to determine SELinux context, ignoring: %m");
}
}
}
@@ -4714,9 +4716,12 @@ static int exec_child(
if (exec_context) {
r = setexeccon(exec_context);
- if (r < 0 && !context->selinux_context_ignore) {
- *exit_status = EXIT_SELINUX_CONTEXT;
- return log_unit_error_errno(unit, r, "Failed to change SELinux context to %s: %m", exec_context);
+ if (r < 0) {
+ if (!context->selinux_context_ignore) {
+ *exit_status = EXIT_SELINUX_CONTEXT;
+ return log_unit_error_errno(unit, r, "Failed to change SELinux context to %s: %m", exec_context);
+ }
+ log_unit_debug_errno(unit, r, "Failed to change SELinux context to %s, ignoring: %m", exec_context);
}
}
}
@@ -4884,7 +4889,7 @@ static int exec_child(
if (DEBUG_LOGGING) {
_cleanup_free_ char *line = NULL;
- line = quote_command_line(final_argv);
+ line = quote_command_line(final_argv, SHELL_ESCAPE_EMPTY);
if (!line) {
*exit_status = EXIT_MEMORY;
return log_oom();
@@ -4976,7 +4981,7 @@ int exec_spawn(Unit *unit,
if (r < 0)
return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
- line = quote_command_line(command->argv);
+ line = quote_command_line(command->argv, SHELL_ESCAPE_EMPTY);
if (!line)
return log_oom();
@@ -6230,7 +6235,7 @@ static void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
prefix = strempty(prefix);
prefix2 = strjoina(prefix, "\t");
- cmd = quote_command_line(c->argv);
+ cmd = quote_command_line(c->argv, SHELL_ESCAPE_EMPTY);
fprintf(f,
"%sCommand Line: %s\n",
prefix, cmd ? cmd : strerror_safe(ENOMEM));
diff --git a/src/core/job.c b/src/core/job.c
index 6dd01a6f49..b7ac75ac71 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -1107,17 +1107,13 @@ void job_add_to_run_queue(Job *j) {
if (j->in_run_queue)
return;
- if (prioq_isempty(j->manager->run_queue)) {
- r = sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT);
- if (r < 0)
- log_warning_errno(r, "Failed to enable job run queue event source, ignoring: %m");
- }
-
r = prioq_put(j->manager->run_queue, j, &j->run_queue_idx);
if (r < 0)
log_warning_errno(r, "Failed put job in run queue, ignoring: %m");
else
j->in_run_queue = true;
+
+ manager_trigger_run_queue(j->manager);
}
void job_add_to_dbus_queue(Job *j) {
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index 5315d2da88..71a5904a70 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -394,6 +394,7 @@ Service.StartLimitAction, config_parse_emergency_action,
Service.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action)
Service.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
Service.Type, config_parse_service_type, 0, offsetof(Service, type)
+Service.ExitType, config_parse_service_exit_type, 0, offsetof(Service, exit_type)
Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only)
Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 8cf821cd22..6efcba3265 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -136,6 +136,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "
DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value");
DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_service_exit_type, service_exit_type, ServiceExitType, "Failed to parse service exit type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value");
@@ -3792,7 +3793,7 @@ int config_parse_unit_slice(
return 0;
}
- r = unit_set_slice(u, slice, UNIT_DEPENDENCY_FILE);
+ r = unit_set_slice(u, slice);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id);
return 0;
@@ -6194,6 +6195,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_unit_deps, "UNIT [...]" },
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
{ config_parse_service_type, "SERVICETYPE" },
+ { config_parse_service_exit_type, "SERVICEEXITTYPE" },
{ config_parse_service_restart, "SERVICERESTART" },
{ config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
{ config_parse_kill_mode, "KILLMODE" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index ab2a0393fc..26b8de28f7 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -37,6 +37,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout);
CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_abort);
CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_failure_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_service_type);
+CONFIG_PARSER_PROTOTYPE(config_parse_service_exit_type);
CONFIG_PARSER_PROTOTYPE(config_parse_service_restart);
CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_output);
diff --git a/src/core/manager.c b/src/core/manager.c
index 61895ab092..b21747daea 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -2248,6 +2248,18 @@ static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) {
return 1;
}
+void manager_trigger_run_queue(Manager *m) {
+ int r;
+
+ assert(m);
+
+ r = sd_event_source_set_enabled(
+ m->run_queue_event_source,
+ prioq_isempty(m->run_queue) ? SD_EVENT_OFF : SD_EVENT_ONESHOT);
+ if (r < 0)
+ log_warning_errno(r, "Failed to enable job run queue event source, ignoring: %m");
+}
+
static unsigned manager_dispatch_dbus_queue(Manager *m) {
unsigned n = 0, budget;
Unit *u;
diff --git a/src/core/manager.h b/src/core/manager.h
index 29ce812121..a6b27c3777 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -195,7 +195,7 @@ struct Manager {
sd_event *event;
- /* This maps PIDs we care about to units that are interested in. We allow multiple units to he interested in
+ /* This maps PIDs we care about to units that are interested in. We allow multiple units to be interested in
* the same PID and multiple PIDs to be relevant to the same unit. Since in most cases only a single unit will
* be interested in the same PID we use a somewhat special encoding here: the first unit interested in a PID is
* stored directly in the hashmap, keyed by the PID unmodified. If there are other units interested too they'll
@@ -507,6 +507,8 @@ int manager_get_effective_environment(Manager *m, char ***ret);
int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit);
+void manager_trigger_run_queue(Manager *m);
+
int manager_loop(Manager *m);
int manager_reload(Manager *m);
diff --git a/src/core/meson.build b/src/core/meson.build
index de7c2ae798..c02543bd06 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -149,8 +149,7 @@ load_fragment_gperf_gperf = custom_target(
'load-fragment-gperf.gperf',
input : 'load-fragment-gperf.gperf.in',
output: 'load-fragment-gperf.gperf',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true)
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'])
load_fragment_gperf_c = custom_target(
'load-fragment-gperf.c',
@@ -202,8 +201,7 @@ foreach item : in_files
file,
input : file + '.in',
output: file,
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : (dir == pkgsysconfdir) ? install_sysconfdir_samples : (dir != 'no'),
install_dir : dir)
endforeach
diff --git a/src/core/mount.c b/src/core/mount.c
index 321c7986b3..4f76b552c2 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1838,6 +1838,18 @@ static bool mount_is_mounted(Mount *m) {
return UNIT(m)->perpetual || FLAGS_SET(m->proc_flags, MOUNT_PROC_IS_MOUNTED);
}
+static int mount_on_ratelimit_expire(sd_event_source *s, void *userdata) {
+ Manager *m = userdata;
+
+ assert(m);
+
+ /* By entering ratelimited state we made all mount start jobs not runnable, now rate limit is over so
+ * let's make sure we dispatch them in the next iteration. */
+ manager_trigger_run_queue(m);
+
+ return 0;
+}
+
static void mount_enumerate(Manager *m) {
int r;
@@ -1891,6 +1903,12 @@ static void mount_enumerate(Manager *m) {
goto fail;
}
+ r = sd_event_source_set_ratelimit_expire_callback(m->mount_event_source, mount_on_ratelimit_expire);
+ if (r < 0) {
+ log_error_errno(r, "Failed to enable rate limit for mount events: %m");
+ goto fail;
+ }
+
(void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch");
}
@@ -2135,19 +2153,22 @@ static int mount_can_clean(Unit *u, ExecCleanMask *ret) {
return exec_context_get_clean_mask(&m->exec_context, ret);
}
-static int mount_test_start_limit(Unit *u) {
+static int mount_can_start(Unit *u) {
Mount *m = MOUNT(u);
int r;
assert(m);
+ if (sd_event_source_is_ratelimited(u->manager->mount_event_source))
+ return -EAGAIN;
+
r = unit_test_start_limit(u);
if (r < 0) {
mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
return r;
}
- return 0;
+ return 1;
}
static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
@@ -2248,5 +2269,5 @@ const UnitVTable mount_vtable = {
},
},
- .test_start_limit = mount_test_start_limit,
+ .can_start = mount_can_start,
};
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 68704dff06..c01975b9de 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -806,8 +806,7 @@ static int clone_device_node(
*make_devnode = false;
}
- /* We're about to fall back to bind-mounting the device
- * node. So create a dummy bind-mount target.
+ /* We're about to fall back to bind-mounting the device node. So create a dummy bind-mount target.
* Do not prepare device-node SELinux label (see issue 13762) */
r = mknod(dn, S_IFREG, 0);
if (r < 0 && errno != EEXIST)
@@ -853,13 +852,10 @@ static int mount_private_dev(MountEntry *m) {
char temporary_mount[] = "/tmp/namespace-dev-XXXXXX";
const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
bool can_mknod = true;
- _unused_ _cleanup_umask_ mode_t u;
int r;
assert(m);
- u = umask(0000);
-
if (!mkdtemp(temporary_mount))
return log_debug_errno(errno, "Failed to create temporary directory '%s': %m", temporary_mount);
@@ -930,10 +926,8 @@ static int mount_private_dev(MountEntry *m) {
if (r < 0)
log_debug_errno(r, "Failed to set up basic device tree at '%s', ignoring: %m", temporary_mount);
- /* Create the /dev directory if missing. It is more likely to be
- * missing when the service is started with RootDirectory. This is
- * consistent with mount units creating the mount points when missing.
- */
+ /* Create the /dev directory if missing. It is more likely to be missing when the service is started
+ * with RootDirectory. This is consistent with mount units creating the mount points when missing. */
(void) mkdir_p_label(mount_entry_path(m), 0755);
/* Unmount everything in old /dev */
@@ -975,8 +969,8 @@ static int mount_bind_dev(const MountEntry *m) {
assert(m);
- /* Implements the little brother of mount_private_dev(): simply bind mounts the host's /dev into the service's
- * /dev. This is only used when RootDirectory= is set. */
+ /* Implements the little brother of mount_private_dev(): simply bind mounts the host's /dev into the
+ * service's /dev. This is only used when RootDirectory= is set. */
(void) mkdir_p_label(mount_entry_path(m), 0755);
@@ -1085,7 +1079,8 @@ static int mount_tmpfs(const MountEntry *m) {
entry_path = mount_entry_path(m);
inner_path = mount_entry_unprefixed_path(m);
- /* First, get rid of everything that is below if there is anything. Then, overmount with our new tmpfs */
+ /* First, get rid of everything that is below if there is anything. Then, overmount with our new
+ * tmpfs */
(void) mkdir_p_label(entry_path, 0755);
(void) umount_recursive(entry_path, 0);
@@ -1900,6 +1895,10 @@ int setup_namespace(
assert(ns_info);
+ /* Make sure that all mknod(), mkdir() calls we do are unaffected by the umask, and the access modes
+ * we configure take effect */
+ BLOCK_WITH_UMASK(0000);
+
if (!isempty(propagate_dir) && !isempty(incoming_dir))
setup_propagate = true;
@@ -1972,11 +1971,11 @@ int setup_namespace(
* we create it if it doesn't already exist. */
(void) mkdir_p_label("/run/systemd", 0755);
- /* Always create the mount namespace in a temporary directory, instead of operating
- * directly in the root. The temporary directory prevents any mounts from being
- * potentially obscured my other mounts we already applied.
- * We use the same mount point for all images, which is safe, since they all live
- * in their own namespaces after all, and hence won't see each other. */
+ /* Always create the mount namespace in a temporary directory, instead of operating directly
+ * in the root. The temporary directory prevents any mounts from being potentially obscured
+ * my other mounts we already applied. We use the same mount point for all images, which is
+ * safe, since they all live in their own namespaces after all, and hence won't see each
+ * other. */
root = "/run/systemd/unit-root";
(void) mkdir_label(root, 0700);
@@ -2240,8 +2239,8 @@ int setup_namespace(
(void) mkdir_p(propagate_dir, 0600);
if (n_extension_images > 0)
- /* ExtensionImages mountpoint directories will be created
- * while parsing the mounts to create, so have the parent ready */
+ /* ExtensionImages mountpoint directories will be created while parsing the mounts to create,
+ * so have the parent ready */
(void) mkdir_p(extension_dir, 0600);
/* Remount / as SLAVE so that nothing now mounted in the namespace
@@ -2509,7 +2508,8 @@ static int make_tmp_prefix(const char *prefix) {
if (errno != ENOENT)
return -errno;
- r = mkdir_parents(prefix, 0755);
+ RUN_WITH_UMASK(000)
+ r = mkdir_parents(prefix, 0755);
if (r < 0)
return r;
@@ -2517,7 +2517,8 @@ static int make_tmp_prefix(const char *prefix) {
if (r < 0)
return r;
- if (mkdir(t, 0777) < 0)
+ if (mkdir(t, 0777) < 0) /* umask will corrupt this access mode, but that doesn't matter, we need to
+ * call chmod() anyway for the suid bit, below. */
return -errno;
if (chmod(t, 01777) < 0) {
@@ -2575,10 +2576,9 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
if (!y)
return -ENOMEM;
- RUN_WITH_UMASK(0000) {
+ RUN_WITH_UMASK(0000)
if (mkdir(y, 0777 | S_ISVTX) < 0)
return -errno;
- }
r = label_fix_container(y, prefix, 0);
if (r < 0)
@@ -2590,7 +2590,8 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, ch
/* Trouble: we failed to create the directory. Instead of failing, let's simulate /tmp being
* read-only. This way the service will get the EROFS result as if it was writing to the real
* file system. */
- r = mkdir_p(RUN_SYSTEMD_EMPTY, 0500);
+ RUN_WITH_UMASK(0000)
+ r = mkdir_p(RUN_SYSTEMD_EMPTY, 0500);
if (r < 0)
return r;
diff --git a/src/core/path.c b/src/core/path.c
index 0a3d86e9db..cdab9dcf8c 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -811,7 +811,7 @@ static void path_reset_failed(Unit *u) {
p->result = PATH_SUCCESS;
}
-static int path_test_start_limit(Unit *u) {
+static int path_can_start(Unit *u) {
Path *p = PATH(u);
int r;
@@ -823,7 +823,7 @@ static int path_test_start_limit(Unit *u) {
return r;
}
- return 0;
+ return 1;
}
static const char* const path_type_table[_PATH_TYPE_MAX] = {
@@ -882,5 +882,5 @@ const UnitVTable path_vtable = {
.bus_set_property = bus_path_set_property,
- .test_start_limit = path_test_start_limit,
+ .can_start = path_can_start,
};
diff --git a/src/core/service.c b/src/core/service.c
index 4b99311e5a..17c19a2c4a 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -602,6 +602,9 @@ static int service_verify(Service *s) {
if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status))
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing.");
+ if (s->type == SERVICE_ONESHOT && s->exit_type == SERVICE_EXIT_CGROUP)
+ return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has ExitType=cgroup set, which isn't allowed for Type=oneshot services. Refusing.");
+
if (s->type == SERVICE_DBUS && !s->bus_name)
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service is of type D-Bus but no D-Bus service name has been specified. Refusing.");
@@ -3289,6 +3292,9 @@ static void service_notify_cgroup_empty_event(Unit *u) {
break;
}
+ if (s->exit_type == SERVICE_EXIT_CGROUP && main_pid_good(s) <= 0)
+ service_enter_start_post(s);
+
_fallthrough_;
case SERVICE_START_POST:
if (s->pid_file_pathspec &&
@@ -3477,79 +3483,82 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
service_run_next_main(s);
} else {
-
- /* The service exited, so the service is officially gone. */
s->main_command = NULL;
- switch (s->state) {
-
- case SERVICE_START_POST:
- case SERVICE_RELOAD:
- /* If neither main nor control processes are running then
- * the current state can never exit cleanly, hence immediately
- * terminate the service. */
- if (control_pid_good(s) <= 0)
- service_enter_stop(s, f);
+ /* Services with ExitType=cgroup do not act on main PID exiting,
+ * unless the cgroup is already empty */
+ if (s->exit_type == SERVICE_EXIT_MAIN || cgroup_good(s) <= 0) {
+ /* The service exited, so the service is officially gone. */
+ switch (s->state) {
+
+ case SERVICE_START_POST:
+ case SERVICE_RELOAD:
+ /* If neither main nor control processes are running then
+ * the current state can never exit cleanly, hence immediately
+ * terminate the service. */
+ if (control_pid_good(s) <= 0)
+ service_enter_stop(s, f);
+
+ /* Otherwise need to wait until the operation is done. */
+ break;
- /* Otherwise need to wait until the operation is done. */
- break;
+ case SERVICE_STOP:
+ /* Need to wait until the operation is done. */
+ break;
- case SERVICE_STOP:
- /* Need to wait until the operation is done. */
- break;
+ case SERVICE_START:
+ if (s->type == SERVICE_ONESHOT) {
+ /* This was our main goal, so let's go on */
+ if (f == SERVICE_SUCCESS)
+ service_enter_start_post(s);
+ else
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
+ break;
+ } else if (s->type == SERVICE_NOTIFY) {
+ /* Only enter running through a notification, so that the
+ * SERVICE_START state signifies that no ready notification
+ * has been received */
+ if (f != SERVICE_SUCCESS)
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
+ else if (!s->remain_after_exit || s->notify_access == NOTIFY_MAIN)
+ /* The service has never been and will never be active */
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL);
+ break;
+ }
- case SERVICE_START:
- if (s->type == SERVICE_ONESHOT) {
- /* This was our main goal, so let's go on */
- if (f == SERVICE_SUCCESS)
- service_enter_start_post(s);
- else
- service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
+ _fallthrough_;
+ case SERVICE_RUNNING:
+ service_enter_running(s, f);
break;
- } else if (s->type == SERVICE_NOTIFY) {
- /* Only enter running through a notification, so that the
- * SERVICE_START state signifies that no ready notification
- * has been received */
- if (f != SERVICE_SUCCESS)
- service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
- else if (!s->remain_after_exit || s->notify_access == NOTIFY_MAIN)
- /* The service has never been and will never be active */
- service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL);
- break;
- }
- _fallthrough_;
- case SERVICE_RUNNING:
- service_enter_running(s, f);
- break;
-
- case SERVICE_STOP_WATCHDOG:
- case SERVICE_STOP_SIGTERM:
- case SERVICE_STOP_SIGKILL:
+ case SERVICE_STOP_WATCHDOG:
+ case SERVICE_STOP_SIGTERM:
+ case SERVICE_STOP_SIGKILL:
- if (control_pid_good(s) <= 0)
- service_enter_stop_post(s, f);
+ if (control_pid_good(s) <= 0)
+ service_enter_stop_post(s, f);
- /* If there is still a control process, wait for that first */
- break;
+ /* If there is still a control process, wait for that first */
+ break;
- case SERVICE_STOP_POST:
+ case SERVICE_STOP_POST:
- if (control_pid_good(s) <= 0)
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
+ if (control_pid_good(s) <= 0)
+ service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
- break;
+ break;
- case SERVICE_FINAL_WATCHDOG:
- case SERVICE_FINAL_SIGTERM:
- case SERVICE_FINAL_SIGKILL:
+ case SERVICE_FINAL_WATCHDOG:
+ case SERVICE_FINAL_SIGTERM:
+ case SERVICE_FINAL_SIGKILL:
- if (control_pid_good(s) <= 0)
- service_enter_dead(s, f, true);
- break;
+ if (control_pid_good(s) <= 0)
+ service_enter_dead(s, f, true);
+ break;
- default:
- assert_not_reached();
+ default:
+ assert_not_reached();
+ }
}
}
@@ -4473,7 +4482,7 @@ static const char *service_finished_job(Unit *u, JobType t, JobResult result) {
return NULL;
}
-static int service_test_start_limit(Unit *u) {
+static int service_can_start(Unit *u) {
Service *s = SERVICE(u);
int r;
@@ -4486,7 +4495,7 @@ static int service_test_start_limit(Unit *u) {
return r;
}
- return 0;
+ return 1;
}
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
@@ -4513,6 +4522,13 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
+static const char* const service_exit_type_table[_SERVICE_EXIT_TYPE_MAX] = {
+ [SERVICE_EXIT_MAIN] = "main",
+ [SERVICE_EXIT_CGROUP] = "cgroup",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_exit_type, ServiceExitType);
+
static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
[SERVICE_EXEC_CONDITION] = "ExecCondition",
[SERVICE_EXEC_START_PRE] = "ExecStartPre",
@@ -4653,5 +4669,5 @@ const UnitVTable service_vtable = {
.finished_job = service_finished_job,
},
- .test_start_limit = service_test_start_limit,
+ .can_start = service_can_start,
};
diff --git a/src/core/service.h b/src/core/service.h
index eaad95df6d..70ce70fba5 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -35,6 +35,13 @@ typedef enum ServiceType {
_SERVICE_TYPE_INVALID = -EINVAL,
} ServiceType;
+typedef enum ServiceExitType {
+ SERVICE_EXIT_MAIN, /* we consider the main PID when deciding if the service exited */
+ SERVICE_EXIT_CGROUP, /* we wait for the last process in the cgroup to exit */
+ _SERVICE_EXIT_TYPE_MAX,
+ _SERVICE_EXIT_TYPE_INVALID = -EINVAL,
+} ServiceExitType;
+
typedef enum ServiceExecCommand {
SERVICE_EXEC_CONDITION,
SERVICE_EXEC_START_PRE,
@@ -97,6 +104,7 @@ struct Service {
Unit meta;
ServiceType type;
+ ServiceExitType exit_type;
ServiceRestart restart;
ExitStatusSet restart_prevent_status;
ExitStatusSet restart_force_status;
@@ -227,6 +235,9 @@ ServiceRestart service_restart_from_string(const char *s) _pure_;
const char* service_type_to_string(ServiceType i) _const_;
ServiceType service_type_from_string(const char *s) _pure_;
+const char* service_exit_type_to_string(ServiceExitType i) _const_;
+ServiceExitType service_exit_type_from_string(const char *s) _pure_;
+
const char* service_exec_command_to_string(ServiceExecCommand i) _const_;
ServiceExecCommand service_exec_command_from_string(const char *s) _pure_;
diff --git a/src/core/socket.c b/src/core/socket.c
index 6534311bef..f265aab594 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -3427,7 +3427,7 @@ static int socket_can_clean(Unit *u, ExecCleanMask *ret) {
return exec_context_get_clean_mask(&s->exec_context, ret);
}
-static int socket_test_start_limit(Unit *u) {
+static int socket_can_start(Unit *u) {
Socket *s = SOCKET(u);
int r;
@@ -3439,7 +3439,7 @@ static int socket_test_start_limit(Unit *u) {
return r;
}
- return 0;
+ return 1;
}
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
@@ -3570,5 +3570,5 @@ const UnitVTable socket_vtable = {
},
},
- .test_start_limit = socket_test_start_limit,
+ .can_start = socket_can_start,
};
diff --git a/src/core/swap.c b/src/core/swap.c
index de72ac9232..3b28235194 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -1581,7 +1581,7 @@ static int swap_can_clean(Unit *u, ExecCleanMask *ret) {
return exec_context_get_clean_mask(&s->exec_context, ret);
}
-static int swap_test_start_limit(Unit *u) {
+static int swap_can_start(Unit *u) {
Swap *s = SWAP(u);
int r;
@@ -1593,7 +1593,7 @@ static int swap_test_start_limit(Unit *u) {
return r;
}
- return 0;
+ return 1;
}
static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
@@ -1692,5 +1692,5 @@ const UnitVTable swap_vtable = {
},
},
- .test_start_limit = swap_test_start_limit,
+ .can_start = swap_can_start,
};
diff --git a/src/core/timer.c b/src/core/timer.c
index 240a2f473b..b22168fad5 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -889,7 +889,7 @@ static int timer_can_clean(Unit *u, ExecCleanMask *ret) {
return 0;
}
-static int timer_test_start_limit(Unit *u) {
+static int timer_can_start(Unit *u) {
Timer *t = TIMER(u);
int r;
@@ -901,7 +901,7 @@ static int timer_test_start_limit(Unit *u) {
return r;
}
- return 0;
+ return 1;
}
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
@@ -965,5 +965,5 @@ const UnitVTable timer_vtable = {
.bus_set_property = bus_timer_set_property,
- .test_start_limit = timer_test_start_limit,
+ .can_start = timer_can_start,
};
diff --git a/src/core/unit-serialize.c b/src/core/unit-serialize.c
index 3458d7017b..7d2e6bc130 100644
--- a/src/core/unit-serialize.c
+++ b/src/core/unit-serialize.c
@@ -593,6 +593,7 @@ static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependency
{ UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT, "mountinfo-implicit" },
{ UNIT_DEPENDENCY_MOUNTINFO_DEFAULT, "mountinfo-default" },
{ UNIT_DEPENDENCY_PROC_SWAP, "proc-swap" },
+ { UNIT_DEPENDENCY_SLICE_PROPERTY, "slice-property" },
};
assert(f);
diff --git a/src/core/unit.c b/src/core/unit.c
index 59be3b78eb..929cc85e13 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -1904,9 +1904,9 @@ int unit_start(Unit *u) {
return unit_start(following);
}
- /* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */
- if (UNIT_VTABLE(u)->test_start_limit) {
- r = UNIT_VTABLE(u)->test_start_limit(u);
+ /* Check our ability to start early so that failure conditions don't cause us to enter a busy loop. */
+ if (UNIT_VTABLE(u)->can_start) {
+ r = UNIT_VTABLE(u)->can_start(u);
if (r < 0)
return r;
}
@@ -3285,7 +3285,7 @@ reset:
return r;
}
-int unit_set_slice(Unit *u, Unit *slice, UnitDependencyMask mask) {
+int unit_set_slice(Unit *u, Unit *slice) {
int r;
assert(u);
@@ -3318,7 +3318,11 @@ int unit_set_slice(Unit *u, Unit *slice, UnitDependencyMask mask) {
if (UNIT_GET_SLICE(u) && u->cgroup_realized)
return -EBUSY;
- r = unit_add_dependency(u, UNIT_IN_SLICE, slice, true, mask);
+ /* Remove any slices assigned prior; we should only have one UNIT_IN_SLICE dependency */
+ if (UNIT_GET_SLICE(u))
+ unit_remove_dependencies(u, UNIT_DEPENDENCY_SLICE_PROPERTY);
+
+ r = unit_add_dependency(u, UNIT_IN_SLICE, slice, true, UNIT_DEPENDENCY_SLICE_PROPERTY);
if (r < 0)
return r;
@@ -3374,7 +3378,7 @@ int unit_set_default_slice(Unit *u) {
if (r < 0)
return r;
- return unit_set_slice(u, slice, UNIT_DEPENDENCY_FILE);
+ return unit_set_slice(u, slice);
}
const char *unit_slice_name(Unit *u) {
diff --git a/src/core/unit.h b/src/core/unit.h
index b49ae7c1b8..76701519c2 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -89,7 +89,10 @@ typedef enum UnitDependencyMask {
/* A dependency created because of data read from /proc/swaps and no other configuration source */
UNIT_DEPENDENCY_PROC_SWAP = 1 << 7,
- _UNIT_DEPENDENCY_MASK_FULL = (1 << 8) - 1,
+ /* A dependency for units in slices assigned by directly setting Slice= */
+ UNIT_DEPENDENCY_SLICE_PROPERTY = 1 << 8,
+
+ _UNIT_DEPENDENCY_MASK_FULL = (1 << 9) - 1,
} UnitDependencyMask;
/* The Unit's dependencies[] hashmaps use this structure as value. It has the same size as a void pointer, and thus can
@@ -662,7 +665,7 @@ typedef struct UnitVTable {
/* If this function is set, it's invoked first as part of starting a unit to allow start rate
* limiting checks to occur before we do anything else. */
- int (*test_start_limit)(Unit *u);
+ int (*can_start)(Unit *u);
/* The strings to print in status messages */
UnitStatusMessageFormats status_message_formats;
@@ -782,7 +785,7 @@ Unit *unit_follow_merge(Unit *u) _pure_;
int unit_load_fragment_and_dropin(Unit *u, bool fragment_required);
int unit_load(Unit *unit);
-int unit_set_slice(Unit *u, Unit *slice, UnitDependencyMask mask);
+int unit_set_slice(Unit *u, Unit *slice);
int unit_set_default_slice(Unit *u);
const char *unit_description(Unit *u) _pure_;
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index cd7adfaeb9..745d01ff50 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -52,7 +52,7 @@ static void start_target(const char *target, const char *mode) {
return;
}
- log_info("Running request %s/start/replace", target);
+ log_info("Running request %s/start/%s", target, mode);
/* Start these units only if we can replace base.target with it */
r = sd_bus_call_method(bus,
@@ -412,10 +412,7 @@ static int run(int argc, char *argv[]) {
/* System should be rebooted. */
start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly");
return -EINVAL;
- } else if (exit_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED))
- /* Some other problem */
- start_target(SPECIAL_EMERGENCY_TARGET, "replace");
- else
+ } else if (!(exit_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)))
log_warning("Ignoring error.");
}
diff --git a/src/fundamental/bootspec-fundamental.c b/src/fundamental/bootspec-fundamental.c
new file mode 100644
index 0000000000..9c4aee9744
--- /dev/null
+++ b/src/fundamental/bootspec-fundamental.c
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "bootspec-fundamental.h"
+
+sd_bool bootspec_pick_name_version(
+ const sd_char *os_pretty_name,
+ const sd_char *os_image_id,
+ const sd_char *os_name,
+ const sd_char *os_id,
+ const sd_char *os_image_version,
+ const sd_char *os_version,
+ const sd_char *os_version_id,
+ const sd_char *os_build_id,
+ const sd_char **ret_name,
+ const sd_char **ret_version) {
+
+ const sd_char *good_name, *good_version;
+
+ /* Find the best human readable title and version string for a boot entry (using the os-release(5)
+ * fields). Precise is preferred over vague, and human readable over machine readable. Thus:
+ *
+ * 1. First priority gets the PRETTY_NAME field, which is the primary string intended for display,
+ * and should already contain both a nice description and a version indication (if that concept
+ * applies).
+ *
+ * 2. Otherwise we go for IMAGE_ID and IMAGE_VERSION (thus we show details about the image,
+ * i.e. specific combination of packages and configuration), if that concept applies.
+ *
+ * 3. Otherwise we go for NAME and VERSION (i.e. human readable OS name and version)
+ *
+ * 4. Otherwise we go for ID and VERSION_ID (i.e. machine readable OS name and version)
+ *
+ * 5. Finally, for the version we'll use BUILD_ID (i.e. a machine readable version that identifies
+ * the original OS build used during installation)
+ *
+ * Note that the display logic will show only the name by default, except if that isn't unique in
+ * which case the version is shown too.
+ *
+ * Note that name/version determined here are used only for display purposes. Boot entry preference
+ * sorting (i.e. algorithmic ordering of boot entries) is done based on the order of the entry "id"
+ * string (i.e. not on os-release(5) data). */
+
+ good_name = os_pretty_name ?: (os_image_id ?: (os_name ?: os_id));
+ good_version = os_image_version ?: (os_version ?: (os_version_id ? : os_build_id));
+
+ if (!good_name || !good_version)
+ return sd_false;
+
+ if (ret_name)
+ *ret_name = good_name;
+
+ if (ret_version)
+ *ret_version = good_version;
+
+ return sd_true;
+}
diff --git a/src/fundamental/bootspec-fundamental.h b/src/fundamental/bootspec-fundamental.h
new file mode 100644
index 0000000000..2cb6d7daa1
--- /dev/null
+++ b/src/fundamental/bootspec-fundamental.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "types-fundamental.h"
+
+sd_bool bootspec_pick_name_version(
+ const sd_char *os_pretty_name,
+ const sd_char *os_image_id,
+ const sd_char *os_name,
+ const sd_char *os_id,
+ const sd_char *os_image_version,
+ const sd_char *os_version,
+ const sd_char *os_version_id,
+ const sd_char *os_build_id,
+ const sd_char **ret_name,
+ const sd_char **ret_version);
diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h
index 44af0bd0a0..1f640b3ae3 100644
--- a/src/fundamental/macro-fundamental.h
+++ b/src/fundamental/macro-fundamental.h
@@ -6,7 +6,7 @@
#endif
#include <limits.h>
-#include "type.h"
+#include "types-fundamental.h"
#define _align_(x) __attribute__((__aligned__(x)))
#define _const_ __attribute__((__const__))
@@ -85,8 +85,8 @@
#define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
#define __ONCE(o) \
({ \
- static bool (o) = false; \
- __sync_bool_compare_and_swap(&(o), false, true); \
+ static sd_bool (o) = sd_false; \
+ __sync_bool_compare_and_swap(&(o), sd_false, sd_true); \
})
#undef MAX
@@ -236,7 +236,7 @@
#define IN_SET(x, ...) \
({ \
- sd_bool _found = false; \
+ sd_bool _found = sd_false; \
/* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \
* type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \
* the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \
@@ -245,7 +245,7 @@
assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
switch(x) { \
FOR_EACH_MAKE_CASE(__VA_ARGS__) \
- _found = true; \
+ _found = sd_true; \
break; \
default: \
break; \
diff --git a/src/fundamental/meson.build b/src/fundamental/meson.build
index 3c9f07b191..287f0fe36a 100644
--- a/src/fundamental/meson.build
+++ b/src/fundamental/meson.build
@@ -3,13 +3,15 @@
fundamental_path = meson.current_source_dir()
fundamental_headers = files(
+ 'bootspec-fundamental.h',
'efivars-fundamental.h',
'macro-fundamental.h',
- 'string-util-fundamental.h',
'sha256.h',
- 'type.h')
+ 'string-util-fundamental.h',
+ 'types-fundamental.h')
sources = '''
+ bootspec-fundamental.c
efivars-fundamental.c
string-util-fundamental.c
sha256.c
diff --git a/src/fundamental/sha256.h b/src/fundamental/sha256.h
index 9fc090b4e0..abc4167628 100644
--- a/src/fundamental/sha256.h
+++ b/src/fundamental/sha256.h
@@ -6,7 +6,7 @@
#include <efilib.h>
#endif
-#include "type.h"
+#include "types-fundamental.h"
struct sha256_ctx {
uint32_t H[8];
diff --git a/src/fundamental/type.h b/src/fundamental/type.h
deleted file mode 100644
index 2a9a114bbc..0000000000
--- a/src/fundamental/type.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#ifdef SD_BOOT
-#include <efi.h>
-
-typedef BOOLEAN sd_bool;
-typedef CHAR16 sd_char;
-typedef INTN sd_int;
-typedef UINTN size_t;
-
-#define true TRUE
-#define false FALSE
-#else
-#include <stdbool.h>
-#include <stdint.h>
-
-typedef bool sd_bool;
-typedef char sd_char;
-typedef int sd_int;
-#endif
diff --git a/src/fundamental/types-fundamental.h b/src/fundamental/types-fundamental.h
new file mode 100644
index 0000000000..5977e40c6c
--- /dev/null
+++ b/src/fundamental/types-fundamental.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* This defines a number of basic types that are one thing in userspace and another in the UEFI environment,
+ * but mostly the same in concept and behaviour.
+ *
+ * Note: if the definition of these types/values has slightly different semantics in userspace and in the
+ * UEFI environment then please prefix its name with "sd_" to make clear these types have special semantics,
+ * and *we* defined them. Otherwise, if the types are effectively 100% identical in behaviour in userspace
+ * and UEFI environment you can omit the prefix. (Examples: sd_char is 8 bit in userspace and 16 bit in UEFI
+ * space hence it should have the sd_ prefix; but size_t in userspace and UINTN in UEFI environment are 100%
+ * defined the same way ultimately, hence it's OK to just define size_t as alias to UINTN in UEFI
+ * environment, so that size_t can be used everywhere, without any "sd_" prefix.)
+ *
+ * Note: we generally prefer the userspace names of types and concepts. i.e. if in doubt please name types
+ * after the userspace vocabulary, and let's keep UEFI vocabulary specific to the UEFI build environment. */
+
+#ifdef SD_BOOT
+#include <efi.h>
+
+typedef BOOLEAN sd_bool;
+typedef CHAR16 sd_char;
+typedef INTN sd_int;
+typedef UINTN size_t;
+
+#define sd_true TRUE
+#define sd_false FALSE
+#else
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef bool sd_bool;
+typedef char sd_char;
+typedef int sd_int;
+
+#define sd_true true
+#define sd_false false
+
+#endif
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c
index 7aee82a980..ae101dd5bb 100644
--- a/src/home/homework-luks.c
+++ b/src/home/homework-luks.c
@@ -2155,8 +2155,8 @@ int home_create_luks(
setup->temporary_image_path = TAKE_PTR(t);
- r = chattr_fd(setup->image_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
- if (r < 0)
+ r = chattr_full(t, setup->image_fd, FS_NOCOW_FL|FS_NOCOMP_FL, FS_NOCOW_FL|FS_NOCOMP_FL, NULL, NULL, CHATTR_FALLBACK_BITWISE);
+ if (r < 0 && r != -ENOANO) /* ENOANO → some bits didn't work; which we skip logging about because chattr_full() already debug logs about those flags */
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set file attributes on %s, ignoring: %m", setup->temporary_image_path);
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index 26869a8d71..b525aaa642 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -442,7 +442,7 @@ static int set_hostname(int argc, char **argv, void *userdata) {
* dot if there is one. If it was not valid, then it will be made fully valid by truncating, dropping
* multiple dots, and dropping weird chars. Note that we clean the name up only if we also are
* supposed to set the pretty name. If the pretty name is not being set we assume the user knows what
- * he does and pass the name as-is. */
+ * they are doing and pass the name as-is. */
h = strdup(hostname);
if (!h)
return log_oom();
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index bcd2449861..e2c9f8ab67 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -302,7 +302,7 @@ try_acpi:
}
try_devicetree:
- r = read_one_line_file("/sys/firmware/devicetree/base/chassis-type", &type);
+ r = read_one_line_file("/proc/device-tree/chassis-type", &type);
if (r < 0) {
log_debug_errno(r, "Failed to read device-tree chassis type, ignoring: %m");
return NULL;
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index 13a9f74e6f..0539d4d0bf 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -243,13 +243,13 @@ int journal_remote_add_source(RemoteServer *s, int fd, char* name, bool own_name
r = sd_event_add_defer(s->events, &source->buffer_event,
dispatch_raw_source_until_block, source);
if (r == 0)
- sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
+ r = sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
} else if (r == -EPERM) {
log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
r = sd_event_add_defer(s->events, &source->event,
dispatch_blocking_source_event, source);
if (r == 0)
- sd_event_source_set_enabled(source->event, SD_EVENT_ON);
+ r = sd_event_source_set_enabled(source->event, SD_EVENT_ON);
}
if (r < 0) {
log_error_errno(r, "Failed to register event source for fd:%d: %m",
diff --git a/src/journal-remote/meson.build b/src/journal-remote/meson.build
index 5670d55ec5..54b314552b 100644
--- a/src/journal-remote/meson.build
+++ b/src/journal-remote/meson.build
@@ -54,8 +54,7 @@ foreach tuple : in_files
file,
input : file + '.in',
output: file,
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : tuple[1],
install_dir : pkgsysconfdir)
endforeach
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index cbff5036a4..26909840da 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -109,7 +109,6 @@ StdoutStream* stdout_stream_free(StdoutStream *s) {
return NULL;
if (s->server) {
-
if (s->context)
client_context_release(s->server, s->context);
@@ -123,11 +122,7 @@ StdoutStream* stdout_stream_free(StdoutStream *s) {
(void) server_start_or_stop_idle_timer(s->server); /* Maybe we are idle now? */
}
- if (s->event_source) {
- sd_event_source_set_enabled(s->event_source, SD_EVENT_OFF);
- s->event_source = sd_event_source_unref(s->event_source);
- }
-
+ sd_event_source_disable_unref(s->event_source);
safe_close(s->fd);
free(s->label);
free(s->identifier);
diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c
index 7ef96a6ab8..99a5f69b70 100644
--- a/src/libsystemd-network/arp-util.c
+++ b/src/libsystemd-network/arp-util.c
@@ -48,7 +48,6 @@ int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == X ? */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
- BPF_STMT(BPF_LDX + BPF_IMM, htobe32(a->s_addr)), /* A <- clients IP */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)), /* A <- TPA */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1), /* A == 0 ? */
BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c
index 23021f7bad..3ea6c7ce24 100644
--- a/src/libsystemd-network/dhcp-identifier.c
+++ b/src/libsystemd-network/dhcp-identifier.c
@@ -9,7 +9,7 @@
#include "dhcp-identifier.h"
#include "dhcp6-protocol.h"
-#include "network-util.h"
+#include "netif-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "stat-util.h"
@@ -193,7 +193,7 @@ int dhcp_identifier_set_iaid(
/* device is under renaming */
return -EBUSY;
- name = net_get_name_persistent(device);
+ name = net_get_persistent_name(device);
}
if (name)
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 04db791f64..2178668d11 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -766,4 +766,6 @@ global:
LIBSYSTEMD_250 {
global:
sd_device_get_diskseq;
+ sd_event_add_inotify_fd;
+ sd_event_source_set_ratelimit_expire_callback;
} LIBSYSTEMD_249;
diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build
index 02b2cd64b2..6ee78fd195 100644
--- a/src/libsystemd/meson.build
+++ b/src/libsystemd/meson.build
@@ -183,8 +183,7 @@ custom_target(
'libsystemd.pc',
input : 'libsystemd.pc.in',
output : 'libsystemd.pc',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : pkgconfiglibdir != 'no',
install_dir : pkgconfiglibdir)
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index 9c448e3639..bce123ae1c 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -301,8 +301,8 @@ static int verify_external_token(sd_bus *b, const char *p, size_t l) {
uid_t u;
int r;
- /* We don't do any real authentication here. Instead, we if
- * the owner of this bus wanted authentication he should have
+ /* We don't do any real authentication here. Instead, if
+ * the owner of this bus wanted authentication they should have
* checked SO_PEERCRED before even creating the bus object. */
if (!b->anonymous_auth && !b->ucred_valid)
@@ -968,7 +968,7 @@ int bus_socket_exec(sd_bus *b) {
_cleanup_free_ char *line = NULL;
if (b->exec_argv)
- line = quote_command_line(b->exec_argv);
+ line = quote_command_line(b->exec_argv, SHELL_ESCAPE_EMPTY);
log_debug("sd-bus: starting bus%s%s with %s%s",
b->description ? " " : "", strempty(b->description),
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 9ebf7b03c0..9e1d29cc1d 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -63,7 +63,6 @@
static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
static void bus_detach_io_events(sd_bus *b);
-static void bus_detach_inotify_event(sd_bus *b);
static thread_local sd_bus *default_system_bus = NULL;
static thread_local sd_bus *default_user_bus = NULL;
@@ -140,7 +139,7 @@ void bus_close_io_fds(sd_bus *b) {
void bus_close_inotify_fd(sd_bus *b) {
assert(b);
- bus_detach_inotify_event(b);
+ b->inotify_event_source = sd_event_source_disable_unref(b->inotify_event_source);
b->inotify_fd = safe_close(b->inotify_fd);
b->inotify_watches = mfree(b->inotify_watches);
@@ -3284,7 +3283,7 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
return e;
if (need_more)
- /* The caller really needs some more data, he doesn't
+ /* The caller really needs some more data, they don't
* care about what's already read, or any timeouts
* except its own. */
e |= POLLIN;
@@ -3747,15 +3746,8 @@ int bus_attach_io_events(sd_bus *bus) {
static void bus_detach_io_events(sd_bus *bus) {
assert(bus);
- if (bus->input_io_event_source) {
- sd_event_source_set_enabled(bus->input_io_event_source, SD_EVENT_OFF);
- bus->input_io_event_source = sd_event_source_unref(bus->input_io_event_source);
- }
-
- if (bus->output_io_event_source) {
- sd_event_source_set_enabled(bus->output_io_event_source, SD_EVENT_OFF);
- bus->output_io_event_source = sd_event_source_unref(bus->output_io_event_source);
- }
+ bus->input_io_event_source = sd_event_source_disable_unref(bus->input_io_event_source);
+ bus->output_io_event_source = sd_event_source_disable_unref(bus->output_io_event_source);
}
int bus_attach_inotify_event(sd_bus *bus) {
@@ -3787,15 +3779,6 @@ int bus_attach_inotify_event(sd_bus *bus) {
return 0;
}
-static void bus_detach_inotify_event(sd_bus *bus) {
- assert(bus);
-
- if (bus->inotify_event_source) {
- sd_event_source_set_enabled(bus->inotify_event_source, SD_EVENT_OFF);
- bus->inotify_event_source = sd_event_source_unref(bus->inotify_event_source);
- }
-}
-
_public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) {
int r;
@@ -3860,17 +3843,9 @@ _public_ int sd_bus_detach_event(sd_bus *bus) {
return 0;
bus_detach_io_events(bus);
- bus_detach_inotify_event(bus);
-
- if (bus->time_event_source) {
- sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF);
- bus->time_event_source = sd_event_source_unref(bus->time_event_source);
- }
-
- if (bus->quit_event_source) {
- sd_event_source_set_enabled(bus->quit_event_source, SD_EVENT_OFF);
- bus->quit_event_source = sd_event_source_unref(bus->quit_event_source);
- }
+ bus->inotify_event_source = sd_event_source_disable_unref(bus->inotify_event_source);
+ bus->time_event_source = sd_event_source_disable_unref(bus->time_event_source);
+ bus->quit_event_source = sd_event_source_disable_unref(bus->quit_event_source);
bus->event = sd_event_unref(bus->event);
return 1;
diff --git a/src/libsystemd/sd-event/event-source.h b/src/libsystemd/sd-event/event-source.h
index 08b409fef1..74cbc26962 100644
--- a/src/libsystemd/sd-event/event-source.h
+++ b/src/libsystemd/sd-event/event-source.h
@@ -71,6 +71,7 @@ struct sd_event_source {
uint64_t prepare_iteration;
sd_event_destroy_t destroy_callback;
+ sd_event_handler_t ratelimit_expire_callback;
LIST_FIELDS(sd_event_source, sources);
@@ -214,6 +215,11 @@ struct inotify_data {
* the events locally if they can't be coalesced). */
unsigned n_pending;
+ /* If this counter is non-zero, don't GC the inotify data object even if not used to watch any inode
+ * anymore. This is useful to pin the object for a bit longer, after the last event source needing it
+ * is gone. */
+ unsigned n_busy;
+
/* A linked list of all inotify objects with data already read, that still need processing. We keep this list
* to make it efficient to figure out what inotify objects to process data on next. */
LIST_FIELDS(struct inotify_data, buffered);
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index b06e00fae2..37efe3f425 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -1820,6 +1820,29 @@ static void event_free_inode_data(
free(d);
}
+static void event_gc_inotify_data(
+ sd_event *e,
+ struct inotify_data *d) {
+
+ assert(e);
+
+ /* GCs the inotify data object if we don't need it anymore. That's the case if we don't want to watch
+ * any inode with it anymore, which in turn happens if no event source of this priority is interested
+ * in any inode any longer. That said, we maintain an extra busy counter: if non-zero we'll delay GC
+ * (under the expectation that the GC is called again once the counter is decremented). */
+
+ if (!d)
+ return;
+
+ if (!hashmap_isempty(d->inodes))
+ return;
+
+ if (d->n_busy > 0)
+ return;
+
+ event_free_inotify_data(e, d);
+}
+
static void event_gc_inode_data(
sd_event *e,
struct inode_data *d) {
@@ -1837,8 +1860,7 @@ static void event_gc_inode_data(
inotify_data = d->inotify_data;
event_free_inode_data(e, d);
- if (inotify_data && hashmap_isempty(inotify_data->inodes))
- event_free_inotify_data(e, inotify_data);
+ event_gc_inotify_data(e, inotify_data);
}
static int event_make_inode_data(
@@ -1967,24 +1989,25 @@ static int inotify_exit_callback(sd_event_source *s, const struct inotify_event
return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
}
-_public_ int sd_event_add_inotify(
+static int event_add_inotify_fd_internal(
sd_event *e,
sd_event_source **ret,
- const char *path,
+ int fd,
+ bool donate,
uint32_t mask,
sd_event_inotify_handler_t callback,
void *userdata) {
+ _cleanup_close_ int donated_fd = donate ? fd : -1;
+ _cleanup_(source_freep) sd_event_source *s = NULL;
struct inotify_data *inotify_data = NULL;
struct inode_data *inode_data = NULL;
- _cleanup_close_ int fd = -1;
- _cleanup_(source_freep) sd_event_source *s = NULL;
struct stat st;
int r;
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
- assert_return(path, -EINVAL);
+ assert_return(fd >= 0, -EBADF);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1997,12 +2020,6 @@ _public_ int sd_event_add_inotify(
if (mask & IN_MASK_ADD)
return -EINVAL;
- fd = open(path, O_PATH|O_CLOEXEC|
- (mask & IN_ONLYDIR ? O_DIRECTORY : 0)|
- (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0));
- if (fd < 0)
- return -errno;
-
if (fstat(fd, &st) < 0)
return -errno;
@@ -2022,14 +2039,24 @@ _public_ int sd_event_add_inotify(
r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data);
if (r < 0) {
- event_free_inotify_data(e, inotify_data);
+ event_gc_inotify_data(e, inotify_data);
return r;
}
/* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of
* the event source, until then, for which we need the original inode. */
if (inode_data->fd < 0) {
- inode_data->fd = TAKE_FD(fd);
+ if (donated_fd >= 0)
+ inode_data->fd = TAKE_FD(donated_fd);
+ else {
+ inode_data->fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (inode_data->fd < 0) {
+ r = -errno;
+ event_gc_inode_data(e, inode_data);
+ return r;
+ }
+ }
+
LIST_PREPEND(to_close, e->inode_data_to_close, inode_data);
}
@@ -2042,8 +2069,6 @@ _public_ int sd_event_add_inotify(
if (r < 0)
return r;
- (void) sd_event_source_set_description(s, path);
-
if (ret)
*ret = s;
TAKE_PTR(s);
@@ -2051,6 +2076,48 @@ _public_ int sd_event_add_inotify(
return 0;
}
+_public_ int sd_event_add_inotify_fd(
+ sd_event *e,
+ sd_event_source **ret,
+ int fd,
+ uint32_t mask,
+ sd_event_inotify_handler_t callback,
+ void *userdata) {
+
+ return event_add_inotify_fd_internal(e, ret, fd, /* donate= */ false, mask, callback, userdata);
+}
+
+_public_ int sd_event_add_inotify(
+ sd_event *e,
+ sd_event_source **ret,
+ const char *path,
+ uint32_t mask,
+ sd_event_inotify_handler_t callback,
+ void *userdata) {
+
+ sd_event_source *s;
+ int fd, r;
+
+ assert_return(path, -EINVAL);
+
+ fd = open(path, O_PATH|O_CLOEXEC|
+ (mask & IN_ONLYDIR ? O_DIRECTORY : 0)|
+ (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0));
+ if (fd < 0)
+ return -errno;
+
+ r = event_add_inotify_fd_internal(e, &s, fd, /* donate= */ true, mask, callback, userdata);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(s, path);
+
+ if (ret)
+ *ret = s;
+
+ return r;
+}
+
static sd_event_source* event_source_free(sd_event_source *s) {
if (!s)
return NULL;
@@ -2841,7 +2908,7 @@ fail:
return r;
}
-static int event_source_leave_ratelimit(sd_event_source *s) {
+static int event_source_leave_ratelimit(sd_event_source *s, bool run_callback) {
int r;
assert(s);
@@ -2873,6 +2940,30 @@ static int event_source_leave_ratelimit(sd_event_source *s) {
ratelimit_reset(&s->rate_limit);
log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description));
+
+ if (run_callback && s->ratelimit_expire_callback) {
+ s->dispatching = true;
+ r = s->ratelimit_expire_callback(s, s->userdata);
+ s->dispatching = false;
+
+ if (r < 0) {
+ log_debug_errno(r, "Ratelimit expiry callback of event source %s (type %s) returned error, %s: %m",
+ strna(s->description),
+ event_source_type_to_string(s->type),
+ s->exit_on_failure ? "exiting" : "disabling");
+
+ if (s->exit_on_failure)
+ (void) sd_event_exit(s->event, r);
+ }
+
+ if (s->n_ref == 0)
+ source_free(s);
+ else if (r < 0)
+ sd_event_source_set_enabled(s, SD_EVENT_OFF);
+
+ return 1;
+ }
+
return 0;
fail:
@@ -3072,6 +3163,7 @@ static int process_timer(
struct clock_data *d) {
sd_event_source *s;
+ bool callback_invoked = false;
int r;
assert(e);
@@ -3089,9 +3181,11 @@ static int process_timer(
* again. */
assert(s->ratelimited);
- r = event_source_leave_ratelimit(s);
+ r = event_source_leave_ratelimit(s, /* run_callback */ true);
if (r < 0)
return r;
+ else if (r == 1)
+ callback_invoked = true;
continue;
}
@@ -3106,7 +3200,7 @@ static int process_timer(
event_source_time_prioq_reshuffle(s);
}
- return 0;
+ return callback_invoked;
}
static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priority) {
@@ -3556,13 +3650,23 @@ static int source_dispatch(sd_event_source *s) {
sz = offsetof(struct inotify_event, name) + d->buffer.ev.len;
assert(d->buffer_filled >= sz);
+ /* If the inotify callback destroys the event source then this likely means we don't need to
+ * watch the inode anymore, and thus also won't need the inotify object anymore. But if we'd
+ * free it immediately, then we couldn't drop the event from the inotify event queue without
+ * memory corruption anymore, as below. Hence, let's not free it immediately, but mark it
+ * "busy" with a counter (which will ensure it's not GC'ed away prematurely). Let's then
+ * explicitly GC it after we are done dropping the inotify event from the buffer. */
+ d->n_busy++;
r = s->inotify.callback(s, &d->buffer.ev, s->userdata);
+ d->n_busy--;
- /* When no event is pending anymore on this inotify object, then let's drop the event from the
- * buffer. */
+ /* When no event is pending anymore on this inotify object, then let's drop the event from
+ * the inotify event queue buffer. */
if (d->n_pending == 0)
event_inotify_data_drop(e, d, sz);
+ /* Now we don't want to access 'd' anymore, it's OK to GC now. */
+ event_gc_inotify_data(e, d);
break;
}
@@ -3587,7 +3691,7 @@ static int source_dispatch(sd_event_source *s) {
if (s->n_ref == 0)
source_free(s);
else if (r < 0)
- sd_event_source_set_enabled(s, SD_EVENT_OFF);
+ assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
return 1;
}
@@ -3628,7 +3732,7 @@ static int event_prepare(sd_event *e) {
if (s->n_ref == 0)
source_free(s);
else if (r < 0)
- sd_event_source_set_enabled(s, SD_EVENT_OFF);
+ assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
}
return 0;
@@ -4020,15 +4124,15 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
if (r < 0)
goto finish;
- r = process_timer(e, e->timestamp.realtime, &e->realtime);
+ r = process_inotify(e);
if (r < 0)
goto finish;
- r = process_timer(e, e->timestamp.boottime, &e->boottime);
+ r = process_timer(e, e->timestamp.realtime, &e->realtime);
if (r < 0)
goto finish;
- r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
+ r = process_timer(e, e->timestamp.boottime, &e->boottime);
if (r < 0)
goto finish;
@@ -4040,9 +4144,20 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
if (r < 0)
goto finish;
- r = process_inotify(e);
+ r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
if (r < 0)
goto finish;
+ else if (r == 1) {
+ /* Ratelimit expiry callback was called. Let's postpone processing pending sources and
+ * put loop in the initial state in order to evaluate (in the next iteration) also sources
+ * there were potentially re-enabled by the callback.
+ *
+ * Wondering why we treat only this invocation of process_timer() differently? Once event
+ * source is ratelimited we essentially transform it into CLOCK_MONOTONIC timer hence
+ * ratelimit expiry callback is never called for any other timer type. */
+ r = 0;
+ goto finish;
+ }
if (event_next_pending(e)) {
e->state = SD_EVENT_PENDING;
@@ -4411,7 +4526,7 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
/* When ratelimiting is configured we'll always reset the rate limit state first and start fresh,
* non-ratelimited. */
- r = event_source_leave_ratelimit(s);
+ r = event_source_leave_ratelimit(s, /* run_callback */ false);
if (r < 0)
return r;
@@ -4419,6 +4534,13 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
return 0;
}
+_public_ int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback) {
+ assert_return(s, -EINVAL);
+
+ s->ratelimit_expire_callback = callback;
+ return 0;
+}
+
_public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) {
assert_return(s, -EINVAL);
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 406d10d92b..0ac23c1118 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -623,6 +623,11 @@ static int ratelimit_time_handler(sd_event_source *s, uint64_t usec, void *userd
return 0;
}
+static int expired = -1;
+static int ratelimit_expired(sd_event_source *s, void *userdata) {
+ return ++expired;
+}
+
static void test_ratelimit(void) {
_cleanup_close_pair_ int p[2] = {-1, -1};
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
@@ -686,12 +691,19 @@ static void test_ratelimit(void) {
assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 10) >= 0);
+ /* Set callback that will be invoked when we leave rate limited state. */
+ assert_se(sd_event_source_set_ratelimit_expire_callback(s, ratelimit_expired) >= 0);
+
do {
assert_se(sd_event_run(e, UINT64_MAX) >= 0);
} while (!sd_event_source_is_ratelimited(s));
log_info("ratelimit_time_handler: called 10 more times, event source got ratelimited");
assert_se(count == 20);
+
+ /* Dispatch the event loop once more and check that ratelimit expiration callback got called */
+ assert_se(sd_event_run(e, UINT64_MAX) >= 0);
+ assert_se(expired == 0);
}
static void test_simple_timeout(void) {
@@ -713,6 +725,40 @@ static void test_simple_timeout(void) {
assert_se(t >= usec_add(f, some_time));
}
+static int inotify_self_destroy_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
+ sd_event_source **p = userdata;
+
+ assert_se(ev);
+ assert_se(p);
+ assert_se(*p == s);
+
+ assert_se(FLAGS_SET(ev->mask, IN_ATTRIB));
+
+ assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0);
+
+ *p = sd_event_source_unref(*p); /* here's what we actually intend to test: we destroy the event
+ * source from inside the event source handler */
+ return 1;
+}
+
+static void test_inotify_self_destroy(void) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ char path[] = "/tmp/inotifyXXXXXX";
+ _cleanup_close_ int fd = -1;
+
+ /* Tests that destroying an inotify event source from its own handler is safe */
+
+ assert_se(sd_event_default(&e) >= 0);
+
+ fd = mkostemp_safe(path);
+ assert_se(fd >= 0);
+ assert_se(sd_event_add_inotify_fd(e, &s, fd, IN_ATTRIB, inotify_self_destroy_handler, &s) >= 0);
+ fd = safe_close(fd);
+ assert_se(unlink(path) >= 0); /* This will trigger IN_ATTRIB because link count goes to zero */
+ assert_se(sd_event_loop(e) >= 0);
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
@@ -731,5 +777,7 @@ int main(int argc, char *argv[]) {
test_ratelimit();
+ test_inotify_self_destroy();
+
return 0;
}
diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c
index 19937110b0..7c66d1c2db 100644
--- a/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libsystemd/sd-id128/id128-util.c
@@ -219,7 +219,7 @@ int id128_get_product(sd_id128_t *ret) {
r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
if (r == -ENOENT)
- r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
+ r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-netlink/netlink-types-rtnl.c b/src/libsystemd/sd-netlink/netlink-types-rtnl.c
index 2c01751a52..624422eea2 100644
--- a/src/libsystemd/sd-netlink/netlink-types-rtnl.c
+++ b/src/libsystemd/sd-netlink/netlink-types-rtnl.c
@@ -968,7 +968,7 @@ DEFINE_TYPE_SYSTEM(rtnl_route);
static const NLType rtnl_neigh_types[] = {
[NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR },
- [NDA_LLADDR] = { /* struct ether_addr, struct in_addr, or struct in6_addr */ },
+ [NDA_LLADDR] = { .type = NETLINK_TYPE_ETHER_ADDR },
[NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) },
[NDA_PROBES] = { .type = NETLINK_TYPE_U32 },
[NDA_VLAN] = { .type = NETLINK_TYPE_U16 },
@@ -1028,9 +1028,23 @@ static const NLType rtnl_nexthop_types[] = {
DEFINE_TYPE_SYSTEM(rtnl_nexthop);
static const NLType rtnl_tca_option_data_cake_types[] = {
- [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
- [TCA_CAKE_OVERHEAD] = { .type = NETLINK_TYPE_S32 },
- [TCA_CAKE_MPU] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
+ [TCA_CAKE_DIFFSERV_MODE] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_ATM] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_FLOW_MODE] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_OVERHEAD] = { .type = NETLINK_TYPE_S32 },
+ [TCA_CAKE_RTT] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_TARGET] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_AUTORATE] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_MEMORY] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_NAT] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_RAW] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_WASH] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_MPU] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_INGRESS] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_ACK_FILTER] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_SPLIT_GSO] = { .type = NETLINK_TYPE_U32 },
+ [TCA_CAKE_FWMARK] = { .type = NETLINK_TYPE_U32 },
};
static const NLType rtnl_tca_option_data_codel_types[] = {
diff --git a/src/libsystemd/sd-network/network-util.c b/src/libsystemd/sd-network/network-util.c
index 4a648caa5d..971247714a 100644
--- a/src/libsystemd/sd-network/network-util.c
+++ b/src/libsystemd/sd-network/network-util.c
@@ -1,14 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include "sd-id128.h"
+#include "sd-network.h"
#include "alloc-util.h"
-#include "arphrd-list.h"
-#include "device-util.h"
-#include "fd-util.h"
#include "network-util.h"
-#include "siphash24.h"
-#include "sparse-endian.h"
#include "string-table.h"
#include "strv.h"
@@ -140,79 +135,3 @@ int parse_operational_state_range(const char *str, LinkOperationalStateRange *ou
return 0;
}
-
-int link_get_type_string(sd_device *device, unsigned short iftype, char **ret) {
- const char *t;
- char *p;
-
- if (device &&
- sd_device_get_devtype(device, &t) >= 0 &&
- !isempty(t)) {
- p = strdup(t);
- if (!p)
- return -ENOMEM;
-
- *ret = p;
- return 0;
- }
-
- t = arphrd_to_name(iftype);
- if (!t)
- return -ENOENT;
-
- p = strdup(t);
- if (!p)
- return -ENOMEM;
-
- *ret = ascii_strlower(p);
- return 0;
-}
-
-const char *net_get_name_persistent(sd_device *device) {
- const char *name, *field;
-
- assert(device);
-
- /* fetch some persistent data unique (on this machine) to this device */
- FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC")
- if (sd_device_get_property_value(device, field, &name) >= 0)
- return name;
-
- return NULL;
-}
-
-#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
-
-int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result) {
- size_t l, sz = 0;
- const char *name;
- int r;
- uint8_t *v;
-
- assert(device);
-
- /* net_get_name_persistent() will return one of the device names based on stable information about
- * the device. If this is not available, we fall back to using the actual device name. */
- name = net_get_name_persistent(device);
- if (!name && use_sysname)
- (void) sd_device_get_sysname(device, &name);
- if (!name)
- return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
- "No stable identifying information found");
-
- log_device_debug(device, "Using \"%s\" as stable identifying information", name);
- l = strlen(name);
- sz = sizeof(sd_id128_t) + l;
- v = newa(uint8_t, sz);
-
- /* Fetch some persistent data unique to this machine */
- r = sd_id128_get_machine((sd_id128_t*) v);
- if (r < 0)
- return r;
- memcpy(v + sizeof(sd_id128_t), name, l);
-
- /* Let's hash the machine ID plus the device name. We use
- * a fixed, but originally randomly created hash key here. */
- *result = htole64(siphash24(v, sz, HASH_KEY.bytes));
- return 0;
-}
diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h
index 3a2e4a7f6c..6e2ad28426 100644
--- a/src/libsystemd/sd-network/network-util.h
+++ b/src/libsystemd/sd-network/network-util.h
@@ -1,12 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include <inttypes.h>
+#include <errno.h>
#include <stdbool.h>
-#include "sd-device.h"
-#include "sd-network.h"
-
#include "macro.h"
bool network_is_online(void);
@@ -86,7 +83,3 @@ typedef struct LinkOperationalStateRange {
LINK_OPERSTATE_ROUTABLE }
int parse_operational_state_range(const char *str, LinkOperationalStateRange *out);
-
-int link_get_type_string(sd_device *device, unsigned short iftype, char **ret);
-int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result);
-const char *net_get_name_persistent(sd_device *device);
diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c
index c8af16a918..ad637bcf1d 100644
--- a/src/libsystemd/sd-resolve/sd-resolve.c
+++ b/src/libsystemd/sd-resolve/sd-resolve.c
@@ -1285,11 +1285,7 @@ _public_ int sd_resolve_detach_event(sd_resolve *resolve) {
if (!resolve->event)
return 0;
- if (resolve->event_source) {
- sd_event_source_set_enabled(resolve->event_source, SD_EVENT_OFF);
- resolve->event_source = sd_event_source_unref(resolve->event_source);
- }
-
+ resolve->event_source = sd_event_source_disable_unref(resolve->event_source);
resolve->event = sd_event_unref(resolve->event);
return 1;
}
diff --git a/src/libudev/meson.build b/src/libudev/meson.build
index 130374d14f..488ae62480 100644
--- a/src/libudev/meson.build
+++ b/src/libudev/meson.build
@@ -43,8 +43,7 @@ custom_target(
'libudev.pc',
input : 'libudev.pc.in',
output : 'libudev.pc',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : pkgconfiglibdir != 'no',
install_dir : pkgconfiglibdir)
diff --git a/src/locale/test-keymap-util.c b/src/locale/test-keymap-util.c
index a5d40af448..f726e8e524 100644
--- a/src/locale/test-keymap-util.c
+++ b/src/locale/test-keymap-util.c
@@ -196,11 +196,11 @@ int main(int argc, char **argv) {
test_find_language_fallback();
test_find_converted_keymap();
- test_find_legacy_keymap();
assert_se(get_testdata_dir("test-keymap-util/kbd-model-map", &map) >= 0);
assert_se(setenv("SYSTEMD_KBD_MODEL_MAP", map, 1) == 0);
+ test_find_legacy_keymap();
test_vconsole_convert_to_x11();
test_x11_convert_to_vconsole();
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
index 57d2d64e49..254a1a69fb 100644
--- a/src/login/logind-core.c
+++ b/src/login/logind-core.c
@@ -721,7 +721,9 @@ int manager_read_utmp(Manager *m) {
errno = 0;
u = getutxent();
if (!u) {
- if (errno != 0)
+ if (errno == ENOENT)
+ log_debug_errno(errno, _PATH_UTMPX " does not exist, ignoring.");
+ else if (errno != 0)
log_warning_errno(errno, "Failed to read " _PATH_UTMPX ", ignoring: %m");
return 0;
}
diff --git a/src/login/meson.build b/src/login/meson.build
index da704d238f..a583025a1b 100644
--- a/src/login/meson.build
+++ b/src/login/meson.build
@@ -81,8 +81,7 @@ foreach tuple : in_files
file,
input : file + '.in',
output: file,
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : tuple[2] and install,
install_dir : dir)
endforeach
diff --git a/src/network/generator/network-generator.c b/src/network/generator/network-generator.c
index c07b269f36..3185e10a83 100644
--- a/src/network/generator/network-generator.c
+++ b/src/network/generator/network-generator.c
@@ -353,7 +353,7 @@ static int network_set_mac_address(Context *context, const char *ifname, const c
if (!network)
return -ENODEV;
- return ether_addr_from_string(mac, &network->mac);
+ return parse_ether_addr(mac, &network->mac);
}
static int network_set_address(Context *context, const char *ifname, int family, unsigned char prefixlen,
@@ -909,7 +909,7 @@ static int parse_cmdline_ifname(Context *context, const char *key, const char *v
name = strndupa_safe(value, p - value);
- r = ether_addr_from_string(p + 1, &mac);
+ r = parse_ether_addr(p + 1, &mac);
if (r < 0)
return r;
diff --git a/src/network/netdev/bareudp.c b/src/network/netdev/bareudp.c
index e210625ac5..8c0c895a0d 100644
--- a/src/network/netdev/bareudp.c
+++ b/src/network/netdev/bareudp.c
@@ -17,55 +17,17 @@ DEFINE_STRING_TABLE_LOOKUP(bare_udp_protocol, BareUDPProtocol);
DEFINE_CONFIG_PARSE_ENUM(config_parse_bare_udp_iftype, bare_udp_protocol, BareUDPProtocol,
"Failed to parse EtherType=");
-/* callback for bareudp netdev's created without a backing Link */
-static int bare_udp_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
- int r;
-
- assert(netdev);
- assert(netdev->state != _NETDEV_STATE_INVALID);
-
- r = sd_netlink_message_get_errno(m);
- if (r == -EEXIST)
- log_netdev_info(netdev, "BareUDP netdev exists, using existing without changing its parameters.");
- else if (r < 0) {
- log_netdev_warning_errno(netdev, r, "BareUDP netdev could not be created: %m");
- netdev_enter_failed(netdev);
-
- return 1;
- }
-
- log_netdev_debug(netdev, "BareUDP created.");
-
- return 1;
-}
-
-static int netdev_bare_udp_create(NetDev *netdev) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+static int netdev_bare_udp_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
BareUDP *u;
int r;
assert(netdev);
+ assert(m);
u = BAREUDP(netdev);
assert(u);
- r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m");
-
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m");
-
- r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
-
- r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind));
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
-
r = sd_netlink_message_append_u16(m, IFLA_BAREUDP_ETHERTYPE, htobe16(u->iftype));
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BAREUDP_ETHERTYPE attribute: %m");
@@ -74,25 +36,7 @@ static int netdev_bare_udp_create(NetDev *netdev) {
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BAREUDP_PORT attribute: %m");
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
-
- r = netlink_call_async(netdev->manager->rtnl, NULL, m, bare_udp_netdev_create_handler,
- netdev_destroy_callback, netdev);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
-
- netdev_ref(netdev);
- netdev->state = NETDEV_STATE_CREATING;
-
- log_netdev_debug(netdev, "Creating");
-
- return r;
+ return 0;
}
static int netdev_bare_udp_verify(NetDev *netdev, const char *filename) {
@@ -133,6 +77,6 @@ const NetDevVTable bare_udp_vtable = {
.sections = NETDEV_COMMON_SECTIONS "BareUDP\0",
.init = bare_udp_init,
.config_verify = netdev_bare_udp_verify,
- .create = netdev_bare_udp_create,
+ .fill_message_create = netdev_bare_udp_fill_message_create,
.create_type = NETDEV_CREATE_INDEPENDENT,
};
diff --git a/src/network/netdev/bond.c b/src/network/netdev/bond.c
index a7a4adce45..1b66ed837c 100644
--- a/src/network/netdev/bond.c
+++ b/src/network/netdev/bond.c
@@ -386,7 +386,7 @@ int config_parse_ad_actor_system(
assert(rvalue);
assert(data);
- r = ether_addr_from_string(rvalue, &n);
+ r = parse_ether_addr(rvalue, &n);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Not a valid MAC address %s. Ignoring assignment: %m",
diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c
index 99fb9e1c3c..a4d68985b8 100644
--- a/src/network/netdev/bridge.c
+++ b/src/network/netdev/bridge.c
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
#include "bridge.h"
#include "netlink-util.h"
@@ -8,11 +10,16 @@
#include "string-table.h"
#include "vlan-util.h"
+assert_cc((int) MULTICAST_ROUTER_NONE == (int) MDB_RTR_TYPE_DISABLED);
+assert_cc((int) MULTICAST_ROUTER_TEMPORARY_QUERY == (int) MDB_RTR_TYPE_TEMP_QUERY);
+assert_cc((int) MULTICAST_ROUTER_PERMANENT == (int) MDB_RTR_TYPE_PERM);
+assert_cc((int) MULTICAST_ROUTER_TEMPORARY == (int) MDB_RTR_TYPE_TEMP);
+
static const char* const multicast_router_table[_MULTICAST_ROUTER_MAX] = {
- [MULTICAST_ROUTER_NONE] = "no",
+ [MULTICAST_ROUTER_NONE] = "no",
[MULTICAST_ROUTER_TEMPORARY_QUERY] = "query",
- [MULTICAST_ROUTER_PERMANENT] = "permanent",
- [MULTICAST_ROUTER_TEMPORARY] = "temporary",
+ [MULTICAST_ROUTER_PERMANENT] = "permanent",
+ [MULTICAST_ROUTER_TEMPORARY] = "temporary",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(multicast_router, MulticastRouter, _MULTICAST_ROUTER_INVALID);
diff --git a/src/network/netdev/bridge.h b/src/network/netdev/bridge.h
index 4510842022..a6f3224044 100644
--- a/src/network/netdev/bridge.h
+++ b/src/network/netdev/bridge.h
@@ -1,9 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include <netinet/in.h>
-#include <linux/if_bridge.h>
-
#include "conf-parser.h"
#include "netdev.h"
@@ -30,10 +27,10 @@ typedef struct Bridge {
} Bridge;
typedef enum MulticastRouter {
- MULTICAST_ROUTER_NONE = MDB_RTR_TYPE_DISABLED,
- MULTICAST_ROUTER_TEMPORARY_QUERY = MDB_RTR_TYPE_TEMP_QUERY,
- MULTICAST_ROUTER_PERMANENT = MDB_RTR_TYPE_PERM,
- MULTICAST_ROUTER_TEMPORARY = MDB_RTR_TYPE_TEMP,
+ MULTICAST_ROUTER_NONE,
+ MULTICAST_ROUTER_TEMPORARY_QUERY,
+ MULTICAST_ROUTER_PERMANENT,
+ MULTICAST_ROUTER_TEMPORARY,
_MULTICAST_ROUTER_MAX,
_MULTICAST_ROUTER_INVALID = -EINVAL,
} MulticastRouter;
diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c
index 1bffbc9586..46f5ea41c8 100644
--- a/src/network/netdev/geneve.c
+++ b/src/network/netdev/geneve.c
@@ -25,65 +25,15 @@ static const char* const geneve_df_table[_NETDEV_GENEVE_DF_MAX] = {
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(geneve_df, GeneveDF, NETDEV_GENEVE_DF_YES);
DEFINE_CONFIG_PARSE_ENUM(config_parse_geneve_df, geneve_df, GeneveDF, "Failed to parse Geneve IPDoNotFragment= setting");
-/* callback for geneve netdev's created without a backing Link */
-static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
- int r;
-
- assert(netdev);
- assert(netdev->state != _NETDEV_STATE_INVALID);
-
- r = sd_netlink_message_get_errno(m);
- if (r == -EEXIST)
- log_netdev_info(netdev, "Geneve netdev exists, using existing without changing its parameters");
- else if (r < 0) {
- log_netdev_warning_errno(netdev, r, "Geneve netdev could not be created: %m");
- netdev_enter_failed(netdev);
-
- return 1;
- }
-
- log_netdev_debug(netdev, "Geneve created");
-
- return 1;
-}
-
-static int netdev_geneve_create(NetDev *netdev) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+static int netdev_geneve_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
Geneve *v;
int r;
assert(netdev);
+ assert(m);
v = GENEVE(netdev);
- r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m");
-
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m");
-
- if (netdev->mac) {
- r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m");
- }
-
- if (netdev->mtu != 0) {
- r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m");
- }
-
- r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
-
- r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind));
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
-
if (v->id <= GENEVE_VID_MAX) {
r = sd_netlink_message_append_u32(m, IFLA_GENEVE_ID, v->id);
if (r < 0)
@@ -143,25 +93,7 @@ static int netdev_geneve_create(NetDev *netdev) {
return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_DF attribute: %m");
}
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
-
- r = netlink_call_async(netdev->manager->rtnl, NULL, m, geneve_netdev_create_handler,
- netdev_destroy_callback, netdev);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
-
- netdev_ref(netdev);
- netdev->state = NETDEV_STATE_CREATING;
-
- log_netdev_debug(netdev, "Creating");
-
- return r;
+ return 0;
}
int config_parse_geneve_vni(
@@ -357,7 +289,7 @@ const NetDevVTable geneve_vtable = {
.object_size = sizeof(Geneve),
.init = geneve_init,
.sections = NETDEV_COMMON_SECTIONS "GENEVE\0",
- .create = netdev_geneve_create,
+ .fill_message_create = netdev_geneve_fill_message_create,
.create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = netdev_geneve_verify,
.generate_mac = true,
diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c
index 3b0c940804..b50d93710d 100644
--- a/src/network/netdev/macsec.c
+++ b/src/network/netdev/macsec.c
@@ -620,7 +620,7 @@ int config_parse_macsec_hw_address(
if (r < 0)
return log_oom();
- r = ether_addr_from_string(rvalue, b ? &b->sci.mac : &c->sci.mac);
+ r = parse_ether_addr(rvalue, b ? &b->sci.mac : &c->sci.mac);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse MAC address for secure channel identifier. "
diff --git a/src/network/netdev/macvlan.c b/src/network/netdev/macvlan.c
index 9d037c2f36..c295cae2cd 100644
--- a/src/network/netdev/macvlan.c
+++ b/src/network/netdev/macvlan.c
@@ -127,7 +127,7 @@ static void macvlan_done(NetDev *n) {
assert(m);
- set_free_free(m->match_source_mac);
+ set_free(m->match_source_mac);
}
static void macvlan_init(NetDev *n) {
diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf
index 9dd081425e..878d15d6c8 100644
--- a/src/network/netdev/netdev-gperf.gperf
+++ b/src/network/netdev/netdev-gperf.gperf
@@ -49,7 +49,7 @@ NetDev.Description, config_parse_string,
NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname)
NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind)
NetDev.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(NetDev, mtu)
-NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac)
+NetDev.MACAddress, config_parse_ether_addr, 0, offsetof(NetDev, mac)
VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id)
VLAN.Protocol, config_parse_vlanprotocol, 0, offsetof(VLan, protocol)
VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp)
@@ -59,10 +59,10 @@ VLAN.ReorderHeader, config_parse_tristate,
VLAN.EgressQOSMaps, config_parse_vlan_qos_maps, 0, offsetof(VLan, egress_qos_maps)
VLAN.IngressQOSMaps, config_parse_vlan_qos_maps, 0, offsetof(VLan, ingress_qos_maps)
MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
-MACVLAN.SourceMACAddress, config_parse_hwaddrs, 0, offsetof(MacVlan, match_source_mac)
+MACVLAN.SourceMACAddress, config_parse_ether_addrs, 0, offsetof(MacVlan, match_source_mac)
MACVLAN.BroadcastMulticastQueueLength, config_parse_macvlan_broadcast_queue_size, 0, offsetof(MacVlan, bc_queue_length)
MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode)
-MACVTAP.SourceMACAddress, config_parse_hwaddrs, 0, offsetof(MacVlan, match_source_mac)
+MACVTAP.SourceMACAddress, config_parse_ether_addrs, 0, offsetof(MacVlan, match_source_mac)
IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags)
IPVTAP.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
@@ -111,7 +111,7 @@ L2TPSession.PeerSessionId, config_parse_l2tp_session_id,
L2TPSession.Layer2SpecificHeader, config_parse_l2tp_session_l2spec, 0, 0
L2TPSession.Name, config_parse_l2tp_session_name, 0, 0
Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
-Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
+Peer.MACAddress, config_parse_ether_addr, 0, offsetof(Veth, mac_peer)
VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer)
VXLAN.VNI, config_parse_uint32, 0, offsetof(VxLan, vni)
VXLAN.Id, config_parse_uint32, 0, offsetof(VxLan, vni) /* deprecated */
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index ce14d52186..b3db55f199 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -46,81 +46,81 @@
#include "xfrm.h"
const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
- [NETDEV_KIND_BATADV] = &batadv_vtable,
- [NETDEV_KIND_BRIDGE] = &bridge_vtable,
- [NETDEV_KIND_BOND] = &bond_vtable,
- [NETDEV_KIND_VLAN] = &vlan_vtable,
- [NETDEV_KIND_MACVLAN] = &macvlan_vtable,
- [NETDEV_KIND_MACVTAP] = &macvtap_vtable,
- [NETDEV_KIND_IPVLAN] = &ipvlan_vtable,
- [NETDEV_KIND_IPVTAP] = &ipvtap_vtable,
- [NETDEV_KIND_VXLAN] = &vxlan_vtable,
- [NETDEV_KIND_IPIP] = &ipip_vtable,
- [NETDEV_KIND_GRE] = &gre_vtable,
- [NETDEV_KIND_GRETAP] = &gretap_vtable,
- [NETDEV_KIND_IP6GRE] = &ip6gre_vtable,
+ [NETDEV_KIND_BAREUDP] = &bare_udp_vtable,
+ [NETDEV_KIND_BATADV] = &batadv_vtable,
+ [NETDEV_KIND_BOND] = &bond_vtable,
+ [NETDEV_KIND_BRIDGE] = &bridge_vtable,
+ [NETDEV_KIND_DUMMY] = &dummy_vtable,
+ [NETDEV_KIND_ERSPAN] = &erspan_vtable,
+ [NETDEV_KIND_FOU] = &foutnl_vtable,
+ [NETDEV_KIND_GENEVE] = &geneve_vtable,
+ [NETDEV_KIND_GRE] = &gre_vtable,
+ [NETDEV_KIND_GRETAP] = &gretap_vtable,
+ [NETDEV_KIND_IFB] = &ifb_vtable,
+ [NETDEV_KIND_IP6GRE] = &ip6gre_vtable,
[NETDEV_KIND_IP6GRETAP] = &ip6gretap_vtable,
- [NETDEV_KIND_SIT] = &sit_vtable,
- [NETDEV_KIND_VTI] = &vti_vtable,
- [NETDEV_KIND_VTI6] = &vti6_vtable,
- [NETDEV_KIND_VETH] = &veth_vtable,
- [NETDEV_KIND_DUMMY] = &dummy_vtable,
- [NETDEV_KIND_TUN] = &tun_vtable,
- [NETDEV_KIND_TAP] = &tap_vtable,
- [NETDEV_KIND_IP6TNL] = &ip6tnl_vtable,
- [NETDEV_KIND_VRF] = &vrf_vtable,
- [NETDEV_KIND_VCAN] = &vcan_vtable,
- [NETDEV_KIND_GENEVE] = &geneve_vtable,
- [NETDEV_KIND_VXCAN] = &vxcan_vtable,
- [NETDEV_KIND_WIREGUARD] = &wireguard_vtable,
+ [NETDEV_KIND_IP6TNL] = &ip6tnl_vtable,
+ [NETDEV_KIND_IPIP] = &ipip_vtable,
+ [NETDEV_KIND_IPVLAN] = &ipvlan_vtable,
+ [NETDEV_KIND_IPVTAP] = &ipvtap_vtable,
+ [NETDEV_KIND_L2TP] = &l2tptnl_vtable,
+ [NETDEV_KIND_MACSEC] = &macsec_vtable,
+ [NETDEV_KIND_MACVLAN] = &macvlan_vtable,
+ [NETDEV_KIND_MACVTAP] = &macvtap_vtable,
[NETDEV_KIND_NETDEVSIM] = &netdevsim_vtable,
- [NETDEV_KIND_FOU] = &foutnl_vtable,
- [NETDEV_KIND_ERSPAN] = &erspan_vtable,
- [NETDEV_KIND_L2TP] = &l2tptnl_vtable,
- [NETDEV_KIND_MACSEC] = &macsec_vtable,
- [NETDEV_KIND_NLMON] = &nlmon_vtable,
- [NETDEV_KIND_XFRM] = &xfrm_vtable,
- [NETDEV_KIND_IFB] = &ifb_vtable,
- [NETDEV_KIND_BAREUDP] = &bare_udp_vtable,
+ [NETDEV_KIND_NLMON] = &nlmon_vtable,
+ [NETDEV_KIND_SIT] = &sit_vtable,
+ [NETDEV_KIND_TAP] = &tap_vtable,
+ [NETDEV_KIND_TUN] = &tun_vtable,
+ [NETDEV_KIND_VCAN] = &vcan_vtable,
+ [NETDEV_KIND_VETH] = &veth_vtable,
+ [NETDEV_KIND_VLAN] = &vlan_vtable,
+ [NETDEV_KIND_VRF] = &vrf_vtable,
+ [NETDEV_KIND_VTI6] = &vti6_vtable,
+ [NETDEV_KIND_VTI] = &vti_vtable,
+ [NETDEV_KIND_VXCAN] = &vxcan_vtable,
+ [NETDEV_KIND_VXLAN] = &vxlan_vtable,
+ [NETDEV_KIND_WIREGUARD] = &wireguard_vtable,
+ [NETDEV_KIND_XFRM] = &xfrm_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
- [NETDEV_KIND_BAREUDP] = "bareudp",
- [NETDEV_KIND_BATADV] = "batadv",
- [NETDEV_KIND_BRIDGE] = "bridge",
- [NETDEV_KIND_BOND] = "bond",
- [NETDEV_KIND_VLAN] = "vlan",
- [NETDEV_KIND_MACVLAN] = "macvlan",
- [NETDEV_KIND_MACVTAP] = "macvtap",
- [NETDEV_KIND_IPVLAN] = "ipvlan",
- [NETDEV_KIND_IPVTAP] = "ipvtap",
- [NETDEV_KIND_VXLAN] = "vxlan",
- [NETDEV_KIND_IPIP] = "ipip",
- [NETDEV_KIND_GRE] = "gre",
- [NETDEV_KIND_GRETAP] = "gretap",
- [NETDEV_KIND_IP6GRE] = "ip6gre",
+ [NETDEV_KIND_BAREUDP] = "bareudp",
+ [NETDEV_KIND_BATADV] = "batadv",
+ [NETDEV_KIND_BOND] = "bond",
+ [NETDEV_KIND_BRIDGE] = "bridge",
+ [NETDEV_KIND_DUMMY] = "dummy",
+ [NETDEV_KIND_ERSPAN] = "erspan",
+ [NETDEV_KIND_FOU] = "fou",
+ [NETDEV_KIND_GENEVE] = "geneve",
+ [NETDEV_KIND_GRE] = "gre",
+ [NETDEV_KIND_GRETAP] = "gretap",
+ [NETDEV_KIND_IFB] = "ifb",
+ [NETDEV_KIND_IP6GRE] = "ip6gre",
[NETDEV_KIND_IP6GRETAP] = "ip6gretap",
- [NETDEV_KIND_SIT] = "sit",
- [NETDEV_KIND_VETH] = "veth",
- [NETDEV_KIND_VTI] = "vti",
- [NETDEV_KIND_VTI6] = "vti6",
- [NETDEV_KIND_DUMMY] = "dummy",
- [NETDEV_KIND_TUN] = "tun",
- [NETDEV_KIND_TAP] = "tap",
- [NETDEV_KIND_IP6TNL] = "ip6tnl",
- [NETDEV_KIND_VRF] = "vrf",
- [NETDEV_KIND_VCAN] = "vcan",
- [NETDEV_KIND_GENEVE] = "geneve",
- [NETDEV_KIND_VXCAN] = "vxcan",
- [NETDEV_KIND_WIREGUARD] = "wireguard",
+ [NETDEV_KIND_IP6TNL] = "ip6tnl",
+ [NETDEV_KIND_IPIP] = "ipip",
+ [NETDEV_KIND_IPVLAN] = "ipvlan",
+ [NETDEV_KIND_IPVTAP] = "ipvtap",
+ [NETDEV_KIND_L2TP] = "l2tp",
+ [NETDEV_KIND_MACSEC] = "macsec",
+ [NETDEV_KIND_MACVLAN] = "macvlan",
+ [NETDEV_KIND_MACVTAP] = "macvtap",
[NETDEV_KIND_NETDEVSIM] = "netdevsim",
- [NETDEV_KIND_FOU] = "fou",
- [NETDEV_KIND_ERSPAN] = "erspan",
- [NETDEV_KIND_L2TP] = "l2tp",
- [NETDEV_KIND_MACSEC] = "macsec",
- [NETDEV_KIND_NLMON] = "nlmon",
- [NETDEV_KIND_XFRM] = "xfrm",
- [NETDEV_KIND_IFB] = "ifb",
+ [NETDEV_KIND_NLMON] = "nlmon",
+ [NETDEV_KIND_SIT] = "sit",
+ [NETDEV_KIND_TAP] = "tap",
+ [NETDEV_KIND_TUN] = "tun",
+ [NETDEV_KIND_VCAN] = "vcan",
+ [NETDEV_KIND_VETH] = "veth",
+ [NETDEV_KIND_VLAN] = "vlan",
+ [NETDEV_KIND_VRF] = "vrf",
+ [NETDEV_KIND_VTI6] = "vti6",
+ [NETDEV_KIND_VTI] = "vti",
+ [NETDEV_KIND_VXCAN] = "vxcan",
+ [NETDEV_KIND_VXLAN] = "vxlan",
+ [NETDEV_KIND_WIREGUARD] = "wireguard",
+ [NETDEV_KIND_XFRM] = "xfrm",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
@@ -468,6 +468,7 @@ int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
}
static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(netdev);
@@ -482,82 +483,80 @@ static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handle
return r;
log_netdev_debug(netdev, "Created");
- } else {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ return 0;
+ }
+
+ r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m");
- r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m");
+
+ if (netdev->mac) {
+ r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac);
if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m");
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m");
+ }
- r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname);
+ if (netdev->mtu != 0) {
+ r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu);
if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m");
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m");
+ }
- if (netdev->mac) {
- r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m");
- }
+ if (link) {
+ r = sd_netlink_message_append_u32(m, IFLA_LINK, link->ifindex);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINK attribute: %m");
+ }
- if (netdev->mtu != 0) {
- r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m");
- }
+ r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
- if (link) {
- r = sd_netlink_message_append_u32(m, IFLA_LINK, link->ifindex);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINK attribute: %m");
- }
+ if (NETDEV_VTABLE(netdev)->fill_message_create) {
+ r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
- r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
+ r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m);
if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
-
- if (NETDEV_VTABLE(netdev)->fill_message_create) {
- r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind));
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
-
- r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_close_container(m);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
- } else {
- r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, netdev_kind_to_string(netdev->kind));
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_KIND attribute: %m");
- }
+ return r;
r = sd_netlink_message_close_container(m);
if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
-
- if (link) {
- r = netlink_call_async(netdev->manager->rtnl, NULL, m, callback,
- link_netlink_destroy_callback, link);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
-
- link_ref(link);
- } else {
- r = netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler,
- netdev_destroy_callback, netdev);
- if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
-
- netdev_ref(netdev);
- }
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m");
+ } else {
+ r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, netdev_kind_to_string(netdev->kind));
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_KIND attribute: %m");
+ }
+
+ r = sd_netlink_message_close_container(m);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
+
+ if (link) {
+ r = netlink_call_async(netdev->manager->rtnl, NULL, m, callback,
+ link_netlink_destroy_callback, link);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
- netdev->state = NETDEV_STATE_CREATING;
+ link_ref(link);
+ } else {
+ r = netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler,
+ netdev_destroy_callback, netdev);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m");
- log_netdev_debug(netdev, "Creating");
+ netdev_ref(netdev);
}
+ netdev->state = NETDEV_STATE_CREATING;
+
+ log_netdev_debug(netdev, "Creating");
return 0;
}
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index 4614f6566e..10dff0177f 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -23,15 +23,15 @@
"-L2TP\0" \
"-L2TPSession\0" \
"-MACsec\0" \
+ "-MACsecReceiveAssociation\0" \
"-MACsecReceiveChannel\0" \
"-MACsecTransmitAssociation\0" \
- "-MACsecReceiveAssociation\0" \
- "-MACVTAP\0" \
"-MACVLAN\0" \
- "-Tunnel\0" \
- "-Tun\0" \
- "-Tap\0" \
+ "-MACVTAP\0" \
"-Peer\0" \
+ "-Tap\0" \
+ "-Tun\0" \
+ "-Tunnel\0" \
"-VLAN\0" \
"-VRF\0" \
"-VXCAN\0" \
@@ -41,42 +41,42 @@
"-Xfrm\0"
typedef enum NetDevKind {
- NETDEV_KIND_BRIDGE,
+ NETDEV_KIND_BAREUDP,
+ NETDEV_KIND_BATADV,
NETDEV_KIND_BOND,
- NETDEV_KIND_VLAN,
- NETDEV_KIND_MACVLAN,
- NETDEV_KIND_MACVTAP,
- NETDEV_KIND_IPVLAN,
- NETDEV_KIND_IPVTAP,
- NETDEV_KIND_VXLAN,
- NETDEV_KIND_IPIP,
+ NETDEV_KIND_BRIDGE,
+ NETDEV_KIND_DUMMY,
+ NETDEV_KIND_ERSPAN,
+ NETDEV_KIND_FOU,
+ NETDEV_KIND_GENEVE,
NETDEV_KIND_GRE,
NETDEV_KIND_GRETAP,
+ NETDEV_KIND_IFB,
NETDEV_KIND_IP6GRE,
NETDEV_KIND_IP6GRETAP,
+ NETDEV_KIND_IP6TNL,
+ NETDEV_KIND_IPIP,
+ NETDEV_KIND_IPVLAN,
+ NETDEV_KIND_IPVTAP,
+ NETDEV_KIND_L2TP,
+ NETDEV_KIND_MACSEC,
+ NETDEV_KIND_MACVLAN,
+ NETDEV_KIND_MACVTAP,
+ NETDEV_KIND_NETDEVSIM,
+ NETDEV_KIND_NLMON,
NETDEV_KIND_SIT,
+ NETDEV_KIND_TAP,
+ NETDEV_KIND_TUN,
+ NETDEV_KIND_VCAN,
NETDEV_KIND_VETH,
+ NETDEV_KIND_VLAN,
+ NETDEV_KIND_VRF,
NETDEV_KIND_VTI,
NETDEV_KIND_VTI6,
- NETDEV_KIND_IP6TNL,
- NETDEV_KIND_DUMMY,
- NETDEV_KIND_TUN,
- NETDEV_KIND_TAP,
- NETDEV_KIND_VRF,
- NETDEV_KIND_VCAN,
- NETDEV_KIND_GENEVE,
NETDEV_KIND_VXCAN,
+ NETDEV_KIND_VXLAN,
NETDEV_KIND_WIREGUARD,
- NETDEV_KIND_NETDEVSIM,
- NETDEV_KIND_FOU,
- NETDEV_KIND_ERSPAN,
- NETDEV_KIND_L2TP,
- NETDEV_KIND_MACSEC,
- NETDEV_KIND_NLMON,
NETDEV_KIND_XFRM,
- NETDEV_KIND_IFB,
- NETDEV_KIND_BAREUDP,
- NETDEV_KIND_BATADV,
_NETDEV_KIND_MAX,
_NETDEV_KIND_TUNNEL, /* Used by config_parse_stacked_netdev() */
_NETDEV_KIND_INVALID = -EINVAL,
@@ -159,7 +159,7 @@ typedef struct NetDevVTable {
/* verify that compulsory configuration options were specified */
int (*config_verify)(NetDev *netdev, const char *filename);
- /* Generate MAC address or not When MACAddress= is not specified. */
+ /* Generate MAC address when MACAddress= is not specified. */
bool generate_mac;
} NetDevVTable;
diff --git a/src/network/netdev/vrf.c b/src/network/netdev/vrf.c
index ae71ae916c..1ba1916f1a 100644
--- a/src/network/netdev/vrf.c
+++ b/src/network/netdev/vrf.c
@@ -18,7 +18,7 @@ static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink
r = sd_netlink_message_append_u32(m, IFLA_VRF_TABLE, v->table);
if (r < 0)
- return log_netdev_error_errno(netdev, r, "Could not append IPLA_VRF_TABLE attribute: %m");
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_VRF_TABLE attribute: %m");
return r;
}
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 6dd05145c9..b47cd8cd7b 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -42,6 +42,7 @@
#include "macro.h"
#include "macvlan-util.h"
#include "main-func.h"
+#include "netif-util.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "network-util.h"
@@ -553,7 +554,7 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b
info->has_mac_address =
netlink_message_read_hw_addr(m, IFLA_ADDRESS, &info->hw_address) >= 0 &&
- !hw_addr_is_null(&info->hw_address);
+ info->hw_address.length > 0;
info->has_permanent_mac_address =
ethtool_get_permanent_macaddr(NULL, info->name, &info->permanent_mac_address) >= 0 &&
@@ -845,7 +846,7 @@ static int list_links(int argc, char *argv[], void *userdata) {
setup_state = strdup("unmanaged");
setup_state_to_color(setup_state, &on_color_setup, NULL);
- r = link_get_type_string(links[i].sd_device, links[i].iftype, &t);
+ r = net_get_type_string(links[i].sd_device, links[i].iftype, &t);
if (r == -ENOMEM)
return log_oom();
@@ -1568,7 +1569,7 @@ static int link_status_one(
(void) sd_device_get_property_value(info->sd_device, "ID_MODEL", &model);
}
- r = link_get_type_string(info->sd_device, info->iftype, &t);
+ r = net_get_type_string(info->sd_device, info->iftype, &t);
if (r == -ENOMEM)
return log_oom();
diff --git a/src/network/networkd-bridge-fdb.c b/src/network/networkd-bridge-fdb.c
index ab1b766d48..1298727a72 100644
--- a/src/network/networkd-bridge-fdb.c
+++ b/src/network/networkd-bridge-fdb.c
@@ -286,7 +286,7 @@ int config_parse_fdb_hwaddr(
if (r < 0)
return log_oom();
- r = ether_addr_from_string(rvalue, &fdb->mac_addr);
+ r = parse_ether_addr(rvalue, &fdb->mac_addr);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
return 0;
diff --git a/src/network/networkd-bridge-mdb.c b/src/network/networkd-bridge-mdb.c
index ed8a10cad8..10025a97ae 100644
--- a/src/network/networkd-bridge-mdb.c
+++ b/src/network/networkd-bridge-mdb.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <net/if.h>
+#include <linux/if_bridge.h>
#include "netlink-util.h"
#include "networkd-bridge-mdb.h"
diff --git a/src/network/networkd-dhcp-server-static-lease.c b/src/network/networkd-dhcp-server-static-lease.c
index 0a9b7def25..6acd838e2b 100644
--- a/src/network/networkd-dhcp-server-static-lease.c
+++ b/src/network/networkd-dhcp-server-static-lease.c
@@ -184,7 +184,7 @@ int config_parse_dhcp_static_lease_hwaddr(
return 0;
}
- r = ether_addr_from_string(rvalue, &hwaddr);
+ r = parse_ether_addr(rvalue, &hwaddr);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse MAC address for DHCPv4 static lease, ignoring assignment: %s", rvalue);
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index a3e5b4ba74..d05182fde7 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -3,7 +3,7 @@
#include <netinet/in.h>
#include <linux/if.h>
-#include "network-internal.h"
+#include "netif-util.h"
#include "networkd-address.h"
#include "networkd-ipv4ll.h"
#include "networkd-link.h"
diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c
index 2bae2f2913..7148c2ee14 100644
--- a/src/network/networkd-json.c
+++ b/src/network/networkd-json.c
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include "network-util.h"
+#include "netif-util.h"
#include "networkd-json.h"
#include "networkd-link.h"
#include "networkd-manager.h"
@@ -45,7 +45,7 @@ int link_build_json(Link *link, JsonVariant **ret) {
assert(link);
assert(ret);
- r = link_get_type_string(link->sd_device, link->iftype, &type);
+ r = net_get_type_string(link->sd_device, link->iftype, &type);
if (r == -ENOMEM)
return r;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index ab2d42a482..5136a8820e 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -2036,7 +2036,7 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message)
if (hw_addr_equal(&link->hw_addr, &addr))
return 0;
- if (hw_addr_is_null(&link->hw_addr))
+ if (link->hw_addr.length == 0)
log_link_debug(link, "Saved hardware address: %s", HW_ADDR_TO_STR(&addr));
else {
log_link_debug(link, "Hardware address is changed: %s → %s",
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 5f831b58a4..470c969329 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -290,7 +290,6 @@ static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
}
static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
- _cleanup_(route_freep) Route *route = NULL;
usec_t lifetime_usec, timestamp_usec;
struct in6_addr gateway;
uint16_t lifetime_sec;
@@ -299,8 +298,13 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
int r;
assert(link);
+ assert(link->network);
assert(rt);
+ if (!link->network->ipv6_accept_ra_use_gateway &&
+ hashmap_isempty(link->network->routes_by_section))
+ return 0;
+
r = sd_ndisc_router_get_lifetime(rt, &lifetime_sec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m");
@@ -339,23 +343,29 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m");
}
- r = route_new(&route);
- if (r < 0)
- return log_oom();
+ if (link->network->ipv6_accept_ra_use_gateway) {
+ _cleanup_(route_freep) Route *route = NULL;
- route->family = AF_INET6;
- route->pref = preference;
- route->gw_family = AF_INET6;
- route->gw.in6 = gateway;
- route->lifetime_usec = lifetime_usec;
- route->mtu = mtu;
+ r = route_new(&route);
+ if (r < 0)
+ return log_oom();
- r = ndisc_request_route(TAKE_PTR(route), link, rt);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not request default route: %m");
+ route->family = AF_INET6;
+ route->pref = preference;
+ route->gw_family = AF_INET6;
+ route->gw.in6 = gateway;
+ route->lifetime_usec = lifetime_usec;
+ route->mtu = mtu;
+
+ r = ndisc_request_route(TAKE_PTR(route), link, rt);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not request default route: %m");
+ }
Route *route_gw;
HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
+ _cleanup_(route_freep) Route *route = NULL;
+
if (!route_gw->gateway_from_dhcp_or_ra)
continue;
@@ -390,8 +400,12 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
int r;
assert(link);
+ assert(link->network);
assert(rt);
+ if (!link->network->ipv6_accept_ra_use_autonomous_prefix)
+ return 0;
+
r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &timestamp_usec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
@@ -479,8 +493,12 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
int r;
assert(link);
+ assert(link->network);
assert(rt);
+ if (!link->network->ipv6_accept_ra_use_onlink_prefix)
+ return 0;
+
r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_sec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
@@ -516,6 +534,56 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
return 0;
}
+static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) {
+ unsigned prefixlen;
+ struct in6_addr a;
+ uint8_t flags;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(rt);
+
+ r = sd_ndisc_router_prefix_get_address(rt, &a);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix address: %m");
+
+ r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix length: %m");
+
+ if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *b = NULL;
+
+ (void) in6_addr_prefix_to_string(&a, prefixlen, &b);
+ if (!set_isempty(link->network->ndisc_allow_listed_prefix))
+ log_link_debug(link, "Prefix '%s' is not in allow list, ignoring", strna(b));
+ else
+ log_link_debug(link, "Prefix '%s' is in deny list, ignoring", strna(b));
+ }
+ return 0;
+ }
+
+ r = sd_ndisc_router_prefix_get_flags(rt, &flags);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get RA prefix flags: %m");
+
+ if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
+ r = ndisc_router_process_onlink_prefix(link, rt);
+ if (r < 0)
+ return r;
+ }
+
+ if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
+ r = ndisc_router_process_autonomous_prefix(link, rt);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
unsigned preference, prefixlen;
@@ -526,6 +594,9 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
assert(link);
+ if (!link->network->ipv6_accept_ra_use_route_prefix)
+ return 0;
+
r = sd_ndisc_router_route_get_lifetime(rt, &lifetime_sec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get route lifetime from RA: %m");
@@ -619,8 +690,12 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
int n, r;
assert(link);
+ assert(link->network);
assert(rt);
+ if (!link->network->ipv6_accept_ra_use_dns)
+ return 0;
+
r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
@@ -710,8 +785,12 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
int r;
assert(link);
+ assert(link->network);
assert(rt);
+ if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_NO)
+ return 0;
+
r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
@@ -775,11 +854,13 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
}
static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
+ int r;
+
assert(link);
assert(link->network);
assert(rt);
- for (int r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
+ for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
uint8_t type;
if (r < 0)
@@ -793,51 +874,11 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
switch (type) {
- case SD_NDISC_OPTION_PREFIX_INFORMATION: {
- unsigned prefixlen;
- struct in6_addr a;
- uint8_t flags;
-
- r = sd_ndisc_router_prefix_get_address(rt, &a);
+ case SD_NDISC_OPTION_PREFIX_INFORMATION:
+ r = ndisc_router_process_prefix(link, rt);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to get prefix address: %m");
-
- r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get prefix length: %m");
-
- if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
- if (DEBUG_LOGGING) {
- _cleanup_free_ char *b = NULL;
-
- (void) in6_addr_prefix_to_string(&a, prefixlen, &b);
- if (!set_isempty(link->network->ndisc_allow_listed_prefix))
- log_link_debug(link, "Prefix '%s' is not in allow list, ignoring", strna(b));
- else
- log_link_debug(link, "Prefix '%s' is in deny list, ignoring", strna(b));
- }
- break;
- }
-
- r = sd_ndisc_router_prefix_get_flags(rt, &flags);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get RA prefix flags: %m");
-
- if (link->network->ipv6_accept_ra_use_onlink_prefix &&
- FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
- r = ndisc_router_process_onlink_prefix(link, rt);
- if (r < 0)
- return r;
- }
-
- if (link->network->ipv6_accept_ra_use_autonomous_prefix &&
- FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
- r = ndisc_router_process_autonomous_prefix(link, rt);
- if (r < 0)
- return r;
- }
+ return r;
break;
- }
case SD_NDISC_OPTION_ROUTE_INFORMATION:
r = ndisc_router_process_route(link, rt);
@@ -846,19 +887,15 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
break;
case SD_NDISC_OPTION_RDNSS:
- if (link->network->ipv6_accept_ra_use_dns) {
- r = ndisc_router_process_rdnss(link, rt);
- if (r < 0)
- return r;
- }
+ r = ndisc_router_process_rdnss(link, rt);
+ if (r < 0)
+ return r;
break;
case SD_NDISC_OPTION_DNSSL:
- if (link->network->ipv6_accept_ra_use_dns) {
- r = ndisc_router_process_dnssl(link, rt);
- if (r < 0)
- return r;
- }
+ r = ndisc_router_process_dnssl(link, rt);
+ if (r < 0)
+ return r;
break;
}
}
@@ -961,6 +998,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
r = ndisc_router_process_default(link, rt);
if (r < 0)
return r;
+
r = ndisc_router_process_options(link, rt);
if (r < 0)
return r;
diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c
index aaa3f2f1ad..1766095e53 100644
--- a/src/network/networkd-neighbor.c
+++ b/src/network/networkd-neighbor.c
@@ -91,7 +91,6 @@ void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
assert(neighbor);
siphash24_compress(&neighbor->family, sizeof(neighbor->family), state);
- siphash24_compress(&neighbor->lladdr_size, sizeof(neighbor->lladdr_size), state);
switch (neighbor->family) {
case AF_INET:
@@ -104,7 +103,7 @@ void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
break;
}
- siphash24_compress(&neighbor->lladdr, neighbor->lladdr_size, state);
+ hw_addr_hash_func(&neighbor->ll_addr, state);
}
int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
@@ -114,10 +113,6 @@ int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
if (r != 0)
return r;
- r = CMP(a->lladdr_size, b->lladdr_size);
- if (r != 0)
- return r;
-
switch (a->family) {
case AF_INET:
case AF_INET6:
@@ -126,7 +121,7 @@ int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
return r;
}
- return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size);
+ return hw_addr_compare(&a->ll_addr, &b->ll_addr);
}
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free);
@@ -163,7 +158,7 @@ static int neighbor_add(Link *link, Neighbor *neighbor) {
}
static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const Link *link) {
- _cleanup_free_ char *state = NULL, *lladdr = NULL, *dst = NULL;
+ _cleanup_free_ char *state = NULL, *dst = NULL;
assert(neighbor);
assert(str);
@@ -172,19 +167,12 @@ static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const
return;
(void) network_config_state_to_string_alloc(neighbor->state, &state);
- if (neighbor->lladdr_size == sizeof(struct ether_addr))
- (void) ether_addr_to_string_alloc(&neighbor->lladdr.mac, &lladdr);
- else if (neighbor->lladdr_size == sizeof(struct in_addr))
- (void) in_addr_to_string(AF_INET, &neighbor->lladdr.ip, &lladdr);
- else if (neighbor->lladdr_size == sizeof(struct in6_addr))
- (void) in_addr_to_string(AF_INET6, &neighbor->lladdr.ip, &lladdr);
-
(void) in_addr_to_string(neighbor->family, &neighbor->in_addr, &dst);
log_link_debug(link,
"%s %s neighbor (%s): lladdr: %s, dst: %s",
str, strna(network_config_source_to_string(neighbor->source)), strna(state),
- strna(lladdr), strna(dst));
+ HW_ADDR_TO_STR(&neighbor->ll_addr), strna(dst));
}
static int neighbor_configure(
@@ -213,7 +201,7 @@ static int neighbor_configure(
if (r < 0)
return log_link_error_errno(link, r, "Could not set state: %m");
- r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr, neighbor->lladdr_size);
+ r = netlink_message_append_hw_addr(req, NDA_LLADDR, &neighbor->ll_addr);
if (r < 0)
return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m");
@@ -466,7 +454,6 @@ int request_process_neighbor(Request *req) {
int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
_cleanup_(neighbor_freep) Neighbor *tmp = NULL;
- _cleanup_free_ void *lladdr = NULL;
Neighbor *neighbor = NULL;
uint16_t type, state;
int ifindex, r;
@@ -536,15 +523,11 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
return 0;
}
- r = sd_netlink_message_read_data(message, NDA_LLADDR, &tmp->lladdr_size, &lladdr);
+ r = netlink_message_read_hw_addr(message, NDA_LLADDR, &tmp->ll_addr);
if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received neighbor message without valid lladdr, ignoring: %m");
- return 0;
- } else if (!IN_SET(tmp->lladdr_size, sizeof(struct ether_addr), sizeof(struct in_addr), sizeof(struct in6_addr))) {
- log_link_warning(link, "rtnl: received neighbor message with invalid lladdr size (%zu), ignoring: %m", tmp->lladdr_size);
+ log_link_warning_errno(link, r, "rtnl: received neighbor message without valid link layer address, ignoring: %m");
return 0;
}
- memcpy(&tmp->lladdr, lladdr, tmp->lladdr_size);
(void) neighbor_get(link, tmp, &neighbor);
@@ -596,7 +579,7 @@ static int neighbor_section_verify(Neighbor *neighbor) {
"Ignoring [Neighbor] section from line %u.",
neighbor->section->filename, neighbor->section->line);
- if (neighbor->lladdr_size == 0)
+ if (neighbor->ll_addr.length == 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Neighbor section without LinkLayerAddress= configured. "
"Ignoring [Neighbor] section from line %u.",
@@ -628,20 +611,27 @@ int config_parse_neighbor_address(
void *data,
void *userdata) {
- Network *network = userdata;
_cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
+ Network *network = userdata;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
- assert(data);
+ assert(userdata);
r = neighbor_new_static(network, filename, section_line, &n);
if (r < 0)
return log_oom();
+ if (isempty(rvalue)) {
+ n->family = AF_UNSPEC;
+ n->in_addr = IN_ADDR_NULL;
+ TAKE_PTR(n);
+ return 0;
+ }
+
r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
@@ -650,7 +640,6 @@ int config_parse_neighbor_address(
}
TAKE_PTR(n);
-
return 0;
}
@@ -666,74 +655,34 @@ int config_parse_neighbor_lladdr(
void *data,
void *userdata) {
- Network *network = userdata;
_cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
- int family, r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = neighbor_new_static(network, filename, section_line, &n);
- if (r < 0)
- return log_oom();
-
- r = ether_addr_from_string(rvalue, &n->lladdr.mac);
- if (r >= 0)
- n->lladdr_size = sizeof(n->lladdr.mac);
- else {
- r = in_addr_from_string_auto(rvalue, &family, &n->lladdr.ip);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s",
- rvalue);
- return 0;
- }
- n->lladdr_size = family == AF_INET ? sizeof(n->lladdr.ip.in) : sizeof(n->lladdr.ip.in6);
- }
-
- TAKE_PTR(n);
-
- return 0;
-}
-
-int config_parse_neighbor_hwaddr(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
Network *network = userdata;
- _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
- assert(data);
+ assert(userdata);
r = neighbor_new_static(network, filename, section_line, &n);
if (r < 0)
return log_oom();
- r = ether_addr_from_string(rvalue, &n->lladdr.mac);
+ if (isempty(rvalue)) {
+ n->ll_addr = HW_ADDR_NULL;
+ TAKE_PTR(n);
+ return 0;
+ }
+
+ r = parse_hw_addr(rvalue, &n->ll_addr);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
- "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue);
+ "Neighbor %s= is invalid, ignoring assignment: %s",
+ lvalue, rvalue);
return 0;
}
- n->lladdr_size = sizeof(n->lladdr.mac);
TAKE_PTR(n);
-
return 0;
}
diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h
index d4f2ff55f2..e9e1854110 100644
--- a/src/network/networkd-neighbor.h
+++ b/src/network/networkd-neighbor.h
@@ -15,11 +15,6 @@ typedef struct Manager Manager;
typedef struct Network Network;
typedef struct Request Request;
-union lladdr_union {
- struct ether_addr mac;
- union in_addr_union ip;
-};
-
typedef struct Neighbor {
Network *network;
Link *link;
@@ -29,8 +24,7 @@ typedef struct Neighbor {
int family;
union in_addr_union in_addr;
- union lladdr_union lladdr;
- size_t lladdr_size;
+ struct hw_addr_data ll_addr;
} Neighbor;
Neighbor *neighbor_free(Neighbor *neighbor);
@@ -52,5 +46,4 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Neighbor, neighbor);
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address);
-CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_hwaddr);
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_lladdr);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 1f0a625183..515a5b5bd9 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -45,14 +45,14 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.MACAddress, config_parse_hwaddrs, 0, offsetof(Network, match.mac)
-Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(Network, match.permanent_mac)
+Match.MACAddress, config_parse_ether_addrs, 0, offsetof(Network, match.mac)
+Match.PermanentMACAddress, config_parse_ether_addrs, 0, offsetof(Network, match.permanent_mac)
Match.Path, config_parse_match_strv, 0, offsetof(Network, match.path)
Match.Driver, config_parse_match_strv, 0, offsetof(Network, match.driver)
Match.Type, config_parse_match_strv, 0, offsetof(Network, match.iftype)
Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match.wlan_iftype)
Match.SSID, config_parse_match_strv, 0, offsetof(Network, match.ssid)
-Match.BSSID, config_parse_hwaddrs, 0, offsetof(Network, match.bssid)
+Match.BSSID, config_parse_ether_addrs, 0, offsetof(Network, match.bssid)
Match.Name, config_parse_match_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(Network, match.ifname)
Match.Property, config_parse_match_property, 0, offsetof(Network, match.property)
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions)
@@ -61,7 +61,7 @@ Match.KernelCommandLine, config_parse_net_condition,
Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions)
Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(Network, conditions)
-Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
+Link.MACAddress, config_parse_ether_addr, 0, offsetof(Network, mac)
Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(Network, mtu)
Link.Group, config_parse_link_group, 0, 0
Link.ARP, config_parse_tristate, 0, offsetof(Network, arp)
@@ -156,7 +156,7 @@ IPv6AddressLabel.Prefix, config_parse_address_label_prefix,
IPv6AddressLabel.Label, config_parse_address_label, 0, 0
Neighbor.Address, config_parse_neighbor_address, 0, 0
Neighbor.LinkLayerAddress, config_parse_neighbor_lladdr, 0, 0
-Neighbor.MACAddress, config_parse_neighbor_hwaddr, 0, 0 /* deprecated */
+Neighbor.MACAddress, config_parse_neighbor_lladdr, 0, 0 /* deprecated */
RoutingPolicyRule.TypeOfService, config_parse_routing_policy_rule_tos, 0, 0
RoutingPolicyRule.Priority, config_parse_routing_policy_rule_priority, 0, 0
RoutingPolicyRule.Table, config_parse_routing_policy_rule_table, 0, 0
@@ -255,6 +255,8 @@ DHCPv6.IAID, config_parse_iaid,
DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Network, dhcp6_duid)
DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, dhcp6_duid)
DHCPv6.RouteTable, config_parse_dhcp_or_ra_route_table, (RTPROT_DHCP<<16) | AF_INET6, 0
+IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_gateway)
+IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_route_prefix)
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
@@ -386,7 +388,17 @@ BFIFO.LimitBytes, config_parse_bfifo_size,
CAKE.Parent, config_parse_qdisc_parent, QDISC_KIND_CAKE, 0
CAKE.Handle, config_parse_qdisc_handle, QDISC_KIND_CAKE, 0
CAKE.Bandwidth, config_parse_cake_bandwidth, QDISC_KIND_CAKE, 0
+CAKE.AutoRateIngress, config_parse_cake_tristate, QDISC_KIND_CAKE, 0
CAKE.OverheadBytes, config_parse_cake_overhead, QDISC_KIND_CAKE, 0
+CAKE.MPUBytes, config_parse_cake_mpu, QDISC_KIND_CAKE, 0
+CAKE.CompensationMode, config_parse_cake_compensation_mode, QDISC_KIND_CAKE, 0
+CAKE.UseRawPacketSize, config_parse_cake_tristate, QDISC_KIND_CAKE, 0
+CAKE.FlowIsolationMode, config_parse_cake_flow_isolation_mode, QDISC_KIND_CAKE, 0
+CAKE.NAT, config_parse_cake_tristate, QDISC_KIND_CAKE, 0
+CAKE.PriorityQueueingPreset, config_parse_cake_priority_queueing_preset, QDISC_KIND_CAKE, 0
+CAKE.FirewallMark, config_parse_cake_fwmark, QDISC_KIND_CAKE, 0
+CAKE.Wash, config_parse_cake_tristate, QDISC_KIND_CAKE, 0
+CAKE.SplitGSO, config_parse_cake_tristate, QDISC_KIND_CAKE, 0
ControlledDelay.Parent, config_parse_qdisc_parent, QDISC_KIND_CODEL, 0
ControlledDelay.Handle, config_parse_qdisc_handle, QDISC_KIND_CODEL, 0
ControlledDelay.PacketLimit, config_parse_controlled_delay_u32, QDISC_KIND_CODEL, 0
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 1c00b98660..a41bc25cca 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -77,16 +77,16 @@ static int network_resolve_netdev_one(Network *network, const char *name, NetDev
if (netdev->kind != kind && !(kind == _NETDEV_KIND_TUNNEL &&
IN_SET(netdev->kind,
- NETDEV_KIND_IPIP,
- NETDEV_KIND_SIT,
+ NETDEV_KIND_ERSPAN,
NETDEV_KIND_GRE,
NETDEV_KIND_GRETAP,
NETDEV_KIND_IP6GRE,
NETDEV_KIND_IP6GRETAP,
- NETDEV_KIND_VTI,
- NETDEV_KIND_VTI6,
NETDEV_KIND_IP6TNL,
- NETDEV_KIND_ERSPAN)))
+ NETDEV_KIND_IPIP,
+ NETDEV_KIND_SIT,
+ NETDEV_KIND_VTI,
+ NETDEV_KIND_VTI6)))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: NetDev %s is not a %s, ignoring assignment",
network->filename, name, kind_string);
@@ -464,6 +464,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.ipv6_accept_ra = -1,
.ipv6_accept_ra_use_dns = true,
+ .ipv6_accept_ra_use_gateway = true,
+ .ipv6_accept_ra_use_route_prefix = true,
.ipv6_accept_ra_use_autonomous_prefix = true,
.ipv6_accept_ra_use_onlink_prefix = true,
.ipv6_accept_ra_use_mtu = true,
@@ -823,10 +825,16 @@ int config_parse_stacked_netdev(
assert(rvalue);
assert(data);
assert(IN_SET(kind,
- NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
- NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVTAP, NETDEV_KIND_VXLAN,
- NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL,
- NETDEV_KIND_XFRM));
+ NETDEV_KIND_IPVLAN,
+ NETDEV_KIND_IPVTAP,
+ NETDEV_KIND_L2TP,
+ NETDEV_KIND_MACSEC,
+ NETDEV_KIND_MACVLAN,
+ NETDEV_KIND_MACVTAP,
+ NETDEV_KIND_VLAN,
+ NETDEV_KIND_VXLAN,
+ NETDEV_KIND_XFRM,
+ _NETDEV_KIND_TUNNEL));
if (!ifname_valid(rvalue)) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index f5258c9fcd..c86a492a17 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -301,6 +301,8 @@ struct Network {
/* IPv6 accept RA */
int ipv6_accept_ra;
bool ipv6_accept_ra_use_dns;
+ bool ipv6_accept_ra_use_gateway;
+ bool ipv6_accept_ra_use_route_prefix;
bool ipv6_accept_ra_use_autonomous_prefix;
bool ipv6_accept_ra_use_onlink_prefix;
bool ipv6_accept_ra_use_mtu;
diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c
index 54243e7968..cc44b400b2 100644
--- a/src/network/networkd-setlink.c
+++ b/src/network/networkd-setlink.c
@@ -3,6 +3,7 @@
#include <netinet/in.h>
#include <linux/if.h>
#include <linux/if_arp.h>
+#include <linux/if_bridge.h>
#include "missing_network.h"
#include "netlink-util.h"
diff --git a/src/network/networkd-sriov.c b/src/network/networkd-sriov.c
index 106560974e..6da0f83521 100644
--- a/src/network/networkd-sriov.c
+++ b/src/network/networkd-sriov.c
@@ -523,7 +523,7 @@ int config_parse_sr_iov_mac(
return 0;
}
- r = ether_addr_from_string(rvalue, &sr_iov->mac);
+ 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);
diff --git a/src/network/tc/cake.c b/src/network/tc/cake.c
index 70dd095e93..b4cbc4f970 100644
--- a/src/network/tc/cake.c
+++ b/src/network/tc/cake.c
@@ -9,8 +9,28 @@
#include "netlink-util.h"
#include "parse-util.h"
#include "qdisc.h"
+#include "string-table.h"
#include "string-util.h"
+static int cake_init(QDisc *qdisc) {
+ CommonApplicationsKeptEnhanced *c;
+
+ assert(qdisc);
+
+ c = CAKE(qdisc);
+
+ c->autorate = -1;
+ c->compensation_mode = _CAKE_COMPENSATION_MODE_INVALID;
+ c->raw = -1;
+ c->flow_isolation_mode = _CAKE_FLOW_ISOLATION_MODE_INVALID;
+ c->nat = -1;
+ c->preset = _CAKE_PRESET_INVALID;
+ c->wash = -1;
+ c->split_gso = -1;
+
+ return 0;
+}
+
static int cake_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
CommonApplicationsKeptEnhanced *c;
int r;
@@ -31,9 +51,72 @@ static int cake_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req)
return log_link_error_errno(link, r, "Could not append TCA_CAKE_BASE_RATE64 attribute: %m");
}
- r = sd_netlink_message_append_s32(req, TCA_CAKE_OVERHEAD, c->overhead);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append TCA_CAKE_OVERHEAD attribute: %m");
+ if (c->autorate >= 0) {
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_AUTORATE, c->autorate);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_AUTORATE attribute: %m");
+ }
+
+ if (c->overhead_set) {
+ r = sd_netlink_message_append_s32(req, TCA_CAKE_OVERHEAD, c->overhead);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_OVERHEAD attribute: %m");
+ }
+
+ if (c->mpu > 0) {
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_MPU, c->mpu);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_MPU attribute: %m");
+ }
+
+ if (c->compensation_mode >= 0) {
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_ATM, c->compensation_mode);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_ATM attribute: %m");
+ }
+
+ if (c->raw > 0) {
+ /* TCA_CAKE_RAW attribute is mostly a flag, not boolean. */
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_RAW, 0);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_RAW attribute: %m");
+ }
+
+ if (c->flow_isolation_mode >= 0) {
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_FLOW_MODE, c->flow_isolation_mode);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_FLOW_MODE attribute: %m");
+ }
+
+ if (c->nat >= 0) {
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_NAT, c->nat);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_NAT attribute: %m");
+ }
+
+ if (c->preset >= 0) {
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_DIFFSERV_MODE, c->preset);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_DIFFSERV_MODE attribute: %m");
+ }
+
+ if (c->fwmark > 0) {
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_FWMARK, c->fwmark);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_FWMARK attribute: %m");
+ }
+
+ if (c->wash >= 0) {
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_WASH, c->wash);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_WASH attribute: %m");
+ }
+
+ if (c->split_gso >= 0) {
+ r = sd_netlink_message_append_u32(req, TCA_CAKE_SPLIT_GSO, c->split_gso);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append TCA_CAKE_SPLIT_GSO attribute: %m");
+ }
r = sd_netlink_message_close_container(req);
if (r < 0)
@@ -132,7 +215,7 @@ int config_parse_cake_overhead(
c = CAKE(qdisc);
if (isempty(rvalue)) {
- c->overhead = 0;
+ c->overhead_set = false;
TAKE_PTR(qdisc);
return 0;
}
@@ -152,6 +235,380 @@ int config_parse_cake_overhead(
}
c->overhead = v;
+ c->overhead_set = true;
+ TAKE_PTR(qdisc);
+ return 0;
+}
+
+int config_parse_cake_mpu(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ CommonApplicationsKeptEnhanced *c;
+ Network *network = data;
+ uint32_t v;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+ return 0;
+ }
+
+ c = CAKE(qdisc);
+
+ if (isempty(rvalue)) {
+ c->mpu = 0;
+ TAKE_PTR(qdisc);
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &v);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+ if (v <= 0 || v > 256) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ c->mpu = v;
+ TAKE_PTR(qdisc);
+ return 0;
+}
+
+int config_parse_cake_tristate(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ CommonApplicationsKeptEnhanced *c;
+ Network *network = data;
+ int *dest, r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+ return 0;
+ }
+
+ c = CAKE(qdisc);
+
+ if (streq(lvalue, "AutoRateIngress"))
+ dest = &c->autorate;
+ else if (streq(lvalue, "UseRawPacketSize"))
+ dest = &c->raw;
+ else if (streq(lvalue, "NAT"))
+ dest = &c->nat;
+ else if (streq(lvalue, "Wash"))
+ dest = &c->wash;
+ else if (streq(lvalue, "SplitGSO"))
+ dest = &c->split_gso;
+ else
+ assert_not_reached();
+
+ if (isempty(rvalue)) {
+ *dest = -1;
+ TAKE_PTR(qdisc);
+ return 0;
+ }
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ *dest = r;
+ TAKE_PTR(qdisc);
+ return 0;
+}
+
+static const char * const cake_compensation_mode_table[_CAKE_COMPENSATION_MODE_MAX] = {
+ [CAKE_COMPENSATION_MODE_NONE] = "none",
+ [CAKE_COMPENSATION_MODE_ATM] = "atm",
+ [CAKE_COMPENSATION_MODE_PTM] = "ptm",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cake_compensation_mode, CakeCompensationMode);
+
+int config_parse_cake_compensation_mode(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ CommonApplicationsKeptEnhanced *c;
+ Network *network = data;
+ CakeCompensationMode mode;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+ return 0;
+ }
+
+ c = CAKE(qdisc);
+
+ if (isempty(rvalue)) {
+ c->compensation_mode = _CAKE_COMPENSATION_MODE_INVALID;
+ TAKE_PTR(qdisc);
+ return 0;
+ }
+
+ mode = cake_compensation_mode_from_string(rvalue);
+ if (mode < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, mode,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ c->compensation_mode = mode;
+ TAKE_PTR(qdisc);
+ return 0;
+}
+
+static const char * const cake_flow_isolation_mode_table[_CAKE_FLOW_ISOLATION_MODE_MAX] = {
+ [CAKE_FLOW_ISOLATION_MODE_NONE] = "none",
+ [CAKE_FLOW_ISOLATION_MODE_SRC_IP] = "src-host",
+ [CAKE_FLOW_ISOLATION_MODE_DST_IP] = "dst-host",
+ [CAKE_FLOW_ISOLATION_MODE_HOSTS] = "hosts",
+ [CAKE_FLOW_ISOLATION_MODE_FLOWS] = "flows",
+ [CAKE_FLOW_ISOLATION_MODE_DUAL_SRC] = "dual-src-host",
+ [CAKE_FLOW_ISOLATION_MODE_DUAL_DST] = "dual-dst-host",
+ [CAKE_FLOW_ISOLATION_MODE_TRIPLE] = "triple",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cake_flow_isolation_mode, CakeFlowIsolationMode);
+
+int config_parse_cake_flow_isolation_mode(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ CommonApplicationsKeptEnhanced *c;
+ Network *network = data;
+ CakeFlowIsolationMode mode;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+ return 0;
+ }
+
+ c = CAKE(qdisc);
+
+ if (isempty(rvalue)) {
+ c->flow_isolation_mode = _CAKE_FLOW_ISOLATION_MODE_INVALID;
+ TAKE_PTR(qdisc);
+ return 0;
+ }
+
+ mode = cake_flow_isolation_mode_from_string(rvalue);
+ if (mode < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, mode,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ c->flow_isolation_mode = mode;
+ TAKE_PTR(qdisc);
+ return 0;
+}
+
+static const char * const cake_priority_queueing_preset_table[_CAKE_PRESET_MAX] = {
+ [CAKE_PRESET_DIFFSERV3] = "diffserv3",
+ [CAKE_PRESET_DIFFSERV4] = "diffserv4",
+ [CAKE_PRESET_DIFFSERV8] = "diffserv8",
+ [CAKE_PRESET_BESTEFFORT] = "besteffort",
+ [CAKE_PRESET_PRECEDENCE] = "precedence",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(cake_priority_queueing_preset, CakePriorityQueueingPreset);
+
+int config_parse_cake_priority_queueing_preset(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ CommonApplicationsKeptEnhanced *c;
+ CakePriorityQueueingPreset preset;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+ return 0;
+ }
+
+ c = CAKE(qdisc);
+
+ if (isempty(rvalue)) {
+ c->preset = _CAKE_PRESET_INVALID;
+ TAKE_PTR(qdisc);
+ return 0;
+ }
+
+ preset = cake_priority_queueing_preset_from_string(rvalue);
+ if (preset < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, preset,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ c->preset = preset;
+ TAKE_PTR(qdisc);
+ return 0;
+}
+
+int config_parse_cake_fwmark(
+ 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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+ CommonApplicationsKeptEnhanced *c;
+ Network *network = data;
+ uint32_t fwmark;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "More than one kind of queueing discipline, ignoring assignment: %m");
+ return 0;
+ }
+
+ c = CAKE(qdisc);
+
+ if (isempty(rvalue)) {
+ c->fwmark = 0;
+ TAKE_PTR(qdisc);
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &fwmark);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+ if (fwmark <= 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid '%s=', ignoring assignment: %s",
+ lvalue, rvalue);
+ return 0;
+ }
+
+ c->fwmark = fwmark;
TAKE_PTR(qdisc);
return 0;
}
@@ -159,5 +616,6 @@ int config_parse_cake_overhead(
const QDiscVTable cake_vtable = {
.object_size = sizeof(CommonApplicationsKeptEnhanced),
.tca_kind = "cake",
+ .init = cake_init,
.fill_message = cake_fill_message,
};
diff --git a/src/network/tc/cake.h b/src/network/tc/cake.h
index 1da28b7d20..ff68cedabf 100644
--- a/src/network/tc/cake.h
+++ b/src/network/tc/cake.h
@@ -2,15 +2,68 @@
* Copyright © 2020 VMware, Inc. */
#pragma once
+#include <linux/pkt_sched.h>
+
#include "conf-parser.h"
#include "qdisc.h"
+typedef enum CakeCompensationMode {
+ CAKE_COMPENSATION_MODE_NONE = CAKE_ATM_NONE,
+ CAKE_COMPENSATION_MODE_ATM = CAKE_ATM_ATM,
+ CAKE_COMPENSATION_MODE_PTM = CAKE_ATM_PTM,
+ _CAKE_COMPENSATION_MODE_MAX,
+ _CAKE_COMPENSATION_MODE_INVALID = -EINVAL,
+} CakeCompensationMode;
+
+typedef enum CakeFlowIsolationMode {
+ CAKE_FLOW_ISOLATION_MODE_NONE = CAKE_FLOW_NONE,
+ CAKE_FLOW_ISOLATION_MODE_SRC_IP = CAKE_FLOW_SRC_IP,
+ CAKE_FLOW_ISOLATION_MODE_DST_IP = CAKE_FLOW_DST_IP,
+ CAKE_FLOW_ISOLATION_MODE_HOSTS = CAKE_FLOW_HOSTS,
+ CAKE_FLOW_ISOLATION_MODE_FLOWS = CAKE_FLOW_FLOWS,
+ CAKE_FLOW_ISOLATION_MODE_DUAL_SRC = CAKE_FLOW_DUAL_SRC,
+ CAKE_FLOW_ISOLATION_MODE_DUAL_DST = CAKE_FLOW_DUAL_DST,
+ CAKE_FLOW_ISOLATION_MODE_TRIPLE = CAKE_FLOW_TRIPLE,
+ _CAKE_FLOW_ISOLATION_MODE_MAX,
+ _CAKE_FLOW_ISOLATION_MODE_INVALID = -EINVAL,
+} CakeFlowIsolationMode;
+
+typedef enum CakePriorityQueueingPreset {
+ CAKE_PRESET_DIFFSERV3 = CAKE_DIFFSERV_DIFFSERV3,
+ CAKE_PRESET_DIFFSERV4 = CAKE_DIFFSERV_DIFFSERV4,
+ CAKE_PRESET_DIFFSERV8 = CAKE_DIFFSERV_DIFFSERV8,
+ CAKE_PRESET_BESTEFFORT = CAKE_DIFFSERV_BESTEFFORT,
+ CAKE_PRESET_PRECEDENCE = CAKE_DIFFSERV_PRECEDENCE,
+ _CAKE_PRESET_MAX,
+ _CAKE_PRESET_INVALID = -EINVAL,
+} CakePriorityQueueingPreset;
+
typedef struct CommonApplicationsKeptEnhanced {
QDisc meta;
- int overhead;
+ /* Shaper parameters */
+ int autorate;
uint64_t bandwidth;
+ /* Overhead compensation parameters */
+ bool overhead_set;
+ int overhead;
+ uint32_t mpu;
+ CakeCompensationMode compensation_mode;
+ int raw;
+
+ /* Flow isolation parameters */
+ CakeFlowIsolationMode flow_isolation_mode;
+ int nat;
+
+ /* Priority queue parameters */
+ CakePriorityQueueingPreset preset;
+ uint32_t fwmark;
+
+ /* Other parameters */
+ int wash;
+ int split_gso;
+
} CommonApplicationsKeptEnhanced;
DEFINE_QDISC_CAST(CAKE, CommonApplicationsKeptEnhanced);
@@ -18,3 +71,9 @@ extern const QDiscVTable cake_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_cake_bandwidth);
CONFIG_PARSER_PROTOTYPE(config_parse_cake_overhead);
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_mpu);
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_tristate);
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_compensation_mode);
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_flow_isolation_mode);
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_priority_queueing_preset);
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_fwmark);
diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c
index 3d7ce3aca8..4b00a98086 100644
--- a/src/network/test-networkd-conf.c
+++ b/src/network/test-networkd-conf.c
@@ -51,11 +51,11 @@ static void test_config_parse_duid_rawdata_one(const char *rvalue, int ret, cons
}
}
-static void test_config_parse_hwaddr_one(const char *rvalue, int ret, const struct ether_addr* expected) {
+static void test_config_parse_ether_addr_one(const char *rvalue, int ret, const struct ether_addr* expected) {
struct ether_addr *actual = NULL;
int r;
- r = config_parse_hwaddr("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL);
+ r = config_parse_ether_addr("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL);
assert_se(ret == r);
if (expected) {
assert_se(actual);
@@ -66,10 +66,10 @@ static void test_config_parse_hwaddr_one(const char *rvalue, int ret, const stru
free(actual);
}
-static void test_config_parse_hwaddrs_one(const char *rvalue, const struct ether_addr* list, size_t n) {
+static void test_config_parse_ether_addrs_one(const char *rvalue, const struct ether_addr* list, size_t n) {
_cleanup_set_free_free_ Set *s = NULL;
- assert_se(config_parse_hwaddrs("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &s, NULL) == 0);
+ assert_se(config_parse_ether_addrs("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &s, NULL) == 0);
assert_se(set_size(s) == n);
for (size_t m = 0; m < n; m++) {
@@ -101,68 +101,68 @@ static void test_config_parse_duid_rawdata(void) {
test_config_parse_duid_rawdata_one(&BYTES_0_128[2], 0, &(DUID){0, 128, BYTES_1_128});
}
-static void test_config_parse_hwaddr(void) {
+static void test_config_parse_ether_addr(void) {
const struct ether_addr t[] = {
{ .ether_addr_octet = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff } },
{ .ether_addr_octet = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab } },
};
- test_config_parse_hwaddr_one("", 0, NULL);
- test_config_parse_hwaddr_one("no:ta:ma:ca:dd:re", 0, NULL);
- test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:fx", 0, NULL);
- test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff", 0, &t[0]);
- test_config_parse_hwaddr_one(" aa:bb:cc:dd:ee:ff", 0, &t[0]);
- test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\n", 0, NULL);
- test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\nxxx", 0, NULL);
- test_config_parse_hwaddr_one("aa:bb:cc: dd:ee:ff", 0, NULL);
- test_config_parse_hwaddr_one("aa:bb:cc:d d:ee:ff", 0, NULL);
- test_config_parse_hwaddr_one("aa:bb:cc:dd:ee", 0, NULL);
- test_config_parse_hwaddr_one("9:aa:bb:cc:dd:ee:ff", 0, NULL);
- test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff:gg", 0, NULL);
- test_config_parse_hwaddr_one("aa:Bb:CC:dd:ee:ff", 0, &t[0]);
- test_config_parse_hwaddr_one("01:23:45:67:89:aB", 0, &t[1]);
- test_config_parse_hwaddr_one("1:23:45:67:89:aB", 0, &t[1]);
- test_config_parse_hwaddr_one("aa-bb-cc-dd-ee-ff", 0, &t[0]);
- test_config_parse_hwaddr_one("AA-BB-CC-DD-EE-FF", 0, &t[0]);
- test_config_parse_hwaddr_one("01-23-45-67-89-ab", 0, &t[1]);
- test_config_parse_hwaddr_one("aabb.ccdd.eeff", 0, &t[0]);
- test_config_parse_hwaddr_one("0123.4567.89ab", 0, &t[1]);
- test_config_parse_hwaddr_one("123.4567.89ab.", 0, NULL);
- test_config_parse_hwaddr_one("aabbcc.ddeeff", 0, NULL);
- test_config_parse_hwaddr_one("aabbccddeeff", 0, NULL);
- test_config_parse_hwaddr_one("aabbccddee:ff", 0, NULL);
- test_config_parse_hwaddr_one("012345.6789ab", 0, NULL);
- test_config_parse_hwaddr_one("123.4567.89ab", 0, &t[1]);
-
- test_config_parse_hwaddrs_one("", t, 0);
- test_config_parse_hwaddrs_one("no:ta:ma:ca:dd:re", t, 0);
- test_config_parse_hwaddrs_one("aa:bb:cc:dd:ee:fx", t, 0);
- test_config_parse_hwaddrs_one("aa:bb:cc:dd:ee:ff", t, 1);
- test_config_parse_hwaddrs_one(" aa:bb:cc:dd:ee:ff", t, 1);
- test_config_parse_hwaddrs_one("aa:bb:cc:dd:ee:ff \t\n", t, 1);
- test_config_parse_hwaddrs_one("aa:bb:cc:dd:ee:ff \t\nxxx", t, 1);
- test_config_parse_hwaddrs_one("aa:bb:cc: dd:ee:ff", t, 0);
- test_config_parse_hwaddrs_one("aa:bb:cc:d d:ee:ff", t, 0);
- test_config_parse_hwaddrs_one("aa:bb:cc:dd:ee", t, 0);
- test_config_parse_hwaddrs_one("9:aa:bb:cc:dd:ee:ff", t, 0);
- test_config_parse_hwaddrs_one("aa:bb:cc:dd:ee:ff:gg", t, 0);
- test_config_parse_hwaddrs_one("aa:Bb:CC:dd:ee:ff", t, 1);
- test_config_parse_hwaddrs_one("01:23:45:67:89:aB", &t[1], 1);
- test_config_parse_hwaddrs_one("1:23:45:67:89:aB", &t[1], 1);
- test_config_parse_hwaddrs_one("aa-bb-cc-dd-ee-ff", t, 1);
- test_config_parse_hwaddrs_one("AA-BB-CC-DD-EE-FF", t, 1);
- test_config_parse_hwaddrs_one("01-23-45-67-89-ab", &t[1], 1);
- test_config_parse_hwaddrs_one("aabb.ccdd.eeff", t, 1);
- test_config_parse_hwaddrs_one("0123.4567.89ab", &t[1], 1);
- test_config_parse_hwaddrs_one("123.4567.89ab.", t, 0);
- test_config_parse_hwaddrs_one("aabbcc.ddeeff", t, 0);
- test_config_parse_hwaddrs_one("aabbccddeeff", t, 0);
- test_config_parse_hwaddrs_one("aabbccddee:ff", t, 0);
- test_config_parse_hwaddrs_one("012345.6789ab", t, 0);
- test_config_parse_hwaddrs_one("123.4567.89ab", &t[1], 1);
-
- test_config_parse_hwaddrs_one("123.4567.89ab aa:bb:cc:dd:ee:ff 01-23-45-67-89-ab aa:Bb:CC:dd:ee:ff", t, 2);
- test_config_parse_hwaddrs_one("123.4567.89ab aa:bb:cc:dd:ee:fx hogehoge 01-23-45-67-89-ab aaaa aa:Bb:CC:dd:ee:ff", t, 2);
+ test_config_parse_ether_addr_one("", 0, NULL);
+ test_config_parse_ether_addr_one("no:ta:ma:ca:dd:re", 0, NULL);
+ test_config_parse_ether_addr_one("aa:bb:cc:dd:ee:fx", 0, NULL);
+ test_config_parse_ether_addr_one("aa:bb:cc:dd:ee:ff", 0, &t[0]);
+ test_config_parse_ether_addr_one(" aa:bb:cc:dd:ee:ff", 0, NULL);
+ test_config_parse_ether_addr_one("aa:bb:cc:dd:ee:ff \t\n", 0, NULL);
+ test_config_parse_ether_addr_one("aa:bb:cc:dd:ee:ff \t\nxxx", 0, NULL);
+ test_config_parse_ether_addr_one("aa:bb:cc: dd:ee:ff", 0, NULL);
+ test_config_parse_ether_addr_one("aa:bb:cc:d d:ee:ff", 0, NULL);
+ test_config_parse_ether_addr_one("aa:bb:cc:dd:ee", 0, NULL);
+ test_config_parse_ether_addr_one("9:aa:bb:cc:dd:ee:ff", 0, NULL);
+ test_config_parse_ether_addr_one("aa:bb:cc:dd:ee:ff:gg", 0, NULL);
+ test_config_parse_ether_addr_one("aa:Bb:CC:dd:ee:ff", 0, &t[0]);
+ test_config_parse_ether_addr_one("01:23:45:67:89:aB", 0, &t[1]);
+ test_config_parse_ether_addr_one("1:23:45:67:89:aB", 0, &t[1]);
+ test_config_parse_ether_addr_one("aa-bb-cc-dd-ee-ff", 0, &t[0]);
+ test_config_parse_ether_addr_one("AA-BB-CC-DD-EE-FF", 0, &t[0]);
+ test_config_parse_ether_addr_one("01-23-45-67-89-ab", 0, &t[1]);
+ test_config_parse_ether_addr_one("aabb.ccdd.eeff", 0, &t[0]);
+ test_config_parse_ether_addr_one("0123.4567.89ab", 0, &t[1]);
+ test_config_parse_ether_addr_one("123.4567.89ab.", 0, NULL);
+ test_config_parse_ether_addr_one("aabbcc.ddeeff", 0, NULL);
+ test_config_parse_ether_addr_one("aabbccddeeff", 0, NULL);
+ test_config_parse_ether_addr_one("aabbccddee:ff", 0, NULL);
+ test_config_parse_ether_addr_one("012345.6789ab", 0, NULL);
+ test_config_parse_ether_addr_one("123.4567.89ab", 0, &t[1]);
+
+ test_config_parse_ether_addrs_one("", t, 0);
+ test_config_parse_ether_addrs_one("no:ta:ma:ca:dd:re", t, 0);
+ test_config_parse_ether_addrs_one("aa:bb:cc:dd:ee:fx", t, 0);
+ test_config_parse_ether_addrs_one("aa:bb:cc:dd:ee:ff", t, 1);
+ test_config_parse_ether_addrs_one(" aa:bb:cc:dd:ee:ff", t, 1);
+ test_config_parse_ether_addrs_one("aa:bb:cc:dd:ee:ff \t\n", t, 1);
+ test_config_parse_ether_addrs_one("aa:bb:cc:dd:ee:ff \t\nxxx", t, 1);
+ test_config_parse_ether_addrs_one("aa:bb:cc: dd:ee:ff", t, 0);
+ test_config_parse_ether_addrs_one("aa:bb:cc:d d:ee:ff", t, 0);
+ test_config_parse_ether_addrs_one("aa:bb:cc:dd:ee", t, 0);
+ test_config_parse_ether_addrs_one("9:aa:bb:cc:dd:ee:ff", t, 0);
+ test_config_parse_ether_addrs_one("aa:bb:cc:dd:ee:ff:gg", t, 0);
+ test_config_parse_ether_addrs_one("aa:Bb:CC:dd:ee:ff", t, 1);
+ test_config_parse_ether_addrs_one("01:23:45:67:89:aB", &t[1], 1);
+ test_config_parse_ether_addrs_one("1:23:45:67:89:aB", &t[1], 1);
+ test_config_parse_ether_addrs_one("aa-bb-cc-dd-ee-ff", t, 1);
+ test_config_parse_ether_addrs_one("AA-BB-CC-DD-EE-FF", t, 1);
+ test_config_parse_ether_addrs_one("01-23-45-67-89-ab", &t[1], 1);
+ test_config_parse_ether_addrs_one("aabb.ccdd.eeff", t, 1);
+ test_config_parse_ether_addrs_one("0123.4567.89ab", &t[1], 1);
+ test_config_parse_ether_addrs_one("123.4567.89ab.", t, 0);
+ test_config_parse_ether_addrs_one("aabbcc.ddeeff", t, 0);
+ test_config_parse_ether_addrs_one("aabbccddeeff", t, 0);
+ test_config_parse_ether_addrs_one("aabbccddee:ff", t, 0);
+ test_config_parse_ether_addrs_one("012345.6789ab", t, 0);
+ test_config_parse_ether_addrs_one("123.4567.89ab", &t[1], 1);
+
+ test_config_parse_ether_addrs_one("123.4567.89ab aa:bb:cc:dd:ee:ff 01-23-45-67-89-ab aa:Bb:CC:dd:ee:ff", t, 2);
+ test_config_parse_ether_addrs_one("123.4567.89ab aa:bb:cc:dd:ee:fx hogehoge 01-23-45-67-89-ab aaaa aa:Bb:CC:dd:ee:ff", t, 2);
}
static void test_config_parse_address_one(const char *rvalue, int family, unsigned n_addresses, const union in_addr_union *u, unsigned char prefixlen) {
@@ -248,7 +248,7 @@ int main(int argc, char **argv) {
test_config_parse_duid_type();
test_config_parse_duid_rawdata();
- test_config_parse_hwaddr();
+ test_config_parse_ether_addr();
test_config_parse_address();
test_config_parse_match_ifnames();
test_config_parse_match_strv();
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index 4af00c8d95..d25bef7468 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -20,7 +20,7 @@ struct ConfigPerfItem;
%includes
%%
Exec.Boot, config_parse_boot, 0, 0
-Exec.Ephemeral, config_parse_bool, 0, offsetof(Settings, ephemeral)
+Exec.Ephemeral, config_parse_tristate, 0, offsetof(Settings, ephemeral)
Exec.ProcessTwo, config_parse_pid2, 0, 0
Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters)
Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment)
@@ -34,7 +34,7 @@ Exec.MachineID, config_parse_id128, 0, of
Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory)
Exec.PivotRoot, config_parse_pivot_root, 0, 0
Exec.PrivateUsers, config_parse_private_users, 0, 0
-Exec.NotifyReady, config_parse_bool, 0, offsetof(Settings, notify_ready)
+Exec.NotifyReady, config_parse_tristate, 0, offsetof(Settings, notify_ready)
Exec.SystemCallFilter, config_parse_syscall_filter, 0, 0,
Exec.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof(Settings, rlimit)
Exec.LimitFSIZE, config_parse_rlimit, RLIMIT_FSIZE, offsetof(Settings, rlimit)
@@ -59,7 +59,7 @@ Exec.CPUAffinity, config_parse_cpu_affinity, 0, 0
Exec.ResolvConf, config_parse_resolv_conf, 0, offsetof(Settings, resolv_conf)
Exec.LinkJournal, config_parse_link_journal, 0, 0
Exec.Timezone, config_parse_timezone, 0, offsetof(Settings, timezone)
-Exec.SuppressSync, config_parse_bool, 0, offsetof(Settings, suppress_sync)
+Exec.SuppressSync, config_parse_tristate, 0, offsetof(Settings, suppress_sync)
Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only)
Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
Files.Bind, config_parse_bind, 0, 0
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index edc0f663bb..1f58bf3ed4 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -27,6 +27,7 @@ Settings *settings_new(void) {
*s = (Settings) {
.start_mode = _START_MODE_INVALID,
+ .ephemeral = -1,
.personality = PERSONALITY_INVALID,
.resolv_conf = _RESOLV_CONF_MODE_INVALID,
@@ -57,6 +58,9 @@ Settings *settings_new(void) {
.clone_ns_flags = ULONG_MAX,
.use_cgns = -1,
+
+ .notify_ready = -1,
+ .suppress_sync = -1,
};
return s;
@@ -170,6 +174,8 @@ Settings* settings_free(Settings *s) {
bool settings_private_network(Settings *s) {
assert(s);
+ /* Determines whether we shall open up our own private network */
+
return
s->private_network > 0 ||
s->network_veth > 0 ||
@@ -190,6 +196,25 @@ bool settings_network_veth(Settings *s) {
s->network_zone;
}
+bool settings_network_configured(Settings *s) {
+ assert(s);
+
+ /* Determines whether any network configuration setting was used. (i.e. in contrast to
+ * settings_private_network() above this might also indicate if private networking was explicitly
+ * turned off.) */
+
+ return
+ s->private_network >= 0 ||
+ s->network_veth >= 0 ||
+ s->network_bridge ||
+ s->network_zone ||
+ s->network_interfaces ||
+ s->network_macvlan ||
+ s->network_ipvlan ||
+ s->network_veth_extra ||
+ s->network_namespace_path;
+}
+
int settings_allocate_properties(Settings *s) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
@@ -285,9 +310,6 @@ int config_parse_capability(
}
}
- if (u == 0)
- return 0;
-
*result |= u;
return 0;
}
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index 1b3ace5f8f..59397ca54b 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -162,7 +162,7 @@ typedef struct OciHook {
typedef struct Settings {
/* [Exec] */
StartMode start_mode;
- bool ephemeral;
+ int ephemeral;
char **parameters;
char **environment;
char *user;
@@ -177,7 +177,7 @@ typedef struct Settings {
char *pivot_root_old;
UserNamespaceMode userns_mode;
uid_t uid_shift, uid_range;
- bool notify_ready;
+ int notify_ready;
char **syscall_allow_list;
char **syscall_deny_list;
struct rlimit *rlimit[_RLIMIT_MAX];
@@ -190,7 +190,7 @@ typedef struct Settings {
LinkJournal link_journal;
bool link_journal_try;
TimezoneMode timezone;
- bool suppress_sync;
+ int suppress_sync;
/* [Files] */
int read_only;
@@ -242,6 +242,8 @@ Settings* settings_free(Settings *s);
bool settings_network_veth(Settings *s);
bool settings_private_network(Settings *s);
+bool settings_network_configured(Settings *s);
+
int settings_allocate_properties(Settings *s);
DEFINE_TRIVIAL_CLEANUP_FUNC(Settings*, settings_free);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 1f327b0952..b85b8c3d43 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -2216,13 +2216,12 @@ static int copy_devnodes(const char *dest) {
"tty\0"
"net/tun\0";
- _unused_ _cleanup_umask_ mode_t u;
const char *d;
int r = 0;
assert(dest);
- u = umask(0000);
+ BLOCK_WITH_UMASK(0000);
/* Create /dev/net, so that we can create /dev/net/tun in it */
if (userns_mkdir(dest, "/dev/net", 0755, 0, 0) < 0)
@@ -2299,11 +2298,10 @@ static int copy_devnodes(const char *dest) {
}
static int make_extra_nodes(const char *dest) {
- _unused_ _cleanup_umask_ mode_t u;
size_t i;
int r;
- u = umask(0000);
+ BLOCK_WITH_UMASK(0000);
for (i = 0; i < arg_n_extra_nodes; i++) {
_cleanup_free_ char *path = NULL;
@@ -2500,12 +2498,11 @@ static int setup_kmsg(int kmsg_socket) {
_cleanup_(unlink_and_freep) char *from = NULL;
_cleanup_free_ char *fifo = NULL;
_cleanup_close_ int fd = -1;
- _unused_ _cleanup_umask_ mode_t u;
int r;
assert(kmsg_socket >= 0);
- u = umask(0000);
+ BLOCK_WITH_UMASK(0000);
/* We create the kmsg FIFO as as temporary file in /run, but immediately delete it after bind mounting it to
* /proc/kmsg. While FIFOs on the reading side behave very similar to /proc/kmsg, their writing side behaves
@@ -4284,7 +4281,8 @@ static int merge_settings(Settings *settings, const char *path) {
strv_free_and_replace(arg_parameters, settings->parameters);
}
- if ((arg_settings_mask & SETTING_EPHEMERAL) == 0)
+ if ((arg_settings_mask & SETTING_EPHEMERAL) == 0 &&
+ settings->ephemeral >= 0)
arg_ephemeral = settings->ephemeral;
if ((arg_settings_mask & SETTING_DIRECTORY) == 0 &&
@@ -4336,7 +4334,8 @@ static int merge_settings(Settings *settings, const char *path) {
plus = settings->capability;
minus = settings->drop_capability;
- if ((arg_settings_mask & SETTING_NETWORK) == 0) {
+ if ((arg_settings_mask & SETTING_NETWORK) == 0 &&
+ settings_network_configured(settings)) {
if (settings_private_network(settings))
plus |= UINT64_C(1) << CAP_NET_ADMIN;
else
@@ -4407,15 +4406,7 @@ static int merge_settings(Settings *settings, const char *path) {
}
if ((arg_settings_mask & SETTING_NETWORK) == 0 &&
- (settings->private_network >= 0 ||
- settings->network_veth >= 0 ||
- settings->network_bridge ||
- settings->network_zone ||
- settings->network_interfaces ||
- settings->network_macvlan ||
- settings->network_ipvlan ||
- settings->network_veth_extra ||
- settings->network_namespace_path)) {
+ settings_network_configured(settings)) {
if (!arg_settings_trusted)
log_warning("Ignoring network settings, file %s is not trusted.", path);
@@ -4459,27 +4450,33 @@ static int merge_settings(Settings *settings, const char *path) {
}
}
- if ((arg_settings_mask & SETTING_BIND_USER) == 0)
+ if ((arg_settings_mask & SETTING_BIND_USER) == 0 &&
+ !strv_isempty(settings->bind_user))
strv_free_and_replace(arg_bind_user, settings->bind_user);
- if ((arg_settings_mask & SETTING_NOTIFY_READY) == 0)
+ if ((arg_settings_mask & SETTING_NOTIFY_READY) == 0 &&
+ settings->notify_ready >= 0)
arg_notify_ready = settings->notify_ready;
if ((arg_settings_mask & SETTING_SYSCALL_FILTER) == 0) {
- if (!arg_settings_trusted && !strv_isempty(settings->syscall_allow_list))
- log_warning("Ignoring SystemCallFilter= settings, file %s is not trusted.", path);
- else {
- strv_free_and_replace(arg_syscall_allow_list, settings->syscall_allow_list);
- strv_free_and_replace(arg_syscall_deny_list, settings->syscall_deny_list);
+ if (!strv_isempty(settings->syscall_allow_list) || !strv_isempty(settings->syscall_deny_list)) {
+ if (!arg_settings_trusted && !strv_isempty(settings->syscall_allow_list))
+ log_warning("Ignoring SystemCallFilter= settings, file %s is not trusted.", path);
+ else {
+ strv_free_and_replace(arg_syscall_allow_list, settings->syscall_allow_list);
+ strv_free_and_replace(arg_syscall_deny_list, settings->syscall_deny_list);
+ }
}
#if HAVE_SECCOMP
- if (!arg_settings_trusted && settings->seccomp)
- log_warning("Ignoring SECCOMP filter, file %s is not trusted.", path);
- else {
- seccomp_release(arg_seccomp);
- arg_seccomp = TAKE_PTR(settings->seccomp);
+ if (settings->seccomp) {
+ if (!arg_settings_trusted)
+ log_warning("Ignoring SECCOMP filter, file %s is not trusted.", path);
+ else {
+ seccomp_release(arg_seccomp);
+ arg_seccomp = TAKE_PTR(settings->seccomp);
+ }
}
#endif
}
@@ -4585,7 +4582,8 @@ static int merge_settings(Settings *settings, const char *path) {
arg_console_mode = settings->console_mode;
}
- if ((arg_settings_mask & SETTING_SUPPRESS_SYNC) == 0)
+ if ((arg_settings_mask & SETTING_SUPPRESS_SYNC) == 0 &&
+ settings->suppress_sync >= 0)
arg_suppress_sync = settings->suppress_sync;
/* The following properties can only be set through the OCI settings logic, not from the command line, hence we
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 62cc8ff7ae..5e6e88df94 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -505,18 +505,21 @@ static uint64_t free_area_available_for_new_partitions(const FreeArea *a) {
avail = free_area_available(a);
if (a->after) {
- uint64_t need, space;
+ uint64_t need, space_end, new_end;
need = partition_min_size_with_padding(a->after);
assert(a->after->offset != UINT64_MAX);
assert(a->after->current_size != UINT64_MAX);
- space = round_up_size(a->after->offset + a->after->current_size, 4096) - a->after->offset + avail;
- if (need >= space)
- return 0;
+ /* Calculate where the free area ends, based on the offset of the partition preceding it */
+ space_end = round_up_size(a->after->offset + a->after->current_size, 4096) + avail;
+
+ /* Calculate where the partition would end when we give it as much as it needs */
+ new_end = round_up_size(a->after->offset + need, 4096);
- return space - need;
+ /* Calculate saturated difference of the two: that's how much we have free for other partitions */
+ return LESS_BY(space_end, new_end);
}
return avail;
@@ -528,16 +531,9 @@ static int free_area_compare(FreeArea *const *a, FreeArea *const*b) {
}
static uint64_t charge_size(uint64_t total, uint64_t amount) {
- uint64_t rounded;
-
- assert(amount <= total);
-
/* Subtract the specified amount from total, rounding up to multiple of 4K if there's room */
- rounded = round_up_size(amount, 4096);
- if (rounded >= total)
- return 0;
-
- return total - rounded;
+ assert(amount <= total);
+ return LESS_BY(total, round_up_size(amount, 4096));
}
static uint64_t charge_weight(uint64_t total, uint64_t amount) {
@@ -651,6 +647,8 @@ typedef enum GrowPartitionPhase {
/* The third phase: we distribute what remains among the remaining partitions, according to the weights */
PHASE_DISTRIBUTE,
+
+ _GROW_PARTITION_PHASE_MAX,
} GrowPartitionPhase;
static int context_grow_partitions_phase(
@@ -786,20 +784,14 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
span += round_up_size(a->after->offset + a->after->current_size, 4096) - a->after->offset;
}
- GrowPartitionPhase phase = PHASE_OVERCHARGE;
- for (;;) {
+ for (GrowPartitionPhase phase = 0; phase < _GROW_PARTITION_PHASE_MAX;) {
r = context_grow_partitions_phase(context, a, phase, &span, &weight_sum);
if (r < 0)
return r;
if (r == 0) /* not done yet, re-run this phase */
continue;
- if (phase == PHASE_OVERCHARGE)
- phase = PHASE_UNDERCHARGE;
- else if (phase == PHASE_UNDERCHARGE)
- phase = PHASE_DISTRIBUTE;
- else if (phase == PHASE_DISTRIBUTE)
- break;
+ phase++; /* got to next phase */
}
/* We still have space left over? Donate to preceding partition if we have one */
@@ -807,7 +799,9 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
uint64_t m, xsz;
assert(a->after->new_size != UINT64_MAX);
- m = a->after->new_size + span;
+
+ /* Calculate new size and align (but ensure this doesn't shrink the size) */
+ m = MAX(a->after->new_size, round_down_size(a->after->new_size + span, 4096));
xsz = partition_max_size(a->after);
if (xsz != UINT64_MAX && m > xsz)
@@ -832,7 +826,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
continue;
assert(p->new_size != UINT64_MAX);
- m = p->new_size + span;
+ m = MAX(p->new_size, round_down_size(p->new_size + span, 4096));
xsz = partition_max_size(p);
if (xsz != UINT64_MAX && m > xsz)
@@ -1491,11 +1485,7 @@ static int determine_current_padding(
offset = round_up_size(offset, 4096);
next = round_down_size(next, 4096);
- if (next >= offset) /* Check again, rounding might have fucked things up */
- *ret = next - offset;
- else
- *ret = 0;
-
+ *ret = LESS_BY(next, offset); /* Saturated substraction, rounding might have fucked things up */
return 0;
}
@@ -4988,7 +4978,7 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
- /* Now calculate where each partition gets placed */
+ /* Now calculate where each new partition gets placed */
context_place_partitions(context);
/* Make sure each partition has a unique UUID and unique label */
diff --git a/src/resolve/meson.build b/src/resolve/meson.build
index 1f9b8af4f3..c7cb88ac04 100644
--- a/src/resolve/meson.build
+++ b/src/resolve/meson.build
@@ -166,8 +166,7 @@ custom_target(
'resolved.conf',
input : 'resolved.conf.in',
output : 'resolved.conf',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : conf.get('ENABLE_RESOLVE') == 1 and install_sysconfdir_samples,
install_dir : pkgsysconfdir)
diff --git a/src/rpm/meson.build b/src/rpm/meson.build
index 2ad3308cc1..8decb11f3b 100644
--- a/src/rpm/meson.build
+++ b/src/rpm/meson.build
@@ -18,8 +18,7 @@ foreach tuple : in_files
file,
input : file + '.in',
output : file,
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : tuple[1],
install_dir : tuple.length() > 2 ? tuple[2] : '',
install_mode : tuple.length() > 3 ? tuple[3] : false,
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index 016eb7b82a..47c1ea2802 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -20,10 +20,10 @@
#include "user-util.h"
typedef struct BaseFilesystem {
- const char *dir;
+ const char *dir; /* directory or symlink to create */
mode_t mode;
- const char *target;
- const char *exists;
+ const char *target; /* if non-NULL create as symlink to this target */
+ const char *exists; /* conditionalize this entry on existance of this file */
bool ignore_failure;
} BaseFilesystem;
@@ -39,11 +39,26 @@ static const BaseFilesystem table[] = {
{ "sys", 0755, NULL, NULL, true },
{ "dev", 0755, NULL, NULL, true },
#if defined(__i386__) || defined(__x86_64__)
+ /* Various architecture ABIs define the path to the dynamic loader via the /lib64/ subdirectory of
+ * the root directory. When booting from an otherwise empty root file system (where only /usr/ has
+ * been mounted into) it is thus necessary to create a symlink pointing to the right subdirectory of
+ * /usr/ first — otherwise we couldn't invoke any dynamic binary. Let's detect this case here, and
+ * create the symlink as needed should it be missing. We prefer doing this consistently with Debian's
+ * multiarch logic, but support Fedora-style multilib too.*/
{ "lib64", 0, "usr/lib/x86_64-linux-gnu\0"
"usr/lib64\0", "ld-linux-x86-64.so.2" },
+#else
+ /* gcc doesn't allow pragma to be used within constructs, hence log about this separately below */
+# define WARN_LIB64 1
#endif
};
+#ifdef WARN_LIB64
+#pragma message "If your architecture knows a /lib64/ or /lib32/ directory, please add an entry creating it here."
+ /* And if your architecture doesn't know these directories, make sure to add ifdeffery here to
+ * suppress this pragma message. */
+#endif
+
int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
int r;
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index e538e2bd87..8fdb5272e9 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -9,6 +9,7 @@
#include "alloc-util.h"
#include "blkid-util.h"
+#include "bootspec-fundamental.h"
#include "bootspec.h"
#include "conf-files.h"
#include "def.h"
@@ -287,13 +288,13 @@ static int boot_entry_load_unified(
const char *cmdline,
BootEntry *ret) {
- _cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL;
+ _cleanup_free_ char *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
+ *os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
_cleanup_(boot_entry_free) BootEntry tmp = {
.type = BOOT_ENTRY_UNIFIED,
};
+ const char *k, *good_name, *good_version;
_cleanup_fclose_ FILE *f = NULL;
- const char *k;
- char *b;
int r;
assert(root);
@@ -310,24 +311,42 @@ static int boot_entry_load_unified(
r = parse_env_file(f, "os-release",
"PRETTY_NAME", &os_pretty_name,
+ "IMAGE_ID", &os_image_id,
+ "NAME", &os_name,
"ID", &os_id,
- "VERSION_ID", &version_id,
- "BUILD_ID", &build_id);
+ "IMAGE_VERSION", &os_image_version,
+ "VERSION", &os_version,
+ "VERSION_ID", &os_version_id,
+ "BUILD_ID", &os_build_id);
if (r < 0)
return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path);
- if (!os_pretty_name || !os_id || !(version_id || build_id))
+ if (!bootspec_pick_name_version(
+ os_pretty_name,
+ os_image_id,
+ os_name,
+ os_id,
+ os_image_version,
+ os_version,
+ os_version_id,
+ os_build_id,
+ &good_name,
+ &good_version))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
- b = basename(path);
- tmp.id = strdup(b);
- tmp.id_old = strjoin(os_id, "-", version_id ?: build_id);
- if (!tmp.id || !tmp.id_old)
- return log_oom();
+ r = path_extract_filename(path, &tmp.id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract file name from '%s': %m", path);
if (!efi_loader_entry_name_valid(tmp.id))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", tmp.id);
+ if (os_id && os_version_id) {
+ tmp.id_old = strjoin(os_id, "-", os_version_id);
+ if (!tmp.id_old)
+ return log_oom();
+ }
+
tmp.path = strdup(path);
if (!tmp.path)
return log_oom();
@@ -346,7 +365,13 @@ static int boot_entry_load_unified(
delete_trailing_chars(tmp.options[0], WHITESPACE);
- tmp.title = TAKE_PTR(os_pretty_name);
+ tmp.title = strdup(good_name);
+ if (!tmp.title)
+ return log_oom();
+
+ tmp.version = strdup(good_version);
+ if (!tmp.version)
+ return log_oom();
*ret = tmp;
tmp = (BootEntry) {};
diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h
index c7b4a2ee02..81845f47e3 100644
--- a/src/shared/bootspec.h
+++ b/src/shared/bootspec.h
@@ -20,7 +20,7 @@ typedef enum BootEntryType {
typedef struct BootEntry {
BootEntryType type;
- char *id; /* This is the file basename without extension */
+ char *id; /* This is the file basename (including extension!) */
char *id_old; /* Old-style ID, for deduplication purposes. */
char *path; /* This is the full path to the drop-in file */
char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index d1068440f7..bbf7499e57 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -2132,6 +2132,7 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
if (STR_IN_SET(field, "PIDFile",
"Type",
+ "ExitType",
"Restart",
"BusName",
"NotifyAccess",
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 1d28f7ba57..3e6ae79553 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -509,7 +509,7 @@ static int condition_test_architecture(Condition *c, char **env) {
return a == b;
}
-#define DTCOMPAT_FILE "/sys/firmware/devicetree/base/compatible"
+#define DTCOMPAT_FILE "/proc/device-tree/compatible"
static int condition_test_firmware_devicetree_compatible(const char *dtcarg) {
int r;
_cleanup_free_ char *dtcompat = NULL;
@@ -530,11 +530,8 @@ static int condition_test_firmware_devicetree_compatible(const char *dtcarg) {
return false;
}
- /*
- * /sys/firmware/devicetree/base/compatible consists of one or more
- * strings, each ending in '\0'. So the last character in dtcompat must
- * be a '\0'.
- */
+ /* /proc/device-tree/compatible consists of one or more strings, each ending in '\0'.
+ * So the last character in dtcompat must be a '\0'. */
if (dtcompat[size - 1] != '\0') {
log_debug("%s is in an unknown format, assuming machine is incompatible", DTCOMPAT_FILE);
return false;
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 9a367d757f..7eda2da1c0 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -1320,7 +1320,101 @@ int config_parse_vlanprotocol(
return 0;
}
-int config_parse_hwaddr(
+int config_parse_hw_addr(
+ 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) {
+
+ struct hw_addr_data a, *hwaddr = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ *hwaddr = HW_ADDR_NULL;
+ return 0;
+ }
+
+ r = parse_hw_addr_full(rvalue, ltype, &a);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Not a valid hardware address, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ *hwaddr = a;
+ return 0;
+}
+
+int config_parse_hw_addrs(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Set **hwaddrs = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ *hwaddrs = set_free(*hwaddrs);
+ return 0;
+ }
+
+ for (const char *p = rvalue;;) {
+ _cleanup_free_ char *word = NULL;
+ _cleanup_free_ struct hw_addr_data *n = NULL;
+
+ r = extract_first_word(&p, &word, NULL, 0);
+ if (r == 0)
+ return 0;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ n = new(struct hw_addr_data, 1);
+ if (!n)
+ return log_oom();
+
+ r = parse_hw_addr_full(word, ltype, n);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Not a valid hardware address, ignoring: %s", word);
+ continue;
+ }
+
+ r = set_ensure_consume(hwaddrs, &hw_addr_hash_ops_free, TAKE_PTR(n));
+ if (r < 0)
+ return log_oom();
+ }
+}
+
+int config_parse_ether_addr(
const char *unit,
const char *filename,
unsigned line,
@@ -1350,7 +1444,7 @@ int config_parse_hwaddr(
if (!n)
return log_oom();
- r = ether_addr_from_string(rvalue, n);
+ r = parse_ether_addr(rvalue, n);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Not a valid MAC address, ignoring assignment: %s", rvalue);
@@ -1362,7 +1456,7 @@ int config_parse_hwaddr(
return 0;
}
-int config_parse_hwaddrs(
+int config_parse_ether_addrs(
const char *unit,
const char *filename,
unsigned line,
@@ -1384,7 +1478,7 @@ int config_parse_hwaddrs(
if (isempty(rvalue)) {
/* Empty assignment resets the list */
- *hwaddrs = set_free_free(*hwaddrs);
+ *hwaddrs = set_free(*hwaddrs);
return 0;
}
@@ -1407,18 +1501,16 @@ int config_parse_hwaddrs(
if (!n)
return log_oom();
- r = ether_addr_from_string(word, n);
+ r = parse_ether_addr(word, n);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Not a valid MAC address, ignoring: %s", word);
continue;
}
- r = set_ensure_put(hwaddrs, &ether_addr_hash_ops, n);
+ r = set_ensure_consume(hwaddrs, &ether_addr_hash_ops_free, TAKE_PTR(n));
if (r < 0)
return log_oom();
- if (r > 0)
- TAKE_PTR(n); /* avoid cleanup */
}
}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index f893a53aa0..63b749d42a 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -149,8 +149,10 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
-CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
-CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs);
+CONFIG_PARSER_PROTOTYPE(config_parse_hw_addr);
+CONFIG_PARSER_PROTOTYPE(config_parse_hw_addrs);
+CONFIG_PARSER_PROTOTYPE(config_parse_ether_addr);
+CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs);
CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null);
CONFIG_PARSER_PROTOTYPE(config_parse_percent);
CONFIG_PARSER_PROTOTYPE(config_parse_permyriad);
diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c
index c3e717ae11..0390abbfdc 100644
--- a/src/shared/dev-setup.c
+++ b/src/shared/dev-setup.c
@@ -81,13 +81,12 @@ int make_inaccessible_nodes(
{ "inaccessible/blk", S_IFBLK | 0000 },
};
- _unused_ _cleanup_umask_ mode_t u;
int r;
if (!parent_dir)
parent_dir = "/run/systemd";
- u = umask(0000);
+ BLOCK_WITH_UMASK(0000);
/* Set up inaccessible (and empty) file nodes of all types. This are used to as mount sources for over-mounting
* ("masking") file nodes that shall become inaccessible and empty for specific containers or services. We try
diff --git a/src/shared/gpt.c b/src/shared/gpt.c
index a2e39dc1a1..0459ce5bf1 100644
--- a/src/shared/gpt.c
+++ b/src/shared/gpt.c
@@ -4,6 +4,16 @@
#include "string-util.h"
#include "utf8.h"
+/* Gently push people towards defining GPT type UUIDs for all architectures we know */
+#if !defined(GPT_ROOT_NATIVE) || \
+ !defined(GPT_ROOT_NATIVE_VERITY) || \
+ !defined(GPT_ROOT_NATIVE_VERITY_SIG) || \
+ !defined(GPT_USR_NATIVE) || \
+ !defined(GPT_USR_NATIVE_VERITY) || \
+ !defined(GPT_USR_NATIVE_VERITY_SIG)
+#pragma message "Please define GPT partition types for your architecture."
+#endif
+
const GptPartitionType gpt_partition_type_table[] = {
{ GPT_ROOT_X86, "root-x86" },
{ GPT_ROOT_X86_VERITY, "root-x86-verity" },
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 6e09ef8479..a23d43ce09 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -218,6 +218,8 @@ shared_sources = files('''
net-condition.h
netif-naming-scheme.c
netif-naming-scheme.h
+ netif-util.c
+ netif-util.h
nscd-flush.h
nsflags.c
nsflags.h
diff --git a/src/shared/net-condition.c b/src/shared/net-condition.c
index 1da60810ee..ac0a364f06 100644
--- a/src/shared/net-condition.c
+++ b/src/shared/net-condition.c
@@ -6,6 +6,7 @@
#include "env-util.h"
#include "log.h"
#include "net-condition.h"
+#include "netif-util.h"
#include "network-util.h"
#include "socket-util.h"
#include "string-table.h"
@@ -16,8 +17,8 @@ void net_match_clear(NetMatch *match) {
if (!match)
return;
- match->mac = set_free_free(match->mac);
- match->permanent_mac = set_free_free(match->permanent_mac);
+ match->mac = set_free(match->mac);
+ match->permanent_mac = set_free(match->permanent_mac);
match->path = strv_free(match->path);
match->driver = strv_free(match->driver);
match->iftype = strv_free(match->iftype);
@@ -25,7 +26,7 @@ void net_match_clear(NetMatch *match) {
match->property = strv_free(match->property);
match->wlan_iftype = strv_free(match->wlan_iftype);
match->ssid = strv_free(match->ssid);
- match->bssid = set_free_free(match->bssid);
+ match->bssid = set_free(match->bssid);
}
bool net_match_is_empty(const NetMatch *match) {
@@ -137,7 +138,7 @@ int net_match_config(
assert(match);
- r = link_get_type_string(device, iftype, &iftype_str);
+ r = net_get_type_string(device, iftype, &iftype_str);
if (r == -ENOMEM)
return r;
diff --git a/src/shared/netif-util.c b/src/shared/netif-util.c
new file mode 100644
index 0000000000..85b6ad45c8
--- /dev/null
+++ b/src/shared/netif-util.c
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "arphrd-util.h"
+#include "device-util.h"
+#include "netif-util.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+#include "strv.h"
+
+int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) {
+ const char *t;
+ char *p;
+
+ if (device &&
+ sd_device_get_devtype(device, &t) >= 0 &&
+ !isempty(t)) {
+ p = strdup(t);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = p;
+ return 0;
+ }
+
+ t = arphrd_to_name(iftype);
+ if (!t)
+ return -ENOENT;
+
+ p = strdup(t);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = ascii_strlower(p);
+ return 0;
+}
+
+const char *net_get_persistent_name(sd_device *device) {
+ const char *name, *field;
+
+ assert(device);
+
+ /* fetch some persistent data unique (on this machine) to this device */
+ FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC")
+ if (sd_device_get_property_value(device, field, &name) >= 0)
+ return name;
+
+ return NULL;
+}
+
+/* Used when generating hardware address by udev, and IPv4LL seed by networkd. */
+#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
+
+int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret) {
+ const char *name;
+
+ assert(device);
+ assert(ret);
+
+ /* net_get_persistent_name() will return one of the device names based on stable information about
+ * the device. If this is not available, we fall back to using the actual device name. */
+ name = net_get_persistent_name(device);
+ if (!name && use_sysname)
+ (void) sd_device_get_sysname(device, &name);
+ if (!name)
+ return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
+ "No stable identifying information found");
+
+ log_device_debug(device, "Using \"%s\" as stable identifying information", name);
+
+ return net_get_unique_predictable_data_from_name(name, &HASH_KEY, ret);
+}
+
+int net_get_unique_predictable_data_from_name(
+ const char *name,
+ const sd_id128_t *key,
+ uint64_t *ret) {
+
+ size_t l, sz;
+ uint8_t *v;
+ int r;
+
+ assert(name);
+ assert(key);
+ assert(ret);
+
+ l = strlen(name);
+ sz = sizeof(sd_id128_t) + l;
+ v = newa(uint8_t, sz);
+
+ /* Fetch some persistent data unique to this machine */
+ r = sd_id128_get_machine((sd_id128_t*) v);
+ if (r < 0)
+ return r;
+
+ memcpy(v + sizeof(sd_id128_t), name, l);
+
+ /* Let's hash the machine ID plus the device name. We use
+ * a fixed, but originally randomly created hash key here. */
+ *ret = htole64(siphash24(v, sz, key->bytes));
+ return 0;
+}
diff --git a/src/shared/netif-util.h b/src/shared/netif-util.h
new file mode 100644
index 0000000000..f239f042b5
--- /dev/null
+++ b/src/shared/netif-util.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "sd-device.h"
+#include "sd-id128.h"
+
+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);
+int net_get_unique_predictable_data_from_name(const char *name, const sd_id128_t *key, uint64_t *ret);
diff --git a/src/shared/varlink.c b/src/shared/varlink.c
index b31ca108fe..e0038dfd28 100644
--- a/src/shared/varlink.c
+++ b/src/shared/varlink.c
@@ -2365,14 +2365,8 @@ int varlink_server_detach_event(VarlinkServer *s) {
assert_return(s, -EINVAL);
- LIST_FOREACH(sockets, ss, s->sockets) {
-
- if (!ss->event_source)
- continue;
-
- (void) sd_event_source_set_enabled(ss->event_source, SD_EVENT_OFF);
- ss->event_source = sd_event_source_unref(ss->event_source);
- }
+ LIST_FOREACH(sockets, ss, s->sockets)
+ ss->event_source = sd_event_source_disable_unref(ss->event_source);
sd_event_unref(s->event);
return 0;
diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h
index 2ae2a0da48..63984eef15 100644
--- a/src/systemd/sd-event.h
+++ b/src/systemd/sd-event.h
@@ -93,6 +93,7 @@ int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_sign
int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata);
int sd_event_add_child_pidfd(sd_event *e, sd_event_source **s, int pidfd, int options, sd_event_child_handler_t callback, void *userdata);
int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
+int sd_event_add_inotify_fd(sd_event *e, sd_event_source **s, int fd, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
@@ -165,6 +166,7 @@ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b);
int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst);
int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst);
int sd_event_source_is_ratelimited(sd_event_source *s);
+int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback);
/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);
diff --git a/src/test/meson.build b/src/test/meson.build
index b22413029d..84811c038f 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -570,7 +570,7 @@ tests += [
[['src/test/test-af-list.c',
generated_gperf_headers]],
- [['src/test/test-arphrd-list.c',
+ [['src/test/test-arphrd-util.c',
generated_gperf_headers]],
[['src/test/test-ip-protocol-list.c',
diff --git a/src/test/test-arphrd-list.c b/src/test/test-arphrd-util.c
index 9e21f32189..f12388dcce 100644
--- a/src/test/test-arphrd-list.c
+++ b/src/test/test-arphrd-util.c
@@ -2,11 +2,10 @@
#include <linux/if_arp.h>
+#include "arphrd-util.h"
#include "string-util.h"
#include "tests.h"
-#include "arphrd-list.h"
-
int main(int argc, const char *argv[]) {
test_setup_logging(LOG_INFO);
diff --git a/src/test/test-engine.c b/src/test/test-engine.c
index 880af36fb5..673c665612 100644
--- a/src/test/test-engine.c
+++ b/src/test/test-engine.c
@@ -8,6 +8,7 @@
#include "manager-dump.h"
#include "rm-rf.h"
#include "service.h"
+#include "slice.h"
#include "special.h"
#include "strv.h"
#include "tests.h"
@@ -75,7 +76,8 @@ int main(int argc, char *argv[]) {
_cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
_cleanup_(manager_freep) Manager *m = NULL;
Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL,
- *h = NULL, *i = NULL, *a_conj = NULL, *unit_with_multiple_dashes = NULL, *stub = NULL;
+ *h = NULL, *i = NULL, *a_conj = NULL, *unit_with_multiple_dashes = NULL, *stub = NULL,
+ *tomato = NULL, *sauce = NULL, *fruit = NULL, *zupa = NULL;
Job *j;
int r;
@@ -260,5 +262,32 @@ int main(int argc, char *argv[]) {
verify_dependency_atoms();
+ /* Test adding multiple Slice= dependencies; only the last should remain */
+ assert_se(unit_new_for_name(m, sizeof(Service), "tomato.service", &tomato) >= 0);
+ assert_se(unit_new_for_name(m, sizeof(Slice), "sauce.slice", &sauce) >= 0);
+ assert_se(unit_new_for_name(m, sizeof(Slice), "fruit.slice", &fruit) >= 0);
+ assert_se(unit_new_for_name(m, sizeof(Slice), "zupa.slice", &zupa) >= 0);
+
+ unit_set_slice(tomato, sauce);
+ unit_set_slice(tomato, fruit);
+ unit_set_slice(tomato, zupa);
+
+ assert_se(UNIT_GET_SLICE(tomato) == zupa);
+ assert_se(!unit_has_dependency(tomato, UNIT_ATOM_IN_SLICE, sauce));
+ assert_se(!unit_has_dependency(tomato, UNIT_ATOM_IN_SLICE, fruit));
+ assert_se(unit_has_dependency(tomato, UNIT_ATOM_IN_SLICE, zupa));
+
+ assert_se(!unit_has_dependency(tomato, UNIT_ATOM_REFERENCES, sauce));
+ assert_se(!unit_has_dependency(tomato, UNIT_ATOM_REFERENCES, fruit));
+ assert_se(unit_has_dependency(tomato, UNIT_ATOM_REFERENCES, zupa));
+
+ assert_se(!unit_has_dependency(sauce, UNIT_ATOM_SLICE_OF, tomato));
+ assert_se(!unit_has_dependency(fruit, UNIT_ATOM_SLICE_OF, tomato));
+ assert_se(unit_has_dependency(zupa, UNIT_ATOM_SLICE_OF, tomato));
+
+ assert_se(!unit_has_dependency(sauce, UNIT_ATOM_REFERENCED_BY, tomato));
+ assert_se(!unit_has_dependency(fruit, UNIT_ATOM_REFERENCED_BY, tomato));
+ assert_se(unit_has_dependency(zupa, UNIT_ATOM_REFERENCED_BY, tomato));
+
return 0;
}
diff --git a/src/test/test-escape.c b/src/test/test-escape.c
index 3fd318653c..75aa86bf69 100644
--- a/src/test/test-escape.c
+++ b/src/test/test-escape.c
@@ -206,7 +206,7 @@ static void test_shell_maybe_quote(void) {
static void test_quote_command_line_one(char **argv, const char *expected) {
_cleanup_free_ char *s;
- assert_se(s = quote_command_line(argv));
+ assert_se(s = quote_command_line(argv, SHELL_ESCAPE_EMPTY));
log_info("%s", s);
assert_se(streq(s, expected));
}
diff --git a/src/test/test-ether-addr-util.c b/src/test/test-ether-addr-util.c
index 894215844a..e708f019d7 100644
--- a/src/test/test-ether-addr-util.c
+++ b/src/test/test-ether-addr-util.c
@@ -1,8 +1,39 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "ether-addr-util.h"
+#include "string-util.h"
#include "tests.h"
+static void test_ether_addr_helpers(void) {
+ struct ether_addr a;
+
+ log_info("/* %s */", __func__);
+
+ a = ETHER_ADDR_NULL;
+ assert_se(ether_addr_is_null(&a));
+ assert_se(!ether_addr_is_broadcast(&a));
+ assert_se(!ether_addr_is_multicast(&a));
+ assert_se(ether_addr_is_unicast(&a));
+ assert_se(!ether_addr_is_local(&a));
+ assert_se(ether_addr_is_global(&a));
+
+ memset(a.ether_addr_octet, 0xff, sizeof(a));
+ assert_se(!ether_addr_is_null(&a));
+ assert_se(ether_addr_is_broadcast(&a));
+ assert_se(ether_addr_is_multicast(&a));
+ assert_se(!ether_addr_is_unicast(&a));
+ assert_se(ether_addr_is_local(&a));
+ assert_se(!ether_addr_is_global(&a));
+
+ a = (struct ether_addr) { { 0x01, 0x23, 0x34, 0x56, 0x78, 0x9a } };
+ assert_se(!ether_addr_is_null(&a));
+ assert_se(!ether_addr_is_broadcast(&a));
+ assert_se(ether_addr_is_multicast(&a));
+ assert_se(!ether_addr_is_unicast(&a));
+ assert_se(!ether_addr_is_local(&a));
+ assert_se(ether_addr_is_global(&a));
+}
+
#define INFINIBAD_ADDR_1 ((const struct hw_addr_data){ .length = 20, .infiniband = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20} })
static void test_HW_ADDR_TO_STRING(void) {
@@ -34,9 +65,111 @@ static void test_HW_ADDR_TO_STRING(void) {
log_info("joint: %s, %s", s, p);
}
+static void test_parse_hw_addr_full_one(const char *in, size_t expected_len, const char *expected) {
+ struct hw_addr_data h;
+ int r;
+
+ r = parse_hw_addr_full(in, expected_len, &h);
+ log_debug_errno(r, "parse_hw_addr(\"%s\", len=%zu) → \"%s\" (expected: \"%s\") : %d/%m",
+ in, expected_len, r >= 0 ? HW_ADDR_TO_STR(&h) : "n/a", strna(expected), r);
+ assert_se((r >= 0) == !!expected);
+ if (r >= 0) {
+ if (!IN_SET(expected_len, 0, SIZE_MAX))
+ assert_se(h.length == expected_len);
+ assert_se(streq(HW_ADDR_TO_STR(&h), expected));
+ }
+}
+
+static void test_parse_hw_addr(void) {
+ log_info("/* %s */", __func__);
+
+ /* IPv4 */
+ test_parse_hw_addr_full_one("10.0.0.1", 0, "0a:00:00:01");
+ test_parse_hw_addr_full_one("10.0.0.1", 4, "0a:00:00:01");
+ test_parse_hw_addr_full_one("192.168.0.1", 0, "c0:a8:00:01");
+ test_parse_hw_addr_full_one("192.168.0.1", 4, "c0:a8:00:01");
+ /* IPv6 */
+ test_parse_hw_addr_full_one("::", 0, "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00");
+ test_parse_hw_addr_full_one("::", 16, "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00");
+ test_parse_hw_addr_full_one("::1", 0, "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01");
+ test_parse_hw_addr_full_one("::1", 16, "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01");
+ test_parse_hw_addr_full_one("1234::", 0, "12:34:00:00:00:00:00:00:00:00:00:00:00:00:00:00");
+ test_parse_hw_addr_full_one("1234::", 16, "12:34:00:00:00:00:00:00:00:00:00:00:00:00:00:00");
+ test_parse_hw_addr_full_one("12:34::56", 0, "00:12:00:34:00:00:00:00:00:00:00:00:00:00:00:56");
+ test_parse_hw_addr_full_one("12:34::56", 16, "00:12:00:34:00:00:00:00:00:00:00:00:00:00:00:56");
+ test_parse_hw_addr_full_one("12aa:34::56", 0, "12:aa:00:34:00:00:00:00:00:00:00:00:00:00:00:56");
+ test_parse_hw_addr_full_one("12aa:34::56", 16, "12:aa:00:34:00:00:00:00:00:00:00:00:00:00:00:56");
+ test_parse_hw_addr_full_one("1234:5678:90ab:cdef:1234:5678:90ab:cdef", 0, "12:34:56:78:90:ab:cd:ef:12:34:56:78:90:ab:cd:ef");
+ test_parse_hw_addr_full_one("1234:5678:90ab:cdef:1234:5678:90ab:cdef", 16, "12:34:56:78:90:ab:cd:ef:12:34:56:78:90:ab:cd:ef");
+ /* Dot */
+ test_parse_hw_addr_full_one("12.34", 0, "00:12:00:34");
+ test_parse_hw_addr_full_one("12.34", 4, "00:12:00:34");
+ test_parse_hw_addr_full_one("12.34", SIZE_MAX, "00:12:00:34");
+ test_parse_hw_addr_full_one("12.34.56", 0, "00:12:00:34:00:56");
+ test_parse_hw_addr_full_one("12.34.56", 6, "00:12:00:34:00:56");
+ test_parse_hw_addr_full_one("12.34.56", SIZE_MAX, "00:12:00:34:00:56");
+ test_parse_hw_addr_full_one("12.34.56.78", 0, "0c:22:38:4e"); /* IPv4 address */
+ test_parse_hw_addr_full_one("12.34.56.78", 4, "0c:22:38:4e"); /* IPv4 address */
+ test_parse_hw_addr_full_one("12.34.56.78", 8, "00:12:00:34:00:56:00:78");
+ test_parse_hw_addr_full_one("12.34.56.78", SIZE_MAX, "00:12:00:34:00:56:00:78");
+ test_parse_hw_addr_full_one("12.34.56.78.90", 0, NULL);
+ test_parse_hw_addr_full_one("12.34.56.78.90", 10, "00:12:00:34:00:56:00:78:00:90");
+ test_parse_hw_addr_full_one("12.34.56.78.90", SIZE_MAX, "00:12:00:34:00:56:00:78:00:90");
+ test_parse_hw_addr_full_one("aabb.ccdd", 0, "aa:bb:cc:dd");
+ test_parse_hw_addr_full_one("aabb.ccdd", 4, "aa:bb:cc:dd");
+ test_parse_hw_addr_full_one("aabb.ccdd", SIZE_MAX, "aa:bb:cc:dd");
+ test_parse_hw_addr_full_one("aabb.ccdd.eeff", 0, "aa:bb:cc:dd:ee:ff");
+ test_parse_hw_addr_full_one("aabb.ccdd.eeff", 6, "aa:bb:cc:dd:ee:ff");
+ test_parse_hw_addr_full_one("aabb.ccdd.eeff", SIZE_MAX, "aa:bb:cc:dd:ee:ff");
+ /* Colon */
+ test_parse_hw_addr_full_one("12:34", 0, NULL);
+ test_parse_hw_addr_full_one("12:34", 2, "12:34");
+ test_parse_hw_addr_full_one("12:34", SIZE_MAX, "12:34");
+ test_parse_hw_addr_full_one("12:34:56:78:90:ab", 0, "12:34:56:78:90:ab");
+ test_parse_hw_addr_full_one("12:34:56:78:90:ab", 6, "12:34:56:78:90:ab");
+ test_parse_hw_addr_full_one("12:34:56:78:90:ab", SIZE_MAX, "12:34:56:78:90:ab");
+ test_parse_hw_addr_full_one("12:34:56:78:90:ab:cd:ef", 0, "00:12:00:34:00:56:00:78:00:90:00:ab:00:cd:00:ef"); /* IPv6 */
+ test_parse_hw_addr_full_one("12:34:56:78:90:ab:cd:ef", 8, "12:34:56:78:90:ab:cd:ef");
+ test_parse_hw_addr_full_one("12:34:56:78:90:ab:cd:ef", 16, "00:12:00:34:00:56:00:78:00:90:00:ab:00:cd:00:ef"); /* IPv6 */
+ test_parse_hw_addr_full_one("12:34:56:78:90:ab:cd:ef", SIZE_MAX, "12:34:56:78:90:ab:cd:ef");
+ test_parse_hw_addr_full_one("12:34:56:78:90:AB:CD:EF", 0, "00:12:00:34:00:56:00:78:00:90:00:ab:00:cd:00:ef"); /* IPv6 */
+ test_parse_hw_addr_full_one("12:34:56:78:90:AB:CD:EF", 8, "12:34:56:78:90:ab:cd:ef");
+ test_parse_hw_addr_full_one("12:34:56:78:90:AB:CD:EF", 16, "00:12:00:34:00:56:00:78:00:90:00:ab:00:cd:00:ef"); /* IPv6 */
+ test_parse_hw_addr_full_one("12:34:56:78:90:AB:CD:EF", SIZE_MAX, "12:34:56:78:90:ab:cd:ef");
+ /* Hyphen */
+ test_parse_hw_addr_full_one("12-34", 0, NULL);
+ test_parse_hw_addr_full_one("12-34", 2, "12:34");
+ test_parse_hw_addr_full_one("12-34", SIZE_MAX, "12:34");
+ test_parse_hw_addr_full_one("12-34-56-78-90-ab-cd-ef", 0, NULL);
+ test_parse_hw_addr_full_one("12-34-56-78-90-ab-cd-ef", 8, "12:34:56:78:90:ab:cd:ef");
+ test_parse_hw_addr_full_one("12-34-56-78-90-ab-cd-ef", SIZE_MAX, "12:34:56:78:90:ab:cd:ef");
+ test_parse_hw_addr_full_one("12-34-56-78-90-AB-CD-EF", 0, NULL);
+ test_parse_hw_addr_full_one("12-34-56-78-90-AB-CD-EF", 8, "12:34:56:78:90:ab:cd:ef");
+ test_parse_hw_addr_full_one("12-34-56-78-90-AB-CD-EF", SIZE_MAX, "12:34:56:78:90:ab:cd:ef");
+
+ /* Invalid */
+ test_parse_hw_addr_full_one("", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("12", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("12.", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("12.34.", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one(".12", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one(".12.34", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("12.34:56", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("1234:56", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("1234:56", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("12:34:", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one(":12:34", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("::1", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("aa:bb-cc", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("aa:xx", SIZE_MAX, NULL);
+ test_parse_hw_addr_full_one("aa bb", SIZE_MAX, NULL);
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
+ test_ether_addr_helpers();
test_HW_ADDR_TO_STRING();
+ test_parse_hw_addr();
return 0;
}
diff --git a/src/test/test-execute.c b/src/test/test-execute.c
index c81842a0d0..5818adbb55 100644
--- a/src/test/test-execute.c
+++ b/src/test/test-execute.c
@@ -1109,6 +1109,10 @@ static void test_exec_condition(Manager *m) {
test_service(m, "exec-condition-skip.service", SERVICE_SKIP_CONDITION);
}
+static void test_exec_umask_namespace(Manager *m) {
+ test(m, "exec-umask-namespace.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
+}
+
typedef struct test_entry {
test_function_t f;
const char *name;
@@ -1191,6 +1195,7 @@ int main(int argc, char *argv[]) {
entry(test_exec_specifier),
entry(test_exec_execsearchpath_specifier),
entry(test_exec_systemcallfilter_system),
+ entry(test_exec_umask_namespace),
{},
};
int r;
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index 41ddec4783..d8273bc846 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -765,7 +765,6 @@ static void test_rename_noreplace(void) {
static void test_chmod_and_chown(void) {
_cleanup_(rm_rf_physical_and_freep) char *d = NULL;
- _unused_ _cleanup_umask_ mode_t u = umask(0000);
struct stat st;
const char *p;
@@ -774,6 +773,8 @@ static void test_chmod_and_chown(void) {
log_info("/* %s */", __func__);
+ BLOCK_WITH_UMASK(0000);
+
assert_se(mkdtemp_malloc(NULL, &d) >= 0);
p = strjoina(d, "/reg");
diff --git a/src/timesync/meson.build b/src/timesync/meson.build
index ec80b16e82..6d6ddeb285 100644
--- a/src/timesync/meson.build
+++ b/src/timesync/meson.build
@@ -38,8 +38,7 @@ custom_target(
'timesyncd.conf',
input : 'timesyncd.conf.in',
output : 'timesyncd.conf',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : conf.get('ENABLE_TIMESYNCD') == 1 and install_sysconfdir_samples,
install_dir : pkgsysconfdir)
diff --git a/src/udev/dmi_memory_id/dmi_memory_id.c b/src/udev/dmi_memory_id/dmi_memory_id.c
index 14b893ae8f..67861cd364 100644
--- a/src/udev/dmi_memory_id/dmi_memory_id.c
+++ b/src/udev/dmi_memory_id/dmi_memory_id.c
@@ -539,7 +539,7 @@ static void dmi_table_decode(const uint8_t *buf, size_t len, uint16_t num) {
/* If a short entry is found (less than 4 bytes), not only it
* is invalid, but we cannot reliably locate the next entry.
- * Better stop at this point, and let the user know his/her
+ * Better stop at this point, and let the user know their
* table is broken. */
if (h.length < 4)
break;
diff --git a/src/udev/meson.build b/src/udev/meson.build
index 3423d6de94..0b692df3d8 100644
--- a/src/udev/meson.build
+++ b/src/udev/meson.build
@@ -162,8 +162,7 @@ custom_target(
'udev.pc',
input : 'udev.pc.in',
output : 'udev.pc',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : pkgconfigdatadir != 'no',
install_dir : pkgconfigdatadir)
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index a9c94b48ed..a3efb4e6c7 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -21,8 +21,8 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Match.MACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.mac)
-Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.permanent_mac)
+Match.MACAddress, config_parse_ether_addrs, 0, offsetof(LinkConfig, match.mac)
+Match.PermanentMACAddress, config_parse_ether_addrs, 0, offsetof(LinkConfig, match.permanent_mac)
Match.OriginalName, config_parse_match_ifnames, 0, offsetof(LinkConfig, match.ifname)
Match.Path, config_parse_match_strv, 0, offsetof(LinkConfig, match.path)
Match.Driver, config_parse_match_strv, 0, offsetof(LinkConfig, match.driver)
@@ -35,7 +35,7 @@ Match.KernelVersion, config_parse_net_condition,
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions)
Link.Description, config_parse_string, 0, offsetof(LinkConfig, description)
Link.MACAddressPolicy, config_parse_mac_address_policy, 0, offsetof(LinkConfig, mac_address_policy)
-Link.MACAddress, config_parse_hwaddr, 0, offsetof(LinkConfig, mac)
+Link.MACAddress, config_parse_ether_addr, 0, offsetof(LinkConfig, mac)
Link.NamePolicy, config_parse_name_policy, 0, offsetof(LinkConfig, name_policy)
Link.Name, config_parse_ifname, 0, offsetof(LinkConfig, name)
Link.AlternativeName, config_parse_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(LinkConfig, alternative_names)
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 1ce8e471e7..f91d2b6df1 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -22,8 +22,8 @@
#include "memory-util.h"
#include "net-condition.h"
#include "netif-naming-scheme.h"
+#include "netif-util.h"
#include "netlink-util.h"
-#include "network-util.h"
#include "parse-util.h"
#include "path-lookup.h"
#include "path-util.h"
@@ -118,8 +118,8 @@ static int link_parse_wol_password(LinkConfig *link, const char *str) {
if (!p)
return -ENOMEM;
- /* Reuse ether_addr_from_string(), as their formats are equivalent. */
- r = ether_addr_from_string(str, (struct ether_addr*) p);
+ /* Reuse parse_ether_addr(), as their formats are equivalent. */
+ r = parse_ether_addr(str, (struct ether_addr*) p);
if (r < 0)
return r;
diff --git a/src/udev/test-udev-netlink.c b/src/udev/test-udev-netlink.c
index d177b09567..c1213b7ca8 100644
--- a/src/udev/test-udev-netlink.c
+++ b/src/udev/test-udev-netlink.c
@@ -2,7 +2,7 @@
#include "sd-device.h"
-#include "arphrd-list.h"
+#include "arphrd-util.h"
#include "ether-addr-util.h"
#include "parse-util.h"
#include "tests.h"
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index c52f796361..4d16591750 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -957,7 +957,7 @@ static int builtin_net_id(sd_device *dev, sd_netlink **rtnl, int argc, char *arg
if (names_mac(dev, &info) >= 0) {
char str[ALTIFNAMSIZ];
- xsprintf(str, "%s%s", prefix, HW_ADDR_TO_STR(&info.hw_addr));
+ xsprintf(str, "%s%s", prefix, HW_ADDR_TO_STR_FULL(&info.hw_addr, HW_ADDR_TO_STRING_NO_COLON));
udev_builtin_add_property(dev, test, "ID_NET_NAME_MAC", str);
ieee_oui(dev, &info, test);
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
index b0ee3ce258..4dac6b0e44 100644
--- a/src/userdb/userdbctl.c
+++ b/src/userdb/userdbctl.c
@@ -5,6 +5,7 @@
#include "dirent-util.h"
#include "errno-list.h"
+#include "escape.h"
#include "fd-util.h"
#include "format-table.h"
#include "format-util.h"
@@ -33,6 +34,8 @@ static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static char** arg_services = NULL;
static UserDBFlags arg_userdb_flags = 0;
+static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
+static bool arg_chain = false;
STATIC_DESTRUCTOR_REGISTER(arg_services, strv_freep);
@@ -58,7 +61,7 @@ static int show_user(UserRecord *ur, Table *table) {
break;
case OUTPUT_JSON:
- json_variant_dump(ur->json, JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_PRETTY, NULL, 0);
+ json_variant_dump(ur->json, arg_json_format_flags, NULL, 0);
break;
case OUTPUT_FRIENDLY:
@@ -152,35 +155,49 @@ static int display_user(int argc, char *argv[], void *userdata) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
r = userdb_all(arg_userdb_flags, &iterator);
- if (r < 0)
+ if (r == -ENOLINK) /* ENOLINK → Didn't find answer without Varlink, and didn't try Varlink because was configured to off. */
+ log_debug_errno(r, "No entries found. (Didn't check via Varlink.)");
+ else if (r == -ESRCH) /* ESRCH → Couldn't find any suitable entry, but we checked all sources */
+ log_debug_errno(r, "No entries found.");
+ else if (r < 0)
return log_error_errno(r, "Failed to enumerate users: %m");
+ else {
+ for (;;) {
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
- for (;;) {
- _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
-
- r = userdb_iterator_get(iterator, &ur);
- if (r == -ESRCH)
- break;
- if (r == -EHOSTDOWN)
- return log_error_errno(r, "Selected user database service is not available for this request.");
- if (r < 0)
- return log_error_errno(r, "Failed acquire next user: %m");
+ r = userdb_iterator_get(iterator, &ur);
+ if (r == -ESRCH)
+ break;
+ if (r == -EHOSTDOWN)
+ return log_error_errno(r, "Selected user database service is not available for this request.");
+ if (r < 0)
+ return log_error_errno(r, "Failed acquire next user: %m");
- if (draw_separator && arg_output == OUTPUT_FRIENDLY)
- putchar('\n');
+ if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+ putchar('\n');
- r = show_user(ur, table);
- if (r < 0)
- return r;
+ r = show_user(ur, table);
+ if (r < 0)
+ return r;
- draw_separator = true;
+ draw_separator = true;
+ }
}
}
if (table) {
- r = table_print(table, NULL);
- if (r < 0)
- return table_log_print_error(r);
+ if (table_get_rows(table) > 1) {
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
+ if (r < 0)
+ return table_log_print_error(r);
+ }
+
+ if (arg_legend) {
+ if (table_get_rows(table) > 1)
+ printf("\n%zu users listed.\n", table_get_rows(table) - 1);
+ else
+ printf("No users.\n");
+ }
}
return ret;
@@ -211,7 +228,7 @@ static int show_group(GroupRecord *gr, Table *table) {
}
case OUTPUT_JSON:
- json_variant_dump(gr->json, JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_PRETTY, NULL, 0);
+ json_variant_dump(gr->json, arg_json_format_flags, NULL, 0);
break;
case OUTPUT_FRIENDLY:
@@ -303,36 +320,49 @@ static int display_group(int argc, char *argv[], void *userdata) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
r = groupdb_all(arg_userdb_flags, &iterator);
- if (r < 0)
+ if (r == -ENOLINK)
+ log_debug_errno(r, "No entries found. (Didn't check via Varlink.)");
+ else if (r == -ESRCH)
+ log_debug_errno(r, "No entries found.");
+ else if (r < 0)
return log_error_errno(r, "Failed to enumerate groups: %m");
+ else {
+ for (;;) {
+ _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
- for (;;) {
- _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
-
- r = groupdb_iterator_get(iterator, &gr);
- if (r == -ESRCH)
- break;
- if (r == -EHOSTDOWN)
- return log_error_errno(r, "Selected group database service is not available for this request.");
- if (r < 0)
- return log_error_errno(r, "Failed acquire next group: %m");
+ r = groupdb_iterator_get(iterator, &gr);
+ if (r == -ESRCH)
+ break;
+ if (r == -EHOSTDOWN)
+ return log_error_errno(r, "Selected group database service is not available for this request.");
+ if (r < 0)
+ return log_error_errno(r, "Failed acquire next group: %m");
- if (draw_separator && arg_output == OUTPUT_FRIENDLY)
- putchar('\n');
+ if (draw_separator && arg_output == OUTPUT_FRIENDLY)
+ putchar('\n');
- r = show_group(gr, table);
- if (r < 0)
- return r;
+ r = show_group(gr, table);
+ if (r < 0)
+ return r;
- draw_separator = true;
+ draw_separator = true;
+ }
}
-
}
if (table) {
- r = table_print(table, NULL);
- if (r < 0)
- return table_log_print_error(r);
+ if (table_get_rows(table) > 1) {
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
+ if (r < 0)
+ return table_log_print_error(r);
+ }
+
+ if (arg_legend) {
+ if (table_get_rows(table) > 1)
+ printf("\n%zu groups listed.\n", table_get_rows(table) - 1);
+ else
+ printf("No groups.\n");
+ }
}
return ret;
@@ -362,7 +392,7 @@ static int show_membership(const char *user, const char *group, Table *table) {
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
- json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO, NULL, NULL);
+ json_variant_dump(v, arg_json_format_flags, NULL, NULL);
break;
}
@@ -442,30 +472,44 @@ static int display_memberships(int argc, char *argv[], void *userdata) {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
r = membershipdb_all(arg_userdb_flags, &iterator);
- if (r < 0)
+ if (r == -ENOLINK)
+ log_debug_errno(r, "No entries found. (Didn't check via Varlink.)");
+ else if (r == -ESRCH)
+ log_debug_errno(r, "No entries found.");
+ else if (r < 0)
return log_error_errno(r, "Failed to enumerate memberships: %m");
+ else {
+ for (;;) {
+ _cleanup_free_ char *user = NULL, *group = NULL;
- for (;;) {
- _cleanup_free_ char *user = NULL, *group = NULL;
-
- r = membershipdb_iterator_get(iterator, &user, &group);
- if (r == -ESRCH)
- break;
- if (r == -EHOSTDOWN)
- return log_error_errno(r, "Selected membership database service is not available for this request.");
- if (r < 0)
- return log_error_errno(r, "Failed acquire next membership: %m");
+ r = membershipdb_iterator_get(iterator, &user, &group);
+ if (r == -ESRCH)
+ break;
+ if (r == -EHOSTDOWN)
+ return log_error_errno(r, "Selected membership database service is not available for this request.");
+ if (r < 0)
+ return log_error_errno(r, "Failed acquire next membership: %m");
- r = show_membership(user, group, table);
- if (r < 0)
- return r;
+ r = show_membership(user, group, table);
+ if (r < 0)
+ return r;
+ }
}
}
if (table) {
- r = table_print(table, NULL);
- if (r < 0)
- return table_log_print_error(r);
+ if (table_get_rows(table) > 1) {
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
+ if (r < 0)
+ return table_log_print_error(r);
+ }
+
+ if (arg_legend) {
+ if (table_get_rows(table) > 1)
+ printf("\n%zu memberships listed.\n", table_get_rows(table) - 1);
+ else
+ printf("No memberships.\n");
+ }
}
return ret;
@@ -526,48 +570,102 @@ static int display_services(int argc, char *argv[], void *userdata) {
return table_log_add_error(r);
}
- if (table_get_rows(t) <= 0) {
- log_info("No services.");
- return 0;
+ if (table_get_rows(t) > 1) {
+ r = table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
+ if (r < 0)
+ return table_log_print_error(r);
}
- if (arg_output == OUTPUT_JSON)
- table_print_json(t, NULL, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO);
- else
- table_print(t, NULL);
+ if (arg_legend) {
+ if (table_get_rows(t) > 1)
+ printf("\n%zu services listed.\n", table_get_rows(t) - 1);
+ else
+ printf("No services.\n");
+ }
return 0;
}
static int ssh_authorized_keys(int argc, char *argv[], void *userdata) {
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+ char **chain_invocation;
int r;
+ assert(argc >= 2);
+
+ if (arg_chain) {
+ /* If --chain is specified, the rest of the command line is the chain command */
+
+ if (argc < 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "No chain command line specified, refusing.");
+
+ /* Make similar restrictions on the chain command as OpenSSH itself makes on the primary command. */
+ if (!path_is_absolute(argv[2]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Chain invocation of ssh-authorized-keys commands requires an absolute binary path argument.");
+
+ if (!path_is_normalized(argv[2]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Chain invocation of ssh-authorized-keys commands requires an normalized binary path argument.");
+
+ chain_invocation = argv + 2;
+ } else {
+ /* If --chain is not specified, then refuse any further arguments */
+
+ if (argc > 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many arguments.");
+
+ chain_invocation = NULL;
+ }
+
r = userdb_by_name(argv[1], arg_userdb_flags, &ur);
if (r == -ESRCH)
- return log_error_errno(r, "User %s does not exist.", argv[1]);
+ log_error_errno(r, "User %s does not exist.", argv[1]);
else if (r == -EHOSTDOWN)
- return log_error_errno(r, "Selected user database service is not available for this request.");
+ log_error_errno(r, "Selected user database service is not available for this request.");
else if (r == -EINVAL)
- return log_error_errno(r, "Failed to find user %s: %m (Invalid user name?)", argv[1]);
+ log_error_errno(r, "Failed to find user %s: %m (Invalid user name?)", argv[1]);
else if (r < 0)
- return log_error_errno(r, "Failed to find user %s: %m", argv[1]);
-
- if (strv_isempty(ur->ssh_authorized_keys))
- log_debug("User record for %s has no public SSH keys.", argv[1]);
+ log_error_errno(r, "Failed to find user %s: %m", argv[1]);
else {
- char **i;
+ if (strv_isempty(ur->ssh_authorized_keys))
+ log_debug("User record for %s has no public SSH keys.", argv[1]);
+ else {
+ char **i;
- STRV_FOREACH(i, ur->ssh_authorized_keys)
- printf("%s\n", *i);
+ STRV_FOREACH(i, ur->ssh_authorized_keys)
+ printf("%s\n", *i);
+ }
+
+ if (ur->incomplete) {
+ fflush(stdout);
+ log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", ur->user_name);
+ }
}
- if (ur->incomplete) {
- fflush(stdout);
- log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", ur->user_name);
+ if (chain_invocation) {
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *s = NULL;
+
+ s = quote_command_line(chain_invocation, SHELL_ESCAPE_EMPTY);
+ if (!s)
+ return log_oom();
+
+ log_debug("Chain invoking: %s", s);
+ }
+
+ execv(chain_invocation[0], chain_invocation);
+ if (errno == ENOENT) /* Let's handle ENOENT gracefully */
+ log_warning_errno(errno, "Chain executable '%s' does not exist, ignoring chain invocation.", chain_invocation[0]);
+ else {
+ log_error_errno(errno, "Failed to invoke chain executable '%s': %m", chain_invocation[0]);
+ if (r >= 0)
+ r = -errno;
+ }
}
- return EXIT_SUCCESS;
+ return r;
}
static int help(int argc, char *argv[], void *userdata) {
@@ -588,6 +686,7 @@ static int help(int argc, char *argv[], void *userdata) {
" users-in-group [GROUP…] Show users that are members of specified group(s)\n"
" groups-of-user [USER…] Show groups the specified user(s) is a member of\n"
" services Show enabled database services\n"
+ " ssh-authorized-keys USER Show SSH authorized keys for user\n"
"\nOptions:\n"
" -h --help Show this help\n"
" --version Show package version\n"
@@ -603,6 +702,9 @@ static int help(int argc, char *argv[], void *userdata) {
" --synthesize=BOOL Synthesize root/nobody user\n"
" --with-dropin=BOOL Control whether to include drop-in records\n"
" --with-varlink=BOOL Control whether to talk to services at all\n"
+ " --multiplexer=BOOL Control whether to use the multiplexer\n"
+ " --json=pretty|short JSON output mode\n"
+ " --chain Chain another command\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@@ -623,19 +725,25 @@ static int parse_argv(int argc, char *argv[]) {
ARG_WITH_DROPIN,
ARG_WITH_VARLINK,
ARG_SYNTHESIZE,
+ ARG_MULTIPLEXER,
+ ARG_JSON,
+ ARG_CHAIN,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
- { "output", required_argument, NULL, ARG_OUTPUT },
- { "service", required_argument, NULL, 's' },
- { "with-nss", required_argument, NULL, ARG_WITH_NSS },
- { "with-dropin", required_argument, NULL, ARG_WITH_DROPIN },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
+ { "output", required_argument, NULL, ARG_OUTPUT },
+ { "service", required_argument, NULL, 's' },
+ { "with-nss", required_argument, NULL, ARG_WITH_NSS },
+ { "with-dropin", required_argument, NULL, ARG_WITH_DROPIN },
{ "with-varlink", required_argument, NULL, ARG_WITH_VARLINK },
- { "synthesize", required_argument, NULL, ARG_SYNTHESIZE },
+ { "synthesize", required_argument, NULL, ARG_SYNTHESIZE },
+ { "multiplexer", required_argument, NULL, ARG_MULTIPLEXER },
+ { "json", required_argument, NULL, ARG_JSON },
+ { "chain", no_argument, NULL, ARG_CHAIN },
{}
};
@@ -661,7 +769,9 @@ static int parse_argv(int argc, char *argv[]) {
for (;;) {
int c;
- c = getopt_long(argc, argv, "hjs:N", options, NULL);
+ c = getopt_long(argc, argv,
+ arg_chain ? "+hjs:N" : "hjs:N", /* When --chain was used disable parsing of further switches */
+ options, NULL);
if (c < 0)
break;
@@ -682,7 +792,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_OUTPUT:
- if (streq(optarg, "classic"))
+ if (isempty(optarg))
+ arg_output = _OUTPUT_INVALID;
+ else if (streq(optarg, "classic"))
arg_output = OUTPUT_CLASSIC;
else if (streq(optarg, "friendly"))
arg_output = OUTPUT_FRIENDLY;
@@ -699,9 +811,19 @@ static int parse_argv(int argc, char *argv[]) {
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid --output= mode: %s", optarg);
+ arg_json_format_flags = arg_output == OUTPUT_JSON ? JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO : JSON_FORMAT_OFF;
+ break;
+
+ case ARG_JSON:
+ r = parse_json_argument(optarg, &arg_json_format_flags);
+ if (r <= 0)
+ return r;
+
+ arg_output = FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF) ? _OUTPUT_INVALID : OUTPUT_JSON;
break;
case 'j':
+ arg_json_format_flags = JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR_AUTO;
arg_output = OUTPUT_JSON;
break;
@@ -758,6 +880,18 @@ static int parse_argv(int argc, char *argv[]) {
SET_FLAG(arg_userdb_flags, USERDB_DONT_SYNTHESIZE, !r);
break;
+ case ARG_MULTIPLEXER:
+ r = parse_boolean_argument("--multiplexer=", optarg, NULL);
+ if (r < 0)
+ return r;
+
+ SET_FLAG(arg_userdb_flags, USERDB_AVOID_MULTIPLEXER, !r);
+ break;
+
+ case ARG_CHAIN:
+ arg_chain = true;
+ break;
+
case '?':
return -EINVAL;
@@ -780,7 +914,7 @@ static int run(int argc, char *argv[]) {
/* This one is a helper for sshd_config's AuthorizedKeysCommand= setting, it's not a
* user-facing verb and thus should not appear in man pages or --help texts. */
- { "ssh-authorized-keys", 2, 2, 0, ssh_authorized_keys },
+ { "ssh-authorized-keys", 2, VERB_ANY, 0, ssh_authorized_keys },
{}
};
diff --git a/src/userdb/userwork.c b/src/userdb/userwork.c
index 3e670d61f7..1b1a4835e6 100644
--- a/src/userdb/userwork.c
+++ b/src/userdb/userwork.c
@@ -114,7 +114,6 @@ static int build_user_json(Varlink *link, UserRecord *ur, JsonVariant **ret) {
static int userdb_flags_from_service(Varlink *link, const char *service, UserDBFlags *ret) {
assert(link);
- assert(service);
assert(ret);
if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
@@ -153,7 +152,8 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var
return r;
r = userdb_flags_from_service(link, p.service, &userdb_flags);
- if (r < 0)
+ if (r != 0) /* return value of < 0 means error (as usual); > 0 means 'already processed and replied,
+ * we are done'; == 0 means 'not processed, caller should process now' */
return r;
if (uid_is_valid(p.uid))
@@ -165,6 +165,14 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
r = userdb_all(userdb_flags, &iterator);
+ if (IN_SET(r, -ESRCH, -ENOLINK))
+ /* We turn off Varlink lookups in various cases (e.g. in case we only enable DropIn
+ * backend) — this might make userdb_all return ENOLINK (which indicates that varlink
+ * was off and no other suitable source or entries were found). Let's hide this
+ * implementation detail and always return NoRecordFound in this case, since from a
+ * client's perspective it's irrelevant if there was no entry at all or just not on
+ * the service that the query was limited to. */
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
if (r < 0)
return r;
@@ -280,7 +288,7 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
return r;
r = userdb_flags_from_service(link, p.service, &userdb_flags);
- if (r < 0)
+ if (r != 0)
return r;
if (gid_is_valid(p.gid))
@@ -292,6 +300,8 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
r = groupdb_all(userdb_flags, &iterator);
+ if (IN_SET(r, -ESRCH, -ENOLINK))
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
if (r < 0)
return r;
@@ -361,7 +371,7 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
return r;
r = userdb_flags_from_service(link, p.service, &userdb_flags);
- if (r < 0)
+ if (r != 0)
return r;
if (p.group_name)
@@ -370,6 +380,8 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
else
r = membershipdb_all(userdb_flags, &iterator);
+ if (IN_SET(r, -ESRCH, -ENOLINK))
+ return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
if (r < 0)
return r;
diff --git a/src/vconsole/meson.build b/src/vconsole/meson.build
index dea4121f1a..eb22358c20 100644
--- a/src/vconsole/meson.build
+++ b/src/vconsole/meson.build
@@ -4,7 +4,6 @@ custom_target(
'90-vconsole.rules',
input : '90-vconsole.rules.in',
output : '90-vconsole.rules',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : conf.get('ENABLE_VCONSOLE') == 1,
install_dir : udevrulesdir)
diff --git a/src/xdg-autostart-generator/xdg-autostart-service.c b/src/xdg-autostart-generator/xdg-autostart-service.c
index 501cdca0b6..241a5b3cfd 100644
--- a/src/xdg-autostart-generator/xdg-autostart-service.c
+++ b/src/xdg-autostart-generator/xdg-autostart-service.c
@@ -597,6 +597,7 @@ int xdg_autostart_service_generate_unit(
fprintf(f,
"\n[Service]\n"
"Type=exec\n"
+ "ExitType=cgroup\n"
"ExecStart=:%s\n"
"Restart=no\n"
"TimeoutSec=5s\n"
diff --git a/sysctl.d/meson.build b/sysctl.d/meson.build
index b058f9364b..1745a13bfb 100644
--- a/sysctl.d/meson.build
+++ b/sysctl.d/meson.build
@@ -16,8 +16,7 @@ custom_target(
'50-coredump.conf',
input : '50-coredump.conf.in',
output : '50-coredump.conf',
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : conf.get('ENABLE_COREDUMP') == 1,
install_dir : sysctldir)
diff --git a/sysusers.d/meson.build b/sysusers.d/meson.build
index 132d8a187a..73d507f1f1 100644
--- a/sysusers.d/meson.build
+++ b/sysusers.d/meson.build
@@ -33,8 +33,7 @@ foreach tuple : in_files
file,
input : file + '.in',
output: file,
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : tuple[1],
install_dir : sysusersdir)
endforeach
diff --git a/test/TEST-56-EXIT-TYPE/Makefile b/test/TEST-56-EXIT-TYPE/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-56-EXIT-TYPE/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-56-EXIT-TYPE/test.sh b/test/TEST-56-EXIT-TYPE/test.sh
new file mode 100755
index 0000000000..0f84dca1ba
--- /dev/null
+++ b/test/TEST-56-EXIT-TYPE/test.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+set -e
+
+TEST_DESCRIPTION="test ExitType=cgroup"
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+do_test "$@"
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 2ff20e0f08..26ab3931e0 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -352,6 +352,8 @@ RouteTable=
RouteMetric=
UseDNS=
DHCPv6Client=
+UseGateway=
+UseRoutePrefix=
UseAutonomousPrefix=
UseOnLinkPrefix=
RouterAllowList=
@@ -468,7 +470,17 @@ ECN=
Parent=
Handle=
Bandwidth=
+AutoRateIngress=
OverheadBytes=
+MPUBytes=
+CompensationMode=
+UseRawPacketSize=
+FlowIsolationMode=
+NAT=
+PriorityQueueingPreset=
+FirewallMark=
+Wash=
+SplitGSO=
[TrafficControlQueueingDiscipline]
Parent=
NetworkEmulatorDelaySec=
diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
index d2e2a120a0..176c825cab 100644
--- a/test/fuzz/fuzz-unit-file/directives.service
+++ b/test/fuzz/fuzz-unit-file/directives.service
@@ -161,6 +161,7 @@ ExecStartPost=
ExecStartPre=
ExecStop=
ExecStopPost=
+ExitType=
ExtensionImages=
FailureAction=
FileDescriptorStoreMax=
diff --git a/test/test-execute/exec-umask-namespace.service b/test/test-execute/exec-umask-namespace.service
new file mode 100644
index 0000000000..8419c86c9a
--- /dev/null
+++ b/test/test-execute/exec-umask-namespace.service
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Test for UMask= + namespacing
+
+[Service]
+ExecStart=/bin/ls -lahd /tmp/subdir
+Type=oneshot
+User=65534
+Group=65534
+TemporaryFileSystem=/tmp:ro
+BindPaths=/etc:/tmp/subdir/subsub
+UMask=0007
diff --git a/test/test-network/conf/25-qdisc-cake.network b/test/test-network/conf/25-qdisc-cake.network
index 9220a20bf5..b13720c6dd 100644
--- a/test/test-network/conf/25-qdisc-cake.network
+++ b/test/test-network/conf/25-qdisc-cake.network
@@ -9,5 +9,15 @@ Address=10.1.2.3/16
[CAKE]
Parent=root
Handle=3a
-OverheadBytes=128
Bandwidth=500M
+AutoRateIngress=yes
+OverheadBytes=128
+MPUBytes=20
+CompensationMode=atm
+UseRawPacketSize=yes
+FlowIsolationMode=dual-dst-host
+NAT=yes
+PriorityQueueingPreset=diffserv8
+FirewallMark=0xff00
+Wash=yes
+SplitGSO=yes
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 9d12d6a609..a68055086c 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -3262,9 +3262,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
output = check_output('tc qdisc show dev dummy98')
print(output)
- self.assertRegex(output, 'qdisc cake 3a: root')
- self.assertRegex(output, 'bandwidth 500Mbit')
- self.assertRegex(output, 'overhead 128')
+ self.assertIn('qdisc cake 3a: root', output)
+ self.assertIn('bandwidth 500Mbit', output)
+ self.assertIn('autorate-ingress', output)
+ self.assertIn('diffserv8', output)
+ self.assertIn('dual-dsthost', output)
+ self.assertIn(' nat', output)
+ self.assertIn(' wash', output)
+ self.assertIn(' split-gso', output)
+ self.assertIn(' raw', output)
+ self.assertIn(' atm', output)
+ self.assertIn('overhead 128', output)
+ self.assertIn('mpu 20', output)
+ self.assertIn('fwmark 0xff00', output)
@expectedFailureIfPIEIsNotAvailable()
def test_qdisc_pie(self):
diff --git a/test/units/testsuite-20.sh b/test/units/testsuite-20.sh
index 17a27d7d7c..fc87c18c4c 100755
--- a/test/units/testsuite-20.sh
+++ b/test/units/testsuite-20.sh
@@ -143,6 +143,23 @@ systemd-run --unit=test20-mainpidsh3.service \
# Test that this failed due to timeout, and not some other error
test "$(systemctl show -P Result test20-mainpidsh3.service)" = timeout
+# Test that scope units work
+systemd-run --scope --unit test20-true.scope /bin/true
+test "$(systemctl show -P Result test20-true.scope)" = success
+
+# Test that user scope units work as well
+
+runas() {
+ declare userid=$1
+ shift
+ # shellcheck disable=SC2016
+ su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@"
+}
+
+systemctl start user@4711.service
+runas testuser systemd-run --scope --user --unit test20-true.scope /bin/true
+test "$(systemctl show -P Result test20-true.scope)" = success
+
systemd-analyze log-level info
echo OK >/testok
diff --git a/test/units/testsuite-56.service b/test/units/testsuite-56.service
new file mode 100644
index 0000000000..d8ad589ca0
--- /dev/null
+++ b/test/units/testsuite-56.service
@@ -0,0 +1,6 @@
+[Unit]
+Description=TEST-56-EXIT-TYPE
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-56.sh b/test/units/testsuite-56.sh
new file mode 100755
index 0000000000..b167320615
--- /dev/null
+++ b/test/units/testsuite-56.sh
@@ -0,0 +1,79 @@
+#!/usr/bin/env bash
+set -eux
+
+systemd-analyze log-level debug
+
+# Multiple level process tree, parent process stays up
+cat >/tmp/test56-exit-cgroup.sh <<EOF
+#!/usr/bin/env bash
+set -eux
+
+# process tree: systemd -> sleep
+sleep infinity &
+disown
+
+# process tree: systemd -> bash -> bash -> sleep
+((sleep infinity); true) &
+
+systemd-notify --ready
+
+# process tree: systemd -> bash -> sleep
+sleep infinity
+EOF
+chmod +x /tmp/test56-exit-cgroup.sh
+
+# service should be stopped cleanly
+systemd-run --wait --unit=one -p Type=notify -p ExitType=cgroup \
+ -p ExecStartPost='bash -c "systemctl stop one &"' \
+ /tmp/test56-exit-cgroup.sh
+
+# same thing with a truthy exec condition
+systemd-run --wait --unit=two -p Type=notify -p ExitType=cgroup \
+ -p ExecCondition=true \
+ -p ExecStartPost='bash -c "systemctl stop two &"' \
+ /tmp/test56-exit-cgroup.sh
+
+# false exec condition: systemd-run should exit immediately with status code: 1
+systemd-run --wait --unit=three -p Type=notify -p ExitType=cgroup \
+ -p ExecCondition=false \
+ /tmp/test56-exit-cgroup.sh \
+ && { echo 'unexpected success'; exit 1; }
+
+# service should exit uncleanly (main process exits with SIGKILL)
+systemd-run --wait --unit=four -p Type=notify -p ExitType=cgroup \
+ -p ExecStartPost='bash -c "systemctl kill --signal 9 four &"' \
+ /tmp/test56-exit-cgroup.sh \
+ && { echo 'unexpected success'; exit 1; }
+
+
+# Multiple level process tree, parent process exits quickly
+cat >/tmp/test56-exit-cgroup-parentless.sh <<EOF
+#!/usr/bin/env bash
+set -eux
+
+# process tree: systemd -> sleep
+sleep infinity &
+
+# process tree: systemd -> bash -> sleep
+((sleep infinity); true) &
+
+systemd-notify --ready
+EOF
+chmod +x /tmp/test56-exit-cgroup-parentless.sh
+
+# service should be stopped cleanly
+systemd-run --wait --unit=five -p Type=notify -p ExitType=cgroup \
+ -p ExecStartPost='bash -c "systemctl stop five &"' \
+ /tmp/test56-exit-cgroup-parentless.sh
+
+# service should still exit cleanly despite SIGKILL (the main process already exited cleanly)
+systemd-run --wait --unit=six -p Type=notify -p ExitType=cgroup \
+ -p ExecStartPost='bash -c "systemctl kill --signal 9 six &"' \
+ /tmp/test56-exit-cgroup-parentless.sh
+
+
+systemd-analyze log-level info
+
+echo OK >/testok
+
+exit 0
diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh
index 16cd8385fb..62d0a29cb5 100755
--- a/test/units/testsuite-58.sh
+++ b/test/units/testsuite-58.sh
@@ -84,6 +84,38 @@ cmp /var/tmp/testsuite-58.img /var/tmp/testsuite-58.2.img
rm /var/tmp/testsuite-58.img /var/tmp/testsuite-58.2.img /tmp/testsuite-58.dump
rm -r /tmp/testsuite-58-defs/
+# Third part: operate on an an image with unaligned partition, to see if that works.
+
+rm -f /var/tmp/testsuite-58.3.img /tmp/testsuite-58-3.dump
+mkdir -p /tmp/testsuite-58.3-defs/
+
+cat >/tmp/testsuite-58.3-defs/root.conf <<EOF
+[Partition]
+Type=root
+EOF
+
+truncate -s 10g /var/tmp/testsuite-58.3.img
+sfdisk /var/tmp/testsuite-58.3.img <<EOF
+label: gpt
+
+start=2048, size=69044
+start=71092, size=3591848
+EOF
+
+systemd-repart --definitions=/tmp/testsuite-58.3-defs/ \
+ --seed=750b6cd5c4ae4012a15e7be3c29e6a47 \
+ --dry-run=no \
+ /var/tmp/testsuite-58.3.img
+
+sfdisk --dump /var/tmp/testsuite-58.3.img | tee /tmp/testsuite-58.3.dump
+
+grep -qF '/var/tmp/testsuite-58.3.img1 : start= 2048, size= 69044,' /tmp/testsuite-58.3.dump
+grep -qF '/var/tmp/testsuite-58.3.img2 : start= 71092, size= 3591848,' /tmp/testsuite-58.3.dump
+grep -qxF '/var/tmp/testsuite-58.3.img3 : start= 3662944, size= 17308536, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0, name="root-x86-64", attrs="GUID:59"' /tmp/testsuite-58.3.dump
+
+rm /var/tmp/testsuite-58.3.img /tmp/testsuite-58.3.dump
+rm -r /tmp/testsuite-58.3-defs/
+
echo OK >/testok
exit 0
diff --git a/test/units/testsuite-65.sh b/test/units/testsuite-65.sh
index 3218462c76..f98a933cb1 100755
--- a/test/units/testsuite-65.sh
+++ b/test/units/testsuite-65.sh
@@ -76,6 +76,14 @@ systemd-analyze verify /tmp/.testfile.service
rm /tmp/.testfile.service
+# Alias a unit file's name on disk (see #20061)
+cp /tmp/testfile.service /tmp/testsrvc
+
+systemd-analyze verify /tmp/testsrvc \
+ && { echo 'unexpected success'; exit 1; }
+
+systemd-analyze verify /tmp/testsrvc:alias.service
+
# Zero exit status since the value used for comparison determine exposure to security threats is by default 100
systemd-analyze security --offline=true /tmp/testfile.service
diff --git a/tmpfiles.d/meson.build b/tmpfiles.d/meson.build
index de36f5743d..6ae9e3e0b8 100644
--- a/tmpfiles.d/meson.build
+++ b/tmpfiles.d/meson.build
@@ -40,8 +40,7 @@ foreach pair : in_files
pair[0],
input : pair[0] + '.in',
output: pair[0],
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : enable_tmpfiles,
install_dir : tmpfilesdir)
else
diff --git a/tools/meson-render-jinja2.py b/tools/meson-render-jinja2.py
index 9a6fc7c31c..0f9fc43124 100755
--- a/tools/meson-render-jinja2.py
+++ b/tools/meson-render-jinja2.py
@@ -2,6 +2,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
import ast
+import os
import re
import sys
@@ -27,4 +28,8 @@ def render(filename, defines):
if __name__ == '__main__':
defines = parse_config_h(sys.argv[1])
- print(render(sys.argv[2], defines))
+ output = render(sys.argv[2], defines)
+ with open(sys.argv[3], 'w') as f:
+ f.write(output)
+ info = os.stat(sys.argv[2])
+ os.chmod(sys.argv[3], info.st_mode)
diff --git a/units/meson.build b/units/meson.build
index c106284e0f..903443a85d 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -272,8 +272,7 @@ foreach tuple : in_units
file,
input : file + '.in',
output : file,
- command : [meson_render_jinja2, config_h, '@INPUT@'],
- capture : true,
+ command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : install,
install_dir : systemunitdir)
diff --git a/units/systemd-fsck-root.service.in b/units/systemd-fsck-root.service.in
index 6897f13159..8378df84c7 100644
--- a/units/systemd-fsck-root.service.in
+++ b/units/systemd-fsck-root.service.in
@@ -14,6 +14,8 @@ DefaultDependencies=no
Conflicts=shutdown.target
Before=local-fs.target shutdown.target
ConditionPathIsReadWrite=!/
+OnFailure=emergency.target
+OnFailureJobMode=replace-irreversibly
[Service]
Type=oneshot