- 'systemd-growfs'
- 'systemd-homed'
- 'systemd-hostnamed'
+ - 'systemd-hwdb'
- 'systemd-import'
- 'systemd-journal-gatewayd'
- 'systemd-journal-remote'
- 'systemd-growfs'
- 'systemd-homed'
- 'systemd-hostnamed'
+ - 'systemd-hwdb'
- 'systemd-import'
- 'systemd-journal-gatewayd'
- 'systemd-journal-remote'
growfs: ['systemd-growfs']
homed: ['systemd-homed', 'homectl', 'pam_systemd_home']
hostname: ['systemd-hostnamed', 'hostnamectl']
- hwdb: ['hardware database files']
+ hwdb: ['systemd-hwdb', 'hardware database files']
import: ['systemd-import']
journal: ['systemd-journald', 'journalctl']
journal-remote: ['systemd-journal-remote', 'systemd-journal-upload', 'systemd-journal-gatewayd']
rfkill: ['systemd-rfkill']
rpm: ['rpm scriptlets']
run: ['systemd-run']
- sd-boot: ['bootctl', 'systemd-boot', 'systemd-stub']
+ sd-boot/sd-stub/bootctl: ['bootctl', 'systemd-boot', 'systemd-stub']
sysctl: ['systemd-sysctl']
sysext: ['systemd-sysext']
systemctl: ['systemctl']
--- /dev/null
+# CODE FREEZE NOTICE
+
+An -rc1 tag has been created and a release is being prepared, so please note that
+PRs introducing new features and APIs will be held back until the new version
+has been released.
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
- name: Initialize CodeQL
- uses: github/codeql-action/init@3f62b754e23e0dd60f91b744033e1dc1654c0ec6
+ uses: github/codeql-action/init@0c670bbf0414f39666df6ce8e718ec5662c21e03
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql-config.yml
- run: sudo -E .github/workflows/unit_tests.sh SETUP
- name: Autobuild
- uses: github/codeql-action/autobuild@3f62b754e23e0dd60f91b744033e1dc1654c0ec6
+ uses: github/codeql-action/autobuild@0c670bbf0414f39666df6ce8e718ec5662c21e03
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@3f62b754e23e0dd60f91b744033e1dc1654c0ec6
+ uses: github/codeql-action/analyze@0c670bbf0414f39666df6ce8e718ec5662c21e03
runs-on: ubuntu-22.04
if: github.repository == 'systemd/systemd'
env:
- COVERITY_SCAN_BRANCH_PATTERN: "${{ github.ref}}"
- COVERITY_SCAN_NOTIFICATION_EMAIL: ""
- COVERITY_SCAN_PROJECT_NAME: "${{ github.repository }}"
- # Set in repo settings -> secrets -> repository secrets
+ # Set in repo settings -> secrets -> actions
COVERITY_SCAN_TOKEN: "${{ secrets.COVERITY_SCAN_TOKEN }}"
- CURRENT_REF: "${{ github.ref }}"
+ COVERITY_SCAN_NOTIFICATION_EMAIL: "${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }}"
steps:
- name: Repository checkout
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
- # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
- - name: Set the $COVERITY_SCAN_NOTIFICATION_EMAIL env variable
- run: echo "COVERITY_SCAN_NOTIFICATION_EMAIL=$(git log -1 ${{ github.sha }} --pretty=\"%aE\")" >> "$GITHUB_ENV"
- - name: Install Coverity tools
- run: tools/get-coverity.sh
# Reuse the setup phase of the unit test script to avoid code duplication
- name: Install build dependencies
run: sudo -E .github/workflows/unit_tests.sh SETUP
- # Preconfigure with meson to prevent Coverity from capturing meson metadata
- - name: Preconfigure the build directory
- run: meson cov-build -Dman=false
- - name: Build
- run: tools/coverity.sh build
- - name: Upload the results
- run: tools/coverity.sh upload
+ - name: Build & upload the results
+ run: tools/coverity.sh
--- /dev/null
+---
+# https://github.com/redhat-plumbers-in-action/differential-shellcheck#readme
+
+name: Differential ShellCheck
+on:
+ pull_request:
+ branches:
+ - main
+
+permissions:
+ contents: read
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+
+ permissions:
+ security-events: write
+ pull-requests: write
+
+ steps:
+ - name: Repository checkout
+ uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
+ with:
+ fetch-depth: 0
+
+ - name: Differential ShellCheck
+ uses: redhat-plumbers-in-action/differential-shellcheck@60360c0fb283149ed03ad16b66257a967c3e5231
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
- - uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd
+ - uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93
with:
node-version: '16'
fetch-depth: 0
- name: Lint Code Base
- uses: github/super-linter/slim@a320804d310fdeb8d1a46c6c6c1e615d443b10c9
+ uses: github/super-linter/slim@2d64ac1c067c34beaf7d24cc12733cd46236f76e
env:
DEFAULT_BRANCH: main
MULTI_STATUS: false
# missing shebangs)
# - .*\.(in|SKELETON) - all template/skeleton files
# except kernel-install
- # - tools/coverity\.sh - external file (with some modifications)
- FILTER_REGEX_EXCLUDE: .*/(man/.*|([^k]|k(k|ek)*([^ek]|e[^kr]))*(k(k|ek)*e?)?\.(in|SKELETON)|tools/coverity\.sh)$
+ FILTER_REGEX_EXCLUDE: .*/(man/.*|([^k]|k(k|ek)*([^ek]|e[^kr]))*(k(k|ek)*e?)?\.(in|SKELETON))$
VALIDATE_ALL_CODEBASE: false
VALIDATE_BASH: true
VALIDATE_GITHUB_ACTIONS: true
release: jammy
- distro: fedora
release: "36"
+ - distro: fedora
+ release: rawhide
- distro: opensuse
release: tumbleweed
- distro: centos_epel
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
- - uses: systemd/mkosi@679bcc98fb8fbe7b53ed809c7019992877e18c14
+ - uses: systemd/mkosi@f37c19e7217a41c52d9a9a8769e98496255e2e2d
- name: Install
run: sudo apt-get update && sudo apt-get install --no-install-recommends python3-pexpect python3-jinja2
-meson==0.62.2 \
- --hash=sha256:a7669e4c4110b06b743d57cc5d6432591a6677ef2402139fe4f3d42ac13380b0 \
- --hash=sha256:c245d2b39e1ce1d1968e0b7067771fd02ca1bade1990adb3cf4088375ba188c9
+meson==0.63.0 \
+ --hash=sha256:399f2ca3181ef257fe3adb2deaff46bc19435cb8b1e883f26db831ce32139820 \
+ --hash=sha256:3b51d451744c2bc71838524ec8d96cd4f8c4793d5b8d5d0d0a9c8a4f7c94cd6f
ninja==1.10.2.3 \
--hash=sha256:0560eea57199e41e86ac2c1af0108b63ae77c3ca4d05a9425a750e908135935a \
--hash=sha256:21a1d84d4c7df5881bfd86c25cce4cf7af44ba2b8b255c57bc1c434ec30a2dfc \
for ((i = 0; i < 5; i++)); do
EC=0
- (sudo python3 -m mkosi --extra-tree="$TEMP_EXTRA_TREE" "$@") |& tee "$TEMPFILE" || EC=$?
+ (sudo timeout -k 30 10m python3 -m mkosi --extra-tree="$TEMP_EXTRA_TREE" "$@") |& tee "$TEMPFILE" || EC=$?
if [[ $EC -eq 0 ]]; then
# The command passed — let's return immediately
break
/mkosi.default.d/**/*local*.conf
/tags
.dir-locals-2.el
+.vscode/
UBUNTU_RELEASE="$(lsb_release -cs)"
create_container() {
- # Create autopkgtest LXC image; this sometimes fails with "Unable to fetch
- # GPG key from keyserver", so retry a few times with different keyservers.
- for keyserver in "keys.openpgp.org" "" "keyserver.ubuntu.com" "keys.gnupg.net"; do
- for retry in {1..5}; do
- sudo lxc-create -n "$CONTAINER" -t download -- -d "$DISTRO" -r "$RELEASE" -a "$ARCH" ${keyserver:+--keyserver "$keyserver"} && break 2
- sleep $((retry*retry))
- done
- done
+ sudo lxc-create -n "$CONTAINER" -t download -- -d "$DISTRO" -r "$RELEASE" -a "$ARCH"
# unconfine the container, otherwise some tests fail
echo 'lxc.apparmor.profile = unconfined' | sudo tee -a "/var/lib/lxc/$CONTAINER/config"
# now build the package and run the tests
rm -rf "$ARTIFACTS_DIR"
# autopkgtest exits with 2 for "some tests skipped", accept that
- "$AUTOPKGTEST_DIR/runner/autopkgtest" --env DEB_BUILD_OPTIONS=noudeb \
- --env TEST_UPSTREAM=1 ../systemd_*.dsc \
- -o "$ARTIFACTS_DIR" \
- -- lxc -s "$CONTAINER" \
+ sudo "$AUTOPKGTEST_DIR/runner/autopkgtest" --env DEB_BUILD_OPTIONS=noudeb \
+ --env TEST_UPSTREAM=1 ../systemd_*.dsc \
+ -o "$ARTIFACTS_DIR" \
+ -- lxc -s "$CONTAINER" \
|| [ $? -eq 2 ]
;;
*)
systemd System and Service Manager
-CHANGES WITH 252:
- Backwards-incompatible changes:
+CHANGES WITH 252 in spe:
+
+ Announcement of Future Feature Removal
+
+ * Please note that we intend to remove cgroupsv1 support from systemd
+ release after EOY 2023. If you run services that make explicit use of
+ cgroupsv1 features, please implement compatibility with cgroupsv2
+ sooner rather than later, if you haven't done so yet. Most of Linux
+ userspace has been ported over already.
+
+ New features:
+
+ * systemd-measure is a new helper to precalculate PCR measurements
+ to make it easier to set TPM2 policies.
+
+ Changes in systemd itself, i.e. the manager, and units
+
+ * The cpu controller is delegated to user manager units, and CPUWeight=
+ settings are applied to the top-level user slice units (app.slice,
+ background.slice, session.slice). This provides a degree of resource
+ isolation between different user services competing for the CPU.
+
+ * Systemd can optionally do a full preset in the "first boot" condition
+ (instead of just enable-only). This behaviour is controlled by the
+ compile-time option -Dfirst-boot-full-preset=. Right now it defaults
+ to 'false', but the plan is to switch it to 'true' for the subsequent
+ release.
+
+ * Systemd will set the taint flag 'support-ended' if it detects that
+ the os image is past its end-of-support date.
+
+ * Two new settings ConditionCredential= and AssertCredential= can
+ be used to skip or fail units if a certain credential is not provided.
+
+ * ConditionMemory= accepts size suffixes.
+
+ * DefaultSmackProcessLabel= can be used in system.conf and user.conf
+ to specify the smack label to use when not specified in a unit file.
+
+ * DefaultDeviceTimeoutSec= can be used system.conf and user.conf
+ to specify the default timeout for devices.
+
+ * C.UTF-8 is used as the default locale if nothing else has been configured.
+
+ Changes in sd-boot, bootctl, and the Boot Loader Specification:
+
+ * The Boot Loader Specification has been cleaned up and clarified.
+ Various corner cases in version string comparisons have been fixed
+ (e.g. comparisons for empty strings). Boot counting is now part of
+ the main specification.
+
+ * New PCRs measurements are set during boot: PCR 11 for the the
+ kernel+initrd combo, PCR 13 for any sysext images.
+
+ * The UEFI monotonic boot counter is now included in the random seed,
+ providing some additional entropy.
+
+ * Booting in EFI mixed mode (a 64-bit kernel over 32-bit UEFI firmware)
+ is now supported.
+
+ * bootctl gained a bunch of new options: '--all-architectures' to
+ install binaries for all supported EFI architectures, '--root=' and
+ '--image=' options to operate on a directory or disk image, and
+ '--install-source=' to specify the source for binaries to install.
+
+ * The sd-boot stub exports a StubFeatures flag, which is used by
+ bootctl to show features supported by the stub that was used to boot.
+
+ Changes in the hardware database:
+
+ * 'systemd-hwdb query' now supports the '--root' option.
+
+ Changes in systemctl:
+
+ * systemctl now supports '--state' and '--type' options for the 'show'
+ and 'status' verbs.
+
+ * systemctl gained a new verb 'list-automounts' to list automount
+ points.
+
+ Changes in systemd-networkd:
+
+ * networkd can set Linux NetLabel labels for integration with the
+ network control in security modules via a new NetLabel= option.
+
+ * networkd gained new options NFTSet=, IPv4NFTSet=, IPv6NFTSet= that
+ take names of nft sets as arguments. It will automatically add rules
+ for the subnets configured for an interface to those sets.
+
+ * The RapidCommit= is (re-)introduced to enable faster configuration
+ via DHCPv6 (RFC 3315).
+
+ Changes in systemd-nspawn:
+
+ * The --bind= and --overlay= options now support relative paths.
+
+ Changes in libsystemd and other libraries:
+
+ * libsystemd now exports the sd-netlink interface that provides
+ functions to send/receive/parse netlink and rtnl messages.
+
+ * libsystemd now exports sd_bus_error_setfv (a convenience function for
+ setting bus errors), sd_id128_string_equal (a convenience function
+ for identifier comparisons), sd_bus_message_read_strv_extend (a
+ function to incrementally read string arrays).
+
+ * Private shared libraries (libsystemd-shared-nnn.so,
+ libsystemd-core-nnn.so) are now installed into arch-specific
+ directories to allow multi-arch installs.
+
+ Changes in other components:
+
+ * sysusers and tmpfiles configuration can now be provided via the
+ credential mechanism.
+
+ * tmpfiles can read file contents to write from a credential (and a new
+ modifier char '^' to specify that the argument is a credential name).
+ This mechanism is used to automatically populate /etc/motd, /etc/issue,
+ and /etc/hosts from credentials.
+
+ * systemd-analyze gained a new verb 'compare-versions' that implements
+ comparisons for versions strings (similarly to 'rpmdev-vercmp' and
+ 'dpkg --compare-versions').
+
+ * The pkgconfig and rpm macros files now export the directory for user
+ units as 'user_tmpfiles_dir' and '_user_tmpfilesdir'.
+
+ * Detection of Parallels and KubeVirt virtualization has been improved.
+
+ * os-release gained a new field SUPPORT_END=YYYY-MM-DD to inform the
+ user when their system will become unsupported.
+
+ * When performing suspend-then-hibernate, the system will estimate the
+ discharge rate and use that to set the delay until hibernation, and
+ will hibernate immediately instead of suspending when running from a
+ battery and the capacity is below 5%.
+
+ * systemd-sysctl gained a '--strict' option to fail when a sysctl
+ setting is unknown to the kernel.
+
+ * machinectl supports '--force' for the 'copy-to' and 'copy-from'
+ verbs.
+
+ * openssl is the default crypto backend for systemd-resolved. (gnutls
+ is still supported.)
+
+ Experimental features:
+
+ * BPF programs can now be compiled with bpf-gcc.
+
+ * sd-boot can automatically enroll SecureBoot keys from files found on
+ the ESP. This enrollment can be either automatic ('force' mode) or
+ controlled by the user ('manual' mode).
+
+ – Somewhere, sometime
- * systemd-logind's background session class, which is by default used
- for sessions started via cron, is no longer going to start systemd
- --user instance. Note that because of that the user dbus instance
- will not start either. Other session types remain unaffected.
CHANGES WITH 251:
python >= 3.5
meson >= 0.53.2
ninja
- gcc, awk, sed, grep, and similar tools
+ gcc >= 4.7
+ awk, sed, grep, and similar tools
clang >= 10.0, llvm >= 10.0 (optional, required to build BPF programs
from source code in C)
gnu-efi >= 3.0.5 (optional, required for systemd-boot)
* rework mount.c and swap.c to follow proper state enumeration/deserialization
semantics, like we do for device.c now
+Deprecations and removals:
+
+* Remove any support for booting without /usr pre-mounted in the initrd entirely.
+ Update INITRD_INTERFACE.md accordingly.
+
+* 2019-10 – Remove POINTINGSTICK_CONST_ACCEL references from the hwdb, see #9573
+
+* remove cgrouspv1 support EOY 2023. As per
+ https://lists.freedesktop.org/archives/systemd-devel/2022-July/048120.html
+ and then rework cgroupsv2 support around fds, i.e. keep one fd per active
+ unit around, and always operate on that, instead of cgroup fs paths.
+
+* drop support for kernels that lack ambient capabilities support (i.e. make
+ 4.3 new baseline). Then drop support for "!!" modifier for ExecStart= which
+ is only supported for such old kernels.
+
+* drop support for getrandom()-less kernels. (GRND_INSECURE means once kernel
+ 5.6 becomes our baseline). See
+ https://github.com/systemd/systemd/pull/24101#issuecomment-1193966468 for
+ details. Maybe before that: at taint-flags/warn about kernels that lack
+ getrandom()/environments where it is blocked.
+
+* drop support for LOOP_CONFIGURE-less loopback block devices, once kernel
+ baseline is 5.8.
+
+* drop fd_is_mount_point() fallback mess once we can rely on
+ STATX_ATTR_MOUNT_ROOT to exist i.e. kernel baseline 5.8
+
+* rework our PID tracking in services and so on, to be strictly based on pidfd,
+ once kernel baseline is 5.13.
+
+* ~2023: remove support for TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT
+
Features:
+* sd-stub/sd-boot: write RNG seed to LINUX_EFI_RANDOM_SEED_TABLE_GUID config
+ table as well. (and possibly drop our efi var). Current kernels will pick up
+ the seed from there already, if EFI_RNG_PROTOCOL is not implemented by
+ firmware.
+
+* sd-boot: include domain specific hash string in hash function for random seed
+ plus sizes of everything. also include DMI/SMBIOS blob
+
+* accept a random seed via DMI/SMBIOS vendor string that is credited to the
+ kernel RNG, as cheap alternative to virtio-rng
+
+* sd-stub: invoke random seed logic the same way as in sd-boot, except if
+ random seed EFI variable is already set. That way, the variable set will be
+ set in all cases: if you just use sd-stub, or just sd-boot, or both.
+
+* sd-boot: we probably should include all BootXY EFI variable defined boot
+ entries in our menu, and then suppress ourselves. Benefit: instant
+ compatibility with all other OSes which register things there, in particular
+ on other disks. Always boot into them via NextBoot EFI variable, to not
+ affect PCR values.
+
+* systemd-measure tool:
+ - pre-calculate PCR 12 (command line) + PCR 13 (sysext) the same way we can precalculate PCR 11
+ - sign pre-calculated hashes in a way compatible with TPM2 PCR hash signature
+ policies, in a way they can be included in unified PE kernel images, and
+ made available to userspace. There, this should be consumed by
+ systemd-cryptsetup to implement PCR signature based TPM volume unlock
+ policies.
+
+* in sd-boot: load EFI drivers from a new PE section. That way, one can have a
+ "supercharged" sd-boot binary, that could carry ext4 drivers built-in.
+
+* sd-bus: document that sd_bus_process() only returns messages that non of the
+ filters/handlers installed on the connection took possession of.
+
+* sd-device: add an API for opening a child device, given a device object
+
+* sd-device: add an API for acquiring list of child devices, given a device
+ objects (i.e. all child dirents that dirs or symlinks to dirs)
+
+* sd-device: maybe pin the sysfs dir with an fd, during the entire runtime of
+ an sd_device, then always work based on that.
+
+* add small wrapper around qemu that implements sd_notify/AF_VSOCK + machined and
+ maybe some other stuff and boots it
+
+* maybe add new flags to gpt partition tables for rootfs and usrfs indicating
+ purpose, i.e. whether something is supposed to be bootable in a VM, on
+ baremetal, on an nspawn-style container, if it is a portable service image,
+ or a sysext for initrd, for host os, or for portable container. Then hook
+ portabled/… up to udev to watch block devices coming up with the flags set, and
+ use it.
+
+* portabled: read a credential "portable.extra" or so, that takes a list of
+ file system paths to enable on start.
+
+* sd-boot should look for information what to boot in SMBIOS, too, so that VM
+ managers can tell sd-boot what to boot into and suchlike
+
+* PID 1 should look for an SMBIOS variable that encodes an AF_VSOCK address it
+ should send sd_notify() ready notifications to. That way a VMM can boot up a
+ system, and generically know when it finished booting.
+
+* add "systemd-sysext identify" verb, that you can point on any file in /usr/
+ and that determines from which overlayfs layer it originates, which image, and with
+ what it was signed.
+
* journald: generate recognizable log events whenever we shutdown journald
cleanly, and when we migrate run → var. This way tools can verify that a
previous boot terminated cleanly, because either of these two messages must
* networkd/udevd: add a way to define additional .link, .network, .netdev files
via the credentials logic.
-* fstab-generator: allow definining additional fstab-like mounts via
+* fstab-generator: allow defining additional fstab-like mounts via
credentials (similar: crypttab-generator, verity-generator,
integrity-generator)
* resolved: allow defining additional /etc/hosts entries via a credential (it
might make sense to then synthesize a new combined /etc/hosts file in /run
and bind mount it on /etc/hosts for other clients that want to read it.
+ Similar, allow picking up DNS server IP addresses from credential.
+
+* repart: allow defining additional partitions via credential
+
+* tmpfiles: add snippet that provisions /etc/hosts, /etc/motd,
+ /root/.ssh/authorized_keys from credential
+
+* timesyncd: pick NTP server info from credential
* define a JSON format for units, separating out unit definitions from unit
runtime state. Then, expose it:
* pam_systemd: on interactive logins, maybe show SUPPORT_END information at
login time, á la motd
-* similar to the existing fw_cfg support, also support reading system
- credentials from DMI vendor fields, as supported by qemu
-
-* sd-boot: instead of uncondtionally deriving the ESP to search boot loader
+* sd-boot: instead of unconditionally deriving the ESP to search boot loader
spec entries in from the paths of sd-boot binary, let's optionally allow it
- to be configured on sd-boot cmdline + efi var. Usecase: embedd sd-boot in the
+ to be configured on sd-boot cmdline + efi var. Usecase: embed sd-boot in the
UEFI firmware (for example, ovmf supports that via qemu cmdline option), and
- use it to load stuff from the ESP).
+ use it to load stuff from the ESP.
* make tmpfiles read lines from creds, so that we can provision SSH host keys
via creds. Similar: sysusers, sysctl, homed
* ask dracut to generate usr= on the kernel cmdline so that we don't need to
read /etc/fstab from the root fs from the initrd and do daemon-reload
-* document that process resource limits are bullshit
-
* add PR_SET_DUMPABLE service setting
* homed/userdb: maybe define a "companion" dir for home directories where apps
cloud-init/ignitation and similar can parameterize the host with data they
acquire.
-* Add ConditionCredentialExists= or so, that allows conditionalizing services
- depending on whether a specific system credential is set. Usecase: a service
- similar to the ssh keygen service that installs any SSH host key supplied via
- system credentials into /etc/ssh.
-
-* drop support for kernels that lack ambient capabilities support (i.e. make
- 4.3 new baseline). Then drop support for "!!" modifier for ExecStart= which
- is only supported for such old kernels
-
* sd-event: compat wd reuse in inotify code: keep a set of removed watch
descriptors, and clear this set piecemeal when we see the IN_IGNORED event
for it, or when read() returns EAGAIN or on IN_Q_OVERFLOW. Then, whenever we
case the same wd is reused multiple times before we start processing
IN_IGNORED again)
-* sd-stub: set efi var indicating stub features, i.e. whether they pick up
- creds, sysexts and so on. similar to existing variable of sd-boot
-
-* sd-stub: set efi vars declaring TPM PCRs we measured creds/cmdline + sysext
- into (even if we hardcode them)
-
* systemd-fstab-generator: support addition mount specifications via kernel
cmdline. Usecase: invoke a VM, and mount a host homedir into it via
virtio-fs.
- sd-stub: automatically pick up microcode from ESP (/loader/microcode/*)
and synthesize initrd from it, and measure it. Signing is not necessary, as
microcode does that on its own. Pass as first initrd to kernel.
- - sd-stub should measure the kernel/initrd/… into a separate PCR, so that we
- have one PCR we can bind the encrypted creds to that is not effected by
- anything else but what we drop in via kernel-install, i.e. by earlier EFI
- code running (i.e. like PCR 4)
* Add a new service type very similar to Type=notify, that goes one step
further and extends the protocol to cover reloads. Specifically, SIGHUP will
dep in the base OS image)
* sysext: automatically activate sysext images dropped in via new sd-stub
- sysext pickup logic.
+ sysext pickup logic. (must insist on verity + signature on those though)
* add concept for "exitrd" as inverse of "initrd", that we can transition to at
shutdown, and has similar security semantics. This should then take the place
what must be read-only, what requires encryption, and what requires
authentication.
-* in uefi stub: query firmware regarding which PCRs are being used, store that
- in EFI var. then use this when enrolling TPM2 in cryptsetup to verify that
- the selected PCRs actually are used by firmware.
+* in uefi stub: query firmware regarding which PCR banks are being used, store
+ that in EFI var. then use this when enrolling TPM2 in cryptsetup to verify
+ that the selected PCRs actually are used by firmware.
* rework recursive read-only remount to use new mount API
* if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it
-* Remove any support for booting without /usr pre-mounted in the initrd entirely.
- Update INITRD_INTERFACE.md accordingly.
-
* pid1: Move to tracking of main pid/control pid of units per pidfd
* pid1: support new clone3() fork-into-cgroup feature
- show whether UEFI audit mode is available
- teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation
- teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host
- - make it operate on loopback files, dissecting enough to find ESP to operate on
- bootspec: properly support boot attempt counters when parsing entry file names
* kernel-install:
6. Service credentials are placed in non-swappable memory. (If permissions
allow it, via `ramfs`.)
-7. Credentials may be acquired from a hosting VM hypervisor (qemu `fw_cfg`), a
- hosting container manager, the kernel command line, or from the UEFI
- environment and the EFI System Partition (via `systemd-stub`). Such system
- credentials may then be propagated into individual services as needed.
+7. Credentials may be acquired from a hosting VM hypervisor (SMBIOS OEM strings
+ or qemu `fw_cfg`), a hosting container manager, the kernel command line, or
+ from the UEFI environment and the EFI System Partition (via
+ `systemd-stub`). Such system credentials may then be propagated into
+ individual services as needed.
8. Credentials are an effective way to pass parameters into services that run
with `RootImage=` or `RootDirectory=` and thus cannot read these resources
the [Container Interface](CONTAINER_INTERFACE.md)
documentation.
-2. Quite similar, qemu VMs can be invoked with `-fw_cfg
+2. Quite similar, VMs can be passed credentials via SMBIOS OEM strings (example
+ qemu command line switch `-smbios
+ type=11,value=io.systemd.credential:foo=bar` or `-smbios
+ type=11,value=io.systemd.credential.binary:foo=YmFyCg==`, the latter taking
+ a Base64 encoded argument to permit binary credentials being passed
+ in). Alternatively, qemu VMs can be invoked with `-fw_cfg
name=opt/io.systemd.credentials/foo,string=bar` to pass credentials from
- host through the hypervisor into the VM. (This specific switch would set
- credential `foo` to `bar`.)
+ host through the hypervisor into the VM via qemu's `fw_cfg` mechanism. (All
+ three of these specific switches would set credential `foo` to `bar`.)
+ Passing credentials via the SMBIOS mechanism is typically preferable over
+ `fw_cfg` since it is faster and less specific to the chosen VMM
+ implementation. Moreover, `fw_cfg` has a 55 character limitation
+ on names passed that way. So some settings may not fit.
3. Credentials can also be passed into a system via the kernel command line,
via the `systemd.set-credential=` kernel command line option. Note though
-drive if=none,id=hd,file=test.raw,format=raw \
-device virtio-scsi-pci,id=scsi \
-device scsi-hd,drive=hd,bootindex=1 \
- -fw_cfg name=opt/io.systemd.credentials/mycred,string=supersecret
+ -smbios type=11,value=io.systemd.credential:mycred=supersecret
```
Either of these lines will boot a disk image `test.raw`, once as container via
-drive if=none,id=hd,file=test.raw,format=raw \
-device virtio-scsi-pci,id=scsi \
-device scsi-hd,drive=hd,bootindex=1 \
- -fw_cfg name=opt/io.systemd.credentials/passwd.hashed-password.root,string=$(mkpasswd mysecret) \
- -fw_cfg name=opt/io.systemd.credentials/firstboot.locale,string=C.UTF-8
+ -smbios type=11,value=io.systemd.credential:passwd.hashed-password.root=$(mkpasswd mysecret) \
+ -smbios type=11,value=io.systemd.credential:firstboot.locale=C.UTF-8
```
## Relevant Paths
`/usr/lib/credstore/`. `LoadCredentialEncrypted=` will also search
`/etc/credstore.encrypted/` and similar directories. These directories are
hence a great place to store credentials to load on the system.
+
+## Conditionalizing Services
+
+Sometimes it makes sense to conditionalize system services and invoke them only
+if the right system credential is passed to the system. use the
+`ConditionCredential=` and `AssertCredential=` unit file settings for that.
in the local working directory. To make use of this, please install the
`mkosi` package (if not packaged for your distro, it can be downloaded from
the [GitHub repository](https://github.com/systemd/mkosi). `mkosi` will build an
-image for the host distro by default. It is sufficient to type `mkosi` in the
-systemd project directory to generate a disk image `image.raw` you can boot either
-in `systemd-nspawn` or in an UEFI-capable VM:
+image for the host distro by default. mkosi-13 or newer version is required.
+It is sufficient to type `mkosi` in the systemd project directory to generate
+a disk image `image.raw` you can boot either in `systemd-nspawn` or
+in an UEFI-capable VM:
```sh
$ mkosi boot
mkosi.default.d/ (e.g 20-local.conf) and add the following contents:
```
-[Packages]
+[Content]
Cache=<full-path-to-package-manager-cache> # (e.g. /var/cache/dnf)
```
$ meson build # configure the build
$ ninja -C build # build it locally, see if everything compiles fine
$ meson test -C build # run some simple regression tests
-$ sudo mkosi # build a test image
+$ sudo mkosi # mkosi-13 or newer required to build a test image
$ sudo mkosi boot # boot up the test image
$ git add -p # interactively put together your patch
$ git commit # commit it
documentation consistency checks). Those are not useful when compiling for
distribution and can be disabled by setting `-Dmode=release`.
+## Sanitizers in mkosi
+
+See [Testing systemd using sanitizers](TESTING_WITH_SANITIZERS.md) for more information
+on how to build with sanitizers enabled in mkosi.
+
## Fuzzers
systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically
add the following contents:
```
-[Packages]
+[Content]
IncludeDirectory=mkosi.includedir
```
host in the mkosi-clangd.sh script.
We also need to make sure clangd is installed in the build image. To have mkosi install clangd in the build
-image, edit the 20-local.conf file we created earlier and add the following contents under the `[Packages]`
+image, edit the 20-local.conf file we created earlier and add the following contents under the `[Content]`
section:
```
4. Update hwdb (`ninja -C build update-hwdb`, `ninja -C build update-hwdb-autosuspend`, commit separately).
5. Update syscall numbers (`ninja -C build update-syscall-tables update-syscall-header`).
6. [RC1] Update version and library numbers in `meson.build`
-7. Check dbus docs with `ninja -C build update-dbus-docs`
-8. Tag the release: `version=vXXX-rcY && git tag -s "${version}" -m "systemd ${version}"`
-9. Do `ninja -C build`
-10. Make sure that the version string and package string match: `build/systemctl --version`
-11. Upload the documentation: `ninja -C build doc-sync`
-12. [FINAL] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones)
-13. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate.
-14. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
-15. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released`)
-16. [FINAL] Push commits to stable, create an empty -stable branch: `git push systemd-stable --atomic origin/main:main origin/main:refs/heads/${version}-stable`, and change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches).
+7. [RC1] Rename `.github/pull_request_template.md.disabled` to `.github/pull_request_template.md` to display the warning about soft-freeze for new features
+8. [FINAL] Rename `.github/pull_request_template.md` to `.github/pull_request_template.md.disabled` to hide the warning about soft-freeze for new features
+9. Check dbus docs with `ninja -C build update-dbus-docs`
+10. Tag the release: `version=vXXX-rcY && git tag -s "${version}" -m "systemd ${version}"`
+11. Do `ninja -C build`
+12. Make sure that the version string and package string match: `build/systemctl --version`
+13. Upload the documentation: `ninja -C build doc-sync`
+14. [FINAL] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones)
+15. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate.
+16. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
+17. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released`)
+18. [FINAL] Push commits to stable, create an empty -stable branch: `git push systemd-stable --atomic origin/main:main origin/main:refs/heads/${version}-stable`, and change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches).
want to do it locally as well. The process slightly varies depending on the
compiler you want to use and which part of the test suite you want to run.
+## mkosi
+
+To build with sanitizers in mkosi, create a file 20-local.conf in mkosi.default.d/ and add the following
+contents:
+
+```
+[Content]
+Environment=SANITIZERS=address,undefined
+```
+
+The value of `SANITIZERS` is passed directly to meson's `b_sanitize` option, See
+https://mesonbuild.com/Builtin-options.html#base-options for the format expected by the option. Currently,
+only the sanitizers supported by gcc can be used, which are `address` and `undefined`.
+
+Note that this will only work with a recent version of mkosi (>= 14 or by running mkosi directly from source).
+
## gcc
gcc compiles in sanitizer libraries dynamically by default, so you need to get
the shared libraries first - on Fedora these are shipped as a separate packages
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspire*7750G:*
KEYBOARD_KEY_e0=!pageup
+# Acer Aspire 3 A317-33
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAspireA317-33:*
+ KEYBOARD_KEY_55=power
+
# Acer Aspire One AO532h
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAO532h:*
KEYBOARD_KEY_84=bluetooth
sensor:modalias:acpi:INVN6500*:dmi:*svnDell*:pnVenue10Pro5055:*
ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1
+#########################################
+# DERE
+#########################################
+
+# DBook D10
+sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInternational,LLC.:bvrJP2V1.05:bd04/27/2022:br1.5:efr1.3:svnDefaultstring:*
+ ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+
#########################################
# DEXP
#########################################
#########################################
# Google Chromebooks
#########################################
-sensor:modalias:platform:cros-ec-accel:dmi:*:svnGOOGLE:*
- ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
-
-# caroline board (Samsung Chromebook Pro) reports itself as svnGoogle
-sensor:modalias:platform:cros-ec-accel:dmi:*:svnGoogle:pnCaroline*:*
- ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
-# Dell Inspiron Chromebook 14 2-in-1
-sensor:modalias:platform:cros-ec-accel:dmi:*svnGoogle:pnVayne*:*
+# CrOS EC & kernel drivers internally correct for per-board sensor orientations,
+# but they return values in the inverse direction (Android & W3C specs vs HID).
+sensor:modalias:platform:cros-ec-accel:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
-# nocturne board (Google Pixel Slate)
-sensor:modalias:platform:cros-ec-accel:dmi:*Google_Nocturne*:*
- ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
-
-# rammus board (Asus Chromebook Flip C433)
-sensor:modalias:platform:cros-ec-accel:dmi:*svnGoogle:pnRammus*:*
- ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
-
-# Lenovo ThinkPad C13 Yoga
-sensor:modalias:platform:cros-ec-accel:dmi:*svnGoogle:pnMorphius*:*
+sensor:modalias:platform:cros-ec-accel-legacy:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
#########################################
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.12:bd07/17/2019:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnDefaultstring:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
+# One-Netbook OneXPlayer Mini (and maybe others)
+sensor:modalias:acpi:BMI0160*:dmi:*:rnONEXPLAYER:rvrV01:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, -1
+
#########################################
# Peaq
#########################################
# This file is part of systemd.
#
# Database for signal analyzers (protocol analyzers, logic analyzers,
-# oscilloscopes, multimeters, bench power supplies, etc.) that should
-# be accessible to the seat owner.
+# oscilloscopes, multimeters, bench power supplies, etc.) or just
+# anything that has to do with electronics and that should be
+# accessible to the seat owner.
#
# Permitted keys:
# Specify if a device is a signal analyzer
# ID_SIGNAL_ANALYZER=1|0
+###########################################################
+# Greaseweazle
+###########################################################
+# Greaseweazle
+usb:v1209p4D69*
+ ID_SIGNAL_ANALYZER=1
+
###########################################################
# Total Phase
###########################################################
<term><option>keyfile-timeout=</option></term>
<listitem><para> Specifies the timeout for the device on
- which the key file resides and falls back to a password if
- it could not be mounted. See
+ which the key file resides or the device used as the key file,
+ and falls back to a password if it could not be accessed. See
<citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for key files on external devices.
</para></listitem>
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
- <refentry id="journalctl"
- xmlns:xi="http://www.w3.org/2001/XInclude">
-
- <refentryinfo>
- <title>journalctl</title>
- <productname>systemd</productname>
- </refentryinfo>
-
- <refmeta>
- <refentrytitle>journalctl</refentrytitle>
- <manvolnum>1</manvolnum>
- </refmeta>
-
- <refnamediv>
- <refname>journalctl</refname>
- <refpurpose>Query the systemd journal</refpurpose>
- </refnamediv>
-
- <refsynopsisdiv>
- <cmdsynopsis>
- <command>journalctl</command>
- <arg choice="opt" rep="repeat">OPTIONS</arg>
- <arg choice="opt" rep="repeat">MATCHES</arg>
- </cmdsynopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para><command>journalctl</command> may be used to query the
- contents of the
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- journal as written by
- <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
-
- <para>If called without parameters, it will show the full
- contents of the journal, starting with the oldest entry
- collected.</para>
-
- <para>If one or more match arguments are passed, the output is
- filtered accordingly. A match is in the format
- <literal>FIELD=VALUE</literal>,
- e.g. <literal>_SYSTEMD_UNIT=httpd.service</literal>, referring
- to the components of a structured journal entry. See
- <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- for a list of well-known fields. If multiple matches are
- specified matching different fields, the log entries are
- filtered by both, i.e. the resulting output will show only
- entries matching all the specified matches of this kind. If two
- matches apply to the same field, then they are automatically
- matched as alternatives, i.e. the resulting output will show
- entries matching any of the specified matches for the same
- field. Finally, the character <literal>+</literal> may appear
- as a separate word between other terms on the command line. This
- causes all matches before and after to be combined in a
- disjunction (i.e. logical OR).</para>
-
- <para>It is also possible to filter the entries by specifying an
- absolute file path as an argument. The file path may be a file or
- a symbolic link and the file must exist at the time of the query. If a
- file path refers to an executable binary, an <literal>_EXE=</literal>
- match for the canonicalized binary path is added to the query. If a
- file path refers to an executable script, a <literal>_COMM=</literal>
- match for the script name is added to the query. If a file path
- refers to a device node, <literal>_KERNEL_DEVICE=</literal> matches for
- the kernel name of the device and for each of its ancestor devices is
- added to the query. Symbolic links are dereferenced, kernel names are
- synthesized, and parent devices are identified from the environment at
- the time of the query. In general, a device node is the best proxy for
- an actual device, as log entries do not usually contain fields that
- identify an actual device. For the resulting log entries to be correct
- for the actual device, the relevant parts of the environment at the time
- the entry was logged, in particular the actual device corresponding to
- the device node, must have been the same as those at the time of the
- query. Because device nodes generally change their corresponding devices
- across reboots, specifying a device node path causes the resulting
- entries to be restricted to those from the current boot.</para>
-
- <para>Additional constraints may be added using options
- <option>--boot</option>, <option>--unit=</option>, etc., to
- further limit what entries will be shown (logical AND).</para>
-
- <para>Output is interleaved from all accessible journal files, whether they are rotated or currently
- being written, and regardless of whether they belong to the system itself or are accessible user
- journals. The <option>--header</option> option can be used to identify which files
- <emphasis>are</emphasis> being shown.</para>
-
- <para>The set of journal files which will be used can be
- modified using the <option>--user</option>,
- <option>--system</option>, <option>--directory</option>, and
- <option>--file</option> options, see below.</para>
-
- <para>All users are granted access to their private per-user
- journals. However, by default, only root and users who are
- members of a few special groups are granted access to the system
- journal and the journals of other users. Members of the groups
- <literal>systemd-journal</literal>, <literal>adm</literal>, and
- <literal>wheel</literal> can read all journal files. Note
- that the two latter groups traditionally have additional
- privileges specified by the distribution. Members of the
- <literal>wheel</literal> group can often perform administrative
- tasks.</para>
-
- <para>The output is paged through <command>less</command> by
- default, and long lines are "truncated" to screen width. The
- hidden part can be viewed by using the left-arrow and
- right-arrow keys. Paging can be disabled; see the
- <option>--no-pager</option> option and the "Environment" section
- below.</para>
-
- <para>When outputting to a tty, lines are colored according to
- priority: lines of level ERROR and higher are colored red; lines
- of level NOTICE and higher are highlighted; lines of level DEBUG
- are colored lighter grey; other lines are displayed normally.</para>
- </refsect1>
-
- <refsect1>
- <title>Options</title>
-
- <para>The following options are understood:</para>
-
- <variablelist>
- <varlistentry>
- <term><option>--no-full</option></term>
- <term><option>--full</option></term>
- <term><option>-l</option></term>
-
- <listitem><para>Ellipsize fields when they do not fit in
- available columns. The default is to show full fields,
- allowing them to wrap or be truncated by the pager, if one
- is used.</para>
-
- <para>The old options
- <option>-l</option>/<option>--full</option> are not useful
- anymore, except to undo <option>--no-full</option>.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-a</option></term>
- <term><option>--all</option></term>
-
- <listitem><para>Show all fields in full, even if they include unprintable characters or are very long. By
- default, fields with unprintable characters are abbreviated as "blob data". (Note that the pager may escape
- unprintable characters again.)</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-f</option></term>
- <term><option>--follow</option></term>
-
- <listitem><para>Show only the most recent journal entries,
- and continuously print new entries as they are appended to
- the journal.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-e</option></term>
- <term><option>--pager-end</option></term>
-
- <listitem><para>Immediately jump to the end of the journal
- inside the implied pager tool. This implies
- <option>-n1000</option> to guarantee that the pager will not
- buffer logs of unbounded size. This may be overridden with
- an explicit <option>-n</option> with some other numeric
- value, while <option>-nall</option> will disable this cap.
- Note that this option is only supported for the
- <citerefentry project='man-pages'><refentrytitle>less</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- pager.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-n</option></term>
- <term><option>--lines=</option></term>
-
- <listitem><para>Show the most recent journal events and
- limit the number of events shown. If
- <option>--follow</option> is used, this option is
- implied. The argument is a positive integer or
- <literal>all</literal> to disable line limiting. The default
- value is 10 if no argument is given.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>--no-tail</option></term>
-
- <listitem><para>Show all stored output lines, even in follow
- mode. Undoes the effect of <option>--lines=</option>.
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-r</option></term>
- <term><option>--reverse</option></term>
-
- <listitem><para>Reverse output so that the newest entries
- are displayed first.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-o</option></term>
- <term><option>--output=</option></term>
-
- <listitem><para>Controls the formatting of the journal
- entries that are shown. Takes one of the following
- options:</para>
- <variablelist>
- <varlistentry>
- <term>
- <option>short</option>
- </term>
- <listitem>
- <para>is the default and generates an output that is
- mostly identical to the formatting of classic syslog
- files, showing one line per journal entry.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>short-full</option>
- </term>
- <listitem>
- <para>is very similar, but shows timestamps in the format the <option>--since=</option> and
- <option>--until=</option> options accept. Unlike the timestamp information shown in
- <option>short</option> output mode this mode includes weekday, year and timezone information in the
- output, and is locale-independent.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>short-iso</option>
- </term>
- <listitem>
- <para>is very similar, but shows ISO 8601 wallclock
- timestamps.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>short-iso-precise</option>
- </term>
- <listitem>
- <para>as for <option>short-iso</option> but includes full
- microsecond precision.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>short-precise</option>
- </term>
- <listitem>
- <para>is very similar, but shows classic syslog timestamps
- with full microsecond precision.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>short-monotonic</option>
- </term>
- <listitem>
- <para>is very similar, but shows monotonic timestamps
- instead of wallclock timestamps.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>short-unix</option>
- </term>
- <listitem>
- <para>is very similar, but shows seconds passed since January 1st 1970 UTC instead of wallclock
- timestamps ("UNIX time"). The time is shown with microsecond accuracy.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>verbose</option>
- </term>
- <listitem>
- <para>shows the full-structured entry items with all
- fields.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>export</option>
- </term>
- <listitem>
- <para>serializes the journal into a binary (but mostly
- text-based) stream suitable for backups and network
- transfer (see
- <ulink url="https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-export-format">Journal Export Format</ulink>
- for more information). To import the binary stream back
- into native journald format use
- <citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>json</option>
- </term>
- <listitem>
- <para>formats entries as JSON objects, separated by newline characters (see <ulink
- url="https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-json-format">Journal JSON Format</ulink> for more
- information). Field values are generally encoded as JSON strings, with three exceptions:
- <orderedlist>
- <listitem><para>Fields larger than 4096 bytes are encoded as <constant>null</constant> values. (This
- may be turned off by passing <option>--all</option>, but be aware that this may allocate overly long
- JSON objects.) </para></listitem>
-
- <listitem><para>Journal entries permit non-unique fields within the same log entry. JSON does not allow
- non-unique fields within objects. Due to this, if a non-unique field is encountered a JSON array is
- used as field value, listing all field values as elements.</para></listitem>
-
- <listitem><para>Fields containing non-printable or non-UTF8 bytes are encoded as arrays containing
- the raw bytes individually formatted as unsigned numbers.</para></listitem>
- </orderedlist>
-
- Note that this encoding is reversible (with the exception of the size limit).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>json-pretty</option>
- </term>
- <listitem>
- <para>formats entries as JSON data structures, but
- formats them in multiple lines in order to make them
- more readable by humans.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>json-sse</option>
- </term>
- <listitem>
- <para>formats entries as JSON data structures, but wraps
- them in a format suitable for
- <ulink url="https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events">Server-Sent Events</ulink>.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>json-seq</option>
- </term>
- <listitem>
- <para>formats entries as JSON data structures, but prefixes them with an ASCII Record Separator
- character (0x1E) and suffixes them with an ASCII Line Feed character (0x0A), in accordance with <ulink
- url="https://tools.ietf.org/html/rfc7464">JavaScript Object Notation (JSON) Text Sequences </ulink>
- (<literal>application/json-seq</literal>).
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>cat</option>
- </term>
- <listitem>
- <para>generates a very terse output, only showing the actual message of each journal entry
- with no metadata, not even a timestamp. If combined with the
- <option>--output-fields=</option> option will output the listed fields for each log record,
- instead of the message.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>
- <option>with-unit</option>
- </term>
- <listitem>
- <para>similar to short-full, but prefixes the unit and
- user unit names instead of the traditional syslog
- identifier. Useful when using templated instances, as it
- will include the arguments in the unit names.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </listitem>
+<refentry id="journalctl"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>journalctl</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>journalctl</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>journalctl</refname>
+ <refpurpose>Query the systemd journal</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>journalctl</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="opt" rep="repeat">MATCHES</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>journalctl</command> may be used to query the contents of the
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> journal as
+ written by
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+ <para>If called without parameters, it will show the full contents of the journal, starting with the
+ oldest entry collected.</para>
+
+ <para>If one or more match arguments are passed, the output is filtered accordingly. A match is in the
+ format <literal>FIELD=VALUE</literal>, e.g. <literal>_SYSTEMD_UNIT=httpd.service</literal>, referring to
+ the components of a structured journal entry. See
+ <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for a list of well-known fields. If multiple matches are specified matching different fields, the log
+ entries are filtered by both, i.e. the resulting output will show only entries matching all the specified
+ matches of this kind. If two matches apply to the same field, then they are automatically matched as
+ alternatives, i.e. the resulting output will show entries matching any of the specified matches for the
+ same field. Finally, the character <literal>+</literal> may appear as a separate word between other terms
+ on the command line. This causes all matches before and after to be combined in a disjunction
+ (i.e. logical OR).</para>
+
+ <para>It is also possible to filter the entries by specifying an absolute file path as an argument. The
+ file path may be a file or a symbolic link and the file must exist at the time of the query. If a file
+ path refers to an executable binary, an <literal>_EXE=</literal> match for the canonicalized binary path
+ is added to the query. If a file path refers to an executable script, a <literal>_COMM=</literal> match
+ for the script name is added to the query. If a file path refers to a device node,
+ <literal>_KERNEL_DEVICE=</literal> matches for the kernel name of the device and for each of its ancestor
+ devices is added to the query. Symbolic links are dereferenced, kernel names are synthesized, and parent
+ devices are identified from the environment at the time of the query. In general, a device node is the
+ best proxy for an actual device, as log entries do not usually contain fields that identify an actual
+ device. For the resulting log entries to be correct for the actual device, the relevant parts of the
+ environment at the time the entry was logged, in particular the actual device corresponding to the device
+ node, must have been the same as those at the time of the query. Because device nodes generally change
+ their corresponding devices across reboots, specifying a device node path causes the resulting entries to
+ be restricted to those from the current boot.</para>
+
+ <para>Additional constraints may be added using options <option>--boot</option>,
+ <option>--unit=</option>, etc., to further limit what entries will be shown (logical AND).</para>
+
+ <para>Output is interleaved from all accessible journal files, whether they are rotated or currently
+ being written, and regardless of whether they belong to the system itself or are accessible user
+ journals. The <option>--header</option> option can be used to identify which files
+ <emphasis>are</emphasis> being shown.</para>
+
+ <para>The set of journal files which will be used can be modified using the <option>--user</option>,
+ <option>--system</option>, <option>--directory</option>, and <option>--file</option> options, see
+ below.</para>
+
+ <para>All users are granted access to their private per-user journals. However, by default, only root and
+ users who are members of a few special groups are granted access to the system journal and the journals
+ of other users. Members of the groups <literal>systemd-journal</literal>, <literal>adm</literal>, and
+ <literal>wheel</literal> can read all journal files. Note that the two latter groups traditionally have
+ additional privileges specified by the distribution. Members of the <literal>wheel</literal> group can
+ often perform administrative tasks.</para>
+
+ <para>The output is paged through <command>less</command> by default, and long lines are "truncated" to
+ screen width. The hidden part can be viewed by using the left-arrow and right-arrow keys. Paging can be
+ disabled; see the <option>--no-pager</option> option and the "Environment" section below.</para>
+
+ <para>When outputting to a tty, lines are colored according to priority: lines of level ERROR and higher
+ are colored red; lines of level NOTICE and higher are highlighted; lines of level DEBUG are colored
+ lighter grey; other lines are displayed normally.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Source Options</title>
+
+ <para>The following options control where to read journal records from:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>--system</option></term>
+ <term><option>--user</option></term>
+
+ <listitem><para>Show messages from system services and the kernel (with
+ <option>--system</option>). Show messages from service of current user (with
+ <option>--user</option>). If neither is specified, show all messages that the user can see.
+ </para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--output-fields=</option></term>
+ <term><option>-M</option></term>
+ <term><option>--machine=</option></term>
- <listitem><para>A comma separated list of the fields which should be included in the output. This has
- an effect only for the output modes which would normally show all fields (<option>verbose</option>,
- <option>export</option>, <option>json</option>, <option>json-pretty</option>,
- <option>json-sse</option> and <option>json-seq</option>), as well as on <option>cat</option>. For the
- former, the <literal>__CURSOR</literal>, <literal>__REALTIME_TIMESTAMP</literal>,
- <literal>__MONOTONIC_TIMESTAMP</literal>, and <literal>_BOOT_ID</literal> fields are always
- printed.</para></listitem>
+ <listitem><para>Show messages from a running, local container. Specify a container name to connect
+ to.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--utc</option></term>
+ <term><option>-m</option></term>
+ <term><option>--merge</option></term>
- <listitem><para>Express time in Coordinated Universal Time
- (UTC).</para></listitem>
+ <listitem><para>Show entries interleaved from all available journals, including remote
+ ones.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--no-hostname</option></term>
+ <term><option>-D <replaceable>DIR</replaceable></option></term>
+ <term><option>--directory=<replaceable>DIR</replaceable></option></term>
- <listitem><para>Don't show the hostname field of log messages originating from the local host. This
- switch has an effect only on the <option>short</option> family of output modes (see above).
- </para>
+ <listitem><para>Takes a directory path as argument. If specified, journalctl will operate on the
+ specified journal directory <replaceable>DIR</replaceable> instead of the default runtime and system
+ journal paths.</para></listitem>
+ </varlistentry>
- <para>Note: this option does not remove occurrences of the hostname from log entries themselves, so
- it does not prevent the hostname from being visible in the logs.</para>
- </listitem>
+ <varlistentry>
+ <term><option>--file=<replaceable>GLOB</replaceable></option></term>
+
+ <listitem><para>Takes a file glob as an argument. If specified, journalctl will operate on the
+ specified journal files matching <replaceable>GLOB</replaceable> instead of the default runtime and
+ system journal paths. May be specified multiple times, in which case files will be suitably
+ interleaved.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-x</option></term>
- <term><option>--catalog</option></term>
+ <term><option>--root=<replaceable>ROOT</replaceable></option></term>
- <listitem><para>Augment log lines with explanation texts from
- the message catalog. This will add explanatory help texts to
- log messages in the output where this is available. These
- short help texts will explain the context of an error or log
- event, possible solutions, as well as pointers to support
- forums, developer documentation, and any other relevant
- manuals. Note that help texts are not available for all
- messages, but only for selected ones. For more information on
- the message catalog, please refer to the
- <ulink url="https://www.freedesktop.org/wiki/Software/systemd/catalog">Message Catalog Developer Documentation</ulink>.</para>
-
- <para>Note: when attaching <command>journalctl</command>
- output to bug reports, please do <emphasis>not</emphasis> use
- <option>-x</option>.</para>
- </listitem>
+ <listitem><para>Takes a directory path as an argument. If specified, <command>journalctl</command>
+ will operate on journal directories and catalog file hierarchy underneath the specified directory
+ instead of the root directory (e.g. <option>--update-catalog</option> will create
+ <filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>, and journal
+ files under <filename><replaceable>ROOT</replaceable>/run/journal/</filename> or
+ <filename><replaceable>ROOT</replaceable>/var/log/journal/</filename> will be displayed).
+ </para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-q</option></term>
- <term><option>--quiet</option></term>
+ <term><option>--image=<replaceable>IMAGE</replaceable></option></term>
- <listitem><para>Suppresses all informational messages
- (i.e. "-- Journal begins at …", "-- Reboot --"),
- any warning messages regarding
- inaccessible system journals when run as a normal
- user.</para></listitem>
+ <listitem><para>Takes a path to a disk image file or block device node. If specified,
+ <command>journalctl</command> will operate on the file system in the indicated disk image. This is
+ similar to <option>--root=</option> but operates on file systems stored in disk images or block
+ devices, thus providing an easy way to extract log data from disk images. The disk image should
+ either contain just a file system or a set of file systems within a GPT partition table, following
+ the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
+ Specification</ulink>. For further information on supported disk images, see
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ switch of the same name.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-m</option></term>
- <term><option>--merge</option></term>
+ <term><option>--namespace=<replaceable>NAMESPACE</replaceable></option></term>
- <listitem><para>Show entries interleaved from all available
- journals, including remote ones.</para></listitem>
+ <listitem><para>Takes a journal namespace identifier string as argument. If not specified the data
+ collected by the default namespace is shown. If specified shows the log data of the specified
+ namespace instead. If the namespace is specified as <literal>*</literal> data from all namespaces is
+ shown, interleaved. If the namespace identifier is prefixed with <literal>+</literal> data from the
+ specified namespace and the default namespace is shown, interleaved, but no other. For details about
+ journal namespaces see
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1>
+ <title>Filtering Options</title>
+
+ <para>The following options control how to filter journal records:</para>
+
+ <variablelist>
<varlistentry>
- <term><option>-b <optional><optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional>|<constant>all</constant></optional></option></term>
- <term><option>--boot<optional>=<optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional>|<constant>all</constant></optional></option></term>
+ <term><option>-S</option></term>
+ <term><option>--since=</option></term>
+ <term><option>-U</option></term>
+ <term><option>--until=</option></term>
- <listitem><para>Show messages from a specific boot. This will
- add a match for <literal>_BOOT_ID=</literal>.</para>
-
- <para>The argument may be empty, in which case logs for the
- current boot will be shown.</para>
-
- <para>If the boot ID is omitted, a positive
- <replaceable>offset</replaceable> will look up the boots
- starting from the beginning of the journal, and an
- equal-or-less-than zero <replaceable>offset</replaceable> will
- look up boots starting from the end of the journal. Thus,
- <constant>1</constant> means the first boot found in the
- journal in chronological order, <constant>2</constant> the
- second and so on; while <constant>-0</constant> is the last
- boot, <constant>-1</constant> the boot before last, and so
- on. An empty <replaceable>offset</replaceable> is equivalent
- to specifying <constant>-0</constant>, except when the current
- boot is not the last boot (e.g. because
- <option>--directory</option> was specified to look at logs
- from a different machine).</para>
-
- <para>If the 32-character <replaceable>ID</replaceable> is
- specified, it may optionally be followed by
- <replaceable>offset</replaceable> which identifies the boot
- relative to the one given by boot
- <replaceable>ID</replaceable>. Negative values mean earlier
- boots and positive values mean later boots. If
- <replaceable>offset</replaceable> is not specified, a value of
- zero is assumed, and the logs for the boot given by
- <replaceable>ID</replaceable> are shown.</para>
-
- <para>The special argument <constant>all</constant> can be
- used to negate the effect of an earlier use of
- <option>-b</option>.</para>
- </listitem>
+ <listitem><para>Start showing entries on or newer than the specified date, or on or older than the
+ specified date, respectively. Date specifications should be of the format <literal>2012-10-30
+ 18:17:16</literal>. If the time part is omitted, <literal>00:00:00</literal> is assumed. If only
+ the seconds component is omitted, <literal>:00</literal> is assumed. If the date component is
+ omitted, the current day is assumed. Alternatively the strings <literal>yesterday</literal>,
+ <literal>today</literal>, <literal>tomorrow</literal> are understood, which refer to 00:00:00 of the
+ day before the current day, the current day, or the day after the current day,
+ respectively. <literal>now</literal> refers to the current time. Finally, relative times may be
+ specified, prefixed with <literal>-</literal> or <literal>+</literal>, referring to times before or
+ after the current time, respectively. For complete time and date specification, see
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>. Note
+ that <option>--output=short-full</option> prints timestamps that follow precisely this format.
+ </para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--list-boots</option></term>
+ <term><option>-c</option></term>
+ <term><option>--cursor=</option></term>
- <listitem><para>Show a tabular list of boot numbers (relative to
- the current boot), their IDs, and the timestamps of the first
- and last message pertaining to the boot.</para></listitem>
+ <listitem><para>Start showing entries from the location in the journal specified by the passed
+ cursor.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-k</option></term>
- <term><option>--dmesg</option></term>
+ <term><option>--after-cursor=</option></term>
- <listitem><para>Show only kernel messages. This implies
- <option>-b</option> and adds the match
- <literal>_TRANSPORT=kernel</literal>.</para></listitem>
+ <listitem><para>Start showing entries from the location in the journal <emphasis>after</emphasis>
+ the location specified by the passed cursor. The cursor is shown when the
+ <option>--show-cursor</option> option is used.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-t</option></term>
- <term><option>--identifier=<replaceable>SYSLOG_IDENTIFIER</replaceable></option></term>
+ <term><option>--cursor-file=<replaceable>FILE</replaceable></option></term>
- <listitem><para>Show messages for the specified syslog
- identifier
- <replaceable>SYSLOG_IDENTIFIER</replaceable>.</para>
+ <listitem><para>If <replaceable>FILE</replaceable> exists and contains a cursor, start showing
+ entries <emphasis>after</emphasis> this location. Otherwise show entries according to the other
+ given options. At the end, write the cursor of the last entry to
+ <replaceable>FILE</replaceable>. Use this option to continually read the journal by sequentially
+ calling <command>journalctl</command>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-b <optional><optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional>|<constant>all</constant></optional></option></term>
+ <term><option>--boot<optional>=<optional><replaceable>ID</replaceable></optional><optional><replaceable>±offset</replaceable></optional>|<constant>all</constant></optional></option></term>
+
+ <listitem><para>Show messages from a specific boot. This will add a match for
+ <literal>_BOOT_ID=</literal>.</para>
+
+ <para>The argument may be empty, in which case logs for the current boot will be shown.</para>
+
+ <para>If the boot ID is omitted, a positive <replaceable>offset</replaceable> will look up the boots
+ starting from the beginning of the journal, and an equal-or-less-than zero
+ <replaceable>offset</replaceable> will look up boots starting from the end of the journal. Thus,
+ <constant>1</constant> means the first boot found in the journal in chronological order,
+ <constant>2</constant> the second and so on; while <constant>-0</constant> is the last boot,
+ <constant>-1</constant> the boot before last, and so on. An empty <replaceable>offset</replaceable>
+ is equivalent to specifying <constant>-0</constant>, except when the current boot is not the last
+ boot (e.g. because <option>--directory</option> was specified to look at logs from a different
+ machine).</para>
+
+ <para>If the 32-character <replaceable>ID</replaceable> is specified, it may optionally be followed
+ by <replaceable>offset</replaceable> which identifies the boot relative to the one given by boot
+ <replaceable>ID</replaceable>. Negative values mean earlier boots and positive values mean later
+ boots. If <replaceable>offset</replaceable> is not specified, a value of zero is assumed, and the
+ logs for the boot given by <replaceable>ID</replaceable> are shown.</para>
- <para>This parameter can be specified multiple
- times.</para></listitem>
+ <para>The special argument <constant>all</constant> can be used to negate the effect of an earlier
+ use of <option>-b</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-u</option></term>
<term><option>--unit=<replaceable>UNIT</replaceable>|<replaceable>PATTERN</replaceable></option></term>
- <listitem><para>Show messages for the specified systemd unit
- <replaceable>UNIT</replaceable> (such as a service unit), or
- for any of the units matched by
- <replaceable>PATTERN</replaceable>. If a pattern is
- specified, a list of unit names found in the journal is
- compared with the specified pattern and all that match are
- used. For each unit name, a match is added for messages from
- the unit
- (<literal>_SYSTEMD_UNIT=<replaceable>UNIT</replaceable></literal>),
- along with additional matches for messages from systemd and
- messages about coredumps for the specified unit. A match
- is also added for <literal>_SYSTEMD_SLICE=<replaceable>UNIT</replaceable></literal>,
- such that if the provided <replaceable>UNIT</replaceable> is a
+ <listitem><para>Show messages for the specified systemd unit <replaceable>UNIT</replaceable> (such as
+ a service unit), or for any of the units matched by <replaceable>PATTERN</replaceable>. If a pattern
+ is specified, a list of unit names found in the journal is compared with the specified pattern and
+ all that match are used. For each unit name, a match is added for messages from the unit
+ (<literal>_SYSTEMD_UNIT=<replaceable>UNIT</replaceable></literal>), along with additional matches for
+ messages from systemd and messages about coredumps for the specified unit. A match is also added for
+ <literal>_SYSTEMD_SLICE=<replaceable>UNIT</replaceable></literal>, such that if the provided
+ <replaceable>UNIT</replaceable> is a
<citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- unit, all logs of children of the slice will be shown.
- </para>
+ unit, all logs of children of the slice will be shown.</para>
- <para>This parameter can be specified multiple times.</para>
- </listitem>
+ <para>This parameter can be specified multiple times.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--user-unit=</option></term>
- <listitem><para>Show messages for the specified user session
- unit. This will add a match for messages from the unit
- (<literal>_SYSTEMD_USER_UNIT=</literal> and
- <literal>_UID=</literal>) and additional matches for messages
- from session systemd and messages about coredumps for the
- specified unit. A match
- is also added for <literal>_SYSTEMD_USER_SLICE=<replaceable>UNIT</replaceable></literal>,
- such that if the provided <replaceable>UNIT</replaceable> is a
+ <listitem><para>Show messages for the specified user session unit. This will add a match for messages
+ from the unit (<literal>_SYSTEMD_USER_UNIT=</literal> and <literal>_UID=</literal>) and additional
+ matches for messages from session systemd and messages about coredumps for the specified unit. A
+ match is also added for <literal>_SYSTEMD_USER_SLICE=<replaceable>UNIT</replaceable></literal>, such
+ that if the provided <replaceable>UNIT</replaceable> is a
<citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
unit, all logs of children of the unit will be shown.</para>
- <para>This parameter can be specified multiple times.</para>
- </listitem>
+ <para>This parameter can be specified multiple times.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-t</option></term>
+ <term><option>--identifier=<replaceable>SYSLOG_IDENTIFIER</replaceable></option></term>
+
+ <listitem><para>Show messages for the specified syslog identifier
+ <replaceable>SYSLOG_IDENTIFIER</replaceable>.</para>
+
+ <para>This parameter can be specified multiple times.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-p</option></term>
<term><option>--priority=</option></term>
- <listitem><para>Filter output by message priorities or
- priority ranges. Takes either a single numeric or textual log
- level (i.e. between 0/<literal>emerg</literal> and
- 7/<literal>debug</literal>), or a range of numeric/text log
- levels in the form FROM..TO. The log levels are the usual
- syslog log levels as documented in
- <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- i.e. <literal>emerg</literal> (0),
- <literal>alert</literal> (1), <literal>crit</literal> (2),
- <literal>err</literal> (3), <literal>warning</literal> (4),
- <literal>notice</literal> (5), <literal>info</literal> (6),
- <literal>debug</literal> (7). If a single log level is
- specified, all messages with this log level or a lower (hence
- more important) log level are shown. If a range is specified,
- all messages within the range are shown, including both the
- start and the end value of the range. This will add
- <literal>PRIORITY=</literal> matches for the specified
+ <listitem><para>Filter output by message priorities or priority ranges. Takes either a single numeric
+ or textual log level (i.e. between 0/<literal>emerg</literal> and 7/<literal>debug</literal>), or a
+ range of numeric/text log levels in the form FROM..TO. The log levels are the usual syslog log levels
+ as documented in <citerefentry
+ project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ i.e. <literal>emerg</literal> (0), <literal>alert</literal> (1), <literal>crit</literal> (2),
+ <literal>err</literal> (3), <literal>warning</literal> (4), <literal>notice</literal> (5),
+ <literal>info</literal> (6), <literal>debug</literal> (7). If a single log level is specified, all
+ messages with this log level or a lower (hence more important) log level are shown. If a range is
+ specified, all messages within the range are shown, including both the start and the end value of the
+ range. This will add <literal>PRIORITY=</literal> matches for the specified
priorities.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--facility=</option></term>
- <listitem><para>Filter output by syslog facility. Takes a comma-separated list of numbers or facility
- names. The names are the usual syslog facilities as documented in
- <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ <listitem><para>Filter output by syslog facility. Takes a comma-separated list of numbers or
+ facility names. The names are the usual syslog facilities as documented in <citerefentry
+ project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
<option>--facility=help</option> may be used to display a list of known facility names and exit.
</para></listitem>
</varlistentry>
<term><option>-g</option></term>
<term><option>--grep=</option></term>
- <listitem><para>Filter output to entries where the <varname>MESSAGE=</varname>
- field matches the specified regular expression. PERL-compatible regular expressions
- are used, see
- <citerefentry project='url'><refentrytitle url='http://pcre.org/current/doc/html/pcre2pattern.html'>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ <listitem><para>Filter output to entries where the <varname>MESSAGE=</varname> field matches the
+ specified regular expression. PERL-compatible regular expressions are used, see <citerefentry
+ project='url'><refentrytitle
+ url='http://pcre.org/current/doc/html/pcre2pattern.html'>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry>
for a detailed description of the syntax.</para>
- <para>If the pattern is all lowercase, matching is case insensitive.
- Otherwise, matching is case sensitive. This can be overridden with the
- <option>--case-sensitive</option> option, see below.</para>
- </listitem>
+ <para>If the pattern is all lowercase, matching is case insensitive. Otherwise, matching is case
+ sensitive. This can be overridden with the <option>--case-sensitive</option> option, see
+ below.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--case-sensitive<optional>=BOOLEAN</optional></option></term>
- <listitem><para>Make pattern matching case sensitive or case insensitive.</para>
- </listitem>
+ <listitem><para>Make pattern matching case sensitive or case insensitive.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-c</option></term>
- <term><option>--cursor=</option></term>
+ <term><option>-k</option></term>
+ <term><option>--dmesg</option></term>
- <listitem><para>Start showing entries from the location in the
- journal specified by the passed cursor.</para></listitem>
+ <listitem><para>Show only kernel messages. This implies <option>-b</option> and adds the match
+ <literal>_TRANSPORT=kernel</literal>.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Output Options</title>
+
+ <para>The following options control how journal records are printed:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>-o</option></term>
+ <term><option>--output=</option></term>
+
+ <listitem><para>Controls the formatting of the journal entries that are shown. Takes one of the
+ following options:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>short</option></term>
+ <listitem><para>is the default and generates an output that is mostly identical to the
+ formatting of classic syslog files, showing one line per journal entry.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>short-full</option></term>
+ <listitem><para>is very similar, but shows timestamps in the format the
+ <option>--since=</option> and <option>--until=</option> options accept. Unlike the timestamp
+ information shown in <option>short</option> output mode this mode includes weekday, year and
+ timezone information in the output, and is locale-independent.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>short-iso</option></term>
+ <listitem><para>is very similar, but shows ISO 8601 wallclock timestamps.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>short-iso-precise</option></term>
+ <listitem><para>as for <option>short-iso</option> but includes full microsecond
+ precision.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>short-precise</option></term>
+ <listitem><para>is very similar, but shows classic syslog timestamps with full microsecond
+ precision.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>short-monotonic</option></term>
+ <listitem><para>is very similar, but shows monotonic timestamps instead of wallclock
+ timestamps.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>short-unix</option></term>
+ <listitem><para>is very similar, but shows seconds passed since January 1st 1970 UTC instead of
+ wallclock timestamps ("UNIX time"). The time is shown with microsecond accuracy.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>verbose</option></term>
+ <listitem><para>shows the full-structured entry items with all fields.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>export</option></term>
+ <listitem><para>serializes the journal into a binary (but mostly text-based) stream suitable
+ for backups and network transfer (see <ulink
+ url="https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-export-format">Journal Export
+ Format</ulink> for more information). To import the binary stream back into native journald
+ format use
+ <citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>json</option></term>
+ <listitem><para>formats entries as JSON objects, separated by newline characters (see <ulink
+ url="https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-json-format">Journal JSON Format</ulink>
+ for more information). Field values are generally encoded as JSON strings, with three exceptions:
+ <orderedlist>
+ <listitem><para>Fields larger than 4096 bytes are encoded as <constant>null</constant>
+ values. (This may be turned off by passing <option>--all</option>, but be aware that this may
+ allocate overly long JSON objects.)</para></listitem>
+
+ <listitem><para>Journal entries permit non-unique fields within the same log entry. JSON does
+ not allow non-unique fields within objects. Due to this, if a non-unique field is encountered a
+ JSON array is used as field value, listing all field values as elements.</para></listitem>
+
+ <listitem><para>Fields containing non-printable or non-UTF8 bytes are encoded as arrays
+ containing the raw bytes individually formatted as unsigned numbers.</para></listitem>
+ </orderedlist>
+
+ Note that this encoding is reversible (with the exception of the size limit).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>json-pretty</option></term>
+ <listitem><para>formats entries as JSON data structures, but formats them in multiple lines in
+ order to make them more readable by humans.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>json-sse</option></term>
+ <listitem><para>formats entries as JSON data structures, but wraps them in a format suitable for
+ <ulink
+ url="https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events">Server-Sent
+ Events</ulink>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>json-seq</option></term>
+ <listitem><para>formats entries as JSON data structures, but prefixes them with an ASCII Record
+ Separator character (0x1E) and suffixes them with an ASCII Line Feed character (0x0A), in
+ accordance with <ulink url="https://tools.ietf.org/html/rfc7464">JavaScript Object Notation
+ (JSON) Text Sequences </ulink> (<literal>application/json-seq</literal>).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>cat</option></term>
+ <listitem><para>generates a very terse output, only showing the actual message of each journal
+ entry with no metadata, not even a timestamp. If combined with the
+ <option>--output-fields=</option> option will output the listed fields for each log record,
+ instead of the message.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>with-unit</option></term>
+ <listitem><para>similar to short-full, but prefixes the unit and user unit names instead of the
+ traditional syslog identifier. Useful when using templated instances, as it will include the
+ arguments in the unit names.</para></listitem>
+ </varlistentry>
+ </variablelist></listitem>
</varlistentry>
<varlistentry>
- <term><option>--cursor-file=<replaceable>FILE</replaceable></option></term>
+ <term><option>--output-fields=</option></term>
- <listitem><para>If <replaceable>FILE</replaceable> exists and contains a
- cursor, start showing entries <emphasis>after</emphasis> this location.
- Otherwise the show entries according the other given options. At the end,
- write the cursor of the last entry to <replaceable>FILE</replaceable>. Use
- this option to continually read the journal by sequentially calling
- <command>journalctl</command>.</para></listitem>
+ <listitem><para>A comma separated list of the fields which should be included in the output. This
+ has an effect only for the output modes which would normally show all fields
+ (<option>verbose</option>, <option>export</option>, <option>json</option>,
+ <option>json-pretty</option>, <option>json-sse</option> and <option>json-seq</option>), as well as
+ on <option>cat</option>. For the former, the <literal>__CURSOR</literal>,
+ <literal>__REALTIME_TIMESTAMP</literal>, <literal>__MONOTONIC_TIMESTAMP</literal>, and
+ <literal>_BOOT_ID</literal> fields are always printed.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--after-cursor=</option></term>
+ <term><option>-n</option></term>
+ <term><option>--lines=</option></term>
- <listitem><para>Start showing entries from the location in the
- journal <emphasis>after</emphasis> the location specified by
- the passed cursor. The cursor is shown when the
- <option>--show-cursor</option> option is used.</para>
- </listitem>
+ <listitem><para>Show the most recent journal events and limit the number of events shown. If
+ <option>--follow</option> is used, this option is implied. The argument is a positive integer or
+ <literal>all</literal> to disable line limiting. The default value is 10 if no argument is
+ given.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--show-cursor</option></term>
+ <term><option>-r</option></term>
+ <term><option>--reverse</option></term>
- <listitem><para>The cursor is shown after the last entry after
- two dashes:</para>
- <programlisting>-- cursor: s=0639…</programlisting>
- <para>The format of the cursor is private
- and subject to change.</para></listitem>
+ <listitem><para>Reverse output so that the newest entries are displayed first.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-S</option></term>
- <term><option>--since=</option></term>
- <term><option>-U</option></term>
- <term><option>--until=</option></term>
+ <term><option>--show-cursor</option></term>
- <listitem><para>Start showing entries on or newer than the specified date, or on or older than the specified
- date, respectively. Date specifications should be of the format <literal>2012-10-30 18:17:16</literal>. If the
- time part is omitted, <literal>00:00:00</literal> is assumed. If only the seconds component is omitted,
- <literal>:00</literal> is assumed. If the date component is omitted, the current day is assumed. Alternatively
- the strings <literal>yesterday</literal>, <literal>today</literal>, <literal>tomorrow</literal> are understood,
- which refer to 00:00:00 of the day before the current day, the current day, or the day after the current day,
- respectively. <literal>now</literal> refers to the current time. Finally, relative times may be specified,
- prefixed with <literal>-</literal> or <literal>+</literal>, referring to times before or after the current
- time, respectively. For complete time and date specification, see
- <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>. Note that
- <option>--output=short-full</option> prints timestamps that follow precisely this format.
- </para>
- </listitem>
+ <listitem><para>The cursor is shown after the last entry after two dashes:</para>
+ <programlisting>-- cursor: s=0639…</programlisting>
+ <para>The format of the cursor is private and subject to change.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-F</option></term>
- <term><option>--field=</option></term>
+ <term><option>--utc</option></term>
- <listitem><para>Print all possible data values the specified
- field can take in all entries of the journal.</para></listitem>
+ <listitem><para>Express time in Coordinated Universal Time (UTC).</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-N</option></term>
- <term><option>--fields</option></term>
+ <term><option>-x</option></term>
+ <term><option>--catalog</option></term>
- <listitem><para>Print all field names currently used in all entries of the journal.</para></listitem>
+ <listitem><para>Augment log lines with explanation texts from the message catalog. This will add
+ explanatory help texts to log messages in the output where this is available. These short help texts
+ will explain the context of an error or log event, possible solutions, as well as pointers to support
+ forums, developer documentation, and any other relevant manuals. Note that help texts are not
+ available for all messages, but only for selected ones. For more information on the message catalog,
+ please refer to the <ulink url="https://www.freedesktop.org/wiki/Software/systemd/catalog">Message
+ Catalog Developer Documentation</ulink>.</para>
+
+ <para>Note: when attaching <command>journalctl</command> output to bug reports, please do
+ <emphasis>not</emphasis> use <option>-x</option>.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--system</option></term>
- <term><option>--user</option></term>
+ <term><option>--no-hostname</option></term>
- <listitem><para>Show messages from system services and the
- kernel (with <option>--system</option>). Show messages from
- service of current user (with <option>--user</option>). If
- neither is specified, show all messages that the user can see.
- </para></listitem>
+ <listitem><para>Don't show the hostname field of log messages originating from the local host. This
+ switch has an effect only on the <option>short</option> family of output modes (see above).</para>
+
+ <para>Note: this option does not remove occurrences of the hostname from log entries themselves, so
+ it does not prevent the hostname from being visible in the logs.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-M</option></term>
- <term><option>--machine=</option></term>
+ <term><option>--no-full</option></term>
+ <term><option>--full</option></term>
+ <term><option>-l</option></term>
+
+ <listitem><para>Ellipsize fields when they do not fit in available columns. The default is to show
+ full fields, allowing them to wrap or be truncated by the pager, if one is used.</para>
- <listitem><para>Show messages from a running, local
- container. Specify a container name to connect to.</para>
- </listitem>
+ <para>The old options <option>-l</option>/<option>--full</option> are not useful anymore, except to
+ undo <option>--no-full</option>.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>-D <replaceable>DIR</replaceable></option></term>
- <term><option>--directory=<replaceable>DIR</replaceable></option></term>
+ <term><option>-a</option></term>
+ <term><option>--all</option></term>
- <listitem><para>Takes a directory path as argument. If
- specified, journalctl will operate on the specified journal
- directory <replaceable>DIR</replaceable> instead of the
- default runtime and system journal paths.</para></listitem>
+ <listitem><para>Show all fields in full, even if they include unprintable characters or are very
+ long. By default, fields with unprintable characters are abbreviated as "blob data". (Note that the
+ pager may escape unprintable characters again.)</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--file=<replaceable>GLOB</replaceable></option></term>
+ <term><option>-f</option></term>
+ <term><option>--follow</option></term>
- <listitem><para>Takes a file glob as an argument. If
- specified, journalctl will operate on the specified journal
- files matching <replaceable>GLOB</replaceable> instead of the
- default runtime and system journal paths. May be specified
- multiple times, in which case files will be suitably
- interleaved.</para></listitem>
+ <listitem><para>Show only the most recent journal entries, and continuously print new entries as
+ they are appended to the journal.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--root=<replaceable>ROOT</replaceable></option></term>
+ <term><option>--no-tail</option></term>
- <listitem><para>Takes a directory path as an argument. If specified, <command>journalctl</command>
- will operate on journal directories and catalog file hierarchy underneath the specified directory
- instead of the root directory (e.g. <option>--update-catalog</option> will create
- <filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>, and journal
- files under <filename><replaceable>ROOT</replaceable>/run/journal/</filename> or
- <filename><replaceable>ROOT</replaceable>/var/log/journal/</filename> will be displayed).
- </para></listitem>
+ <listitem><para>Show all stored output lines, even in follow mode. Undoes the effect of
+ <option>--lines=</option>.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--image=<replaceable>IMAGE</replaceable></option></term>
+ <term><option>-q</option></term>
+ <term><option>--quiet</option></term>
- <listitem><para>Takes a path to a disk image file or block device node. If specified,
- <command>journalctl</command> will operate on the file system in the indicated disk image. This is
- similar to <option>--root=</option> but operates on file systems stored in disk images or block
- devices, thus providing an easy way to extract log data from disk images. The disk image should
- either contain just a file system or a set of file systems within a GPT partition table, following
- the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
- Specification</ulink>. For further information on supported disk images, see
- <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
- switch of the same name.</para></listitem>
+ <listitem><para>Suppresses all informational messages (i.e. "-- Journal begins at …", "-- Reboot
+ --"), any warning messages regarding inaccessible system journals when run as a normal
+ user.</para></listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Pager Control Options</title>
+
+ <para>The following options control page support:</para>
+
+ <variablelist>
+
+ <xi:include href="standard-options.xml" xpointer="no-pager" />
<varlistentry>
- <term><option>--namespace=<replaceable>NAMESPACE</replaceable></option></term>
+ <term><option>-e</option></term>
+ <term><option>--pager-end</option></term>
- <listitem><para>Takes a journal namespace identifier string as argument. If not specified the data
- collected by the default namespace is shown. If specified shows the log data of the specified
- namespace instead. If the namespace is specified as <literal>*</literal> data from all namespaces is
- shown, interleaved. If the namespace identifier is prefixed with <literal>+</literal> data from the
- specified namespace and the default namespace is shown, interleaved, but no other. For details about
- journal namespaces see
- <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
+ <listitem><para>Immediately jump to the end of the journal inside the implied pager tool. This
+ implies <option>-n1000</option> to guarantee that the pager will not buffer logs of unbounded
+ size. This may be overridden with an explicit <option>-n</option> with some other numeric value,
+ while <option>-nall</option> will disable this cap. Note that this option is only supported for
+ the <citerefentry
+ project='man-pages'><refentrytitle>less</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ pager.</para></listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
- <varlistentry>
- <term><option>--header</option></term>
+ <refsect1>
+ <title>Forward Secure Sealing (FSS) Options</title>
- <listitem><para>Instead of showing journal contents, show internal header information of the journal
- fields accessed.</para>
+ <para>The following options make be used together with the <option>--setup-keys</option> command, see below.</para>
- <para>This option is particularly useful when trying to identify out-of-order journal entries, as
- happens for example when the machine is booted with the wrong system time.</para></listitem>
+ <variablelist>
+ <varlistentry>
+ <term><option>--interval=</option></term>
+
+ <listitem><para>Specifies the change interval for the sealing key when generating an FSS key pair
+ with <option>--setup-keys</option>. Shorter intervals increase CPU consumption but shorten the time
+ range of undetectable journal alterations. Defaults to 15min.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--disk-usage</option></term>
+ <term><option>--verify-key=</option></term>
- <listitem><para>Shows the current disk usage of all journal
- files. This shows the sum of the disk usage of all archived
- and active journal files.</para></listitem>
+ <listitem><para>Specifies the FSS verification key to use for the <option>--verify</option>
+ operation.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--vacuum-size=</option></term>
- <term><option>--vacuum-time=</option></term>
- <term><option>--vacuum-files=</option></term>
+ <term><option>--force</option></term>
- <listitem><para>Removes the oldest archived journal files until the disk space they use falls below the
- specified size (specified with the usual <literal>K</literal>, <literal>M</literal>, <literal>G</literal> and
- <literal>T</literal> suffixes), or all archived journal files contain no data older than the specified timespan
- (specified with the usual <literal>s</literal>, <literal>m</literal>, <literal>h</literal>,
- <literal>days</literal>, <literal>months</literal>, <literal>weeks</literal> and <literal>years</literal>
- suffixes), or no more than the specified number of separate journal files remain. Note that running
- <option>--vacuum-size=</option> has only an indirect effect on the output shown by
- <option>--disk-usage</option>, as the latter includes active journal files, while the vacuuming operation only
- operates on archived journal files. Similarly, <option>--vacuum-files=</option> might not actually reduce the
- number of journal files to below the specified number, as it will not remove active journal
- files.</para>
-
- <para><option>--vacuum-size=</option>, <option>--vacuum-time=</option> and <option>--vacuum-files=</option>
- may be combined in a single invocation to enforce any combination of a size, a time and a number of files limit
- on the archived journal files. Specifying any of these three parameters as zero is equivalent to not enforcing
- the specific limit, and is thus redundant.</para>
-
- <para>These three switches may also be combined with <option>--rotate</option> into one command. If so, all
- active files are rotated first, and the requested vacuuming operation is executed right after. The rotation has
- the effect that all currently active files are archived (and potentially new, empty journal files opened as
- replacement), and hence the vacuuming operation has the greatest effect as it can take all log data written so
- far into account.</para></listitem>
+ <listitem><para>When <option>--setup-keys</option> is passed and Forward Secure Sealing (FSS) has
+ already been configured, recreate FSS keys.</para></listitem>
</varlistentry>
+ </variablelist>
+ </refsect1>
- <varlistentry>
- <term><option>--list-catalog
- <optional><replaceable>128-bit-ID…</replaceable></optional>
- </option></term>
+ <refsect1>
+ <title>Commands</title>
+
+ <para>The following commands are understood. If none is specified the default is to display journal records.</para>
- <listitem><para>List the contents of the message catalog as a
- table of message IDs, plus their short description strings.
- </para>
+ <variablelist>
+ <varlistentry>
+ <term><option>-N</option></term>
+ <term><option>--fields</option></term>
- <para>If any <replaceable>128-bit-ID</replaceable>s are
- specified, only those entries are shown.</para>
- </listitem>
+ <listitem><para>Print all field names currently used in all entries of the journal.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--dump-catalog
- <optional><replaceable>128-bit-ID…</replaceable></optional>
- </option></term>
-
- <listitem><para>Show the contents of the message catalog, with
- entries separated by a line consisting of two dashes and the
- ID (the format is the same as <filename>.catalog</filename>
- files).</para>
+ <term><option>-F</option></term>
+ <term><option>--field=</option></term>
- <para>If any <replaceable>128-bit-ID</replaceable>s are
- specified, only those entries are shown.</para>
- </listitem>
+ <listitem><para>Print all possible data values the specified field can take in all entries of the
+ journal.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--update-catalog</option></term>
+ <term><option>--list-boots</option></term>
- <listitem><para>Update the message catalog index. This command
- needs to be executed each time new catalog files are
- installed, removed, or updated to rebuild the binary catalog
- index.</para></listitem>
+ <listitem><para>Show a tabular list of boot numbers (relative to the current boot), their IDs, and
+ the timestamps of the first and last message pertaining to the boot.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--setup-keys</option></term>
+ <term><option>--disk-usage</option></term>
- <listitem><para>Instead of showing journal contents, generate
- a new key pair for Forward Secure Sealing (FSS). This will
- generate a sealing key and a verification key. The sealing key
- is stored in the journal data directory and shall remain on
- the host. The verification key should be stored
- externally. Refer to the <option>Seal=</option> option in
- <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for information on Forward Secure Sealing and for a link to a
- refereed scholarly paper detailing the cryptographic theory it
- is based on.</para></listitem>
+ <listitem><para>Shows the current disk usage of all journal files. This shows the sum of the disk
+ usage of all archived and active journal files.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--force</option></term>
+ <term><option>--vacuum-size=</option></term>
+ <term><option>--vacuum-time=</option></term>
+ <term><option>--vacuum-files=</option></term>
+
+ <listitem><para>Removes the oldest archived journal files until the disk space they use falls below
+ the specified size (specified with the usual <literal>K</literal>, <literal>M</literal>,
+ <literal>G</literal> and <literal>T</literal> suffixes), or all archived journal files contain no
+ data older than the specified timespan (specified with the usual <literal>s</literal>,
+ <literal>m</literal>, <literal>h</literal>, <literal>days</literal>, <literal>months</literal>,
+ <literal>weeks</literal> and <literal>years</literal> suffixes), or no more than the specified
+ number of separate journal files remain. Note that running <option>--vacuum-size=</option> has only
+ an indirect effect on the output shown by <option>--disk-usage</option>, as the latter includes
+ active journal files, while the vacuuming operation only operates on archived journal
+ files. Similarly, <option>--vacuum-files=</option> might not actually reduce the number of journal
+ files to below the specified number, as it will not remove active journal files.</para>
- <listitem><para>When <option>--setup-keys</option> is passed
- and Forward Secure Sealing (FSS) has already been configured,
- recreate FSS keys.</para></listitem>
+ <para><option>--vacuum-size=</option>, <option>--vacuum-time=</option> and
+ <option>--vacuum-files=</option> may be combined in a single invocation to enforce any combination
+ of a size, a time and a number of files limit on the archived journal files. Specifying any of
+ these three parameters as zero is equivalent to not enforcing the specific limit, and is thus
+ redundant.</para>
+
+ <para>These three switches may also be combined with <option>--rotate</option> into one command. If
+ so, all active files are rotated first, and the requested vacuuming operation is executed right
+ after. The rotation has the effect that all currently active files are archived (and potentially new,
+ empty journal files opened as replacement), and hence the vacuuming operation has the greatest effect
+ as it can take all log data written so far into account.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--interval=</option></term>
+ <term><option>--verify</option></term>
- <listitem><para>Specifies the change interval for the sealing
- key when generating an FSS key pair with
- <option>--setup-keys</option>. Shorter intervals increase CPU
- consumption but shorten the time range of undetectable journal
- alterations. Defaults to 15min.</para></listitem>
+ <listitem><para>Check the journal file for internal consistency. If the file has been generated
+ with FSS enabled and the FSS verification key has been specified with
+ <option>--verify-key=</option>, authenticity of the journal file is verified.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--verify</option></term>
+ <term><option>--sync</option></term>
- <listitem><para>Check the journal file for internal
- consistency. If the file has been generated with FSS enabled and
- the FSS verification key has been specified with
- <option>--verify-key=</option>, authenticity of the journal file
- is verified.</para></listitem>
+ <listitem><para>Asks the journal daemon to write all yet unwritten journal data to the backing file
+ system and synchronize all journals. This call does not return until the synchronization operation
+ is complete. This command guarantees that any log messages written before its invocation are safely
+ stored on disk at the time it returns.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--verify-key=</option></term>
+ <term><option>--relinquish-var</option></term>
- <listitem><para>Specifies the FSS verification key to use for
- the <option>--verify</option> operation.</para></listitem>
+ <listitem><para>Asks the journal daemon for the reverse operation to <option>--flush</option>: if
+ requested the daemon will write further log data to <filename>/run/log/journal/</filename> and
+ stops writing to <filename>/var/log/journal/</filename>. A subsequent call to
+ <option>--flush</option> causes the log output to switch back to
+ <filename>/var/log/journal/</filename>, see above.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--sync</option></term>
+ <term><option>--smart-relinquish-var</option></term>
- <listitem><para>Asks the journal daemon to write all yet
- unwritten journal data to the backing file system and
- synchronize all journals. This call does not return until the
- synchronization operation is complete. This command guarantees
- that any log messages written before its invocation are safely
- stored on disk at the time it returns.</para></listitem>
+ <listitem><para>Similar to <option>--relinquish-var</option> but executes no operation if the root
+ file system and <filename>/var/lib/journal/</filename> reside on the same mount point. This
+ operation is used during system shutdown in order to make the journal daemon stop writing data to
+ <filename>/var/log/journal/</filename> in case that directory is located on a mount point that
+ needs to be unmounted.</para></listitem>
</varlistentry>
<varlistentry>
</varlistentry>
<varlistentry>
- <term><option>--relinquish-var</option></term>
+ <term><option>--rotate</option></term>
- <listitem><para>Asks the journal daemon for the reverse operation to <option>--flush</option>: if
- requested the daemon will write further log data to <filename>/run/log/journal/</filename> and stops
- writing to <filename>/var/log/journal/</filename>. A subsequent call to <option>--flush</option>
- causes the log output to switch back to <filename>/var/log/journal/</filename>, see
- above.</para></listitem>
+ <listitem><para>Asks the journal daemon to rotate journal files. This call does not return until
+ the rotation operation is complete. Journal file rotation has the effect that all currently active
+ journal files are marked as archived and renamed, so that they are never written to in future. New
+ (empty) journal files are then created in their place. This operation may be combined with
+ <option>--vacuum-size=</option>, <option>--vacuum-time=</option> and
+ <option>--vacuum-file=</option> into a single command, see above.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--smart-relinquish-var</option></term>
+ <term><option>--header</option></term>
- <listitem><para>Similar to <option>--relinquish-var</option> but executes no operation if the root file
- system and <filename>/var/lib/journal/</filename> reside on the same mount point. This operation is
- used during system shutdown in order to make the journal daemon stop writing data to
- <filename>/var/log/journal/</filename> in case that directory is located on a mount point that needs
- to be unmounted.</para></listitem>
+ <listitem><para>Instead of showing journal contents, show internal header information of the
+ journal fields accessed.</para>
+
+ <para>This option is particularly useful when trying to identify out-of-order journal entries, as
+ happens for example when the machine is booted with the wrong system time.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--rotate</option></term>
+ <term><option>--list-catalog <optional><replaceable>128-bit-ID…</replaceable></optional></option></term>
- <listitem><para>Asks the journal daemon to rotate journal files. This call does not return until the rotation
- operation is complete. Journal file rotation has the effect that all currently active journal files are marked
- as archived and renamed, so that they are never written to in future. New (empty) journal files are then
- created in their place. This operation may be combined with <option>--vacuum-size=</option>,
- <option>--vacuum-time=</option> and <option>--vacuum-file=</option> into a single command, see
- above.</para></listitem>
+ <listitem><para>List the contents of the message catalog as a table of message IDs, plus their
+ short description strings.</para>
+
+ <para>If any <replaceable>128-bit-ID</replaceable>s are specified, only those entries are
+ shown.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--dump-catalog <optional><replaceable>128-bit-ID…</replaceable></optional></option></term>
+
+ <listitem><para>Show the contents of the message catalog, with entries separated by a line
+ consisting of two dashes and the ID (the format is the same as <filename>.catalog</filename>
+ files).</para>
+
+ <para>If any <replaceable>128-bit-ID</replaceable>s are specified, only those entries are
+ shown.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--update-catalog</option></term>
+
+ <listitem><para>Update the message catalog index. This command needs to be executed each time new
+ catalog files are installed, removed, or updated to rebuild the binary catalog
+ index.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--setup-keys</option></term>
+
+ <listitem><para>Instead of showing journal contents, generate a new key pair for Forward Secure
+ Sealing (FSS). This will generate a sealing key and a verification key. The sealing key is stored in
+ the journal data directory and shall remain on the host. The verification key should be stored
+ externally. Refer to the <option>Seal=</option> option in
+ <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ information on Forward Secure Sealing and for a link to a refereed scholarly paper detailing the
+ cryptographic theory it is based on.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
- <xi:include href="standard-options.xml" xpointer="no-pager" />
</variablelist>
</refsect1>
<refsect1>
<title>Exit status</title>
- <para>On success, 0 is returned; otherwise, a non-zero failure
- code is returned.</para>
+ <para>On success, 0 is returned; otherwise, a non-zero failure code is returned.</para>
</refsect1>
<xi:include href="common-variables.xml" />
<refsect1>
<title>Examples</title>
- <para>Without arguments, all collected logs are shown
- unfiltered:</para>
+ <para>Without arguments, all collected logs are shown unfiltered:</para>
<programlisting>journalctl</programlisting>
- <para>With one match specified, all entries with a field matching
- the expression are shown:</para>
+ <para>With one match specified, all entries with a field matching the expression are shown:</para>
<programlisting>journalctl _SYSTEMD_UNIT=avahi-daemon.service
journalctl _SYSTEMD_CGROUP=/user.slice/user-42.slice/session-c1.scope</programlisting>
- <para>If two different fields are matched, only entries matching
- both expressions at the same time are shown:</para>
+ <para>If two different fields are matched, only entries matching both expressions at the same time are
+ shown:</para>
<programlisting>journalctl _SYSTEMD_UNIT=avahi-daemon.service _PID=28097</programlisting>
- <para>If two matches refer to the same field, all entries matching
- either expression are shown:</para>
+ <para>If two matches refer to the same field, all entries matching either expression are shown:</para>
<programlisting>journalctl _SYSTEMD_UNIT=avahi-daemon.service _SYSTEMD_UNIT=dbus.service</programlisting>
- <para>If the separator <literal>+</literal> is used, two
- expressions may be combined in a logical OR. The following will
- show all messages from the Avahi service process with the PID
- 28097 plus all messages from the D-Bus service (from any of its
- processes):</para>
+ <para>If the separator <literal>+</literal> is used, two expressions may be combined in a logical OR. The
+ following will show all messages from the Avahi service process with the PID 28097 plus all messages from
+ the D-Bus service (from any of its processes):</para>
<programlisting>journalctl _SYSTEMD_UNIT=avahi-daemon.service _PID=28097 + _SYSTEMD_UNIT=dbus.service</programlisting>
- <para>To show all fields emitted <emphasis>by</emphasis> a unit and <emphasis>about</emphasis>
- the unit, option <option>-u</option>/<option>--unit=</option> should be used.
- <command>journalctl -u <replaceable>name</replaceable></command>
- expands to a complex filter similar to
+ <para>To show all fields emitted <emphasis>by</emphasis> a unit and <emphasis>about</emphasis> the unit,
+ option <option>-u</option>/<option>--unit=</option> should be used. <command>journalctl -u
+ <replaceable>name</replaceable></command> expands to a complex filter similar to
+
<programlisting>_SYSTEMD_UNIT=<replaceable>name</replaceable>.service
+ UNIT=<replaceable>name</replaceable>.service _PID=1
+ OBJECT_SYSTEMD_UNIT=<replaceable>name</replaceable>.service _UID=0
- + COREDUMP_UNIT=<replaceable>name</replaceable>.service _UID=0 MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1
- </programlisting>
- (see <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- for an explanation of those patterns).
- </para>
+ + COREDUMP_UNIT=<replaceable>name</replaceable>.service _UID=0 MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1</programlisting>
+
+ (see
+ <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for an explanation of those patterns).</para>
<para>Show all logs generated by the D-Bus executable:</para>
<programlisting>journalctl -k -b -1</programlisting>
- <para>Show a live log display from a system service
- <filename>apache.service</filename>:</para>
+ <para>Show a live log display from a system service <filename>apache.service</filename>:</para>
<programlisting>journalctl -f -u apache</programlisting>
-
</refsect1>
<refsect1>
<para><varname>$KERNEL_INSTALL_MACHINE_ID</varname> is set for the plugins to the desired machine-id to
use. It's always a 128-bit ID. Normally it's read from <filename>/etc/machine-id</filename>, but it can
- also be overriden via <varname>$MACHINE_ID</varname> (see below). If not specified via these methods a
+ also be overridden via <varname>$MACHINE_ID</varname> (see below). If not specified via these methods a
fallback value will generated by <command>kernel-install</command>, and used only for a single
invocation.</para>
Currently, only x86 is supported, where it uses the PC speaker.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term>secure-boot-enroll</term>
+
+ <listitem><para>Danger: this feature might soft-brick your device if used improperly.</para>
+
+ <para>Takes one of <literal>off</literal>, <literal>manual</literal> or <literal>force</literal>.
+ Controls the enrollment of secure boot keys. If set to <literal>off</literal>, no action whatsoever
+ is taken. If set to <literal>manual</literal> (the default) and the UEFI firmware is in setup-mode
+ then entries to manually enroll Secure Boot variables are created in the boot menu. If set to
+ <literal>force</literal>, in addition, if a directory named <filename>/loader/keys/auto/</filename>
+ exists on the ESP then the keys in that directory are enrolled automatically.</para>
+
+ <para>The different sets of variables can be set up under <filename>/loader/keys/<replaceable>NAME</replaceable></filename>
+ where <replaceable>NAME</replaceable> is the name that is going to be used as the name of the entry.
+ This allows to ship multiple sets of Secure Boot variables and choose which one to enroll at runtime.</para>
+
+ <para>Supported secure boot variables are one database for authorized images, one key exchange key (KEK)
+ and one platform key (PK). For more information, refer to the <ulink url="https://uefi.org/specifications">UEFI specification</ulink>,
+ under Secure Boot and Driver Signing. Another resource that describe the interplay of the different variables is the
+ <ulink url="https://edk2-docs.gitbook.io/understanding-the-uefi-secure-boot-chain/secure_boot_chain_in_uefi/uefi_secure_boot">
+ EDK2 documentation</ulink>.</para>
+
+ <para>A complete set of UEFI variable includes <filename>db.auth</filename>, <filename>KEK.auth</filename>
+ and <filename>PK.auth</filename>. Note that these files need to be authenticated UEFI variables. See
+ below for an example of how to generate them from regular X.509 keys.</para>
+
+ <programlisting>uuid=$(systemd-id128 new --uuid)
+for key in PK KEK db; do
+ openssl req -new -x509 -subj "/CN=${key}/" -keyout "${key}.key" -out "${key}.crt"
+ openssl x509 -outform DER -in "${key}.crt" -out "${key}.cer"
+ cert-to-efi-sig-list -g "${uuid}" "${key}.crt" "${key}.esl"
+done
+
+for key in MicWinProPCA2011_2011-10-19.crt MicCorUEFCA2011_2011-06-27.crt MicCorKEKCA2011_2011-06-24.crt; do
+ curl "https://www.microsoft.com/pkiops/certs/${key}" --output "${key}"
+ sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output "${key%crt}esl" "${key}"
+done
+
+# Optionally add Microsoft Windows Production CA 2011 (needed to boot into Windows).
+cat MicWinProPCA2011_2011-10-19.esl >> db.esl
+
+# Optionally add Microsoft Corporation UEFI CA 2011 (for firmware drivers / option ROMs
+# and third-party boot loaders (including shim). This is highly recommended on real
+# hardware as not including this may soft-brick your device (see next paragraph).
+cat MicCorUEFCA2011_2011-06-27.esl >> db.esl
+
+# Optionally add Microsoft Corporation KEK CA 2011. Recommended if either of the
+# Microsoft keys is used as the official UEFI revocation database is signed with this
+# key. The revocation database can be updated with <citerefentry><refentrytitle>fwupdmgr</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+cat MicCorKEKCA2011_2011-06-24.esl >> KEK.esl
+
+sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth
+sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth
+sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth
+ </programlisting>
+
+ <para>This feature is considered dangerous because even if all the required files are signed with the
+ keys being loaded, some files necessary for the system to function properly still won't be. This
+ is especially the case with Option ROMs (e.g. for storage controllers or graphics cards). See
+ <ulink url="https://github.com/Foxboron/sbctl/wiki/FAQ#option-rom">Secure Boot and Option ROMs</ulink>
+ for more details.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term>reboot-for-bitlocker</term>
</varlistentry>
<varlistentry>
- <term><command>copy-to</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
+ <term><command>copy-to</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>] <option>--force</option></term>
<listitem><para>Copies files or directories from the host
system into a running container. Takes a container name,
</varlistentry>
<varlistentry>
- <term><command>copy-from</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
+ <term><command>copy-from</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>] <option>--force</option></term>
<listitem><para>Copies files or directories from a container
into the host system. Takes a container name, followed by the
CopyToMachine(in s name,
in s source,
in s destination);
+ CopyFromMachineWithFlags(in s name,
+ in s source,
+ in s destination,
+ in t flags);
+ CopyToMachineWithFlags(in s name,
+ in s source,
+ in s destination,
+ in t flags);
OpenMachineRootDirectory(in s name,
out h fd);
GetMachineUIDShift(in s name,
<!--method UnregisterMachine is not documented!-->
+ <!--method CopyToMachineWithFlags is not documented!-->
+
<!--method OpenMachineRootDirectory is not documented!-->
<!--method GetMachineUIDShift is not documented!-->
<variablelist class="dbus-method" generated="True" extra-ref="CopyToMachine()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="CopyFromMachineWithFlags()"/>
+
+ <variablelist class="dbus-method" generated="True" extra-ref="CopyToMachineWithFlags()"/>
+
<variablelist class="dbus-method" generated="True" extra-ref="OpenMachineRootDirectory()"/>
<variablelist class="dbus-method" generated="True" extra-ref="GetMachineUIDShift()"/>
<para><function>CopyFromMachine()</function> copies files or directories from a container into the
host. It takes a container name, a source directory in the container and a destination directory on the
- host as arguments. <function>CopyToMachine()</function> does the opposite and copies files from a source
- directory on the host into a destination directory in the container.</para>
+ host as arguments.
+ <function>CopyToMachine()</function> does the opposite and copies files from a source
+ directory on the host into a destination directory in the container.
+ <function>CopyFromMachineWithFlags()</function> and <function>CopyToMachineWithFlags</function> do the same but take an additional flags argument.</para>
<para><function>RemoveImage()</function> removes the image with the specified name.</para>
in s destination);
CopyTo(in s source,
in s destination);
+ CopyFromWithFlags(in s source,
+ in s destination,
+ in t flags);
+ CopyToWithFlags(in s source,
+ in s destination,
+ in t flags);
OpenRootDirectory(out h fd);
properties:
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--method CopyTo is not documented!-->
+ <!--method CopyFromWithFlags is not documented!-->
+
+ <!--method CopyToWithFlags is not documented!-->
+
<!--method OpenRootDirectory is not documented!-->
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-method" generated="True" extra-ref="CopyTo()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="CopyFromWithFlags()"/>
+
+ <variablelist class="dbus-method" generated="True" extra-ref="CopyToWithFlags()"/>
+
<variablelist class="dbus-method" generated="True" extra-ref="OpenRootDirectory()"/>
<variablelist class="dbus-property" generated="True" extra-ref="Name"/>
interface org.freedesktop.oom1.Manager {
methods:
DumpByFileDescriptor(out h fd);
+ signals:
+ Killed(s cgroup,
+ s reason);
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
<variablelist class="dbus-method" generated="True" extra-ref="DumpByFileDescriptor()"/>
+ <variablelist class="dbus-signal" generated="True" extra-ref="Killed"/>
+
<!--End of Autogenerated section-->
<refsect2>
<title>Methods</title>
- <para>...</para>
+ <para><function>Killed</function> signal is sent when any cgroup is killed by oomd.</para>
+ <para>Note that more reasons will be added in the future, and the table below will be expanded accordingly.</para>
+ <table>
+ <title>Killing reasons</title>
+ <tgroup cols="2" align="left" colsep="1" rowsep="1">
+ <colspec colname="reason"/>
+ <colspec colname="description"/>
+ <thead>
+ <row>
+ <entry>Reason</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>memory-used</entry>
+ <entry>Application took too much memory and swap.</entry>
+ </row>
+ <row>
+ <entry>memory-pressure</entry>
+ <entry>Application took enough memory and swap to cause sufficient slowdown of other applications.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
</refsect2>
</refsect1>
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultTimeoutAbortUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly t DefaultDeviceTimeoutUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t DefaultRestartUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t DefaultStartLimitIntervalUSec = ...;
<!--property DefaultTimeoutAbortUSec is not documented!-->
+ <!--property DefaultDeviceTimeoutUSec is not documented!-->
+
<!--property DefaultRestartUSec is not documented!-->
<!--property DefaultStartLimitIntervalUSec is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="DefaultTimeoutAbortUSec"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="DefaultDeviceTimeoutUSec"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="DefaultRestartUSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultStartLimitIntervalUSec"/>
that the root path is encoded as the empty string here (not as <literal>/</literal>!), so that it can be
appended to <filename>/sys/fs/cgroup/systemd</filename> easily. This value will be set to the empty
string for the host instance and some other string for container instances.</para>
+
+ <para><varname>AccessSELinuxContext</varname> contains the SELinux context that is used to control
+ access to the unit. It's read from the unit file when it is loaded and cached until the service manager
+ is reloaded. This property contains an empty string if SELinux is not used or if no label could be read
+ (for example because the unit is not backed by a file on disk).</para>
</refsect2>
<refsect2>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s Description = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s AccessSELinuxContext = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s LoadState = '...';
readonly s ActiveState = '...';
readonly s FreezerState = '...';
<variablelist class="dbus-property" generated="True" extra-ref="Description"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="AccessSELinuxContext"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="LoadState"/>
<variablelist class="dbus-property" generated="True" extra-ref="ActiveState"/>
not</emphasis> provided.</para>
<para>For example, <literal>SUPPORT_END=2001-01-01</literal> means that the system was supported
- until the end of the last day of the previous millenium.</para></listitem>
+ until the end of the last day of the previous millennium.</para></listitem>
</varlistentry>
<varlistentry>
determines the fallback hostname.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ARCHITECTURE=</varname></term>
+ <listitem><para>A string that specifies which CPU architecture the userspace binaries require.
+ The architecture identifiers are the same as for <varname>ConditionArchitecture=</varname>
+ described in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ The field is optional and should only be used when just single architecture is supported.
+ It may provide redundant information when used in a GPT partition with a GUID type that already
+ encodes the architecture. If this is not the case, the architecture should be specified in
+ e.g., an extension image, to prevent an incompatible host from loading it.
+ </para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>SYSEXT_LEVEL=</varname></term>
<listitem><para>Takes a space-separated list of one or more valid prefix match strings for the
<ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> logic. This field
serves two purposes: it is informational, identifying portable service images as such (and thus
- allowing them to be distinguished from other OS images, such as bootable system images). In is also
+ allowing them to be distinguished from other OS images, such as bootable system images). It is also
used when a portable service image is attached: the specified or implied portable service prefix is
checked against the list specified here, to enforce restrictions how images may be attached to a
system.</para></listitem>
'systemd-makefs',
'systemd-mkswap@.service'],
''],
+ ['systemd-measure', '1', [], 'HAVE_GNU_EFI'],
['systemd-modules-load.service', '8', ['systemd-modules-load'], 'HAVE_KMOD'],
['systemd-mount', '1', ['systemd-umount'], ''],
['systemd-network-generator.service', '8', ['systemd-network-generator'], ''],
<refsect1>
<para id="singular">This option is only available for system services, or for services running in per-user
- instances of the service manager when unprivileged user namespaces are available.</para>
+ instances of the service manager when <varname>PrivateUsers=</varname> is enabled.</para>
<para id="plural">These options are only available for system services, or for services running in per-user
- instances of the service manager when unprivileged user namespaces are available.</para>
+ instances of the service manager when <varname>PrivateUsers=</varname> is enabled.</para>
</refsect1>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><command>list-automounts</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
+
+ <listitem>
+ <para>List automount units currently in memory, ordered by mount path. If one or more
+ <replaceable>PATTERN</replaceable>s are specified, only automount units matching one of them are shown.
+ Produces output similar to
+ <programlisting>
+WHAT WHERE MOUNTED IDLE TIMEOUT UNIT
+/dev/sdb1 /mnt/test no 120s mnt-test.automount
+binfmt_misc /proc/sys/fs/binfmt_misc yes 0 proc-sys-fs-binfmt_misc.automount
+
+2 automounts listed.</programlisting>
+ </para>
+
+ <para>Also see <option>--show-types</option>, <option>--all</option>, and <option>--state=</option>.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><command>list-sockets</command> <optional><replaceable>PATTERN</replaceable>…</optional></term>
<option>--type=</option>, <option>--state=</option>, or <option>--failed</option> are used, units
are additionally filtered by the TYPE and ACTIVE state.</para>
- <para>This function is intended to generate human-readable
- output. If you are looking for computer-parsable output,
- use <command>show</command> instead. By default, this
- function only shows 10 lines of output and ellipsizes
- lines to fit in the terminal window. This can be changed
- with <option>--lines</option> and <option>--full</option>,
- see above. In addition, <command>journalctl
- --unit=<replaceable>NAME</replaceable></command> or
- <command>journalctl
- --user-unit=<replaceable>NAME</replaceable></command> use
- a similar filter for messages and might be more
- convenient.
- </para>
-
- <para>systemd implicitly loads units as necessary, so just running the <command>status</command> will
- attempt to load a file. The command is thus not useful for determining if something was already loaded or
- not. The units may possibly also be quickly unloaded after the operation is completed if there's no reason
- to keep it in memory thereafter.
- </para>
+ <para>This function is intended to generate human-readable output. If you are looking for
+ computer-parsable output, use <command>show</command> instead. By default, this function only
+ shows 10 lines of output and ellipsizes lines to fit in the terminal window. This can be changed
+ with <option>--lines</option> and <option>--full</option>, see above. In addition,
+ <command>journalctl --unit=<replaceable>NAME</replaceable></command> or <command>journalctl
+ --user-unit=<replaceable>NAME</replaceable></command> use a similar filter for messages and might
+ be more convenient.</para>
+
+ <para>Note that this operation only displays <emphasis>runtime</emphasis> status, i.e. information about
+ the current invocation of the unit (if it is running) or the most recent invocation (if it is not
+ running anymore, and has not been released from memory). Information about earlier invocations,
+ invocations from previous system boots, or prior invocations that have already been released from
+ memory may be retrieved via <command>journalctl --unit=</command>.</para>
+
+ <para>systemd implicitly loads units as necessary, so just running the <command>status</command>
+ will attempt to load a file. The command is thus not useful for determining if something was
+ already loaded or not. The units may possibly also be quickly unloaded after the operation is
+ completed if there's no reason to keep it in memory thereafter.</para>
<example>
<title>Example output from systemctl status </title>
<listitem><para>The EFI Shell binary, if installed.</para></listitem>
<listitem><para>A reboot into the UEFI firmware setup option, if supported by the firmware.</para></listitem>
+
+ <listitem><para>Secure boot variables enrollement if the UEFI firmware is in setup-mode and files are provided
+ on the ESP.</para></listitem>
</itemizedlist>
<para><command>systemd-boot</command> supports the following features:</para>
<listitem><para>The boot manager optionally reads a random seed from the ESP partition, combines it
with a 'system token' stored in a persistent EFI variable and derives a random seed to use by the OS as
entropy pool initialization, providing a full entropy pool during early boot.</para></listitem>
+
+ <listitem><para>The boot manager allows for secure boot variables to be enrolled if the UEFI firmware is
+ in setup-mode. Additionally, variables can be automatically enrolled if configured.</para></listitem>
</itemizedlist>
<para><citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
extension of the EFI architecture ID followed by <filename>.efi</filename> (e.g. for x86-64 this means a
suffix of <filename>x64.efi</filename>). This may be used to automatically load file system drivers and
similar, to extend the native firmware support.</para>
+
+ <para>Enrollment of Secure Boot variables can be performed manually or automatically if files are available
+ under <filename>/keys/<replaceable>NAME</replaceable>/{db,KEK,PK}.auth</filename>, <replaceable>NAME</replaceable>
+ being the display name for the set of variables in the menu. If one of the sets is named <filename>auto</filename>
+ then it might be enrolled automatically depending on whether <literal>secure-boot-enroll</literal> is set
+ to force or not.</para>
</refsect1>
<refsect1>
<term><command>has-tpm2</command></term>
<listitem><para>Reports whether the system is equipped with a TPM2 device usable for protecting
- credentials. If the a TPM2 device has been discovered, is supported, and is being used by firmware,
+ credentials. If a TPM2 device has been discovered, is supported, and is being used by firmware,
by the OS kernel drivers and by userspace (i.e. systemd) this prints <literal>yes</literal> and exits
with exit status zero. If no such device is discovered/supported/used, prints
<literal>no</literal>. Otherwise prints <literal>partial</literal>. In either of these two cases
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--unlock-key-file=</option><replaceable>PATH</replaceable></term>
+
+ <listitem><para>Use a file instead of a password/passphrase read from stdin to unlock the volume.
+ Expects the PATH to the file containing your key to unlock the volume. Currently there is nothing like
+ <option>--key-file-offset=</option> or <option>--key-file-size=</option> so this file has to only
+ contain the full key.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--pkcs11-token-uri=</option><replaceable>URI</replaceable></term>
<entry>The IMA project measures its runtime state into this PCR.</entry>
</row>
+ <row>
+ <entry>11</entry>
+ <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. Unlike PCR 4 (where the same data should be measured into), this PCR value should be easy to pre-calculate, as this only contains static parts of the PE binary. Use this PCR to bind TPM policies to a specific kernel image, possibly with an embedded initial RAM disk (initrd).</entry>
+ </row>
+
<row>
<entry>12</entry>
<entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any specified kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
</row>
+ <row>
+ <entry>13</entry>
+ <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> images it loads and passed to the booted kernel into this PCR.</entry>
+ </row>
+
<row>
<entry>14</entry>
<entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>
<cmdsynopsis>
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--mount</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
</cmdsynopsis>
+ <cmdsynopsis>
+ <command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--umount</option> <arg choice="plain"><replaceable>PATH</replaceable></arg></command>
+ </cmdsynopsis>
<cmdsynopsis>
<command>systemd-dissect <arg choice="opt" rep="repeat">OPTIONS</arg> <option>--copy-from</option> <arg choice="plain"><replaceable>IMAGE</replaceable></arg> <arg choice="plain"><replaceable>PATH</replaceable></arg> <arg choice="opt"><replaceable>TARGET</replaceable></arg></command>
</cmdsynopsis>
<title>Description</title>
<para><command>systemd-dissect</command> is a tool for introspecting and interacting with file system OS
- disk images. It supports four different operations:</para>
+ disk images. It supports five different operations:</para>
<orderedlist>
<listitem><para>Show general OS image information, including the image's
mount the included partitions according to their designation onto a directory and possibly
sub-directories.</para></listitem>
+ <listitem><para>Unmount an OS image from a local directory. In this mode it will recursively unmount
+ the mounted partitions and remove the underlying loop device, including all the partition sub-devices.
+ </para></listitem>
+
<listitem><para>Copy files and directories in and out of an OS image.</para></listitem>
</orderedlist>
multiple nested mounts are established. This command expects two arguments: a path to an image file
and a path to a directory where to mount the image.</para>
- <para>To unmount an OS image mounted like this use <citerefentry
- project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
- <option>-R</option> switch (for recursive operation), so that the OS image and all nested partition
- mounts are unmounted.</para>
+ <para>To unmount an OS image mounted like this use the <option>--umount</option> operation.</para>
<para>When the OS image contains LUKS encrypted or Verity integrity protected file systems
appropriate volumes are automatically set up and marked for automatic disassembly when the image is
<listitem><para>This is a shortcut for <option>--mount --mkdir</option>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--umount</option></term>
+ <term><option>-u</option></term>
+
+ <listitem><para>Unmount an OS image from the specified directory. This command expects one argument:
+ a directory where an OS image was mounted.</para>
+
+ <para>All mounted partitions will be recursively unmounted, and the underlying loop device will be
+ removed, along with all it's partition sub-devices.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-U</option></term>
+
+ <listitem><para>This is a shortcut for <option>--umount --rmdir</option>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--copy-from</option></term>
<term><option>-x</option></term>
unmounted again.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--rmdir</option></term>
+
+ <listitem><para>If combined with <option>--umount</option> the specified directory where the OS image
+ is mounted is removed after unmounting the OS image.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--discard=</option></term>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-integritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
- <citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry project='die-net'><refentrytitle>integritysetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
--- /dev/null
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+<refentry id="systemd-measure" xmlns:xi="http://www.w3.org/2001/XInclude" conditional='HAVE_GNU_EFI'>
+
+ <refentryinfo>
+ <title>systemd-measure</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-measure</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-measure</refname>
+ <refpurpose>Pre-calculate expected TPM2 PCR values for booted unified kernel images</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>/usr/lib/systemd/systemd-measure <arg choice="opt" rep="repeat">OPTIONS</arg></command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>Note: this command is experimental for now. While it is likely to become a regular component of
+ systemd, it might still change in behaviour and interface.</para>
+
+ <para><command>systemd-measure</command> is a tool that may be used to pre-calculate the expected TPM2
+ PCR 11 values that should be seen when a unified Linux kernel image based on
+ <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> is
+ booted up. It accepts paths to the ELF kernel image file, initial ram disk image file, devicetree file,
+ kernel command line file,
+ <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file, and
+ boot splash file that make up the unified kernel image, and determines the PCR values expected to be in
+ place after booting the image. Calculation starts with a zero-initialized PCR 11, and is executed in a
+ fashion compatible with what <filename>systemd-stub</filename> does at boot.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Commands</title>
+
+ <para>The following commands are understood:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><command>status</command></term>
+
+ <listitem><para>This is the default command if none is specified. This queries the local system's
+ TPM2 PCR 11+12+13 values and displays them. The data is written in a similar format as the
+ <command>calculate</command> command below, and may be used to quickly compare expectation with
+ reality.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>calculate</command></term>
+
+ <listitem><para>Pre-calculate the expected value seen in PCR register 11 after boot-up of a unified
+ kernel image consisting of the components specified with <option>--linux=</option>,
+ <option>--osrel=</option>, <option>--cmdline=</option>, <option>--initrd=</option>,
+ <option>--splash=</option>, <option>--dtb=</option>, see below. Only <option>--linux=</option> is
+ mandatory.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>--linux=PATH</option></term>
+ <term><option>--osrel=PATH</option></term>
+ <term><option>--cmdline=PATH</option></term>
+ <term><option>--initrd=PATH</option></term>
+ <term><option>--splash=PATH</option></term>
+ <term><option>--dtb=PATH</option></term>
+
+ <listitem><para>When used with the <command>calculate</command> verb, configures the files to read
+ the unified kernel image components from. Each option corresponds with the equally named section in
+ the unified kernel PE file. The <option>--linux=</option> switch expects the path to the ELF kernel
+ file that the unified PE kernel will wrap. All switches except <option>--linux=</option> are
+ optional. Each option may be used at most once.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--bank=DIGEST</option></term>
+
+ <listitem><para>Controls the PCR banks to pre-calculate the PCR values for – in case
+ <command>calculate</command> is invoked –, or the banks to show in the <command>status</command>
+ output. May be used more then once to specify multiple banks. If not specified, defaults to the four
+ banks <literal>sha1</literal>, <literal>sha256</literal>, <literal>sha384</literal>,
+ <literal>sha512</literal>.</para></listitem>
+ </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Generate a unified kernel image, and calculate the expected TPM PCR 11 value</title>
+
+ <programlisting># objcopy \
+ --add-section .linux=vmlinux --change-section-vma .linux=0x2000000 \
+ --add-section .osrel=os-release.txt --change-section-vma .osrel=0x20000 \
+ --add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
+ --add-section .initrd=initrd.cpio --change-section-vma .initrd=0x3000000 \
+ --add-section .splash=splash.bmp --change-section-vma .splash=0x100000 \
+ --add-section .dtb=devicetree.dtb --change-section-vma .dtb=0x40000 \
+ /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
+ foo.efi
+# systemd-measure calculate \
+ --linux=vmlinux \
+ --osrel=os-release \
+ --cmdline=cmdline.txt \
+ --initrd=initrd.cpio \
+ --splash=splash.bmp \
+ --dtb=devicetree.dtb
+11:sha1=d775a7b4482450ac77e03ee19bda90bd792d6ec7
+11:sha256=bc6170f9ce28eb051ab465cd62be8cf63985276766cf9faf527ffefb66f45651
+11:sha384=1cf67dff4757e61e5a73d2a21a6694d668629bbc3761747d493f7f49ad720be02fd07263e1f93061243aec599d1ee4b4
+11:sha512=8e79acd3ddbbc8282e98091849c3530f996303c8ac8e87a3b2378b71c8b3a6e86d5c4f41ecea9e1517090c3e8ec0c714821032038f525f744960bcd082d937da
+</programlisting>
+ </example>
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<orderedlist>
<listitem><para>The <filename>repart.d/*.conf</filename> configuration files are loaded and parsed,
- and ordered by filename (without the directory prefix).</para></listitem>
+ and ordered by filename (without the directory prefix). For each configuration file,
+ drop-in files are looked for in directories with same name as the configuration file
+ with a suffix ".d" added.</para></listitem>
<listitem><para>The partition table already existing on the block device is loaded and
parsed.</para></listitem>
<listitem><para>Takes a file system path. If specified the <filename>*.conf</filename> files are read
from the specified directory instead of searching in <filename>/usr/lib/repart.d/*.conf</filename>,
<filename>/etc/repart.d/*.conf</filename>,
- <filename>/run/repart.d/*.conf</filename>.</para></listitem>
+ <filename>/run/repart.d/*.conf</filename>.</para>
+
+ <para>This parameter can be specified multiple times.</para></listitem>
</varlistentry>
<varlistentry>
<itemizedlist>
<listitem><para><command>systemd-resolved</command> maintains the
<filename>/run/systemd/resolve/stub-resolv.conf</filename> file for compatibility with traditional
- Linux programs. This file may be symlinked from <filename>/etc/resolv.conf</filename>. This file lists
- the 127.0.0.53 DNS stub (see above) as the only DNS server. It also contains a list of search domains
- that are in use by systemd-resolved. The list of search domains is always kept up-to-date. Note that
- <filename>/run/systemd/resolve/stub-resolv.conf</filename> should not be used directly by applications,
- but only through a symlink from <filename>/etc/resolv.conf</filename>. This file may be symlinked from
+ Linux programs. This file lists the 127.0.0.53 DNS stub (see above) as the only DNS server. It also
+ contains a list of search domains that are in use by systemd-resolved. The list of search domains is
+ always kept up-to-date. Note that <filename>/run/systemd/resolve/stub-resolv.conf</filename> should not
+ be used directly by applications, but only through a symlink from
+ <filename>/etc/resolv.conf</filename>. This file may be symlinked from
<filename>/etc/resolv.conf</filename> in order to connect all local clients that bypass local DNS APIs
to <command>systemd-resolved</command> with correct search domains settings. This mode of operation is
recommended.</para></listitem>
<term>suspend-then-hibernate</term>
<listitem><para>A low power state where the system is initially suspended
- (the state is stored in RAM). If not interrupted within the delay specified by
- <command>HibernateDelaySec=</command>, the system will be woken using an RTC
- alarm and hibernated (the state is then stored on disk).
+ (the state is stored in RAM) and then hibernated based on battery percentage
+ capacity. If the current battery capacity is higher than 5%, the system goes
+ back to suspend for interval calculated using battery discharge rate per hour.
+ Battery discharge rate per hour is stored in a file which is created after
+ initial suspend-resume cycle. The value is calculated using battery decreasing
+ charge level a timespan. In case of manual wakeup before RTC alarm, the timespan
+ is the duration for which system was suspended. If the file does not exist or
+ has invalid value, initial suspend duration is set to
+ <command>HibernateDelaySec=</command>.
+ After wakeup via an RTC alarm, the battery discharge rate per hour is again
+ estimated. If the current battery charge level is equal to or less than 5%,
+ the system will be hibernated (the state is then stored on disk) else the
+ system goes back to suspend for the amount of time it would take to fully
+ discharge the battery minus 30 minutes.
</para></listitem>
</varlistentry>
</varlistentry>
<varlistentry>
<term><varname>HibernateDelaySec=</varname></term>
-
- <listitem><para>The amount of time the system spends in suspend mode before the system is
- automatically put into hibernate mode, when using
+ <listitem><para>The amount of time the system spends in suspend mode
+ before the RTC alarm wakes the system, before the battery discharge rate
+ can be estimated and used instead to calculate the suspension interval.
<citerefentry><refentrytitle>systemd-suspend-then-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. Defaults
to 2h.</para></listitem>
</varlistentry>
<listitem><para>The ELF Linux kernel images will be looked for in the <literal>.linux</literal> PE
section of the executed image.</para></listitem>
+ <listitem><para>OS release information, i.e. the
+ <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file of
+ the OS the kernel belongs to, in the <literal>.osrel</literal> PE section.</para></listitem>
+
<listitem><para>The initial RAM disk (initrd) will be looked for in the <literal>.initrd</literal> PE
section.</para></listitem>
<para>If a DeviceTree is embedded in the <literal>.dtb</literal> section, it replaces an existing
DeviceTree in the corresponding EFI configuration table. systemd-stub will ask the firmware via the
<literal>EFI_DT_FIXUP_PROTOCOL</literal> for hardware specific fixups to the DeviceTree.</para>
+
+ <para>The contents of these six PE sections are measured into TPM PCR 11, that is otherwise not
+ used. Thus, it can be pre-calculated without too much effort.</para>
</refsect1>
<refsect1>
images to the initrd. See
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
details on system extension images. The generated <command>cpio</command> archive containing these
- system extension images is measured into TPM PCR 4 (if a TPM is present).</para></listitem>
+ system extension images is measured into TPM PCR 13 (if a TPM is present).</para></listitem>
<listitem><para>Files <filename>/loader/credentials/*.cred</filename> are packed up in a
<command>cpio</command> archive and placed in the <filename>/.extra/global_credentials/</filename>
core kernel, the embedded initrd and kernel command line (see above for a full list).</para>
<para>Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means
- every type of initrd will be measured twice: the initrd embedded in the kernel image will be measured to
- both PCR 4 and PCR 9; the initrd synthesized from credentials will be measured to both PCR 12 and PCR 9;
- the initrd synthesized from system extensions will be measured to both PCR 4 and PCR 9. Let's summarize
- the OS resources and the PCRs they are measured to:</para>
+ every type of initrd will be measured two or three times: the initrd embedded in the kernel image will be
+ measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials will be measured to both PCR
+ 9 and PCR 12; the initrd synthesized from system extensions will be measured to both PCR 4 and PCR
+ 9. Let's summarize the OS resources and the PCRs they are measured to:</para>
<table>
<title>OS Resource PCR Summary</title>
<row>
<entry>Boot splash (embedded in the unified PE binary)</entry>
- <entry>4</entry>
+ <entry>4 + 11</entry>
</row>
<row>
<entry>Core kernel code (embedded in unified PE binary)</entry>
- <entry>4</entry>
+ <entry>4 + 11</entry>
</row>
<row>
<entry>Main initrd (embedded in unified PE binary)</entry>
- <entry>4 + 9</entry>
+ <entry>4 + 9 + 11</entry>
</row>
<row>
<entry>Default kernel command line (embedded in unified PE binary)</entry>
- <entry>4</entry>
+ <entry>4 + 11</entry>
</row>
<row>
<row>
<entry>Credentials (synthesized initrd from companion files)</entry>
- <entry>12 + 9</entry>
+ <entry>9 + 12</entry>
</row>
<row>
<entry>System Extensions (synthesized initrd from companion files)</entry>
- <entry>4 + 9</entry>
+ <entry>9 + 13</entry>
</row>
</tbody>
</tgroup>
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view
this data.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>StubPcrKernelImage</varname></term>
+
+ <listitem><para>The PCR register index the ELF kernel image/initial RAM disk image/boot
+ splash/devicetree database/embedded command line are measured into, formatted as decimal ASCII string
+ (i.e. <literal>11</literal>). This variable is set if a measurement was successfully completed, and
+ remains unset otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>StubPcrKernelParameters</varname></term>
+
+ <listitem><para>The PCR register index the kernel command line and credentials are measured into,
+ formatted as decimal ASCII string (i.e. <literal>12</literal>). This variable is set if a measurement
+ was successfully completed, and remains unset otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>StubPcrInitRDSysExts</varname></term>
+
+ <listitem><para>The PCR register index the systemd extensions for the initial RAM disk image, which
+ are picked up from the file system the kernel image is located on. Formatted as decimal ASCII string
+ (i.e. <literal>13</literal>). This variable is set if a measurement was successfully completed, and
+ remains unset otherwise.</para></listitem>
+ </varlistentry>
</variablelist>
<para>Note that some of the variables above may also be set by the boot loader. The stub will only set
<para>Only apply rules with the specified prefix.</para>
</listitem>
</varlistentry>
+ <varlistentry id='strict'>
+ <term><option>--strict=</option></term>
+ <listitem>
+ <para>Always return non-zero exit code on failure (including invalid sysctl variable
+ name and insufficient permissions), unless the sysctl variable name is prefixed with a "-"
+ character.</para>
+ </listitem>
+ </varlistentry>
<xi:include href="standard-options.xml" xpointer="cat-config" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
</variablelist>
</refsect1>
+ <refsect1>
+ <title>Credentials</title>
+
+ <para><command>systemd-sysctl</command> supports the service credentials logic as implemented by
+ <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ details). The following credentials are used when passed in:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>sysctl.extra</literal></term>
+
+ <listitem><para>The contents of this credential may contain additional lines to operate on. The
+ credential contents should follow the same format as any other <filename>sysctl.d/</filename> drop-in
+ configuration file. If this credential is passed it is processed after all of the drop-in files read
+ from the file system. The settings configured in the credential hence take precedence over those in
+ the file system.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Note that by default the <filename>systemd-sysctl.service</filename> unit file is set up to inherit
+ the <literal>sysctl.extra</literal> credential from the service manager.</para>
+ </refsect1>
+
<refsect1>
<title>Examples</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- <citerefentry project='man-pages'><refentrytitle>sysctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>sysctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
<para>System extensions are supposed to be purely additive, i.e. they are supposed to include only files
that do not exist in the underlying basic OS image. However, the underlying mechanism (overlayfs) also
- allows removing files, but it is recommended not to make use of this.</para>
+ allows overlaying or removing files, but it is recommended not to make use of this.</para>
<para>System extension images may be provided in the following formats:</para>
accessed.</para>
<para>Note that there is no concept of enabling/disabling installed system extension images: all
- installed extension images are automatically activated at boot.</para>
+ installed extension images are automatically activated at boot. However, you can place an empty directory
+ named like the extension (no <filename>.raw</filename>) in <filename>/etc/extensions/</filename> to "mask"
+ an extension with the same name in a system folder with lower precedence.</para>
<para>A simple mechanism for version compatibility is enforced: a system extension image must carry a
<filename>/usr/lib/extension-release.d/extension-release.<replaceable>$name</replaceable></filename>
file, which must match its image name, that is compared with the host <filename>os-release</filename>
- file: the contained <varname>ID=</varname> fields have to match, as well as the
- <varname>SYSEXT_LEVEL=</varname> field (if defined). If the latter is not defined, the
- <varname>VERSION_ID=</varname> field has to match instead. System extensions should not ship a
- <filename>/usr/lib/os-release</filename> file (as that would be merged into the host
- <filename>/usr/</filename> tree, overriding the host OS version data, which is not desirable). The
- <filename>extension-release</filename> file follows the same format and semantics, and carries the same
+ file: the contained <varname>ID=</varname> fields have to match unless <literal>_any</literal> is set
+ for the extension. If the extension <varname>ID=</varname> is not <literal>_any</literal>, the
+ <varname>SYSEXT_LEVEL=</varname> field (if defined) has to match. If the latter is not defined, the
+ <varname>VERSION_ID=</varname> field has to match instead. If the extension defines the
+ <varname>ARCHITECTURE=</varname> field and the value is not <literal>_any</literal> it has to match the kernel's
+ architecture reported by <citerefentry><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+ but the used architecture identifiers are the same as for <varname>ConditionArchitecture=</varname>
+ described in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ System extensions should not ship a <filename>/usr/lib/os-release</filename> file (as that would be merged
+ into the host <filename>/usr/</filename> tree, overriding the host OS version data, which is not desirable).
+ The <filename>extension-release</filename> file follows the same format and semantics, and carries the same
content, as the <filename>os-release</filename> file of the OS, but it describes the resources carried
in the extension image.</para>
</refsect1>
100ms.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>DefaultDeviceTimeoutSec=</varname></term>
+
+ <listitem><para>Configures the default timeout for waiting for devices. It can be changed per
+ device via the <varname>x-systemd.device-timeout=</varname> option in <filename>/etc/fstab</filename>
+ and <filename>/etc/crypttab</filename> (see
+ <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>crypttab</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+ Defaults to 90s.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>DefaultStartLimitIntervalSec=</varname></term>
<term><varname>DefaultStartLimitBurst=</varname></term>
<listitem><para>Specifies the shell binary to use for the specified account when creating it.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>sysusers.extra</literal></term>
+
+ <listitem><para>The contents of this credential may contain additional lines to operate on. The
+ credential contents should follow the same format as any other <filename>sysusers.d/</filename>
+ drop-in. If this credential is passed it is processed after all of the drop-in files read from the
+ file system.</para></listitem>
+ </varlistentry>
</variablelist>
<para>Note that by default the <filename>systemd-sysusers.service</filename> unit file is set up to
inherit the <literal>passwd.hashed-password.root</literal>,
- <literal>passwd.plaintext-password.root</literal> and <literal>passwd.shell.root</literal> credentials
- from the service manager. Thus, when invoking a container with an unpopulated <filename>/etc/</filename>
- for the first time it is possible to configure the root user's password to be <literal>systemd</literal>
- like this:</para>
+ <literal>passwd.plaintext-password.root</literal>, <literal>passwd.shell.root</literal> and
+ <literal>sysusers.extra</literal> credentials from the service manager. Thus, when invoking a container
+ with an unpopulated <filename>/etc/</filename> for the first time it is possible to configure the root
+ user's password to be <literal>systemd</literal> like this:</para>
<para><programlisting># systemd-nspawn --image=… --set-credential=passwd.hashed-password.root:'$y$j9T$yAuRJu1o5HioZAGDYPU5d.$F64ni6J2y2nNQve90M/p0ZP0ECP/qqzipNyaY9fjGpC' …</programlisting></para>
- <para>Note again that the data specified in these credentials is consulted only when creating an account
+ <para>Note again that the data specified in this credential is consulted only when creating an account
for the first time, it may not be used for changing the password or shell of an account that already
exists.</para>
<programlisting>systemd-tmpfiles --remove --create</programlisting>
</refsect1>
+ <refsect1>
+ <title>Credentials</title>
+
+ <para><command>systemd-tmpfiles</command> supports the service credentials logic as implemented by
+ <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ details). The following credentials are used when passed in:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>tmpfiles.extra</literal></term>
+
+ <listitem><para> The contents of this credential may contain additional lines to operate on. The
+ credential contents should follow the same format as any other <filename>tmpfiles.d/</filename>
+ drop-in configuration file. If this credential is passed it is processed after all of the drop-in
+ files read from the file system. The lines in the credential can hence augment existing lines of the
+ OS, but not override them.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Note that by default the <filename>systemd-tmpfiles-setup.service</filename> unit file (and related
+ unit files) is set up to inherit the <literal>tmpfiles.extra</literal> credential from the service
+ manager.</para>
+ </refsect1>
+
<refsect1>
<title>Environment</title>
<listitem><para>Set soft and hard limits on various resources for executed processes. See
<citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
- details on the resource limit concept. Resource limits may be specified in two formats: either as
- single value to set a specific soft and hard limit to the same value, or as colon-separated pair
- <option>soft:hard</option> to set both limits individually (e.g. <literal>LimitAS=4G:16G</literal>).
- Use the string <option>infinity</option> to configure no limit on a specific resource. The
- multiplicative suffixes K, M, G, T, P and E (to the base 1024) may be used for resource limits
- measured in bytes (e.g. <literal>LimitAS=16G</literal>). For the limits referring to time values, the
- usual time units ms, s, min, h and so on may be used (see
+ details on the process resource limit concept. Process resource limits may be specified in two formats:
+ either as single value to set a specific soft and hard limit to the same value, or as colon-separated
+ pair <option>soft:hard</option> to set both limits individually
+ (e.g. <literal>LimitAS=4G:16G</literal>). Use the string <option>infinity</option> to configure no
+ limit on a specific resource. The multiplicative suffixes K, M, G, T, P and E (to the base 1024) may
+ be used for resource limits measured in bytes (e.g. <literal>LimitAS=16G</literal>). For the limits
+ referring to time values, the usual time units ms, s, min, h and so on may be used (see
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
details). Note that if no time unit is specified for <varname>LimitCPU=</varname> the default unit of
seconds is implied, while for <varname>LimitRTTIME=</varname> the default unit of microseconds is
<table>
<title>Resource limit directives, their equivalent <command>ulimit</command> shell commands and the unit used</title>
- <tgroup cols='3'>
+ <tgroup cols='4'>
<colspec colname='directive' />
<colspec colname='equivalent' />
<colspec colname='unit' />
+ <colspec colname='notes' />
<thead>
<row>
<entry>Directive</entry>
<entry><command>ulimit</command> equivalent</entry>
<entry>Unit</entry>
+ <entry>Notes</entry>
</row>
</thead>
<tbody>
<entry>LimitCPU=</entry>
<entry>ulimit -t</entry>
<entry>Seconds</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitFSIZE=</entry>
<entry>ulimit -f</entry>
<entry>Bytes</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitDATA=</entry>
<entry>ulimit -d</entry>
<entry>Bytes</entry>
+ <entry>Don't use. This limits the allowed address range, not memory use! Defaults to unlimited and should not be lowered. To limit memory use, see <varname>MemoryMax=</varname> in <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
</row>
<row>
<entry>LimitSTACK=</entry>
<entry>ulimit -s</entry>
<entry>Bytes</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitCORE=</entry>
<entry>ulimit -c</entry>
<entry>Bytes</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitRSS=</entry>
<entry>ulimit -m</entry>
<entry>Bytes</entry>
+ <entry>Don't use. No effect on Linux.</entry>
</row>
<row>
<entry>LimitNOFILE=</entry>
<entry>ulimit -n</entry>
<entry>Number of File Descriptors</entry>
+ <entry>Don't use. Be careful when raising the soft limit above 1024, since <function>select()</function> cannot function with file descriptors above 1023 on Linux. Nowadays, the hard limit defaults to 524288, a very high value compared to historical defaults. Typically applications should increase their soft limit to the hard limit on their own, if they are OK with working with file descriptors above 1023, i.e. do not use <function>select()</function>. Note that file descriptors are nowadays accounted like any other form of memory, thus there should not be any need to lower the hard limit. Use <varname>MemoryMax=</varname> to control overall service memory use, including file descriptor memory.</entry>
</row>
<row>
<entry>LimitAS=</entry>
<entry>ulimit -v</entry>
<entry>Bytes</entry>
+ <entry>Don't use. This limits the allowed address range, not memory use! Defaults to unlimited and should not be lowered. To limit memory use, see <varname>MemoryMax=</varname> in <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
</row>
<row>
<entry>LimitNPROC=</entry>
<entry>ulimit -u</entry>
<entry>Number of Processes</entry>
+ <entry>This limit is enforced based on the number of processes belonging to the user. Typically it's better to track processes per service, i.e. use <varname>TasksMax=</varname>, see <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
</row>
<row>
<entry>LimitMEMLOCK=</entry>
<entry>ulimit -l</entry>
<entry>Bytes</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitLOCKS=</entry>
<entry>ulimit -x</entry>
<entry>Number of Locks</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitSIGPENDING=</entry>
<entry>ulimit -i</entry>
<entry>Number of Queued Signals</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitMSGQUEUE=</entry>
<entry>ulimit -q</entry>
<entry>Bytes</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitNICE=</entry>
<entry>ulimit -e</entry>
<entry>Nice Level</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitRTPRIO=</entry>
<entry>ulimit -r</entry>
<entry>Realtime Priority</entry>
+ <entry>-</entry>
</row>
<row>
<entry>LimitRTTIME=</entry>
- <entry>No equivalent</entry>
+ <entry>ulimit -R</entry>
<entry>Microseconds</entry>
+ <entry>-</entry>
</row>
</tbody>
</tgroup>
writing text to stderr will not work. To mitigate this use the construct <command>echo "hello"
>&2</command> instead, which is mostly equivalent and avoids this pitfall.</para>
- <para>This setting defaults to the value set with <varname>DefaultStandardOutput=</varname> in
+ <para>If <varname>StandardInput=</varname> is set to one of <option>tty</option>, <option>tty-force</option>,
+ <option>tty-fail</option>, <option>socket</option>, or <option>fd:<replaceable>name</replaceable></option>, this
+ setting defaults to <option>inherit</option>.</para>
+
+ <para>In other cases, this setting defaults to the value set with <varname>DefaultStandardOutput=</varname> in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, which
defaults to <option>journal</option>. Note that setting this parameter might result in additional dependencies
to be added to the unit (see above).</para></listitem>
<para>The service manager itself may receive system credentials that can be propagated to services
from a hosting container manager or VM hypervisor. See the <ulink
url="https://systemd.io/CONTAINER_INTERFACE">Container Interface</ulink> documentation for details
- about the former. For the latter, use the <command>qemu</command> <literal>fw_cfg</literal> node
+ about the former. For the latter, pass <ulink
+ url="https://www.dmtf.org/standards/smbios">DMI/SMBIOS</ulink> OEM string table entries (field type
+ 11) with a prefix of <literal>io.systemd.credential:</literal> or
+ <literal>io.systemd.credential.binary:</literal>. In both cases a key/value pair separated by
+ <literal>=</literal> is expected, in the latter case the right-hand side is Base64 decoded when
+ parsed (thus permitting binary data to be passed in). Example qemu switch: <literal>-smbios
+ type=11,value=io.systemd.credential:xx=yy</literal>, or <literal>-smbios
+ type=11,value=io.systemd.credential.binary:rick=TmV2ZXIgR29ubmEgR2l2ZSBZb3UgVXA=</literal>. Alternatively,
+ use the <command>qemu</command> <literal>fw_cfg</literal> node
<literal>opt/io.systemd.credentials/</literal>. Example qemu switch: <literal>-fw_cfg
name=opt/io.systemd.credentials/mycred,string=supersecret</literal>. They may also be specified on
the kernel command line using the <literal>systemd.set_credential=</literal> switch (see
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>)
- and from the UEFI firmware environment via
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>) and from
+ the UEFI firmware environment via
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
<para>If referencing an <constant>AF_UNIX</constant> stream socket to connect to, the connection will
<term><varname>$MONITOR_INVOCATION_ID</varname></term>
<term><varname>$MONITOR_UNIT</varname></term>
- <listitem><para>Only defined for the service unit type. Those environment variable are passed to
+ <listitem><para>Only defined for the service unit type. Those environment variables are passed to
all <varname>ExecStart=</varname> and <varname>ExecStartPre=</varname> processes which run in
services triggered by <varname>OnFailure=</varname> or <varname>OnSuccess=</varname> dependencies.
</para>
and <varname>$MONITOR_EXIT_STATUS</varname> take the same values as for
<varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes. Variables
<varname>$MONITOR_INVOCATION_ID</varname> and <varname>$MONITOR_UNIT</varname> are set to the
- invocaton id and unit name of the service which triggered the dependency.</para>
+ invocation id and unit name of the service which triggered the dependency.</para>
<para>Note that when multiple services trigger the same unit, those variables will be
<emphasis>not</emphasis> be passed. Consider using a template handler unit for that case instead:
<para>Matches against the hostname or machine ID of the host. See <varname>ConditionHost=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
+ If an empty string is assigned, the previously assigned value is cleared.
</para>
</listitem>
</varlistentry>
whether it is a specific implementation. See <varname>ConditionVirtualization=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
+ If an empty string is assigned, the previously assigned value is cleared.
</para>
</listitem>
</varlistentry>
<varname>ConditionKernelCommandLine=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
+ If an empty string is assigned, the previously assigned value is cleared.
</para>
</listitem>
</varlistentry>
expression. See <varname>ConditionKernelVersion=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
+ If an empty string is assigned, the previously assigned value is cleared.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id='credential'>
+ <term><varname>Credential=</varname></term>
+ <listitem>
+ <para>Checks whether the specified credential was passed to the
+ <filename>systemd-networkd.service</filename> service. See <ulink
+ url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for details. When
+ prefixed with an exclamation mark (<literal>!</literal>), the result is negated. If an empty
+ string is assigned, the previously assigned value is cleared.
</para>
</listitem>
</varlistentry>
<varname>ConditionArchitecture=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
+ If an empty string is assigned, the previously assigned value is cleared.
</para>
</listitem>
</varlistentry>
<varname>ConditionFirmware=</varname> in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. When prefixed with an exclamation mark (<literal>!</literal>), the result is negated.
- If an empty string is assigned, then previously assigned value is cleared.
+ If an empty string is assigned, the previously assigned value is cleared.
</para>
</listitem>
</varlistentry>
<refsect1>
<title><filename>fstab</filename></title>
- <para>Mount units may either be configured via unit files, or via
- <filename>/etc/fstab</filename> (see
+ <para>Mount units may either be configured via unit files, or via <filename>/etc/fstab</filename> (see
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for details). Mounts listed in <filename>/etc/fstab</filename>
- will be converted into native units dynamically at boot and when
- the configuration of the system manager is reloaded. In general,
- configuring mount points through <filename>/etc/fstab</filename>
- is the preferred approach. See
- <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- for details about the conversion.</para>
+ for details). Mounts listed in <filename>/etc/fstab</filename> will be converted into native units
+ dynamically at boot and when the configuration of the system manager is reloaded. In general, configuring
+ mount points through <filename>/etc/fstab</filename> is the preferred approach to manage mounts for
+ humans. For tooling, writing mount units should be preferred over editing <filename>/etc/fstab</filename>.
+ See <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ for details about the conversion from <filename>/etc/fstab</filename> to mount units.</para>
<para>The NFS mount option <option>bg</option> for NFS background mounts
as documented in <citerefentry project='man-pages'><refentrytitle>nfs</refentrytitle><manvolnum>5</manvolnum></citerefentry>
<variablelist>
<varlistentry>
<term><varname>ID_NET_NAME_ONBOARD=<replaceable>prefix</replaceable><constant>o</constant><replaceable>number</replaceable></varname></term>
+ <term><varname>ID_NET_NAME_ONBOARD=<replaceable>prefix</replaceable><constant>d</constant><replaceable>number</replaceable></varname></term>
<listitem><para>This name is set based on the numeric ordering information given by the firmware
- for on-board devices. The name consists of the prefix, letter <constant>o</constant>, and a number
- specified by the firmware. This is only available for PCI devices.</para>
+ for on-board devices. Different schemes are used depending on the firmware type, as described in the table below.</para>
+
+ <table>
+ <title>Onboard naming schemes</title>
+
+ <tgroup cols='2'>
+ <thead>
+ <row>
+ <entry>Format</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><replaceable>prefix</replaceable><constant>o</constant><replaceable>number</replaceable></entry>
+ <entry>PCI onboard index</entry>
+ </row>
+
+ <row>
+ <entry><replaceable>prefix</replaceable><constant>d</constant><replaceable>number</replaceable></entry>
+ <entry>Devicetree alias index</entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
</listitem>
</varlistentry>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><constant>v252</constant></term>
+
+ <listitem><para>Added naming scheme for platform devices with devicetree aliases.</para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
<para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
<xi:include href="systemd.link.xml" xpointer="virtualization" />
<xi:include href="systemd.link.xml" xpointer="kernel-command-line" />
<xi:include href="systemd.link.xml" xpointer="kernel-version" />
+ <xi:include href="systemd.link.xml" xpointer="credential" />
<xi:include href="systemd.link.xml" xpointer="architecture" />
<xi:include href="systemd.link.xml" xpointer="firmware" />
</variablelist>
<filename>/dev/net/tun</filename> device.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>KeepCarrier=</varname></term>
+ <listitem>
+ <para>Takes a boolean. If enabled, to make the interface maintain its carrier status, the file
+ descriptor of the interface is kept open. This may be useful to keep the interface in running
+ state, for example while the backing process is temporarily shutdown. Defaults to
+ <literal>no</literal>.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<term><varname>PhysicalDevice=</varname></term>
<listitem>
<para>Specifies the name or index of the physical WLAN device (e.g. <literal>0</literal> or
- <literal>phy0</literal>). The list of the physical WLAN devices that exist os the host can be
+ <literal>phy0</literal>). The list of the physical WLAN devices that exist on the host can be
obtained by <command>iw phy</command> command. This option is mandatory.</para>
</listitem>
</varlistentry>
<xi:include href="systemd.link.xml" xpointer="virtualization" />
<xi:include href="systemd.link.xml" xpointer="kernel-command-line" />
<xi:include href="systemd.link.xml" xpointer="kernel-version" />
+ <xi:include href="systemd.link.xml" xpointer="credential" />
<xi:include href="systemd.link.xml" xpointer="architecture" />
<xi:include href="systemd.link.xml" xpointer="firmware" />
</variablelist>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>IPv4LLStartAddress=</varname></term>
+ <listitem>
+ <para>Specifies the first IPv4 link-local address to try. Takes an IPv4 address for example
+ 169.254.1.2, from the link-local address range: 169.254.0.0/16 except for 169.254.0.0/24 and
+ 169.254.255.0/24. This setting may be useful if the device should always have the same address
+ as long as there is no address conflict. When unset, a random address will be automatically
+ selected. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>IPv4LLRoute=</varname></term>
<listitem>
IGMP snooping since the switch would not replicate multicast packets on ports that did not
have IGMP reports for the multicast addresses. Linux vxlan interfaces created via
<command>ip link add vxlan</command> or networkd's netdev kind vxlan have the group option
- that enables then to do the required join. By extending ip address command with option
- <literal>autojoin</literal> we can get similar functionality for openvswitch (OVS) vxlan
- interfaces as well as other tunneling mechanisms that need to receive multicast traffic.
+ that enables them to do the required join. By extending <command>ip address</command> command
+ with option <literal>autojoin</literal> we can get similar functionality for openvswitch (OVS)
+ vxlan interfaces as well as other tunneling mechanisms that need to receive multicast traffic.
Defaults to <literal>no</literal>.</para>
</listitem>
</varlistentry>
<para>For IPv4 route, defaults to <literal>host</literal> if <varname>Type=</varname> is
<literal>local</literal> or <literal>nat</literal>, and <literal>link</literal> if
<varname>Type=</varname> is <literal>broadcast</literal>, <literal>multicast</literal>,
- <literal>anycast</literal>, or direct <literal>unicast</literal> routes. In other cases,
+ <literal>anycast</literal>, or <literal>unicast</literal>. In other cases,
defaults to <literal>global</literal>. The value is not used for IPv6.</para>
</listitem>
</varlistentry>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>RapidCommit=</varname></term>
+ <listitem>
+ <para>Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server
+ through a rapid two-message exchange (solicit and reply). When the rapid commit option is set by
+ both the DHCPv6 client and the DHCPv6 server, the two-message exchange is used. Otherwise, the
+ four-message exchange (solicit, advertise, request, and reply) is used. The two-message exchange
+ provides faster client configuration. See
+ <ulink url="https://tools.ietf.org/html/rfc3315#section-17.2.1">RFC 3315</ulink> for details.
+ Defaults to true, and the two-message exchange will be used if the server support it.</para>
+ </listitem>
+ </varlistentry>
+
<!-- How to use the DHCP lease -->
<varlistentry>
<varlistentry>
<term><varname>ServerAddress=</varname></term>
<listitem><para>Specifies server address for the DHCP server. Takes an IPv4 address with prefix
- length, for example <literal>192.168.0.1/24</literal>. This setting may be useful when the link on
+ length, for example 192.168.0.1/24. This setting may be useful when the link on
which the DHCP server is running has multiple static addresses. When unset, one of static addresses
in the link will be automatically selected. Defaults to unset.</para></listitem>
</varlistentry>
<listitem><para>The IPv6 route that is to be distributed to hosts. Similarly to configuring static
IPv6 routes, the setting is configured as an IPv6 prefix routes and its prefix route length,
- separated by a <literal>/</literal> character. Use multiple [IPv6PrefixRoutes] sections to configure
+ separated by a <literal>/</literal> character. Use multiple [IPv6RoutePrefix] sections to configure
multiple IPv6 prefix routes.</para></listitem>
</varlistentry>
<term><varname>StartupCPUWeight=<replaceable>weight</replaceable></varname></term>
<listitem>
- <para>Assign the specified CPU time weight to the processes executed, if the unified control group
- hierarchy is used on the system. These options take an integer value and control the
- <literal>cpu.weight</literal> control group attribute. The allowed range is 1 to 10000. Defaults to
- 100. For details about this control group attribute, see <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v2.html">Control Groups v2</ulink>
- and <ulink url="https://docs.kernel.org/scheduler/sched-design-CFS.html">CFS
- Scheduler</ulink>. The available CPU time is split up among all units within one slice relative to
- their CPU time weight. A higher weight means more CPU time, a lower weight means less.</para>
+ <para>These options accept an integer value or a the special string "idle":</para>
+ <itemizedlist>
+ <listitem>
+ <para>If set to an integer value, assign the specified CPU time weight to the processes executed,
+ if the unified control group hierarchy is used on the system. These options control the
+ <literal>cpu.weight</literal> control group attribute. The allowed range is 1 to 10000. Defaults to
+ 100. For details about this control group attribute, see <ulink
+ url="https://docs.kernel.org/admin-guide/cgroup-v2.html">Control Groups v2</ulink>
+ and <ulink url="https://docs.kernel.org/scheduler/sched-design-CFS.html">CFS
+ Scheduler</ulink>. The available CPU time is split up among all units within one slice relative to
+ their CPU time weight. A higher weight means more CPU time, a lower weight means less.</para>
+ </listitem>
+ <listitem>
+ <para>If set to the special string "idle", mark the cgroup for "idle scheduling", which means
+ that it will get CPU resources only when there are no processes not marked in this way to execute in this
+ cgroup or its siblings. This setting corresponds to the <literal>cpu.idle</literal> cgroup attribute.</para>
+
+ <para>Note that this value only has an effect on cgroup-v2, for cgroup-v1 it is equivalent to the minimum weight.</para>
+ </listitem>
+ </itemizedlist>
<para>While <varname>StartupCPUWeight=</varname> applies to the startup and shutdown phases of the system,
<varname>CPUWeight=</varname> applies to normal runtime of the system, and if the former is not set also to
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ConditionCredential=</varname></term>
+
+ <listitem><para><varname>ConditionCredential=</varname> may be used to check whether a credential
+ by the specified name was passed into the service manager. See <ulink
+ url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for details about
+ credentials. If used in services for the system service manager this may be used to conditionalize
+ services based on system credentials passed in. If used in services for the per-user service
+ manager this may be used to conditionalize services based on credentials passed into the
+ <filename>unit@.service</filename> service instance belonging to the user. The argument must be a
+ valid credential name.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>ConditionEnvironment=</varname></term>
<term><varname>systemd.import_credentials=</varname></term>
<listitem><para>Takes a boolean argument. If false disables importing credentials from the kernel
- command line, qemu_fw_cfg subsystem or the kernel command line.</para></listitem>
+ command line, the DMI/SMBIOS OEM string table, the qemu_fw_cfg subsystem or the EFI kernel
+ stub.</para></listitem>
</varlistentry>
<varlistentry>
<listitem><para>Similarly, a file <literal>https://download.example.com/foobarOS_47.verity.xz</literal>
should be downloaded, decompressed and written to a previously empty partition with GPT partition type
- UUID of 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 (i.e the partition type for Verity integrity information
+ UUID of 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 (i.e. the partition type for Verity integrity information
for x86-64 root file systems).</para></listitem>
<listitem><para>Finally, a file <literal>https://download.example.com/foobarOS_47.efi.xz</literal> (a
<itemizedlist>
<listitem><para>For partitions: the surrounding GPT partition table contains a list of defined
partitions, including a partition type UUID and a partition label (in this scheme the partition label
- plays a role for the partition similar to the filename for a regular file)</para></listitem>
+ plays a role for the partition similar to the filename for a regular file).</para></listitem>
<listitem><para>For regular files: the directory listing of the directory the files are contained in
provides a list of existing files in a straightforward way.</para></listitem>
<entry><literal>@r</literal></entry>
<entry>Read-only flag</entry>
<entry>Either <literal>0</literal> or <literal>1</literal></entry>
- <entry>Controls ReadOnly bit of the GPT partition flags, as per <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink> and other output read-only flags, see <varname>ReadOnly=</varname> below.</entry>
+ <entry>Controls ReadOnly bit of the GPT partition flags, as per <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink> and other output read-only flags, see <varname>ReadOnly=</varname> below</entry>
</row>
<row>
<entry><literal>@l</literal></entry>
<entry>Tries left</entry>
<entry>Formatted decimal integer</entry>
- <entry>Useful when operating with kernel images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot Assessment</ulink></entry>
+ <entry>Useful when operating with kernel image files, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot Assessment</ulink></entry>
</row>
<row>
<entry><literal>@h</literal></entry>
<entry>SHA256 hash of compressed file</entry>
<entry>64 hexadecimal characters</entry>
- <entry>The SHA256 hash of the compressed file; not useful for <constant>url-file</constant> or <constant>url-tar</constant> where the SHA256 hash is already included in the manifest file anyway.</entry>
+ <entry>The SHA256 hash of the compressed file; not useful for <constant>url-file</constant> or <constant>url-tar</constant> where the SHA256 hash is already included in the manifest file anyway</entry>
</row>
</tbody>
</tgroup>
<refsect1>
<title>[Transfer] Section Options</title>
- <para>This section defines general properties of this transfer.</para>
+ <para>This section defines general properties of this transfer:</para>
<variablelist>
<varlistentry>
<listitem><para>Specifies a file system path where to look for already installed versions or place
newly downloaded versions of this configured resource. If <varname>Type=</varname> is set to
<constant>partition</constant>, expects a path to a (whole) block device node, or the special string
- <literal>auto</literal> in which case the block device the root file system of the currently booted
- system is automatically determined and used. If <varname>Type=</varname> is set to
+ <literal>auto</literal> in which case the block device which contains the root file system of the
+ currently booted system is automatically determined and used. If <varname>Type=</varname> is set to
<constant>regular-file</constant>, <constant>directory</constant> or <constant>subvolume</constant>,
must refer to a path in the local file system referencing the directory to find or place the version
files or directories under.</para>
MatchPattern=foobarOS_@v.efi.xz
[Target]
-Type=file
+Type=regular-file
Path=/efi/EFI/Linux
MatchPattern=foobarOS_@v+@l-@d.efi \
foobarOS_@v+@l.efi \
<para>The above installs a unified kernel image into the ESP (which is mounted to
<filename>/efi/</filename>), as per <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
Loader Specification</ulink> Type #2. This defines three possible patterns for the names of the
- kernel images images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot
+ kernel images, as per <ulink url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT">Automatic Boot
Assessment</ulink>, and ensures when installing new kernels, they are set up with 3 tries left. No
more than two parallel kernels are kept.</para>
<refsect2>
<title>Type</title>
- <para>The type consists of a single letter and optionally an exclamation mark (<literal>!</literal>)
- minus sign (<literal>-</literal>), and/or equals sign (<literal>=</literal>).</para>
+ <para>The type consists of a single letter and optionally one or emore modifier characters: a plus sign
+ (<literal>+</literal>), exclamation mark (<literal>!</literal>), minus sign (<literal>-</literal>),
+ equals sign (<literal>=</literal>), tilde character (<literal>~</literal>) and/or caret
+ (<literal>^</literal>).</para>
<para>The following line types are understood:</para>
exists and is not empty. Instead, the entire copy operation is
skipped. If the argument is omitted, files from the source directory
<filename>/usr/share/factory/</filename> with the same name
- are copied. Does not follow symlinks. Contents of the directories
+ are copied. Does not follow symlinks. Contents of the directories
are subject to time based cleanup if the age argument is specified.
</para></listitem>
</varlistentry>
symlinks.</para></listitem>
</varlistentry>
</variablelist>
+ </refsect2>
+
+ <refsect2>
+ <title>Type Modifiers</title>
<para>If the exclamation mark (<literal>!</literal>) is used, this line is only safe to execute during
boot, and can break a running system. Lines without the exclamation mark are presumed to be safe to
be either directories or directory symlinks). For example, if there is a FIFO in place of one of the parent path
components it will be replaced with a directory.</para>
+ <para>If the tilde character (<literal>~</literal>) is used, the argument (i.e. 6th) column is <ulink
+ url="https://www.rfc-editor.org/rfc/rfc4648.html">Base64 decoded</ulink> before use. This modifier is
+ only supported on line types that can write file contents, i.e. <varname>f</varname>,
+ <varname>f+</varname>, <varname>w</varname>, <varname>+</varname>. This is useful for writing arbitrary
+ binary data (including newlines and NUL bytes) to files. Note that if this switch is used, the argument
+ is not subject to specifier expansion, neither before nor after Base64 decoding.</para>
+
+ <para>If the caret character (<literal>^</literal>) is used, the argument (i.e. 6th) column takes a
+ service credential name to read the argument data from. See <ulink
+ url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for details about the
+ credentials concept. This modifier is only supported on line types that can write file contents,
+ i.e. <varname>f</varname>, <varname>f+</varname>, <varname>w</varname>, <varname>w+</varname>. This is
+ useful for writing arbitrary files with contents sourced from elsewhere, including from VM or container
+ managers further up. If the specified credential is not set for the <command>systemd-tmpfiles</command>
+ service, the line is silently skipped. If <literal>^</literal> and <literal>~</literal> are combined
+ Base64 decoding is applied to the credential contents.</para>
+
<para>Note that for all line types that result in creation of any kind of file node
(i.e. <varname>f</varname>/<varname>F</varname>,
<varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
<para><command>udevadm lock</command> takes an (advisory) exclusive lock(s) on a block device (or
multiple thereof), as per <ulink url="https://systemd.io/BLOCK_DEVICE_LOCKING">Locking Block Device
Access</ulink> and invokes a program with the lock(s) taken. When the invoked program exits the lock(s)
- are automatically released.</para>
+ are automatically released and its return value is propagated as exit code of <command>udevadm
+ lock</command>.</para>
<para>This tool is in particular useful to ensure that
<citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
conf.set10('FIRST_BOOT_FULL_PRESET', get_option('first-boot-full-preset'))
+conf.set10('EFI_TPM_PCR_COMPAT', get_option('efi-tpm-pcr-compat'))
+
#####################################################################
cc = meson.get_compiler('c')
c_args = get_option('c_args')
+# Our json library does not support -ffinite-math-only, which is enabled by -Ofast or -ffast-math.
+if (('-Ofast' in c_args or '-ffast-math' in c_args or '-ffinite-math-only' in c_args) and '-fno-finite-math-only' not in c_args)
+ error('-Ofast, -ffast-math, or -ffinite-math-only is specified in c_args.')
+endif
+
# Disable -Wmaybe-uninitialized when compiling with -Os/-O1/-O3/etc. There are
# too many false positives with gcc >= 8. Effectively, we only test with -O0
# and -O2; this should be enough to catch most important cases without too much
#include <uchar.h>
#include <sys/mount.h>
#include <sys/stat.h>
-#include <linux/fs.h>
'''
foreach decl : ['char16_t',
# We get -1 if the size cannot be determined
have = cc.sizeof(decl, prefix : decl_headers, args : '-D_GNU_SOURCE') > 0
+ if decl == 'struct mount_attr'
+ if have
+ want_linux_fs_h = false
+ else
+ have = cc.sizeof(decl,
+ prefix : decl_headers + '#include <linux/fs.h>',
+ args : '-D_GNU_SOURCE') > 0
+ want_linux_fs_h = have
+ endif
+ endif
+
if decl == 'struct statx'
if have
want_linux_stat_h = false
endforeach
conf.set10('WANT_LINUX_STAT_H', want_linux_stat_h)
+conf.set10('WANT_LINUX_FS_H', want_linux_fs_h)
foreach ident : ['secure_getenv', '__secure_getenv']
conf.set10('HAVE_' + ident.to_upper(), cc.has_function(ident))
endif
time_epoch = get_option('time-epoch')
-if time_epoch == -1
+if time_epoch <= 0
time_epoch = run_command(sh, '-c', 'echo "$SOURCE_DATE_EPOCH"', check : true).stdout().strip()
if time_epoch == '' and git.found() and fs.exists('.git')
# If we're in a git repository, use the creation time of the latest git tag.
time_epoch = run_command(stat, '-c', '%Y', NEWS,
check : true).stdout()
endif
- time_epoch = time_epoch.to_int()
+ time_epoch = time_epoch.strip().to_int()
endif
conf.set('TIME_EPOCH', time_epoch)
['system-alloc-gid-min', 'SYS_GID_MIN', 1],
['system-gid-max', 'SYS_GID_MAX', 999]]
v = get_option(tuple[0])
- if v == -1
+ if v <= 0
v = run_command(
awk,
'/^\s*@0@\s+/ { uid=$2 } END { print uid }'.format(tuple[1]),
endif
id_result = run_command('id', '-u', nobody_user, check : false)
if id_result.returncode() == 0
- id = id_result.stdout().to_int()
+ id = id_result.stdout().strip().to_int()
if id != 65534
warning('\n' +
'The local user with the configured user name "@0@" of the nobody user does not have UID 65534 (it has @1@).\n'.format(nobody_user, id) +
endif
id_result = run_command('id', '-g', nobody_group, check : false)
if id_result.returncode() == 0
- id = id_result.stdout().to_int()
+ id = id_result.stdout().strip().to_int()
if id != 65534
warning('\n' +
'The local group with the configured group name "@0@" of the nobody group does not have GID 65534 (it has @1@).\n'.format(nobody_group, id) +
val = get_option(option)
# Ensure provided GID argument is numeric, otherwise fall back to default assignment
- conf.set(name, val >= 0 ? val : '-')
- if val >= 0
+ conf.set(name, val > 0 ? val : '-')
+ if val > 0
static_ugids += '@0@:@1@'.format(option, val)
endif
endforeach
dependencies : [versiondep,
libseccomp],
install_rpath : rootpkglibdir,
- install : conf.get('ENABLE_ANALYZE'))
+ install : conf.get('ENABLE_ANALYZE') == 1)
public_programs += exe
if want_tests != 'false'
install_rpath : rootpkglibdir,
install : true)
+if get_option('link-journalctl-shared')
+ journalctl_link_with = [libshared]
+else
+ journalctl_link_with = [libsystemd_static,
+ libshared_static,
+ libbasic_gcrypt]
+endif
+
public_programs += executable(
'journalctl',
journalctl_sources,
include_directories : includes,
- link_with : [libshared],
+ link_with : [journalctl_link_with],
dependencies : [threads,
libdl,
libxz,
install_rpath : rootpkglibdir,
install : true,
install_dir : systemgeneratordir)
+
+ if conf.get('HAVE_OPENSSL') == 1
+ executable(
+ 'systemd-measure',
+ 'src/boot/measure.c',
+ include_directories : includes,
+ link_with : [libshared],
+ dependencies : [libopenssl],
+ install_rpath : rootpkglibdir,
+ install : true,
+ install_dir : rootlibexecdir)
+ endif
endif
executable(
# components
['backlight'],
['binfmt'],
- ['bpf-framework', conf.get('BPF_FRAMEWORK') == 1],
+ ['bpf-framework', conf.get('BPF_FRAMEWORK') == 1],
['coredump'],
['environment.d'],
['efi'],
['resolve'],
['rfkill'],
['sysext'],
- ['systemd-analyze', conf.get('ENABLE_ANALYZE') == 1],
+ ['systemd-analyze', conf.get('ENABLE_ANALYZE') == 1],
['sysupdate'],
['sysusers'],
['timedated'],
['idn'],
['polkit'],
['nscd'],
- ['legacy-pkla', install_polkit_pkla],
+ ['legacy-pkla', install_polkit_pkla],
['kmod'],
['dbus'],
['glib'],
['tpm'],
- ['man pages', want_man],
- ['html pages', want_html],
- ['man page indices', want_man and have_lxml],
+ ['man pages', want_man],
+ ['html pages', want_html],
+ ['man page indices', want_man and have_lxml],
['SysV compat'],
['compat-mutable-uid-boundaries'],
['utmp'],
['ldconfig'],
- ['adm group', get_option('adm-group')],
- ['wheel group', get_option('wheel-group')],
+ ['adm group', get_option('adm-group')],
+ ['wheel group', get_option('wheel-group')],
['gshadow'],
['debug hashmap'],
['debug mmap cache'],
['debug siphash'],
- ['valgrind', conf.get('VALGRIND') == 1],
- ['trace logging', conf.get('LOG_TRACE') == 1],
- ['install tests', install_tests],
- ['link-udev-shared', get_option('link-udev-shared')],
- ['link-systemctl-shared', get_option('link-systemctl-shared')],
- ['link-networkd-shared', get_option('link-networkd-shared')],
- ['link-timesyncd-shared', get_option('link-timesyncd-shared')],
- ['link-boot-shared', get_option('link-boot-shared')],
+ ['valgrind', conf.get('VALGRIND') == 1],
+ ['trace logging', conf.get('LOG_TRACE') == 1],
+ ['install tests', install_tests],
+ ['link-udev-shared', get_option('link-udev-shared')],
+ ['link-systemctl-shared', get_option('link-systemctl-shared')],
+ ['link-networkd-shared', get_option('link-networkd-shared')],
+ ['link-timesyncd-shared', get_option('link-timesyncd-shared')],
+ ['link-journalctl-shared', get_option('link-journalctl-shared')],
+ ['link-boot-shared', get_option('link-boot-shared')],
['first-boot-full-preset'],
['fexecve'],
- ['standalone-binaries', get_option('standalone-binaries')],
- ['coverage', get_option('b_coverage')],
+ ['standalone-binaries', get_option('standalone-binaries')],
+ ['coverage', get_option('b_coverage')],
]
if tuple.length() >= 2
description : 'link systemd-networkd and its helpers to libsystemd-shared.so')
option('link-timesyncd-shared', type: 'boolean',
description : 'link systemd-timesyncd and its helpers to libsystemd-shared.so')
+option('link-journalctl-shared', type: 'boolean',
+ description : 'link journalctl against libsystemd-shared.so')
option('link-boot-shared', type: 'boolean',
description : 'link bootctl and systemd-bless-boot against libsystemd-shared.so')
option('first-boot-full-preset', type: 'boolean', value: false,
option('static-libudev', type : 'combo',
choices : ['false', 'true', 'pic', 'no-pic'],
description : 'install a static library for libudev')
-option('standalone-binaries', type : 'boolean', value : 'false',
+option('standalone-binaries', type : 'boolean', value : false,
description : 'also build standalone versions of supported binaries')
option('sysvinit-path', type : 'string', value : '/etc/init.d',
value : '/etc/rc.local')
option('initrd', type : 'boolean',
description : 'install services for use when running systemd in initrd')
-option('compat-mutable-uid-boundaries', type : 'boolean', value : 'false',
+option('compat-mutable-uid-boundaries', type : 'boolean', value : false,
description : 'look at uid boundaries in /etc/login.defs for compatibility')
option('nscd', type : 'boolean',
description : 'build support for flushing of the nscd caches')
option('status-unit-format-default', type : 'combo',
choices : ['auto', 'description', 'name', 'combined'],
description : 'use unit name or description in messages by default')
-option('time-epoch', type : 'integer', value : '-1',
+option('time-epoch', type : 'integer', value : 0,
description : 'time epoch for time clients')
-option('clock-valid-range-usec-max', type : 'integer', value : '473364000000000', # 15 years
+option('clock-valid-range-usec-max', type : 'integer', value : 473364000000000, # 15 years
description : 'maximum value in microseconds for the difference between RTC and epoch, exceeding which is considered an RTC error')
option('default-user-shell', type : 'string', value : '/bin/bash',
description : 'default interactive shell')
-option('system-alloc-uid-min', type : 'integer', value : '-1',
+option('system-alloc-uid-min', type : 'integer', value : 0,
description : 'minimum system UID used when allocating')
-option('system-alloc-gid-min', type : 'integer', value : '-1',
+option('system-alloc-gid-min', type : 'integer', value : 0,
description : 'minimum system GID used when allocating')
-option('system-uid-max', type : 'integer', value : '-1',
+option('system-uid-max', type : 'integer', value : 0,
description : 'maximum system UID')
-option('system-gid-max', type : 'integer', value : '-1',
+option('system-gid-max', type : 'integer', value : 0,
description : 'maximum system GID')
option('dynamic-uid-min', type : 'integer', value : 0x0000EF00,
description : 'minimum dynamic UID')
option('nobody-group', type : 'string',
description : 'The name of the nobody group (the one with GID 65534)',
value : 'nobody')
-option('adm-gid', type : 'integer', value : '-1',
+option('adm-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "adm" group')
-option('audio-gid', type : 'integer', value : '-1',
+option('audio-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "audio" group')
-option('cdrom-gid', type : 'integer', value : '-1',
+option('cdrom-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "cdrom" group')
-option('dialout-gid', type : 'integer', value : '-1',
+option('dialout-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "dialout" group')
-option('disk-gid', type : 'integer', value : '-1',
+option('disk-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "disk" group')
-option('input-gid', type : 'integer', value : '-1',
+option('input-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "input" group')
-option('kmem-gid', type : 'integer', value : '-1',
+option('kmem-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "kmem" group')
-option('kvm-gid', type : 'integer', value : '-1',
+option('kvm-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "kvm" group')
-option('lp-gid', type : 'integer', value : '-1',
+option('lp-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "lp" group')
-option('render-gid', type : 'integer', value : '-1',
+option('render-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "render" group')
-option('sgx-gid', type : 'integer', value : '-1',
+option('sgx-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "sgx" group')
-option('tape-gid', type : 'integer', value : '-1',
+option('tape-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "tape" group')
option('tty-gid', type : 'integer', value : 5,
description : 'the numeric GID of the "tty" group')
-option('users-gid', type : 'integer', value : '-1',
+option('users-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "users" group')
-option('utmp-gid', type : 'integer', value : '-1',
+option('utmp-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "utmp" group')
-option('video-gid', type : 'integer', value : '-1',
+option('video-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "video" group')
-option('wheel-gid', type : 'integer', value : '-1',
+option('wheel-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the "wheel" group')
-option('systemd-journal-gid', type : 'integer', value : '-1',
+option('systemd-journal-gid', type : 'integer', value : 0,
description : 'soft-static allocation for the systemd-journal group')
-option('systemd-network-uid', type : 'integer', value : '-1',
+option('systemd-network-uid', type : 'integer', value : 0,
description : 'soft-static allocation for the systemd-network user')
-option('systemd-resolve-uid', type : 'integer', value : '-1',
+option('systemd-resolve-uid', type : 'integer', value : 0,
description : 'soft-static allocation for the systemd-resolve user')
-option('systemd-timesync-uid', type : 'integer', value : '-1',
+option('systemd-timesync-uid', type : 'integer', value : 0,
description : 'soft-static allocation for the systemd-timesync user')
option('dev-kvm-mode', type : 'string', value : '0666',
description : 'path to the EFI lib directory')
option('efi-includedir', type : 'string', value : '/usr/include/efi',
description : 'path to the EFI header directory')
-option('efi-tpm-pcr-compat', type : 'boolean', value : 'false',
+option('efi-tpm-pcr-compat', type : 'boolean', value : false,
description : 'Measure kernel command line also into TPM PCR 8 (in addition to 12)')
option('sbat-distro', type : 'string', value : 'auto',
description : 'SBAT distribution ID, e.g. fedora, or auto for autodetection')
option('tests', type : 'combo', choices : ['true', 'unsafe', 'false'],
description : 'enable extra tests with =unsafe')
-option('slow-tests', type : 'boolean', value : 'false',
+option('slow-tests', type : 'boolean', value : false,
description : 'run the slow tests by default')
-option('fuzz-tests', type : 'boolean', value : 'false',
+option('fuzz-tests', type : 'boolean', value : false,
description : 'run the fuzzer regression tests by default (with sanitizers)')
-option('install-tests', type : 'boolean', value : 'false',
+option('install-tests', type : 'boolean', value : false,
description : 'install test executables')
option('log-message-verification', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'do fake printf() calls to verify format strings')
'highlight-cyan', 'highlight-white'],
value : 'green',
description: 'color of the "OK" status message')
-option('urlify', type : 'boolean', value : 'true',
+option('urlify', type : 'boolean', value : true,
description : 'enable pager Hyperlink ANSI sequence support')
-option('fexecve', type : 'boolean', value : 'false',
+option('fexecve', type : 'boolean', value : false,
description : 'use fexecve() to spawn children')
-option('oss-fuzz', type : 'boolean', value : 'false',
+option('oss-fuzz', type : 'boolean', value : false,
description : 'build against oss-fuzz')
-option('llvm-fuzz', type : 'boolean', value : 'false',
+option('llvm-fuzz', type : 'boolean', value : false,
description : 'build against LLVM libFuzzer')
-option('kernel-install', type: 'boolean', value: 'true',
+option('kernel-install', type: 'boolean', value: true,
description : 'install kernel-install and associated files')
-option('analyze', type: 'boolean', value: 'true',
+option('analyze', type: 'boolean', value: true,
description : 'install systemd-analyze')
option('bpf-compiler', type : 'combo', choices : ['clang', 'gcc'],
option('bpf-framework', type : 'combo', choices : ['auto', 'true', 'false'],
description: 'build BPF programs from source code in restricted C')
-option('skip-deps', type : 'boolean', value : 'false',
+option('skip-deps', type : 'boolean', value : false,
description : 'skip optional dependencies')
# This is a build script for OS image generation using mkosi (https://github.com/systemd/mkosi).
# Simply invoke "mkosi" in the project directory to build an OS image.
+ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1:disable_coredump=0:use_madv_dontdump=1
+UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
+
# On Fedora "ld" is (unfortunately — if you ask me) managed via
# "alternatives". Since we'd like to support building images in environments
# with only /usr/ around (e.g. mkosi's UsrOnly=1 option), we have the problem
init_path=$(realpath /sbin/init 2>/dev/null)
if [ -z "$init_path" ] ; then
- rootprefix=""
+ rootprefix=""
else
- rootprefix=${init_path%/lib/systemd/systemd}
- rootprefix=/${rootprefix#/}
+ rootprefix=${init_path%/lib/systemd/systemd}
+ rootprefix=/${rootprefix#/}
fi
meson "$BUILDDIR" \
-D "rootprefix=$rootprefix" \
-D man=false \
-D translations=false \
- -D version-tag="${VERSION_TAG}"
+ -D version-tag="${VERSION_TAG}" \
+ -D mode=developer \
+ -D b_sanitize="${SANITIZERS:-none}"
fi
cd "$BUILDDIR"
getent group $id >/dev/null || groupadd -g $id testgroup$id
done
- ninja test
+ if [ -n "$SANITIZERS" ]; then
+ export ASAN_OPTIONS="$ASAN_OPTIONS"
+ export UBSAN_OPTIONS="$UBSAN_OPTIONS"
+ TIMEOUT_MULTIPLIER=3
+ else
+ TIMEOUT_MULTIPLIER=1
+ fi
+
+ meson test --timeout-multiplier=$TIMEOUT_MULTIPLIER
fi
cd "$SRCDIR"
EOF
if [ -n "$IMAGE_ID" ] ; then
- mkdir -p "$DESTDIR"/usr/lib
- sed -n \
- -e '/^IMAGE_ID=/!p' \
- -e "\$aIMAGE_ID=$IMAGE_ID" <"/usr/lib/os-release" >"${DESTDIR}/usr/lib/os-release"
+ mkdir -p "$DESTDIR"/usr/lib
+ sed -n \
+ -e '/^IMAGE_ID=/!p' \
+ -e "\$aIMAGE_ID=$IMAGE_ID" <"/usr/lib/os-release" >"${DESTDIR}/usr/lib/os-release"
- OSRELEASEFILE="$DESTDIR"/usr/lib/os-release
+ OSRELEASEFILE="$DESTDIR"/usr/lib/os-release
else
- OSRELEASEFILE=/usr/lib/os-release
+ OSRELEASEFILE=/usr/lib/os-release
fi
if [ -n "$IMAGE_VERSION" ] ; then
- mkdir -p "$DESTDIR"/usr/lib
- sed -n \
- -e '/^IMAGE_VERSION=/!p' \
- -e "\$aIMAGE_VERSION=$IMAGE_VERSION" <$OSRELEASEFILE >"/tmp/os-release.tmp"
+ mkdir -p "$DESTDIR"/usr/lib
+ sed -n \
+ -e '/^IMAGE_VERSION=/!p' \
+ -e "\$aIMAGE_VERSION=$IMAGE_VERSION" <$OSRELEASEFILE >"/tmp/os-release.tmp"
- cat /tmp/os-release.tmp > "$DESTDIR"/usr/lib/os-release
- rm /tmp/os-release.tmp
+ cat /tmp/os-release.tmp > "$DESTDIR"/usr/lib/os-release
+ rm /tmp/os-release.tmp
fi
# If $CI_BUILD is set, copy over the CI service which executes a service check
# after boot and then shuts down the machine
if [ -n "$CI_BUILD" ]; then
- mkdir -p "$DESTDIR/usr/lib/systemd/system"
- cp -v "$SRCDIR/test/mkosi-check-and-shutdown.service" "$DESTDIR/usr/lib/systemd/system/mkosi-check-and-shutdown.service"
- cp -v "$SRCDIR/test/mkosi-check-and-shutdown.sh" "$DESTDIR/usr/lib/systemd/mkosi-check-and-shutdown.sh"
- chmod +x "$DESTDIR/usr/lib/systemd/mkosi-check-and-shutdown.sh"
+ mkdir -p "$DESTDIR/usr/lib/systemd/system"
+ cp -v "$SRCDIR/test/mkosi-check-and-shutdown.service" "$DESTDIR/usr/lib/systemd/system/mkosi-check-and-shutdown.service"
+ cp -v "$SRCDIR/test/mkosi-check-and-shutdown.sh" "$DESTDIR/usr/lib/systemd/mkosi-check-and-shutdown.sh"
+ chmod +x "$DESTDIR/usr/lib/systemd/mkosi-check-and-shutdown.sh"
+fi
+
+if [ -n "$SANITIZERS" ]; then
+ LD_PRELOAD=$(ldd $BUILDDIR/systemd | grep libasan.so | awk '{print $3}')
+
+ mkdir -p "$DESTDIR/etc/systemd/system.conf.d"
+
+ cat > "$DESTDIR/etc/systemd/system.conf.d/10-asan.conf" <<EOF
+[Manager]
+ManagerEnvironment=ASAN_OPTIONS=$ASAN_OPTIONS\\
+ UBSAN_OPTIONS=$UBSAN_OPTIONS\\
+ LD_PRELOAD=$LD_PRELOAD
+DefaultEnvironment=ASAN_OPTIONS=$ASAN_OPTIONS\\
+ UBSAN_OPTIONS=$UBSAN_OPTIONS\\
+ LD_PRELOAD=$LD_PRELOAD
+EOF
+
+ # ASAN logs to stderr by default. However, journald's stderr is connected to /dev/null, so we lose
+ # all the ASAN logs. To rectify that, let's connect journald's stdout to the console so that any
+ # sanitizer failures appear directly on the user's console.
+ mkdir -p "$DESTDIR/etc/systemd/system/systemd-journald.service.d"
+
+ cat > "$DESTDIR/etc/systemd/system/systemd-journald.service.d/10-stdout-tty.conf" <<EOF
+[Service]
+StandardOutput=tty
+EOF
+
+ # Both systemd and util-linux's login call vhangup() on /dev/console which disconnects all users.
+ # This means systemd-journald can't log to /dev/console even if we configure `StandardOutput=tty`. As
+ # a workaround, we modify console-getty.service to disable systemd's vhangup() and disallow login
+ # from calling vhangup() so that journald's ASAN logs correctly end up in the console.
+
+ mkdir -p "$DESTDIR/etc/systemd/system/console-getty.service.d"
+
+ cat > "$DESTDIR/etc/systemd/system/console-getty.service.d/10-no-vhangup.conf" <<EOF
+[Service]
+TTYVHangup=no
+CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG
+EOF
fi
Format=gpt_btrfs
Bootable=yes
HostonlyInitrd=yes
+# Prevent ASAN warnings when building the image
+Environment=ASAN_OPTIONS=verify_asan_link_order=false
+OutputDirectory=mkosi.output
-[Packages]
+[Content]
BuildDirectory=mkosi.builddir
Cache=mkosi.cache
-InstallDirectory=mkosi.installdir
SourceFileTransferFinal=copy-git-others
[Host]
QemuHeadless=yes
-NetworkVeth=yes
+Netdev=yes
+QemuMem=2G
[Validation]
Password=
[Distribution]
Distribution=arch
-[Packages]
+[Content]
BuildPackages=
acl
bzip2
# For testing systemd's zsh completion scripts
# Run `autoload -Uz compinit; compinit` from a zsh shell in the booted image to enable completions.
zsh
+ # xxd is provided by the vim package
+ vim
# Required to run systemd-networkd-tests.py
python
iproute
Format=gpt_xfs
HostonlyInitrd=no
-[Packages]
+[Content]
BuildPackages=
diffutils
docbook-style-xsl
less
netcat
e2fsprogs
+ # xxd is provided by the vim-common package
+ vim-common
+ libasan
+ libubsan
# Required to run systemd-networkd-tests.py
python3
iproute
Distribution=debian
Release=testing
-[Packages]
+[Content]
BuildPackages=
acl
clang
locales
nano
strace
+ xxd
+ # Provides libasan/libubsan
+ gcc
# Required to run systemd-networkd-tests.py
python3
iproute2
Distribution=fedora
Release=36
-[Packages]
+[Content]
BuildPackages=
diffutils
docbook-style-xsl
netcat
e2fsprogs
compsize
+ # xxd is provided by the vim-common package
+ vim-common
+ # Sanitizers
+ libasan
+ libubsan
# Required to run systemd-networkd-tests.py
python
iproute
Distribution=opensuse
Release=tumbleweed
-[Packages]
+[Content]
BuildPackages=
docbook-xsl-stylesheets
fdupes
nano
strace
util-linux
+ # xxd is provided by the vim package
+ vim
+ # Provides libasan/libubsan
+ gcc
[Distribution]
Distribution=ubuntu
-Release=focal
+Release=jammy
Repositories=main,universe
-[Packages]
+[Content]
BuildPackages=
acl
docbook-xml
libqrencode4
# We pull in the -dev package here, since the binary ones appear to change names too often, and the -dev package pulls the right deps in automatically
libtss2-dev
+ libfdisk1
locales
nano
strace
+ xxd
+ # Provides libasan/libubsan
+ gcc
# Required to run systemd-networkd-tests.py
python3
iproute2
#!/bin/sh
# SPDX-License-Identifier: LGPL-2.1-or-later
-if [ "$1" = "final" ] && command -v bootctl > /dev/null; then
- bootctl install
+if [ "$1" = "final" ]; then
+ if command -v bootctl > /dev/null && [ -d "/efi" ]; then
+ bootctl install
+ fi
+
+ cat >> /root/.gdbinit <<EOF
+set debuginfod enabled off
+set build-id-verbose 0
+EOF
+
+ if [ -n "$SANITIZERS" ]; then
+ # ASAN and syscall filters aren't compatible with each other.
+ find / -name '*.service' -type f -exec sed -i 's/^\(MemoryDeny\|SystemCall\)/# \1/' {} +
+
+ # `systemd-hwdb update` takes > 50s when built with sanitizers so let's not run it by default.
+ systemctl mask systemd-hwdb-update.service
+ fi
fi
# Temporary workaround until https://github.com/openSUSE/suse-module-tools/commit/158643414ddb8d8208016a5f03a4484d58944d7a
de
el
es
+et
fi
fr
gl
id
it
ja
+ka
kab
ko
lt
uk
zh_CN
zh_TW
-ka
-et
#
# Gabor Kelemen <kelemeng at gnome dot hu>, 2015, 2016.
# Balázs Úr <urbalazs at gmail dot com>, 2016.
+# Balázs Meskó <meskobalazs@mailbox.org>, 2022.
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2016-08-23 18:03+0100\n"
-"Last-Translator: Balázs Úr <urbalazs@gmail.com>\n"
-"Language-Team: Hungarian <openscope at googlegroups dot com>\n"
+"PO-Revision-Date: 2022-08-09 20:19+0000\n"
+"Last-Translator: Balázs Meskó <meskobalazs@mailbox.org>\n"
+"Language-Team: Hungarian <https://translate.fedoraproject.org/projects/"
+"systemd/master/hu/>\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Lokalize 2.0\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.13\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/home/org.freedesktop.home1.policy:13
msgid "Create a home area"
-msgstr ""
+msgstr "Saját terület létrehozása"
#: src/home/org.freedesktop.home1.policy:14
-#, fuzzy
-#| msgid "Authentication is required to reload the systemd state."
msgid "Authentication is required to create a user's home area."
-msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
+msgstr "Hitelesítés szükséges a felhasználó saját területének létrehozásához."
#: src/home/org.freedesktop.home1.policy:23
msgid "Remove a home area"
-msgstr ""
+msgstr "Saját terület eltávolítása"
#: src/home/org.freedesktop.home1.policy:24
-#, fuzzy
-#| msgid "Authentication is required to reload the systemd state."
msgid "Authentication is required to remove a user's home area."
-msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
+msgstr "Hitelesítés szükséges a felhasználó saját területének eltávolításához."
#: src/home/org.freedesktop.home1.policy:33
msgid "Check credentials of a home area"
-msgstr ""
+msgstr "Saját terület hitelesítő adatainak ellenőrzése"
#: src/home/org.freedesktop.home1.policy:34
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to manage active sessions, users and seats."
msgid ""
"Authentication is required to check credentials against a user's home area."
msgstr ""
-"Hitelesítés szükséges az aktív munkamenetek, felhasználók és munkaállomások "
-"kezeléséhez."
+"Hitelesítés szükséges a hitelesítő adatok a felhasználó saját területével "
+"való összevetéséhez."
#: src/home/org.freedesktop.home1.policy:43
msgid "Update a home area"
-msgstr ""
+msgstr "Saját terület frissítése"
#: src/home/org.freedesktop.home1.policy:44
-#, fuzzy
-#| msgid "Authentication is required to attach a device to a seat."
msgid "Authentication is required to update a user's home area."
-msgstr ""
-"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
-"munkaállomáshoz"
+msgstr "Hitelesítés szükséges a felhasználó saját területének frissítéséhez."
#: src/home/org.freedesktop.home1.policy:53
msgid "Resize a home area"
-msgstr ""
+msgstr "Saját terület átméretezése"
#: src/home/org.freedesktop.home1.policy:54
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to resize a user's home area."
-msgstr "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a felhasználó saját területének átméretezéséhez."
#: src/home/org.freedesktop.home1.policy:63
msgid "Change password of a home area"
-msgstr ""
+msgstr "Saját terület hitelesítő adatainak módosítása"
#: src/home/org.freedesktop.home1.policy:64
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to manage active sessions, users and seats."
msgid ""
"Authentication is required to change the password of a user's home area."
msgstr ""
-"Hitelesítés szükséges az aktív munkamenetek, felhasználók és munkaállomások "
-"kezeléséhez."
+"Hitelesítés szükséges a felhasználó saját területének jelszavának "
+"módosításához."
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set hostname"
#: src/hostname/org.freedesktop.hostname1.policy:42
msgid "Authentication is required to set local machine information."
-msgstr "Hitelesítés szükséges a helyi gép információinak beállításához."
+msgstr "Hitelesítés szükséges a helyi gépinformációk beállításához."
#: src/hostname/org.freedesktop.hostname1.policy:51
msgid "Get product UUID"
-msgstr ""
+msgstr "Termék UUID-jának lekérése"
#: src/hostname/org.freedesktop.hostname1.policy:52
-#, fuzzy
-#| msgid "Authentication is required to reload '$(unit)'."
msgid "Authentication is required to get product UUID."
-msgstr "Hitelesítés szükséges a következő újratöltéséhez: „$(unit)”."
+msgstr "Hitelesítés szükséges a termék UUID-jának lekéréséhez."
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
-msgstr "VM vagy konténer lemezkép importálása"
+msgstr "VM vagy konténer lemezképének importálása"
#: src/import/org.freedesktop.import1.policy:23
msgid "Authentication is required to import a VM or container image"
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép importálásához."
+msgstr "Hitelesítés szükséges a VM vagy konténer lemezképének importálásához"
#: src/import/org.freedesktop.import1.policy:32
msgid "Export a VM or container image"
-msgstr "VM vagy konténer lemezkép exportálása"
+msgstr "VM vagy konténer lemezképének exportálása"
#: src/import/org.freedesktop.import1.policy:33
msgid "Authentication is required to export a VM or container image"
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép exportálásához."
+msgstr "Hitelesítés szükséges a VM vagy konténer lemezképének exportálásához"
#: src/import/org.freedesktop.import1.policy:42
msgid "Download a VM or container image"
-msgstr "VM vagy konténer lemezkép letöltése"
+msgstr "VM vagy konténer lemezképének letöltése"
#: src/import/org.freedesktop.import1.policy:43
msgid "Authentication is required to download a VM or container image"
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép letöltéséhez."
+msgstr "Hitelesítés szükséges a VM vagy konténer lemezképének letöltéséhez"
#: src/locale/org.freedesktop.locale1.policy:22
msgid "Set system locale"
-msgstr "Területi beállítás megadása"
+msgstr "Rendszer területi beállításának megadása"
#: src/locale/org.freedesktop.locale1.policy:23
msgid "Authentication is required to set the system locale."
#: src/locale/org.freedesktop.locale1.policy:33
msgid "Set system keyboard settings"
-msgstr "Rendszer billentyűzetbeállítások megadása"
+msgstr "Rendszer billentyűzetbeállításainak megadása"
#: src/locale/org.freedesktop.locale1.policy:34
msgid "Authentication is required to set the system keyboard settings."
#: src/login/org.freedesktop.login1.policy:22
msgid "Allow applications to inhibit system shutdown"
-msgstr "Alkalmazások meggátolhatják a rendszer leállítását"
+msgstr "Az alkalmazások meggátolhatják a rendszer leállítását"
#: src/login/org.freedesktop.login1.policy:23
msgid ""
#: src/login/org.freedesktop.login1.policy:33
msgid "Allow applications to delay system shutdown"
-msgstr "Alkalmazások késleltethetik a rendszer leállítását"
+msgstr "Az alkalmazások késleltethetik a rendszer leállítását"
#: src/login/org.freedesktop.login1.policy:34
msgid "Authentication is required for an application to delay system shutdown."
#: src/login/org.freedesktop.login1.policy:44
msgid "Allow applications to inhibit system sleep"
-msgstr "Alkalmazások meggátolhatják a rendszer altatását"
+msgstr "Az alkalmazások meggátolhatják a rendszer altatását"
#: src/login/org.freedesktop.login1.policy:45
msgid "Authentication is required for an application to inhibit system sleep."
#: src/login/org.freedesktop.login1.policy:55
msgid "Allow applications to delay system sleep"
-msgstr "Alkalmazások késleltethetik a rendszer altatását"
+msgstr "Az alkalmazások késleltethetik a rendszer altatását"
#: src/login/org.freedesktop.login1.policy:56
msgid "Authentication is required for an application to delay system sleep."
#: src/login/org.freedesktop.login1.policy:65
msgid "Allow applications to inhibit automatic system suspend"
-msgstr "Alkalmazások meggátolhatják a rendszer automatikus felfüggesztését"
+msgstr "Az alkalmazások meggátolhatják a rendszer automatikus felfüggesztését"
#: src/login/org.freedesktop.login1.policy:66
msgid ""
#: src/login/org.freedesktop.login1.policy:75
msgid "Allow applications to inhibit system handling of the power key"
msgstr ""
-"Alkalmazások meggátolhatják a bekapcsoló gomb rendszer általi kezelését"
+"Az alkalmazások meggátolhatják a bekapcsoló gomb rendszer általi kezelését"
#: src/login/org.freedesktop.login1.policy:76
msgid ""
#: src/login/org.freedesktop.login1.policy:86
msgid "Allow applications to inhibit system handling of the suspend key"
msgstr ""
-"Alkalmazások meggátolhatják a felfüggesztés gomb rendszer általi kezelését"
+"Az alkalmazások meggátolhatják a felfüggesztés gomb rendszer általi kezelését"
#: src/login/org.freedesktop.login1.policy:87
msgid ""
#: src/login/org.freedesktop.login1.policy:97
msgid "Allow applications to inhibit system handling of the hibernate key"
msgstr ""
-"Alkalmazások meggátolhatják a hibernálás gomb rendszer általi kezelését"
+"Az alkalmazások meggátolhatják a hibernálás gomb rendszer általi kezelését"
#: src/login/org.freedesktop.login1.policy:98
msgid ""
#: src/login/org.freedesktop.login1.policy:107
msgid "Allow applications to inhibit system handling of the lid switch"
-msgstr "Alkalmazások meggátolhatják a fedélkapcsoló rendszer általi kezelését"
+msgstr ""
+"Az alkalmazások meggátolhatják a fedélkapcsoló rendszer általi kezelését"
#: src/login/org.freedesktop.login1.policy:108
msgid ""
"kezelésének meggátlásához."
#: src/login/org.freedesktop.login1.policy:117
-#, fuzzy
-#| msgid "Allow applications to inhibit system handling of the power key"
msgid "Allow applications to inhibit system handling of the reboot key"
msgstr ""
-"Alkalmazások meggátolhatják a bekapcsoló gomb rendszer általi kezelését"
+"Az alkalmazások meggátolhatják az újraindítás gomb rendszer általi kezelését"
#: src/login/org.freedesktop.login1.policy:118
-#, fuzzy
-#| msgid ""
-#| "Authentication is required for an application to inhibit system handling "
-#| "of the power key."
msgid ""
"Authentication is required for an application to inhibit system handling of "
"the reboot key."
msgstr ""
-"Hitelesítés szükséges egy alkalmazás számára a bekapcsoló gomb rendszer "
+"Hitelesítés szükséges egy alkalmazás számára az újraindítás gomb rendszer "
"általi kezelésének meggátlásához."
#: src/login/org.freedesktop.login1.policy:128
#: src/login/org.freedesktop.login1.policy:129
msgid "Explicit request is required to run programs as a non-logged-in user."
msgstr ""
-"Határozott kérés szükséges a programfuttatáshoz be nem jelentkezett "
-"felhasználóként."
+"Határozott kérés szükséges a be nem jelentkezett felhasználókénti "
+"programfuttatáshoz."
#: src/login/org.freedesktop.login1.policy:138
msgid "Allow non-logged-in users to run programs"
#: src/login/org.freedesktop.login1.policy:139
msgid "Authentication is required to run programs as a non-logged-in user."
msgstr ""
-"Hitelesítés szükséges a programfuttatáshoz be nem jelentkezett "
-"felhasználóként."
+"Hitelesítés szükséges a be nem jelentkezett felhasználókénti "
+"programfuttatáshoz."
#: src/login/org.freedesktop.login1.policy:148
msgid "Allow attaching devices to seats"
-msgstr "Eszközök csatolásának engedélyezése munkaállomásokhoz"
+msgstr "Eszközök munkaállomásokhoz csatolásának engedélyezése"
#: src/login/org.freedesktop.login1.policy:149
msgid "Authentication is required to attach a device to a seat."
msgstr ""
-"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
-"munkaállomáshoz"
+"Hitelesítés szükséges az eszköz munkaállomáshoz csatolásának "
+"engedélyezéséhez."
#: src/login/org.freedesktop.login1.policy:159
msgid "Flush device to seat attachments"
#: src/login/org.freedesktop.login1.policy:213
msgid "Reboot the system while other users are logged in"
-msgstr "A rendszer újraindítása mialatt be vannak jelentkezve más felhasználók"
+msgstr "A rendszer újraindítása miközben be vannak jelentkezve más felhasználók"
#: src/login/org.freedesktop.login1.policy:214
msgid ""
"ennek meggátlását kérte."
#: src/login/org.freedesktop.login1.policy:235
-#, fuzzy
-#| msgid "Hibernate the system"
msgid "Halt the system"
-msgstr "A rendszer hibernálása"
+msgstr "A rendszer leállítása"
#: src/login/org.freedesktop.login1.policy:236
-#, fuzzy
-#| msgid "Authentication is required to hibernate the system."
msgid "Authentication is required to halt the system."
-msgstr "Hitelesítés szükséges a rendszer hibernálásához."
+msgstr "Hitelesítés szükséges a rendszer leállításához."
#: src/login/org.freedesktop.login1.policy:246
-#, fuzzy
-#| msgid "Hibernate the system while other users are logged in"
msgid "Halt the system while other users are logged in"
-msgstr "A rendszer hibernálása mialatt be vannak jelentkezve más felhasználók"
+msgstr "A rendszer leállítása miközben más felhasználók be vannak jelentkezve"
#: src/login/org.freedesktop.login1.policy:247
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to hibernate the system while other users are "
-#| "logged in."
msgid ""
"Authentication is required to halt the system while other users are logged "
"in."
msgstr ""
-"Hitelesítés szükséges a rendszer hibernálásához miközben be vannak "
-"jelentkezve más felhasználók."
+"Hitelesítés szükséges a rendszer leállításához miközben más felhasználók be "
+"vannak jelentkezve."
#: src/login/org.freedesktop.login1.policy:257
-#, fuzzy
-#| msgid "Hibernate the system while an application is inhibiting this"
msgid "Halt the system while an application is inhibiting this"
-msgstr "A rendszer hibernálása miközben egy alkalmazás ennek meggátlását kérte"
+msgstr "A rendszer leállítása miközben egy alkalmazás ennek meggátlását kérte"
#: src/login/org.freedesktop.login1.policy:258
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to hibernate the system while an application "
-#| "is inhibiting this."
msgid ""
"Authentication is required to halt the system while an application is "
"inhibiting this."
msgstr ""
-"Hitelesítés szükséges a rendszer hibernálásához miközben egy alkalmazás "
-"ennek meggátlását kérte."
+"Hitelesítés szükséges a rendszer leállításához miközben egy alkalmazás ennek "
+"meggátlását kérte."
#: src/login/org.freedesktop.login1.policy:268
msgid "Suspend the system"
#: src/login/org.freedesktop.login1.policy:310
msgid "Hibernate the system while other users are logged in"
-msgstr "A rendszer hibernálása mialatt be vannak jelentkezve más felhasználók"
+msgstr "A rendszer hibernálása miközben be vannak jelentkezve más felhasználók"
#: src/login/org.freedesktop.login1.policy:311
msgid ""
#: src/login/org.freedesktop.login1.policy:352
msgid "Set the reboot \"reason\" in the kernel"
-msgstr ""
+msgstr "Az újraindítás „okának” beállítása a kernelben"
#: src/login/org.freedesktop.login1.policy:353
-#, fuzzy
-#| msgid "Authentication is required to set the system timezone."
msgid "Authentication is required to set the reboot \"reason\" in the kernel."
-msgstr "Hitelesítés szükséges a rendszer időzónájának beállításához."
+msgstr ""
+"Hitelesítés szükséges a rendszer újraindítási „okának” beállításához a "
+"kernelben."
#: src/login/org.freedesktop.login1.policy:363
-#, fuzzy
-#| msgid "Allow indication to the firmware to boot to setup interface"
msgid "Indicate to the firmware to boot to setup interface"
-msgstr "A firmware-nek jelezhető, hogy a beállítófelületet bootolja"
+msgstr ""
+"Jelzés a firmware számára, hogy a beállítófelületet indítsa el "
+"rendszerindításkor"
#: src/login/org.freedesktop.login1.policy:364
msgid ""
"Authentication is required to indicate to the firmware to boot to setup "
"interface."
msgstr ""
-"Hitelesítés szükséges a firmware-nek jelzéshez, hogy a beállítófelületet "
-"bootolja"
+"Hitelesítés szükséges, hogy a firmware a beállítófelületet indítsa el "
+"rendszerindításkor."
#: src/login/org.freedesktop.login1.policy:374
msgid "Indicate to the boot loader to boot to the boot loader menu"
msgstr ""
+"Jelzés a rendszerbetöltő számára, hogy a rendszerbetöltő menüt indítsa el "
+"rendszerindításkor"
#: src/login/org.freedesktop.login1.policy:375
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to indicate to the firmware to boot to setup "
-#| "interface."
msgid ""
"Authentication is required to indicate to the boot loader to boot to the "
"boot loader menu."
msgstr ""
-"Hitelesítés szükséges a firmware-nek jelzéshez, hogy a beállítófelületet "
-"bootolja"
+"Hitelesítés szükséges, hogy a firmware a rendszerbetöltő menüt indítsa el "
+"rendszerindításkor."
#: src/login/org.freedesktop.login1.policy:385
msgid "Indicate to the boot loader to boot a specific entry"
msgstr ""
+"Jelzés a rendszerbetöltő számára, hogy egy adott bejegyzést indítson el "
+"rendszerindításkor"
#: src/login/org.freedesktop.login1.policy:386
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to indicate to the firmware to boot to setup "
-#| "interface."
msgid ""
"Authentication is required to indicate to the boot loader to boot into a "
"specific boot loader entry."
msgstr ""
-"Hitelesítés szükséges a firmware-nek jelzéshez, hogy a beállítófelületet "
-"bootolja"
+"Hitelesítés szükséges, hogy a rendszerbetöltő egy adott bejegyzést indítson "
+"el rendszerindításkor."
#: src/login/org.freedesktop.login1.policy:396
msgid "Set a wall message"
#: src/login/org.freedesktop.login1.policy:406
msgid "Change Session"
-msgstr ""
+msgstr "Munkamenet módosítása"
#: src/login/org.freedesktop.login1.policy:407
-#, fuzzy
-#| msgid "Authentication is required to set the local hostname."
msgid "Authentication is required to change the virtual terminal."
-msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr "Hitelesítés szükséges a virtuális terminál módosításához."
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
#: src/machine/org.freedesktop.machine1.policy:23
msgid "Authentication is required to log into a local container."
-msgstr "Hitelesítés szükséges a bejelentkezéshez egy helyi konténerbe."
+msgstr "Hitelesítés szükséges a helyi konténerbe történő bejelentkezéshez."
#: src/machine/org.freedesktop.machine1.policy:32
msgid "Log into the local host"
#: src/machine/org.freedesktop.machine1.policy:33
msgid "Authentication is required to log into the local host."
-msgstr "Hitelesítés szükséges a bejelentkezéshez a helyi gépre."
+msgstr "Hitelesítés szükséges a helyi gépre történtő bejelentkezéshez."
#: src/machine/org.freedesktop.machine1.policy:42
msgid "Acquire a shell in a local container"
#: src/machine/org.freedesktop.machine1.policy:95
msgid "Manage local virtual machine and container images"
-msgstr "Helyi virtuális gép és konténer lemezképek kezelése"
+msgstr "Helyi virtuális gépek és konténerek lemezképeinek kezelése"
#: src/machine/org.freedesktop.machine1.policy:96
msgid ""
"Authentication is required to manage local virtual machine and container "
"images."
msgstr ""
-"Hitelesítés szükséges a helyi virtuális gép és konténer lemezképek "
+"Hitelesítés szükséges a helyi virtuális gépek és konténerek lemezképeinek "
"kezeléséhez."
#: src/network/org.freedesktop.network1.policy:22
msgid "Set NTP servers"
-msgstr ""
+msgstr "NTP-kiszolgálók beállítása"
#: src/network/org.freedesktop.network1.policy:23
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
msgid "Authentication is required to set NTP servers."
-msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges az NTP-kiszolgálók beállításához."
#: src/network/org.freedesktop.network1.policy:33
#: src/resolve/org.freedesktop.resolve1.policy:44
msgid "Set DNS servers"
-msgstr ""
+msgstr "DNS-kiszolgálók beállítása"
#: src/network/org.freedesktop.network1.policy:34
#: src/resolve/org.freedesktop.resolve1.policy:45
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
msgid "Authentication is required to set DNS servers."
-msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges a DNS-kiszolgálók beállításához."
#: src/network/org.freedesktop.network1.policy:44
#: src/resolve/org.freedesktop.resolve1.policy:55
msgid "Set domains"
-msgstr ""
+msgstr "Domainek beállítása"
#: src/network/org.freedesktop.network1.policy:45
#: src/resolve/org.freedesktop.resolve1.policy:56
-#, fuzzy
-#| msgid "Authentication is required to stop '$(unit)'."
msgid "Authentication is required to set domains."
-msgstr "Hitelesítés szükséges a következő leállításához: „$(unit)”."
+msgstr "Hitelesítés szükséges a domainek beállításához."
#: src/network/org.freedesktop.network1.policy:55
#: src/resolve/org.freedesktop.resolve1.policy:66
msgid "Set default route"
-msgstr ""
+msgstr "Alapértelmezett hálózati útvonal beállítása"
#: src/network/org.freedesktop.network1.policy:56
#: src/resolve/org.freedesktop.resolve1.policy:67
-#, fuzzy
-#| msgid "Authentication is required to set the local hostname."
msgid "Authentication is required to set default route."
-msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr ""
+"Hitelesítés szükséges az alapértelmezett hálózati útvonal beállításához."
#: src/network/org.freedesktop.network1.policy:66
#: src/resolve/org.freedesktop.resolve1.policy:77
msgid "Enable/disable LLMNR"
-msgstr ""
+msgstr "LLMNR engedélyezése/letiltása"
#: src/network/org.freedesktop.network1.policy:67
#: src/resolve/org.freedesktop.resolve1.policy:78
-#, fuzzy
-#| msgid "Authentication is required to hibernate the system."
msgid "Authentication is required to enable or disable LLMNR."
-msgstr "Hitelesítés szükséges a rendszer hibernálásához."
+msgstr "Hitelesítés szükséges az LLMNR engedélyezéséhez vagy kikapcsolásához."
#: src/network/org.freedesktop.network1.policy:77
#: src/resolve/org.freedesktop.resolve1.policy:88
msgid "Enable/disable multicast DNS"
-msgstr ""
+msgstr "Multicast DNS engedélyezése/letiltása"
#: src/network/org.freedesktop.network1.policy:78
#: src/resolve/org.freedesktop.resolve1.policy:89
-#, fuzzy
-#| msgid "Authentication is required to log into the local host."
msgid "Authentication is required to enable or disable multicast DNS."
-msgstr "Hitelesítés szükséges a bejelentkezéshez a helyi gépre."
+msgstr ""
+"Hitelesítés szükséges a multicast DNS engedélyezéséhez vagy kikapcsolásához."
#: src/network/org.freedesktop.network1.policy:88
#: src/resolve/org.freedesktop.resolve1.policy:99
msgid "Enable/disable DNS over TLS"
-msgstr ""
+msgstr "TLS feletti DNS engedélyezése/letiltása"
#: src/network/org.freedesktop.network1.policy:89
#: src/resolve/org.freedesktop.resolve1.policy:100
-#, fuzzy
-#| msgid "Authentication is required to set the local hostname."
msgid "Authentication is required to enable or disable DNS over TLS."
-msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
+msgstr ""
+"Hitelesítés szükséges a TLS feletti DNS engedélyezéséhez vagy "
+"kikapcsolásához."
#: src/network/org.freedesktop.network1.policy:99
#: src/resolve/org.freedesktop.resolve1.policy:110
msgid "Enable/disable DNSSEC"
-msgstr ""
+msgstr "DNSSEC engedélyezése/letiltása"
#: src/network/org.freedesktop.network1.policy:100
#: src/resolve/org.freedesktop.resolve1.policy:111
-#, fuzzy
-#| msgid "Authentication is required to hibernate the system."
msgid "Authentication is required to enable or disable DNSSEC."
-msgstr "Hitelesítés szükséges a rendszer hibernálásához."
+msgstr "Hitelesítés szükséges a DNSSEC engedélyezéséhez vagy kikapcsolásához."
#: src/network/org.freedesktop.network1.policy:110
#: src/resolve/org.freedesktop.resolve1.policy:121
msgid "Set DNSSEC Negative Trust Anchors"
-msgstr ""
+msgstr "DNSSEC Negative Trust Anchor beállítása"
#: src/network/org.freedesktop.network1.policy:111
#: src/resolve/org.freedesktop.resolve1.policy:122
-#, fuzzy
-#| msgid "Authentication is required to set the system locale."
msgid "Authentication is required to set DNSSEC Negative Trust Anchors."
-msgstr "Hitelesítés szükséges a rendszer területi beállításainak megadásához."
+msgstr "Hitelestés szükséges a DNSSEC Negative Trust Anchor beállításához."
#: src/network/org.freedesktop.network1.policy:121
msgid "Revert NTP settings"
-msgstr ""
+msgstr "NTP-beállítások visszaállítása"
#: src/network/org.freedesktop.network1.policy:122
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
msgid "Authentication is required to reset NTP settings."
-msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges az NTP-beállítások visszaállításához."
#: src/network/org.freedesktop.network1.policy:132
msgid "Revert DNS settings"
-msgstr ""
+msgstr "DNS-beállítások visszaállítása"
#: src/network/org.freedesktop.network1.policy:133
-#, fuzzy
-#| msgid "Authentication is required to set the system time."
msgid "Authentication is required to reset DNS settings."
-msgstr "Hitelesítés szükséges a rendszeridő beállításához."
+msgstr "Hitelesítés szükséges a DNS-beállítások visszaállításához."
#: src/network/org.freedesktop.network1.policy:143
msgid "DHCP server sends force renew message"
-msgstr ""
+msgstr "A DHCP-kiszolgáló kényszerített megújítási üzenetet küld"
#: src/network/org.freedesktop.network1.policy:144
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to send force renew message."
-msgstr "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a kényszerített megújítási üzenetet küldéséhez."
#: src/network/org.freedesktop.network1.policy:154
msgid "Renew dynamic addresses"
-msgstr ""
+msgstr "Dinamikus címke megújítása"
#: src/network/org.freedesktop.network1.policy:155
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to renew dynamic addresses."
-msgstr "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a dinamikus címke megújításához."
#: src/network/org.freedesktop.network1.policy:165
msgid "Reload network settings"
-msgstr ""
+msgstr "Hálózati beállítások újratöltése"
#: src/network/org.freedesktop.network1.policy:166
-#, fuzzy
-#| msgid "Authentication is required to reload the systemd state."
msgid "Authentication is required to reload network settings."
-msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
+msgstr "Hitelesítés szükséges a hálózati beállítások újratöltéséhez."
#: src/network/org.freedesktop.network1.policy:176
msgid "Reconfigure network interface"
-msgstr ""
+msgstr "Hálózati csatoló újrakonfigurálása"
#: src/network/org.freedesktop.network1.policy:177
-#, fuzzy
-#| msgid "Authentication is required to reboot the system."
msgid "Authentication is required to reconfigure network interface."
-msgstr "Hitelesítés szükséges a rendszer újraindításához."
+msgstr "Hitelesítés szükséges a hálózati csatoló újrakonfigurálásához."
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
-msgstr ""
+msgstr "Hordozható szolgáltatás lemezképének vizsgálata"
#: src/portable/org.freedesktop.portable1.policy:14
-#, fuzzy
-#| msgid "Authentication is required to import a VM or container image"
msgid "Authentication is required to inspect a portable service image."
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép importálásához."
+msgstr ""
+"Hitelesítés szükséges a hordozható szolgáltatás lemezképének vizsgálatához."
#: src/portable/org.freedesktop.portable1.policy:23
msgid "Attach or detach a portable service image"
-msgstr ""
+msgstr "Hordozható szolgáltatás lemezképének csatolása vagy leválasztása"
#: src/portable/org.freedesktop.portable1.policy:24
-#, fuzzy
-#| msgid "Authentication is required to attach a device to a seat."
msgid ""
"Authentication is required to attach or detach a portable service image."
msgstr ""
-"Hitelesítés szükséges eszköz csatolásának engedélyezéséhez egy "
-"munkaállomáshoz"
+"Hitelesítés szükséges a hordozható szolgáltatás lemezképének csatolásához "
+"vagy leválasztásához."
#: src/portable/org.freedesktop.portable1.policy:34
msgid "Delete or modify portable service image"
-msgstr ""
+msgstr "Hordozható szolgáltatás lemezképének törlése vagy módosítása"
#: src/portable/org.freedesktop.portable1.policy:35
-#, fuzzy
-#| msgid "Authentication is required to download a VM or container image"
msgid ""
"Authentication is required to delete or modify a portable service image."
-msgstr "Hitelesítés szükséges a VM vagy konténer lemezkép letöltéséhez."
+msgstr ""
+"Hitelesítés szükséges a hordozható szolgáltatás lemezképének törléséhez vagy "
+"módosításához."
#: src/resolve/org.freedesktop.resolve1.policy:22
msgid "Register a DNS-SD service"
-msgstr ""
+msgstr "DNS-SD szolgáltatás regisztrálása"
#: src/resolve/org.freedesktop.resolve1.policy:23
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to register a DNS-SD service"
-msgstr "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a DNS-SD szolgáltatás regisztrálásához"
#: src/resolve/org.freedesktop.resolve1.policy:33
msgid "Unregister a DNS-SD service"
-msgstr ""
+msgstr "DNS-SD szolgáltatás kiregisztrálása"
#: src/resolve/org.freedesktop.resolve1.policy:34
-#, fuzzy
-#| msgid "Authentication is required to set a wall message"
msgid "Authentication is required to unregister a DNS-SD service"
-msgstr "Hitelesítés szükséges a falüzenet beállításához"
+msgstr "Hitelesítés szükséges a DNS-SD szolgáltatás kiregisztrálásához"
#: src/resolve/org.freedesktop.resolve1.policy:132
msgid "Revert name resolution settings"
-msgstr ""
+msgstr "Névfeloldási beállítások visszaállítása"
#: src/resolve/org.freedesktop.resolve1.policy:133
-#, fuzzy
-#| msgid "Authentication is required to set the system keyboard settings."
msgid "Authentication is required to reset name resolution settings."
-msgstr ""
-"Hitelesítés szükséges a rendszer billentyűzetbeállításainak megadásához."
+msgstr "Hitelesítés szükséges a névfeloldási beállítások visszaállításához."
#: src/timedate/org.freedesktop.timedate1.policy:22
msgid "Set system time"
msgstr "Hitelesítés szükséges a következő újraindításához: „$(unit)”."
#: src/core/dbus-unit.c:535
-#, fuzzy
-#| msgid "Authentication is required to set properties on '$(unit)'."
msgid ""
"Authentication is required to send a UNIX signal to the processes of "
"'$(unit)'."
msgstr ""
-"Hitelesítés szükséges a következő tulajdonságainak beállításához: „$(unit)”."
+"Hitelesítés szükséges a UNIX szignál elküldéséhez a következő folyamatai "
+"számára: „$(unit)”."
#: src/core/dbus-unit.c:566
msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
"Hitelesítés szükséges a következő tulajdonságainak beállításához: „$(unit)”."
#: src/core/dbus-unit.c:708
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgid ""
"Authentication is required to delete files and directories associated with "
"'$(unit)'."
msgstr ""
-"Hitelesítés szükséges a következő „sikertelen” állapotának törléséhez: "
-"„$(unit)”."
+"Hitelesítés szükséges a következővel kapcsolatos fájlok és könyvtárak "
+"törléséhez: „$(unit)”."
#: src/core/dbus-unit.c:757
-#, fuzzy
-#| msgid ""
-#| "Authentication is required to reset the \"failed\" state of '$(unit)'."
msgid ""
"Authentication is required to freeze or thaw the processes of '$(unit)' unit."
msgstr ""
-"Hitelesítés szükséges a következő „sikertelen” állapotának törléséhez: "
+"Hitelesítés szükséges a következő egység folyamatainak befagyasztásához: "
"„$(unit)”."
#~ msgid "Authentication is required to kill '$(unit)'."
# Seong-ho Cho <shcho@gnome.org>, 2015, 2021.
# Dongsu Park <dongsu@endocode.com>, 2015.
# simmon <simmon@nplob.com>, 2021.
+# 김인수 <simmon@nplob.com>, 2022.
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2021-06-22 10:04+0000\n"
-"Last-Translator: simmon <simmon@nplob.com>\n"
+"PO-Revision-Date: 2022-07-18 15:19+0000\n"
+"Last-Translator: 김인수 <simmon@nplob.com>\n"
"Language-Team: Korean <https://translate.fedoraproject.org/projects/systemd/"
"master/ko/>\n"
"Language: ko\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.7\n"
+"X-Generator: Weblate 4.13\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid ""
"Authentication is required to delete files and directories associated with "
"'$(unit)'."
-msgstr ""
-"'$(unit)'에 해당하는 파일 또는 디렉터리를 삭제하려면 인증이 필요합니다."
+msgstr "인증은 '$(unit)'과 관련된 파일과 디렉토리를 삭제하는데 필요합니다."
#: src/core/dbus-unit.c:757
msgid ""
# SPDX-License-Identifier: LGPL-2.1-or-later
-i18n = import('i18n')
want_translations = get_option('translations')
if want_translations
+ i18n = import('i18n')
i18n.gettext(meson.project_name(),
preset : 'glib',
data_dirs : '.')
KERNEL=="vd*[!0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}"
KERNEL=="vd*[0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}-part%n"
+# allow admin to disable probing the filesystem for slow devices like floppy disk drives
+ENV{UDEV_DISABLE_PERSISTENT_STORAGE_BLKID_FLAG}=="1", GOTO="persistent_storage_blkid_probe_end"
+
# probe filesystem metadata of optical drives which have a media inserted
KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \
IMPORT{builtin}="blkid --hint=session_offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}"
# probe filesystem metadata of disks
KERNEL!="sr*|mmcblk[0-9]boot[0-9]", IMPORT{builtin}="blkid"
+LABEL="persistent_storage_blkid_probe_end"
+
# by-label/by-uuid links (filesystem metadata)
ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
# Protocol analyzers
ENV{ID_SIGNAL_ANALYZER}=="?*", ENV{DEVTYPE}=="usb_device", TAG+="uaccess"
+ENV{ID_SIGNAL_ANALYZER}=="?*", KERNEL=="ttyACM[0-9]*", TAG+="uaccess"
# rfkill / radio killswitches
KERNEL=="rfkill", SUBSYSTEM=="misc", TAG+="uaccess"
suspend-then-hibernate kexec list-jobs list-sockets
list-timers list-units list-unit-files poweroff
reboot rescue show-environment suspend get-default
- is-system-running preset-all'
+ is-system-running preset-all list-automounts'
[FILE]='link switch-root bind mount-image'
[TARGETS]='set-default'
[MACHINES]='list-machines'
fi
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
+ if [ -d /sys/fs/cgroup/systemd/ ]; then
+ COMPREPLY+=( $(cd /sys/fs/cgroup/systemd/ && compgen -o nospace -o dirnames "$cur") )
+ elif [ -d /sys/fs/cgroup/ ]; then
+ COMPREPLY+=( $(cd /sys/fs/cgroup/ && compgen -o nospace -o dirnames "$cur") )
+ fi
}
complete -F _systemd_cgtop systemd-cgtop
local -a unit_commands=(
# Unit Commands
+ "list-automounts:List automounts"
"list-sockets:List sockets"
"list-timers:List timers"
"list-units:List units"
#include "analyze-security.h"
#include "analyze-verify.h"
#include "bus-error.h"
+#include "bus-locator.h"
#include "bus-map-properties.h"
#include "bus-unit-util.h"
#include "bus-util.h"
_cleanup_strv_free_ char **list = NULL;
size_t n = 0;
- r = sd_bus_call_method(
+ r = bus_call_method(
bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
+ bus_systemd_mgr,
"ListUnits",
&error,
&reply,
#include "terminal-util.h"
#include "util.h"
+#define PCI_CLASS_GRAPHICS_CARD 0x30000
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
return 0;
}
+static int has_multiple_graphics_cards(void) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
+ bool found = false;
+ int r;
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_subsystem(e, "pci", /* match = */ true);
+ if (r < 0)
+ return r;
+
+ /* class is an unsigned number, let's validate the value later. */
+ r = sd_device_enumerator_add_match_sysattr(e, "class", NULL, /* match = */ true);
+ if (r < 0)
+ return r;
+
+ FOREACH_DEVICE(e, dev) {
+ const char *s;
+ unsigned long c;
+
+ if (sd_device_get_sysattr_value(dev, "class", &s) < 0)
+ continue;
+
+ if (safe_atolu(s, &c) < 0)
+ continue;
+
+ if (c != PCI_CLASS_GRAPHICS_CARD)
+ continue;
+
+ if (found)
+ return true; /* This is the second device. */
+
+ found = true; /* Found the first device. */
+ }
+
+ return false;
+}
+
static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
const char *subsystem, *sysname, *value;
sd_device *parent;
return -ENODATA;
c += strspn(c, DIGITS);
- if (*c == '-' && !STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-"))
+ if (*c == '-' && !STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-", "-eDP-"))
/* A connector DRM device, let's ignore all but LVDS and eDP! */
return -EOPNOTSUPP;
value, subsystem, sysname);
/* Graphics card */
- if (class == 0x30000) {
+ if (class == PCI_CLASS_GRAPHICS_CARD) {
*ret = parent;
return 0;
}
static int validate_device(sd_device *device) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerate = NULL;
- const char *v, *subsystem;
+ const char *v, *sysname, *subsystem;
sd_device *parent, *other;
int r;
* device to userspace. However, we still need to make sure that we use "raw" only if no
* "firmware" or "platform" device for the same device exists. */
+ r = sd_device_get_sysname(device, &sysname);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to get sysname: %m");
+
r = sd_device_get_subsystem(device, &subsystem);
if (r < 0)
- return r;
+ return log_device_debug_errno(device, r, "Failed to get subsystem: %m");
if (!streq(subsystem, "backlight"))
return true;
r = sd_device_get_sysattr_value(device, "type", &v);
if (r < 0)
- return r;
+ return log_device_debug_errno(device, r, "Failed to read 'type' sysattr: %m");
if (!streq(v, "raw"))
return true;
r = find_pci_or_platform_parent(device, &parent);
if (r < 0)
- return r;
+ return log_device_debug_errno(device, r, "Failed to find PCI or platform parent: %m");
r = sd_device_get_subsystem(parent, &subsystem);
if (r < 0)
- return r;
+ return log_device_debug_errno(parent, r, "Failed to get subsystem: %m");
+
+ if (DEBUG_LOGGING) {
+ const char *s = NULL;
+
+ (void) sd_device_get_syspath(parent, &s);
+ log_device_debug(device, "Found %s parent device: %s", subsystem, strna(s));
+ }
r = sd_device_enumerator_new(&enumerate);
if (r < 0)
- return r;
+ return log_oom_debug();
r = sd_device_enumerator_allow_uninitialized(enumerate);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to allow uninitialized devices: %m");
- r = sd_device_enumerator_add_match_subsystem(enumerate, "backlight", true);
+ r = sd_device_enumerator_add_match_subsystem(enumerate, "backlight", /* match = */ true);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to add subsystem match: %m");
+
+ r = sd_device_enumerator_add_nomatch_sysname(enumerate, sysname);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add sysname unmatch: %m");
+
+ r = sd_device_enumerator_add_match_sysattr(enumerate, "type", "platform", /* match = */ true);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add sysattr match: %m");
+
+ r = sd_device_enumerator_add_match_sysattr(enumerate, "type", "firmware", /* match = */ true);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add sysattr match: %m");
+
+ if (streq(subsystem, "pci")) {
+ r = has_multiple_graphics_cards();
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check if the system has multiple graphics cards: %m");
+ if (r > 0) {
+ /* If the system has multiple graphics cards, then we cannot associate platform
+ * devices on non-PCI bus (especially WMI bus) with PCI devices. Let's ignore all
+ * backlight devices that do not have the same parent PCI device. */
+ log_debug("Found multiple graphics cards on PCI bus. "
+ "Skipping to associate platform backlight devices on non-PCI bus.");
+
+ r = sd_device_enumerator_add_match_parent(enumerate, parent);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add parent match: %m");
+ }
+ }
FOREACH_DEVICE(enumerate, other) {
const char *other_subsystem;
sd_device *other_parent;
- if (same_device(device, other) > 0)
- continue;
-
- if (sd_device_get_sysattr_value(other, "type", &v) < 0 ||
- !STR_IN_SET(v, "platform", "firmware"))
- continue;
-
/* OK, so there's another backlight device, and it's a platform or firmware device.
* Let's see if we can verify it belongs to the same device as ours. */
- if (find_pci_or_platform_parent(other, &other_parent) < 0)
+ r = find_pci_or_platform_parent(other, &other_parent);
+ if (r < 0) {
+ log_device_debug_errno(other, r, "Failed to get PCI or platform parent, ignoring: %m");
continue;
+ }
if (same_device(parent, other_parent) > 0) {
- const char *device_sysname = NULL, *other_sysname = NULL;
-
/* Both have the same PCI parent, that means we are out. */
-
- (void) sd_device_get_sysname(device, &device_sysname);
- (void) sd_device_get_sysname(other, &other_sysname);
-
- log_debug("Skipping backlight device %s, since device %s is on same PCI device and takes precedence.",
- device_sysname, other_sysname);
+ if (DEBUG_LOGGING) {
+ const char *other_sysname = NULL, *other_type = NULL;
+
+ (void) sd_device_get_sysname(other, &other_sysname);
+ (void) sd_device_get_sysattr_value(other, "type", &other_type);
+ log_device_debug(device,
+ "Found another %s backlight device %s on the same PCI, skipping.",
+ strna(other_type), strna(other_sysname));
+ }
return false;
}
- if (sd_device_get_subsystem(other_parent, &other_subsystem) < 0)
+ r = sd_device_get_subsystem(other_parent, &other_subsystem);
+ if (r < 0) {
+ log_device_debug_errno(other_parent, r, "Failed to get subsystem, ignoring: %m");
continue;
+ }
if (streq(other_subsystem, "platform") && streq(subsystem, "pci")) {
- const char *device_sysname = NULL, *other_sysname = NULL;
-
/* The other is connected to the platform bus and we are a PCI device, that also means we are out. */
-
- (void) sd_device_get_sysname(device, &device_sysname);
- (void) sd_device_get_sysname(other, &other_sysname);
-
- log_debug("Skipping backlight device %s, since device %s is a platform device and takes precedence.",
- device_sysname, other_sysname);
+ if (DEBUG_LOGGING) {
+ const char *other_sysname = NULL, *other_type = NULL;
+
+ (void) sd_device_get_sysname(other, &other_sysname);
+ (void) sd_device_get_sysattr_value(other, "type", &other_type);
+ log_device_debug(device,
+ "Found another %s backlight device %s, which has higher precedence, skipping.",
+ strna(other_type), strna(other_sysname));
+ }
return false;
}
}
#define malloc0(n) (calloc(1, (n) ?: 1))
-#define free_and_replace(a, b) \
+#define free_and_replace_full(a, b, free_func) \
({ \
typeof(a)* _a = &(a); \
typeof(b)* _b = &(b); \
- free(*_a); \
+ free_func(*_a); \
*_a = *_b; \
*_b = NULL; \
0; \
})
+#define free_and_replace(a, b) \
+ free_and_replace_full(a, b, free)
+
+/* This is similar to free_and_replace_full(), but NULL is not assigned to 'b', and its reference counter is
+ * increased. */
+#define unref_and_replace_full(a, b, ref_func, unref_func) \
+ ({ \
+ typeof(a)* _a = &(a); \
+ typeof(b) _b = ref_func(b); \
+ unref_func(*_a); \
+ *_a = _b; \
+ 0; \
+ })
+
void* memdup(const void *p, size_t l) _alloc_(2);
void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, since we return a buffer one byte larger than the specified size */
/* Special values for all weight knobs on unified hierarchy */
#define CGROUP_WEIGHT_INVALID UINT64_MAX
+#define CGROUP_WEIGHT_IDLE UINT64_C(0)
#define CGROUP_WEIGHT_MIN UINT64_C(1)
#define CGROUP_WEIGHT_MAX UINT64_C(10000)
#define CGROUP_WEIGHT_DEFAULT UINT64_C(100)
return buffer;
}
+struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length) {
+ assert(addr);
+ assert(length <= HW_ADDR_MAX_SIZE);
+
+ addr->length = length;
+ memcpy_safe(addr->bytes, bytes, length);
+ return addr;
+}
+
int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) {
int r;
#define HW_ADDR_NULL ((const struct hw_addr_data){})
+struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length);
+
void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state);
int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b);
static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) {
#include <errno.h>
#include <fcntl.h>
#include <linux/btrfs.h>
+#if WANT_LINUX_FS_H
#include <linux/fs.h>
+#endif
#include <linux/magic.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
return ret;
}
-int touch(const char *path) {
- return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
-}
-
int symlink_idempotent(const char *from, const char *to, bool make_relative) {
_cleanup_free_ char *relpath = NULL;
int r;
#include "alloc-util.h"
#include "errno-util.h"
#include "time-util.h"
+#include "user-util.h"
#define MODE_INVALID ((mode_t) -1)
RET_NERRNO(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW))
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
-int touch(const char *path);
+
+static inline int touch(const char *path) {
+ return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
+}
int symlink_idempotent(const char *from, const char *to, bool make_relative);
if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
return;
+ gcry_control(GCRYCTL_SET_PREFERRED_RNG_TYPE, GCRY_RNG_TYPE_SYSTEM);
assert_se(gcry_check_version("1.4.5"));
/* Turn off "secmem". Clients which wish to make use of this
* feature should initialize the library manually */
if (!secmem)
gcry_control(GCRYCTL_DISABLE_SECMEM);
+
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
#include "hash-funcs.h"
#include "path-util.h"
+#include "strv.h"
void string_hash_func(const char *p, struct siphash *state) {
siphash24_compress(p, strlen(p) + 1, state);
DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
char, string_hash_func, string_compare_func, free,
void, free);
+DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free,
+ char, string_hash_func, string_compare_func, free,
+ char*, strv_free);
void path_hash_func(const char *q, struct siphash *state) {
bool add_slash = false;
extern const struct hash_ops string_hash_ops;
extern const struct hash_ops string_hash_ops_free;
extern const struct hash_ops string_hash_ops_free_free;
+extern const struct hash_ops string_hash_ops_free_strv_free;
void path_hash_func(const char *p, struct siphash *state);
extern const struct hash_ops path_hash_ops;
#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define hashmap_free_and_replace(a, b) \
- ({ \
- hashmap_free(a); \
- (a) = (b); \
- (b) = NULL; \
- 0; \
- })
+ free_and_replace_full(a, b, hashmap_free)
HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
static inline Hashmap* hashmap_free(Hashmap *h) {
return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
}
+bool in4_addr_is_link_local_dynamic(const struct in_addr *a) {
+ assert(a);
+
+ if (!in4_addr_is_link_local(a))
+ return false;
+
+ /* 169.254.0.0/24 and 169.254.255.0/24 must not be used for the dynamic IPv4LL assignment.
+ * See RFC 3927 Section 2.1:
+ * The IPv4 prefix 169.254/16 is registered with the IANA for this purpose. The first 256 and last
+ * 256 addresses in the 169.254/16 prefix are reserved for future use and MUST NOT be selected by a
+ * host using this dynamic configuration mechanism. */
+ return !IN_SET(be32toh(a->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
+}
+
bool in6_addr_is_link_local(const struct in6_addr *a) {
assert(a);
int in_addr_is_multicast(int family, const union in_addr_union *u);
bool in4_addr_is_link_local(const struct in_addr *a);
+bool in4_addr_is_link_local_dynamic(const struct in_addr *a);
bool in6_addr_is_link_local(const struct in6_addr *a);
int in_addr_is_link_local(int family, const union in_addr_union *u);
bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a);
_Pragma("GCC diagnostic push")
#endif
-#define DISABLE_WARNING_FLOAT_EQUAL \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"")
-
#define DISABLE_WARNING_TYPE_LIMITS \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <math.h>
+
+#include "macro.h"
+
+/* On some optimization level, iszero(x) is converted to (x == 0.0), and emits warning -Wfloat-equal.
+ * The argument must be a floating point, i.e. one of float, double, or long double. */
+#define iszero_safe(x) (fpclassify(x) == FP_ZERO)
+
+/* To avoid x == y and triggering compile warning -Wfloat-equal. This returns false if one of the argument is
+ * NaN or infinity. One of the argument must be a floating point. */
+#define fp_equal(x, y) iszero_safe((x) - (y))
'login-util.c',
'login-util.h',
'macro.h',
+ 'math-util.h',
'memfd-util.c',
'memfd-util.h',
'memory-util.c',
'linux/gfs2_ondisk.h']
check_filesystems = find_program('check-filesystems.sh')
-r = run_command([check_filesystems, cpp, 'filesystems-gperf.gperf'] + filesystem_includes, check: false)
+r = run_command([check_filesystems, cpp, files('filesystems-gperf.gperf')] + filesystem_includes, check: false)
if r.returncode() != 0
error('found unknown filesystem(s) defined in kernel headers:\n\n' + r.stdout())
r.stdout()
#ifndef FS_PROJINHERIT_FL
#define FS_PROJINHERIT_FL 0x20000000
#endif
+
+/* linux/fscrypt.h */
+#ifndef FS_KEY_DESCRIPTOR_SIZE
+#define FS_KEY_DESCRIPTOR_SIZE 8
+#endif
int r;
assert(fd >= 0);
- assert(filename);
- assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
+ assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
- /* Insist that the specified filename is actually a filename, and not a path, i.e. some inode further
- * up or down the tree then immediately below the specified directory fd. */
- if (!filename_possibly_with_slash_suffix(filename))
+ if (!filename) {
+ /* If the file name is specified as NULL we'll see if the specified 'fd' is a mount
+ * point. That's only supported if the kernel supports statx(), or if the inode specified via
+ * 'fd' refers to a directory. Otherwise, we'll have to fail (ENOTDIR), because we have no
+ * kernel API to query the information we need. */
+ flags |= AT_EMPTY_PATH;
+ filename = "";
+ } else if (!filename_possibly_with_slash_suffix(filename))
+ /* Insist that the specified filename is actually a filename, and not a path, i.e. some inode further
+ * up or down the tree then immediately below the specified directory fd. */
return -EINVAL;
/* First we will try statx()' STATX_ATTR_MOUNT_ROOT attribute, which is our ideal API, available
nosupp = true;
}
- r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
+ if (isempty(filename))
+ r = name_to_handle_at_loop(fd, "..", &h_parent, &mount_id_parent, 0); /* can't work for non-directories 😢 */
+ else
+ r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
if (r < 0) {
if (is_name_to_handle_at_fatal_error(r))
return r;
if (r < 0)
return r;
- r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
+ if (isempty(filename))
+ r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent); /* can't work for non-directories 😢 */
+ else
+ r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
if (r < 0)
return r;
if (S_ISLNK(a.st_mode)) /* Symlinks are never mount points */
return false;
- if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
+ if (isempty(filename))
+ r = fstatat(fd, "..", &b, 0);
+ else
+ r = fstatat(fd, "", &b, AT_EMPTY_PATH);
+ if (r < 0)
return -errno;
/* A directory with same device and inode as its parent? Must be the root directory */
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
+#include <fnmatch.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include "extract-word.h"
#include "fd-util.h"
#include "fs-util.h"
+#include "glob-util.h"
#include "log.h"
#include "macro.h"
#include "path-util.h"
return false;
}
+
+int path_glob_can_match(const char *pattern, const char *prefix, char **ret) {
+ assert(pattern);
+ assert(prefix);
+
+ for (const char *a = pattern, *b = prefix;;) {
+ _cleanup_free_ char *g = NULL, *h = NULL;
+ const char *p, *q;
+ int r, s;
+
+ r = path_find_first_component(&a, /* accept_dot_dot = */ false, &p);
+ if (r < 0)
+ return r;
+
+ s = path_find_first_component(&b, /* accept_dot_dot = */ false, &q);
+ if (s < 0)
+ return s;
+
+ if (s == 0) {
+ /* The pattern matches the prefix. */
+ if (ret) {
+ char *t;
+
+ t = path_join(prefix, p);
+ if (!t)
+ return -ENOMEM;
+
+ *ret = t;
+ }
+ return true;
+ }
+
+ if (r == 0)
+ break;
+
+ if (r == s && strneq(p, q, r))
+ continue; /* common component. Check next. */
+
+ g = strndup(p, r);
+ if (!g)
+ return -ENOMEM;
+
+ if (!string_is_glob(g))
+ break;
+
+ /* We found a glob component. Check if the glob pattern matches the prefix component. */
+
+ h = strndup(q, s);
+ if (!h)
+ return -ENOMEM;
+
+ if (fnmatch(g, h, 0) != 0)
+ break;
+ }
+
+ /* The pattern does not match the prefix. */
+ if (ret)
+ *ret = NULL;
+ return false;
+}
bool path_strv_contains(char **l, const char *path);
bool prefixed_path_strv_contains(char **l, const char *path);
+
+int path_glob_can_match(const char *pattern, const char *prefix, char **ret);
pid_t getpid_cached(void) {
static bool installed = false;
- pid_t current_value;
+ pid_t current_value = CACHED_PID_UNSET;
/* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a
* system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally
* https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
*/
- current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY);
+ __atomic_compare_exchange_n(
+ &cached_pid,
+ ¤t_value,
+ CACHED_PID_BUSY,
+ false,
+ __ATOMIC_SEQ_CST,
+ __ATOMIC_SEQ_CST);
switch (current_value) {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#if defined(__i386__) || defined(__x86_64__)
-#include <cpuid.h>
-#endif
-
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include "sha256.h"
#include "time-util.h"
-/* This is a "best effort" kind of thing, but has no real security value.
- * So, this should only be used by random_bytes(), which is not meant for
- * crypto. This could be made better, but we're *not* trying to roll a
- * userspace prng here, or even have forward secrecy, but rather just do
- * the shortest thing that is at least better than libc rand(). */
+/* This is a "best effort" kind of thing, but has no real security value. So, this should only be used by
+ * random_bytes(), which is not meant for crypto. This could be made better, but we're *not* trying to roll a
+ * userspace prng here, or even have forward secrecy, but rather just do the shortest thing that is at least
+ * better than libc rand(). */
static void fallback_random_bytes(void *p, size_t n) {
static thread_local uint64_t fallback_counter = 0;
struct {
.stamp_mono = now(CLOCK_MONOTONIC),
.stamp_real = now(CLOCK_REALTIME),
.pid = getpid(),
- .tid = gettid()
+ .tid = gettid(),
};
#if HAVE_SYS_AUXV_H
#include "macro.h"
#define set_free_and_replace(a, b) \
- ({ \
- set_free(a); \
- (a) = (b); \
- (b) = NULL; \
- 0; \
- })
+ free_and_replace_full(a, b, set_free)
Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
assert(addr);
/* Find a free place, increase the number of entries and leave, if we can */
- for (size_t u = 0; u < SIGBUS_QUEUE_MAX; u++)
- if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) {
- __sync_fetch_and_add(&n_sigbus_queue, 1);
+ for (size_t u = 0; u < SIGBUS_QUEUE_MAX; u++) {
+ /* OK to initialize this here since we haven't started the atomic ops yet */
+ void *tmp = NULL;
+ if (__atomic_compare_exchange_n(&sigbus_queue[u], &tmp, addr, false,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
+ __atomic_fetch_add(&n_sigbus_queue, 1, __ATOMIC_SEQ_CST);
return;
}
+ }
/* If we can't, make sure the queue size is out of bounds, to
* mark it as overflow */
for (;;) {
- unsigned c;
+ sig_atomic_t c;
- __sync_synchronize();
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
c = n_sigbus_queue;
if (c > SIGBUS_QUEUE_MAX) /* already overflow */
return;
- if (__sync_bool_compare_and_swap(&n_sigbus_queue, c, c + SIGBUS_QUEUE_MAX))
+ /* OK if we clobber c here, since we either immediately return
+ * or it will be immediately reinitialized on next loop */
+ if (__atomic_compare_exchange_n(&n_sigbus_queue, &c, c + SIGBUS_QUEUE_MAX, false,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
return;
}
}
for (;;) {
unsigned u, c;
- __sync_synchronize();
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
c = n_sigbus_queue;
if (_likely_(c == 0))
if (!addr)
continue;
- if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) {
- __sync_fetch_and_sub(&n_sigbus_queue, 1);
+ /* OK if we clobber addr here, since we either immediately return
+ * or it will be immediately reinitialized on next loop */
+ if (__atomic_compare_exchange_n(&sigbus_queue[u], &addr, NULL, false,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
+ __atomic_fetch_sub(&n_sigbus_queue, 1, __ATOMIC_SEQ_CST);
+ /* If we successfully entered this if condition, addr won't
+ * have been modified since its assignment, so safe to use it */
*ret = addr;
return 1;
}
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
#define strv_free_and_replace(a, b) \
- ({ \
- char ***_a = &(a); \
- char ***_b = &(b); \
- strv_free(*_a); \
- (*_a) = (*_b); \
- (*_b) = NULL; \
- 0; \
- })
+ free_and_replace_full(a, b, strv_free)
extern const struct hash_ops string_strv_hash_ops;
int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS);
static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
[SCOPE_DEAD] = "dead",
+ [SCOPE_START_CHOWN] = "start-chown",
[SCOPE_RUNNING] = "running",
[SCOPE_ABANDONED] = "abandoned",
[SCOPE_STOP_SIGTERM] = "stop-sigterm",
typedef enum ScopeState {
SCOPE_DEAD,
+ SCOPE_START_CHOWN,
SCOPE_RUNNING,
SCOPE_ABANDONED,
SCOPE_STOP_SIGTERM,
continue;
}
- r = set_consume(*names, TAKE_PTR(inst));
- if (r > 0)
- log_debug("Unit %s has alias %s.", unit_name, inst);
+ r = add_name(unit_name, names, inst);
} else
r = add_name(unit_name, names, *alias);
-
if (r < 0)
return r;
}
} dmi_vendor_table[] = {
{ "KVM", VIRTUALIZATION_KVM },
{ "OpenStack", VIRTUALIZATION_KVM }, /* Detect OpenStack instance as KVM in non x86 architecture */
+ { "KubeVirt", VIRTUALIZATION_KVM }, /* Detect KubeVirt instance as KVM in non x86 architecture */
{ "Amazon EC2", VIRTUALIZATION_AMAZON },
{ "QEMU", VIRTUALIZATION_QEMU },
{ "VMware", VIRTUALIZATION_VMWARE }, /* https://kb.vmware.com/s/article/1009458 */
return EFI_MACHINE_TYPE_NAME;
}
-static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
+static int enumerate_binaries(
+ const char *esp_path,
+ const char *path,
+ const char *prefix,
+ char **previous,
+ bool *is_first) {
+
_cleanup_closedir_ DIR *d = NULL;
const char *p;
int c = 0, r;
assert(esp_path);
assert(path);
+ assert(previous);
+ assert(is_first);
p = prefix_roota(esp_path, path);
d = opendir(p);
r = get_file_version(fd, &v);
if (r < 0)
return r;
+
+ if (*previous) { /* let's output the previous entry now, since now we know that there will be one more, and can draw the tree glyph properly */
+ printf(" %s %s%s\n",
+ *is_first ? "File:" : " ",
+ special_glyph(SPECIAL_GLYPH_TREE_BRANCH), *previous);
+ *is_first = false;
+ *previous = mfree(*previous);
+ }
+
+ /* Do not output this entry immediately, but store what should be printed in a state
+ * variable, because we only will know the tree glyph to print (branch or final edge) once we
+ * read one more entry */
if (r > 0)
- printf(" File: %s/%s/%s (%s%s%s)\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal());
+ r = asprintf(previous, "/%s/%s (%s%s%s)", path, de->d_name, ansi_highlight(), v, ansi_normal());
else
- printf(" File: %s/%s/%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name);
+ r = asprintf(previous, "/%s/%s", path, de->d_name);
+ if (r < 0)
+ return log_oom();
c++;
}
}
static int status_binaries(const char *esp_path, sd_id128_t partition) {
- int r;
+ _cleanup_free_ char *last = NULL;
+ bool is_first = true;
+ int r, k;
- printf("Available Boot Loaders on ESP:\n");
+ printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
if (!esp_path) {
printf(" ESP: Cannot find or access mount point of ESP.\n\n");
printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition));
printf("\n");
- r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
- if (r < 0)
- goto finish;
- if (r == 0 && !arg_quiet)
- log_info("systemd-boot not installed in ESP.");
+ r = enumerate_binaries(esp_path, "EFI/systemd", NULL, &last, &is_first);
+ if (r < 0) {
+ printf("\n");
+ return r;
+ }
+
+ k = enumerate_binaries(esp_path, "EFI/BOOT", "boot", &last, &is_first);
+ if (k < 0) {
+ printf("\n");
+ return k;
+ }
+
+ if (last) /* let's output the last entry now, since now we know that there will be no more, and can draw the tree glyph properly */
+ printf(" %s %s%s\n",
+ is_first ? "File:" : " ",
+ special_glyph(SPECIAL_GLYPH_TREE_RIGHT), last);
- r = enumerate_binaries(esp_path, "EFI/BOOT", "boot");
- if (r < 0)
- goto finish;
if (r == 0 && !arg_quiet)
+ log_info("systemd-boot not installed in ESP.");
+ if (k == 0 && !arg_quiet)
log_info("No default/fallback boot loader installed in ESP.");
- r = 0;
-
-finish:
printf("\n");
- return r;
+ return 0;
}
-static int print_efi_option(uint16_t id, bool in_order) {
+static int print_efi_option(uint16_t id, int *n_printed, bool in_order) {
_cleanup_free_ char *title = NULL;
_cleanup_free_ char *path = NULL;
sd_id128_t partition;
bool active;
int r;
+ assert(n_printed);
+
r = efi_get_boot_option(id, &title, &partition, &path, &active);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to read boot option %u: %m", id);
/* print only configured entries with partition information */
- if (!path || sd_id128_is_null(partition))
+ if (!path || sd_id128_is_null(partition)) {
+ log_debug("Ignoring boot entry %u without partition information.", id);
return 0;
+ }
efi_tilt_backslashes(path);
+ if (*n_printed == 0) /* Print section title before first entry */
+ printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
+
printf(" Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
printf(" ID: 0x%04X\n", id);
printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path);
printf("\n");
- return 0;
+ (*n_printed)++;
+ return 1;
}
static int status_variables(void) {
_cleanup_free_ uint16_t *options = NULL, *order = NULL;
- int n_options, n_order;
+ int n_options, n_order, n_printed = 0;
n_options = efi_get_boot_options(&options);
if (n_options == -ENOENT)
return log_error_errno(n_order, "Failed to read EFI boot order: %m");
/* print entries in BootOrder first */
- printf("Boot Loaders Listed in EFI Variables:\n");
for (int i = 0; i < n_order; i++)
- print_efi_option(order[i], true);
+ (void) print_efi_option(order[i], &n_printed, /* in_order= */ true);
/* print remaining entries */
for (int i = 0; i < n_options; i++) {
if (options[i] == order[j])
goto next_option;
- print_efi_option(options[i], false);
+ (void) print_efi_option(options[i], &n_printed, /* in_order= */ false);
next_option:
continue;
}
+ if (n_printed == 0)
+ printf("No boot loaders listed in EFI Variables.\n\n");
+
return 0;
}
dollar_boot_partition_uuid = esp_partition_uuid;
}
- printf("Boot Loader Entries:\n"
- " $BOOT: %s", dollar_boot_path);
+ printf("%sBoot Loader Entries:%s\n"
+ " $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path);
if (!sd_id128_is_null(dollar_boot_partition_uuid))
printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")",
SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
if (config->default_entry < 0)
printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
else {
- printf("Default Boot Loader Entry:\n");
+ printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
r = show_boot_entry(
boot_config_default_entry(config),
"Failed to resolve path %s%s%s: %m",
p,
root ? " under directory " : "",
- root ?: "");
+ strempty(root));
q = path_join("/EFI/systemd/", dest_name);
if (!q)
if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
r = chase_symlinks_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT, &path, &d);
if (r < 0)
- return log_error_errno(r, "Failed to open boot loader directory %s%s: %m", root ?: "", BOOTLIBDIR);
+ return log_error_errno(r, "Failed to open boot loader directory %s%s: %m", strempty(root), BOOTLIBDIR);
const char *suffix = strjoina(arch, ".efi");
const char *suffix_signed = strjoina(arch, ".efi.signed");
static const struct {
uint64_t flag;
const char *name;
- } flags[] = {
+ } loader_flags[] = {
{ EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
{ EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
{ EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" },
{ EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
{ EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" },
+ { EFI_LOADER_FEATURE_SORT_KEY, "Support Type #1 sort-key field" },
+ { EFI_LOADER_FEATURE_SAVED_ENTRY, "Support @saved pseudo-entry" },
+ { EFI_LOADER_FEATURE_DEVICETREE, "Support Type #1 devicetree field" },
+ };
+ static const struct {
+ uint64_t flag;
+ const char *name;
+ } stub_flags[] = {
+ { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets ESP information" },
+ { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" },
+ { EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" },
+ { EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" },
};
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
- uint64_t loader_features = 0;
+ uint64_t loader_features = 0, stub_features = 0;
Tpm2Support s;
int have;
read_efi_var(EFI_LOADER_VARIABLE(StubInfo), &stub);
read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
(void) efi_loader_get_features(&loader_features);
+ (void) efi_stub_get_features(&stub_features);
if (loader_path)
efi_tilt_backslashes(loader_path);
r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
SecureBootMode secure = efi_get_secure_boot_mode();
- printf("System:\n");
+ printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
printf(" Firmware Arch: %s\n", get_efi_arch());
printf(" Secure Boot: %sd (%s)\n",
}
printf("\n");
- printf("Current Boot Loader:\n");
+ printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
- for (size_t i = 0; i < ELEMENTSOF(flags); i++)
- print_yes_no_line(i == 0, FLAGS_SET(loader_features, flags[i].flag), flags[i].name);
+ for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
+ print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
sd_id128_t bootloader_esp_uuid;
bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
SD_ID128_FORMAT_VAL(bootloader_esp_uuid),
SD_ID128_FORMAT_VAL(esp_uuid));
- if (stub)
+ if (stub) {
printf(" Stub: %s\n", stub);
+ for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
+ print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
+ }
if (!sd_id128_is_null(loader_part_uuid))
printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
SD_ID128_FORMAT_VAL(loader_part_uuid));
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
printf("\n");
- printf("Random Seed:\n");
+ printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)), F_OK) >= 0;
printf(" Passed to OS: %s\n", yes_no(have));
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
printf("\n");
} else
- printf("System:\n Not booted with EFI\n\n");
+ printf("%sSystem:%s\n"
+ "Not booted with EFI\n\n",
+ ansi_underline(), ansi_normal());
if (arg_esp_path) {
k = status_binaries(arg_esp_path, esp_uuid);
LOADER_EFI,
LOADER_LINUX, /* Boot loader spec type #1 entries */
LOADER_UNIFIED_LINUX, /* Boot loader spec type #2 entries */
+ LOADER_SECURE_BOOT_KEYS,
};
typedef struct {
bool auto_entries;
bool auto_firmware;
bool reboot_for_bitlocker;
+ secure_boot_enroll secure_boot_enroll;
bool force_menu;
bool use_saved_entry;
bool use_saved_entry_efivar;
ps_bool(L" reboot-for-bitlocker: %s\n", config->reboot_for_bitlocker);
ps_string(L" random-seed-mode: %s\n", random_seed_modes_table[config->random_seed_mode]);
+ switch (config->secure_boot_enroll) {
+ case ENROLL_OFF:
+ Print(L" secure-boot-enroll: off\n"); break;
+ case ENROLL_MANUAL:
+ Print(L" secure-boot-enroll: manual\n"); break;
+ case ENROLL_FORCE:
+ Print(L" secure-boot-enroll: force\n"); break;
+ default:
+ assert_not_reached();
+ }
+
switch (config->console_mode) {
case CONSOLE_MODE_AUTO:
Print(L" console-mode (config): %s\n", L"auto"); break;
err = parse_boolean(value, &config->reboot_for_bitlocker);
if (err != EFI_SUCCESS)
log_error_stall(L"Error parsing 'reboot-for-bitlocker' config option: %a", value);
+ }
+
+ if (streq8(key, "secure-boot-enroll")) {
+ if (streq8(value, "manual"))
+ config->secure_boot_enroll = ENROLL_MANUAL;
+ else if (streq8(value, "force"))
+ config->secure_boot_enroll = ENROLL_FORCE;
+ else if (streq8(value, "off"))
+ config->secure_boot_enroll = ENROLL_OFF;
+ else
+ log_error_stall(L"Error parsing 'secure-boot-enroll' config option: %a", value);
continue;
}
.auto_entries = true,
.auto_firmware = true,
.reboot_for_bitlocker = false,
+ .secure_boot_enroll = ENROLL_MANUAL,
.random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN,
.idx_default_efivar = IDX_INVALID,
.console_mode = CONSOLE_MODE_KEEP,
loaded_image->LoadOptionsSize = strsize16(options);
/* Try to log any options to the TPM, especially to catch manually edited options */
- (void) tpm_log_load_options(options);
+ (void) tpm_log_load_options(options, NULL);
}
efivar_set_time_usec(LOADER_GUID, L"LoaderTimeExecUSec", 0);
(void) efivar_set(LOADER_GUID, L"LoaderEntryLastBooted", NULL, EFI_VARIABLE_NON_VOLATILE);
}
+static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir) {
+ EFI_STATUS err;
+ _cleanup_(file_closep) EFI_FILE *keys_basedir = NULL;
+
+ if (secure_boot_mode() != SECURE_BOOT_SETUP)
+ return EFI_SUCCESS;
+
+ /* the lack of a 'keys' directory is not fatal and is silently ignored */
+ err = open_directory(root_dir, u"\\loader\\keys", &keys_basedir);
+ if (err == EFI_NOT_FOUND)
+ return EFI_SUCCESS;
+ if (err != EFI_SUCCESS)
+ return err;
+
+ for (;;) {
+ _cleanup_free_ EFI_FILE_INFO *dirent = NULL;
+ size_t dirent_size = 0;
+ ConfigEntry *entry = NULL;
+
+ err = readdir_harder(keys_basedir, &dirent, &dirent_size);
+ if (err != EFI_SUCCESS || !dirent)
+ return err;
+
+ if (dirent->FileName[0] == '.')
+ continue;
+
+ if (!FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY))
+ continue;
+
+ entry = xnew(ConfigEntry, 1);
+ *entry = (ConfigEntry) {
+ .id = xpool_print(L"secure-boot-keys-%s", dirent->FileName),
+ .title = xpool_print(L"Enroll Secure Boot keys: %s", dirent->FileName),
+ .path = xpool_print(L"\\loader\\keys\\%s", dirent->FileName),
+ .type = LOADER_SECURE_BOOT_KEYS,
+ .tries_done = -1,
+ .tries_left = -1,
+ };
+ config_add_entry(config, entry);
+
+ if (config->secure_boot_enroll == ENROLL_FORCE && strcaseeq16(dirent->FileName, u"auto"))
+ /* if we auto enroll successfully this call does not return, if it fails we still
+ * want to add other potential entries to the menu */
+ secure_boot_enroll_at(root_dir, entry->path);
+ }
+
+ return EFI_SUCCESS;
+}
+
static void export_variables(
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
const char16_t *loaded_image_path,
EFI_LOADER_FEATURE_XBOOTLDR |
EFI_LOADER_FEATURE_RANDOM_SEED |
EFI_LOADER_FEATURE_LOAD_DRIVER |
+ EFI_LOADER_FEATURE_SORT_KEY |
+ EFI_LOADER_FEATURE_SAVED_ENTRY |
+ EFI_LOADER_FEATURE_DEVICETREE |
0;
_cleanup_free_ char16_t *infostr = NULL, *typestr = NULL;
config_add_entry(config, entry);
}
+ /* find if secure boot signing keys exist and autoload them if necessary
+ otherwise creates menu entries so that the user can load them manually
+ if the secure-boot-enroll variable is set to no (the default), we do not
+ even search for keys on the ESP */
+ if (config->secure_boot_enroll != ENROLL_OFF)
+ secure_boot_discover_keys(config, root_dir);
+
if (config->entry_count == 0)
return;
break;
}
+ /* if auto enrollment is activated, we try to load keys for the given entry. */
+ if (entry->type == LOADER_SECURE_BOOT_KEYS && config.secure_boot_enroll != ENROLL_OFF) {
+ err = secure_boot_enroll_at(root_dir, entry->path);
+ if (err != EFI_SUCCESS)
+ return err;
+ continue;
+ }
+
/* Run special entry like "reboot" now. Those that have a loader
* will be handled by image_start() instead. */
if (entry->call && !entry->loader) {
UINTN n_tpm_pcr,
const char16_t *tpm_description,
void **ret_buffer,
- UINTN *ret_buffer_size) {
+ UINTN *ret_buffer_size,
+ bool *ret_measured) {
_cleanup_(file_closep) EFI_FILE *root = NULL, *extra_dir = NULL;
UINTN dirent_size = 0, buffer_size = 0, n_items = 0, n_allocated = 0;
_cleanup_(strv_freep) char16_t **items = NULL;
_cleanup_free_ void *buffer = NULL;
uint32_t inode = 1; /* inode counter, so that each item gets a new inode */
+ int measured = -1;
EFI_STATUS err;
assert(loaded_image);
assert(ret_buffer);
assert(ret_buffer_size);
- if (!loaded_image->DeviceHandle) {
- *ret_buffer = NULL;
- *ret_buffer_size = 0;
- return EFI_SUCCESS;
- }
+ if (!loaded_image->DeviceHandle)
+ goto nothing;
err = open_volume(loaded_image->DeviceHandle, &root);
- if (err == EFI_UNSUPPORTED) {
+ if (err == EFI_UNSUPPORTED)
/* Error will be unsupported if the bootloader doesn't implement the file system protocol on
* its file handles. */
- *ret_buffer = NULL;
- *ret_buffer_size = 0;
- return EFI_SUCCESS;
- }
+ goto nothing;
if (err != EFI_SUCCESS)
return log_error_status_stall(
err, L"Unable to open root directory: %r", err);
dropin_dir = rel_dropin_dir = xpool_print(L"%D.extra.d", loaded_image->FilePath);
err = open_directory(root, dropin_dir, &extra_dir);
- if (err == EFI_NOT_FOUND) {
+ if (err == EFI_NOT_FOUND)
/* No extra subdir, that's totally OK */
- *ret_buffer = NULL;
- *ret_buffer_size = 0;
- return EFI_SUCCESS;
- }
+ goto nothing;
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to open extra directory of loaded image: %r", err);
items[n_items] = NULL; /* Let's always NUL terminate, to make freeing via strv_free() easy */
}
- if (n_items == 0) {
+ if (n_items == 0)
/* Empty directory */
- *ret_buffer = NULL;
- *ret_buffer_size = 0;
- return EFI_SUCCESS;
- }
+ goto nothing;
/* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements
* are not dependent on read order) */
return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
for (UINTN i = 0; i < n_tpm_pcr; i++) {
+ bool m;
+
+ if (tpm_pcr[i] == UINT32_MAX) /* Disabled */
+ continue;
+
err = tpm_log_event(
tpm_pcr[i],
POINTER_TO_PHYSICAL_ADDRESS(buffer),
buffer_size,
- tpm_description);
- if (err != EFI_SUCCESS)
+ tpm_description,
+ &m);
+ if (err != EFI_SUCCESS) {
log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr[i], tpm_description, err);
+ measured = false;
+ continue;
+ }
+
+ measured = measured < 0 ? m : (measured && m);
}
*ret_buffer = TAKE_PTR(buffer);
*ret_buffer_size = buffer_size;
+ if (ret_measured)
+ *ret_measured = measured;
+
+ return EFI_SUCCESS;
+
+nothing:
+ *ret_buffer = NULL;
+ *ret_buffer_size = 0;
+
+ if (ret_measured)
+ *ret_measured = true;
+
return EFI_SUCCESS;
}
#pragma once
#include <efi.h>
+#include <stdbool.h>
#include <uchar.h>
EFI_STATUS pack_cpio(
UINTN n_tpm_pcr,
const char16_t *tpm_description,
void **ret_buffer,
- UINTN *ret_buffer_size);
+ UINTN *ret_buffer_size,
+ bool *ret_measured);
*/
/* allocate SizeOfImage + SectionAlignment because the new_buffer can move up to Alignment-1 bytes */
kernel.num = EFI_SIZE_TO_PAGES(ALIGN_TO(kernel_size_of_image, kernel_alignment) + kernel_alignment);
- err = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, kernel.num, &kernel.addr);
+ err = BS->AllocatePages(AllocateAnyPages, EfiLoaderCode, kernel.num, &kernel.addr);
if (err != EFI_SUCCESS)
return EFI_OUT_OF_RESOURCES;
new_buffer = PHYSICAL_ADDRESS_TO_POINTER(ALIGN_TO(kernel.addr, kernel_alignment));
#include <efi.h>
#include <efilib.h>
+#include "tpm-pcr.h"
#include "macro-fundamental.h"
#include "measure.h"
#include "missing_efi.h"
return tcg2_interface_check() || tcg1_interface_check();
}
-EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description) {
- EFI_TCG *tpm1;
+EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description, bool *ret_measured) {
EFI_TCG2 *tpm2;
+ EFI_STATUS err;
assert(description);
- /* PCR disabled */
- if (pcrindex == UINT32_MAX)
+ /* If EFI_SUCCESS is returned, will initialize ret_measured to true if we actually measured
+ * something, or false if measurement was turned off. */
+
+ if (pcrindex == UINT32_MAX) { /* PCR disabled? */
+ if (ret_measured)
+ *ret_measured = false;
+
return EFI_SUCCESS;
+ }
tpm2 = tcg2_interface_check();
if (tpm2)
- return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
+ err = tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
+ else {
+ EFI_TCG *tpm1;
- tpm1 = tcg1_interface_check();
- if (tpm1)
- return tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description);
+ tpm1 = tcg1_interface_check();
+ if (tpm1)
+ err = tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description);
+ else {
+ /* No active TPM found, so don't return an error */
- /* No active TPM found, so don't return an error */
- return EFI_SUCCESS;
+ if (ret_measured)
+ *ret_measured = false;
+
+ return EFI_SUCCESS;
+ }
+ }
+
+ if (err == EFI_SUCCESS && ret_measured)
+ *ret_measured = true;
+
+ return err;
+}
+
+EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char *description, bool *ret_measured) {
+ _cleanup_free_ char16_t *c = NULL;
+
+ if (description)
+ c = xstra_to_str(description);
+
+ return tpm_log_event(pcrindex, buffer, buffer_size, c, ret_measured);
}
-EFI_STATUS tpm_log_load_options(const char16_t *load_options) {
+EFI_STATUS tpm_log_load_options(const char16_t *load_options, bool *ret_measured) {
+ int measured = -1;
EFI_STATUS err;
/* Measures a load options string into the TPM2, i.e. the kernel command line */
for (UINTN i = 0; i < 2; i++) {
uint32_t pcr = i == 0 ? TPM_PCR_INDEX_KERNEL_PARAMETERS : TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT;
+ bool m;
+
+ if (pcr == UINT32_MAX) /* Skip this one, if it's invalid, so that our 'measured' return value is not corrupted by it */
+ continue;
- err = tpm_log_event(pcr,
- POINTER_TO_PHYSICAL_ADDRESS(load_options),
- strsize16(load_options), load_options);
+ err = tpm_log_event(pcr, POINTER_TO_PHYSICAL_ADDRESS(load_options), strsize16(load_options), load_options, &m);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement to PCR %u: %r", pcr, err);
+
+ measured = measured < 0 ? m : (measured && m);
}
+ if (ret_measured)
+ *ret_measured = measured < 0 ? false : measured;
+
return EFI_SUCCESS;
}
#include <stdbool.h>
#include <uchar.h>
-/* This TPM PCR is where we extend the kernel command line and any passed credentials here. */
-#define TPM_PCR_INDEX_KERNEL_PARAMETERS 12U
-
-/* We used to write the kernel command line/credentials into PCR 8, in systemd <= 250. Let's provide for
- * some compatibility. (Remove in 2023!) */
-#if EFI_TPM_PCR_COMPAT
-#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT 8U
-#else
-#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT UINT32_MAX
-#endif
-
-/* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */
-#define TPM_PCR_INDEX_INITRD 4U
-
#if ENABLE_TPM
bool tpm_present(void);
-EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description);
-EFI_STATUS tpm_log_load_options(const char16_t *cmdline);
+EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description, bool *ret_measured);
+EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char *description, bool *ret_measured);
+EFI_STATUS tpm_log_load_options(const char16_t *cmdline, bool *ret_measured);
#else
return false;
}
-static inline EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description) {
+static inline EFI_STATUS tpm_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char16_t *description, bool *ret_measured) {
+ if (ret_measured)
+ *ret_measured = false;
+ return EFI_SUCCESS;
+}
+
+static inline EFI_STATUS tpm_log_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const char *description, bool *ret_measured) {
+ if (ret_measured)
+ *ret_measured = false;
return EFI_SUCCESS;
}
-static inline EFI_STATUS tpm_log_load_options(const char16_t *cmdline) {
+static inline EFI_STATUS tpm_log_load_options(const char16_t *cmdline, bool *ret_measured) {
+ if (ret_measured)
+ *ret_measured = false;
return EFI_SUCCESS;
}
]
)
+# On some distros, sd-boot/-stub may trigger some bug somewhere that will cause
+# kernel execution to fail. The cause seems to be purely based on code size and
+# always compiling with at least -O1 will work around that.
+# https://github.com/systemd/systemd/issues/24202
+efi_cflags += '-O1'
+
efi_cflags += cc.get_supported_arguments({
'ia32': ['-mno-sse', '-mno-mmx'],
'x86_64': ['-mno-red-zone', '-mno-sse', '-mno-mmx'],
efi_crt0,
]
-possible_link_flags = [
- '-Wl,--no-warn-execstack',
- '-Wl,--no-warn-rwx-segments',
-]
-efi_ldflags += cc.get_supported_link_arguments(possible_link_flags)
+foreach arg : ['-Wl,--no-warn-execstack',
+ '-Wl,--no-warn-rwx-segments']
+ # We need to check the correct linker for supported args. This is what
+ # cc.has_multi_link_arguments() is for, but it helpfully overrides our
+ # choice of linker by putting its own -fuse-ld= arg after ours.
+ if run_command('bash', '-c',
+ 'exec "$@" -x c -o/dev/null <(echo "int main(void){return 0;}")' +
+ ' -fuse-ld=' + efi_ld + ' -Wl,--fatal-warnings ' + arg,
+ 'bash', cc.cmd_array(),
+ check : false).returncode() == 0
+ efi_ldflags += arg
+ endif
+endforeach
if efi_arch[1] in ['aarch64', 'arm', 'riscv64']
efi_ldflags += ['-shared']
common_sources = files(
'assert.c',
+ 'console.c',
'devicetree.c',
'disk.c',
'efi-string.c',
systemd_boot_sources = files(
'boot.c',
- 'console.c',
'drivers.c',
'random-seed.c',
'shim.c',
} EFI_CONSOLE_CONTROL_PROTOCOL;
#endif
+
+#ifndef EFI_IMAGE_SECURITY_DATABASE_VARIABLE
+
+#define EFI_IMAGE_SECURITY_DATABASE_VARIABLE \
+ { 0xd719b2cb, 0x3d3a, 0x4596, {0xa3, 0xbc, 0xda, 0xd0, 0xe, 0x67, 0x65, 0x6f }}
+
+#endif
#include "sbat.h"
#include "secure-boot.h"
+#include "console.h"
#include "util.h"
bool secure_boot_enabled(void) {
#ifdef SBAT_DISTRO
static const char sbat[] _used_ _section_(".sbat") = SBAT_SECTION_TEXT;
#endif
+
+EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
+ assert(root_dir);
+ assert(path);
+
+ EFI_STATUS err;
+
+ clear_screen(COLOR_NORMAL);
+
+ Print(L"Enrolling secure boot keys from directory: %s\n"
+ L"Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n",
+ path);
+
+ unsigned timeout_sec = 15;
+ for(;;) {
+ /* Enrolling secure boot keys is safe to do in virtualized environments as there is nothing
+ * we can brick there. */
+ if (in_hypervisor())
+ break;
+
+ PrintAt(0, ST->ConOut->Mode->CursorRow, L"Enrolling in %2u s, press any key to abort.", timeout_sec);
+
+ uint64_t key;
+ err = console_key_read(&key, 1000 * 1000);
+ if (err == EFI_NOT_READY)
+ continue;
+ if (err == EFI_TIMEOUT) {
+ if (timeout_sec == 0) /* continue enrolling keys */
+ break;
+ timeout_sec--;
+ continue;
+ }
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Error waiting for user input to enroll Secure Boot keys: %r", err);
+
+ /* user aborted, returning EFI_SUCCESS here allows the user to go back to the menu */
+ return EFI_SUCCESS;
+ }
+
+ _cleanup_(file_closep) EFI_FILE *dir = NULL;
+
+ err = open_directory(root_dir, path, &dir);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed opening keys directory %s: %r", path, err);
+
+ struct {
+ const char16_t *name;
+ const char16_t *filename;
+ const EFI_GUID vendor;
+ char *buffer;
+ size_t size;
+ } sb_vars[] = {
+ { u"db", u"db.auth", EFI_IMAGE_SECURITY_DATABASE_VARIABLE, NULL, 0 },
+ { u"KEK", u"KEK.auth", EFI_GLOBAL_VARIABLE, NULL, 0 },
+ { u"PK", u"PK.auth", EFI_GLOBAL_VARIABLE, NULL, 0 },
+ };
+
+ /* Make sure all keys files exist before we start enrolling them by loading them from the disk first. */
+ for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++) {
+ err = file_read(dir, sb_vars[i].filename, 0, 0, &sb_vars[i].buffer, &sb_vars[i].size);
+ if (err != EFI_SUCCESS) {
+ log_error_stall(L"Failed reading file %s\\%s: %r", path, sb_vars[i].filename, err);
+ goto out_deallocate;
+ }
+ }
+
+ for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++) {
+ uint32_t sb_vars_opts =
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS |
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+
+ err = efivar_set_raw(&sb_vars[i].vendor, sb_vars[i].name, sb_vars[i].buffer, sb_vars[i].size, sb_vars_opts);
+ if (err != EFI_SUCCESS) {
+ log_error_stall(L"Failed to write %s secure boot variable: %r", sb_vars[i].name, err);
+ goto out_deallocate;
+ }
+ }
+
+ /* The system should be in secure boot mode now and we could continue a regular boot. But at least
+ * TPM PCR7 measurements should change on next boot. Reboot now so that any OS we load does not end
+ * up relying on the old PCR state. */
+ RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
+ assert_not_reached();
+
+out_deallocate:
+ for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++)
+ FreePool(sb_vars[i].buffer);
+
+ return err;
+}
#include <efi.h>
#include "efivars-fundamental.h"
+typedef enum {
+ ENROLL_OFF, /* no Secure Boot key enrollment whatsoever, even manual entries are not generated */
+ ENROLL_MANUAL, /* Secure Boot key enrollment is strictly manual: manual entries are generated and need to be selected by the user */
+ ENROLL_FORCE, /* Secure Boot key enrollment may be automatic if it is available but might not be safe */
+} secure_boot_enroll;
+
bool secure_boot_enabled(void);
SecureBootMode secure_boot_mode(void);
+
+EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path);
#include "pe.h"
#include "secure-boot.h"
#include "splash.h"
+#include "tpm-pcr.h"
#include "util.h"
/* magic string to find in the binary image */
}
static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
+ static const uint64_t stub_features =
+ EFI_STUB_FEATURE_REPORT_BOOT_PARTITION | /* We set LoaderDevicePartUUID */
+ EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */
+ EFI_STUB_FEATURE_PICK_UP_SYSEXTS | /* We pick up system extensions from the boot partition */
+ EFI_STUB_FEATURE_THREE_PCRS | /* We can measure kernel image, parameters and sysext */
+ 0;
+
char16_t uuid[37];
assert(loaded_image);
efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
}
- /* add StubInfo */
- if (efivar_get_raw(LOADER_GUID, L"StubInfo", NULL, NULL) != EFI_SUCCESS)
- efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0);
-}
-EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ /* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our
+ * own data) */
+ (void) efivar_set(LOADER_GUID, L"StubInfo", L"systemd-stub " GIT_VERSION, 0);
- enum {
- SECTION_CMDLINE,
- SECTION_LINUX,
- SECTION_INITRD,
- SECTION_SPLASH,
- SECTION_DTB,
- _SECTION_MAX,
- };
-
- static const char * const sections[_SECTION_MAX + 1] = {
- [SECTION_CMDLINE] = ".cmdline",
- [SECTION_LINUX] = ".linux",
- [SECTION_INITRD] = ".initrd",
- [SECTION_SPLASH] = ".splash",
- [SECTION_DTB] = ".dtb",
- NULL,
- };
+ (void) efivar_set_uint64_le(LOADER_GUID, L"StubFeatures", stub_features, 0);
+}
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
UINTN cmdline_len = 0, linux_size, initrd_size, dt_size;
UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0;
_cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL;
EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
_cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
- UINTN addrs[_SECTION_MAX] = {};
- UINTN szs[_SECTION_MAX] = {};
+ UINTN addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
char *cmdline = NULL;
_cleanup_free_ char *cmdline_owned = NULL;
+ int sections_measured = -1, parameters_measured = -1;
+ bool sysext_measured = false, m;
EFI_STATUS err;
InitializeLib(image, sys_table);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
- err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, szs);
- if (err != EFI_SUCCESS || szs[SECTION_LINUX] == 0) {
+ err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, addrs, szs);
+ if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) {
if (err == EFI_SUCCESS)
err = EFI_NOT_FOUND;
return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
}
+ /* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written
+ * into so far), so that we have one PCR that we can nicely write policies against because it
+ * contains all static data of this image, and thus can be easily be pre-calculated. */
+ for (UnifiedSection section = 0; section < _UNIFIED_SECTION_MAX; section++) {
+ m = false;
+
+ if (szs[section] == 0) /* not found */
+ continue;
+
+ /* First measure the name of the section */
+ (void) tpm_log_event_ascii(
+ TPM_PCR_INDEX_KERNEL_IMAGE,
+ POINTER_TO_PHYSICAL_ADDRESS(unified_sections[section]),
+ strsize8(unified_sections[section]), /* including NUL byte */
+ unified_sections[section],
+ &m);
+
+ sections_measured = sections_measured < 0 ? m : (sections_measured && m);
+
+ /* Then measure the data of the section */
+ (void) tpm_log_event_ascii(
+ TPM_PCR_INDEX_KERNEL_IMAGE,
+ POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[section],
+ szs[section],
+ unified_sections[section],
+ &m);
+
+ sections_measured = sections_measured < 0 ? m : (sections_measured && m);
+ }
+
+ /* After we are done, set an EFI variable that tells userspace this was done successfully, and encode
+ * in it which PCR was used. */
+ if (sections_measured > 0)
+ (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrKernelImage", TPM_PCR_INDEX_KERNEL_IMAGE, 0);
+
/* Show splash screen as early as possible */
- graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[SECTION_SPLASH], szs[SECTION_SPLASH], NULL);
+ graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH], NULL);
- if (szs[SECTION_CMDLINE] > 0) {
- cmdline = (char *) loaded_image->ImageBase + addrs[SECTION_CMDLINE];
- cmdline_len = szs[SECTION_CMDLINE];
+ if (szs[UNIFIED_SECTION_CMDLINE] > 0) {
+ cmdline = (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE];
+ cmdline_len = szs[UNIFIED_SECTION_CMDLINE];
}
- /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace the built-in one */
- if ((!secure_boot_enabled() || cmdline_len == 0) && loaded_image->LoadOptionsSize > 0 &&
- *(char16_t *) loaded_image->LoadOptions > 0x1F) {
+ /* if we are not in secure boot mode, or none was provided, accept a custom command line and replace
+ * the built-in one. We also do a superficial check whether first character of passed command line
+ * is printable character (for compat with some Dell systems which fill in garbage?). */
+ if ((!secure_boot_enabled() || cmdline_len == 0) &&
+ loaded_image->LoadOptionsSize > 0 &&
+ ((char16_t *) loaded_image->LoadOptions)[0] > 0x1F) {
cmdline_len = (loaded_image->LoadOptionsSize / sizeof(char16_t)) * sizeof(char);
- cmdline = cmdline_owned = xmalloc(cmdline_len);
+ cmdline = cmdline_owned = xnew(char, cmdline_len);
- for (UINTN i = 0; i < cmdline_len; i++)
- cmdline[i] = ((char16_t *) loaded_image->LoadOptions)[i];
+ for (UINTN i = 0; i < cmdline_len; i++) {
+ char16_t c = ((char16_t *) loaded_image->LoadOptions)[i];
+ cmdline[i] = c > 0x1F && c < 0x7F ? c : ' '; /* convert non-printable and non_ASCII characters to spaces. */
+ }
/* Let's measure the passed kernel command line into the TPM. Note that this possibly
* duplicates what we already did in the boot menu, if that was already used. However, since
* we want the boot menu to support an EFI binary, and want to this stub to be usable from
* any boot menu, let's measure things anyway. */
- (void) tpm_log_load_options(loaded_image->LoadOptions);
+ m = false;
+ (void) tpm_log_load_options(loaded_image->LoadOptions, &m);
+ parameters_measured = m;
}
export_variables(loaded_image);
- (void) pack_cpio(loaded_image,
- NULL,
- L".cred",
- ".extra/credentials",
- /* dir_mode= */ 0500,
- /* access_mode= */ 0400,
- /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
- /* n_tpm_pcr= */ 2,
- L"Credentials initrd",
- &credential_initrd,
- &credential_initrd_size);
-
- (void) pack_cpio(loaded_image,
- L"\\loader\\credentials",
- L".cred",
- ".extra/global_credentials",
- /* dir_mode= */ 0500,
- /* access_mode= */ 0400,
- /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
- /* n_tpm_pcr= */ 2,
- L"Global credentials initrd",
- &global_credential_initrd,
- &global_credential_initrd_size);
-
- (void) pack_cpio(loaded_image,
- NULL,
- L".raw",
- ".extra/sysext",
- /* dir_mode= */ 0555,
- /* access_mode= */ 0444,
- /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD },
- /* n_tpm_pcr= */ 1,
- L"System extension initrd",
- &sysext_initrd,
- &sysext_initrd_size);
-
- linux_size = szs[SECTION_LINUX];
- linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_LINUX];
-
- initrd_size = szs[SECTION_INITRD];
- initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_INITRD] : 0;
-
- dt_size = szs[SECTION_DTB];
- dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_DTB] : 0;
+ if (pack_cpio(loaded_image,
+ NULL,
+ L".cred",
+ ".extra/credentials",
+ /* dir_mode= */ 0500,
+ /* access_mode= */ 0400,
+ /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
+ /* n_tpm_pcr= */ 2,
+ L"Credentials initrd",
+ &credential_initrd,
+ &credential_initrd_size,
+ &m) == EFI_SUCCESS)
+ parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
+
+ if (pack_cpio(loaded_image,
+ L"\\loader\\credentials",
+ L".cred",
+ ".extra/global_credentials",
+ /* dir_mode= */ 0500,
+ /* access_mode= */ 0400,
+ /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
+ /* n_tpm_pcr= */ 2,
+ L"Global credentials initrd",
+ &global_credential_initrd,
+ &global_credential_initrd_size,
+ &m) == EFI_SUCCESS)
+ parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
+
+ if (pack_cpio(loaded_image,
+ NULL,
+ L".raw",
+ ".extra/sysext",
+ /* dir_mode= */ 0555,
+ /* access_mode= */ 0444,
+ /* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD_SYSEXTS },
+ /* n_tpm_pcr= */ 1,
+ L"System extension initrd",
+ &sysext_initrd,
+ &sysext_initrd_size,
+ &m) == EFI_SUCCESS)
+ sysext_measured = m;
+
+ if (parameters_measured > 0)
+ (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrKernelParameters", TPM_PCR_INDEX_KERNEL_PARAMETERS, 0);
+ if (sysext_measured)
+ (void) efivar_set_uint_string(LOADER_GUID, L"StubPcrInitRDSysExts", TPM_PCR_INDEX_INITRD_SYSEXTS, 0);
+
+ linux_size = szs[UNIFIED_SECTION_LINUX];
+ linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_LINUX];
+
+ initrd_size = szs[UNIFIED_SECTION_INITRD];
+ initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
+
+ dt_size = szs[UNIFIED_SECTION_DTB];
+ dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0;
if (credential_initrd || global_credential_initrd || sysext_initrd) {
/* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
#include <efi.h>
#include <efilib.h>
-#if defined(__i386__) || defined(__x86_64__)
-#include <cpuid.h>
-#endif
-#include <stdbool.h>
#include "ticks.h"
-
-#if defined(__i386__) || defined(__x86_64__)
-static bool in_hypervisor(void) {
- uint32_t eax, ebx, ecx, edx;
-
- /* The TSC might or might not be virtualized in VMs (and thus might not be accurate or start at zero
- * at boot), depending on hypervisor and CPU functionality. If it's not virtualized it's not useful
- * for keeping time, hence don't attempt to use it.
- *
- * This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
- * environment. */
-
- if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
- return false;
-
- return !!(ecx & 0x80000000U);
-}
-#endif
+#include "util.h"
#ifdef __x86_64__
static uint64_t ticks_read(void) {
uint64_t a, d;
+ /* The TSC might or might not be virtualized in VMs (and thus might not be accurate or start at zero
+ * at boot), depending on hypervisor and CPU functionality. If it's not virtualized it's not useful
+ * for keeping time, hence don't attempt to use it. */
if (in_hypervisor())
return 0;
#include <efi.h>
#include <efilib.h>
+#if defined(__i386__) || defined(__x86_64__)
+# include <cpuid.h>
+#endif
#include "ticks.h"
#include "util.h"
}
#endif
+
+#ifdef EFI_DEBUG
+void hexdump(const char16_t *prefix, const void *data, UINTN size) {
+ static const char hex[16] = "0123456789abcdef";
+ _cleanup_free_ char16_t *buf = NULL;
+ const uint8_t *d = data;
+
+ assert(prefix);
+ assert(data || size == 0);
+
+ /* Debugging helper — please keep this around, even if not used */
+
+ buf = xnew(char16_t, size*2+1);
+
+ for (UINTN i = 0; i < size; i++) {
+ buf[i*2] = hex[d[i] >> 4];
+ buf[i*2+1] = hex[d[i] & 0x0F];
+ }
+
+ buf[size*2] = 0;
+
+ log_error_stall(L"%s[%" PRIuN "]: %s", prefix, size, buf);
+}
+#endif
+
#if defined(__i386__) || defined(__x86_64__)
static inline uint8_t inb(uint16_t port) {
uint8_t value;
SetDevicePathEndNode(dp);
return EFI_SUCCESS;
}
+
+#if defined(__i386__) || defined(__x86_64__)
+bool in_hypervisor(void) {
+ uint32_t eax, ebx, ecx, edx;
+
+ /* This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
+ * environment. */
+
+ if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
+ return false;
+
+ return !!(ecx & 0x80000000U);
+}
+#endif
# define debug_hook(identity)
#endif
+#ifdef EFI_DEBUG
+void hexdump(const char16_t *prefix, const void *data, UINTN size);
+#endif
+
#if defined(__i386__) || defined(__x86_64__)
void beep(UINTN beep_count);
#else
EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file);
EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp);
+
+#if defined(__i386__) || defined(__x86_64__)
+bool in_hypervisor(void);
+#else
+static inline bool in_hypervisor(void) {
+ return false;
+}
+#endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "efi-loader.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hexdecoct.h"
+#include "main-func.h"
+#include "openssl-util.h"
+#include "parse-argument.h"
+#include "parse-util.h"
+#include "pretty-print.h"
+#include "terminal-util.h"
+#include "tpm-pcr.h"
+#include "tpm2-util.h"
+#include "verbs.h"
+
+/* Tool for pre-calculating expected TPM PCR values based on measured resources. This is intended to be used
+ * to pre-calculate suitable values for PCR 11, the way sd-stub measures into it. */
+
+static char *arg_sections[_UNIFIED_SECTION_MAX] = {};
+static char **arg_banks = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
+
+static inline void free_sections(char*(*sections)[_UNIFIED_SECTION_MAX]) {
+ for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++)
+ free((*sections)[c]);
+}
+
+STATIC_DESTRUCTOR_REGISTER(arg_sections, free_sections);
+
+static int help(int argc, char *argv[], void *userdata) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-measure", "1", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%1$s [OPTIONS...] COMMAND ...\n"
+ "\n%5$sPre-calculate PCR hash for kernel image.%6$s\n"
+ "\n%3$sCommands:%4$s\n"
+ " status Show current PCR values\n"
+ " calculate Calculate expected PCR values\n"
+ "\n%3$sOptions:%4$s\n"
+ " -h --help Show this help\n"
+ " --version Print version\n"
+ " --linux=PATH Path Linux kernel ELF image\n"
+ " --osrel=PATH Path to os-release file\n"
+ " --cmdline=PATH Path to file with kernel command line\n"
+ " --initrd=PATH Path to initrd image\n"
+ " --splash=PATH Path to splash bitmap\n"
+ " --dtb=PATH Path to Devicetree file\n"
+ " --bank=DIGEST Select TPM bank (SHA1, SHA256)\n"
+ "\nSee the %2$s for details.\n",
+ program_invocation_short_name,
+ link,
+ ansi_underline(),
+ ansi_normal(),
+ ansi_highlight(),
+ ansi_normal());
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_VERSION = 0x100,
+ _ARG_SECTION_FIRST,
+ ARG_LINUX = _ARG_SECTION_FIRST,
+ ARG_OSREL,
+ ARG_CMDLINE,
+ ARG_INITRD,
+ ARG_SPLASH,
+ _ARG_SECTION_LAST,
+ ARG_DTB = _ARG_SECTION_LAST,
+ ARG_BANK,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "linux", required_argument, NULL, ARG_LINUX },
+ { "osrel", required_argument, NULL, ARG_OSREL },
+ { "cmdline", required_argument, NULL, ARG_CMDLINE },
+ { "initrd", required_argument, NULL, ARG_INITRD },
+ { "splash", required_argument, NULL, ARG_SPLASH },
+ { "dtb", required_argument, NULL, ARG_DTB },
+ { "bank", required_argument, NULL, ARG_BANK },
+ {}
+ };
+
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ /* Make sure the arguments list and the section list, stays in sync */
+ assert_cc(_ARG_SECTION_FIRST + _UNIFIED_SECTION_MAX == _ARG_SECTION_LAST + 1);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ switch (c) {
+
+ case 'h':
+ help(0, NULL, NULL);
+ return 0;
+
+ case ARG_VERSION:
+ return version();
+
+ case _ARG_SECTION_FIRST..._ARG_SECTION_LAST: {
+ UnifiedSection section = c - _ARG_SECTION_FIRST;
+
+ r = parse_path_argument(optarg, /* suppress_root= */ false, arg_sections + section);
+ if (r < 0)
+ return r;
+ break;
+ }
+
+ case ARG_BANK: {
+ const EVP_MD *implementation;
+
+ implementation = EVP_get_digestbyname(optarg);
+ if (!implementation)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown bank '%s', refusing.", optarg);
+
+ if (strv_extend(&arg_banks, EVP_MD_name(implementation)) < 0)
+ return log_oom();
+
+ break;
+ }
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+
+ if (strv_isempty(arg_banks)) {
+ /* If no banks are specifically selected, pick all known banks */
+ arg_banks = strv_new("SHA1", "SHA256", "SHA384", "SHA512");
+ if (!arg_banks)
+ return log_oom();
+ }
+
+ strv_sort(arg_banks);
+ strv_uniq(arg_banks);
+
+ return 1;
+}
+
+typedef struct PcrState {
+ const EVP_MD *md;
+ void *value;
+ size_t value_size;
+} PcrState;
+
+static void pcr_state_free_all(PcrState **pcr_state) {
+ assert(pcr_state);
+
+ if (!*pcr_state)
+ return;
+
+ for (size_t i = 0; (*pcr_state)[i].value; i++)
+ free((*pcr_state)[i].value);
+
+ *pcr_state = mfree(*pcr_state);
+}
+
+static void evp_md_ctx_free_all(EVP_MD_CTX **md[]) {
+ assert(md);
+
+ if (!*md)
+ return;
+
+ for (size_t i = 0; (*md)[i]; i++)
+ EVP_MD_CTX_free((*md)[i]);
+
+ *md = mfree(*md);
+}
+
+static int pcr_state_extend(PcrState *pcr_state, const void *data, size_t sz) {
+ _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mc = NULL;
+ unsigned value_size;
+
+ assert(pcr_state);
+ assert(data || sz == 0);
+ assert(pcr_state->md);
+ assert(pcr_state->value);
+ assert(pcr_state->value_size > 0);
+
+ /* Extends a (virtual) PCR by the given data */
+
+ mc = EVP_MD_CTX_new();
+ if (!mc)
+ return log_oom();
+
+ if (EVP_DigestInit_ex(mc, pcr_state->md, NULL) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize %s context.", EVP_MD_name(pcr_state->md));
+
+ /* First thing we do, is hash the old PCR value */
+ if (EVP_DigestUpdate(mc, pcr_state->value, pcr_state->value_size) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
+
+ /* Then, we hash the new data */
+ if (EVP_DigestUpdate(mc, data, sz) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
+
+ if (EVP_DigestFinal_ex(mc, pcr_state->value, &value_size) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finalize hash context.");
+
+ assert(value_size == pcr_state->value_size);
+ return 0;
+}
+
+#define BUFFER_SIZE (16U * 1024U)
+
+static int measure_pcr(PcrState *pcr_states, size_t n) {
+ _cleanup_free_ void *buffer = NULL;
+ int r;
+
+ assert(n > 0);
+ assert(pcr_states);
+
+ buffer = malloc(BUFFER_SIZE);
+ if (!buffer)
+ return log_oom();
+
+ for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++) {
+ _cleanup_(evp_md_ctx_free_all) EVP_MD_CTX **mdctx = NULL;
+ _cleanup_close_ int fd = -1;
+ uint64_t m = 0;
+
+ if (!arg_sections[c])
+ continue;
+
+ fd = open(arg_sections[c], O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open '%s': %m", arg_sections[c]);
+
+ /* Allocate one message digest context per bank (NULL terminated) */
+ mdctx = new0(EVP_MD_CTX*, n + 1);
+ if (!mdctx)
+ return log_oom();
+
+ for (size_t i = 0; i < n; i++) {
+ mdctx[i] = EVP_MD_CTX_new();
+ if (!mdctx[i])
+ return log_oom();
+
+ if (EVP_DigestInit_ex(mdctx[i], pcr_states[i].md, NULL) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize data %s context.", EVP_MD_name(pcr_states[i].md));
+ }
+
+ for (;;) {
+ ssize_t sz;
+
+ sz = read(fd, buffer, BUFFER_SIZE);
+ if (sz < 0)
+ return log_error_errno(errno, "Failed to read '%s': %m", arg_sections[c]);
+ if (sz == 0) /* EOF */
+ break;
+
+ for (size_t i = 0; i < n; i++)
+ if (EVP_DigestUpdate(mdctx[i], buffer, sz) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to run digest.");
+
+ m += sz;
+ }
+
+ fd = safe_close(fd);
+
+ if (m == 0) /* We skip over empty files, the stub does so too */
+ continue;
+
+ for (size_t i = 0; i < n; i++) {
+ _cleanup_free_ void *data_hash = NULL;
+ unsigned data_hash_size;
+
+ data_hash = malloc(pcr_states[i].value_size);
+ if (!data_hash)
+ return log_oom();
+
+ /* Measure name of section */
+ if (EVP_Digest(unified_sections[c], strlen(unified_sections[c]) + 1, data_hash, &data_hash_size, pcr_states[i].md, NULL) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to hash section name with %s.", EVP_MD_name(pcr_states[i].md));
+
+ assert(data_hash_size == (unsigned) pcr_states[i].value_size);
+
+ r = pcr_state_extend(pcr_states + i, data_hash, data_hash_size);
+ if (r < 0)
+ return r;
+
+ /* Retrieve hash of data an measure it*/
+ if (EVP_DigestFinal_ex(mdctx[i], data_hash, &data_hash_size) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finalize hash context.");
+
+ assert(data_hash_size == (unsigned) pcr_states[i].value_size);
+
+ r = pcr_state_extend(pcr_states + i, data_hash, data_hash_size);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+static int verb_calculate(int argc, char *argv[], void *userdata) {
+ _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
+ size_t n = 0;
+ int r;
+
+ if (!arg_sections[UNIFIED_SECTION_LINUX])
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--linux= switch must be specified, refusing.");
+
+ pcr_states = new0(PcrState, strv_length(arg_banks) + 1);
+ if (!pcr_states)
+ return log_oom();
+
+ /* Allocate a PCR state structure, one for each bank */
+ STRV_FOREACH(d, arg_banks) {
+ const EVP_MD *implementation;
+ _cleanup_free_ void *v = NULL;
+ int sz;
+
+ assert_se(implementation = EVP_get_digestbyname(*d)); /* Must work, we already checked while parsing command line */
+
+ sz = EVP_MD_size(implementation);
+ if (sz <= 0 || sz >= INT_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected digest size: %i", sz);
+
+ v = malloc0(sz); /* initial PCR state is all zeroes */
+ if (!v)
+ return log_oom();
+
+ pcr_states[n++] = (struct PcrState) {
+ .md = implementation,
+ .value = TAKE_PTR(v),
+ .value_size = sz,
+ };
+ }
+
+ r = measure_pcr(pcr_states, n);
+ if (r < 0)
+ return r;
+
+ for (size_t i = 0; i < n; i++) {
+ _cleanup_free_ char *hd = NULL, *b = NULL;
+
+ hd = hexmem(pcr_states[i].value, pcr_states[i].value_size);
+ if (!hd)
+ return log_oom();
+
+ b = strdup(EVP_MD_name(pcr_states[i].md));
+ if (!b)
+ return log_oom();
+
+ printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, ascii_strlower(b), hd);
+ }
+
+ return 0;
+}
+
+static int compare_reported_pcr_nr(uint32_t pcr, const char *varname, const char *description) {
+ _cleanup_free_ char *s = NULL;
+ uint32_t v;
+ int r;
+
+ r = efi_get_variable_string(varname, &s);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to read EFI variable '%s': %m", varname);
+
+ r = safe_atou32(s, &v);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse EFI variable '%s': %s", varname, s);
+
+ if (pcr != v)
+ log_warning("PCR number reported by stub for %s (%" PRIu32 ") different from our expectation (%" PRIu32 ").\n"
+ "The measurements are likely inconsistent.", description, v, pcr);
+
+ return 0;
+}
+
+static int validate_stub(void) {
+ uint64_t features;
+ bool found = false;
+ int r;
+
+ if (tpm2_support() != TPM2_SUPPORT_FULL)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Sorry, system lacks full TPM2 support.");
+
+ r = efi_stub_get_features(&features);
+ if (r < 0)
+ return log_error_errno(r, "Unable to get stub features: %m");
+
+ if (!FLAGS_SET(features, EFI_STUB_FEATURE_THREE_PCRS))
+ log_warning("Warning: current kernel image does not support measuring itself, the command line or initrd system extension images.\n"
+ "The PCR measurements seen are unlikely to be valid.");
+
+ r = compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_IMAGE, EFI_LOADER_VARIABLE("StubPcrKernelImage"), "kernel image");
+ if (r < 0)
+ return r;
+
+ r = compare_reported_pcr_nr(TPM_PCR_INDEX_KERNEL_PARAMETERS, EFI_LOADER_VARIABLE("StubPcrKernelParameters"), "kernel parameters");
+ if (r < 0)
+ return r;
+
+ r = compare_reported_pcr_nr(TPM_PCR_INDEX_INITRD_SYSEXTS, EFI_LOADER_VARIABLE("StubPcrInitRDSysExts"), "initrd system extension images");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(bank, arg_banks) {
+ _cleanup_free_ char *b = NULL, *p = NULL;
+
+ b = strdup(*bank);
+ if (!b)
+ return log_oom();
+
+ if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/", ascii_strlower(b)) < 0)
+ return log_oom();
+
+ if (access(p, F_OK) < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to detect if '%s' exists: %m", b);
+ } else
+ found = true;
+ }
+
+ if (!found)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "None of the select PCR banks appear to exist.");
+
+ return 0;
+}
+
+static int verb_status(int argc, char *argv[], void *userdata) {
+
+ static const struct {
+ uint32_t nr;
+ const char *description;
+ } relevant_pcrs[] = {
+ { TPM_PCR_INDEX_KERNEL_IMAGE, "Unified Kernel Image" },
+ { TPM_PCR_INDEX_KERNEL_PARAMETERS, "Kernel Parameters" },
+ { TPM_PCR_INDEX_INITRD_SYSEXTS, "initrd System Extensions" },
+ };
+
+ int r;
+
+ r = validate_stub();
+ if (r < 0)
+ return r;
+
+ for (size_t i = 0; i < ELEMENTSOF(relevant_pcrs); i++) {
+
+ STRV_FOREACH(bank, arg_banks) {
+ _cleanup_free_ char *b = NULL, *p = NULL, *s = NULL, *f = NULL;
+ _cleanup_free_ void *h = NULL;
+ size_t l;
+
+ b = strdup(*bank);
+ if (!b)
+ return log_oom();
+
+ if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32, ascii_strlower(b), relevant_pcrs[i].nr) < 0)
+ return log_oom();
+
+ r = read_virtual_file(p, 4096, &s, NULL);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return log_error_errno(r, "Failed to read '%s': %m", p);
+
+ r = unhexmem(strstrip(s), SIZE_MAX, &h, &l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
+
+ f = hexmem(h, l);
+ if (!h)
+ return log_oom();
+
+ if (bank == arg_banks) {
+ /* before the first line for each PCR, write a short descriptive text to
+ * stderr, and leave the primary content on stdout */
+ fflush(stdout);
+ fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n",
+ ansi_grey(),
+ relevant_pcrs[i].nr,
+ relevant_pcrs[i].description,
+ memeqzero(h, l) ? " (NOT SET!)" : "",
+ ansi_normal());
+ fflush(stderr);
+ }
+
+ printf("%" PRIu32 ":%s=%s\n", relevant_pcrs[i].nr, b, f);
+ }
+ }
+
+ return 0;
+}
+
+static int measure_main(int argc, char *argv[]) {
+ static const Verb verbs[] = {
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
+ { "calculate", VERB_ANY, 1, 0, verb_calculate },
+ {}
+ };
+
+ return dispatch_verb(argc, argv, verbs, NULL);
+}
+
+static int run(int argc, char *argv[]) {
+ int r;
+
+ log_show_color(true);
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ return measure_main(argc, argv);
+}
+
+DEFINE_MAIN_FUNCTION(run);
_cleanup_close_ int fd = -1;
ssize_t n;
size_t l;
+ int r;
+
+ r = rearrange_stdio(-1, -1, -1);
+ if (r < 0) {
+ log_error_errno(r, "Failed to connect stdin/stdout/stderr with /dev/null: %m");
+ return EXIT_FAILURE;
+ }
if (argc != 2) {
log_error("Incorrect number of arguments.");
endif
bpf_clang_flags = [
+ '-std=gnu11',
'-Wno-compare-distinct-pointer-types',
'-O2',
'-target',
]
bpf_gcc_flags = [
+ '-std=gnu11',
'-O2',
'-mkernel=5.2',
'-mcpu=v3',
'-mco-re',
'-gbtf',
+ '-c',
]
# Generate defines that are appropriate to tell the compiler what architecture
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
-const volatile __u8 is_allow_list = 0;
+const volatile __u8 is_allow_list SEC(".rodata") = 0;
/* Map containing the network interfaces indexes.
* The interpretation of the map depends on the value of is_allow_list.
#include "bpf-socket-bind.h"
#include "btrfs-util.h"
#include "bus-error.h"
+#include "bus-locator.h"
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "cgroup.h"
static void cgroup_apply_unified_cpu_weight(Unit *u, uint64_t weight) {
char buf[DECIMAL_STR_MAX(uint64_t) + 2];
+ if (weight == CGROUP_WEIGHT_IDLE)
+ return;
xsprintf(buf, "%" PRIu64 "\n", weight);
(void) set_attribute_and_warn(u, "cpu", "cpu.weight", buf);
}
+static void cgroup_apply_unified_cpu_idle(Unit *u, uint64_t weight) {
+ int r;
+ bool is_idle;
+ const char *idle_val;
+
+ is_idle = weight == CGROUP_WEIGHT_IDLE;
+ idle_val = one_zero(is_idle);
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.idle", idle_val);
+ if (r < 0 && (r != -ENOENT || is_idle))
+ log_unit_full_errno(u, LOG_LEVEL_CGROUP_WRITE(r), r, "Failed to set '%s' attribute on '%s' to '%s': %m",
+ "cpu.idle", empty_to_root(u->cgroup_path), idle_val);
+}
+
static void cgroup_apply_unified_cpu_quota(Unit *u, usec_t quota, usec_t period) {
char buf[(DECIMAL_STR_MAX(usec_t) + 1) * 2 + 1];
}
static uint64_t cgroup_cpu_weight_to_shares(uint64_t weight) {
+ /* we don't support idle in cgroupv1 */
+ if (weight == CGROUP_WEIGHT_IDLE)
+ return CGROUP_CPU_SHARES_MIN;
+
return CLAMP(weight * CGROUP_CPU_SHARES_DEFAULT / CGROUP_WEIGHT_DEFAULT,
CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX);
}
} else
weight = CGROUP_WEIGHT_DEFAULT;
+ cgroup_apply_unified_cpu_idle(u, weight);
cgroup_apply_unified_cpu_weight(u, weight);
cgroup_apply_unified_cpu_quota(u, c->cpu_quota_per_sec_usec, c->cpu_quota_period_usec);
pp = strjoina("/", pp, suffix_path);
path_simplify(pp);
- r = sd_bus_call_method(u->manager->system_bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "AttachProcessesToUnit",
- &error, NULL,
- "ssau",
- NULL /* empty unit name means client's unit, i.e. us */, pp, 1, (uint32_t) pid);
+ r = bus_call_method(u->manager->system_bus,
+ bus_systemd_mgr,
+ "AttachProcessesToUnit",
+ &error, NULL,
+ "ssau",
+ NULL /* empty unit name means client's unit, i.e. us */, pp, 1, (uint32_t) pid);
if (r < 0)
return log_unit_debug_errno(u, r, "Failed to attach unit process " PID_FMT " via the bus: %s", pid, bus_error_message(&error, r));
}
DISABLE_WARNING_TYPE_LIMITS;
-BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_weight, CGROUP_MASK_CPU, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID);
BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_OK, CGROUP_CPU_SHARES_INVALID);
BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID);
BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEIGHT_IS_OK, CGROUP_BLKIO_WEIGHT_INVALID);
BUS_DEFINE_SET_CGROUP_LIMIT(swap, CGROUP_MASK_MEMORY, physical_memory_scale, 0);
REENABLE_WARNING;
+static int bus_cgroup_set_cpu_weight(
+ Unit *u,
+ const char *name,
+ uint64_t *p,
+ sd_bus_message *message,
+ UnitWriteFlags flags,
+ sd_bus_error *error) {
+ uint64_t v;
+ int r;
+ assert(p);
+ r = sd_bus_message_read(message, "t", &v);
+ if (r < 0)
+ return r;
+ if (!CGROUP_WEIGHT_IS_OK(v) && v != CGROUP_WEIGHT_IDLE)
+ return sd_bus_error_setf(
+ error, SD_BUS_ERROR_INVALID_ARGS, "Value specified in %s is out of range", name);
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ *p = v;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+ if (v == CGROUP_WEIGHT_INVALID)
+ unit_write_settingf(u, flags, name, "%s=", name);
+ else if (v == CGROUP_WEIGHT_IDLE)
+ unit_write_settingf(u, flags, name, "%s=idle", name);
+ else
+ unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, v);
+ }
+ return 1;
+}
+
static int bus_cgroup_set_tasks_max(
Unit *u,
const char *name,
SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultTimeoutAbortUSec", "t", property_get_default_timeout_abort_usec, 0, 0),
+ SD_BUS_PROPERTY("DefaultDeviceTimeoutUSec", "t", bus_property_get_usec, offsetof(Manager, default_device_timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultStartLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
/* The following two items are obsolete alias */
r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, flags, error);
if (r != 0)
return r;
+
+ if (streq(name, "User"))
+ return bus_set_transient_user_relaxed(u, name, &s->user, message, flags, error);
+
+ if (streq(name, "Group"))
+ return bus_set_transient_user_relaxed(u, name, &s->group, message, flags, error);
}
return 0;
SD_BUS_PROPERTY("RequiresMountsFor", "as", property_get_requires_mounts_for, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("AccessSELinuxContext", "s", NULL, offsetof(Unit, access_selinux_context), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("FreezerState", "s", property_get_freezer_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
if (r < 0)
return r;
- if (FLAGS_SET(flags, BUS_UNIT_QUEUE_RETURN_SKIP_ON_CONDITION_FAIL))
- j->return_skip_on_cond_failure = true;
-
r = bus_job_track_sender(j, message);
if (r < 0)
return r;
if (r < 0)
return r;
- if (!UNIT_VTABLE(u)->bus_set_property)
- return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY,
- "Objects of this type do not support setting properties.");
-
r = sd_bus_message_enter_container(message, 'v', NULL);
if (r < 0)
return r;
/* If not for real, then mask out the two target flags */
f = for_real ? flags : (flags & ~(UNIT_RUNTIME|UNIT_PERSISTENT));
- r = UNIT_VTABLE(u)->bus_set_property(u, name, message, f, error);
+ if (UNIT_VTABLE(u)->bus_set_property)
+ r = UNIT_VTABLE(u)->bus_set_property(u, name, message, f, error);
+ else
+ r = 0;
if (r == 0 && u->transient && u->load_state == UNIT_STUB)
r = bus_unit_set_transient_property(u, name, message, f, error);
if (r == 0)
typedef enum BusUnitQueueFlags {
BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0,
BUS_UNIT_QUEUE_VERBOSE_REPLY = 1 << 1,
- BUS_UNIT_QUEUE_RETURN_SKIP_ON_CONDITION_FAIL = 1 << 2, // FIXME: currently not used, will be changed soon
} BusUnitQueueFlags;
int bus_unit_queue_job_one(
#include "string-util.h"
#include "strv.h"
#include "strxcpyx.h"
+#include "umask-util.h"
#include "user-util.h"
#define CONNECTIONS_MAX 4096
if (fd < 0)
return log_error_errno(errno, "Failed to allocate private socket: %m");
- r = bind(fd, &sa.sa, sa_len);
+ RUN_WITH_UMASK(0077)
+ r = bind(fd, &sa.sa, sa_len);
if (r < 0)
return log_error_errno(errno, "Failed to bind private socket: %m");
};
static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata);
-static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask);
static void device_unset_sysfs(Device *d) {
Hashmap *devices;
* indefinitely for plugged in devices, something which cannot
* happen for the other units since their operations time out
* anyway. */
- u->job_running_timeout = u->manager->default_timeout_start_usec;
+ u->job_running_timeout = u->manager->default_device_timeout_usec;
u->ignore_on_isolate = true;
device_unset_sysfs(d);
d->wants_property = strv_free(d->wants_property);
+ d->path = mfree(d->path);
}
static int device_load(Unit *u) {
unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], 0);
}
+static void device_found_changed(Device *d, DeviceFound previous, DeviceFound now) {
+ assert(d);
+
+ /* Didn't exist before, but does now? if so, generate a new invocation ID for it */
+ if (previous == DEVICE_NOT_FOUND && now != DEVICE_NOT_FOUND)
+ (void) unit_acquire_invocation_id(UNIT(d));
+
+ if (FLAGS_SET(now, DEVICE_FOUND_UDEV))
+ /* When the device is known to udev we consider it plugged. */
+ device_set_state(d, DEVICE_PLUGGED);
+ else if (now != DEVICE_NOT_FOUND && !FLAGS_SET(previous, DEVICE_FOUND_UDEV))
+ /* If the device has not been seen by udev yet, but is now referenced by the kernel, then we assume the
+ * kernel knows it now, and udev might soon too. */
+ device_set_state(d, DEVICE_TENTATIVE);
+ else
+ /* If nobody sees the device, or if the device was previously seen by udev and now is only referenced
+ * from the kernel, then we consider the device is gone, the kernel just hasn't noticed it yet. */
+ device_set_state(d, DEVICE_DEAD);
+}
+
+static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) {
+ assert(d);
+
+ if (MANAGER_IS_RUNNING(UNIT(d)->manager)) {
+ DeviceFound n, previous;
+
+ /* When we are already running, then apply the new mask right-away, and trigger state changes
+ * right-away */
+
+ n = (d->found & ~mask) | (found & mask);
+ if (n == d->found)
+ return;
+
+ previous = d->found;
+ d->found = n;
+
+ device_found_changed(d, previous, n);
+ } else
+ /* We aren't running yet, let's apply the new mask to the shadow variable instead, which we'll apply as
+ * soon as we catch-up with the state. */
+ d->enumerated_found = (d->enumerated_found & ~mask) | (found & mask);
+}
+
+static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFound found, DeviceFound mask) {
+ Device *l;
+
+ assert(m);
+ assert(sysfs);
+
+ if (mask == 0)
+ return;
+
+ l = hashmap_get(m->devices_by_sysfs, sysfs);
+ LIST_FOREACH(same_sysfs, d, l)
+ device_update_found_one(d, found, mask);
+}
+
+static void device_update_found_by_name(Manager *m, const char *path, DeviceFound found, DeviceFound mask) {
+ _cleanup_free_ char *e = NULL;
+ Unit *u;
+ int r;
+
+ assert(m);
+ assert(path);
+
+ if (mask == 0)
+ return;
+
+ r = unit_name_from_path(path, ".device", &e);
+ if (r < 0)
+ return (void) log_debug_errno(r, "Failed to generate unit name from device path, ignoring: %m");
+
+ u = manager_get_unit(m, e);
+ if (!u)
+ return;
+
+ device_update_found_one(DEVICE(u), found, mask);
+}
+
static int device_coldplug(Unit *u) {
Device *d = DEVICE(u);
* OPTIONS="db_persist". Hence, almost no devices are enumerated in the step 2. However, in general,
* we have several serialized devices. So, DEVICE_FOUND_UDEV bit in the deserialized_found must be
* ignored, as udev rules in initramfs and the main system are often different. If the deserialized
- * state is DEVICE_PLUGGED, we need to downgrade it to DEVICE_TENTATIVE (or DEVICE_DEAD if nobody
- * sees the device). Unlike the other starting mode, Manager.honor_device_enumeration == false
- * (maybe, it is better to rename the flag) when device_coldplug() and device_catchup() are called.
+ * state is DEVICE_PLUGGED, we need to downgrade it to DEVICE_TENTATIVE. Unlike the other starting
+ * mode, MANAGER_IS_SWITCHING_ROOT() is true when device_coldplug() and device_catchup() are called.
* Hence, let's conditionalize the operations by using the flag. After switch-root, systemd-udevd
* will (re-)process all devices, and the Device.found and Device.state will be adjusted.
*
* Of course, deserialized parameters may be outdated, but the unit state can be adjusted later by
* device_catchup() or uevents. */
- if (!m->honor_device_enumeration && !MANAGER_IS_USER(m) &&
+ if (MANAGER_IS_SWITCHING_ROOT(m) &&
!FLAGS_SET(d->enumerated_found, DEVICE_FOUND_UDEV)) {
found &= ~DEVICE_FOUND_UDEV; /* ignore DEVICE_FOUND_UDEV bit */
if (state == DEVICE_PLUGGED)
assert(f);
assert(fds);
+ if (d->path)
+ (void) serialize_item(f, "path", d->path);
+
(void) serialize_item(f, "state", device_state_to_string(d->state));
if (device_found_to_string_many(d->found, &s) >= 0)
assert(value);
assert(fds);
- if (streq(key, "state")) {
+ if (streq(key, "path")) {
+ if (!d->path) {
+ d->path = strdup(value);
+ if (!d->path)
+ log_oom_debug();
+ }
+
+ } else if (streq(key, "state")) {
DeviceState state;
state = device_state_from_string(value);
fprintf(f,
"%sDevice State: %s\n"
+ "%sDevice Path: %s\n"
"%sSysfs Path: %s\n"
"%sFound: %s\n",
prefix, device_state_to_string(d->state),
+ prefix, strna(d->path),
prefix, strna(d->sysfs),
prefix, strna(s));
if (d->state != DEVICE_DEAD)
/* So here's a special hack, to compensate for the fact that the udev database's reload cycles are not
* synchronized with our own reload cycles: when we detect that the SYSTEMD_WANTS property of a device
- * changes while the device unit is already up, let's manually trigger any new units listed in it not
- * seen before. This typically happens during the boot-time switch root transition, as udev devices
- * will generally already be up in the initrd, but SYSTEMD_WANTS properties get then added through udev
- * rules only available on the host system, and thus only when the initial udev coldplug trigger runs.
+ * changes while the device unit is already up, let's skip to trigger units that were already listed
+ * and are active, and start units otherwise. This typically happens during the boot-time switch root
+ * transition, as udev devices will generally already be up in the initrd, but SYSTEMD_WANTS properties
+ * get then added through udev rules only available on the host system, and thus only when the initial
+ * udev coldplug trigger runs.
*
* We do this only if the device has been up already when we parse this, as otherwise the usual
* dependency logic that is run from the dead → plugged transition will trigger these deps. */
STRV_FOREACH(i, added) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- if (strv_contains(d->wants_property, *i)) /* Was this unit already listed before? */
- continue;
+ if (strv_contains(d->wants_property, *i)) {
+ Unit *v;
+
+ v = manager_get_unit(u->manager, *i);
+ if (v && UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(v)))
+ continue; /* The unit was already listed and is running. */
+ }
r = manager_add_job_by_name(u->manager, JOB_START, *i, JOB_FAIL, NULL, &error, NULL);
if (r < 0)
* dependency on the mount unit which was added during the loading of the later. When the device is
* plugged the sysfs might not be initialized yet, as we serialize the device's state but do not
* serialize the sysfs path across reloads/reexecs. Hence, when coming back from a reload/restart we
- * might have the state valid, but not the sysfs path. Hence, let's filter out conflicting devices, but
- * let's accept devices in any state with no sysfs path set. */
-
- if (DEVICE(u)->state == DEVICE_PLUGGED &&
- DEVICE(u)->sysfs &&
- sysfs &&
- !path_equal(DEVICE(u)->sysfs, sysfs))
- return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EEXIST),
- "Device %s appeared twice with different sysfs paths %s and %s, ignoring the latter.",
- e, DEVICE(u)->sysfs, sysfs);
+ * might have the state valid, but not the sysfs path. Also, there is another possibility; when multiple
+ * devices have the same devlink (e.g. /dev/disk/by-uuid/xxxx), adding/updating/removing one of the
+ * device causes syspath change. Hence, let's always update sysfs path.*/
/* Let's remove all dependencies generated due to udev properties. We'll re-add whatever is configured
* now below. */
unit_add_to_load_queue(u);
}
+ if (!DEVICE(u)->path) {
+ DEVICE(u)->path = strdup(path);
+ if (!DEVICE(u)->path)
+ return log_oom();
+ }
+
/* If this was created via some dependency and has not actually been seen yet ->sysfs will not be
* initialized. Hence initialize it if necessary. */
if (sysfs) {
return 0;
}
-static void device_process_new(Manager *m, sd_device *dev, const char *sysfs) {
- const char *dn, *alias;
- dev_t devnum;
+static bool device_is_ready(sd_device *dev) {
int r;
- assert(m);
assert(dev);
- assert(sysfs);
- /* Add the main unit named after the sysfs path. If this one fails, don't bother with the rest, as
- * this one shall be the main device unit the others just follow. (Compare with how
- * device_following() is implemented, see below, which looks for the sysfs device.) */
- if (device_setup_unit(m, dev, sysfs, true) < 0)
- return;
+ r = device_is_renaming(dev);
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to check if device is renaming, assuming device is not renaming: %m");
+ if (r > 0) {
+ log_device_debug(dev, "Device busy: device is renaming");
+ return false;
+ }
- /* Add an additional unit for the device node */
- if (sd_device_get_devname(dev, &dn) >= 0)
- (void) device_setup_unit(m, dev, dn, false);
-
- /* Add additional units for all symlinks */
- if (sd_device_get_devnum(dev, &devnum) >= 0) {
- const char *p;
-
- FOREACH_DEVICE_DEVLINK(dev, p) {
- struct stat st;
-
- if (PATH_STARTSWITH_SET(p, "/dev/block/", "/dev/char/"))
- continue;
-
- /* Verify that the symlink in the FS actually belongs to this device. This is useful
- * to deal with conflicting devices, e.g. when two disks want the same
- * /dev/disk/by-label/xxx link because they have the same label. We want to make sure
- * that the same device that won the symlink wins in systemd, so we check the device
- * node major/minor */
- if (stat(p, &st) >= 0 &&
- ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) ||
- st.st_rdev != devnum)) {
- log_device_debug(dev, "Skipping device unit creation for symlink %s not owned by device", p);
- continue;
- }
+ /* Is it really tagged as 'systemd' right now? */
+ r = sd_device_has_current_tag(dev, "systemd");
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to check if device has \"systemd\" tag, assuming device is not tagged with \"systemd\": %m");
+ if (r == 0)
+ log_device_debug(dev, "Device busy: device is not tagged with \"systemd\"");
+ if (r <= 0)
+ return false;
- (void) device_setup_unit(m, dev, p, false);
- }
- }
+ r = device_get_property_bool(dev, "SYSTEMD_READY");
+ if (r < 0 && r != -ENOENT)
+ log_device_warning_errno(dev, r, "Failed to get device SYSTEMD_READY property, assuming device does not have \"SYSTEMD_READY\" property: %m");
+ if (r == 0)
+ log_device_debug(dev, "Device busy: SYSTEMD_READY property from device is false");
- /* Add additional units for all explicitly configured aliases */
- r = sd_device_get_property_value(dev, "SYSTEMD_ALIAS", &alias);
- if (r < 0) {
- if (r != -ENOENT)
- log_device_error_errno(dev, r, "Failed to get SYSTEMD_ALIAS property, ignoring: %m");
- return;
- }
+ return r != 0;
+}
- for (;;) {
- _cleanup_free_ char *word = NULL;
+static int device_setup_devlink_unit_one(Manager *m, const char *devlink, sd_device **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ int r;
- r = extract_first_word(&alias, &word, NULL, EXTRACT_UNQUOTE);
- if (r == 0)
- break;
- if (r == -ENOMEM)
- return (void) log_oom();
- if (r < 0)
- return (void) log_device_warning_errno(dev, r, "Failed to parse SYSTEMD_ALIAS property, ignoring: %m");
+ assert(m);
+ assert(devlink);
- if (!path_is_absolute(word))
- log_device_warning(dev, "SYSTEMD_ALIAS is not an absolute path, ignoring: %s", word);
- else if (!path_is_normalized(word))
- log_device_warning(dev, "SYSTEMD_ALIAS is not a normalized path, ignoring: %s", word);
- else
- (void) device_setup_unit(m, dev, word, false);
+ if (sd_device_new_from_devname(&dev, devlink) < 0 || !device_is_ready(dev)) {
+ /* the devlink is already removed or not ready */
+ device_update_found_by_name(m, devlink, 0, DEVICE_FOUND_UDEV);
+ *ret = NULL;
+ return 0; /* not ready */
}
+
+ r = device_setup_unit(m, dev, devlink, /* main = */ false);
+ if (r < 0)
+ return log_device_warning_errno(dev, r, "Failed to setup unit for '%s': %m", devlink);
+
+ *ret = TAKE_PTR(dev);
+ return 1; /* ready */
}
-static void device_found_changed(Device *d, DeviceFound previous, DeviceFound now) {
- assert(d);
+static int device_setup_devlink_units(Manager *m, sd_device *dev, char ***ret_ready_devlinks) {
+ _cleanup_strv_free_ char **ready_devlinks = NULL;
+ const char *devlink, *syspath;
+ int r;
- /* Didn't exist before, but does now? if so, generate a new invocation ID for it */
- if (previous == DEVICE_NOT_FOUND && now != DEVICE_NOT_FOUND)
- (void) unit_acquire_invocation_id(UNIT(d));
+ assert(m);
+ assert(dev);
+ assert(ret_ready_devlinks);
- if (FLAGS_SET(now, DEVICE_FOUND_UDEV))
- /* When the device is known to udev we consider it plugged. */
- device_set_state(d, DEVICE_PLUGGED);
- else if (now != DEVICE_NOT_FOUND && !FLAGS_SET(previous, DEVICE_FOUND_UDEV))
- /* If the device has not been seen by udev yet, but is now referenced by the kernel, then we assume the
- * kernel knows it now, and udev might soon too. */
- device_set_state(d, DEVICE_TENTATIVE);
- else
- /* If nobody sees the device, or if the device was previously seen by udev and now is only referenced
- * from the kernel, then we consider the device is gone, the kernel just hasn't noticed it yet. */
- device_set_state(d, DEVICE_DEAD);
-}
+ r = sd_device_get_syspath(dev, &syspath);
+ if (r < 0)
+ return r;
-static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) {
- assert(d);
+ FOREACH_DEVICE_DEVLINK(dev, devlink) {
+ _cleanup_(sd_device_unrefp) sd_device *assigned = NULL;
+ const char *s;
- if (MANAGER_IS_RUNNING(UNIT(d)->manager)) {
- DeviceFound n, previous;
+ if (PATH_STARTSWITH_SET(devlink, "/dev/block/", "/dev/char/"))
+ continue;
- /* When we are already running, then apply the new mask right-away, and trigger state changes
- * right-away */
+ if (device_setup_devlink_unit_one(m, devlink, &assigned) <= 0)
+ continue;
- n = (d->found & ~mask) | (found & mask);
- if (n == d->found)
- return;
+ if (sd_device_get_syspath(assigned, &s) < 0)
+ continue;
- previous = d->found;
- d->found = n;
+ if (path_equal(s, syspath))
+ continue;
- device_found_changed(d, previous, n);
- } else
- /* We aren't running yet, let's apply the new mask to the shadow variable instead, which we'll apply as
- * soon as we catch-up with the state. */
- d->enumerated_found = (d->enumerated_found & ~mask) | (found & mask);
+ r = strv_extend(&ready_devlinks, devlink);
+ if (r < 0)
+ return -ENOMEM;
+ }
+
+ *ret_ready_devlinks = TAKE_PTR(ready_devlinks);
+ return 0;
}
-static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFound found, DeviceFound mask) {
+static int device_setup_devlink_units_on_remove(Manager *m, sd_device *dev, char ***ret_ready_devlinks) {
+ _cleanup_strv_free_ char **ready_devlinks = NULL;
+ const char *syspath;
Device *l;
+ int r;
assert(m);
- assert(sysfs);
+ assert(dev);
+ assert(ret_ready_devlinks);
- if (mask == 0)
- return;
+ r = sd_device_get_syspath(dev, &syspath);
+ if (r < 0)
+ return r;
- l = hashmap_get(m->devices_by_sysfs, sysfs);
- LIST_FOREACH(same_sysfs, d, l)
- device_update_found_one(d, found, mask);
-}
+ l = hashmap_get(m->devices_by_sysfs, syspath);
+ LIST_FOREACH(same_sysfs, d, l) {
+ _cleanup_(sd_device_unrefp) sd_device *assigned = NULL;
+ const char *s;
-static void device_update_found_by_name(Manager *m, const char *path, DeviceFound found, DeviceFound mask) {
- _cleanup_free_ char *e = NULL;
- Unit *u;
- int r;
+ if (!d->path)
+ continue;
- assert(m);
- assert(path);
+ if (!path_startswith(d->path, "/dev/"))
+ continue;
- if (mask == 0)
- return;
+ if (device_setup_devlink_unit_one(m, d->path, &assigned) <= 0)
+ continue;
- r = unit_name_from_path(path, ".device", &e);
- if (r < 0)
- return (void) log_debug_errno(r, "Failed to generate unit name from device path, ignoring: %m");
+ if (sd_device_get_syspath(assigned, &s) < 0)
+ continue;
- u = manager_get_unit(m, e);
- if (!u)
- return;
+ if (path_equal(s, syspath))
+ continue;
- device_update_found_one(DEVICE(u), found, mask);
+ r = strv_extend(&ready_devlinks, d->path);
+ if (r < 0)
+ return -ENOMEM;
+ }
+
+ *ret_ready_devlinks = TAKE_PTR(ready_devlinks);
+ return 0;
}
-static bool device_is_ready(sd_device *dev) {
+static void device_process_new(Manager *m, sd_device *dev, const char *sysfs) {
+ const char *dn, *alias;
int r;
+ assert(m);
assert(dev);
+ assert(sysfs);
- r = device_is_renaming(dev);
- if (r < 0)
- log_device_warning_errno(dev, r, "Failed to check if device is renaming, assuming device is not renaming: %m");
- if (r > 0) {
- log_device_debug(dev, "Device busy: device is renaming");
- return false;
+ /* Add the main unit named after the sysfs path. If this one fails, don't bother with the rest, as
+ * this one shall be the main device unit the others just follow. (Compare with how
+ * device_following() is implemented, see below, which looks for the sysfs device.) */
+ if (device_setup_unit(m, dev, sysfs, /* main = */ true) < 0)
+ return;
+
+ /* Add an additional unit for the device node */
+ if (sd_device_get_devname(dev, &dn) >= 0)
+ (void) device_setup_unit(m, dev, dn, /* main = */ false);
+
+ /* Add additional units for all explicitly configured aliases */
+ r = sd_device_get_property_value(dev, "SYSTEMD_ALIAS", &alias);
+ if (r < 0) {
+ if (r != -ENOENT)
+ log_device_error_errno(dev, r, "Failed to get SYSTEMD_ALIAS property, ignoring: %m");
+ return;
}
- /* Is it really tagged as 'systemd' right now? */
- r = sd_device_has_current_tag(dev, "systemd");
- if (r < 0)
- log_device_warning_errno(dev, r, "Failed to check if device has \"systemd\" tag, assuming device is not tagged with \"systemd\": %m");
- if (r == 0)
- log_device_debug(dev, "Device busy: device is not tagged with \"systemd\"");
- if (r <= 0)
- return false;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
- r = device_get_property_bool(dev, "SYSTEMD_READY");
- if (r < 0 && r != -ENOENT)
- log_device_warning_errno(dev, r, "Failed to get device SYSTEMD_READY property, assuming device does not have \"SYSTEMD_READY\" property: %m");
- if (r == 0)
- log_device_debug(dev, "Device busy: SYSTEMD_READY property from device is false");
+ r = extract_first_word(&alias, &word, NULL, EXTRACT_UNQUOTE);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return (void) log_oom();
+ if (r < 0)
+ return (void) log_device_warning_errno(dev, r, "Failed to parse SYSTEMD_ALIAS property, ignoring: %m");
- return r != 0;
+ if (!path_is_absolute(word))
+ log_device_warning(dev, "SYSTEMD_ALIAS is not an absolute path, ignoring: %s", word);
+ else if (!path_is_normalized(word))
+ log_device_warning(dev, "SYSTEMD_ALIAS is not a normalized path, ignoring: %s", word);
+ else
+ (void) device_setup_unit(m, dev, word, /* main = */ false);
+ }
}
static Unit *device_following(Unit *u) {
}
FOREACH_DEVICE(e, dev) {
+ _cleanup_strv_free_ char **ready_devlinks = NULL;
const char *sysfs;
-
- if (!device_is_ready(dev))
- continue;
+ bool ready;
r = sd_device_get_syspath(dev, &sysfs);
if (r < 0) {
continue;
}
- device_process_new(m, dev, sysfs);
- device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
+ ready = device_is_ready(dev);
+ if (ready)
+ device_process_new(m, dev, sysfs);
+
+ /* Add additional units for all symlinks */
+ (void) device_setup_devlink_units(m, dev, &ready_devlinks);
+
+ if (ready)
+ device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
+
+ STRV_FOREACH(devlink, ready_devlinks)
+ device_update_found_by_name(m, *devlink, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
}
return;
r = manager_propagate_reload(m, UNIT(d), JOB_REPLACE, NULL);
if (r < 0)
- log_warning_errno(r, "Failed to propagate reload, ignoring: %m");
+ log_unit_warning_errno(UNIT(d), r, "Failed to propagate reload, ignoring: %m");
}
}
+static void device_propagate_reload_by_name(Manager *m, const char *path) {
+ _cleanup_free_ char *e = NULL;
+ Unit *u;
+ int r;
+
+ assert(m);
+ assert(path);
+
+ r = unit_name_from_path(path, ".device", &e);
+ if (r < 0)
+ return (void) log_debug_errno(r, "Failed to generate unit name from device path, ignoring: %m");
+
+ u = manager_get_unit(m, e);
+ if (!u)
+ return;
+
+ if (DEVICE(u)->state == DEVICE_DEAD)
+ return;
+
+ r = manager_propagate_reload(m, u, JOB_REPLACE, NULL);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to propagate reload, ignoring: %m");
+}
+
static void device_remove_old_on_move(Manager *m, sd_device *dev) {
_cleanup_free_ char *syspath_old = NULL;
const char *devpath_old;
}
static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
+ _cleanup_strv_free_ char **ready_devlinks = NULL;
Manager *m = ASSERT_PTR(userdata);
sd_device_action_t action;
const char *sysfs;
+ bool ready;
int r;
assert(dev);
return 0;
}
- if (!IN_SET(action, SD_DEVICE_ADD, SD_DEVICE_REMOVE, SD_DEVICE_MOVE))
- device_propagate_reload_by_sysfs(m, sysfs);
-
if (action == SD_DEVICE_MOVE)
device_remove_old_on_move(m, dev);
if (r < 0)
log_device_warning_errno(dev, r, "Failed to process swap device remove event, ignoring: %m");
- /* If we get notified that a device was removed by udev, then it's completely gone, hence
- * unset all found bits */
- device_update_found_by_sysfs(m, sysfs, DEVICE_NOT_FOUND, DEVICE_FOUND_MASK);
+ ready = false;
+
+ (void) device_setup_devlink_units_on_remove(m, dev, &ready_devlinks);
- } else if (device_is_ready(dev)) {
+ } else {
+ ready = device_is_ready(dev);
- device_process_new(m, dev, sysfs);
+ if (ready) {
+ device_process_new(m, dev, sysfs);
- r = swap_process_device_new(m, dev);
- if (r < 0)
- log_device_warning_errno(dev, r, "Failed to process swap device new event, ignoring: %m");
+ r = swap_process_device_new(m, dev);
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to process swap device new event, ignoring: %m");
+ }
+
+ /* Add additional units for all symlinks */
+ (void) device_setup_devlink_units(m, dev, &ready_devlinks);
+ }
+
+ if (!IN_SET(action, SD_DEVICE_ADD, SD_DEVICE_REMOVE, SD_DEVICE_MOVE)) {
+ device_propagate_reload_by_sysfs(m, sysfs);
+
+ STRV_FOREACH(devlink, ready_devlinks)
+ device_propagate_reload_by_name(m, *devlink);
+ }
+ if (ready || !strv_isempty(ready_devlinks))
manager_dispatch_load_queue(m);
+ if (action == SD_DEVICE_REMOVE)
+ /* If we get notified that a device was removed by udev, then it's completely gone, hence
+ * unset all found bits */
+ device_update_found_by_sysfs(m, sysfs, 0, DEVICE_FOUND_MASK);
+ else if (ready)
/* The device is found now, set the udev found bit */
device_update_found_by_sysfs(m, sysfs, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
- } else
+ else
/* The device is nominally around, but not ready for us. Hence unset the udev bit, but leave
* the rest around. */
device_update_found_by_sysfs(m, sysfs, DEVICE_NOT_FOUND, DEVICE_FOUND_UDEV);
+ STRV_FOREACH(devlink, ready_devlinks)
+ device_update_found_by_name(m, *devlink, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
+
return 0;
}
return;
}
- (void) device_setup_unit(m, dev, node, false); /* 'dev' may be NULL. */
+ (void) device_setup_unit(m, dev, node, /* main = */ false); /* 'dev' may be NULL. */
}
/* Update the device unit's state, should it exist */
Unit meta;
char *sysfs;
+ char *path; /* syspath, device node, alias, or devlink */
/* In order to be able to distinguish dependencies on different device nodes we might end up creating multiple
* devices for the same sysfs path. We chain them up here. */
if (getttyname_malloc(STDIN_FILENO, &q) >= 0)
tty = strjoina("/dev/", q);
- else
- /* If everything else failed then let's just use value "systemd". This will cause that session
- * isn't going to be marked as "background" and user manager will be started. */
- tty = "systemd";
}
- pam_code = pam_set_item(handle, PAM_TTY, tty);
- if (pam_code != PAM_SUCCESS)
- goto fail;
+ if (tty) {
+ pam_code = pam_set_item(handle, PAM_TTY, tty);
+ if (pam_code != PAM_SUCCESS)
+ goto fail;
+ }
STRV_FOREACH(nv, *env) {
pam_code = pam_putenv(handle, *nv);
#include "copy.h"
#include "creds-util.h"
+#include "escape.h"
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
+#include "hexdecoct.h"
#include "import-creds.h"
#include "io-util.h"
#include "mkdir-label.h"
* generators invoked by it) can acquire credentials from outside, to mimic how we support it for containers,
* but on VM/physical environments.
*
- * This does three things:
+ * This does four things:
*
* 1. It imports credentials picked up by sd-boot (and placed in the /.extra/credentials/ dir in the initrd)
* and puts them in /run/credentials/@encrypted/. Note that during the initrd→host transition the initrd root
* /sys/firmware/qemu_fw_cfg/by_name/opt/io.systemd.credentials/ is picked up and also placed in
* /run/credentials/@system/.
*
+ * 4. It imports credentials passed in via the DMI/SMBIOS OEM string tables, quite similar to fw_cfg. It
+ * looks for strings starting with "io.systemd.credential:" and "io.systemd.credential.binary:". Both
+ * expect a key=value assignment, but in the latter case the value is Base64 decoded, allowing binary
+ * credentials to be passed in.
+ *
* If it picked up any credentials it will set the $CREDENTIALS_DIRECTORY and
* $ENCRYPTED_CREDENTIALS_DIRECTORY environment variables to point to these directories, so that processes
* can find them there later on. If "ramfs" is available $CREDENTIALS_DIRECTORY will be backed by it (but
return 0;
}
+static int parse_smbios_strings(ImportCredentialContext *c, const char *data, size_t size) {
+ size_t left, skip;
+ const char *p;
+ int r;
+
+ assert(c);
+ assert(data || size == 0);
+
+ /* Unpacks a packed series of SMBIOS OEM vendor strings. These are a series of NUL terminated
+ * strings, one after the other. */
+
+ for (p = data, left = size; left > 0; p += skip, left -= skip) {
+ _cleanup_free_ void *buf = NULL;
+ _cleanup_free_ char *cn = NULL;
+ _cleanup_close_ int nfd = -1;
+ const char *nul, *n, *eq;
+ const void *cdata;
+ size_t buflen, cdata_len;
+ bool unbase64;
+
+ nul = memchr(p, 0, left);
+ if (nul)
+ skip = (nul - p) + 1;
+ else {
+ nul = p + left;
+ skip = left;
+ }
+
+ if (nul - p == 0) /* Skip empty strings */
+ continue;
+
+ /* Only care about strings starting with either of these two prefixes */
+ if ((n = memory_startswith(p, nul - p, "io.systemd.credential:")))
+ unbase64 = false;
+ else if ((n = memory_startswith(p, nul - p, "io.systemd.credential.binary:")))
+ unbase64 = true;
+ else {
+ _cleanup_free_ char *escaped = NULL;
+
+ escaped = cescape_length(p, nul - p);
+ log_debug("Ignoring OEM string: %s", strnull(escaped));
+ continue;
+ }
+
+ eq = memchr(n, '=', nul - n);
+ if (!eq) {
+ log_warning("SMBIOS OEM string lacks '=' character, ignoring.");
+ continue;
+ }
+
+ cn = memdup_suffix0(n, eq - n);
+ if (!cn)
+ return log_oom();
+
+ if (!credential_name_valid(cn)) {
+ log_warning("SMBIOS credential name '%s' is not valid, ignoring: %m", cn);
+ continue;
+ }
+
+ /* Optionally base64 decode the data, if requested, to allow binary credentials */
+ if (unbase64) {
+ r = unbase64mem(eq + 1, nul - (eq + 1), &buf, &buflen);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to base64 decode credential '%s', ignoring: %m", cn);
+ continue;
+ }
+
+ cdata = buf;
+ cdata_len = buflen;
+ } else {
+ cdata = eq + 1;
+ cdata_len = nul - (eq + 1);
+ }
+
+ if (!credential_size_ok(c, cn, cdata_len))
+ continue;
+
+ r = acquire_credential_directory(c);
+ if (r < 0)
+ return r;
+
+ nfd = open_credential_file_for_write(c->target_dir_fd, SYSTEM_CREDENTIALS_DIRECTORY, cn);
+ if (nfd == -EEXIST)
+ continue;
+ if (nfd < 0)
+ return nfd;
+
+ r = loop_write(nfd, cdata, cdata_len, /* do_poll= */ false);
+ if (r < 0) {
+ (void) unlinkat(c->target_dir_fd, cn, 0);
+ return log_error_errno(r, "Failed to write credential: %m");
+ }
+
+ c->size_sum += cdata_len;
+ c->n_credentials++;
+
+ log_debug("Successfully processed SMBIOS credential '%s'.", cn);
+ }
+
+ return 0;
+}
+
+static int import_credentials_smbios(ImportCredentialContext *c) {
+ int r;
+
+ /* Parses DMI OEM strings fields (SMBIOS type 11), as settable with qemu's -smbios type=11,value=… switch. */
+
+ for (unsigned i = 0;; i++) {
+ struct dmi_field_header {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+ uint8_t count;
+ char contents[];
+ } _packed_ *dmi_field_header;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_free_ void *data = NULL;
+ size_t size;
+
+ assert_cc(offsetof(struct dmi_field_header, contents) == 5);
+
+ if (asprintf(&p, "/sys/firmware/dmi/entries/11-%u/raw", i) < 0)
+ return log_oom();
+
+ r = read_virtual_file(p, sizeof(dmi_field_header) + CREDENTIALS_TOTAL_SIZE_MAX, (char**) &data, &size);
+ if (r < 0) {
+ /* Once we reach ENOENT there are no more DMI Type 11 fields around. */
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, "Failed to open '%s', ignoring: %m", p);
+ break;
+ }
+
+ if (size < offsetof(struct dmi_field_header, contents))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DMI field header of '%s' too short.", p);
+
+ dmi_field_header = data;
+ if (dmi_field_header->type != 11 ||
+ dmi_field_header->length != offsetof(struct dmi_field_header, contents))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid DMI field header.");
+
+ r = parse_smbios_strings(c, dmi_field_header->contents, size - offsetof(struct dmi_field_header, contents));
+ if (r < 0)
+ return r;
+
+ if (i == UINT_MAX) /* Prevent overflow */
+ break;
+ }
+
+ return 0;
+}
+
static int import_credentials_trusted(void) {
_cleanup_(import_credentials_context_free) ImportCredentialContext c = {
.target_dir_fd = -1,
};
- int q, r;
+ int q, w, r;
r = import_credentials_qemu(&c);
+ w = import_credentials_smbios(&c);
q = import_credentials_proc_cmdline(&c);
if (c.n_credentials > 0) {
int z;
- log_debug("Imported %u credentials from kernel command line/fw_cfg.", c.n_credentials);
+ log_debug("Imported %u credentials from kernel command line/smbios/fw_cfg.", c.n_credentials);
z = finalize_credentials_dir(SYSTEM_CREDENTIALS_DIRECTORY, "CREDENTIALS_DIRECTORY");
if (z < 0)
return z;
}
- return r < 0 ? r : q;
+ return r < 0 ? r : w < 0 ? w : q;
}
static int symlink_credential_dir(const char *envvar, const char *path, const char *where) {
job_set_state(j, JOB_WAITING); /* Hmm, not ready after all, let's return to JOB_WAITING state */
else if (r == -EALREADY) /* already being executed */
r = job_finish_and_invalidate(j, JOB_DONE, true, true);
- else if (r == -ECOMM) /* condition failed, but all is good. Return 'skip' if caller requested it. */
- r = job_finish_and_invalidate(j, j->return_skip_on_cond_failure ? JOB_SKIPPED : JOB_DONE, true, false);
+ else if (r == -ECOMM)
+ r = job_finish_and_invalidate(j, JOB_DONE, true, false);
else if (r == -EBADR)
r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false);
else if (r == -ENOEXEC)
bool irreversible:1;
bool in_gc_queue:1;
bool ref_by_private_bus:1;
- bool return_skip_on_cond_failure:1;
};
Job* job_new(Unit *unit, JobType type);
/* qemu_fw_cfg would be loaded by udev later, but we want to import credentials from it super early */
{ "qemu_fw_cfg", "/sys/firmware/qemu_fw_cfg", false, false, in_qemu },
+
+ /* dmi-sysfs is needed to import credentials from it super early */
+ { "dmi-sysfs", "/sys/firmware/dmi/entries", false, false, NULL },
};
_cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
unsigned i;
{{type}}.AllowedMemoryNodes, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.cpuset_mems)
{{type}}.StartupAllowedMemoryNodes, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.startup_cpuset_mems)
{{type}}.CPUAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.cpu_accounting)
-{{type}}.CPUWeight, config_parse_cg_weight, 0, offsetof({{type}}, cgroup_context.cpu_weight)
-{{type}}.StartupCPUWeight, config_parse_cg_weight, 0, offsetof({{type}}, cgroup_context.startup_cpu_weight)
+{{type}}.CPUWeight, config_parse_cg_cpu_weight, 0, offsetof({{type}}, cgroup_context.cpu_weight)
+{{type}}.StartupCPUWeight, config_parse_cg_cpu_weight, 0, offsetof({{type}}, cgroup_context.startup_cpu_weight)
{{type}}.CPUShares, config_parse_cpu_shares, 0, offsetof({{type}}, cgroup_context.cpu_shares)
{{type}}.StartupCPUShares, config_parse_cpu_shares, 0, offsetof({{type}}, cgroup_context.startup_cpu_shares)
{{type}}.CPUQuota, config_parse_cpu_quota, 0, offsetof({{type}}, cgroup_context)
Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions)
Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions)
Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions)
+Unit.ConditionCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, offsetof(Unit, conditions)
Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions)
Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions)
Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions)
Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts)
Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts)
Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts)
+Unit.AssertCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, offsetof(Unit, asserts)
Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts)
Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts)
Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts)
#include "seccomp-util.h"
#endif
#include "securebits-util.h"
+#include "selinux-util.h"
#include "signal-util.h"
#include "socket-netlink.h"
#include "specifier.h"
DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value");
DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint64_t, "Invalid block IO weight");
DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight");
+DEFINE_CONFIG_PARSE_PTR(config_parse_cg_cpu_weight, cg_cpu_weight_parse, uint64_t, "Invalid CPU weight");
DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
assert(u->id);
if (u->transient) {
+ u->access_selinux_context = mfree(u->access_selinux_context);
u->load_state = UNIT_LOADED;
return 0;
}
u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
u->fragment_mtime = 0;
+ u->access_selinux_context = mfree(u->access_selinux_context);
} else {
+#if HAVE_SELINUX
+ if (mac_selinux_use()) {
+ _cleanup_freecon_ char *selcon = NULL;
+
+ /* Cache the SELinux context of the unit file here. We'll make use of when checking access permissions to loaded units */
+ r = fgetfilecon_raw(fileno(f), &selcon);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to read SELinux context of '%s', ignoring: %m", fragment);
+
+ r = free_and_strdup(&u->access_selinux_context, selcon);
+ if (r < 0)
+ return r;
+ } else
+#endif
+ u->access_selinux_context = mfree(u->access_selinux_context);
+
u->load_state = UNIT_LOADED;
u->fragment_mtime = timespec_load(&st.st_mtim);
{ config_parse_restrict_filesystems, "FILESYSTEMS" },
{ config_parse_cpu_shares, "SHARES" },
{ config_parse_cg_weight, "WEIGHT" },
+ { config_parse_cg_cpu_weight, "CPUWEIGHT" },
{ config_parse_memory_limit, "LIMIT" },
{ config_parse_device_allow, "DEVICE" },
{ config_parse_device_policy, "POLICY" },
CONFIG_PARSER_PROTOTYPE(config_parse_unset_environ);
CONFIG_PARSER_PROTOTYPE(config_parse_unit_slice);
CONFIG_PARSER_PROTOTYPE(config_parse_cg_weight);
+CONFIG_PARSER_PROTOTYPE(config_parse_cg_cpu_weight);
CONFIG_PARSER_PROTOTYPE(config_parse_cpu_shares);
CONFIG_PARSER_PROTOTYPE(config_parse_memory_limit);
CONFIG_PARSER_PROTOTYPE(config_parse_tasks_max);
static usec_t arg_default_timeout_start_usec;
static usec_t arg_default_timeout_stop_usec;
static usec_t arg_default_timeout_abort_usec;
+static usec_t arg_default_device_timeout_usec;
static bool arg_default_timeout_abort_set;
static usec_t arg_default_start_limit_interval;
static unsigned arg_default_start_limit_burst;
{ "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec },
{ "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec },
{ "Manager", "DefaultTimeoutAbortSec", config_parse_default_timeout_abort, 0, NULL },
+ { "Manager", "DefaultDeviceTimeoutSec", config_parse_sec, 0, &arg_default_device_timeout_usec },
{ "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec },
{ "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */
{ "Manager", "DefaultStartLimitIntervalSec", config_parse_sec, 0, &arg_default_start_limit_interval },
config_item_table_lookup, items,
CONFIG_PARSE_WARN,
NULL,
+ NULL,
NULL);
/* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we use
m->default_timeout_stop_usec = arg_default_timeout_stop_usec;
m->default_timeout_abort_usec = arg_default_timeout_abort_usec;
m->default_timeout_abort_set = arg_default_timeout_abort_set;
+ m->default_device_timeout_usec = arg_default_device_timeout_usec;
m->default_restart_usec = arg_default_restart_usec;
m->default_start_limit_interval = arg_default_start_limit_interval;
m->default_start_limit_burst = arg_default_start_limit_burst;
return objective;
case MANAGER_SWITCH_ROOT:
+ manager_set_switching_root(m, true);
+
if (!m->switch_root_init) {
r = prepare_reexecute(m, &arg_serialization, ret_fds, true);
if (r < 0) {
arg_default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
arg_default_timeout_abort_usec = DEFAULT_TIMEOUT_USEC;
arg_default_timeout_abort_set = false;
+ arg_default_device_timeout_usec = DEFAULT_TIMEOUT_USEC;
arg_default_start_limit_interval = DEFAULT_START_LIMIT_INTERVAL;
arg_default_start_limit_burst = DEFAULT_START_LIMIT_BURST;
arg_runtime_watchdog = 0;
} else {
/* Running as user instance */
arg_system = false;
+ log_set_always_reopen_console(true);
log_set_target(LOG_TARGET_AUTO);
log_open();
/* clear the kernel timestamp, because we are not PID 1 */
kernel_timestamp = DUAL_TIMESTAMP_NULL;
+ /* Clear ambient capabilities, so services do not inherit them implicitly. Dropping them does
+ * not affect the permitted and effective sets which are important for the manager itself to
+ * operate. */
+ capability_ambient_set_apply(0, /* also_inherit= */ false);
+
if (mac_selinux_init() < 0) {
error_message = "Failed to initialize SELinux support";
goto finish;
set_manager_defaults(m);
set_manager_settings(m);
manager_set_first_boot(m, first_boot);
+ manager_set_switching_root(m, arg_switched_root);
/* Remember whether we should queue the default job */
queue_default_job = !arg_serialization || arg_switched_root;
(void) serialize_bool(f, "taint-logged", m->taint_logged);
(void) serialize_bool(f, "service-watchdogs", m->service_watchdogs);
- /* After switching root, udevd has not been started yet. So, enumeration results should not be emitted. */
- (void) serialize_bool(f, "honor-device-enumeration", !switching_root);
-
if (m->show_status_overridden != _SHOW_STATUS_INVALID)
(void) serialize_item(f, "show-status-overridden",
show_status_to_string(m->show_status_overridden));
else
m->service_watchdogs = b;
- } else if ((val = startswith(l, "honor-device-enumeration="))) {
- int b;
-
- b = parse_boolean(val);
- if (b < 0)
- log_notice("Failed to parse honor-device-enumeration flag '%s', ignoring.", val);
- else
- m->honor_device_enumeration = b;
-
} else if ((val = startswith(l, "show-status-overridden="))) {
ShowStatus s;
if (q < _MANAGER_TIMESTAMP_MAX) /* found it */
(void) deserialize_dual_timestamp(val, m->timestamps + q);
- else if (!startswith(l, "kdbus-fd=")) /* ignore kdbus */
+ else if (!STARTSWITH_SET(l, "kdbus-fd=", "honor-device-enumeration=")) /* ignore deprecated values */
log_notice("Unknown serialization item '%s', ignoring.", l);
}
}
return 0;
}
+void manager_set_switching_root(Manager *m, bool switching_root) {
+ m->switching_root = MANAGER_IS_SYSTEM(m) && switching_root;
+}
+
int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **_m) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
.default_timeout_start_usec = DEFAULT_TIMEOUT_USEC,
.default_timeout_stop_usec = DEFAULT_TIMEOUT_USEC,
.default_restart_usec = DEFAULT_RESTART_USEC,
+ .default_device_timeout_usec = DEFAULT_TIMEOUT_USEC,
.original_log_level = -1,
.original_log_target = _LOG_TARGET_INVALID,
(void) touch_file("/run/systemd/systemd-units-load", false,
m->timestamps[MANAGER_TIMESTAMP_UNITS_LOAD].realtime ?: now(CLOCK_REALTIME),
UID_INVALID, GID_INVALID, 0444);
-
- m->honor_device_enumeration = true;
}
Manager* manager_reloading_start(Manager *m) {
manager_ready(m);
+ manager_set_switching_root(m, false);
+
return 0;
}
assert(m->n_reloading > 0);
m->n_reloading--;
- /* On manager reloading, device tag data should exists, thus, we should honor the results of device
- * enumeration. The flag should be always set correctly by the serialized data, but it may fail. So,
- * let's always set the flag here for safety. */
- m->honor_device_enumeration = true;
-
manager_ready(m);
m->send_reloading_done = true;
ExecOutput default_std_output, default_std_error;
usec_t default_restart_usec, default_timeout_start_usec, default_timeout_stop_usec;
+ usec_t default_device_timeout_usec;
usec_t default_timeout_abort_usec;
bool default_timeout_abort_set;
char *switch_root;
char *switch_root_init;
+ /* This is true before and after switching root. */
+ bool switching_root;
+
/* This maps all possible path prefixes to the units needing
* them. It's a hashmap with a path string as key and a Set as
* value where Unit objects are contained. */
unsigned sigchldgen;
unsigned notifygen;
- bool honor_device_enumeration;
-
VarlinkServer *varlink_server;
/* When we're a system manager, this object manages the subscription from systemd-oomd to PID1 that's
* used to report changes in ManagedOOM settings (systemd server - oomd client). When
/* The objective is set to OK as soon as we enter the main loop, and set otherwise as soon as we are done with it */
#define MANAGER_IS_RUNNING(m) ((m)->objective == MANAGER_OK)
+#define MANAGER_IS_SWITCHING_ROOT(m) ((m)->switching_root)
+
#define MANAGER_IS_TEST_RUN(m) ((m)->test_run_flags != 0)
int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **m);
void manager_override_show_status(Manager *m, ShowStatus mode, const char *reason);
void manager_set_first_boot(Manager *m, bool b);
+void manager_set_switching_root(Manager *m, bool switching_root);
void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5);
}
static int mount_add_device_dependencies(Mount *m) {
- UnitDependencyMask mask;
MountParameters *p;
UnitDependency dep;
int r;
* suddenly. */
dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
- /* We always use 'what' from /proc/self/mountinfo if mounted */
- mask = m->from_proc_self_mountinfo ? UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT : UNIT_DEPENDENCY_FILE;
-
- r = unit_add_node_dependency(UNIT(m), p->what, dep, mask);
+ r = unit_add_node_dependency(UNIT(m), p->what, dep, UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
if (r < 0)
return r;
if (r > 0)
log_unit_trace(UNIT(m), "Added %s dependency on %s", unit_dependency_to_string(dep), p->what);
if (mount_propagate_stop(m)) {
- r = unit_add_node_dependency(UNIT(m), p->what, UNIT_STOP_PROPAGATED_FROM, mask);
+ r = unit_add_node_dependency(UNIT(m), p->what, UNIT_STOP_PROPAGATED_FROM, UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
if (r < 0)
return r;
if (r > 0)
unit_dependency_to_string(UNIT_STOP_PROPAGATED_FROM), p->what);
}
- r = unit_add_blockdev_dependency(UNIT(m), p->what, mask);
+ r = unit_add_blockdev_dependency(UNIT(m), p->what, UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
if (r > 0)
log_unit_trace(UNIT(m), "Added %s dependency on %s", unit_dependency_to_string(UNIT_AFTER), p->what);
- return r;
+ return 0;
}
static int mount_add_quota_dependencies(Mount *m) {
static int mount_add_default_ordering_dependencies(
Mount *m,
- MountParameters *p,
- UnitDependencyMask mask) {
+ MountParameters *p) {
const char *after, *before, *e;
int r;
}
if (!mount_is_nofail(m)) {
- r = unit_add_dependency_by_name(UNIT(m), UNIT_BEFORE, before, true, mask);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_BEFORE, before, true,
+ UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
if (r < 0)
return r;
}
if (after) {
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, true, mask);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, true,
+ UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
if (r < 0)
return r;
}
return unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS,
- SPECIAL_UMOUNT_TARGET, true, mask);
+ SPECIAL_UMOUNT_TARGET, true,
+ UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
}
static int mount_add_default_dependencies(Mount *m) {
- UnitDependencyMask mask;
MountParameters *p;
int r;
if (!p)
return 0;
- mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_DEFAULT;
-
- r = mount_add_default_ordering_dependencies(m, p, mask);
+ r = mount_add_default_ordering_dependencies(m, p);
if (r < 0)
return r;
* network.target, so that they are shut down only after this mount unit is
* stopped. */
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, true, mask);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, true,
+ UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
if (r < 0)
return r;
* mounting network file systems, and whose purpose it is to delay this until the
* network is "up". */
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, true, mask);
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER,
+ SPECIAL_NETWORK_ONLINE_TARGET, true,
+ UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
if (r < 0)
return r;
}
/* If this is a tmpfs mount then we have to unmount it before we try to deactivate swaps */
if (streq_ptr(p->fstype, "tmpfs")) {
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, true, mask);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, true,
+ UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
if (r < 0)
return r;
}
static int mount_add_non_exec_dependencies(Mount *m) {
int r;
+
assert(m);
+ /* Any dependencies based on /proc/self/mountinfo may be now stale. */
+ unit_remove_dependencies(UNIT(m),
+ UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT |
+ UNIT_DEPENDENCY_MOUNTINFO_DEFAULT |
+ UNIT_DEPENDENCY_MOUNTINFO_OR_FILE);
+
+ if (!m->where)
+ return 0;
+
/* Adds in all dependencies directly responsible for ordering the mount, as opposed to dependencies
* resulting from the ExecContext and such. */
dynamic_creds_destroy(&m->dynamic_creds);
- /* Any dependencies based on /proc/self/mountinfo are now stale */
- unit_remove_dependencies(UNIT(m), UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
+ /* Any dependencies based on /proc/self/mountinfo are now stale. Let's re-generate dependencies from
+ * .mount unit. */
+ (void) mount_add_non_exec_dependencies(m);
}
static void mount_enter_mounted(Mount *m, MountResult f) {
if (p && mount_is_bind(p)) {
r = mkdir_p_label(p->what, m->directory_mode);
/* mkdir_p_label() can return -EEXIST if the target path exists and is not a directory - which is
- * totally OK, in case the user wants us to overmount a non-directory inode. */
- if (r < 0 && r != -EEXIST) {
- log_unit_error_errno(UNIT(m), r, "Failed to make bind mount source '%s': %m", p->what);
- goto fail;
- }
+ * totally OK, in case the user wants us to overmount a non-directory inode. Also -EROFS can be
+ * returned on read-only filesystem. Moreover, -EACCES (and also maybe -EPERM?) may be returned
+ * when the path is on NFS. See issue #24120. All such errors will be logged in the debug level. */
+ if (r < 0 && r != -EEXIST)
+ log_unit_full_errno(UNIT(m),
+ (r == -EROFS || ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_WARNING,
+ r, "Failed to make bind mount source '%s', ignoring: %m", p->what);
}
if (p) {
if (r < 0)
return r;
- r = mount_add_non_exec_dependencies(MOUNT(u));
- if (r < 0)
- return r;
-
/* This unit was generated because /proc/self/mountinfo reported it. Remember this, so that by the
* time we load the unit file for it (and thus add in extra deps right after) we know what source to
* attributes the deps to. */
MOUNT(u)->from_proc_self_mountinfo = true;
+ r = mount_add_non_exec_dependencies(MOUNT(u));
+ if (r < 0)
+ return r;
+
/* We have only allocated the stub now, let's enqueue this unit for loading now, so that everything
* else is loaded in now. */
unit_add_to_load_queue(u);
if (FLAGS_SET(flags, MOUNT_PROC_JUST_CHANGED)) {
/* If things changed, then make sure that all deps are regenerated. Let's
* first remove all automatic deps, and then add in the new ones. */
-
- unit_remove_dependencies(u, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
-
r = mount_add_non_exec_dependencies(MOUNT(u));
if (r < 0)
return r;
if (!is_path(where))
return 0;
- /* Mount unit names have to be (like all other unit names) short enough to fit into file names. This
- * means there's a good chance that overly long mount point paths after mangling them to look like a
- * unit name would result in unit names we don't actually consider valid. This should be OK however
- * as such long mount point paths should not happen on regular systems — and if they appear
- * nonetheless they are generally synthesized by software, and thus managed by that other
- * software. Having such long names just means you cannot use systemd to manage those specific mount
- * points, which should be an OK restriction to make. After all we don't have to be able to manage
- * all mount points in the world — as long as we don't choke on them when we encounter them. */
r = unit_name_from_path(where, ".mount", &e);
- if (r < 0) {
- static RateLimit rate_limit = { /* Let's log about this at warning level at most once every
- * 5s. Given that we generate this whenever we read the file
- * otherwise we probably shouldn't flood the logs with
- * this */
- .interval = 5 * USEC_PER_SEC,
- .burst = 1,
- };
-
- if (r == -ENAMETOOLONG)
- return log_struct_errno(
- ratelimit_below(&rate_limit) ? LOG_WARNING : LOG_DEBUG, r,
- "MESSAGE_ID=" SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR,
- "MOUNT_POINT=%s", where,
- LOG_MESSAGE("Mount point path '%s' too long to fit into unit name, ignoring mount point.", where));
-
+ if (r < 0)
return log_struct_errno(
- ratelimit_below(&rate_limit) ? LOG_WARNING : LOG_DEBUG, r,
+ LOG_WARNING, r,
"MESSAGE_ID=" SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR,
"MOUNT_POINT=%s", where,
- LOG_MESSAGE("Failed to generate valid unit name from mount point path '%s', ignoring mount point: %m", where));
- }
+ LOG_MESSAGE("Failed to generate valid unit name from mount point path '%s', ignoring mount point: %m",
+ where));
u = manager_get_unit(m, e);
if (u)
}
static int mount_process_proc_self_mountinfo(Manager *m) {
- _cleanup_set_free_free_ Set *around = NULL, *gone = NULL;
+ _cleanup_set_free_ Set *around = NULL, *gone = NULL;
const char *what;
int r;
* existed. */
if (mount->from_proc_self_mountinfo &&
- mount->parameters_proc_self_mountinfo.what) {
-
+ mount->parameters_proc_self_mountinfo.what)
/* Remember that this device might just have disappeared */
- if (set_ensure_allocated(&gone, &path_hash_ops) < 0 ||
- set_put_strdup(&gone, mount->parameters_proc_self_mountinfo.what) < 0)
+ if (set_put_strdup_full(&gone, &path_hash_ops_free, mount->parameters_proc_self_mountinfo.what) < 0)
log_oom(); /* we don't care too much about OOM here... */
- }
mount->from_proc_self_mountinfo = false;
assert_se(update_parameters_proc_self_mountinfo(mount, NULL, NULL, NULL) >= 0);
if (mount_is_mounted(mount) &&
mount->from_proc_self_mountinfo &&
- mount->parameters_proc_self_mountinfo.what) {
+ mount->parameters_proc_self_mountinfo.what)
/* Track devices currently used */
-
- if (set_ensure_allocated(&around, &path_hash_ops) < 0 ||
- set_put_strdup(&around, mount->parameters_proc_self_mountinfo.what) < 0)
+ if (set_put_strdup_full(&around, &path_hash_ops_free, mount->parameters_proc_self_mountinfo.what) < 0)
log_oom();
- }
/* Reset the flags for later calls */
mount->proc_flags = 0;
#include <sys/file.h>
#include <sys/mount.h>
#include <unistd.h>
+#if WANT_LINUX_FS_H
#include <linux/fs.h>
+#endif
#include "alloc-util.h"
#include "base-filesystem.h"
#include "alloc-util.h"
#include "dbus-scope.h"
#include "dbus-unit.h"
+#include "exit-status.h"
#include "load-dropin.h"
#include "log.h"
#include "process-util.h"
#include "strv.h"
#include "unit-name.h"
#include "unit.h"
+#include "user-util.h"
static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
[SCOPE_DEAD] = UNIT_INACTIVE,
+ [SCOPE_START_CHOWN] = UNIT_ACTIVATING,
[SCOPE_RUNNING] = UNIT_ACTIVE,
[SCOPE_ABANDONED] = UNIT_ACTIVE,
[SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING,
s->runtime_max_usec = USEC_INFINITY;
s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
u->ignore_on_isolate = true;
+ s->user = s->group = NULL;
}
static void scope_done(Unit *u) {
s->controller_track = sd_bus_track_unref(s->controller_track);
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
+
+ s->user = mfree(s->user);
+ s->group = mfree(s->group);
}
static usec_t scope_running_timeout(Scope *s) {
old_state = s->state;
s->state = state;
- if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
+ if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL, SCOPE_START_CHOWN))
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
if (IN_SET(state, SCOPE_DEAD, SCOPE_FAILED)) {
scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
}
-static int scope_start(Unit *u) {
- Scope *s = SCOPE(u);
+static int scope_enter_start_chown(Scope *s) {
+ Unit *u = UNIT(s);
+ pid_t pid;
int r;
assert(s);
+ assert(s->user);
- if (unit_has_name(u, SPECIAL_INIT_SCOPE))
- return -EPERM;
+ r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), u->manager->default_timeout_start_usec));
+ if (r < 0)
+ return r;
- if (s->state == SCOPE_FAILED)
- return -EPERM;
+ r = unit_fork_helper_process(u, "(sd-chown-cgroup)", &pid);
+ if (r < 0)
+ goto fail;
- /* We can't fulfill this right now, please try again later */
- if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
- return -EAGAIN;
+ if (r == 0) {
+ uid_t uid = UID_INVALID;
+ gid_t gid = GID_INVALID;
- assert(s->state == SCOPE_DEAD);
+ if (!isempty(s->user)) {
+ const char *user = s->user;
- if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
- return -ENOENT;
+ r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
+ if (r < 0) {
+ log_unit_error_errno(UNIT(s), r, "Failed to resolve user \"%s\": %m", user);
+ _exit(EXIT_USER);
+ }
+ }
+
+ if (!isempty(s->group)) {
+ const char *group = s->group;
+
+ r = get_group_creds(&group, &gid, 0);
+ if (r < 0) {
+ log_unit_error_errno(UNIT(s), r, "Failed to resolve group \"%s\": %m", group);
+ _exit(EXIT_GROUP);
+ }
+ }
+
+ r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, uid, gid);
+ if (r < 0) {
+ log_unit_error_errno(UNIT(s), r, "Failed to adjust control group access: %m");
+ _exit(EXIT_CGROUP);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ r = unit_watch_pid(UNIT(s), pid, true);
+ if (r < 0)
+ goto fail;
+
+ scope_set_state(s, SCOPE_START_CHOWN);
+
+ return 1;
+fail:
+ s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
+ return r;
+}
+
+static int scope_enter_running(Scope *s) {
+ Unit *u = UNIT(s);
+ int r;
+
+ assert(s);
(void) bus_scope_track_controller(s);
if (r < 0)
return r;
- (void) unit_realize_cgroup(u);
- (void) unit_reset_accounting(u);
-
unit_export_state_files(u);
r = unit_attach_pids_to_cgroup(u, u->pids, NULL);
return r;
}
if (r == 0) {
- log_unit_warning(u, "No PIDs left to attach to the scope's control group, refusing: %m");
+ log_unit_warning(u, "No PIDs left to attach to the scope's control group, refusing.");
scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
return -ECHILD;
}
return 1;
}
+static int scope_start(Unit *u) {
+ Scope *s = SCOPE(u);
+
+ assert(s);
+
+ if (unit_has_name(u, SPECIAL_INIT_SCOPE))
+ return -EPERM;
+
+ if (s->state == SCOPE_FAILED)
+ return -EPERM;
+
+ /* We can't fulfill this right now, please try again later */
+ if (IN_SET(s->state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
+ return -EAGAIN;
+
+ assert(s->state == SCOPE_DEAD);
+
+ if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
+ return -ENOENT;
+
+ (void) unit_realize_cgroup(u);
+ (void) unit_reset_accounting(u);
+
+ /* We check only for User= option to keep behavior consistent with logic for service units,
+ * i.e. having 'Delegate=true Group=foo' w/o specifying User= has no effect. */
+ if (s->user && unit_cgroup_delegate(u))
+ return scope_enter_start_chown(s);
+
+ return scope_enter_running(s);
+}
+
static int scope_stop(Unit *u) {
Scope *s = SCOPE(u);
unit_prune_cgroup(u);
}
+static void scope_notify_cgroup_oom_event(Unit *u, bool managed_oom) {
+ Scope *s = SCOPE(u);
+
+ if (managed_oom)
+ log_unit_debug(u, "Process(es) of control group were killed by systemd-oomd.");
+ else
+ log_unit_debug(u, "Process of control group was killed by the OOM killer.");
+
+ /* This will probably need to be modified when scope units get an oom-policy */
+ switch (s->state) {
+
+ case SCOPE_START_CHOWN:
+ case SCOPE_RUNNING:
+ case SCOPE_STOP_SIGTERM:
+ scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_OOM_KILL);
+ break;
+
+ case SCOPE_STOP_SIGKILL:
+ if (s->result == SCOPE_SUCCESS)
+ s->result = SCOPE_FAILURE_OOM_KILL;
+ break;
+ /* SCOPE_DEAD, SCOPE_ABANDONED, and SCOPE_FAILED end up in default */
+ default:
+ ;
+ }
+}
+
static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
- assert(u);
+ Scope *s = SCOPE(u);
+
+ assert(s);
+
+ if (s->state == SCOPE_START_CHOWN) {
+ if (!is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
+ scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
+ else
+ scope_enter_running(s);
+ return;
+ }
/* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to
* watch, under the assumption that we'll sooner or later get a SIGCHLD for them, as the original
scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
break;
+ case SCOPE_START_CHOWN:
+ log_unit_warning(UNIT(s), "User lookup timed out. Entering failed state.");
+ scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
+ break;
+
default:
assert_not_reached();
}
[SCOPE_SUCCESS] = "success",
[SCOPE_FAILURE_RESOURCES] = "resources",
[SCOPE_FAILURE_TIMEOUT] = "timeout",
+ [SCOPE_FAILURE_OOM_KILL] = "oom-kill",
};
DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult);
.reset_failed = scope_reset_failed,
.notify_cgroup_empty = scope_notify_cgroup_empty_event,
+ .notify_cgroup_oom = scope_notify_cgroup_oom_event,
.bus_set_property = bus_scope_set_property,
.bus_commit_properties = bus_scope_commit_properties,
SCOPE_SUCCESS,
SCOPE_FAILURE_RESOURCES,
SCOPE_FAILURE_TIMEOUT,
+ SCOPE_FAILURE_OOM_KILL,
_SCOPE_RESULT_MAX,
_SCOPE_RESULT_INVALID = -EINVAL,
} ScopeResult;
bool was_abandoned;
sd_event_source *timer_event_source;
+
+ char *user;
+ char *group;
};
extern const UnitVTable scope_vtable;
*/
int mac_selinux_access_check_internal(
sd_bus_message *message,
- const char *path,
+ const char *unit_path,
+ const char *unit_context,
const char *permission,
const char *function,
sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- const char *tclass, *scon;
+ const char *tclass, *scon, *acon;
_cleanup_free_ char *cl = NULL;
_cleanup_freecon_ char *fcon = NULL;
char **cmdline = NULL;
if (r < 0)
return r;
- /* The SELinux context is something we really should have
- * gotten directly from the message or sender, and not be an
- * augmented field. If it was augmented we cannot use it for
- * authorization, since this is racy and vulnerable. Let's add
- * an extra check, just in case, even though this really
- * shouldn't be possible. */
+ /* The SELinux context is something we really should have gotten directly from the message or sender,
+ * and not be an augmented field. If it was augmented we cannot use it for authorization, since this
+ * is racy and vulnerable. Let's add an extra check, just in case, even though this really shouldn't
+ * be possible. */
assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM);
r = sd_bus_creds_get_selinux_context(creds, &scon);
if (r < 0)
return r;
- if (path) {
- /* Get the file context of the unit file */
-
- if (getfilecon_raw(path, &fcon) < 0) {
- r = -errno;
-
- log_warning_errno(r, "SELinux getfilecon_raw() on '%s' failed%s (perm=%s): %m",
- path,
- enforce ? "" : ", ignoring",
- permission);
- if (!enforce)
- return 0;
-
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
- }
-
+ if (unit_context) {
+ /* Nice! The unit comes with a SELinux context read from the unit file */
+ acon = unit_context;
tclass = "service";
-
} else {
+ /* If no unit context is known, use our own */
if (getcon_raw(&fcon) < 0) {
r = -errno;
if (!enforce)
return 0;
- return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context: %m");
}
+ acon = fcon;
tclass = "system";
}
struct audit_info audit_info = {
.creds = creds,
- .path = path,
+ .path = unit_path,
.cmdline = cl,
.function = function,
};
- r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
+ r = selinux_check_access(scon, acon, tclass, permission, &audit_info);
if (r < 0) {
- r = errno_or_else(EPERM);
+ errno = -(r = errno_or_else(EPERM));
if (enforce)
- sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
+ sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access: %m");
}
log_full_errno_zerook(LOG_DEBUG, r,
"SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s function=%s path=%s cmdline=%s: %m",
- scon, fcon, tclass, permission, enforce ? "enforcing" : "permissive", function, strna(path), isempty(cl) ? "n/a" : cl);
+ scon, acon, tclass, permission, enforce ? "enforcing" : "permissive", function, strna(unit_path), strna(empty_to_null(cl)));
return enforce ? r : 0;
}
int mac_selinux_access_check_internal(
sd_bus_message *message,
- const char *path,
+ const char *unit_path,
+ const char *unit_label,
const char *permission,
const char *function,
sd_bus_error *error) {
#include "manager.h"
-int mac_selinux_access_check_internal(sd_bus_message *message,
- const char *path,
- const char *permission,
- const char *function,
- sd_bus_error *error);
+int mac_selinux_access_check_internal(sd_bus_message *message, const char *unit_path, const char *unit_label, const char *permission, const char *function, sd_bus_error *error);
#define mac_selinux_access_check(message, permission, error) \
- mac_selinux_access_check_internal((message), NULL, (permission), __func__, (error))
+ mac_selinux_access_check_internal((message), NULL, NULL, (permission), __func__, (error))
#define mac_selinux_unit_access_check(unit, message, permission, error) \
- mac_selinux_access_check_internal((message), unit_label_path(unit), (permission), __func__, (error))
+ mac_selinux_access_check_internal((message), (unit)->fragment_path, (unit)->access_selinux_context, (permission), __func__, (error))
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has Restart= set to either always or on-success, which isn't allowed for Type=oneshot services. Refusing.");
if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status))
- return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing.");
+ return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has RestartForceExitStatus= set, which isn't allowed for Type=oneshot services. Refusing.");
if (s->type == SERVICE_ONESHOT && s->exit_type == SERVICE_EXIT_CGROUP)
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Service has ExitType=cgroup set, which isn't allowed for Type=oneshot services. Refusing.");
int q;
q = unit_name_from_path(devlink, ".swap", &n);
- if (IN_SET(q, -EINVAL, -ENAMETOOLONG)) /* If name too long or otherwise not convertible to
- * unit name, we can't manage it */
+ if (q == -EINVAL) /* If the name is not convertible to unit name, we can't manage it */
continue;
if (q < 0)
return q;
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#DefaultTimeoutAbortSec=
+#DefaultDeviceTimeoutSec=90s
#DefaultRestartSec=100ms
#DefaultStartLimitIntervalSec=10s
#DefaultStartLimitBurst=5
if (v->base == TIMER_CALENDAR) {
usec_t b, rebased;
- /* If we know the last time this was
- * triggered, schedule the job based relative
- * to that. If we don't, just start from
- * the activation time. */
-
- if (t->last_trigger.realtime > 0)
+ /* Update last_trigger to 'now' in case the system time changes, so that
+ * next_elapse is not stuck with a future date. */
+ if (time_change)
+ b = ts.realtime;
+ /* If we know the last time this was triggered, schedule the job based relative
+ * to that. If we don't, just start from the activation time. */
+ else if (t->last_trigger.realtime > 0)
b = t->last_trigger.realtime;
- else {
- if (state_translation_table[t->state] == UNIT_ACTIVE)
- b = UNIT(t)->inactive_exit_timestamp.realtime;
- else
- b = ts.realtime;
- }
+ else if (state_translation_table[t->state] == UNIT_ACTIVE)
+ b = UNIT(t)->inactive_exit_timestamp.realtime;
+ else
+ b = ts.realtime;
r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
if (r < 0)
{ UNIT_DEPENDENCY_PATH, "path" },
{ UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT, "mountinfo-implicit" },
{ UNIT_DEPENDENCY_MOUNTINFO_DEFAULT, "mountinfo-default" },
+ { UNIT_DEPENDENCY_MOUNTINFO_OR_FILE, "mountinfo-or-file" },
{ UNIT_DEPENDENCY_PROC_SWAP, "proc-swap" },
{ UNIT_DEPENDENCY_SLICE_PROPERTY, "slice-property" },
};
STRV_FOREACH(j, u->documentation)
fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
+ if (u->access_selinux_context)
+ fprintf(f, "%s\tAccess SELinux Context: %s\n", prefix, u->access_selinux_context);
+
following = unit_following(u);
if (following)
fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
free(u->job_timeout_reboot_arg);
free(u->reboot_arg);
+ free(u->access_selinux_context);
+
set_free_free(u->aliases);
free(u->id);
Unit *m;
r = unit_name_from_path(prefix, ".mount", &p);
- if (IN_SET(r, -EINVAL, -ENAMETOOLONG))
+ if (r == -EINVAL)
continue; /* If the path cannot be converted to a mount unit name, then it's
* not manageable as a unit by systemd, and hence we don't need a
* dependency on it. Let's thus silently ignore the issue. */
unit_add_to_gc_queue(other);
+ /* The unit 'other' may not be wanted by the unit 'u'. */
+ unit_submit_to_stop_when_unneeded_queue(other);
+
done = false;
break;
}
return exec_context_may_touch_console(ec);
}
-const char *unit_label_path(const Unit *u) {
- const char *p;
-
- assert(u);
-
- /* Returns the file system path to use for MAC access decisions, i.e. the file to read the SELinux label off
- * when validating access checks. */
-
- if (IN_SET(u->load_state, UNIT_MASKED, UNIT_NOT_FOUND, UNIT_MERGED))
- return NULL; /* Shortcut things if we know there is no real, relevant unit file around */
-
- p = u->source_path ?: u->fragment_path;
- if (!p)
- return NULL;
-
- if (IN_SET(u->load_state, UNIT_LOADED, UNIT_BAD_SETTING, UNIT_ERROR))
- return p; /* Shortcut things, if we successfully loaded at least some stuff from the unit file */
-
- /* Not loaded yet, we need to go to disk */
- assert(u->load_state == UNIT_STUB);
-
- /* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense */
- if (null_or_empty_path(p) > 0)
- return NULL;
-
- return p;
-}
-
int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) {
int r;
* DefaultDependencies= and thus also involving configuration from UNIT_DEPENDENCY_FILE sources */
UNIT_DEPENDENCY_MOUNTINFO_DEFAULT = 1 << 6,
+ /* A dependency created because of data read from /proc/self/mountinfo, but fallback to unit configuration
+ * sources */
+ UNIT_DEPENDENCY_MOUNTINFO_OR_FILE = 1 << 7,
+
/* A dependency created because of data read from /proc/swaps and no other configuration source */
- UNIT_DEPENDENCY_PROC_SWAP = 1 << 7,
+ UNIT_DEPENDENCY_PROC_SWAP = 1 << 8,
/* A dependency for units in slices assigned by directly setting Slice= */
- UNIT_DEPENDENCY_SLICE_PROPERTY = 1 << 8,
+ UNIT_DEPENDENCY_SLICE_PROPERTY = 1 << 9,
- _UNIT_DEPENDENCY_MASK_FULL = (1 << 9) - 1,
+ _UNIT_DEPENDENCY_MASK_FULL = (1 << 10) - 1,
} UnitDependencyMask;
/* The Unit's dependencies[] hashmaps use this structure as value. It has the same size as a void pointer, and thus can
char *description;
char **documentation;
+ /* The SELinux context used for checking access to this unit read off the unit file at load time (do
+ * not confuse with the selinux_context field in ExecContext which is the SELinux context we'll set
+ * for processes) */
+ char *access_selinux_context;
+
char *fragment_path; /* if loaded from a config file this is the primary path to it */
char *source_path; /* if converted, the source file */
char **dropin_paths;
bool unit_needs_console(Unit *u);
-const char *unit_label_path(const Unit *u);
-
int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error);
static inline bool unit_has_job_type(Unit *u, JobType type) {
[Manager]
#LogLevel=info
-#LogTarget=console
+#LogTarget=auto
#LogColor=yes
#LogLocation=no
#LogTime=no
#include "fs-util.h"
#include "io-util.h"
#include "journal-importer.h"
+#include "journal-send.h"
#include "log.h"
#include "macro.h"
#include "main-func.h"
core_message = strjoina(core_message, stacktrace ? "\n\n" : NULL, stacktrace);
- if (context->is_journald) {
- /* We cannot log to the journal, so just print the message.
- * The target was set previously to something safe. */
+ if (context->is_journald)
+ /* We might not be able to log to the journal, so let's always print the message to another
+ * log target. The target was set previously to something safe. */
log_dispatch(LOG_ERR, 0, core_message);
- return 0;
- }
(void) iovw_put_string_field(iovw, "MESSAGE=", core_message);
coredump_size, arg_journal_size_max);
}
+ /* If journald is coredumping, we have to be careful that we don't deadlock when trying to write the
+ * coredump to the journal, so we put the journal socket in nonblocking mode before trying to write
+ * the coredump to the socket. */
+
+ if (context->is_journald) {
+ r = journal_fd_nonblock(true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make journal socket non-blocking: %m");
+ }
+
r = sd_journal_sendv(iovw->iovec, iovw->count);
- if (r < 0)
+
+ if (context->is_journald) {
+ int k;
+
+ k = journal_fd_nonblock(false);
+ if (k < 0)
+ return log_error_errno(k, "Failed to make journal socket blocking: %m");
+ }
+
+ if (r == -EAGAIN && context->is_journald)
+ log_warning_errno(r, "Failed to log journal coredump, ignoring: %m");
+ else if (r < 0)
return log_error_errno(r, "Failed to log coredump: %m");
return 0;
struct iovec_wrapper *iovw;
int r;
+ /* When we're invoked by the kernel, stdout/stderr are closed which is dangerous because the fds
+ * could get reallocated. To avoid hard to debug issues, let's instead bind stdout/stderr to
+ * /dev/null. */
+ r = rearrange_stdio(STDIN_FILENO, -1, -1);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect stdout/stderr to /dev/null: %m");
+
log_debug("Processing coredump received from the kernel...");
iovw = iovw_new();
#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-locator.h"
#include "bus-util.h"
#include "compress.h"
#include "def.h"
if (r < 0)
return log_error_errno(r, "Failed to acquire bus: %m");
- r = sd_bus_message_new_method_call(
- bus,
- &m,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "ListUnitsByPatterns");
+ r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsByPatterns");
if (r < 0)
return bus_log_create_error(r);
arg_tpm2_device = streq(optarg, "auto") ? NULL : optarg;
break;
- case ARG_TPM2_PCRS:
+ case ARG_TPM2_PCRS: {
+ uint32_t mask;
+
if (isempty(optarg)) {
arg_tpm2_pcr_mask = 0;
break;
}
- uint32_t mask;
r = tpm2_parse_pcrs(optarg, &mask);
if (r < 0)
return r;
arg_tpm2_pcr_mask |= mask;
break;
+ }
case ARG_NAME:
if (isempty(optarg)) {
#include "cryptsetup-util.h"
#include "env-util.h"
#include "escape.h"
+#include "fileio.h"
#include "libfido2-util.h"
#include "main-func.h"
#include "memory-util.h"
#include "tpm2-util.h"
static EnrollType arg_enroll_type = _ENROLL_TYPE_INVALID;
+static char *arg_unlock_keyfile = NULL;
static char *arg_pkcs11_token_uri = NULL;
static char *arg_fido2_device = NULL;
static char *arg_tpm2_device = NULL;
assert_cc(sizeof(arg_wipe_slots_mask) * 8 >= _ENROLL_TYPE_MAX);
+STATIC_DESTRUCTOR_REGISTER(arg_unlock_keyfile, freep);
STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, freep);
STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_wipe_slots, freep);
static bool wipe_requested(void) {
return arg_n_wipe_slots > 0 ||
" --version Show package version\n"
" --password Enroll a user-supplied password\n"
" --recovery-key Enroll a recovery key\n"
+ " --unlock-key-file=PATH\n"
+ " Use a file to unlock the volume\n"
" --pkcs11-token-uri=URI\n"
" Specify PKCS#11 security token URI\n"
" --fido2-credential-algorithm=STRING\n"
ARG_VERSION = 0x100,
ARG_PASSWORD,
ARG_RECOVERY_KEY,
+ ARG_UNLOCK_KEYFILE,
ARG_PKCS11_TOKEN_URI,
ARG_FIDO2_DEVICE,
ARG_TPM2_DEVICE,
{ "version", no_argument, NULL, ARG_VERSION },
{ "password", no_argument, NULL, ARG_PASSWORD },
{ "recovery-key", no_argument, NULL, ARG_RECOVERY_KEY },
+ { "unlock-key-file", required_argument, NULL, ARG_UNLOCK_KEYFILE },
{ "pkcs11-token-uri", required_argument, NULL, ARG_PKCS11_TOKEN_URI },
{ "fido2-credential-algorithm", required_argument, NULL, ARG_FIDO2_CRED_ALG },
{ "fido2-device", required_argument, NULL, ARG_FIDO2_DEVICE },
arg_enroll_type = ENROLL_RECOVERY;
break;
+ case ARG_UNLOCK_KEYFILE:
+ r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_unlock_keyfile);
+ if (r < 0)
+ return r;
+ break;
+
case ARG_PKCS11_TOKEN_URI: {
_cleanup_free_ char *uri = NULL;
break;
}
- case ARG_TPM2_PIN: {
+ case ARG_TPM2_PIN:
r = parse_boolean_argument("--tpm2-with-pin=", optarg, &arg_tpm2_pin);
if (r < 0)
return r;
break;
- }
case ARG_WIPE_SLOT: {
const char *p = optarg;
if (!vk)
return log_oom();
+ if (arg_unlock_keyfile) {
+ _cleanup_(erase_and_freep) char *password = NULL;
+ size_t password_len;
+
+ r = read_full_file_full(
+ AT_FDCWD,
+ arg_unlock_keyfile,
+ 0,
+ SIZE_MAX,
+ READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
+ NULL,
+ &password,
+ &password_len);
+ if (r < 0)
+ return log_error_errno(r, "Reading keyfile %s failed: %m", arg_unlock_keyfile);
+
+ r = crypt_volume_key_get(
+ cd,
+ CRYPT_ANY_SLOT,
+ vk,
+ &vks,
+ password,
+ password_len);
+ if (r < 0)
+ return log_error_errno(r, "Unlocking via keyfile failed: %m");
+
+ goto out;
+ }
+
r = getenv_steal_erase("PASSWORD", &envpw);
if (r < 0)
return log_error_errno(r, "Failed to acquire password from environment: %m");
}
}
+out:
*ret_cd = TAKE_PTR(cd);
*ret_volume_key = TAKE_PTR(vk);
*ret_volume_key_size = vks;
return 0;
}
-static int print_dependencies(FILE *f, const char* device_path) {
+static int print_dependencies(FILE *f, const char* device_path, const char* timeout_value, bool canfail) {
int r;
+ assert(!canfail || timeout_value);
+
if (STR_IN_SET(device_path, "-", "none"))
/* None, nothing to do */
return 0;
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- fprintf(f,
- "After=%1$s\n"
- "Requires=%1$s\n", unit);
+ fprintf(f, "After=%1$s\n", unit);
+ if (canfail) {
+ fprintf(f, "Wants=%1$s\n", unit);
+ r = write_drop_in_format(arg_dest, unit, 90, "device-timeout",
+ "# Automatically generated by systemd-cryptsetup-generator \n\n"
+ "[Unit]\nJobRunningTimeoutSec=%s", timeout_value);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write device drop-in: %m");
+ } else
+ fprintf(f, "Requires=%1$s\n", unit);
} else {
/* Regular file, add mount dependency */
_cleanup_free_ char *escaped_path = specifier_escape(device_path);
netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
if (key_file && !keydev) {
- r = print_dependencies(f, key_file);
+ r = print_dependencies(f, key_file,
+ keyfile_timeout_value,
+ /* canfail= */ keyfile_can_timeout > 0);
if (r < 0)
return r;
}
/* Check if a header option was specified */
if (detached_header > 0 && !headerdev) {
- r = print_dependencies(f, header_path);
+ r = print_dependencies(f, header_path,
+ NULL,
+ /* canfail= */ false); /* header is always necessary */
if (r < 0)
return r;
}
assert_se(token == r);
assert(json);
- if (pin && memchr(pin, 0, pin_size - 1))
- return crypt_log_error_errno(cd, ENOANO, "PIN must be characters string.");
-
- /* pin was passed as pin = pin, pin_size = strlen(pin). We need to add terminating
- * NULL byte to addressable memory*/
- if (pin && pin[pin_size-1] != '\0') {
- pin_string = strndup(pin, pin_size);
- if (!pin_string)
- return crypt_log_oom(cd);
- }
+ r = crypt_normalize_pin(pin, pin_size, &pin_string);
+ if (r < 0)
+ return crypt_log_debug_errno(cd, r, "Can not normalize PIN: %m");
- return acquire_luks2_key(cd, json, (const char *)usrptr, pin_string ?: pin, password, password_len);
+ return acquire_luks2_key(cd, json, (const char *)usrptr, pin_string, password, password_len);
}
/*
return crypt_log_debug_errno(cd, r, TOKEN_NAME " open failed: %m.");
}
-/*
- * This function is called from within following libcryptsetup calls
- * provided conditions further below are met:
- *
- * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-tpm2'):
- *
- * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
- * (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
- * and token is assigned to at least single keyslot).
- *
- * - if plugin defines validate function (see cryptsetup_token_validate below) it must have
- * passed the check (aka return 0)
- */
-_public_ int cryptsetup_token_open(
+_public_ int cryptsetup_token_open_pin(
struct crypt_device *cd, /* is always LUKS2 context */
int token /* is always >= 0 */,
+ const char *pin,
+ size_t pin_size,
char **password, /* freed by cryptsetup_token_buffer_free */
size_t *password_len,
void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
_cleanup_free_ void *blob = NULL, *policy_hash = NULL;
_cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL;
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
- _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+ _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
+ assert(!pin || pin_size);
assert(password);
assert(password_len);
assert(token >= 0);
assert(token == r);
assert(json);
+ r = crypt_normalize_pin(pin, pin_size, &pin_string);
+ if (r < 0)
+ return crypt_log_debug_errno(cd, r, "Can not normalize PIN: %m");
+
if (usrptr)
params = *(systemd_tpm2_plugin_params *)usrptr;
policy_hash,
policy_hash_size,
flags,
+ pin_string,
&decrypted_key,
&decrypted_key_size);
if (r < 0)
return 0;
}
+/*
+ * This function is called from within following libcryptsetup calls
+ * provided conditions further below are met:
+ *
+ * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-tpm2'):
+ *
+ * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device
+ * (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY
+ * and token is assigned to at least single keyslot).
+ *
+ * - if plugin defines validate function (see cryptsetup_token_validate below) it must have
+ * passed the check (aka return 0)
+ */
+_public_ int cryptsetup_token_open(
+ struct crypt_device *cd, /* is always LUKS2 context */
+ int token /* is always >= 0 */,
+ char **password, /* freed by cryptsetup_token_buffer_free */
+ size_t *password_len,
+ void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
+
+ return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr);
+}
+
/*
* libcryptsetup callback for memory deallocation of 'password' parameter passed in
* any crypt_token_open_* plugin function
return 0;
}
+
+int crypt_normalize_pin(const void *pin, size_t pin_size, char **ret_pin_string) {
+
+ _cleanup_free_ char *pin_string = NULL;
+
+ assert(pin || !pin_size);
+ assert(ret_pin_string);
+
+ if (!pin) {
+ *ret_pin_string = NULL;
+ return 0;
+ }
+
+ /* Refuse embedded NULL bytes, but allow trailing NULL */
+ if (memchr(pin, 0, pin_size - 1))
+ return -EINVAL;
+
+ /* Enforce trailing NULL byte if missing */
+ pin_string = memdup_suffix0(pin, pin_size);
+ if (!pin_string)
+ return -ENOMEM;
+
+ *ret_pin_string = TAKE_PTR(pin_string);
+
+ return 0;
+}
char **ret_dump_str);
int crypt_dump_hex_string(const char *hex_str, char **ret_dump_str);
+
+int crypt_normalize_pin(const void *pin, size_t pin_size, char **ret_pin_string);
const void *policy_hash,
size_t policy_hash_size,
TPM2Flags flags,
+ const char *pin,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
_cleanup_free_ char *auto_device = NULL;
- _cleanup_(erase_and_freep) char *pin_str = NULL;
int r;
assert(ret_decrypted_key);
device = auto_device;
}
- r = getenv_steal_erase("PIN", &pin_str);
- if (r < 0)
- return log_error_errno(r, "Failed to acquire PIN from environment: %m");
- if (!r) {
- /* PIN entry is not supported by plugin, let it fallback, possibly to sd-cryptsetup's
- * internal handling. */
- if (flags & TPM2_FLAGS_USE_PIN)
- return -EOPNOTSUPP;
- }
+ if ((flags & TPM2_FLAGS_USE_PIN) && !pin)
+ return -ENOANO;
return tpm2_unseal(
device,
pcr_mask, pcr_bank,
primary_alg,
key_data, key_data_size,
- policy_hash, policy_hash_size, pin_str,
+ policy_hash, policy_hash_size, pin,
ret_decrypted_key, ret_decrypted_key_size);
}
const void *policy_hash,
size_t policy_hash_size,
TPM2Flags flags,
+ const char *pin,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size);
}
#endif
-static int attach_luks2_by_fido2_via_plugin(
+static int crypt_activate_by_token_pin_ask_password(
struct crypt_device *cd,
const char *name,
+ const char *type,
usec_t until,
bool headless,
void *usrptr,
- uint32_t activation_flags) {
+ uint32_t activation_flags,
+ const char *message,
+ const char *key_name,
+ const char *credential_name) {
#if HAVE_LIBCRYPTSETUP_PLUGINS
AskPasswordFlags flags = ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
_cleanup_strv_free_erase_ char **pins = NULL;
int r;
- r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags);
+ r = crypt_activate_by_token_pin(cd, name, type, CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
if (r != -ENOANO) /* needs pin or pin is wrong */
return r;
STRV_FOREACH(p, pins) {
- r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
+ r = crypt_activate_by_token_pin(cd, name, type, CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
if (r != -ENOANO) /* needs pin or pin is wrong */
for (;;) {
pins = strv_free_erase(pins);
- r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
+ r = ask_password_auto(message, "drive-harddisk", NULL, key_name, credential_name, until, flags, &pins);
if (r < 0)
return r;
STRV_FOREACH(p, pins) {
- r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
+ r = crypt_activate_by_token_pin(cd, name, type, CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
if (r != -ENOANO) /* needs pin or pin is wrong */
#endif
}
+static int attach_luks2_by_fido2_via_plugin(
+ struct crypt_device *cd,
+ const char *name,
+ usec_t until,
+ bool headless,
+ void *usrptr,
+ uint32_t activation_flags) {
+
+ return crypt_activate_by_token_pin_ask_password(
+ cd,
+ name,
+ "systemd-fido2",
+ until,
+ headless,
+ usrptr,
+ activation_flags,
+ "Please enter security token PIN:",
+ "fido2-pin",
+ "cryptsetup.fido2-pin");
+}
+
static int attach_luks_or_plain_or_bitlk_by_fido2(
struct crypt_device *cd,
const char *name,
static int attach_luks2_by_tpm2_via_plugin(
struct crypt_device *cd,
const char *name,
+ usec_t until,
+ bool headless,
uint32_t flags) {
#if HAVE_LIBCRYPTSETUP_PLUGINS
- int r;
-
systemd_tpm2_plugin_params params = {
.search_pcr_mask = arg_tpm2_pcr_mask,
.device = arg_tpm2_device
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Libcryptsetup has external plugins support disabled.");
- r = crypt_activate_by_token_pin(cd, name, "systemd-tpm2", CRYPT_ANY_TOKEN, NULL, 0, ¶ms, flags);
- if (r > 0) /* returns unlocked keyslot id on success */
- r = 0;
-
- return r;
+ return crypt_activate_by_token_pin_ask_password(
+ cd,
+ name,
+ "systemd-tpm2",
+ until,
+ headless,
+ ¶ms,
+ flags,
+ "Please enter TPM2 PIN:",
+ "tpm2-pin",
+ "cryptsetup.tpm2-pin");
#else
return -EOPNOTSUPP;
#endif
return -EAGAIN; /* Mangle error code: let's make any form of TPM2 failure non-fatal. */
}
} else {
- r = attach_luks2_by_tpm2_via_plugin(cd, name, flags);
+ r = attach_luks2_by_tpm2_via_plugin(cd, name, until, arg_headless, flags);
+ if (r >= 0)
+ return 0;
/* EAGAIN means: no tpm2 chip found
* EOPNOTSUPP means: no libcryptsetup plugins support */
if (r == -ENXIO)
/* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */
if (!key_file && !key_data) {
- r = crypt_activate_by_token(cd, volume, CRYPT_ANY_TOKEN, NULL, flags);
+ r = crypt_activate_by_token_pin_ask_password(
+ cd,
+ volume,
+ NULL,
+ until,
+ arg_headless,
+ NULL,
+ flags,
+ "Please enter LUKS2 token PIN:",
+ "luks2-pin",
+ "cryptsetup.luks2-pin");
if (r >= 0) {
log_debug("Volume %s activated with LUKS token id %i.", volume, r);
return 0;
#include <sys/ioctl.h>
#include <sys/mount.h>
+#include "sd-device.h"
+
#include "architecture.h"
+#include "blockdev-util.h"
#include "chase-symlinks.h"
#include "copy.h"
#include "dissect-image.h"
#include "main-func.h"
#include "mkdir.h"
#include "mount-util.h"
+#include "mountpoint-util.h"
#include "namespace-util.h"
#include "parse-argument.h"
#include "parse-util.h"
static enum {
ACTION_DISSECT,
ACTION_MOUNT,
+ ACTION_UMOUNT,
ACTION_COPY_FROM,
ACTION_COPY_TO,
} arg_action = ACTION_DISSECT;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
+static bool arg_rmdir = false;
STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
" --fsck=BOOL Run fsck before mounting\n"
" --growfs=BOOL Grow file system to partition size, if marked\n"
" --mkdir Make mount directory before mounting, if missing\n"
+ " --rmdir Remove mount directory after unmounting\n"
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n"
" --root-hash=HASH Specify root hash for verity\n"
" --root-hash-sig=SIG Specify pkcs7 signature of root hash for verity\n"
" --version Show package version\n"
" -m --mount Mount the image to the specified directory\n"
" -M Shortcut for --mount --mkdir\n"
+ " -u --umount Unmount the image from the specified directory\n"
+ " -U Shortcut for --umount --rmdir\n"
" -x --copy-from Copy files from image to host\n"
" -a --copy-to Copy files from host to image\n"
"\nSee the %2$s for details.\n",
ARG_ROOT_HASH_SIG,
ARG_VERITY_DATA,
ARG_MKDIR,
+ ARG_RMDIR,
ARG_JSON,
};
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "mount", no_argument, NULL, 'm' },
+ { "umount", no_argument, NULL, 'u' },
{ "read-only", no_argument, NULL, 'r' },
{ "discard", required_argument, NULL, ARG_DISCARD },
{ "fsck", required_argument, NULL, ARG_FSCK },
{ "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
{ "verity-data", required_argument, NULL, ARG_VERITY_DATA },
{ "mkdir", no_argument, NULL, ARG_MKDIR },
+ { "rmdir", no_argument, NULL, ARG_RMDIR },
{ "copy-from", no_argument, NULL, 'x' },
{ "copy-to", no_argument, NULL, 'a' },
{ "json", required_argument, NULL, ARG_JSON },
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hmrMxa", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "hmurMUxa", options, NULL)) >= 0) {
switch (c) {
arg_flags |= DISSECT_IMAGE_MKDIR;
break;
+ case 'u':
+ arg_action = ACTION_UMOUNT;
+ break;
+
+ case ARG_RMDIR:
+ arg_rmdir = true;
+ break;
+
+ case 'U':
+ /* Shortcut combination of the above two */
+ arg_action = ACTION_UMOUNT;
+ arg_rmdir = true;
+ break;
+
case 'x':
arg_action = ACTION_COPY_FROM;
arg_flags |= DISSECT_IMAGE_READ_ONLY;
arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
break;
+ case ACTION_UMOUNT:
+ if (optind + 1 != argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Expected a mount point path as only argument.");
+
+ arg_path = argv[optind];
+ break;
+
case ACTION_COPY_FROM:
if (argc < optind + 2 || argc > optind + 3)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
return 0;
}
+static int action_umount(const char *path) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *canonical = NULL;
+ dev_t devno;
+ const char *devname;
+ _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ int r, k;
+
+ fd = chase_symlinks_and_open(path, NULL, 0, O_DIRECTORY, &canonical);
+ if (fd == -ENOTDIR)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "'%s' is not a directory", path);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to resolve path '%s': %m", path);
+
+ r = fd_is_mount_point(fd, NULL, 0);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'%s' is not a mount point", canonical);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether '%s' is a mount point: %m", canonical);
+
+ r = fd_get_whole_disk(fd, /*backing=*/ true, &devno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to find backing block device for '%s': %m", canonical);
+
+ r = sd_device_new_from_devnum(&device, 'b', devno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create sd-device object for block device %u:%u: %m",
+ major(devno), minor(devno));
+
+ r = sd_device_get_devname(device, &devname);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get devname of block device %u:%u: %m",
+ major(devno), minor(devno));
+
+ r = loop_device_open(devname, 0, &d);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open loop device '%s': %m", devname);
+
+ r = loop_device_flock(d, LOCK_EX);
+ if (r < 0)
+ return log_error_errno(r, "Failed to lock loop device '%s': %m", devname);
+
+ /* We've locked the loop device, now we're ready to unmount. To allow the unmount to succeed, we have
+ * to close the O_PATH fd we opened earlier. */
+ fd = safe_close(fd);
+
+ r = umount_recursive(canonical, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unmount '%s': %m", canonical);
+
+ /* We managed to lock and unmount successfully? That means we can try to remove the loop device.*/
+ loop_device_unrelinquish(d);
+
+ if (arg_rmdir) {
+ k = RET_NERRNO(rmdir(canonical));
+ if (k < 0)
+ log_error_errno(k, "Failed to remove mount directory '%s': %m", canonical);
+ } else
+ k = 0;
+
+ /* Before loop_device_unrefp() kicks in, let's explicitly remove all the partition subdevices of the
+ * loop device. We do this to ensure that all traces of the loop device are gone by the time this
+ * command exits. */
+ r = block_device_remove_all_partitions(d->fd);
+ if (r == -EBUSY) {
+ log_error_errno(r, "One or more partitions of '%s' are busy, ignoring", devname);
+ r = 0;
+ }
+ if (r < 0)
+ log_error_errno(r, "Failed to remove one or more partitions of '%s': %m", devname);
+
+
+ return k < 0 ? k : r;
+}
+
static int run(int argc, char *argv[]) {
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
if (r <= 0)
return r;
+ if (arg_action == ACTION_UMOUNT)
+ return action_umount(arg_path);
+
r = verity_settings_load(
&arg_verity_settings,
arg_image, NULL, NULL);
return log_error_errno(r, "Failed to read host timezone: %m");
(void) mkdir_parents(etc_localtime, 0755);
- if (symlink(p, etc_localtime) < 0)
- return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
+ r = symlink_atomic(p, etc_localtime);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create %s symlink: %m", etc_localtime);
log_info("%s copied.", etc_localtime);
return 0;
e = strjoina("../usr/share/zoneinfo/", arg_timezone);
(void) mkdir_parents(etc_localtime, 0755);
- if (symlink(e, etc_localtime) < 0)
- return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime);
+ r = symlink_atomic(e, etc_localtime);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create %s symlink: %m", etc_localtime);
log_info("%s written", etc_localtime);
return 0;
if (arg_root_password)
return 0;
- r = read_credential("passwd.hashed-password.root", (void**) &arg_root_password, NULL);
- if (r == -ENOENT) {
- r = read_credential("passwd.plaintext-password.root", (void**) &arg_root_password, NULL);
- if (r < 0)
- log_debug_errno(r, "Couldn't read credential 'passwd.{hashed|plaintext}-password.root', ignoring: %m");
- else {
- arg_root_password_is_hashed = false;
- return 0;
- }
- } else if (r < 0)
- log_debug_errno(r, "Couldn't read credential 'passwd.hashed-password.root', ignoring: %m");
- else {
- arg_root_password_is_hashed = true;
+ if (get_credential_user_password("root", &arg_root_password, &arg_root_password_is_hashed) >= 0)
return 0;
- }
if (!arg_prompt_root_password)
return 0;
#include <errno.h>
#include "string-util-fundamental.h"
+/* Features of the loader, i.e. systemd-boot */
#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT (UINT64_C(1) << 0)
#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (UINT64_C(1) << 1)
#define EFI_LOADER_FEATURE_ENTRY_DEFAULT (UINT64_C(1) << 2)
#define EFI_LOADER_FEATURE_XBOOTLDR (UINT64_C(1) << 5)
#define EFI_LOADER_FEATURE_RANDOM_SEED (UINT64_C(1) << 6)
#define EFI_LOADER_FEATURE_LOAD_DRIVER (UINT64_C(1) << 7)
+#define EFI_LOADER_FEATURE_SORT_KEY (UINT64_C(1) << 8)
+#define EFI_LOADER_FEATURE_SAVED_ENTRY (UINT64_C(1) << 9)
+#define EFI_LOADER_FEATURE_DEVICETREE (UINT64_C(1) << 10)
+
+/* Features of the stub, i.e. systemd-stub */
+#define EFI_STUB_FEATURE_REPORT_BOOT_PARTITION (UINT64_C(1) << 0)
+#define EFI_STUB_FEATURE_PICK_UP_CREDENTIALS (UINT64_C(1) << 1)
+#define EFI_STUB_FEATURE_PICK_UP_SYSEXTS (UINT64_C(1) << 2)
+#define EFI_STUB_FEATURE_THREE_PCRS (UINT64_C(1) << 3)
typedef enum SecureBootMode {
SECURE_BOOT_UNSUPPORTED,
_expr_; \
})
+#define ASSERT_NONNEG(expr) \
+ ({ \
+ typeof(expr) _expr_ = (expr), _zero = 0; \
+ assert(_expr_ >= _zero); \
+ _expr_; \
+ })
+
+#define ASSERT_SE_NONNEG(expr) \
+ ({ \
+ typeof(expr) _expr_ = (expr), _zero = 0; \
+ assert_se(_expr_ >= _zero); \
+ _expr_; \
+ })
+
#define assert_cc(expr) static_assert(expr, #expr)
* on this macro will run concurrently to all other code conditionalized
* the same way, there's no ordering or completion enforced. */
#define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
-#define __ONCE(o) \
- ({ \
- static bool (o) = false; \
- __sync_bool_compare_and_swap(&(o), false, true); \
+#define __ONCE(o) \
+ ({ \
+ static bool (o) = false; \
+ __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
})
#undef MAX
MIN(_c, z); \
})
+/* Returns true if the passed integer is a positive power of two */
+#define CONST_ISPOWEROF2(x) \
+ ((x) > 0 && ((x) & ((x) - 1)) == 0)
+
+#define ISPOWEROF2(x) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(x), \
+ CONST_ISPOWEROF2(x), \
+ ({ \
+ const typeof(x) _x = (x); \
+ CONST_ISPOWEROF2(_x); \
+ }))
+
#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
#define __LESS_BY(aq, a, bq, b) \
({ \
})
static inline size_t ALIGN_TO(size_t l, size_t ali) {
- /* Check that alignment is exponent of 2 */
-#if SIZE_MAX == UINT_MAX
- assert(__builtin_popcount(ali) == 1);
-#elif SIZE_MAX == ULONG_MAX
- assert(__builtin_popcountl(ali) == 1);
-#elif SIZE_MAX == ULLONG_MAX
- assert(__builtin_popcountll(ali) == 1);
-#else
- #error "Unexpected size_t"
-#endif
+ assert(ISPOWEROF2(ali));
if (l > SIZE_MAX - (ali - 1))
return SIZE_MAX; /* indicate overflow */
__builtin_choose_expr( \
__builtin_constant_p(l) && \
__builtin_constant_p(ali) && \
- __builtin_popcountll(ali) == 1 && /* is power of 2? */ \
+ CONST_ISPOWEROF2(ali) && \
(l <= SIZE_MAX - (ali - 1)), /* overflow? */ \
((l) + (ali) - 1) & ~((ali) - 1), \
VOID_0)
'macro-fundamental.h',
'sha256.h',
'string-util-fundamental.h',
+ 'tpm-pcr.h',
)
# for sd-boot
'efivars-fundamental.c',
'sha256.c',
'string-util-fundamental.c',
+ 'tpm-pcr.c',
)
# for libbasic
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stddef.h>
+
+#include "tpm-pcr.h"
+
+const char* const unified_sections[_UNIFIED_SECTION_MAX + 1] = {
+ [UNIFIED_SECTION_LINUX] = ".linux",
+ [UNIFIED_SECTION_OSREL] = ".osrel",
+ [UNIFIED_SECTION_CMDLINE] = ".cmdline",
+ [UNIFIED_SECTION_INITRD] = ".initrd",
+ [UNIFIED_SECTION_SPLASH] = ".splash",
+ [UNIFIED_SECTION_DTB] = ".dtb",
+ NULL,
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* The various TPM PCRs we measure into from sd-stub and sd-boot. */
+
+/* This TPM PCR is where we extend the sd-stub "payloads" into, before using them. i.e. the kernel ELF image,
+ * embedded initrd, and so on. In contrast to PCR 4 (which also contains this data, given the whole
+ * surrounding PE image is measured into it) this should be reasonably pre-calculatable, because it *only*
+ * consists of static data from the kernel PE image. */
+#define TPM_PCR_INDEX_KERNEL_IMAGE 11U
+
+/* This TPM PCR is where sd-stub extends the kernel command line and any passed credentials into. */
+#define TPM_PCR_INDEX_KERNEL_PARAMETERS 12U
+
+/* sd-stub used to write the kernel command line/credentials into PCR 8, in systemd <= 250. Let's provide for
+ * some compatibility. (Remove in 2023!) */
+#if EFI_TPM_PCR_COMPAT
+#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT 8U
+#else
+#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT UINT32_MAX
+#endif
+
+/* This TPM PCR is where we extend the initrd sysext images into which we pass to the booted kernel */
+#define TPM_PCR_INDEX_INITRD_SYSEXTS 13U
+
+/* List of PE sections that have special meaning for us in unified kernels. This is the canonical order in
+ * which we measure the sections into TPM PCR 11 (see above). PLEASE DO NOT REORDER! */
+typedef enum UnifiedSection {
+ UNIFIED_SECTION_LINUX,
+ UNIFIED_SECTION_OSREL,
+ UNIFIED_SECTION_CMDLINE,
+ UNIFIED_SECTION_INITRD,
+ UNIFIED_SECTION_SPLASH,
+ UNIFIED_SECTION_DTB,
+ _UNIFIED_SECTION_MAX,
+} UnifiedSection;
+
+extern const char* const unified_sections[_UNIFIED_SECTION_MAX + 1];
r = bus_home_path(h, l + k);
if (r < 0)
return r;
+
+ k++;
}
*nodes = TAKE_PTR(l);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <sys/mount.h>
+#if WANT_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include <poll.h>
#include <sys/file.h>
#include <sys/ioctl.h>
-#include <sys/mount.h>
#include <sys/xattr.h>
#if HAVE_VALGRIND_MEMCHECK_H
#include <sched.h>
#include <sys/mount.h>
+#if WANT_LINUX_FS_H
#include <linux/fs.h>
+#endif
#include "alloc-util.h"
#include "fd-util.h"
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include <linux/fs.h>
#include <sys/vfs.h>
#include "sd-id128.h"
+#include "cryptsetup-util.h"
#include "homework-password-cache.h"
#include "loop-util.h"
+#include "missing_fs.h" /* for FS_KEY_DESCRIPTOR_SIZE, do not include linux/fs.h */
#include "missing_keyctl.h"
#include "missing_syscall.h"
#include "user-record.h"
typedef enum PullFlags {
PULL_FORCE = 1 << 0, /* replace existing image */
PULL_READ_ONLY = 1 << 1, /* make generated image read-only */
- PULL_SETTINGS = 1 << 1, /* download .nspawn settings file */
- PULL_ROOTHASH = 1 << 2, /* only for raw: download .roothash file for verity */
- PULL_ROOTHASH_SIGNATURE = 1 << 3, /* only for raw: download .roothash.p7s file for verity */
- PULL_VERITY = 1 << 4, /* only for raw: download .verity file for verity */
- PULL_BTRFS_SUBVOL = 1 << 2, /* tar: preferably create images as btrfs subvols */
- PULL_BTRFS_QUOTA = 1 << 3, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
- PULL_CONVERT_QCOW2 = 1 << 4, /* raw: if we detect a qcow2 image, unpack it */
- PULL_DIRECT = 1 << 5, /* download without rename games */
- PULL_SYNC = 1 << 6, /* fsync() right before we are done */
+ PULL_SETTINGS = 1 << 2, /* download .nspawn settings file */
+ PULL_ROOTHASH = 1 << 3, /* only for raw: download .roothash file for verity */
+ PULL_ROOTHASH_SIGNATURE = 1 << 4, /* only for raw: download .roothash.p7s file for verity */
+ PULL_VERITY = 1 << 5, /* only for raw: download .verity file for verity */
+ PULL_BTRFS_SUBVOL = 1 << 6, /* tar: preferably create images as btrfs subvols */
+ PULL_BTRFS_QUOTA = 1 << 7, /* tar: set up btrfs quota for new subvolume as child of parent subvolume */
+ PULL_CONVERT_QCOW2 = 1 << 8, /* raw: if we detect a qcow2 image, unpack it */
+ PULL_DIRECT = 1 << 9, /* download without rename games */
+ PULL_SYNC = 1 << 10, /* fsync() right before we are done */
/* The supported flags for the tar and the raw pulling */
PULL_FLAGS_MASK_TAR = PULL_FORCE|PULL_READ_ONLY|PULL_SETTINGS|PULL_BTRFS_SUBVOL|PULL_BTRFS_QUOTA|PULL_DIRECT|PULL_SYNC,
return 0;
}
- if (!isempty(arg_existing_data_device)) {
- r = crypt_init_data_device(&cd, device, arg_existing_data_device);
- if (r < 0)
- return log_error_errno(r, "Failed to add separate data device: %m");
- }
-
r = crypt_load(cd,
CRYPT_INTEGRITY,
&(struct crypt_params_integrity) {
if (r < 0)
return log_error_errno(r, "Failed to load integrity superblock: %m");
+ if (!isempty(arg_existing_data_device)) {
+ r = crypt_set_data_device(cd, arg_existing_data_device);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add separate data device: %m");
+ }
+
r = crypt_activate_by_volume_key(cd, volume, key_buf, key_buf_size, arg_activate_flags);
if (r < 0)
return log_error_errno(r, "Failed to set up integrity device: %m");
#include <sys/stat.h>
#include <unistd.h>
-#if HAVE_PCRE2
-# define PCRE2_CODE_UNIT_WIDTH 8
-# include <pcre2.h>
-#endif
-
#include "sd-bus.h"
#include "sd-device.h"
#include "sd-journal.h"
static uint64_t arg_vacuum_n_files = 0;
static usec_t arg_vacuum_time = 0;
static char **arg_output_fields = NULL;
-#if HAVE_PCRE2
static const char *arg_pattern = NULL;
static pcre2_code *arg_compiled_pattern = NULL;
-static int arg_case_sensitive = -1; /* -1 means be smart */
-#endif
+static PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO;
STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_facilities, set_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_output_fields, strv_freep);
-#if HAVE_PCRE2
-STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, sym_pcre2_code_freep);
-#endif
+STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, pattern_freep);
static enum {
ACTION_SHOW,
LIST_FIELDS(struct BootId, boot_list);
} BootId;
-#if HAVE_PCRE2
-static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
- int errorcode, r;
- PCRE2_SIZE erroroffset;
- pcre2_code *p;
-
- p = sym_pcre2_compile((PCRE2_SPTR8) pattern,
- PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
- if (!p) {
- unsigned char buf[LINE_MAX];
-
- r = sym_pcre2_get_error_message(errorcode, buf, sizeof buf);
-
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Bad pattern \"%s\": %s", pattern,
- r < 0 ? "unknown error" : (char *)buf);
- }
-
- *out = p;
- return 0;
-}
-#endif
-
static int add_matches_for_device(sd_journal *j, const char *devpath) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
sd_device *d = NULL;
printf("%1$s [OPTIONS...] [MATCHES...]\n\n"
"%5$sQuery the journal.%6$s\n\n"
- "%3$sOptions:%4$s\n"
+ "%3$sSource Options:%4$s\n"
" --system Show the system journal\n"
" --user Show the user journal for the current user\n"
" -M --machine=CONTAINER Operate on local container\n"
+ " -m --merge Show entries from all available journals\n"
+ " -D --directory=PATH Show journal files from directory\n"
+ " --file=PATH Show journal file\n"
+ " --root=ROOT Operate on files below a root directory\n"
+ " --image=IMAGE Operate on files in filesystem image\n"
+ " --namespace=NAMESPACE Show journal data from specified journal namespace\n"
+ "\n%3$sFiltering Options:%4$s\n"
" -S --since=DATE Show entries not older than the specified date\n"
" -U --until=DATE Show entries not newer than the specified date\n"
" -c --cursor=CURSOR Show entries starting at the specified cursor\n"
" --after-cursor=CURSOR Show entries after the specified cursor\n"
- " --show-cursor Print the cursor after all the entries\n"
" --cursor-file=FILE Show entries after cursor in FILE and update FILE\n"
" -b --boot[=ID] Show current boot or the specified boot\n"
- " --list-boots Show terse information about recorded boots\n"
- " -k --dmesg Show kernel message log from the current boot\n"
" -u --unit=UNIT Show logs from the specified unit\n"
" --user-unit=UNIT Show logs from the specified user unit\n"
" -t --identifier=STRING Show entries with the specified syslog identifier\n"
" --facility=FACILITY... Show entries with the specified facilities\n"
" -g --grep=PATTERN Show entries with MESSAGE matching PATTERN\n"
" --case-sensitive[=BOOL] Force case sensitive or insensitive matching\n"
- " -e --pager-end Immediately jump to the end in the pager\n"
- " -f --follow Follow the journal\n"
- " -n --lines[=INTEGER] Number of journal entries to show\n"
- " --no-tail Show all lines, even in follow mode\n"
- " -r --reverse Show the newest entries first\n"
+ " -k --dmesg Show kernel message log from the current boot\n"
+ "\n%3$sOutput Control Options:%4$s\n"
" -o --output=STRING Change journal output mode (short, short-precise,\n"
" short-iso, short-iso-precise, short-full,\n"
" short-monotonic, short-unix, verbose, export,\n"
" json, json-pretty, json-sse, json-seq, cat,\n"
" with-unit)\n"
" --output-fields=LIST Select fields to print in verbose/export/json modes\n"
+ " -n --lines[=INTEGER] Number of journal entries to show\n"
+ " -r --reverse Show the newest entries first\n"
+ " --show-cursor Print the cursor after all the entries\n"
" --utc Express time in Coordinated Universal Time (UTC)\n"
" -x --catalog Add message explanations where available\n"
+ " --no-hostname Suppress output of hostname field\n"
" --no-full Ellipsize fields\n"
" -a --all Show all fields, including long and unprintable\n"
+ " -f --follow Follow the journal\n"
+ " --no-tail Show all lines, even in follow mode\n"
" -q --quiet Do not show info messages and privilege warning\n"
+ "\n%3$sPager Control Options:%4$s\n"
" --no-pager Do not pipe output into a pager\n"
- " --no-hostname Suppress output of hostname field\n"
- " -m --merge Show entries from all available journals\n"
- " -D --directory=PATH Show journal files from directory\n"
- " --file=PATH Show journal file\n"
- " --root=ROOT Operate on files below a root directory\n"
- " --image=IMAGE Operate on files in filesystem image\n"
- " --namespace=NAMESPACE Show journal data from specified namespace\n"
+ " -e --pager-end Immediately jump to the end in the pager\n"
+ "\n%3$sForward Secure Sealing (FSS) Options:%4$s\n"
" --interval=TIME Time interval for changing the FSS sealing key\n"
" --verify-key=KEY Specify FSS verification key\n"
" --force Override of the FSS key pair with --setup-keys\n"
" --version Show package version\n"
" -N --fields List all field names currently used\n"
" -F --field=FIELD List all values that a specified field takes\n"
+ " --list-boots Show terse information about recorded boots\n"
" --disk-usage Show total disk usage of all journal files\n"
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
" --vacuum-files=INT Leave only the specified number of journal files\n"
break;
}
-#if HAVE_PCRE2
case 'g':
arg_pattern = optarg;
break;
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Bad --case-sensitive= argument \"%s\": %m", optarg);
- arg_case_sensitive = r;
+ arg_case = r ? PATTERN_COMPILE_CASE_SENSITIVE : PATTERN_COMPILE_CASE_INSENSITIVE;
} else
- arg_case_sensitive = true;
+ arg_case = PATTERN_COMPILE_CASE_SENSITIVE;
break;
-#else
- case 'g':
- case ARG_CASE_SENSITIVE:
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Compiled without pattern matching support");
-#endif
case 'S':
r = parse_timestamp(optarg, &arg_since);
if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Please specify only one of --since=, --cursor=, and --after-cursor.");
+ "Please specify only one of --since=, --cursor=, and --after-cursor=.");
if (arg_follow && arg_reverse)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
arg_system_units = strv_free(arg_system_units);
}
-#if HAVE_PCRE2
if (arg_pattern) {
- unsigned flags;
-
- r = dlopen_pcre2();
- if (r < 0)
- return r;
-
- if (arg_case_sensitive >= 0)
- flags = !arg_case_sensitive * PCRE2_CASELESS;
- else {
- _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
- bool has_case;
- _cleanup_(sym_pcre2_code_freep) pcre2_code *cs = NULL;
-
- md = sym_pcre2_match_data_create(1, NULL);
- if (!md)
- return log_oom();
-
- r = pattern_compile("[[:upper:]]", 0, &cs);
- if (r < 0)
- return r;
-
- r = sym_pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
- has_case = r >= 0;
-
- flags = !has_case * PCRE2_CASELESS;
- }
-
- log_debug("Doing case %s matching based on %s",
- flags & PCRE2_CASELESS ? "insensitive" : "sensitive",
- arg_case_sensitive >= 0 ? "request" : "pattern casing");
-
- r = pattern_compile(arg_pattern, flags, &arg_compiled_pattern);
+ r = pattern_compile_and_log(arg_pattern, arg_case, &arg_compiled_pattern);
if (r < 0)
return r;
}
-#endif
return 1;
}
}
}
-#if HAVE_PCRE2
if (arg_compiled_pattern) {
- _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
const void *message;
size_t len;
- PCRE2_SIZE *ovec;
-
- md = sym_pcre2_match_data_create(1, NULL);
- if (!md)
- return log_oom();
r = sd_journal_get_data(j, "MESSAGE", &message, &len);
if (r < 0) {
assert_se(message = startswith(message, "MESSAGE="));
- r = sym_pcre2_match(arg_compiled_pattern,
- message,
- len - strlen("MESSAGE="),
- 0, /* start at offset 0 in the subject */
- 0, /* default options */
- md,
- NULL);
- if (r == PCRE2_ERROR_NOMATCH) {
+ r = pattern_matches_and_log(arg_compiled_pattern, message,
+ len - strlen("MESSAGE="), highlight);
+ if (r < 0)
+ goto finish;
+ if (r == 0) {
need_seek = true;
continue;
}
- if (r < 0) {
- unsigned char buf[LINE_MAX];
- int r2;
-
- r2 = sym_pcre2_get_error_message(r, buf, sizeof buf);
- log_error("Pattern matching failed: %s",
- r2 < 0 ? "unknown error" : (char*) buf);
- r = -EINVAL;
- goto finish;
- }
-
- ovec = sym_pcre2_get_ovector_pointer(md);
- highlight[0] = ovec[0];
- highlight[1] = ovec[1];
}
-#endif
flags =
arg_all * OUTPUT_SHOW_ALL |
finish:
pager_close();
-#if HAVE_PCRE2
if (arg_compiled_pattern && r == 0 && n_shown == 0)
/* --grep was used, no error was thrown, but the pattern didn't
* match anything. Let's mimic grep's behavior here and return
* a non-zero exit code, so journalctl --grep can be used
* in scripts and such */
r = -ENOENT;
-#endif
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
dropin_dirname,
"Journal\0",
config_item_perf_lookup, journald_gperf_lookup,
- CONFIG_PARSE_WARN, s, NULL);
+ CONFIG_PARSE_WARN, s, NULL, NULL);
if (r < 0)
return r;
for (;;) {
switch (f->file->offline_state) {
- case OFFLINE_CANCEL:
- if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_CANCEL, OFFLINE_DONE))
+ case OFFLINE_CANCEL: {
+ OfflineState tmp_state = OFFLINE_CANCEL;
+ if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_DONE,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
continue;
+ }
return;
- case OFFLINE_AGAIN_FROM_SYNCING:
- if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_SYNCING))
+ case OFFLINE_AGAIN_FROM_SYNCING: {
+ OfflineState tmp_state = OFFLINE_AGAIN_FROM_SYNCING;
+ if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_SYNCING,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
continue;
+ }
break;
- case OFFLINE_AGAIN_FROM_OFFLINING:
- if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_SYNCING))
+ case OFFLINE_AGAIN_FROM_OFFLINING: {
+ OfflineState tmp_state = OFFLINE_AGAIN_FROM_OFFLINING;
+ if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_SYNCING,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
continue;
+ }
break;
case OFFLINE_SYNCING:
(void) fsync(f->file->fd);
- if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING))
- continue;
+ {
+ OfflineState tmp_state = OFFLINE_SYNCING;
+ if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_OFFLINING,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+ continue;
+ }
f->file->header->state = f->file->archive ? STATE_ARCHIVED : STATE_OFFLINE;
(void) fsync(f->file->fd);
break;
- case OFFLINE_OFFLINING:
- if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_OFFLINING, OFFLINE_DONE))
+ case OFFLINE_OFFLINING: {
+ OfflineState tmp_state = OFFLINE_OFFLINING;
+ if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_DONE,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
continue;
+ }
_fallthrough_;
case OFFLINE_DONE:
return;
case OFFLINE_AGAIN_FROM_OFFLINING:
return true;
- case OFFLINE_CANCEL:
- if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_CANCEL, OFFLINE_AGAIN_FROM_SYNCING))
+ case OFFLINE_CANCEL: {
+ OfflineState tmp_state = OFFLINE_CANCEL;
+ if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_AGAIN_FROM_SYNCING,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
continue;
+ }
return true;
- case OFFLINE_SYNCING:
- if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_SYNCING, OFFLINE_AGAIN_FROM_SYNCING))
+ case OFFLINE_SYNCING: {
+ OfflineState tmp_state = OFFLINE_SYNCING;
+ if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_AGAIN_FROM_SYNCING,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
continue;
+ }
return true;
- case OFFLINE_OFFLINING:
- if (!__sync_bool_compare_and_swap(&f->file->offline_state, OFFLINE_OFFLINING, OFFLINE_AGAIN_FROM_OFFLINING))
+ case OFFLINE_OFFLINING: {
+ OfflineState tmp_state = OFFLINE_OFFLINING;
+ if (!__atomic_compare_exchange_n(&f->file->offline_state, &tmp_state, OFFLINE_AGAIN_FROM_OFFLINING,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
continue;
+ }
return true;
default:
bool managed_journal_file_is_offlining(ManagedJournalFile *f) {
assert(f);
- __sync_synchronize();
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
if (IN_SET(f->file->offline_state, OFFLINE_DONE, OFFLINE_JOINED))
return false;
cat >"$D/sources/install.conf" <<EOF
layout=bls
initrd_generator=none
-# those are overriden by envvars
+# those are overridden by envvars
BOOT_ROOT="$D/badboot"
MACHINE_ID=badbadbadbadbadbad6abadbadbadbad
EOF
return 0;
}
-static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t t, struct duid *ret_duid, size_t *ret_len) {
+static int dhcp_identifier_set_duid_llt(
+ const struct hw_addr_data *hw_addr,
+ uint16_t arp_type,
+ usec_t t,
+ struct duid *ret_duid,
+ size_t *ret_len) {
+
uint16_t time_from_2000y;
- assert(addr);
+ assert(hw_addr);
assert(ret_duid);
assert(ret_len);
- if (addr_len == 0)
+ if (hw_addr->length == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
- assert_return(addr_len == ETH_ALEN, -EINVAL);
+ assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+ assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
unaligned_write_be16(&ret_duid->llt.htype, arp_type);
unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
- memcpy(ret_duid->llt.haddr, addr, addr_len);
+ memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length);
- *ret_len = offsetof(struct duid, llt.haddr) + addr_len;
+ *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
return 0;
}
-static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uint16_t arp_type, struct duid *ret_duid, size_t *ret_len) {
- assert(addr);
+static int dhcp_identifier_set_duid_ll(
+ const struct hw_addr_data *hw_addr,
+ uint16_t arp_type,
+ struct duid *ret_duid,
+ size_t *ret_len) {
+
+ assert(hw_addr);
assert(ret_duid);
assert(ret_len);
- if (addr_len == 0)
+ if (hw_addr->length == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
- assert_return(addr_len == ETH_ALEN, -EINVAL);
+ assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+ assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
unaligned_write_be16(&ret_duid->ll.htype, arp_type);
- memcpy(ret_duid->ll.haddr, addr, addr_len);
+ memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length);
- *ret_len = offsetof(struct duid, ll.haddr) + addr_len;
+ *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
return 0;
}
int dhcp_identifier_set_duid(
DUIDType duid_type,
- const uint8_t *addr,
- size_t addr_len,
+ const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t llt_time,
bool test_mode,
switch (duid_type) {
case DUID_TYPE_LLT:
- return dhcp_identifier_set_duid_llt(addr, addr_len, arp_type, llt_time, ret_duid, ret_len);
+ return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len);
case DUID_TYPE_EN:
return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
case DUID_TYPE_LL:
- return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len);
+ return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len);
case DUID_TYPE_UUID:
return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
default:
int dhcp_identifier_set_iaid(
int ifindex,
- const uint8_t *mac,
- size_t mac_len,
+ const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
bool use_mac,
void *ret) {
uint64_t id;
int r;
+ assert(ifindex > 0);
+ assert(hw_addr);
+ assert(ret);
+
if (udev_available() && !use_mac) {
/* udev should be around */
id = siphash24(name, strlen(name), HASH_KEY.bytes);
else
/* fall back to MAC address if no predictable name available */
- id = siphash24(mac, mac_len, HASH_KEY.bytes);
+ id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
id32 = (id & 0xffffffff) ^ (id >> 32);
#include "sd-id128.h"
+#include "ether-addr-util.h"
#include "macro.h"
#include "sparse-endian.h"
#include "time-util.h"
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
int dhcp_identifier_set_duid(
DUIDType duid_type,
- const uint8_t *addr,
- size_t addr_len,
+ const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t llt_time,
bool test_mode,
size_t *ret_len);
int dhcp_identifier_set_iaid(
int ifindex,
- const uint8_t *mac,
- size_t mac_len,
+ const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
bool use_mac,
void *ret);
#include "sd-dhcp-client.h"
#include "dhcp-protocol.h"
+#include "ether-addr-util.h"
#include "network-common.h"
#include "socket-util.h"
typedef struct sd_dhcp_client sd_dhcp_client;
-int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid,
- const uint8_t *mac_addr, size_t mac_addr_len,
- const uint8_t *bcast_addr, size_t bcast_addr_len,
- uint16_t arp_type, uint16_t port);
+int dhcp_network_bind_raw_socket(
+ int ifindex,
+ union sockaddr_union *link,
+ uint32_t xid,
+ const struct hw_addr_data *hw_addr,
+ const struct hw_addr_data *bcast_addr,
+ uint16_t arp_type,
+ uint16_t port);
int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type);
int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
const void *packet, size_t len);
#include "socket-util.h"
#include "unaligned.h"
-static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
- uint32_t xid,
- const uint8_t *bcast_addr,
- size_t bcast_addr_len,
- const struct ether_addr *eth_mac,
- uint16_t arp_type, uint8_t dhcp_hlen,
- uint16_t port) {
+static int _bind_raw_socket(
+ int ifindex,
+ union sockaddr_union *link,
+ uint32_t xid,
+ const struct hw_addr_data *hw_addr,
+ const struct hw_addr_data *bcast_addr,
+ uint16_t arp_type,
+ uint16_t port) {
+
+ assert(ifindex > 0);
+ assert(link);
+ assert(hw_addr);
+ assert(bcast_addr);
+ assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
+
+ switch (arp_type) {
+ case ARPHRD_ETHER:
+ assert(hw_addr->length == ETH_ALEN);
+ assert(bcast_addr->length == ETH_ALEN);
+ break;
+ case ARPHRD_INFINIBAND:
+ assert(hw_addr->length == 0);
+ assert(bcast_addr->length == INFINIBAND_ALEN);
+ break;
+ default:
+ assert_not_reached();
+ }
+
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (uint8_t) hw_addr->length, 1, 0), /* address length == hw_addr->length ? */
BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
/* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
* compare chaddr for ETH_ALEN bytes. */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 8), /* A (the MAC address length) == ETH_ALEN ? */
- BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(ð_mac->ether_addr_octet[0])), /* X <- 4 bytes of client's MAC */
- BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(ð_mac->ether_addr_octet[4])), /* X <- remainder of client's MAC */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 8), /* A (the MAC address length) == ETH_ALEN ? */
+ BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(hw_addr->bytes)), /* X <- 4 bytes of client's MAC */
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
+ BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(hw_addr->bytes + 4)), /* X <- remainder of client's MAC */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
+ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */
_cleanup_close_ int s = -1;
int r;
- assert(ifindex > 0);
- assert(link);
-
s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (s < 0)
return -errno;
.sll_protocol = htobe16(ETH_P_IP),
.sll_ifindex = ifindex,
.sll_hatype = htobe16(arp_type),
- .sll_halen = bcast_addr_len,
+ .sll_halen = bcast_addr->length,
};
- memcpy(link->ll.sll_addr, bcast_addr, bcast_addr_len); /* We may overflow link->ll. link->ll_buffer ensures we have enough space. */
+ /* We may overflow link->ll. link->ll_buffer ensures we have enough space. */
+ memcpy(link->ll.sll_addr, bcast_addr->bytes, bcast_addr->length);
r = bind(s, &link->sa, SOCKADDR_LL_LEN(link->ll));
if (r < 0)
int ifindex,
union sockaddr_union *link,
uint32_t xid,
- const uint8_t *mac_addr,
- size_t mac_addr_len,
- const uint8_t *bcast_addr,
- size_t bcast_addr_len,
+ const struct hw_addr_data *hw_addr,
+ const struct hw_addr_data *bcast_addr,
uint16_t arp_type,
uint16_t port) {
- static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
- /* Default broadcast address for IPoIB */
- static const uint8_t ib_bcast[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff
+ static struct hw_addr_data default_eth_bcast = {
+ .length = ETH_ALEN,
+ .ether = {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }},
+ }, default_ib_bcast = {
+ .length = INFINIBAND_ALEN,
+ .infiniband = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff
+ },
};
- struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } };
- const uint8_t *default_bcast_addr;
- size_t expected_bcast_addr_len;
- uint8_t dhcp_hlen = 0;
-
- if (arp_type == ARPHRD_ETHER) {
- assert_return(mac_addr_len == ETH_ALEN, -EINVAL);
- memcpy(ð_mac, mac_addr, ETH_ALEN);
- dhcp_hlen = ETH_ALEN;
-
- default_bcast_addr = eth_bcast;
- expected_bcast_addr_len = ETH_ALEN;
- } else if (arp_type == ARPHRD_INFINIBAND) {
- default_bcast_addr = ib_bcast;
- expected_bcast_addr_len = INFINIBAND_ALEN;
- } else
- return -EINVAL;
- if (bcast_addr && bcast_addr_len > 0)
- assert_return(bcast_addr_len == expected_bcast_addr_len, -EINVAL);
- else {
- bcast_addr = default_bcast_addr;
- bcast_addr_len = expected_bcast_addr_len;
+ assert(ifindex > 0);
+ assert(link);
+ assert(hw_addr);
+
+ switch (arp_type) {
+ case ARPHRD_ETHER:
+ return _bind_raw_socket(ifindex, link, xid,
+ hw_addr,
+ (bcast_addr && !hw_addr_is_null(bcast_addr)) ? bcast_addr : &default_eth_bcast,
+ arp_type, port);
+
+ case ARPHRD_INFINIBAND:
+ return _bind_raw_socket(ifindex, link, xid,
+ &HW_ADDR_NULL,
+ (bcast_addr && !hw_addr_is_null(bcast_addr)) ? bcast_addr : &default_ib_bcast,
+ arp_type, port);
+ default:
+ return -EINVAL;
}
-
- return _bind_raw_socket(ifindex, link, xid, bcast_addr, bcast_addr_len,
- ð_mac, arp_type, dhcp_hlen, port);
}
int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
#define DHCP_IP_SIZE (int32_t)(sizeof(struct iphdr))
#define DHCP_IP_UDP_SIZE (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
-#define DHCP_MESSAGE_SIZE (int32_t)(sizeof(DHCPMessage))
-#define DHCP_DEFAULT_MIN_SIZE 576 /* the minimum internet hosts must be able to receive */
-#define DHCP_MIN_OPTIONS_SIZE (DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE)
+#define DHCP_HEADER_SIZE (int32_t)(sizeof(DHCPMessage))
+#define DHCP_MIN_MESSAGE_SIZE 576 /* the minimum internet hosts must be able to receive, see RFC 2132 Section 9.10 */
+#define DHCP_MIN_OPTIONS_SIZE (DHCP_MIN_MESSAGE_SIZE - DHCP_HEADER_SIZE)
+#define DHCP_MIN_PACKET_SIZE (DHCP_MIN_MESSAGE_SIZE + DHCP_IP_UDP_SIZE)
#define DHCP_MAGIC_COOKIE (uint32_t)(0x63825363)
enum {
char **vendor_class;
OrderedHashmap *extra_options;
OrderedSet *vendor_options;
+ bool rapid_commit;
struct sd_dhcp6_lease *lease;
};
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status);
+
+int dhcp6_message_status_to_errno(DHCP6Status s) {
+ switch (s) {
+ case DHCP6_STATUS_SUCCESS:
+ return 0;
+ case DHCP6_STATUS_NO_BINDING:
+ return -EADDRNOTAVAIL;
+ default:
+ return -EINVAL;
+ }
+}
DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_;
const char *dhcp6_message_status_to_string(DHCP6Status s) _const_;
DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_;
+int dhcp6_message_status_to_errno(DHCP6Status s);
int ifindex,
union sockaddr_union *link,
uint32_t id,
- const uint8_t *addr, size_t addr_len,
- const uint8_t *bcaddr, size_t bcaddr_len,
+ const struct hw_addr_data *hw_addr,
+ const struct hw_addr_data *bcast_addr,
uint16_t arp_type, uint16_t port) {
int fd;
assert_se(IN_SET(client->state, DHCP6_STATE_REQUEST, DHCP6_STATE_BOUND));
break;
case DHCP6_STATE_REQUEST:
- assert_se(client->state == DHCP6_STATE_BOUND);
+ assert_se(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_SOLICITATION));
break;
default:
assert_not_reached();
#include "dhcp-lease-internal.h"
#include "dhcp-protocol.h"
#include "dns-domain.h"
+#include "ether-addr-util.h"
#include "event-util.h"
#include "fd-util.h"
#include "hostname-util.h"
Set *req_opts;
bool anonymize;
be32_t last_addr;
- uint8_t mac_addr[MAX_MAC_ADDR_LEN];
- size_t mac_addr_len;
- uint8_t bcast_addr[MAX_MAC_ADDR_LEN];
- size_t bcast_addr_len;
+ struct hw_addr_data hw_addr;
+ struct hw_addr_data bcast_addr;
uint16_t arp_type;
sd_dhcp_client_id client_id;
size_t client_id_len;
int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
client->request_broadcast = broadcast;
int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
switch (option) {
const struct in_addr *last_addr) {
assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
if (last_addr)
client->last_addr = last_addr->s_addr;
int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(ifindex > 0, -EINVAL);
client->ifindex = ifindex;
int sd_dhcp_client_set_mac(
sd_dhcp_client *client,
- const uint8_t *addr,
+ const uint8_t *hw_addr,
const uint8_t *bcast_addr,
size_t addr_len,
uint16_t arp_type) {
- DHCP_CLIENT_DONT_DESTROY(client);
- bool need_restart = false;
- int r;
-
assert_return(client, -EINVAL);
- assert_return(addr, -EINVAL);
- assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
- assert_return(arp_type > 0, -EINVAL);
-
- if (arp_type == ARPHRD_ETHER)
- assert_return(addr_len == ETH_ALEN, -EINVAL);
- else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
- else
- return -EINVAL;
-
- if (client->mac_addr_len == addr_len &&
- memcmp(&client->mac_addr, addr, addr_len) == 0 &&
- (client->bcast_addr_len > 0) == !!bcast_addr &&
- (!bcast_addr || memcmp(&client->bcast_addr, bcast_addr, addr_len) == 0))
- return 0;
-
- if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting");
- need_restart = true;
- client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
- }
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+ assert_return(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND), -EINVAL);
+ assert_return(hw_addr, -EINVAL);
+ assert_return(addr_len == (arp_type == ARPHRD_ETHER ? ETH_ALEN : INFINIBAND_ALEN), -EINVAL);
- memcpy(&client->mac_addr, addr, addr_len);
- client->mac_addr_len = addr_len;
client->arp_type = arp_type;
- client->bcast_addr_len = 0;
-
- if (bcast_addr) {
- memcpy(&client->bcast_addr, bcast_addr, addr_len);
- client->bcast_addr_len = addr_len;
- }
-
- if (need_restart && client->state != DHCP_STATE_STOPPED) {
- r = sd_dhcp_client_start(client);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
- }
+ hw_addr_set(&client->hw_addr, hw_addr, addr_len);
+ hw_addr_set(&client->bcast_addr, bcast_addr, bcast_addr ? addr_len : 0);
return 0;
}
const uint8_t *data,
size_t data_len) {
- DHCP_CLIENT_DONT_DESTROY(client);
- bool need_restart = false;
- int r;
-
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(data, -EINVAL);
assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
- if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
- client->client_id.type == type &&
- memcmp(&client->client_id.raw.data, data, data_len) == 0)
- return 0;
-
/* For hardware types, log debug message about unexpected data length.
*
* Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only
"unexpected address length %zu",
type, data_len);
- if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Changing client ID on running DHCP "
- "client, restarting");
- need_restart = true;
- client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
- }
-
client->client_id.type = type;
memcpy(&client->client_id.raw.data, data, data_len);
client->client_id_len = data_len + sizeof (client->client_id.type);
- if (need_restart && client->state != DHCP_STATE_STOPPED) {
- r = sd_dhcp_client_start(client);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
- }
-
return 0;
}
size_t duid_len,
usec_t llt_time) {
- DHCP_CLIENT_DONT_DESTROY(client);
- int r;
size_t len;
+ int r;
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(duid_len == 0 || duid, -EINVAL);
if (duid) {
if (iaid_set)
client->client_id.ns.iaid = htobe32(iaid);
else {
- r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
- client->mac_addr_len,
+ r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr,
/* legacy_unstable_byteorder = */ true,
/* use_mac = */ client->test_mode,
&client->client_id.ns.iaid);
len = sizeof(client->client_id.ns.duid.type) + duid_len;
} else {
- r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len,
+ r = dhcp_identifier_set_duid(duid_type, &client->hw_addr,
client->arp_type, llt_time, client->test_mode,
&client->client_id.ns.duid, &len);
if (r == -EOPNOTSUPP)
client->client_id_len = sizeof(client->client_id.type) + len +
(iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
- if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
- client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
- r = sd_dhcp_client_start(client);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
- }
-
return 0;
}
const char *hostname) {
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
/* Make sure hostnames qualify as DNS and as Linux hostnames */
if (hostname &&
const char *vci) {
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
return free_and_strdup(&client->vendor_class_identifier, vci);
}
const char *mudurl) {
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(mudurl, -EINVAL);
assert_return(strlen(mudurl) <= 255, -EINVAL);
assert_return(http_url_is_valid(mudurl), -EINVAL);
char **s = NULL;
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(!strv_isempty(user_class), -EINVAL);
STRV_FOREACH(p, user_class) {
uint16_t port) {
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
client->port = port;
int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
assert_return(client, -EINVAL);
- assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
+ assert_return(mtu >= DHCP_MIN_PACKET_SIZE, -ERANGE);
+
+ /* MTU may be changed by the acquired lease. Hence, we cannot require that the client is stopped here.
+ * Please do not add assertion for !sd_dhcp_client_is_running(client) here. */
client->mtu = mtu;
int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) {
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
client->max_attempts = max_attempts;
int r;
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(v, -EINVAL);
r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp_option_hash_ops, UINT_TO_PTR(v->option), v);
int r;
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(v, -EINVAL);
r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops);
int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
client->ip_service_type = type;
int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(fallback_lease_lifetime > 0, -EINVAL);
client->fallback_lease_lifetime = fallback_lease_lifetime;
_cleanup_free_ DHCPPacket *packet = NULL;
size_t optlen, optoffset, size;
- be16_t max_size;
usec_t time_now;
uint16_t secs;
int r;
return -ENOMEM;
r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
- client->arp_type, client->mac_addr_len, client->mac_addr,
+ client->arp_type, client->hw_addr.length, client->hw_addr.bytes,
optlen, &optoffset);
if (r < 0)
return r;
client->client_id.type = 255;
- r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
+ r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr,
/* legacy_unstable_byteorder = */ true,
/* use_mac = */ client->test_mode,
&client->client_id.ns.iaid);
*/
/* RFC7844 section 3:
SHOULD NOT contain any other option. */
- if (!client->anonymize && type != DHCP_RELEASE) {
- max_size = htobe16(size);
- r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
+ if (!client->anonymize && IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST)) {
+ be16_t max_size = htobe16(MIN(client->mtu - DHCP_IP_UDP_SIZE, (uint32_t) UINT16_MAX));
+ r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
2, &max_size);
if (r < 0)
client->xid = random_u32();
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
- client->mac_addr, client->mac_addr_len,
- client->bcast_addr, client->bcast_addr_len,
+ &client->hw_addr, &client->bcast_addr,
client->arp_type, client->port);
if (r < 0) {
client_stop(client, r);
client->attempt = 0;
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
- client->mac_addr, client->mac_addr_len,
- client->bcast_addr, client->bcast_addr_len,
+ &client->hw_addr, &client->bcast_addr,
client->arp_type, client->port);
if (r < 0) {
client_stop(client, r);
if (client->arp_type == ARPHRD_ETHER) {
expected_hlen = ETH_ALEN;
- expected_chaddr = &client->mac_addr[0];
+ expected_chaddr = client->hw_addr.bytes;
}
if (message->hlen != expected_hlen) {
int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
+ assert_return(sd_dhcp_client_is_running(client), -ESTALE);
assert_return(client->fd >= 0, -EINVAL);
if (!client->lease)
int sd_dhcp_client_send_release(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
- assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
+ assert_return(sd_dhcp_client_is_running(client), -ESTALE);
assert_return(client->lease, -EUNATCH);
_cleanup_free_ DHCPPacket *release = NULL;
/* Fill up release IP and MAC */
release->dhcp.ciaddr = client->lease->address;
- memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
+ memcpy(&release->dhcp.chaddr, client->hw_addr.bytes, client->hw_addr.length);
r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
- assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
+ assert_return(sd_dhcp_client_is_running(client), -ESTALE);
assert_return(client->lease, -EUNATCH);
_cleanup_free_ DHCPPacket *release = NULL;
return r;
release->dhcp.ciaddr = client->lease->address;
- memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
+ memcpy(&release->dhcp.chaddr, client->hw_addr.bytes, client->hw_addr.length);
r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_END, 0, NULL);
assert_return(client, -EINVAL);
assert_return(!client->event, -EBUSY);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
if (event)
client->event = sd_event_ref(event);
int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
client->event = sd_event_unref(client->event);
.state = DHCP_STATE_INIT,
.ifindex = -1,
.fd = -1,
- .mtu = DHCP_DEFAULT_MIN_SIZE,
+ .mtu = DHCP_MIN_PACKET_SIZE,
.port = DHCP_PORT_CLIENT,
.anonymize = !!anonymize,
.max_attempts = UINT64_MAX,
r = lease_parse_u16(option, len, &lease->mtu, 68);
if (r < 0)
log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
- if (lease->mtu < DHCP_DEFAULT_MIN_SIZE) {
- log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_DEFAULT_MIN_SIZE);
- lease->mtu = DHCP_DEFAULT_MIN_SIZE;
+ if (lease->mtu < DHCP_MIN_PACKET_SIZE) {
+ log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_MIN_PACKET_SIZE);
+ lease->mtu = DHCP_MIN_PACKET_SIZE;
}
break;
return 0;
}
- memcpy(client->hw_addr.bytes, addr, addr_len);
- client->hw_addr.length = addr_len;
client->arp_type = arp_type;
+ hw_addr_set(&client->hw_addr, addr, addr_len);
return 0;
}
client->duid_len = sizeof(client->duid.type) + duid_len;
} else {
- r = dhcp_identifier_set_duid(duid_type, client->hw_addr.bytes, client->hw_addr.length,
- client->arp_type, llt_time, client->test_mode, &client->duid, &client->duid_len);
+ r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
+ client->test_mode, &client->duid, &client->duid_len);
if (r == -EOPNOTSUPP)
return log_dhcp6_client_errno(client, r,
"Failed to set %s. MAC address is not set or "
if (client->iaid_set)
return 0;
- r = dhcp_identifier_set_iaid(client->ifindex, client->hw_addr.bytes, client->hw_addr.length,
+ r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr,
/* legacy_unstable_byteorder = */ true,
/* use_mac = */ client->test_mode,
&iaid);
return 0;
}
+int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+ client->rapid_commit = enable;
+ return 0;
+}
+
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
assert_return(client, -EINVAL);
client->callback(client, event, client->userdata);
}
-static void client_stop(sd_dhcp6_client *client, int error) {
- DHCP6_CLIENT_DONT_DESTROY(client);
-
+static void client_cleanup(sd_dhcp6_client *client) {
assert(client);
- client_notify(client, error);
-
client->lease = sd_dhcp6_lease_unref(client->lease);
/* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode,
client_set_state(client, DHCP6_STATE_STOPPED);
}
+static void client_stop(sd_dhcp6_client *client, int error) {
+ DHCP6_CLIENT_DONT_DESTROY(client);
+
+ assert(client);
+
+ client_notify(client, error);
+
+ client_cleanup(client);
+}
+
static int client_append_common_options_in_managed_mode(
sd_dhcp6_client *client,
uint8_t **opt,
req_opts = client->req_opts;
}
+ if (n == 0)
+ return 0;
+
return dhcp6_option_append(opt, optlen, SD_DHCP6_OPTION_ORO, n * sizeof(be16_t), req_opts);
}
break;
case DHCP6_STATE_SOLICITATION:
- r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
- if (r < 0)
- return r;
+ if (client->rapid_commit) {
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+ if (r < 0)
+ return r;
+ }
r = client_append_common_options_in_managed_mode(client, &opt, &optlen,
&client->ia_na, &client->ia_pd);
return log_invalid_message_type(client, message);
r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
+ if (r == -EADDRNOTAVAIL) {
+
+ /* If NoBinding status code is received, we cannot request the address anymore.
+ * Let's restart transaction from the beginning. */
+
+ if (client->state == DHCP6_STATE_REQUEST)
+ /* The lease is not acquired yet, hence it is not necessary to notify the restart. */
+ client_cleanup(client);
+ else
+ /* We need to notify the previous lease was expired. */
+ client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
+
+ return client_start_transaction(client, DHCP6_STATE_SOLICITATION);
+ }
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
if (message->type == DHCP6_MESSAGE_REPLY) {
bool rapid_commit;
+ if (!client->rapid_commit)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+ "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring.");
+
r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
if (r < 0)
return r;
.ifindex = -1,
.request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
.fd = -1,
+ .rapid_commit = true,
};
*ret = TAKE_PTR(client);
return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
if (r > 0)
- return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+ return log_dhcp6_client_errno(client, dhcp6_message_status_to_errno(r),
"Received %s message with non-zero status: %s%s%s",
dhcp6_message_type_to_string(message->type),
strempty(msg), isempty(msg) ? "" : ": ",
return sd_ipv4acd_is_running(ll->acd);
}
-static bool ipv4ll_address_is_valid(const struct in_addr *address) {
- assert(address);
-
- if (!in4_addr_is_link_local(address))
- return false;
-
- return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
-}
-
int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
int r;
assert_return(ll, -EINVAL);
assert_return(address, -EINVAL);
- assert_return(ipv4ll_address_is_valid(address), -EINVAL);
+ assert_return(in4_addr_is_link_local_dynamic(address), -EINVAL);
r = sd_ipv4acd_set_address(ll->acd, address);
if (r < 0)
#include "sd-netlink.h"
#include "in-addr-util.h"
-#include "netlink-util.h"
#include "tests.h"
#include "util.h"
#include "dhcp-identifier.h"
#include "dhcp-internal.h"
#include "dhcp-protocol.h"
+#include "ether-addr-util.h"
#include "fd-util.h"
#include "random-util.h"
#include "tests.h"
#include "util.h"
-static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'};
-static uint8_t bcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-
+static struct hw_addr_data hw_addr = {
+ .length = ETH_ALEN,
+ .ether = {{ 'A', 'B', 'C', '1', '2', '3' }},
+}, bcast_addr = {
+ .length = ETH_ALEN,
+ .ether = {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }},
+};
typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp);
static bool verbose = true;
uint32_t iaid_legacy;
be32_t iaid;
- assert_se(dhcp_identifier_set_iaid(42, mac_addr, sizeof(mac_addr), /* legacy = */ true,
+ assert_se(dhcp_identifier_set_iaid(42, &hw_addr, /* legacy = */ true,
/* use_mac = */ true, &iaid_legacy) >= 0);
- assert_se(dhcp_identifier_set_iaid(42, mac_addr, sizeof(mac_addr), /* legacy = */ false,
+ assert_se(dhcp_identifier_set_iaid(42, &hw_addr, /* legacy = */ false,
/* use_mac = */ true, &iaid) >= 0);
/* we expect, that the MAC address was hashed. The legacy value is in native
size_t duid_len;
assert_se(dhcp_identifier_set_duid_en(/* test_mode = */ true, &duid, &duid_len) >= 0);
- assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, true, /* use_mac = */ true, &iaid) >= 0);
+ assert_se(dhcp_identifier_set_iaid(42, &hw_addr, /* legacy = */ true, /* use_mac = */ true, &iaid) >= 0);
assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
assert_se(len == 19);
assert_se(ip_check == 0xffff);
assert_se(discover->dhcp.xid);
- assert_se(memcmp(discover->dhcp.chaddr, &mac_addr, ETH_ALEN) == 0);
+ assert_se(memcmp(discover->dhcp.chaddr, hw_addr.bytes, hw_addr.length) == 0);
size = len - sizeof(struct iphdr) - sizeof(struct udphdr);
int ifindex,
union sockaddr_union *link,
uint32_t id,
- const uint8_t *addr, size_t addr_len,
- const uint8_t *bcaddr, size_t bcaddr_len,
+ const struct hw_addr_data *_hw_addr,
+ const struct hw_addr_data *_bcast_addr,
uint16_t arp_type, uint16_t port) {
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
assert_se(r >= 0);
assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
- assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0);
+ assert_se(sd_dhcp_client_set_mac(client, hw_addr.bytes, bcast_addr.bytes, hw_addr.length, ARPHRD_ETHER) >= 0);
dhcp_client_set_test_mode(client, true);
assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0);
memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check));
memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid));
- memcpy(&test_addr_acq_ack[56], &mac_addr, ETHER_ADDR_LEN);
+ memcpy(&test_addr_acq_ack[56], hw_addr.bytes, hw_addr.length);
callback_recv = NULL;
memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check));
memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid));
- memcpy(&test_addr_acq_offer[56], &mac_addr, ETHER_ADDR_LEN);
+ memcpy(&test_addr_acq_offer[56], hw_addr.bytes, hw_addr.length);
callback_recv = test_addr_acq_recv_request;
assert_se(r >= 0);
assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
- assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0);
+ assert_se(sd_dhcp_client_set_mac(client, hw_addr.bytes, bcast_addr.bytes, hw_addr.length, ARPHRD_ETHER) >= 0);
dhcp_client_set_test_mode(client, true);
assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e) >= 0);
#include "alloc-util.h"
#include "in-addr-util.h"
-#include "netlink-util.h"
#include "parse-util.h"
#include "string-util.h"
#include "tests.h"
}
}
-static int client_run(int ifindex, const char *seed_str, const struct ether_addr *ha, sd_event *e) {
+static int client_run(int ifindex, const char *seed_str, const struct in_addr *start_address, const struct ether_addr *ha, sd_event *e) {
sd_ipv4ll *ll;
assert_se(sd_ipv4ll_new(&ll) >= 0);
assert_se(sd_ipv4ll_set_address_seed(ll, seed) >= 0);
}
+ if (start_address && in4_addr_is_set(start_address))
+ assert_se(sd_ipv4ll_set_address(ll, start_address) >= 0);
+
log_info("starting IPv4LL client");
assert_se(sd_ipv4ll_start(ll) >= 0);
return EXIT_SUCCESS;
}
-static int test_ll(const char *ifname, const char *seed) {
+static int test_ll(const char *ifname, const char *seed, const struct in_addr *start_address) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0);
assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0);
- client_run(ifindex, seed, &ha, e);
+ client_run(ifindex, seed, start_address, &ha, e);
return EXIT_SUCCESS;
}
test_setup_logging(LOG_DEBUG);
if (argc == 2)
- return test_ll(argv[1], NULL);
- else if (argc == 3)
- return test_ll(argv[1], argv[2]);
- else {
+ return test_ll(argv[1], NULL, NULL);
+ else if (argc == 3) {
+ int r;
+ union in_addr_union a;
+
+ r = in_addr_from_string(AF_INET, argv[2], &a);
+ if (r < 0)
+ return test_ll(argv[1], argv[2], NULL);
+ else
+ return test_ll(argv[1], NULL, &a.in);
+ } else {
log_error("This program takes one or two arguments.\n"
- "\t %s <ifname> [<seed>]", program_invocation_short_name);
+ "\t %s <ifname> [<seed>|<start_address>]", program_invocation_short_name);
return EXIT_FAILURE;
}
}
assert_se(sd_ipv4ll_unref(ll) == NULL);
}
-static void test_basic_request(sd_event *e) {
+static void test_basic_request(sd_event *e, const struct in_addr *start_address) {
sd_ipv4ll *ll;
struct ether_arp arp;
printf("* %s\n", __func__);
assert_se(sd_ipv4ll_new(&ll) == 0);
+ if (in4_addr_is_set(start_address))
+ assert_se(sd_ipv4ll_set_address(ll, start_address) >= 0);
assert_se(sd_ipv4ll_start(ll) == -EINVAL);
assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0);
sd_event_run(e, UINT64_MAX);
assert_se(basic_request_handler_bind == 1);
+
+ if (in4_addr_is_set(start_address)) {
+ struct in_addr address;
+
+ assert_se(sd_ipv4ll_get_address(ll, &address) >= 0);
+ assert_se(start_address->s_addr == address.s_addr);
+ }
}
sd_ipv4ll_stop(ll);
}
int main(int argc, char *argv[]) {
+ struct in_addr start_address = {};
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
test_setup_logging(LOG_DEBUG);
assert_se(sd_event_new(&e) >= 0);
test_public_api_setters(e);
- test_basic_request(e);
+ test_basic_request(e, &start_address);
+
+ basic_request_handler_bind = 0;
+ basic_request_handler_stop = 0;
+ start_address.s_addr = htobe32(169U << 24 | 254U << 16 | 1U << 8 | 2U);
+ test_basic_request(e, &start_address);
return 0;
}
sd_bus_message_read_strv_extend;
sd_bus_error_setfv;
+ sd_device_new_child;
+ sd_device_monitor_set_description;
+ sd_device_monitor_get_description;
+
sd_id128_string_equal;
sd_hwdb_new_from_path;
sd_netlink_message_append_s32;
sd_netlink_message_append_s64;
sd_netlink_message_append_data;
+ sd_netlink_message_append_container_data;
sd_netlink_message_append_in_addr;
sd_netlink_message_append_in6_addr;
sd_netlink_message_append_sockaddr_in;
const char *prefix,
const char *path,
bool require_fallback,
+ bool *found_object_manager,
sd_bus_error *error) {
const char *previous_interface = NULL;
assert(reply);
assert(prefix);
assert(path);
+ assert(found_object_manager);
assert(error);
n = hashmap_get(bus->nodes, prefix);
if (!n)
return 0;
+ if (!require_fallback && n->object_managers)
+ *found_object_manager = true;
+
LIST_FOREACH(vtables, i, n->vtables) {
void *u;
if (r < 0)
return r;
- r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
- if (r < 0)
- return r;
+ if (*found_object_manager) {
+ r = sd_bus_message_append(
+ reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
+ if (r < 0)
+ return r;
+ }
found_something = true;
}
_cleanup_free_ char *prefix = NULL;
size_t pl;
int r;
+ bool found_object_manager = false;
assert(bus);
assert(reply);
assert(error);
/* First, add all vtables registered for this path */
- r = object_manager_serialize_path(bus, reply, path, path, false, error);
+ r = object_manager_serialize_path(bus, reply, path, path, false, &found_object_manager, error);
if (r < 0)
return r;
if (bus->nodes_modified)
return -ENOMEM;
OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
- r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
+ r = object_manager_serialize_path(bus, reply, prefix, path, true, &found_object_manager, error);
if (r < 0)
return r;
if (bus->nodes_modified)
free(n);
}
-static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
+static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path, bool* path_has_object_manager) {
struct node *n;
assert(bus);
assert(path);
+ assert(path_has_object_manager);
n = hashmap_get(bus->nodes, path);
+
+ if (n)
+ *path_has_object_manager = n->object_managers;
+
if (!n) {
_cleanup_free_ char *prefix = NULL;
size_t pl;
return 0;
}
-static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
+static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path, bool path_has_object_manager) {
_cleanup_ordered_set_free_ OrderedSet *s = NULL;
_cleanup_free_ char *prefix = NULL;
size_t pl;
r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
if (r < 0)
return r;
- r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
- if (r < 0)
- return r;
+ if (path_has_object_manager){
+ r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
+ if (r < 0)
+ return r;
+ }
r = object_added_append_all_prefix(bus, m, s, path, path, false);
if (r < 0)
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
- r = bus_find_parent_object_manager(bus, &object_manager, path);
+ bool path_has_object_manager = false;
+ r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = object_added_append_all(bus, m, path);
+ r = object_added_append_all(bus, m, path, path_has_object_manager);
if (r < 0)
return r;
return 0;
}
-static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
+static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path, bool path_has_object_manager) {
_cleanup_ordered_set_free_ OrderedSet *s = NULL;
_cleanup_free_ char *prefix = NULL;
size_t pl;
r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
if (r < 0)
return r;
- r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
- if (r < 0)
- return r;
+
+ if (path_has_object_manager){
+ r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
+ if (r < 0)
+ return r;
+ }
r = object_removed_append_all_prefix(bus, m, s, path, path, false);
if (r < 0)
if (!BUS_IS_OPEN(bus->state))
return -ENOTCONN;
- r = bus_find_parent_object_manager(bus, &object_manager, path);
+ bool path_has_object_manager = false;
+ r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = object_removed_append_all(bus, m, path);
+ r = object_removed_append_all(bus, m, path, path_has_object_manager);
if (r < 0)
return r;
if (strv_isempty(interfaces))
return 0;
- r = bus_find_parent_object_manager(bus, &object_manager, path);
+ bool path_has_object_manager = false;
+ r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
if (r < 0)
return r;
if (r == 0)
if (strv_isempty(interfaces))
return 0;
- r = bus_find_parent_object_manager(bus, &object_manager, path);
+ bool path_has_object_manager = false;
+ r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager);
if (r < 0)
return r;
if (r == 0)
}
static int bus_socket_auth_verify_client(sd_bus *b) {
- char *d, *e, *f, *start;
+ char *l, *lines[4] = {};
sd_id128_t peer;
+ size_t i, n;
int r;
assert(b);
/*
- * We expect three response lines:
- * "DATA\r\n"
+ * We expect up to three response lines:
+ * "DATA\r\n" (optional)
* "OK <server-id>\r\n"
* "AGREE_UNIX_FD\r\n" (optional)
*/
- d = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2);
- if (!d)
- return 0;
-
- e = memmem_safe(d + 2, b->rbuffer_size - (d - (char*) b->rbuffer) - 2, "\r\n", 2);
- if (!e)
- return 0;
-
- if (b->accept_fd) {
- f = memmem_safe(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2);
- if (!f)
- return 0;
-
- start = f + 2;
- } else {
- f = NULL;
- start = e + 2;
+ n = 0;
+ lines[n] = b->rbuffer;
+ for (i = 0; i < 3; ++i) {
+ l = memmem_safe(lines[n], b->rbuffer_size - (lines[n] - (char*) b->rbuffer), "\r\n", 2);
+ if (l)
+ lines[++n] = l + 2;
+ else
+ break;
}
- /* Nice! We got all the lines we need. First check the DATA line. */
-
- if (d - (char*) b->rbuffer == 4) {
- if (memcmp(b->rbuffer, "DATA", 4))
- return -EPERM;
- } else if (d - (char*) b->rbuffer == 3 + 32) {
- /*
- * Old versions of the server-side implementation of `sd-bus` replied with "OK <id>" to
- * "AUTH" requests from a client, even if the "AUTH" line did not contain inlined
- * arguments. Therefore, we also accept "OK <id>" here, even though it is technically the
- * wrong reply. We ignore the "<id>" parameter, though, since it has no real value.
- */
- if (memcmp(b->rbuffer, "OK ", 3))
+ /*
+ * If we sent a non-empty initial response, then we just expect an OK
+ * reply. We currently do this if, and only if, we picked ANONYMOUS.
+ * If we did not send an initial response, then we expect a DATA
+ * challenge, reply with our own DATA, and expect an OK reply. We do
+ * this for EXTERNAL.
+ * If FD negotiation was requested, we additionally expect
+ * an AGREE_UNIX_FD response in all cases.
+ */
+ if (n < (b->anonymous_auth ? 1U : 2U) + !!b->accept_fd)
+ return 0; /* wait for more data */
+
+ i = 0;
+
+ /* In case of EXTERNAL, verify the first response was DATA. */
+ if (!b->anonymous_auth) {
+ l = lines[i++];
+ if (lines[i] - l == 4 + 2) {
+ if (memcmp(l, "DATA", 4))
+ return -EPERM;
+ } else if (lines[i] - l == 3 + 32 + 2) {
+ /*
+ * Old versions of the server-side implementation of
+ * `sd-bus` replied with "OK <id>" to "AUTH" requests
+ * from a client, even if the "AUTH" line did not
+ * contain inlined arguments. Therefore, we also accept
+ * "OK <id>" here, even though it is technically the
+ * wrong reply. We ignore the "<id>" parameter, though,
+ * since it has no real value.
+ */
+ if (memcmp(l, "OK ", 3))
+ return -EPERM;
+ } else
return -EPERM;
- } else
- return -EPERM;
+ }
/* Now check the OK line. */
+ l = lines[i++];
- if (e - d != 2 + 3 + 32)
+ if (lines[i] - l != 3 + 32 + 2)
return -EPERM;
-
- if (memcmp(d + 2, "OK ", 3))
+ if (memcmp(l, "OK ", 3))
return -EPERM;
b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL;
- for (unsigned i = 0; i < 32; i += 2) {
+ for (unsigned j = 0; j < 32; j += 2) {
int x, y;
- x = unhexchar(d[2 + 3 + i]);
- y = unhexchar(d[2 + 3 + i + 1]);
+ x = unhexchar(l[3 + j]);
+ y = unhexchar(l[3 + j + 1]);
if (x < 0 || y < 0)
return -EINVAL;
- peer.bytes[i/2] = ((uint8_t) x << 4 | (uint8_t) y);
+ peer.bytes[j/2] = ((uint8_t) x << 4 | (uint8_t) y);
}
if (!sd_id128_is_null(b->server_id) &&
b->server_id = peer;
/* And possibly check the third line, too */
+ if (b->accept_fd) {
+ l = lines[i++];
+ b->can_fds = !!memory_startswith(l, lines[i] - l, "AGREE_UNIX_FD");
+ }
- if (f)
- b->can_fds =
- (f - e == STRLEN("\r\nAGREE_UNIX_FD")) &&
- memcmp(e + 2, "AGREE_UNIX_FD",
- STRLEN("AGREE_UNIX_FD")) == 0;
+ assert(i == n);
- b->rbuffer_size -= (start - (char*) b->rbuffer);
- memmove(b->rbuffer, start, b->rbuffer_size);
+ b->rbuffer_size -= (lines[i] - (char*) b->rbuffer);
+ memmove(b->rbuffer, lines[i], b->rbuffer_size);
r = bus_start_running(b);
if (r < 0)
* message broker to aid debugging of clients. We fully anonymize the connection and use a
* static default.
*/
- "\0AUTH ANONYMOUS\r\n"
- /* HEX a n o n y m o u s */
- "DATA 616e6f6e796d6f7573\r\n"
+ /* HEX a n o n y m o u s */
+ "\0AUTH ANONYMOUS 616e6f6e796d6f7573\r\n"
};
static const char sasl_auth_external[] = {
"\0AUTH EXTERNAL\r\n"
return 1;
}
+static int emit_object_with_manager_added(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a") >= 0);
+
+ return ASSERT_SE_NONNEG(sd_bus_reply_method_return(m, NULL));
+}
+
static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
int r;
SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0),
SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0),
SD_BUS_METHOD("EmitObjectAdded", NULL, NULL, emit_object_added, 0),
+ SD_BUS_METHOD("EmitObjectWithManagerAdded", NULL, NULL, emit_object_with_manager_added, 0),
SD_BUS_METHOD("EmitObjectRemoved", NULL, NULL, emit_object_removed, 0),
SD_BUS_VTABLE_END
};
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
+ /* Check that /value/b does not have ObjectManager interface but /value/a does */
+ assert_se(sd_bus_message_rewind(reply, 1) > 0);
+ assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{oa{sa{sv}}}") > 0);
+ while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "oa{sa{sv}}")) > 0) {
+ const char *path = NULL;
+ assert_se(sd_bus_message_read_basic(reply, 'o', &path) > 0);
+ if (STR_IN_SET(path, "/value/b", "/value/a")) {
+ /* Check that there is no object manager interface here */
+ bool found_object_manager_interface = false;
+ assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sa{sv}}") > 0);
+ while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}")) > 0) {
+ const char *interface_name = NULL;
+ assert_se(sd_bus_message_read_basic(reply, 's', &interface_name) > 0);
+
+ if (streq(interface_name, "org.freedesktop.DBus.ObjectManager")) {
+ assert_se(!streq(path, "/value/b"));
+ found_object_manager_interface = true;
+ }
+
+ assert_se(sd_bus_message_skip(reply, "a{sv}") >= 0);
+ assert_se(sd_bus_message_exit_container(reply) >= 0);
+ }
+ assert_se(sd_bus_message_exit_container(reply) >= 0);
+
+ if (streq(path, "/value/a")) {
+ /* ObjectManager must be here */
+ assert_se(found_object_manager_interface);
+ }
+
+ } else
+ assert_se(sd_bus_message_skip(reply, "a{sa{sv}}") >= 0);
+
+ assert_se(sd_bus_message_exit_container(reply) >= 0);
+ }
+
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, NULL);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
+ /* Check if /value/a/x does not have org.freedesktop.DBus.ObjectManager */
+ assert_se(sd_bus_message_rewind(reply, 1) >= 0);
+ const char* should_be_value_a_x = NULL;
+ assert_se(sd_bus_message_read_basic(reply, 'o', &should_be_value_a_x) > 0);
+ assert_se(streq(should_be_value_a_x, "/value/a/x"));
+ assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sa{sv}}") > 0);
+ while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}")) > 0) {
+ const char* interface_name = NULL;
+ assert_se(sd_bus_message_read_basic(reply, 's', &interface_name) > 0);
+
+ assert(!streq(interface_name, "org.freedesktop.DBus.ObjectManager"));
+
+ assert_se(sd_bus_message_skip(reply, "a{sv}") >= 0);
+
+ assert_se(sd_bus_message_exit_container(reply) >= 0);
+ }
+
+ reply = sd_bus_message_unref(reply);
+
+ assert_se(sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectWithManagerAdded", &error, NULL, NULL) >= 0);
+
+ assert_se(sd_bus_process(bus, &reply) > 0);
+
+ assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
+ sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
+
+ /* Check if /value/a has org.freedesktop.DBus.ObjectManager */
+ assert_se(sd_bus_message_rewind(reply, 1) >= 0);
+ const char* should_be_value_a = NULL;
+ bool found_object_manager = false;
+ assert_se(sd_bus_message_read_basic(reply, 'o', &should_be_value_a) > 0);
+ assert_se(streq(should_be_value_a, "/value/a"));
+ assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sa{sv}}") > 0);
+ while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}")) > 0) {
+ const char* interface_name = NULL;
+ assert_se(sd_bus_message_read_basic(reply, 's', &interface_name));
+
+ if (streq(interface_name, "org.freedesktop.DBus.ObjectManager")) {
+ found_object_manager = true;
+ break;
+ }
+
+ assert_se(sd_bus_message_skip(reply, "a{sv}") >= 0);
+
+ assert_se(sd_bus_message_exit_container(reply) >= 0);
+ }
+ assert_se(found_object_manager);
+
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &error, NULL, NULL);
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
+ /* Check if /value/a/x does not have org.freedesktop.DBus.ObjectManager */
+ assert_se(sd_bus_message_rewind(reply, 1) >= 0);
+ should_be_value_a_x = NULL;
+ assert_se(sd_bus_message_read_basic(reply, 'o', &should_be_value_a_x) > 0);
+ assert_se(streq(should_be_value_a_x, "/value/a/x"));
+ assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s") > 0);
+ const char* deleted_interface_name = NULL;
+ while (ASSERT_SE_NONNEG(sd_bus_message_read_basic(reply, 's', &deleted_interface_name)) > 0) {
+ assert(!streq(deleted_interface_name, "org.freedesktop.DBus.ObjectManager"));
+ }
+ assert_se(sd_bus_message_exit_container(reply) >= 0);
+
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, NULL);
else
hashmap = &enumerator->nomatch_sysattr;
- /* Do not use string_has_ops_free_free or hashmap_put_strdup() here, as this may be called
- * multiple times with the same sysattr but different value. */
- r = hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value);
+ r = update_match_strv(hashmap, sysattr, value, /* clear_on_null = */ true);
if (r <= 0)
return r;
assert_return(enumerator, -EINVAL);
assert_return(property, -EINVAL);
- /* Do not use string_has_ops_free_free or hashmap_put_strdup() here, as this may be called
- * multiple times with the same property but different value. */
- r = hashmap_put_strdup_full(&enumerator->match_property, &trivial_hash_ops_free_free, property, value);
+ r = update_match_strv(&enumerator->match_property, property, value, /* clear_on_null = */ false);
if (r <= 0)
return r;
}
static bool match_property(sd_device_enumerator *enumerator, sd_device *device) {
- const char *property;
- const char *value;
+ const char *property_pattern;
+ char * const *value_patterns;
assert(enumerator);
assert(device);
+ /* Unlike device_match_sysattr(), this accepts device that has at least one matching property. */
+
if (hashmap_isempty(enumerator->match_property))
return true;
- HASHMAP_FOREACH_KEY(value, property, enumerator->match_property) {
- const char *property_dev, *value_dev;
+ HASHMAP_FOREACH_KEY(value_patterns, property_pattern, enumerator->match_property) {
+ const char *property, *value;
- FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) {
- if (fnmatch(property, property_dev, 0) != 0)
+ FOREACH_DEVICE_PROPERTY(device, property, value) {
+ if (fnmatch(property_pattern, property, 0) != 0)
continue;
- if (!value && !value_dev)
- return true;
-
- if (!value || !value_dev)
- continue;
-
- if (fnmatch(value, value_dev, 0) == 0)
+ if (strv_fnmatch(value_patterns, value))
return true;
}
}
if (!dir)
return -errno;
- log_debug("sd-device-enumerator: Scanning %s", path);
-
FOREACH_DIRENT_ALL(de, dir, return -errno) {
int k;
static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {
int k, r = 0;
- log_debug("sd-device-enumerator: Scan all dirs");
-
k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
if (k < 0)
r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
#include "string-util.h"
#include "strv.h"
+#define log_monitor(m, format, ...) \
+ log_debug("sd-device-monitor(%s): " format, strna(m ? m->description : NULL), ##__VA_ARGS__)
+#define log_monitor_errno(m, r, format, ...) \
+ log_debug_errno(r, "sd-device-monitor(%s): " format, strna(m ? m->description : NULL), ##__VA_ARGS__)
+#define log_device_monitor(d, m, format, ...) \
+ log_device_debug(d, "sd-device-monitor(%s): " format, strna(m ? m->description : NULL), ##__VA_ARGS__)
+#define log_device_monitor_errno(d, m, r, format, ...) \
+ log_device_debug_errno(d, r, "sd-device-monitor(%s): " format, strna(m ? m->description : NULL), ##__VA_ARGS__)
+
struct sd_device_monitor {
unsigned n_ref;
sd_event *event;
sd_event_source *event_source;
+ char *description;
sd_device_monitor_handler_t callback;
void *userdata;
};
* will not receive any messages.
*/
- log_debug("sd-device-monitor: The udev service seems not to be active, disabling the monitor");
+ log_monitor(m, "The udev service seems not to be active, disabling the monitor.");
group = MONITOR_GROUP_NONE;
}
if (fd < 0) {
sock = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
if (sock < 0)
- return log_debug_errno(errno, "sd-device-monitor: Failed to create socket: %m");
+ return log_monitor_errno(m, errno, "Failed to create socket: %m");
}
m = new(sd_device_monitor, 1);
if (fd >= 0) {
r = monitor_set_nl_address(m);
if (r < 0) {
- log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m");
+ log_monitor_errno(m, r, "Failed to set netlink address: %m");
goto fail;
}
}
netns = ioctl(m->sock, SIOCGSKNS);
if (netns < 0)
- log_debug_errno(errno, "sd-device-monitor: Unable to get network namespace of udev netlink socket, unable to determine if we are in host netns, ignoring: %m");
+ log_monitor_errno(m, errno, "Unable to get network namespace of udev netlink socket, unable to determine if we are in host netns, ignoring: %m");
else {
struct stat a, b;
if (fstat(netns, &a) < 0) {
- r = log_debug_errno(errno, "sd-device-monitor: Failed to stat netns of udev netlink socket: %m");
+ r = log_monitor_errno(m, errno, "Failed to stat netns of udev netlink socket: %m");
goto fail;
}
if (ERRNO_IS_PRIVILEGE(errno))
/* If we can't access PID1's netns info due to permissions, it's fine, this is a
* safety check only after all. */
- log_debug_errno(errno, "sd-device-monitor: No permission to stat PID1's netns, unable to determine if we are in host netns, ignoring: %m");
+ log_monitor_errno(m, errno, "No permission to stat PID1's netns, unable to determine if we are in host netns, ignoring: %m");
else
- log_debug_errno(errno, "sd-device-monitor: Failed to stat PID1's netns, ignoring: %m");
+ log_monitor_errno(m, errno, "Failed to stat PID1's netns, ignoring: %m");
} else if (!stat_inode_same(&a, &b))
- log_debug("sd-device-monitor: Netlink socket we listen on is not from host netns, we won't see device events.");
+ log_monitor(m, "Netlink socket we listen on is not from host netns, we won't see device events.");
}
}
if (r < 0)
return r;
- (void) sd_event_source_set_description(m->event_source, "sd-device-monitor");
+ (void) sd_event_source_set_description(m->event_source, m->description ?: "sd-device-monitor");
return 0;
}
return m->event_source;
}
+_public_ int sd_device_monitor_set_description(sd_device_monitor *m, const char *description) {
+ int r;
+
+ assert_return(m, -EINVAL);
+
+ r = free_and_strdup(&m->description, description);
+ if (r <= 0)
+ return r;
+
+ if (m->event_source)
+ (void) sd_event_source_set_description(m->event_source, description);
+
+ return r;
+}
+
+_public_ int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret) {
+ assert_return(m, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ *ret = m->description;
+ return 0;
+}
+
int device_monitor_enable_receiving(sd_device_monitor *m) {
int r;
r = sd_device_monitor_filter_update(m);
if (r < 0)
- return log_debug_errno(r, "sd-device-monitor: Failed to update filter: %m");
+ return log_monitor_errno(m, r, "Failed to update filter: %m");
if (!m->bound) {
/* enable receiving of sender credentials */
r = setsockopt_int(m->sock, SOL_SOCKET, SO_PASSCRED, true);
if (r < 0)
- return log_debug_errno(r, "sd-device-monitor: Failed to set socket option SO_PASSCRED: %m");
+ return log_monitor_errno(m, r, "Failed to set socket option SO_PASSCRED: %m");
if (bind(m->sock, &m->snl.sa, sizeof(struct sockaddr_nl)) < 0)
- return log_debug_errno(errno, "sd-device-monitor: Failed to bind monitoring socket: %m");
+ return log_monitor_errno(m, errno, "Failed to bind monitoring socket: %m");
m->bound = true;
r = monitor_set_nl_address(m);
if (r < 0)
- return log_debug_errno(r, "sd-device-monitor: Failed to set address: %m");
+ return log_monitor_errno(m, r, "Failed to set address: %m");
}
return 0;
(void) sd_device_monitor_detach_event(m);
+ free(m->description);
hashmap_free(m->subsystem_filter);
set_free(m->tag_filter);
hashmap_free(m->match_sysattr_filter);
buflen = recvmsg(m->sock, &smsg, 0);
if (buflen < 0) {
- if (ERRNO_IS_TRANSIENT(errno))
- log_debug_errno(errno, "sd-device-monitor: Failed to receive message: %m");
+ if (!ERRNO_IS_TRANSIENT(errno))
+ log_monitor_errno(m, errno, "Failed to receive message: %m");
return -errno;
}
if (buflen < 32 || (smsg.msg_flags & MSG_TRUNC))
- return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
- "sd-device-monitor: Invalid message length.");
+ return log_monitor_errno(m, SYNTHETIC_ERRNO(EINVAL), "Invalid message length.");
if (snl.nl.nl_groups == MONITOR_GROUP_NONE) {
/* unicast message, check if we trust the sender */
if (m->snl_trusted_sender.nl.nl_pid == 0 ||
snl.nl.nl_pid != m->snl_trusted_sender.nl.nl_pid)
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "sd-device-monitor: Unicast netlink message ignored.");
+ return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+ "Unicast netlink message ignored.");
} else if (snl.nl.nl_groups == MONITOR_GROUP_KERNEL) {
if (snl.nl.nl_pid > 0)
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "sd-device-monitor: Multicast kernel netlink message from PID %"PRIu32" ignored.", snl.nl.nl_pid);
+ return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+ "Multicast kernel netlink message from PID %"PRIu32" ignored.",
+ snl.nl.nl_pid);
}
cmsg = CMSG_FIRSTHDR(&smsg);
if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS)
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "sd-device-monitor: No sender credentials received, message ignored.");
+ return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+ "No sender credentials received, ignoring message.");
cred = (struct ucred*) CMSG_DATA(cmsg);
if (cred->uid != 0)
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "sd-device-monitor: Sender uid="UID_FMT", message ignored.", cred->uid);
+ return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+ "Sender uid="UID_FMT", message ignored.", cred->uid);
if (streq(buf.raw, "libudev")) {
/* udev message needs proper version magic */
if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC))
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "sd-device-monitor: Invalid message signature (%x != %x)",
- buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC));
+ return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+ "Invalid message signature (%x != %x).",
+ buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC));
if (buf.nlh.properties_off+32 > (size_t) buflen)
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "sd-device-monitor: Invalid message length (%u > %zd)",
- buf.nlh.properties_off+32, buflen);
+ return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+ "Invalid message length (%u > %zd).",
+ buf.nlh.properties_off+32, buflen);
bufpos = buf.nlh.properties_off;
/* kernel message with header */
bufpos = strlen(buf.raw) + 1;
if ((size_t) bufpos < sizeof("a@/d") || bufpos >= buflen)
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "sd-device-monitor: Invalid message length");
+ return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+ "Invalid message length.");
/* check message header */
if (!strstr(buf.raw, "@/"))
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "sd-device-monitor: Invalid message header");
+ return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
+ "Invalid message header.");
}
r = device_new_from_nulstr(&device, &buf.raw[bufpos], buflen - bufpos);
if (r < 0)
- return log_debug_errno(r, "sd-device-monitor: Failed to create device from received message: %m");
+ return log_monitor_errno(m, r, "Failed to create device from received message: %m");
if (is_initialized)
device_set_is_initialized(device);
/* Skip device, if it does not pass the current filter */
r = passes_filter(m, device);
if (r < 0)
- return log_device_debug_errno(device, r, "sd-device-monitor: Failed to check received device passing filter: %m");
+ return log_device_monitor_errno(device, m, r, "Failed to check received device passing filter: %m");
if (r == 0)
- log_device_debug(device, "sd-device-monitor: Received device does not pass filter, ignoring");
+ log_device_monitor(device, m, "Received device does not pass filter, ignoring.");
else
*ret = TAKE_PTR(device);
r = device_get_properties_nulstr(device, &buf, &blen);
if (r < 0)
- return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device properties: %m");
+ return log_device_monitor_errno(device, m, r, "Failed to get device properties: %m");
if (blen < 32)
- log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
- "sd-device-monitor: Length of device property nulstr is too small to contain valid device information");
+ return log_device_monitor_errno(device, m, SYNTHETIC_ERRNO(EINVAL),
+ "Length of device property nulstr is too small to contain valid device information.");
/* fill in versioned header */
r = sd_device_get_subsystem(device, &val);
if (r < 0)
- return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device subsystem: %m");
+ return log_device_monitor_errno(device, m, r, "Failed to get device subsystem: %m");
nlh.filter_subsystem_hash = htobe32(string_hash32(val));
if (sd_device_get_devtype(device, &val) >= 0)
count = sendmsg(m->sock, &smsg, 0);
if (count < 0) {
if (!destination && errno == ECONNREFUSED) {
- log_device_debug(device, "sd-device-monitor: Passed to netlink monitor");
+ log_device_monitor(device, m, "Passed to netlink monitor.");
return 0;
} else
- return log_device_debug_errno(device, errno, "sd-device-monitor: Failed to send device to netlink monitor: %m");
+ return log_device_monitor_errno(device, m, errno, "Failed to send device to netlink monitor: %m");
}
- log_device_debug(device, "sd-device-monitor: Passed %zi byte to netlink monitor", count);
+ log_device_monitor(device, m, "Passed %zi byte to netlink monitor.", count);
return count;
}
hashmap = &m->nomatch_sysattr_filter;
/* TODO: unset m->filter_uptodate on success when we support this filter on BPF. */
- return hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value);
+ return update_match_strv(hashmap, sysattr, value, /* clear_on_null = */ true);
}
_public_ int sd_device_monitor_filter_add_match_parent(sd_device_monitor *m, sd_device *device, int match) {
}
int device_get_property_bool(sd_device *device, const char *key);
+int device_get_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_device_id(sd_device *device, const char **ret);
int device_get_devlink_priority(sd_device *device, int *ret);
int device_get_watch_handle(sd_device *device);
int device_get_devnode_uid(sd_device *device, uid_t *ret);
int device_get_devnode_gid(sd_device *device, gid_t *ret);
+void device_clear_sysattr_cache(sd_device *device);
int device_cache_sysattr_value(sd_device *device, const char *key, char *value);
int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
#include "device-util.h"
#include "path-util.h"
-static bool device_match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) {
+int update_match_strv(Hashmap **match_strv, const char *key, const char *value, bool clear_on_null) {
+ char **strv;
+ int r;
+
+ assert(match_strv);
+ assert(key);
+
+ strv = hashmap_get(*match_strv, key);
+ if (strv) {
+ if (!value) {
+ char **v;
+
+ if (strv_isempty(strv) || !clear_on_null)
+ return 0;
+
+ /* Accept all value. Clear previous assignment. */
+
+ v = new0(char*, 1);
+ if (!v)
+ return -ENOMEM;
+
+ strv_free_and_replace(strv, v);
+ } else {
+ if (strv_contains(strv, value))
+ return 0;
+
+ r = strv_extend(&strv, value);
+ if (r < 0)
+ return r;
+ }
+
+ r = hashmap_update(*match_strv, key, strv);
+ if (r < 0)
+ return r;
+
+ } else {
+ _cleanup_strv_free_ char **strv_alloc = NULL;
+ _cleanup_free_ char *key_alloc = NULL;
+
+ key_alloc = strdup(key);
+ if (!key_alloc)
+ return -ENOMEM;
+
+ strv_alloc = strv_new(value);
+ if (!strv_alloc)
+ return -ENOMEM;
+
+ r = hashmap_ensure_put(match_strv, &string_hash_ops_free_strv_free, key_alloc, strv_alloc);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(key_alloc);
+ TAKE_PTR(strv_alloc);
+ }
+
+ return 1;
+}
+
+static bool device_match_sysattr_value(sd_device *device, const char *sysattr, char * const *patterns) {
const char *value;
assert(device);
if (sd_device_get_sysattr_value(device, sysattr, &value) < 0)
return false;
- if (!match_value)
- return true;
-
- if (fnmatch(match_value, value, 0) == 0)
- return true;
-
- return false;
+ return strv_fnmatch_or_empty(patterns, value, 0);
}
bool device_match_sysattr(sd_device *device, Hashmap *match_sysattr, Hashmap *nomatch_sysattr) {
+ char * const *patterns;
const char *sysattr;
- const char *value;
assert(device);
- HASHMAP_FOREACH_KEY(value, sysattr, match_sysattr)
- if (!device_match_sysattr_value(device, sysattr, value))
+ HASHMAP_FOREACH_KEY(patterns, sysattr, match_sysattr)
+ if (!device_match_sysattr_value(device, sysattr, patterns))
return false;
- HASHMAP_FOREACH_KEY(value, sysattr, nomatch_sysattr)
- if (device_match_sysattr_value(device, sysattr, value))
+ HASHMAP_FOREACH_KEY(patterns, sysattr, nomatch_sysattr)
+ if (device_match_sysattr_value(device, sysattr, patterns))
return false;
return true;
#include "macro.h"
#include "set.h"
+#define device_unref_and_replace(a, b) \
+ unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
+
#define FOREACH_DEVICE_PROPERTY(device, key, value) \
for (key = sd_device_get_property_first(device, &(value)); \
key; \
#define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__)
#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
+int update_match_strv(Hashmap **match_strv, const char *key, const char *value, bool clear_on_null);
bool device_match_sysattr(sd_device *device, Hashmap *match_sysattr, Hashmap *nomatch_sysattr);
bool device_match_parent(sd_device *device, Set *match_parent, Set *nomatch_parent);
assert(device);
assert(_syspath);
- /* must be a subdirectory of /sys */
- if (!path_startswith(_syspath, "/sys/"))
- return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
- "sd-device: Syspath '%s' is not a subdirectory of /sys",
- _syspath);
-
if (verify) {
_cleanup_close_ int fd = -1;
+ /* The input path maybe a symlink located outside of /sys. Let's try to chase the symlink at first.
+ * The primary usecase is that e.g. /proc/device-tree is a symlink to /sys/firmware/devicetree/base.
+ * By chasing symlinks in the path at first, we can call sd_device_new_from_path() with such path. */
r = chase_symlinks(_syspath, NULL, 0, &syspath, &fd);
if (r == -ENOENT)
/* the device does not exist (any more?) */
"sd-device: the syspath \"%s\" is outside of sysfs, refusing.", syspath);
}
} else {
+ /* must be a subdirectory of /sys */
+ if (!path_startswith(_syspath, "/sys/"))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "sd-device: Syspath '%s' is not a subdirectory of /sys",
+ _syspath);
+
syspath = strdup(_syspath);
if (!syspath)
return log_oom_debug();
return 0;
}
-_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
+static int device_new_from_syspath(sd_device **ret, const char *syspath, bool strict) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
int r;
assert_return(ret, -EINVAL);
assert_return(syspath, -EINVAL);
+ if (strict && !path_startswith(syspath, "/sys/"))
+ return -EINVAL;
+
r = device_new_aux(&device);
if (r < 0)
return r;
return 0;
}
+_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
+ return device_new_from_syspath(ret, syspath, /* strict = */ true);
+}
+
static int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
_cleanup_free_ char *syspath = NULL;
if (path_startswith(path, "/dev"))
return sd_device_new_from_devname(ret, path);
- return sd_device_new_from_syspath(ret, path);
+ return device_new_from_syspath(ret, path, /* strict = */ false);
}
int device_set_devtype(sd_device *device, const char *devtype) {
return 0;
}
+_public_ int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix) {
+ _cleanup_free_ char *path = NULL;
+ const char *s;
+ int r;
+
+ assert_return(ret, -EINVAL);
+ assert_return(device, -EINVAL);
+ assert_return(suffix, -EINVAL);
+
+ if (!path_is_normalized(suffix))
+ return -EINVAL;
+
+ r = sd_device_get_syspath(device, &s);
+ if (r < 0)
+ return r;
+
+ path = path_join(s, suffix);
+ if (!path)
+ return -ENOMEM;
+
+ return sd_device_new_from_syspath(ret, path);
+}
+
static int device_new_from_child(sd_device **ret, sd_device *child) {
_cleanup_free_ char *path = NULL;
const char *syspath;
return 0;
}
+void device_clear_sysattr_cache(sd_device *device) {
+ device->sysattr_values = hashmap_free(device->sysattr_values);
+}
+
int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
_unused_ _cleanup_free_ char *old_value = NULL;
_cleanup_free_ char *new_key = NULL;
return 0;
}
+int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
+ const char *value;
+ int r;
+
+ assert(device);
+ assert(sysattr);
+
+ r = sd_device_get_sysattr_value(device, sysattr, &value);
+ if (r < 0)
+ return r;
+
+ return parse_boolean(value);
+}
+
static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) {
_cleanup_free_ char *key = NULL;
#include "device-private.h"
#include "device-util.h"
#include "macro.h"
+#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "tests.h"
assert_se(sd_device_get_syspath(loopback, &syspath) >= 0);
assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
assert_se(device_monitor_send_device(monitor_server, monitor_client, loopback) >= 0);
assert_se(sd_event_run(sd_device_monitor_get_event(monitor_client), 0) >= 0);
assert_se(sd_device_get_syspath(device, &syspath) >= 0);
assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
if (subsystem_filter) {
assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0);
assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
assert_se(sd_device_enumerator_new(&e) >= 0);
assert_se(sd_device_enumerator_add_match_subsystem(e, subsystem, false) >= 0);
assert_se(sd_device_get_syspath(d, &p) >= 0);
assert_se(sd_device_get_subsystem(d, &s) >= 0);
+ assert_se(device_add_property(d, "ACTION", "add") >= 0);
+ assert_se(device_add_property(d, "SEQNUM", "10") >= 0);
+
log_device_debug(d, "Sending device subsystem:%s syspath:%s", s, p);
assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
}
assert_se(sd_device_get_syspath(device, &syspath) >= 0);
assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
assert_se(sd_device_monitor_filter_add_match_tag(monitor_client, "TEST_SD_DEVICE_MONITOR") >= 0);
assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
assert_se(sd_device_enumerator_new(&e) >= 0);
FOREACH_DEVICE(e, d) {
assert_se(sd_device_get_syspath(d, &p) >= 0);
+ assert_se(device_add_property(d, "ACTION", "add") >= 0);
+ assert_se(device_add_property(d, "SEQNUM", "10") >= 0);
+
log_device_debug(d, "Sending device syspath:%s", p);
assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
}
static void test_sysattr_filter(sd_device *device, const char *sysattr) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- const char *syspath, *subsystem, *sysattr_value;
+ const char *syspath, *sysattr_value;
sd_device *d;
log_device_info(device, "/* %s(%s) */", __func__, sysattr);
assert_se(sd_device_get_syspath(device, &syspath) >= 0);
- assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
assert_se(sd_device_get_sysattr_value(device, sysattr, &sysattr_value) >= 0);
assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
- /* The sysattr filter is not implemented in BPF yet, so the below device_monito_send_device()
- * may cause EAGAIN. So, let's also filter devices with subsystem. */
- assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0);
assert_se(sd_device_monitor_filter_add_match_sysattr(monitor_client, sysattr, sysattr_value, true) >= 0);
assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
assert_se(sd_device_enumerator_new(&e) >= 0);
assert_se(sd_device_enumerator_add_match_sysattr(e, sysattr, sysattr_value, false) >= 0);
assert_se(sd_device_get_syspath(d, &p) >= 0);
+ assert_se(device_add_property(d, "ACTION", "add") >= 0);
+ assert_se(device_add_property(d, "SEQNUM", "10") >= 0);
+
log_device_debug(d, "Sending device syspath:%s", p);
assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
+
+ /* The sysattr filter is not implemented in BPF yet. So, sending multiple devices may fills up
+ * buffer and device_monitor_send_device() may return EAGAIN. Let's send one device here,
+ * which should be filtered out by the receiver. */
+ break;
}
log_device_info(device, "Sending device syspath:%s", syspath);
static void test_parent_filter(sd_device *device) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- const char *syspath, *subsystem;
+ const char *syspath, *parent_syspath;
sd_device *parent, *d;
int r;
log_device_info(device, "/* %s */", __func__);
assert_se(sd_device_get_syspath(device, &syspath) >= 0);
- assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
r = sd_device_get_parent(device, &parent);
if (r < 0)
return (void) log_device_info(device, "Device does not have parent, skipping.");
+ assert_se(sd_device_get_syspath(parent, &parent_syspath) >= 0);
assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
- /* The parent filter is not implemented in BPF yet, so the below device_monito_send_device()
- * may cause EAGAIN. So, let's also filter devices with subsystem. */
- assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0);
assert_se(sd_device_monitor_filter_add_match_parent(monitor_client, parent, true) >= 0);
assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
assert_se(sd_device_enumerator_new(&e) >= 0);
FOREACH_DEVICE(e, d) {
const char *p;
assert_se(sd_device_get_syspath(d, &p) >= 0);
+ if (path_startswith(p, parent_syspath))
+ continue;
+
+ assert_se(device_add_property(d, "ACTION", "add") >= 0);
+ assert_se(device_add_property(d, "SEQNUM", "10") >= 0);
log_device_debug(d, "Sending device syspath:%s", p);
assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
+
+ /* The parent filter is not implemented in BPF yet. So, sending multiple devices may fills up
+ * buffer and device_monitor_send_device() may return EAGAIN. Let's send one device here,
+ * which should be filtered out by the receiver. */
+ break;
}
log_device_info(device, "Sending device syspath:%s", syspath);
assert_se(sd_device_get_syspath(device, &syspath) >= 0);
assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0);
assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0);
assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
- assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, "hoge", NULL) >= 0);
assert_se(sd_device_monitor_filter_update(monitor_client) >= 0);
#include <ctype.h>
#include <fcntl.h>
+#include <unistd.h>
#include "device-enumerator-private.h"
#include "device-internal.h"
#include "hashmap.h"
#include "nulstr-util.h"
#include "path-util.h"
+#include "rm-rf.h"
#include "string-util.h"
#include "tests.h"
#include "time-util.h"
+#include "tmpfile-util.h"
static void test_sd_device_one(sd_device *d) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
assert_se(n_new_dev + n_removed_dev <= 10);
}
+TEST(sd_device_enumerator_add_match_sysattr) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
+ int ifindex;
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
+ assert_se(sd_device_enumerator_add_match_subsystem(e, "net", true) >= 0);
+ assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true) >= 0);
+ assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "hoge", true) >= 0);
+ assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "foo", true) >= 0);
+ assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "bar", false) >= 0);
+ assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "baz", false) >= 0);
+
+ dev = sd_device_enumerator_get_device_first(e);
+ assert_se(dev);
+ assert_se(sd_device_get_ifindex(dev, &ifindex) >= 0);
+ assert_se(ifindex == 1);
+
+ assert_se(!sd_device_enumerator_get_device_next(e));
+}
+
+TEST(sd_device_enumerator_add_match_property) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
+ int ifindex;
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
+ assert_se(sd_device_enumerator_add_match_subsystem(e, "net", true) >= 0);
+ assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true) >= 0);
+ assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", "1*") >= 0);
+ assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", "hoge") >= 0);
+ assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", NULL) >= 0);
+ assert_se(sd_device_enumerator_add_match_property(e, "AAAAA", "BBBB") >= 0);
+ assert_se(sd_device_enumerator_add_match_property(e, "FOOOO", NULL) >= 0);
+
+ dev = sd_device_enumerator_get_device_first(e);
+ assert_se(dev);
+ assert_se(sd_device_get_ifindex(dev, &ifindex) >= 0);
+ assert_se(ifindex == 1);
+}
+
TEST(sd_device_new_from_nulstr) {
const char *devlinks =
"/dev/disk/by-partuuid/1290d63a-42cc-4c71-b87c-xxxxxxxxxxxx\0"
}
}
+TEST(sd_device_new_from_path) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
+ sd_device *dev;
+ int r;
+
+ assert_se(mkdtemp_malloc("/tmp/test-sd-device.XXXXXXX", &tmpdir) >= 0);
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0);
+ assert_se(sd_device_enumerator_add_match_subsystem(e, "block", true) >= 0);
+ assert_se(sd_device_enumerator_add_nomatch_sysname(e, "loop*") >= 0);
+ assert_se(sd_device_enumerator_add_match_property(e, "DEVNAME", "*") >= 0);
+
+ FOREACH_DEVICE(e, dev) {
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+ const char *syspath, *devpath, *sysname, *s;
+ _cleanup_free_ char *path = NULL;
+
+ assert_se(sd_device_get_sysname(dev, &sysname) >= 0);
+
+ log_debug("%s(%s)", __func__, sysname);
+
+ assert_se(sd_device_get_syspath(dev, &syspath) >= 0);
+ assert_se(sd_device_new_from_path(&d, syspath) >= 0);
+ assert_se(sd_device_get_syspath(d, &s) >= 0);
+ assert_se(streq(s, syspath));
+ d = sd_device_unref(d);
+
+ assert_se(sd_device_get_devname(dev, &devpath) >= 0);
+ r = sd_device_new_from_path(&d, devpath);
+ if (r >= 0) {
+ assert_se(sd_device_get_syspath(d, &s) >= 0);
+ assert_se(streq(s, syspath));
+ d = sd_device_unref(d);
+ } else
+ assert_se(r == -ENODEV || ERRNO_IS_PRIVILEGE(r));
+
+ assert_se(path = path_join(tmpdir, sysname));
+ assert_se(symlink(syspath, path) >= 0);
+ assert_se(sd_device_new_from_path(&d, path) >= 0);
+ assert_se(sd_device_get_syspath(d, &s) >= 0);
+ assert_se(streq(s, syspath));
+ }
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
}
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free);
+#define PROTECT_EVENT(e) \
+ _unused_ _cleanup_(sd_event_unrefp) sd_event *_ref = sd_event_ref(e);
_public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) {
if (s)
* we still retain a valid event source object after
* the callback. */
- if (s->dispatching) {
- if (s->type == SOURCE_IO)
- source_io_unregister(s);
-
+ if (s->dispatching)
source_disconnect(s);
- } else
+ else
source_free(s);
return NULL;
}
static int source_dispatch(sd_event_source *s) {
- _cleanup_(sd_event_unrefp) sd_event *saved_event = NULL;
EventSourceType saved_type;
+ sd_event *saved_event;
int r = 0;
assert(s);
/* Similarly, store a reference to the event loop object, so that we can still access it after the
* callback might have invalidated/disconnected the event source. */
- saved_event = sd_event_ref(s->event);
+ saved_event = s->event;
+ PROTECT_EVENT(saved_event);
/* Check if we hit the ratelimit for this event source, and if so, let's disable it. */
assert(!s->ratelimited);
return 0;
}
- _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+ PROTECT_EVENT(e);
e->iteration++;
e->state = SD_EVENT_EXITING;
r = source_dispatch(p);
assert_return(!e->default_event_ptr || e->tid == gettid(), -EREMOTEIO);
/* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
- _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+ PROTECT_EVENT(e);
if (e->exit_requested)
goto pending;
p = event_next_pending(e);
if (p) {
- _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+ PROTECT_EVENT(e);
e->state = SD_EVENT_RUNNING;
r = source_dispatch(p);
}
/* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
- _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+ PROTECT_EVENT(e);
r = sd_event_prepare(e);
if (r == 0)
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
- _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+ PROTECT_EVENT(e);
while (e->state != SD_EVENT_FINISHED) {
r = sd_event_run(e, UINT64_MAX);
wait = false;
break;
- case OFFLINE_SYNCING:
- if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_CANCEL))
- continue;
+ case OFFLINE_SYNCING: {
+ OfflineState tmp_state = OFFLINE_SYNCING;
+ if (!__atomic_compare_exchange_n(&f->offline_state, &tmp_state, OFFLINE_CANCEL,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+ continue;
+ }
/* Canceled syncing prior to offlining, no need to wait. */
wait = false;
break;
- case OFFLINE_AGAIN_FROM_SYNCING:
- if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_CANCEL))
- continue;
+ case OFFLINE_AGAIN_FROM_SYNCING: {
+ OfflineState tmp_state = OFFLINE_AGAIN_FROM_SYNCING;
+ if (!__atomic_compare_exchange_n(&f->offline_state, &tmp_state, OFFLINE_CANCEL,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+ continue;
+ }
/* Canceled restart from syncing, no need to wait. */
wait = false;
break;
- case OFFLINE_AGAIN_FROM_OFFLINING:
- if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_CANCEL))
- continue;
+ case OFFLINE_AGAIN_FROM_OFFLINING: {
+ OfflineState tmp_state = OFFLINE_AGAIN_FROM_OFFLINING;
+ if (!__atomic_compare_exchange_n(&f->offline_state, &tmp_state, OFFLINE_CANCEL,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+ continue;
+ }
/* Canceled restart from offlining, must wait for offlining to complete however. */
_fallthrough_;
default: {
if (o->object.type != OBJECT_ENTRY)
return -EINVAL;
- __sync_synchronize();
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
/* Link up the entry itself */
r = link_entry_into_array(f,
* trigger IN_MODIFY by truncating the journal file to its
* current size which triggers IN_MODIFY. */
- __sync_synchronize();
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
if (ftruncate(f->fd, f->last_stat.st_size) < 0)
log_debug_errno(errno, "Failed to truncate file to its own size: %m");
fd_inc_sndbuf(fd, SNDBUF_SIZE);
- if (!__sync_bool_compare_and_swap(&fd_plus_one, 0, fd+1)) {
+ if (!__atomic_compare_exchange_n(&fd_plus_one, &(int){0}, fd+1,
+ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
safe_close(fd);
goto retry;
}
return fd;
}
+int journal_fd_nonblock(bool nonblock) {
+ int r;
+
+ r = journal_fd();
+ if (r < 0)
+ return r;
+
+ return fd_nonblock(r, nonblock);
+}
+
#if VALGRIND
void close_journal_fd(void) {
/* Be nice to valgrind. This is not atomic. This must be used only in tests. */
if (errno == ENOENT)
return 0;
- if (!IN_SET(errno, EMSGSIZE, ENOBUFS))
+ if (!IN_SET(errno, EMSGSIZE, ENOBUFS, EAGAIN))
return -errno;
/* Message doesn't fit... Let's dump the data in a memfd or
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <stdbool.h>
+
+int journal_fd_nonblock(bool nonblock);
+
#if VALGRIND
void close_journal_fd(void);
#else
typedef struct GenericNetlinkFamily {
sd_netlink *genl;
- const NLTypeSystem *type_system;
+ const NLAPolicySet *policy_set;
uint16_t id; /* a.k.a nlmsg_type */
char *name;
static int genl_family_new_unsupported(
sd_netlink *nl,
const char *family_name,
- const NLTypeSystem *type_system) {
+ const NLAPolicySet *policy_set) {
_cleanup_(genl_family_freep) GenericNetlinkFamily *f = NULL;
int r;
assert(nl);
assert(family_name);
- assert(type_system);
+ assert(policy_set);
/* Kernel does not support the genl family? To prevent from resolving the family name again,
* let's store the family with zero id to indicate that. */
return -ENOMEM;
*f = (GenericNetlinkFamily) {
- .type_system = type_system,
+ .policy_set = policy_set,
};
f->name = strdup(family_name);
static int genl_family_new(
sd_netlink *nl,
const char *expected_family_name,
- const NLTypeSystem *type_system,
+ const NLAPolicySet *policy_set,
sd_netlink_message *message,
const GenericNetlinkFamily **ret) {
assert(nl);
assert(expected_family_name);
- assert(type_system);
+ assert(policy_set);
assert(message);
assert(ret);
return -ENOMEM;
*f = (GenericNetlinkFamily) {
- .type_system = type_system,
+ .policy_set = policy_set,
};
r = sd_genl_message_get_family_name(nl, message, &family_name);
return 0;
}
-static const NLTypeSystem *genl_family_get_type_system(const GenericNetlinkFamily *family) {
+static const NLAPolicySet *genl_family_get_policy_set(const GenericNetlinkFamily *family) {
assert(family);
- if (family->type_system)
- return family->type_system;
+ if (family->policy_set)
+ return family->policy_set;
- return genl_get_type_system_by_name(family->name);
+ return genl_get_policy_set_by_name(family->name);
}
static int genl_message_new(
sd_netlink_message **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- const NLTypeSystem *type_system;
+ const NLAPolicySet *policy_set;
int r;
assert(nl);
assert(family);
assert(ret);
- type_system = genl_family_get_type_system(family);
- if (!type_system)
+ policy_set = genl_family_get_policy_set(family);
+ if (!policy_set)
return -EOPNOTSUPP;
- r = message_new_full(nl, family->id, type_system,
+ r = message_new_full(nl, family->id, policy_set,
sizeof(struct genlmsghdr) + family->additional_header_size, &m);
if (r < 0)
return r;
const GenericNetlinkFamily **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
- const NLTypeSystem *type_system;
+ const NLAPolicySet *policy_set;
int r;
assert(nl);
assert(name);
assert(ret);
- type_system = genl_get_type_system_by_name(name);
- if (!type_system)
+ policy_set = genl_get_policy_set_by_name(name);
+ if (!policy_set)
return -EOPNOTSUPP;
r = genl_message_new(nl, ctrl, CTRL_CMD_GETFAMILY, &req);
return r;
if (sd_netlink_call(nl, req, 0, &reply) < 0) {
- (void) genl_family_new_unsupported(nl, name, type_system);
+ (void) genl_family_new_unsupported(nl, name, policy_set);
return -EOPNOTSUPP;
}
- return genl_family_new(nl, name, type_system, reply, ret);
+ return genl_family_new(nl, name, policy_set, reply, ret);
}
static int genl_family_get_by_name(sd_netlink *nl, const char *name, const GenericNetlinkFamily **ret) {
return -ENOENT;
}
-int genl_get_type_system_and_header_size(
+int genl_get_policy_set_and_header_size(
sd_netlink *nl,
uint16_t id,
- const NLTypeSystem **ret_type_system,
+ const NLAPolicySet **ret_policy_set,
size_t *ret_header_size) {
const GenericNetlinkFamily *f;
if (r < 0)
return r;
- if (ret_type_system) {
- const NLTypeSystem *t;
+ if (ret_policy_set) {
+ const NLAPolicySet *p;
- t = genl_family_get_type_system(f);
- if (!t)
+ p = genl_family_get_policy_set(f);
+ if (!p)
return -EOPNOTSUPP;
- *ret_type_system = t;
+ *ret_policy_set = p;
}
if (ret_header_size)
*ret_header_size = sizeof(struct genlmsghdr) + f->additional_header_size;
if (r < 0)
return r;
- r = genl_get_type_system_and_header_size(nl, nlmsg_type, NULL, &size);
+ r = genl_get_policy_set_and_header_size(nl, nlmsg_type, NULL, &size);
if (r < 0)
return r;
};
struct netlink_container {
- const struct NLTypeSystem *type_system; /* the type system of the container */
+ const struct NLAPolicySet *policy_set; /* the policy set of the container */
size_t offset; /* offset from hdr to the start of the container */
struct netlink_attribute *attributes;
uint16_t max_attribute; /* the maximum attribute in container */
int message_new_full(
sd_netlink *nl,
uint16_t nlmsg_type,
- const NLTypeSystem *type_system,
+ const NLAPolicySet *policy_set,
size_t header_size,
sd_netlink_message **ret);
int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type);
/* nfnl */
/* TODO: to be exported later */
int sd_nfnl_socket_open(sd_netlink **ret);
-int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret);
-int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret);
-int sd_nfnl_nft_message_del_table(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table);
+int sd_nfnl_send_batch(
+ sd_netlink *nfnl,
+ sd_netlink_message **messages,
+ size_t msgcount,
+ uint32_t **ret_serials);
+int sd_nfnl_call_batch(
+ sd_netlink *nfnl,
+ sd_netlink_message **messages,
+ size_t n_messages,
+ uint64_t usec,
+ sd_netlink_message ***ret_messages);
+int sd_nfnl_message_new(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int nfproto,
+ uint16_t subsys,
+ uint16_t msg_type,
+ uint16_t flags);
int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table);
+ int nfproto, const char *table);
int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, const char *chain,
+ int nfproto, const char *table, const char *chain,
const char *type, uint8_t hook, int prio);
int sd_nfnl_nft_message_new_rule(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, const char *chain);
+ int nfproto, const char *table, const char *chain);
int sd_nfnl_nft_message_new_set(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, const char *set_name,
+ int nfproto, const char *table, const char *set_name,
uint32_t setid, uint32_t klen);
-int sd_nfnl_nft_message_new_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, const char *set_name);
-int sd_nfnl_nft_message_del_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *table, const char *set_name);
-int sd_nfnl_nft_message_add_setelem(sd_netlink_message *m,
- uint32_t num,
- const void *key, uint32_t klen,
- const void *data, uint32_t dlen);
-int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m);
+int sd_nfnl_nft_message_new_setelems(sd_netlink *nfnl, sd_netlink_message **ret,
+ int add, int nfproto, const char *table, const char *set_name);
+int sd_nfnl_nft_message_append_setelem(sd_netlink_message *m,
+ uint32_t index,
+ const void *key, size_t key_len,
+ const void *data, size_t data_len,
+ uint32_t flags);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <netinet/in.h>
-#include <linux/if_addrlabel.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
-#include <linux/nexthop.h>
-#include <stdbool.h>
-#include <unistd.h>
+#include <linux/netfilter.h>
#include "sd-netlink.h"
-#include "format-util.h"
+#include "io-util.h"
#include "netlink-internal.h"
#include "netlink-types.h"
-#include "socket-util.h"
+#include "netlink-util.h"
+
+static bool nfproto_is_valid(int nfproto) {
+ return IN_SET(nfproto,
+ NFPROTO_UNSPEC,
+ NFPROTO_INET,
+ NFPROTO_IPV4,
+ NFPROTO_ARP,
+ NFPROTO_NETDEV,
+ NFPROTO_BRIDGE,
+ NFPROTO_IPV6,
+ NFPROTO_DECNET);
+}
-static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t msg_type, uint16_t flags) {
+int sd_nfnl_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int nfproto, uint16_t subsys, uint16_t msg_type, uint16_t flags) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert_return(nfnl, -EINVAL);
assert_return(ret, -EINVAL);
+ assert_return(nfproto_is_valid(nfproto), -EINVAL);
+ assert_return(NFNL_MSG_TYPE(msg_type) == msg_type, -EINVAL);
- r = message_new(nfnl, &m, NFNL_SUBSYS_NFTABLES << 8 | msg_type);
+ r = message_new(nfnl, &m, subsys << 8 | msg_type);
if (r < 0)
return r;
m->hdr->nlmsg_flags |= flags;
*(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
- .nfgen_family = family,
+ .nfgen_family = nfproto,
.version = NFNETLINK_V0,
- .res_id = nfnl->serial,
};
*ret = TAKE_PTR(m);
return 0;
}
-static int nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, uint16_t msg_type) {
+static int nfnl_message_set_res_id(sd_netlink_message *m, uint16_t res_id) {
+ struct nfgenmsg *nfgen;
+
+ assert(m);
+ assert(m->hdr);
+
+ nfgen = NLMSG_DATA(m->hdr);
+ nfgen->res_id = htobe16(res_id);
+
+ return 0;
+}
+
+static int nfnl_message_get_subsys(sd_netlink_message *m, uint16_t *ret) {
+ uint16_t t;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ r = sd_netlink_message_get_type(m, &t);
+ if (r < 0)
+ return r;
+
+ *ret = NFNL_SUBSYS_ID(t);
+ return 0;
+}
+
+static int nfnl_message_new_batch(sd_netlink *nfnl, sd_netlink_message **ret, uint16_t subsys, uint16_t msg_type) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = message_new(nfnl, &m, NFNL_SUBSYS_NONE << 8 | msg_type);
+ assert_return(nfnl, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(NFNL_MSG_TYPE(msg_type) == msg_type, -EINVAL);
+
+ r = sd_nfnl_message_new(nfnl, &m, NFPROTO_UNSPEC, NFNL_SUBSYS_NONE, msg_type, 0);
if (r < 0)
return r;
- *(struct nfgenmsg*) NLMSG_DATA(m->hdr) = (struct nfgenmsg) {
- .nfgen_family = AF_UNSPEC,
- .version = NFNETLINK_V0,
- .res_id = NFNL_SUBSYS_NFTABLES,
- };
+ r = nfnl_message_set_res_id(m, subsys);
+ if (r < 0)
+ return r;
*ret = TAKE_PTR(m);
return 0;
}
-int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret) {
- return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_BEGIN);
+int sd_nfnl_send_batch(
+ sd_netlink *nfnl,
+ sd_netlink_message **messages,
+ size_t n_messages,
+ uint32_t **ret_serials) {
+
+ /* iovs refs batch_begin and batch_end, hence, free iovs first, then free batch_begin and batch_end. */
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *batch_begin = NULL, *batch_end = NULL;
+ _cleanup_free_ struct iovec *iovs = NULL;
+ _cleanup_free_ uint32_t *serials = NULL;
+ uint16_t subsys;
+ ssize_t k;
+ size_t c = 0;
+ int r;
+
+ assert_return(nfnl, -EINVAL);
+ assert_return(!netlink_pid_changed(nfnl), -ECHILD);
+ assert_return(messages, -EINVAL);
+ assert_return(n_messages > 0, -EINVAL);
+
+ iovs = new(struct iovec, n_messages + 2);
+ if (!iovs)
+ return -ENOMEM;
+
+ if (ret_serials) {
+ serials = new(uint32_t, n_messages);
+ if (!serials)
+ return -ENOMEM;
+ }
+
+ r = nfnl_message_get_subsys(messages[0], &subsys);
+ if (r < 0)
+ return r;
+
+ r = nfnl_message_new_batch(nfnl, &batch_begin, subsys, NFNL_MSG_BATCH_BEGIN);
+ if (r < 0)
+ return r;
+
+ netlink_seal_message(nfnl, batch_begin);
+ iovs[c++] = IOVEC_MAKE(batch_begin->hdr, batch_begin->hdr->nlmsg_len);
+
+ for (size_t i = 0; i < n_messages; i++) {
+ uint16_t s;
+
+ r = nfnl_message_get_subsys(messages[i], &s);
+ if (r < 0)
+ return r;
+
+ if (s != subsys)
+ return -EINVAL;
+
+ netlink_seal_message(nfnl, messages[i]);
+ if (serials)
+ serials[i] = message_get_serial(messages[i]);
+
+ /* It seems that the kernel accepts an arbitrary number. Let's set the serial of the
+ * first message. */
+ nfnl_message_set_res_id(messages[i], message_get_serial(batch_begin));
+
+ iovs[c++] = IOVEC_MAKE(messages[i]->hdr, messages[i]->hdr->nlmsg_len);
+ }
+
+ r = nfnl_message_new_batch(nfnl, &batch_end, subsys, NFNL_MSG_BATCH_END);
+ if (r < 0)
+ return r;
+
+ netlink_seal_message(nfnl, batch_end);
+ iovs[c++] = IOVEC_MAKE(batch_end->hdr, batch_end->hdr->nlmsg_len);
+
+ assert(c == n_messages + 2);
+ k = writev(nfnl->fd, iovs, n_messages + 2);
+ if (k < 0)
+ return -errno;
+
+ if (ret_serials)
+ *ret_serials = TAKE_PTR(serials);
+
+ return 0;
}
-int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret) {
- return nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_END);
+int sd_nfnl_call_batch(
+ sd_netlink *nfnl,
+ sd_netlink_message **messages,
+ size_t n_messages,
+ uint64_t usec,
+ sd_netlink_message ***ret_messages) {
+
+ _cleanup_free_ sd_netlink_message **replies = NULL;
+ _cleanup_free_ uint32_t *serials = NULL;
+ int k, r;
+
+ assert_return(nfnl, -EINVAL);
+ assert_return(!netlink_pid_changed(nfnl), -ECHILD);
+ assert_return(messages, -EINVAL);
+ assert_return(n_messages > 0, -EINVAL);
+
+ if (ret_messages) {
+ replies = new0(sd_netlink_message*, n_messages);
+ if (!replies)
+ return -ENOMEM;
+ }
+
+ r = sd_nfnl_send_batch(nfnl, messages, n_messages, &serials);
+ if (r < 0)
+ return r;
+
+ for (size_t i = 0; i < n_messages; i++) {
+ k = sd_netlink_read(nfnl, serials[i], usec, ret_messages ? replies + i : NULL);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
+ if (r < 0)
+ return r;
+
+ if (ret_messages)
+ *ret_messages = TAKE_PTR(replies);
+
+ return 0;
}
int sd_nfnl_nft_message_new_basechain(
sd_netlink *nfnl,
sd_netlink_message **ret,
- int family,
+ int nfproto,
const char *table,
const char *chain,
const char *type,
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWCHAIN, NLM_F_CREATE);
+ r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWCHAIN, NLM_F_CREATE);
if (r < 0)
return r;
return 0;
}
-int sd_nfnl_nft_message_del_table(
- sd_netlink *nfnl,
- sd_netlink_message **ret,
- int family,
- const char *table) {
-
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- int r;
-
- r = nft_message_new(nfnl, &m, family, NFT_MSG_DELTABLE, NLM_F_CREATE);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_append_string(m, NFTA_TABLE_NAME, table);
- if (r < 0)
- return r;
-
- *ret = TAKE_PTR(m);
- return r;
-}
-
int sd_nfnl_nft_message_new_table(
sd_netlink *nfnl,
sd_netlink_message **ret,
- int family,
+ int nfproto,
const char *table) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL);
+ r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWTABLE, NLM_F_CREATE | NLM_F_EXCL);
if (r < 0)
return r;
int sd_nfnl_nft_message_new_rule(
sd_netlink *nfnl,
sd_netlink_message **ret,
- int family,
+ int nfproto,
const char *table,
const char *chain) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWRULE, NLM_F_CREATE);
+ r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWRULE, NLM_F_CREATE);
if (r < 0)
return r;
int sd_nfnl_nft_message_new_set(
sd_netlink *nfnl,
sd_netlink_message **ret,
- int family,
+ int nfproto,
const char *table,
const char *set_name,
uint32_t set_id,
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSET, NLM_F_CREATE);
+ r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSET, NLM_F_CREATE);
if (r < 0)
return r;
return r;
}
-int sd_nfnl_nft_message_new_setelems_begin(
+int sd_nfnl_nft_message_new_setelems(
sd_netlink *nfnl,
sd_netlink_message **ret,
- int family,
+ int add, /* boolean */
+ int nfproto,
const char *table,
const char *set_name) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSETELEM, NLM_F_CREATE);
+ if (add)
+ r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSETELEM, NLM_F_CREATE);
+ else
+ r = sd_nfnl_message_new(nfnl, &m, nfproto, NFNL_SUBSYS_NFTABLES, NFT_MSG_DELSETELEM, 0);
if (r < 0)
return r;
if (r < 0)
return r;
- r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
- if (r < 0)
- return r;
-
*ret = TAKE_PTR(m);
return r;
}
-int sd_nfnl_nft_message_del_setelems_begin(
- sd_netlink *nfnl,
- sd_netlink_message **ret,
- int family,
- const char *table,
- const char *set_name) {
-
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- int r;
-
- r = nft_message_new(nfnl, &m, family, NFT_MSG_DELSETELEM, 0);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_TABLE, table);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_SET, set_name);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
- if (r < 0)
- return r;
-
- *ret = TAKE_PTR(m);
- return r;
-}
-
-static int add_data(sd_netlink_message *m, uint16_t attr, const void *data, uint32_t dlen) {
- int r;
-
- r = sd_netlink_message_open_container(m, attr);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_append_data(m, NFTA_DATA_VALUE, data, dlen);
- if (r < 0)
- return r;
-
- return sd_netlink_message_close_container(m); /* attr */
-}
-
-int sd_nfnl_nft_message_add_setelem(
+int sd_nfnl_nft_message_append_setelem(
sd_netlink_message *m,
- uint32_t num,
+ uint32_t index,
const void *key,
- uint32_t klen,
+ size_t key_len,
const void *data,
- uint32_t dlen) {
+ size_t data_len,
+ uint32_t flags) {
int r;
- r = sd_netlink_message_open_array(m, num);
+ r = sd_netlink_message_open_array(m, index);
if (r < 0)
return r;
- r = add_data(m, NFTA_SET_ELEM_KEY, key, klen);
+ r = sd_netlink_message_append_container_data(m, NFTA_SET_ELEM_KEY, NFTA_DATA_VALUE, key, key_len);
if (r < 0)
goto cancel;
if (data) {
- r = add_data(m, NFTA_SET_ELEM_DATA, data, dlen);
+ r = sd_netlink_message_append_container_data(m, NFTA_SET_ELEM_DATA, NFTA_DATA_VALUE, data, data_len);
if (r < 0)
goto cancel;
}
- return 0;
+ if (flags != 0) {
+ r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(flags));
+ if (r < 0)
+ goto cancel;
+ }
+
+ return sd_netlink_message_close_container(m); /* array */
cancel:
- sd_netlink_message_cancel_array(m);
+ (void) sd_netlink_message_cancel_array(m);
return r;
}
-int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m) {
- return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
-}
-
int sd_nfnl_socket_open(sd_netlink **ret) {
return netlink_open_family(ret, NETLINK_NETFILTER);
}
#include "socket-util.h"
#include "util.h"
+static bool rtnl_message_type_is_neigh(uint16_t type) {
+ return IN_SET(type, RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH);
+}
+
+static bool rtnl_message_type_is_route(uint16_t type) {
+ return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE);
+}
+
+static bool rtnl_message_type_is_nexthop(uint16_t type) {
+ return IN_SET(type, RTM_NEWNEXTHOP, RTM_GETNEXTHOP, RTM_DELNEXTHOP);
+}
+
+static bool rtnl_message_type_is_link(uint16_t type) {
+ return IN_SET(type,
+ RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK,
+ RTM_NEWLINKPROP, RTM_DELLINKPROP, RTM_GETLINKPROP);
+}
+
+static bool rtnl_message_type_is_addr(uint16_t type) {
+ return IN_SET(type, RTM_NEWADDR, RTM_GETADDR, RTM_DELADDR);
+}
+
+static bool rtnl_message_type_is_addrlabel(uint16_t type) {
+ return IN_SET(type, RTM_NEWADDRLABEL, RTM_DELADDRLABEL, RTM_GETADDRLABEL);
+}
+
+static bool rtnl_message_type_is_routing_policy_rule(uint16_t type) {
+ return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE);
+}
+
+static bool rtnl_message_type_is_traffic_control(uint16_t type) {
+ return IN_SET(type,
+ RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC,
+ RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS);
+}
+
+static bool rtnl_message_type_is_mdb(uint16_t type) {
+ return IN_SET(type, RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB);
+}
+
_public_ int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) {
struct rtmsg *rtm;
int message_new_full(
sd_netlink *nl,
uint16_t nlmsg_type,
- const NLTypeSystem *type_system,
+ const NLAPolicySet *policy_set,
size_t header_size,
sd_netlink_message **ret) {
int r;
assert(nl);
- assert(type_system);
+ assert(policy_set);
assert(ret);
size = NLMSG_SPACE(header_size);
if (r < 0)
return r;
- m->containers[0].type_system = type_system;
+ m->containers[0].policy_set = policy_set;
m->hdr = malloc0(size);
if (!m->hdr)
return 0;
}
-int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t type) {
- const NLTypeSystem *type_system;
+int message_new(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type) {
+ const NLAPolicySet *policy_set;
size_t size;
int r;
assert_return(nl, -EINVAL);
assert_return(ret, -EINVAL);
- r = type_system_root_get_type_system_and_header_size(nl, type, &type_system, &size);
+ r = netlink_get_policy_set_and_header_size(nl, nlmsg_type, &policy_set, &size);
if (r < 0)
return r;
- return message_new_full(nl, type, type_system, size, ret);
+ return message_new_full(nl, nlmsg_type, policy_set, size, ret);
}
int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_netlink_message **ret) {
return NULL;
}
-_public_ int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *type) {
+_public_ int sd_netlink_message_get_type(sd_netlink_message *m, uint16_t *ret) {
assert_return(m, -EINVAL);
- assert_return(type != 0, -EINVAL);
+ assert_return(ret, -EINVAL);
- *type = m->hdr->nlmsg_type;
+ *ret = m->hdr->nlmsg_type;
return 0;
}
return m->multicast_group != 0;
}
-/* If successful the updated message will be correctly aligned, if
- unsuccessful the old message is untouched. */
-static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) {
+/* If successful the updated message will be correctly aligned, if unsuccessful the old message is untouched. */
+static int add_rtattr(sd_netlink_message *m, uint16_t attr_type, const void *data, size_t data_length) {
size_t message_length;
struct nlmsghdr *new_hdr;
struct rtattr *rta;
/* get pointer to the attribute we are about to add */
rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
- rtattr_append_attribute_internal(rta, type, data, data_length);
+ rtattr_append_attribute_internal(rta, attr_type, data, data_length);
/* if we are inside containers, extend them */
for (unsigned i = 0; i < m->n_containers; i++)
return offset;
}
-static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, uint16_t attribute_type, uint16_t data_type) {
- const NLType *type;
+static int message_attribute_has_type(sd_netlink_message *m, size_t *ret_size, uint16_t attr_type, NLAType type) {
+ const NLAPolicy *policy;
assert(m);
- type = type_system_get_type(m->containers[m->n_containers].type_system, attribute_type);
- if (!type)
+ policy = policy_set_get_policy(m->containers[m->n_containers].policy_set, attr_type);
+ if (!policy)
return -EOPNOTSUPP;
- if (type_get_type(type) != data_type)
+ if (policy_get_type(policy) != type)
return -EINVAL;
- if (out_size)
- *out_size = type_get_size(type);
+ if (ret_size)
+ *ret_size = policy_get_size(policy);
return 0;
}
-_public_ int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data) {
+_public_ int sd_netlink_message_append_string(sd_netlink_message *m, uint16_t attr_type, const char *data) {
size_t length, size;
int r;
assert_return(!m->sealed, -EPERM);
assert_return(data, -EINVAL);
- r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING);
+ r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_STRING);
if (r < 0)
return r;
} else
length = strlen(data);
- r = add_rtattr(m, type, data, length + 1);
+ r = add_rtattr(m, attr_type, data, length + 1);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned short type, char * const *data) {
+_public_ int sd_netlink_message_append_strv(sd_netlink_message *m, uint16_t attr_type, char * const *data) {
size_t length, size;
int r;
assert_return(!m->sealed, -EPERM);
assert_return(data, -EINVAL);
- r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_STRING);
+ r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_STRING);
if (r < 0)
return r;
} else
length = strlen(*p);
- r = add_rtattr(m, type, *p, length + 1);
+ r = add_rtattr(m, attr_type, *p, length + 1);
if (r < 0)
return r;
}
return 0;
}
-_public_ int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type) {
+_public_ int sd_netlink_message_append_flag(sd_netlink_message *m, uint16_t attr_type) {
size_t size;
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_FLAG);
+ r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_FLAG);
if (r < 0)
return r;
- r = add_rtattr(m, type, NULL, 0);
+ r = add_rtattr(m, attr_type, NULL, 0);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data) {
+_public_ int sd_netlink_message_append_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U8);
if (r < 0)
return r;
- r = add_rtattr(m, type, &data, sizeof(uint8_t));
+ r = add_rtattr(m, attr_type, &data, sizeof(uint8_t));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data) {
+_public_ int sd_netlink_message_append_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U16);
if (r < 0)
return r;
- r = add_rtattr(m, type, &data, sizeof(uint16_t));
+ r = add_rtattr(m, attr_type, &data, sizeof(uint16_t));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data) {
+_public_ int sd_netlink_message_append_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U32);
if (r < 0)
return r;
- r = add_rtattr(m, type, &data, sizeof(uint32_t));
+ r = add_rtattr(m, attr_type, &data, sizeof(uint32_t));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data) {
+_public_ int sd_netlink_message_append_u64(sd_netlink_message *m, uint16_t attr_type, uint64_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U64);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U64);
if (r < 0)
return r;
- r = add_rtattr(m, type, &data, sizeof(uint64_t));
+ r = add_rtattr(m, attr_type, &data, sizeof(uint64_t));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data) {
+_public_ int sd_netlink_message_append_s8(sd_netlink_message *m, uint16_t attr_type, int8_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S8);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S8);
if (r < 0)
return r;
- r = add_rtattr(m, type, &data, sizeof(int8_t));
+ r = add_rtattr(m, attr_type, &data, sizeof(int8_t));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data) {
+_public_ int sd_netlink_message_append_s16(sd_netlink_message *m, uint16_t attr_type, int16_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S16);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S16);
if (r < 0)
return r;
- r = add_rtattr(m, type, &data, sizeof(int16_t));
+ r = add_rtattr(m, attr_type, &data, sizeof(int16_t));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data) {
+_public_ int sd_netlink_message_append_s32(sd_netlink_message *m, uint16_t attr_type, int32_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S32);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S32);
if (r < 0)
return r;
- r = add_rtattr(m, type, &data, sizeof(int32_t));
+ r = add_rtattr(m, attr_type, &data, sizeof(int32_t));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data) {
+_public_ int sd_netlink_message_append_s64(sd_netlink_message *m, uint16_t attr_type, int64_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S64);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_S64);
if (r < 0)
return r;
- r = add_rtattr(m, type, &data, sizeof(int64_t));
+ r = add_rtattr(m, attr_type, &data, sizeof(int64_t));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) {
+_public_ int sd_netlink_message_append_data(sd_netlink_message *m, uint16_t attr_type, const void *data, size_t len) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = add_rtattr(m, type, data, len);
+ r = add_rtattr(m, attr_type, data, len);
if (r < 0)
return r;
return 0;
}
-int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data) {
+_public_ int sd_netlink_message_append_container_data(
+ sd_netlink_message *m,
+ uint16_t container_type,
+ uint16_t attr_type,
+ const void *data,
+ size_t len) {
+
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(!m->sealed, -EPERM);
+
+ r = sd_netlink_message_open_container(m, container_type);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_append_data(m, attr_type, data, len);
+ if (r < 0)
+ return r;
+
+ return sd_netlink_message_close_container(m);
+}
+
+int netlink_message_append_in_addr_union(sd_netlink_message *m, uint16_t attr_type, int family, const union in_addr_union *data) {
int r;
assert_return(m, -EINVAL);
assert_return(data, -EINVAL);
assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_IN_ADDR);
if (r < 0)
return r;
- r = add_rtattr(m, type, data, FAMILY_ADDRESS_SIZE(family));
+ r = add_rtattr(m, attr_type, data, FAMILY_ADDRESS_SIZE(family));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) {
- return netlink_message_append_in_addr_union(m, type, AF_INET, (const union in_addr_union *) data);
+_public_ int sd_netlink_message_append_in_addr(sd_netlink_message *m, uint16_t attr_type, const struct in_addr *data) {
+ return netlink_message_append_in_addr_union(m, attr_type, AF_INET, (const union in_addr_union *) data);
}
-_public_ int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) {
- return netlink_message_append_in_addr_union(m, type, AF_INET6, (const union in_addr_union *) data);
+_public_ int sd_netlink_message_append_in6_addr(sd_netlink_message *m, uint16_t attr_type, const struct in6_addr *data) {
+ return netlink_message_append_in_addr_union(m, attr_type, AF_INET6, (const union in_addr_union *) data);
}
-int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data) {
+int netlink_message_append_sockaddr_union(sd_netlink_message *m, uint16_t attr_type, const union sockaddr_union *data) {
int r;
assert_return(m, -EINVAL);
assert_return(data, -EINVAL);
assert_return(IN_SET(data->sa.sa_family, AF_INET, AF_INET6), -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_SOCKADDR);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_SOCKADDR);
if (r < 0)
return r;
- r = add_rtattr(m, type, data, data->sa.sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
+ r = add_rtattr(m, attr_type, data, data->sa.sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, unsigned short type, const struct sockaddr_in *data) {
- return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data);
+_public_ int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in *data) {
+ return netlink_message_append_sockaddr_union(m, attr_type, (const union sockaddr_union *) data);
}
-_public_ int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, unsigned short type, const struct sockaddr_in6 *data) {
- return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data);
+_public_ int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in6 *data) {
+ return netlink_message_append_sockaddr_union(m, attr_type, (const union sockaddr_union *) data);
}
-_public_ int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) {
+_public_ int sd_netlink_message_append_ether_addr(sd_netlink_message *m, uint16_t attr_type, const struct ether_addr *data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
assert_return(data, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR);
if (r < 0)
return r;
- r = add_rtattr(m, type, data, ETH_ALEN);
+ r = add_rtattr(m, attr_type, data, ETH_ALEN);
if (r < 0)
return r;
return 0;
}
-int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, const struct hw_addr_data *data) {
+int netlink_message_append_hw_addr(sd_netlink_message *m, uint16_t attr_type, const struct hw_addr_data *data) {
int r;
assert_return(m, -EINVAL);
assert_return(data, -EINVAL);
assert_return(data->length > 0, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR);
if (r < 0)
return r;
- r = add_rtattr(m, type, data->bytes, data->length);
+ r = add_rtattr(m, attr_type, data->bytes, data->length);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info) {
+_public_ int sd_netlink_message_append_cache_info(sd_netlink_message *m, uint16_t attr_type, const struct ifa_cacheinfo *info) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
assert_return(info, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_CACHE_INFO);
if (r < 0)
return r;
- r = add_rtattr(m, type, info, sizeof(struct ifa_cacheinfo));
+ r = add_rtattr(m, attr_type, info, sizeof(struct ifa_cacheinfo));
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type) {
+_public_ int sd_netlink_message_open_container(sd_netlink_message *m, uint16_t attr_type) {
size_t size;
int r;
/* m->containers[m->n_containers + 1] is accessed both in read and write. Prevent access out of bound */
assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
- r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_NESTED);
+ r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_NESTED);
if (r < 0) {
- const NLTypeSystemUnion *type_system_union;
+ const NLAPolicySetUnion *policy_set_union;
int family;
- r = message_attribute_has_type(m, &size, type, NETLINK_TYPE_UNION);
+ r = message_attribute_has_type(m, &size, attr_type, NETLINK_TYPE_NESTED_UNION_BY_FAMILY);
if (r < 0)
return r;
if (r < 0)
return r;
- type_system_union = type_system_get_type_system_union(
- m->containers[m->n_containers].type_system,
- type);
- if (!type_system_union)
+ policy_set_union = policy_set_get_policy_set_union(
+ m->containers[m->n_containers].policy_set,
+ attr_type);
+ if (!policy_set_union)
return -EOPNOTSUPP;
- m->containers[m->n_containers + 1].type_system =
- type_system_union_get_type_system_by_protocol(
- type_system_union,
+ m->containers[m->n_containers + 1].policy_set =
+ policy_set_union_get_policy_set_by_family(
+ policy_set_union,
family);
} else
- m->containers[m->n_containers + 1].type_system =
- type_system_get_type_system(
- m->containers[m->n_containers].type_system,
- type);
- if (!m->containers[m->n_containers + 1].type_system)
+ m->containers[m->n_containers + 1].policy_set =
+ policy_set_get_policy_set(
+ m->containers[m->n_containers].policy_set,
+ attr_type);
+ if (!m->containers[m->n_containers + 1].policy_set)
return -EOPNOTSUPP;
- r = add_rtattr(m, type | NLA_F_NESTED, NULL, size);
+ r = add_rtattr(m, attr_type | NLA_F_NESTED, NULL, size);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key) {
- const NLTypeSystemUnion *type_system_union;
+_public_ int sd_netlink_message_open_container_union(sd_netlink_message *m, uint16_t attr_type, const char *key) {
+ const NLAPolicySetUnion *policy_set_union;
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
- type_system_union = type_system_get_type_system_union(
- m->containers[m->n_containers].type_system,
- type);
- if (!type_system_union)
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_NESTED_UNION_BY_STRING);
+ if (r < 0)
+ return r;
+
+ policy_set_union = policy_set_get_policy_set_union(
+ m->containers[m->n_containers].policy_set,
+ attr_type);
+ if (!policy_set_union)
return -EOPNOTSUPP;
- m->containers[m->n_containers + 1].type_system =
- type_system_union_get_type_system_by_string(
- type_system_union,
+ m->containers[m->n_containers + 1].policy_set =
+ policy_set_union_get_policy_set_by_string(
+ policy_set_union,
key);
- if (!m->containers[m->n_containers + 1].type_system)
+ if (!m->containers[m->n_containers + 1].policy_set)
return -EOPNOTSUPP;
- r = sd_netlink_message_append_string(m, type_system_union_get_match_attribute(type_system_union), key);
+ r = sd_netlink_message_append_string(m, policy_set_union_get_match_attribute(policy_set_union), key);
if (r < 0)
return r;
/* do we ever need non-null size */
- r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
+ r = add_rtattr(m, attr_type | NLA_F_NESTED, NULL, 0);
if (r < 0)
return r;
assert_return(!m->sealed, -EPERM);
assert_return(m->n_containers > 0, -EINVAL);
- m->containers[m->n_containers].type_system = NULL;
+ m->containers[m->n_containers].policy_set = NULL;
m->containers[m->n_containers].offset = 0;
m->n_containers--;
return 0;
}
-_public_ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
+_public_ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t attr_type) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -ERANGE);
- r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
+ r = add_rtattr(m, attr_type | NLA_F_NESTED, NULL, 0);
if (r < 0)
return r;
m->containers[m->n_containers].offset = r;
m->n_containers++;
- m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
+ m->containers[m->n_containers].policy_set = m->containers[m->n_containers - 1].policy_set;
return 0;
}
m->hdr->nlmsg_len -= rta_len;
m->n_containers--;
- m->containers[m->n_containers].type_system = NULL;
+ m->containers[m->n_containers].policy_set = NULL;
return 0;
}
static int netlink_message_read_internal(
sd_netlink_message *m,
- unsigned short type,
+ uint16_t attr_type,
void **ret_data,
bool *ret_net_byteorder) {
if (!m->containers[m->n_containers].attributes)
return -ENODATA;
- if (type > m->containers[m->n_containers].max_attribute)
+ if (attr_type > m->containers[m->n_containers].max_attribute)
return -ENODATA;
- attribute = &m->containers[m->n_containers].attributes[type];
+ attribute = &m->containers[m->n_containers].attributes[attr_type];
if (attribute->offset == 0)
return -ENODATA;
return RTA_PAYLOAD(rta);
}
-_public_ int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data) {
+_public_ int sd_netlink_message_read(sd_netlink_message *m, uint16_t attr_type, size_t size, void *data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return r;
}
-_public_ int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
+_public_ int sd_netlink_message_read_data(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return r;
}
-_public_ int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
+_public_ int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return r;
}
-_public_ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) {
+_public_ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, uint16_t attr_type, char **data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_STRING);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
+_public_ int sd_netlink_message_read_string(sd_netlink_message *m, uint16_t attr_type, const char **data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_STRING);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_STRING);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) {
+_public_ int sd_netlink_message_read_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t *data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U8);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U8);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) {
+_public_ int sd_netlink_message_read_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t *data) {
void *attr_data;
bool net_byteorder;
int r;
assert_return(m, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U16);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U16);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, &net_byteorder);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) {
+_public_ int sd_netlink_message_read_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t *data) {
void *attr_data;
bool net_byteorder;
int r;
assert_return(m, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_U32);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_U32);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, &net_byteorder);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) {
+_public_ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, uint16_t attr_type, struct ether_addr *data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return 0;
}
-int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, struct hw_addr_data *data) {
+int netlink_message_read_hw_addr(sd_netlink_message *m, uint16_t attr_type, struct hw_addr_data *data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_ETHER_ADDR);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_ETHER_ADDR);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) {
+_public_ int sd_netlink_message_read_cache_info(sd_netlink_message *m, uint16_t attr_type, struct ifa_cacheinfo *info) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_CACHE_INFO);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_CACHE_INFO);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return 0;
}
-int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short type, int family, union in_addr_union *data) {
+int netlink_message_read_in_addr_union(sd_netlink_message *m, uint16_t attr_type, int family, union in_addr_union *data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL);
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_IN_ADDR);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data) {
+_public_ int sd_netlink_message_read_in_addr(sd_netlink_message *m, uint16_t attr_type, struct in_addr *data) {
union in_addr_union u;
int r;
- r = netlink_message_read_in_addr_union(m, type, AF_INET, &u);
+ r = netlink_message_read_in_addr_union(m, attr_type, AF_INET, &u);
if (r >= 0 && data)
*data = u.in;
return r;
}
-_public_ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data) {
+_public_ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, uint16_t attr_type, struct in6_addr *data) {
union in_addr_union u;
int r;
- r = netlink_message_read_in_addr_union(m, type, AF_INET6, &u);
+ r = netlink_message_read_in_addr_union(m, attr_type, AF_INET6, &u);
if (r >= 0 && data)
*data = u.in6;
return r;
}
-_public_ int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short type) {
+_public_ int sd_netlink_message_has_flag(sd_netlink_message *m, uint16_t attr_type) {
void *attr_data;
int r;
/* This returns 1 when the flag is set, 0 when not set, negative errno on error. */
- r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_FLAG);
+ r = message_attribute_has_type(m, NULL, attr_type, NETLINK_TYPE_FLAG);
if (r < 0)
return r;
- r = netlink_message_read_internal(m, type, &attr_data, NULL);
+ r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
if (r == -ENODATA)
return 0;
if (r < 0)
return 1;
}
-_public_ int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret) {
+_public_ int sd_netlink_message_read_strv(sd_netlink_message *m, uint16_t container_type, uint16_t attr_type, char ***ret) {
_cleanup_strv_free_ char **s = NULL;
- const NLTypeSystem *type_system;
- const NLType *nl_type;
+ const NLAPolicySet *policy_set;
+ const NLAPolicy *policy;
struct rtattr *rta;
void *container;
size_t rt_len;
assert_return(m, -EINVAL);
assert_return(m->n_containers < NETLINK_CONTAINER_DEPTH, -EINVAL);
- nl_type = type_system_get_type(
- m->containers[m->n_containers].type_system,
+ policy = policy_set_get_policy(
+ m->containers[m->n_containers].policy_set,
container_type);
- if (!nl_type)
+ if (!policy)
return -EOPNOTSUPP;
- if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
+ if (policy_get_type(policy) != NETLINK_TYPE_NESTED)
return -EINVAL;
- type_system = type_system_get_type_system(
- m->containers[m->n_containers].type_system,
+ policy_set = policy_set_get_policy_set(
+ m->containers[m->n_containers].policy_set,
container_type);
- if (!type_system)
+ if (!policy_set)
return -EOPNOTSUPP;
- nl_type = type_system_get_type(type_system, type_id);
- if (!nl_type)
+ policy = policy_set_get_policy(policy_set, attr_type);
+ if (!policy)
return -EOPNOTSUPP;
- if (type_get_type(nl_type) != NETLINK_TYPE_STRING)
+ if (policy_get_type(policy) != NETLINK_TYPE_STRING)
return -EINVAL;
r = netlink_message_read_internal(m, container_type, &container, NULL);
* introduce an unsigned short variable as a workaround. */
unsigned short len = rt_len;
for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
- unsigned short type;
+ uint16_t type;
type = RTA_TYPE(rta);
- if (type != type_id)
+ if (type != attr_type)
continue;
r = strv_extend(&s, RTA_DATA(rta));
return 0;
}
-_public_ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) {
- const NLType *nl_type;
- const NLTypeSystem *type_system;
+_public_ int sd_netlink_message_enter_container(sd_netlink_message *m, uint16_t attr_type) {
+ const NLAPolicy *policy;
+ const NLAPolicySet *policy_set;
void *container;
- uint16_t type;
size_t size;
int r;
assert_return(m, -EINVAL);
assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
- nl_type = type_system_get_type(
- m->containers[m->n_containers].type_system,
- type_id);
- if (!nl_type)
+ policy = policy_set_get_policy(
+ m->containers[m->n_containers].policy_set,
+ attr_type);
+ if (!policy)
return -EOPNOTSUPP;
- type = type_get_type(nl_type);
+ switch (policy_get_type(policy)) {
+ case NETLINK_TYPE_NESTED:
+ policy_set = policy_set_get_policy_set(
+ m->containers[m->n_containers].policy_set,
+ attr_type);
+ break;
+
+ case NETLINK_TYPE_NESTED_UNION_BY_STRING: {
+ const NLAPolicySetUnion *policy_set_union;
+ const char *key;
- if (type == NETLINK_TYPE_NESTED) {
- type_system = type_system_get_type_system(
- m->containers[m->n_containers].type_system,
- type_id);
- if (!type_system)
+ policy_set_union = policy_get_policy_set_union(policy);
+ if (!policy_set_union)
return -EOPNOTSUPP;
- } else if (type == NETLINK_TYPE_UNION) {
- const NLTypeSystemUnion *type_system_union;
- type_system_union = type_system_get_type_system_union(
- m->containers[m->n_containers].type_system,
- type_id);
- if (!type_system_union)
+ r = sd_netlink_message_read_string(
+ m,
+ policy_set_union_get_match_attribute(policy_set_union),
+ &key);
+ if (r < 0)
+ return r;
+
+ policy_set = policy_set_union_get_policy_set_by_string(
+ policy_set_union,
+ key);
+ break;
+ }
+ case NETLINK_TYPE_NESTED_UNION_BY_FAMILY: {
+ const NLAPolicySetUnion *policy_set_union;
+ int family;
+
+ policy_set_union = policy_get_policy_set_union(policy);
+ if (!policy_set_union)
return -EOPNOTSUPP;
- switch (type_system_union_get_match_type(type_system_union)) {
- case NL_MATCH_SIBLING: {
- const char *key;
-
- r = sd_netlink_message_read_string(
- m,
- type_system_union_get_match_attribute(type_system_union),
- &key);
- if (r < 0)
- return r;
-
- type_system = type_system_union_get_type_system_by_string(
- type_system_union,
- key);
- if (!type_system)
- return -EOPNOTSUPP;
-
- break;
- }
- case NL_MATCH_PROTOCOL: {
- int family;
-
- r = sd_rtnl_message_get_family(m, &family);
- if (r < 0)
- return r;
-
- type_system = type_system_union_get_type_system_by_protocol(
- type_system_union,
- family);
- if (!type_system)
- return -EOPNOTSUPP;
-
- break;
- }
- default:
- assert_not_reached();
- }
- } else
- return -EINVAL;
+ r = sd_rtnl_message_get_family(m, &family);
+ if (r < 0)
+ return r;
+
+ policy_set = policy_set_union_get_policy_set_by_family(
+ policy_set_union,
+ family);
+ break;
+ }
+ default:
+ assert_not_reached();
+ }
+ if (!policy_set)
+ return -EOPNOTSUPP;
- r = netlink_message_read_internal(m, type_id, &container, NULL);
+ r = netlink_message_read_internal(m, attr_type, &container, NULL);
if (r < 0)
return r;
return r;
}
- m->containers[m->n_containers].type_system = type_system;
+ m->containers[m->n_containers].policy_set = policy_set;
return 0;
}
-_public_ int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type_id) {
+_public_ int sd_netlink_message_enter_array(sd_netlink_message *m, uint16_t attr_type) {
void *container;
size_t size;
int r;
assert_return(m, -EINVAL);
assert_return(m->n_containers < (NETLINK_CONTAINER_DEPTH - 1), -EINVAL);
- r = netlink_message_read_internal(m, type_id, &container, NULL);
+ r = netlink_message_read_internal(m, attr_type, &container, NULL);
if (r < 0)
return r;
return r;
}
- m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
+ m->containers[m->n_containers].policy_set = m->containers[m->n_containers - 1].policy_set;
return 0;
}
m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes);
m->containers[m->n_containers].max_attribute = 0;
- m->containers[m->n_containers].type_system = NULL;
+ m->containers[m->n_containers].policy_set = NULL;
m->n_containers--;
assert(m->hdr);
- r = type_system_root_get_type_system_and_header_size(nl, m->hdr->nlmsg_type,
- &m->containers[0].type_system, &size);
+ r = netlink_get_policy_set_and_header_size(nl, m->hdr->nlmsg_type,
+ &m->containers[0].policy_set, &size);
if (r < 0)
return r;
#include "io-util.h"
#include "netlink-internal.h"
#include "netlink-types.h"
-#include "netlink-util.h"
#include "socket-util.h"
#include "util.h"
}
/* check that we support this message type */
- r = type_system_root_get_type_system_and_header_size(nl, new_msg->nlmsg_type, NULL, &size);
+ r = netlink_get_policy_set_and_header_size(nl, new_msg->nlmsg_type, NULL, &size);
if (r < 0) {
if (r == -EOPNOTSUPP)
log_debug("sd-netlink: ignored message with unknown type: %i",
#include "netlink-types-internal.h"
/***************** genl ctrl type systems *****************/
-static const NLType genl_ctrl_mcast_group_types[] = {
- [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NETLINK_TYPE_STRING },
- [CTRL_ATTR_MCAST_GRP_ID] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy genl_ctrl_mcast_group_policies[] = {
+ [CTRL_ATTR_MCAST_GRP_NAME] = BUILD_POLICY(STRING),
+ [CTRL_ATTR_MCAST_GRP_ID] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(genl_ctrl_mcast_group);
+DEFINE_POLICY_SET(genl_ctrl_mcast_group);
-static const NLType genl_ctrl_ops_types[] = {
- [CTRL_ATTR_OP_ID] = { .type = NETLINK_TYPE_U32 },
- [CTRL_ATTR_OP_FLAGS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy genl_ctrl_ops_policies[] = {
+ [CTRL_ATTR_OP_ID] = BUILD_POLICY(U32),
+ [CTRL_ATTR_OP_FLAGS] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(genl_ctrl_ops);
+DEFINE_POLICY_SET(genl_ctrl_ops);
-static const NLType genl_ctrl_types[] = {
- [CTRL_ATTR_FAMILY_ID] = { .type = NETLINK_TYPE_U16 },
- [CTRL_ATTR_FAMILY_NAME] = { .type = NETLINK_TYPE_STRING },
- [CTRL_ATTR_VERSION] = { .type = NETLINK_TYPE_U32 },
- [CTRL_ATTR_HDRSIZE] = { .type = NETLINK_TYPE_U32 },
- [CTRL_ATTR_MAXATTR] = { .type = NETLINK_TYPE_U32 },
- [CTRL_ATTR_OPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_ops_type_system },
- [CTRL_ATTR_MCAST_GROUPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_mcast_group_type_system },
+static const NLAPolicy genl_ctrl_policies[] = {
+ [CTRL_ATTR_FAMILY_ID] = BUILD_POLICY(U16),
+ [CTRL_ATTR_FAMILY_NAME] = BUILD_POLICY(STRING),
+ [CTRL_ATTR_VERSION] = BUILD_POLICY(U32),
+ [CTRL_ATTR_HDRSIZE] = BUILD_POLICY(U32),
+ [CTRL_ATTR_MAXATTR] = BUILD_POLICY(U32),
+ [CTRL_ATTR_OPS] = BUILD_POLICY_NESTED(genl_ctrl_ops),
+ [CTRL_ATTR_MCAST_GROUPS] = BUILD_POLICY_NESTED(genl_ctrl_mcast_group),
/*
[CTRL_ATTR_POLICY] = { .type = NETLINK_TYPE_NESTED, },
[CTRL_ATTR_OP_POLICY] = { .type = NETLINK_TYPE_NESTED, }
*/
- [CTRL_ATTR_OP] = { .type = NETLINK_TYPE_U32 },
+ [CTRL_ATTR_OP] = BUILD_POLICY(U32),
};
/***************** genl batadv type systems *****************/
-static const NLType genl_batadv_types[] = {
- [BATADV_ATTR_VERSION] = { .type = NETLINK_TYPE_STRING },
- [BATADV_ATTR_ALGO_NAME] = { .type = NETLINK_TYPE_STRING },
- [BATADV_ATTR_MESH_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_MESH_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
- [BATADV_ATTR_MESH_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [BATADV_ATTR_HARD_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_HARD_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ },
- [BATADV_ATTR_HARD_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [BATADV_ATTR_ORIG_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [BATADV_ATTR_TPMETER_RESULT] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_TPMETER_BYTES] = { .type = NETLINK_TYPE_U64 },
- [BATADV_ATTR_TPMETER_COOKIE] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_PAD] = { .type = NETLINK_TYPE_UNSPEC },
- [BATADV_ATTR_ACTIVE] = { .type = NETLINK_TYPE_FLAG },
- [BATADV_ATTR_TT_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [BATADV_ATTR_TT_TTVN] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_TT_LAST_TTVN] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_TT_CRC32] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_TT_VID] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_TT_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_FLAG_BEST] = { .type = NETLINK_TYPE_FLAG },
- [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_NEIGH_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [BATADV_ATTR_TQ] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_THROUGHPUT] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_BANDWIDTH_UP] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_ROUTER] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [BATADV_ATTR_BLA_OWN] = { .type = NETLINK_TYPE_FLAG },
- [BATADV_ATTR_BLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [BATADV_ATTR_BLA_VID] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_BLA_BACKBONE] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [BATADV_ATTR_BLA_CRC] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_DAT_CACHE_HWADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [BATADV_ATTR_DAT_CACHE_VID] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_MCAST_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_MCAST_FLAGS_PRIV] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_VLANID] = { .type = NETLINK_TYPE_U16 },
- [BATADV_ATTR_AGGREGATED_OGMS_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_AP_ISOLATION_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_ISOLATION_MARK] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_ISOLATION_MASK] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_BONDING_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_FRAGMENTATION_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_GW_BANDWIDTH_DOWN] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_GW_BANDWIDTH_UP] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_GW_MODE] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_GW_SEL_CLASS] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_HOP_PENALTY] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_LOG_LEVEL] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_MULTICAST_FANOUT] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_NETWORK_CODING_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [BATADV_ATTR_ORIG_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_ELP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [BATADV_ATTR_THROUGHPUT_OVERRIDE] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy genl_batadv_policies[] = {
+ [BATADV_ATTR_VERSION] = BUILD_POLICY(STRING),
+ [BATADV_ATTR_ALGO_NAME] = BUILD_POLICY(STRING),
+ [BATADV_ATTR_MESH_IFINDEX] = BUILD_POLICY(U32),
+ [BATADV_ATTR_MESH_IFNAME] = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ),
+ [BATADV_ATTR_MESH_ADDRESS] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [BATADV_ATTR_HARD_IFINDEX] = BUILD_POLICY(U32),
+ [BATADV_ATTR_HARD_IFNAME] = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ),
+ [BATADV_ATTR_HARD_ADDRESS] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [BATADV_ATTR_ORIG_ADDRESS] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [BATADV_ATTR_TPMETER_RESULT] = BUILD_POLICY(U8),
+ [BATADV_ATTR_TPMETER_TEST_TIME] = BUILD_POLICY(U32),
+ [BATADV_ATTR_TPMETER_BYTES] = BUILD_POLICY(U64),
+ [BATADV_ATTR_TPMETER_COOKIE] = BUILD_POLICY(U32),
+ [BATADV_ATTR_PAD] = BUILD_POLICY(UNSPEC),
+ [BATADV_ATTR_ACTIVE] = BUILD_POLICY(FLAG),
+ [BATADV_ATTR_TT_ADDRESS] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [BATADV_ATTR_TT_TTVN] = BUILD_POLICY(U8),
+ [BATADV_ATTR_TT_LAST_TTVN] = BUILD_POLICY(U8),
+ [BATADV_ATTR_TT_CRC32] = BUILD_POLICY(U32),
+ [BATADV_ATTR_TT_VID] = BUILD_POLICY(U16),
+ [BATADV_ATTR_TT_FLAGS] = BUILD_POLICY(U32),
+ [BATADV_ATTR_FLAG_BEST] = BUILD_POLICY(FLAG),
+ [BATADV_ATTR_LAST_SEEN_MSECS] = BUILD_POLICY(U32),
+ [BATADV_ATTR_NEIGH_ADDRESS] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [BATADV_ATTR_TQ] = BUILD_POLICY(U8),
+ [BATADV_ATTR_THROUGHPUT] = BUILD_POLICY(U32),
+ [BATADV_ATTR_BANDWIDTH_UP] = BUILD_POLICY(U32),
+ [BATADV_ATTR_BANDWIDTH_DOWN] = BUILD_POLICY(U32),
+ [BATADV_ATTR_ROUTER] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [BATADV_ATTR_BLA_OWN] = BUILD_POLICY(FLAG),
+ [BATADV_ATTR_BLA_ADDRESS] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [BATADV_ATTR_BLA_VID] = BUILD_POLICY(U16),
+ [BATADV_ATTR_BLA_BACKBONE] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [BATADV_ATTR_BLA_CRC] = BUILD_POLICY(U16),
+ [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = BUILD_POLICY(U32),
+ [BATADV_ATTR_DAT_CACHE_HWADDRESS] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [BATADV_ATTR_DAT_CACHE_VID] = BUILD_POLICY(U16),
+ [BATADV_ATTR_MCAST_FLAGS] = BUILD_POLICY(U32),
+ [BATADV_ATTR_MCAST_FLAGS_PRIV] = BUILD_POLICY(U32),
+ [BATADV_ATTR_VLANID] = BUILD_POLICY(U16),
+ [BATADV_ATTR_AGGREGATED_OGMS_ENABLED] = BUILD_POLICY(U8),
+ [BATADV_ATTR_AP_ISOLATION_ENABLED] = BUILD_POLICY(U8),
+ [BATADV_ATTR_ISOLATION_MARK] = BUILD_POLICY(U32),
+ [BATADV_ATTR_ISOLATION_MASK] = BUILD_POLICY(U32),
+ [BATADV_ATTR_BONDING_ENABLED] = BUILD_POLICY(U8),
+ [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = BUILD_POLICY(U8),
+ [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = BUILD_POLICY(U8),
+ [BATADV_ATTR_FRAGMENTATION_ENABLED] = BUILD_POLICY(U8),
+ [BATADV_ATTR_GW_BANDWIDTH_DOWN] = BUILD_POLICY(U32),
+ [BATADV_ATTR_GW_BANDWIDTH_UP] = BUILD_POLICY(U32),
+ [BATADV_ATTR_GW_MODE] = BUILD_POLICY(U8),
+ [BATADV_ATTR_GW_SEL_CLASS] = BUILD_POLICY(U32),
+ [BATADV_ATTR_HOP_PENALTY] = BUILD_POLICY(U8),
+ [BATADV_ATTR_LOG_LEVEL] = BUILD_POLICY(U32),
+ [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED] = BUILD_POLICY(U8),
+ [BATADV_ATTR_MULTICAST_FANOUT] = BUILD_POLICY(U32),
+ [BATADV_ATTR_NETWORK_CODING_ENABLED] = BUILD_POLICY(U8),
+ [BATADV_ATTR_ORIG_INTERVAL] = BUILD_POLICY(U32),
+ [BATADV_ATTR_ELP_INTERVAL] = BUILD_POLICY(U32),
+ [BATADV_ATTR_THROUGHPUT_OVERRIDE] = BUILD_POLICY(U32),
};
/***************** genl fou type systems *****************/
-static const NLType genl_fou_types[] = {
- [FOU_ATTR_PORT] = { .type = NETLINK_TYPE_U16 },
- [FOU_ATTR_AF] = { .type = NETLINK_TYPE_U8 },
- [FOU_ATTR_IPPROTO] = { .type = NETLINK_TYPE_U8 },
- [FOU_ATTR_TYPE] = { .type = NETLINK_TYPE_U8 },
- [FOU_ATTR_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
- [FOU_ATTR_LOCAL_V4] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
- [FOU_ATTR_PEER_V4] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
- [FOU_ATTR_LOCAL_V6] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [FOU_ATTR_PEER_V6] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [FOU_ATTR_PEER_PORT] = { .type = NETLINK_TYPE_U16 },
- [FOU_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy genl_fou_policies[] = {
+ [FOU_ATTR_PORT] = BUILD_POLICY(U16),
+ [FOU_ATTR_AF] = BUILD_POLICY(U8),
+ [FOU_ATTR_IPPROTO] = BUILD_POLICY(U8),
+ [FOU_ATTR_TYPE] = BUILD_POLICY(U8),
+ [FOU_ATTR_REMCSUM_NOPARTIAL] = BUILD_POLICY(FLAG),
+ [FOU_ATTR_LOCAL_V4] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+ [FOU_ATTR_PEER_V4] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+ [FOU_ATTR_LOCAL_V6] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [FOU_ATTR_PEER_V6] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [FOU_ATTR_PEER_PORT] = BUILD_POLICY(U16),
+ [FOU_ATTR_IFINDEX] = BUILD_POLICY(U32),
};
/***************** genl l2tp type systems *****************/
-static const NLType genl_l2tp_types[] = {
- [L2TP_ATTR_PW_TYPE] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_OFFSET] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_DATA_SEQ] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_L2SPEC_TYPE] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_L2SPEC_LEN] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_PROTO_VERSION] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_IFNAME] = { .type = NETLINK_TYPE_STRING },
- [L2TP_ATTR_CONN_ID] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_PEER_CONN_ID] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_SESSION_ID] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_PEER_SESSION_ID] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_VLAN_ID] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_RECV_SEQ] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_SEND_SEQ] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_LNS_MODE] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_USING_IPSEC] = { .type = NETLINK_TYPE_U8 },
- [L2TP_ATTR_FD] = { .type = NETLINK_TYPE_U32 },
- [L2TP_ATTR_IP_SADDR] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
- [L2TP_ATTR_IP_DADDR] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
- [L2TP_ATTR_UDP_SPORT] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_UDP_DPORT] = { .type = NETLINK_TYPE_U16 },
- [L2TP_ATTR_IP6_SADDR] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [L2TP_ATTR_IP6_DADDR] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [L2TP_ATTR_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_FLAG },
- [L2TP_ATTR_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_FLAG },
+static const NLAPolicy genl_l2tp_policies[] = {
+ [L2TP_ATTR_PW_TYPE] = BUILD_POLICY(U16),
+ [L2TP_ATTR_ENCAP_TYPE] = BUILD_POLICY(U16),
+ [L2TP_ATTR_OFFSET] = BUILD_POLICY(U16),
+ [L2TP_ATTR_DATA_SEQ] = BUILD_POLICY(U16),
+ [L2TP_ATTR_L2SPEC_TYPE] = BUILD_POLICY(U8),
+ [L2TP_ATTR_L2SPEC_LEN] = BUILD_POLICY(U8),
+ [L2TP_ATTR_PROTO_VERSION] = BUILD_POLICY(U8),
+ [L2TP_ATTR_IFNAME] = BUILD_POLICY(STRING),
+ [L2TP_ATTR_CONN_ID] = BUILD_POLICY(U32),
+ [L2TP_ATTR_PEER_CONN_ID] = BUILD_POLICY(U32),
+ [L2TP_ATTR_SESSION_ID] = BUILD_POLICY(U32),
+ [L2TP_ATTR_PEER_SESSION_ID] = BUILD_POLICY(U32),
+ [L2TP_ATTR_UDP_CSUM] = BUILD_POLICY(U8),
+ [L2TP_ATTR_VLAN_ID] = BUILD_POLICY(U16),
+ [L2TP_ATTR_RECV_SEQ] = BUILD_POLICY(U8),
+ [L2TP_ATTR_SEND_SEQ] = BUILD_POLICY(U8),
+ [L2TP_ATTR_LNS_MODE] = BUILD_POLICY(U8),
+ [L2TP_ATTR_USING_IPSEC] = BUILD_POLICY(U8),
+ [L2TP_ATTR_FD] = BUILD_POLICY(U32),
+ [L2TP_ATTR_IP_SADDR] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+ [L2TP_ATTR_IP_DADDR] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+ [L2TP_ATTR_UDP_SPORT] = BUILD_POLICY(U16),
+ [L2TP_ATTR_UDP_DPORT] = BUILD_POLICY(U16),
+ [L2TP_ATTR_IP6_SADDR] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [L2TP_ATTR_IP6_DADDR] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [L2TP_ATTR_UDP_ZERO_CSUM6_TX] = BUILD_POLICY(FLAG),
+ [L2TP_ATTR_UDP_ZERO_CSUM6_RX] = BUILD_POLICY(FLAG),
};
/***************** genl macsec type systems *****************/
-static const NLType genl_macsec_rxsc_types[] = {
- [MACSEC_RXSC_ATTR_SCI] = { .type = NETLINK_TYPE_U64 },
+static const NLAPolicy genl_macsec_rxsc_policies[] = {
+ [MACSEC_RXSC_ATTR_SCI] = BUILD_POLICY(U64),
};
-DEFINE_TYPE_SYSTEM(genl_macsec_rxsc);
+DEFINE_POLICY_SET(genl_macsec_rxsc);
-static const NLType genl_macsec_sa_types[] = {
- [MACSEC_SA_ATTR_AN] = { .type = NETLINK_TYPE_U8 },
- [MACSEC_SA_ATTR_ACTIVE] = { .type = NETLINK_TYPE_U8 },
- [MACSEC_SA_ATTR_PN] = { .type = NETLINK_TYPE_U32 },
- [MACSEC_SA_ATTR_KEYID] = { .type = NETLINK_TYPE_BINARY, .size = MACSEC_KEYID_LEN },
- [MACSEC_SA_ATTR_KEY] = { .type = NETLINK_TYPE_BINARY, .size = MACSEC_MAX_KEY_LEN },
+static const NLAPolicy genl_macsec_sa_policies[] = {
+ [MACSEC_SA_ATTR_AN] = BUILD_POLICY(U8),
+ [MACSEC_SA_ATTR_ACTIVE] = BUILD_POLICY(U8),
+ [MACSEC_SA_ATTR_PN] = BUILD_POLICY(U32),
+ [MACSEC_SA_ATTR_KEYID] = BUILD_POLICY_WITH_SIZE(BINARY, MACSEC_KEYID_LEN),
+ [MACSEC_SA_ATTR_KEY] = BUILD_POLICY_WITH_SIZE(BINARY, MACSEC_MAX_KEY_LEN),
};
-DEFINE_TYPE_SYSTEM(genl_macsec_sa);
+DEFINE_POLICY_SET(genl_macsec_sa);
-static const NLType genl_macsec_types[] = {
- [MACSEC_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [MACSEC_ATTR_RXSC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_rxsc_type_system },
- [MACSEC_ATTR_SA_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_macsec_sa_type_system },
+static const NLAPolicy genl_macsec_policies[] = {
+ [MACSEC_ATTR_IFINDEX] = BUILD_POLICY(U32),
+ [MACSEC_ATTR_RXSC_CONFIG] = BUILD_POLICY_NESTED(genl_macsec_rxsc),
+ [MACSEC_ATTR_SA_CONFIG] = BUILD_POLICY_NESTED(genl_macsec_sa),
};
/***************** genl nl80211 type systems *****************/
-static const NLType genl_nl80211_types[] = {
- [NL80211_ATTR_WIPHY] = { .type = NETLINK_TYPE_U32 },
- [NL80211_ATTR_WIPHY_NAME] = { .type = NETLINK_TYPE_STRING },
- [NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [NL80211_ATTR_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
- [NL80211_ATTR_IFTYPE] = { .type = NETLINK_TYPE_U32 },
- [NL80211_ATTR_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [NL80211_ATTR_SSID] = { .type = NETLINK_TYPE_BINARY, .size = IEEE80211_MAX_SSID_LEN },
- [NL80211_ATTR_STATUS_CODE] = { .type = NETLINK_TYPE_U16 },
- [NL80211_ATTR_4ADDR] = { .type = NETLINK_TYPE_U8 },
+static const NLAPolicy genl_nl80211_policies[] = {
+ [NL80211_ATTR_WIPHY] = BUILD_POLICY(U32),
+ [NL80211_ATTR_WIPHY_NAME] = BUILD_POLICY(STRING),
+ [NL80211_ATTR_IFINDEX] = BUILD_POLICY(U32),
+ [NL80211_ATTR_IFNAME] = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ-1),
+ [NL80211_ATTR_IFTYPE] = BUILD_POLICY(U32),
+ [NL80211_ATTR_MAC] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [NL80211_ATTR_SSID] = BUILD_POLICY_WITH_SIZE(BINARY, IEEE80211_MAX_SSID_LEN),
+ [NL80211_ATTR_STATUS_CODE] = BUILD_POLICY(U16),
+ [NL80211_ATTR_4ADDR] = BUILD_POLICY(U8),
};
/***************** genl wireguard type systems *****************/
-static const NLType genl_wireguard_allowedip_types[] = {
- [WGALLOWEDIP_A_FAMILY] = { .type = NETLINK_TYPE_U16 },
- [WGALLOWEDIP_A_IPADDR] = { .type = NETLINK_TYPE_IN_ADDR },
- [WGALLOWEDIP_A_CIDR_MASK] = { .type = NETLINK_TYPE_U8 },
+static const NLAPolicy genl_wireguard_allowedip_policies[] = {
+ [WGALLOWEDIP_A_FAMILY] = BUILD_POLICY(U16),
+ [WGALLOWEDIP_A_IPADDR] = BUILD_POLICY(IN_ADDR),
+ [WGALLOWEDIP_A_CIDR_MASK] = BUILD_POLICY(U8),
};
-DEFINE_TYPE_SYSTEM(genl_wireguard_allowedip);
+DEFINE_POLICY_SET(genl_wireguard_allowedip);
-static const NLType genl_wireguard_peer_types[] = {
- [WGPEER_A_PUBLIC_KEY] = { .type = NETLINK_TYPE_BINARY, .size = WG_KEY_LEN },
- [WGPEER_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [WGPEER_A_PRESHARED_KEY] = { .type = NETLINK_TYPE_BINARY, .size = WG_KEY_LEN },
- [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U16 },
- [WGPEER_A_ENDPOINT] = { .type = NETLINK_TYPE_SOCKADDR },
- [WGPEER_A_ALLOWEDIPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_allowedip_type_system },
+static const NLAPolicy genl_wireguard_peer_policies[] = {
+ [WGPEER_A_PUBLIC_KEY] = BUILD_POLICY_WITH_SIZE(BINARY, WG_KEY_LEN),
+ [WGPEER_A_FLAGS] = BUILD_POLICY(U32),
+ [WGPEER_A_PRESHARED_KEY] = BUILD_POLICY_WITH_SIZE(BINARY, WG_KEY_LEN),
+ [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = BUILD_POLICY(U16),
+ [WGPEER_A_ENDPOINT] = BUILD_POLICY(SOCKADDR),
+ [WGPEER_A_ALLOWEDIPS] = BUILD_POLICY_NESTED(genl_wireguard_allowedip),
};
-DEFINE_TYPE_SYSTEM(genl_wireguard_peer);
+DEFINE_POLICY_SET(genl_wireguard_peer);
-static const NLType genl_wireguard_types[] = {
- [WGDEVICE_A_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [WGDEVICE_A_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
- [WGDEVICE_A_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [WGDEVICE_A_PRIVATE_KEY] = { .type = NETLINK_TYPE_BINARY, .size = WG_KEY_LEN },
- [WGDEVICE_A_LISTEN_PORT] = { .type = NETLINK_TYPE_U16 },
- [WGDEVICE_A_FWMARK] = { .type = NETLINK_TYPE_U32 },
- [WGDEVICE_A_PEERS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system },
+static const NLAPolicy genl_wireguard_policies[] = {
+ [WGDEVICE_A_IFINDEX] = BUILD_POLICY(U32),
+ [WGDEVICE_A_IFNAME] = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ-1),
+ [WGDEVICE_A_FLAGS] = BUILD_POLICY(U32),
+ [WGDEVICE_A_PRIVATE_KEY] = BUILD_POLICY_WITH_SIZE(BINARY, WG_KEY_LEN),
+ [WGDEVICE_A_LISTEN_PORT] = BUILD_POLICY(U16),
+ [WGDEVICE_A_FWMARK] = BUILD_POLICY(U32),
+ [WGDEVICE_A_PEERS] = BUILD_POLICY_NESTED(genl_wireguard_peer),
};
/***************** genl families *****************/
-static const NLTypeSystemUnionElement genl_type_systems[] = {
- { .name = CTRL_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_ctrl), },
- { .name = BATADV_NL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_batadv), },
- { .name = FOU_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_fou), },
- { .name = L2TP_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_l2tp), },
- { .name = MACSEC_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_macsec), },
- { .name = NL80211_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_nl80211), },
- { .name = WG_GENL_NAME, .type_system = TYPE_SYSTEM_FROM_TYPE(genl_wireguard), },
+static const NLAPolicySetUnionElement genl_policy_set_union_elements[] = {
+ BUILD_UNION_ELEMENT_BY_STRING(CTRL_GENL_NAME, genl_ctrl),
+ BUILD_UNION_ELEMENT_BY_STRING(BATADV_NL_NAME, genl_batadv),
+ BUILD_UNION_ELEMENT_BY_STRING(FOU_GENL_NAME, genl_fou),
+ BUILD_UNION_ELEMENT_BY_STRING(L2TP_GENL_NAME, genl_l2tp),
+ BUILD_UNION_ELEMENT_BY_STRING(MACSEC_GENL_NAME, genl_macsec),
+ BUILD_UNION_ELEMENT_BY_STRING(NL80211_GENL_NAME, genl_nl80211),
+ BUILD_UNION_ELEMENT_BY_STRING(WG_GENL_NAME, genl_wireguard),
};
/* This is the root type system union, so match_attribute is not necessary. */
-DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(genl, 0);
+DEFINE_POLICY_SET_UNION(genl, 0);
-const NLTypeSystem *genl_get_type_system_by_name(const char *name) {
- return type_system_union_get_type_system_by_string(&genl_type_system_union, name);
+const NLAPolicySet *genl_get_policy_set_by_name(const char *name) {
+ return policy_set_union_get_policy_set_by_string(&genl_policy_set_union, name);
}
#include "macro.h"
#include "netlink-types.h"
-struct NLType {
- uint16_t type;
+/* C.f. see 'struct nla_policy' at include/net/netlink.h. */
+struct NLAPolicy {
+ NLAType type;
size_t size;
- const NLTypeSystem *type_system;
- const NLTypeSystemUnion *type_system_union;
+ union {
+ const NLAPolicySet *policy_set;
+ const NLAPolicySetUnion *policy_set_union;
+ };
};
-struct NLTypeSystem {
+struct NLAPolicySet {
uint16_t count;
- const NLType *types;
+ const NLAPolicy *policies;
};
-typedef struct NLTypeSystemUnionElement {
+typedef struct NLAPolicySetUnionElement {
union {
- int protocol;
- const char *name;
+ int family; /* used by NETLINK_TYPE_NESTED_UNION_BY_FAMILY */
+ const char *string; /* used by NETLINK_TYPE_NESTED_UNION_BY_STRING */
};
- NLTypeSystem type_system;
-} NLTypeSystemUnionElement;
+ NLAPolicySet policy_set;
+} NLAPolicySetUnionElement;
-struct NLTypeSystemUnion {
+struct NLAPolicySetUnion {
size_t count;
- const NLTypeSystemUnionElement *elements;
- NLMatchType match_type;
- uint16_t match_attribute;
+ const NLAPolicySetUnionElement *elements;
+ uint16_t match_attribute; /* used by NETLINK_TYPE_NESTED_UNION_BY_STRING */
};
-#define TYPE_SYSTEM_FROM_TYPE(name) \
- { .count = ELEMENTSOF(name##_types), .types = name##_types }
-#define DEFINE_TYPE_SYSTEM(name) \
- static const NLTypeSystem name##_type_system = TYPE_SYSTEM_FROM_TYPE(name)
+#define BUILD_POLICY_WITH_SIZE(t, n) \
+ { .type = NETLINK_TYPE_##t, .size = n }
+#define BUILD_POLICY(t) \
+ BUILD_POLICY_WITH_SIZE(t, 0)
+#define BUILD_POLICY_NESTED_WITH_SIZE(name, n) \
+ { .type = NETLINK_TYPE_NESTED, .size = n, .policy_set = &name##_policy_set }
+#define BUILD_POLICY_NESTED(name) \
+ BUILD_POLICY_NESTED_WITH_SIZE(name, 0)
+#define _BUILD_POLICY_NESTED_UNION(name, by) \
+ { .type = NETLINK_TYPE_NESTED_UNION_BY_##by, .policy_set_union = &name##_policy_set_union }
+#define BUILD_POLICY_NESTED_UNION_BY_STRING(name) \
+ _BUILD_POLICY_NESTED_UNION(name, STRING)
+#define BUILD_POLICY_NESTED_UNION_BY_FAMILY(name) \
+ _BUILD_POLICY_NESTED_UNION(name, FAMILY)
+
+#define _BUILD_POLICY_SET(name) \
+ { .count = ELEMENTSOF(name##_policies), .policies = name##_policies }
+#define DEFINE_POLICY_SET(name) \
+ static const NLAPolicySet name##_policy_set = _BUILD_POLICY_SET(name)
+
+# define BUILD_UNION_ELEMENT_BY_STRING(s, name) \
+ { .string = s, .policy_set = _BUILD_POLICY_SET(name) }
+# define BUILD_UNION_ELEMENT_BY_FAMILY(f, name) \
+ { .family = f, .policy_set = _BUILD_POLICY_SET(name) }
-#define _DEFINE_TYPE_SYSTEM_UNION(name, type, attr) \
- static const NLTypeSystemUnion name##_type_system_union = { \
- .count = ELEMENTSOF(name##_type_systems), \
- .elements = name##_type_systems, \
- .match_type = type, \
+#define DEFINE_POLICY_SET_UNION(name, attr) \
+ static const NLAPolicySetUnion name##_policy_set_union = { \
+ .count = ELEMENTSOF(name##_policy_set_union_elements), \
+ .elements = name##_policy_set_union_elements, \
.match_attribute = attr, \
}
-#define DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(name) \
- _DEFINE_TYPE_SYSTEM_UNION(name, NL_MATCH_PROTOCOL, 0)
-#define DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(name, attr) \
- _DEFINE_TYPE_SYSTEM_UNION(name, NL_MATCH_SIBLING, attr)
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <netinet/in.h>
-#include <sys/socket.h>
#include <linux/if.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nfnetlink.h>
#include "netlink-types-internal.h"
-#include "string-table.h"
-static const NLType nfnl_nft_table_types[] = {
- [NFTA_TABLE_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_TABLE_FLAGS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy nfnl_nft_table_policies[] = {
+ [NFTA_TABLE_NAME] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+ [NFTA_TABLE_FLAGS] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(nfnl_nft_table);
+DEFINE_POLICY_SET(nfnl_nft_table);
-static const NLType nfnl_nft_chain_hook_types[] = {
- [NFTA_HOOK_HOOKNUM] = { .type = NETLINK_TYPE_U32 },
- [NFTA_HOOK_PRIORITY] = { .type = NETLINK_TYPE_U32 },
- [NFTA_HOOK_DEV] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
+static const NLAPolicy nfnl_nft_chain_hook_policies[] = {
+ [NFTA_HOOK_HOOKNUM] = BUILD_POLICY(U32),
+ [NFTA_HOOK_PRIORITY] = BUILD_POLICY(U32),
+ [NFTA_HOOK_DEV] = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ - 1),
};
-DEFINE_TYPE_SYSTEM(nfnl_nft_chain_hook);
+DEFINE_POLICY_SET(nfnl_nft_chain_hook);
-static const NLType nfnl_nft_chain_types[] = {
- [NFTA_CHAIN_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_CHAIN_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_CHAIN_HOOK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_hook_type_system },
- [NFTA_CHAIN_TYPE] = { .type = NETLINK_TYPE_STRING, .size = 16 },
- [NFTA_CHAIN_FLAGS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy nfnl_nft_chain_policies[] = {
+ [NFTA_CHAIN_TABLE] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+ [NFTA_CHAIN_NAME] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+ [NFTA_CHAIN_HOOK] = BUILD_POLICY_NESTED(nfnl_nft_chain_hook),
+ [NFTA_CHAIN_TYPE] = BUILD_POLICY_WITH_SIZE(STRING, 16),
+ [NFTA_CHAIN_FLAGS] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(nfnl_nft_chain);
+DEFINE_POLICY_SET(nfnl_nft_chain);
-static const NLType nfnl_nft_expr_meta_types[] = {
- [NFTA_META_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_META_KEY] = { .type = NETLINK_TYPE_U32 },
- [NFTA_META_SREG] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy nfnl_nft_expr_meta_policies[] = {
+ [NFTA_META_DREG] = BUILD_POLICY(U32),
+ [NFTA_META_KEY] = BUILD_POLICY(U32),
+ [NFTA_META_SREG] = BUILD_POLICY(U32),
};
-static const NLType nfnl_nft_expr_payload_types[] = {
- [NFTA_PAYLOAD_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_PAYLOAD_BASE] = { .type = NETLINK_TYPE_U32 },
- [NFTA_PAYLOAD_OFFSET] = { .type = NETLINK_TYPE_U32 },
- [NFTA_PAYLOAD_LEN] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy nfnl_nft_expr_payload_policies[] = {
+ [NFTA_PAYLOAD_DREG] = BUILD_POLICY(U32),
+ [NFTA_PAYLOAD_BASE] = BUILD_POLICY(U32),
+ [NFTA_PAYLOAD_OFFSET] = BUILD_POLICY(U32),
+ [NFTA_PAYLOAD_LEN] = BUILD_POLICY(U32),
};
-static const NLType nfnl_nft_expr_nat_types[] = {
- [NFTA_NAT_TYPE] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_FAMILY] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_REG_ADDR_MIN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_REG_ADDR_MAX] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
- [NFTA_NAT_FLAGS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy nfnl_nft_expr_nat_policies[] = {
+ [NFTA_NAT_TYPE] = BUILD_POLICY(U32),
+ [NFTA_NAT_FAMILY] = BUILD_POLICY(U32),
+ [NFTA_NAT_REG_ADDR_MIN] = BUILD_POLICY(U32),
+ [NFTA_NAT_REG_ADDR_MAX] = BUILD_POLICY(U32),
+ [NFTA_NAT_REG_PROTO_MIN] = BUILD_POLICY(U32),
+ [NFTA_NAT_REG_PROTO_MAX] = BUILD_POLICY(U32),
+ [NFTA_NAT_FLAGS] = BUILD_POLICY(U32),
};
-static const NLType nfnl_nft_data_types[] = {
+static const NLAPolicy nfnl_nft_data_policies[] = {
[NFTA_DATA_VALUE] = { .type = NETLINK_TYPE_BINARY },
};
-DEFINE_TYPE_SYSTEM(nfnl_nft_data);
+DEFINE_POLICY_SET(nfnl_nft_data);
-static const NLType nfnl_nft_expr_bitwise_types[] = {
- [NFTA_BITWISE_SREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_BITWISE_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_BITWISE_LEN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_BITWISE_MASK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
- [NFTA_BITWISE_XOR] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+static const NLAPolicy nfnl_nft_expr_bitwise_policies[] = {
+ [NFTA_BITWISE_SREG] = BUILD_POLICY(U32),
+ [NFTA_BITWISE_DREG] = BUILD_POLICY(U32),
+ [NFTA_BITWISE_LEN] = BUILD_POLICY(U32),
+ [NFTA_BITWISE_MASK] = BUILD_POLICY_NESTED(nfnl_nft_data),
+ [NFTA_BITWISE_XOR] = BUILD_POLICY_NESTED(nfnl_nft_data),
};
-static const NLType nfnl_nft_expr_cmp_types[] = {
- [NFTA_CMP_SREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_CMP_OP] = { .type = NETLINK_TYPE_U32 },
- [NFTA_CMP_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
+static const NLAPolicy nfnl_nft_expr_cmp_policies[] = {
+ [NFTA_CMP_SREG] = BUILD_POLICY(U32),
+ [NFTA_CMP_OP] = BUILD_POLICY(U32),
+ [NFTA_CMP_DATA] = BUILD_POLICY_NESTED(nfnl_nft_data),
};
-static const NLType nfnl_nft_expr_fib_types[] = {
- [NFTA_FIB_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_FIB_RESULT] = { .type = NETLINK_TYPE_U32 },
- [NFTA_FIB_FLAGS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy nfnl_nft_expr_fib_policies[] = {
+ [NFTA_FIB_DREG] = BUILD_POLICY(U32),
+ [NFTA_FIB_RESULT] = BUILD_POLICY(U32),
+ [NFTA_FIB_FLAGS] = BUILD_POLICY(U32),
};
-static const NLType nfnl_nft_expr_lookup_types[] = {
+static const NLAPolicy nfnl_nft_expr_lookup_policies[] = {
[NFTA_LOOKUP_SET] = { .type = NETLINK_TYPE_STRING },
- [NFTA_LOOKUP_SREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_LOOKUP_DREG] = { .type = NETLINK_TYPE_U32 },
- [NFTA_LOOKUP_FLAGS] = { .type = NETLINK_TYPE_U32 },
+ [NFTA_LOOKUP_SREG] = BUILD_POLICY(U32),
+ [NFTA_LOOKUP_DREG] = BUILD_POLICY(U32),
+ [NFTA_LOOKUP_FLAGS] = BUILD_POLICY(U32),
};
-static const NLType nfnl_nft_expr_masq_types[] = {
- [NFTA_MASQ_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [NFTA_MASQ_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_MASQ_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy nfnl_nft_expr_masq_policies[] = {
+ [NFTA_MASQ_FLAGS] = BUILD_POLICY(U32),
+ [NFTA_MASQ_REG_PROTO_MIN] = BUILD_POLICY(U32),
+ [NFTA_MASQ_REG_PROTO_MAX] = BUILD_POLICY(U32),
};
-static const NLTypeSystemUnionElement nfnl_expr_data_type_systems[] = {
- { .name = "bitwise", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_bitwise), },
- { .name = "cmp", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_cmp), },
- { .name = "fib", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_fib), },
- { .name = "lookup", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_lookup), },
- { .name = "masq", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_masq), },
- { .name = "meta", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_meta), },
- { .name = "nat", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_nat), },
- { .name = "payload", .type_system = TYPE_SYSTEM_FROM_TYPE(nfnl_nft_expr_payload), },
+static const NLAPolicySetUnionElement nfnl_expr_data_policy_set_union_elements[] = {
+ BUILD_UNION_ELEMENT_BY_STRING("bitwise", nfnl_nft_expr_bitwise),
+ BUILD_UNION_ELEMENT_BY_STRING("cmp", nfnl_nft_expr_cmp),
+ BUILD_UNION_ELEMENT_BY_STRING("fib", nfnl_nft_expr_fib),
+ BUILD_UNION_ELEMENT_BY_STRING("lookup", nfnl_nft_expr_lookup),
+ BUILD_UNION_ELEMENT_BY_STRING("masq", nfnl_nft_expr_masq),
+ BUILD_UNION_ELEMENT_BY_STRING("meta", nfnl_nft_expr_meta),
+ BUILD_UNION_ELEMENT_BY_STRING("nat", nfnl_nft_expr_nat),
+ BUILD_UNION_ELEMENT_BY_STRING("payload", nfnl_nft_expr_payload),
};
-DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(nfnl_expr_data, NFTA_EXPR_NAME);
+DEFINE_POLICY_SET_UNION(nfnl_expr_data, NFTA_EXPR_NAME);
-static const NLType nfnl_nft_rule_expr_types[] = {
- [NFTA_EXPR_NAME] = { .type = NETLINK_TYPE_STRING, .size = 16 },
- [NFTA_EXPR_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &nfnl_expr_data_type_system_union },
+static const NLAPolicy nfnl_nft_rule_expr_policies[] = {
+ [NFTA_EXPR_NAME] = BUILD_POLICY_WITH_SIZE(STRING, 16),
+ [NFTA_EXPR_DATA] = BUILD_POLICY_NESTED_UNION_BY_STRING(nfnl_expr_data),
};
-DEFINE_TYPE_SYSTEM(nfnl_nft_rule_expr);
+DEFINE_POLICY_SET(nfnl_nft_rule_expr);
-static const NLType nfnl_nft_rule_types[] = {
- [NFTA_RULE_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_RULE_CHAIN] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_RULE_EXPRESSIONS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_expr_type_system }
+static const NLAPolicy nfnl_nft_rule_policies[] = {
+ [NFTA_RULE_TABLE] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+ [NFTA_RULE_CHAIN] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+ [NFTA_RULE_EXPRESSIONS] = BUILD_POLICY_NESTED(nfnl_nft_rule_expr),
};
-DEFINE_TYPE_SYSTEM(nfnl_nft_rule);
+DEFINE_POLICY_SET(nfnl_nft_rule);
-static const NLType nfnl_nft_set_types[] = {
- [NFTA_SET_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_SET_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_SET_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_KEY_TYPE] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_KEY_LEN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_DATA_TYPE] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_DATA_LEN] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_POLICY] = { .type = NETLINK_TYPE_U32 },
- [NFTA_SET_ID] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy nfnl_nft_set_policies[] = {
+ [NFTA_SET_TABLE] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+ [NFTA_SET_NAME] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+ [NFTA_SET_FLAGS] = BUILD_POLICY(U32),
+ [NFTA_SET_KEY_TYPE] = BUILD_POLICY(U32),
+ [NFTA_SET_KEY_LEN] = BUILD_POLICY(U32),
+ [NFTA_SET_DATA_TYPE] = BUILD_POLICY(U32),
+ [NFTA_SET_DATA_LEN] = BUILD_POLICY(U32),
+ [NFTA_SET_POLICY] = BUILD_POLICY(U32),
+ [NFTA_SET_ID] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(nfnl_nft_set);
+DEFINE_POLICY_SET(nfnl_nft_set);
-static const NLType nfnl_nft_setelem_types[] = {
- [NFTA_SET_ELEM_KEY] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
- [NFTA_SET_ELEM_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
- [NFTA_SET_ELEM_FLAGS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy nfnl_nft_setelem_policies[] = {
+ [NFTA_SET_ELEM_KEY] = BUILD_POLICY_NESTED(nfnl_nft_data),
+ [NFTA_SET_ELEM_DATA] = BUILD_POLICY_NESTED(nfnl_nft_data),
+ [NFTA_SET_ELEM_FLAGS] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(nfnl_nft_setelem);
+DEFINE_POLICY_SET(nfnl_nft_setelem);
-static const NLType nfnl_nft_setelem_list_types[] = {
- [NFTA_SET_ELEM_LIST_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_SET_ELEM_LIST_SET] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
- [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_type_system },
+static const NLAPolicy nfnl_nft_setelem_list_policies[] = {
+ [NFTA_SET_ELEM_LIST_TABLE] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+ [NFTA_SET_ELEM_LIST_SET] = BUILD_POLICY_WITH_SIZE(STRING, NFT_TABLE_MAXNAMELEN - 1),
+ [NFTA_SET_ELEM_LIST_ELEMENTS] = BUILD_POLICY_NESTED(nfnl_nft_setelem),
};
-DEFINE_TYPE_SYSTEM(nfnl_nft_setelem_list);
+DEFINE_POLICY_SET(nfnl_nft_setelem_list);
-static const NLType nfnl_subsys_nft_types [] = {
- [NFT_MSG_DELTABLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWTABLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWCHAIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWSET] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_set_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_NEWSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
- [NFT_MSG_DELSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
+static const NLAPolicy nfnl_subsys_nft_policies[] = {
+ [NFT_MSG_DELTABLE] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_table, sizeof(struct nfgenmsg)),
+ [NFT_MSG_NEWTABLE] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_table, sizeof(struct nfgenmsg)),
+ [NFT_MSG_NEWCHAIN] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_chain, sizeof(struct nfgenmsg)),
+ [NFT_MSG_NEWRULE] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_rule, sizeof(struct nfgenmsg)),
+ [NFT_MSG_NEWSET] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_set, sizeof(struct nfgenmsg)),
+ [NFT_MSG_NEWSETELEM] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_setelem_list, sizeof(struct nfgenmsg)),
+ [NFT_MSG_DELSETELEM] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_nft_setelem_list, sizeof(struct nfgenmsg)),
};
-DEFINE_TYPE_SYSTEM(nfnl_subsys_nft);
+DEFINE_POLICY_SET(nfnl_subsys_nft);
-static const NLType nfnl_msg_batch_types [] = {
- [NFNL_BATCH_GENID] = { .type = NETLINK_TYPE_U32 }
+static const NLAPolicy nfnl_msg_batch_policies[] = {
+ [NFNL_BATCH_GENID] = BUILD_POLICY(U32)
};
-DEFINE_TYPE_SYSTEM(nfnl_msg_batch);
+DEFINE_POLICY_SET(nfnl_msg_batch);
-static const NLType nfnl_subsys_none_types[] = {
- [NFNL_MSG_BATCH_BEGIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
- [NFNL_MSG_BATCH_END] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
+static const NLAPolicy nfnl_subsys_none_policies[] = {
+ [NFNL_MSG_BATCH_BEGIN] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_msg_batch, sizeof(struct nfgenmsg)),
+ [NFNL_MSG_BATCH_END] = BUILD_POLICY_NESTED_WITH_SIZE(nfnl_msg_batch, sizeof(struct nfgenmsg)),
};
-DEFINE_TYPE_SYSTEM(nfnl_subsys_none);
+DEFINE_POLICY_SET(nfnl_subsys_none);
-static const NLType nfnl_types[] = {
- [NFNL_SUBSYS_NONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_subsys_none_type_system },
- [NFNL_SUBSYS_NFTABLES] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_subsys_nft_type_system },
+static const NLAPolicy nfnl_policies[] = {
+ [NFNL_SUBSYS_NONE] = BUILD_POLICY_NESTED(nfnl_subsys_none),
+ [NFNL_SUBSYS_NFTABLES] = BUILD_POLICY_NESTED(nfnl_subsys_nft),
};
-DEFINE_TYPE_SYSTEM(nfnl);
+DEFINE_POLICY_SET(nfnl);
-const NLType *nfnl_get_type(uint16_t nlmsg_type) {
- const NLTypeSystem *subsys;
+const NLAPolicy *nfnl_get_policy(uint16_t nlmsg_type) {
+ const NLAPolicySet *subsys;
- subsys = type_system_get_type_system(&nfnl_type_system, nlmsg_type >> 8);
+ subsys = policy_set_get_policy_set(&nfnl_policy_set, NFNL_SUBSYS_ID(nlmsg_type));
if (!subsys)
return NULL;
- return type_system_get_type(subsys, nlmsg_type & ((1U << 8) - 1));
+ return policy_set_get_policy(subsys, NFNL_MSG_TYPE(nlmsg_type));
}
#include <linux/veth.h>
#include <linux/wireguard.h>
-#include "sd-netlink.h"
-
#include "missing_network.h"
#include "netlink-types-internal.h"
-#include "string-table.h"
enum {
BOND_ARP_TARGETS_0,
assert_cc(_BOND_ARP_TARGETS_MAX == BOND_MAX_ARP_TARGETS);
-static const NLTypeSystem rtnl_link_type_system;
-
-static const NLType rtnl_link_info_data_bareudp_types[] = {
- [IFLA_BAREUDP_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BAREUDP_ETHERTYPE] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BAREUDP_SRCPORT_MIN] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BAREUDP_MULTIPROTO_MODE] = { .type = NETLINK_TYPE_FLAG },
-};
-
-static const NLType rtnl_link_info_data_batadv_types[] = {
- [IFLA_BATADV_ALGO_NAME] = { .type = NETLINK_TYPE_STRING, .size = 20 },
-};
-
-static const NLType rtnl_bond_arp_ip_target_types[] = {
- [BOND_ARP_TARGETS_0] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_1] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_2] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_3] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_4] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_5] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_6] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_7] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_8] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_9] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_10] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_11] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_12] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_13] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_14] = { .type = NETLINK_TYPE_U32 },
- [BOND_ARP_TARGETS_15] = { .type = NETLINK_TYPE_U32 },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_bond_arp_ip_target);
-
-static const NLType rtnl_bond_ad_info_types[] = {
- [IFLA_BOND_AD_INFO_AGGREGATOR] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BOND_AD_INFO_NUM_PORTS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BOND_AD_INFO_ACTOR_KEY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BOND_AD_INFO_PARTNER_KEY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BOND_AD_INFO_PARTNER_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_bond_ad_info);
-
-static const NLType rtnl_link_info_data_bond_types[] = {
- [IFLA_BOND_MODE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_ACTIVE_SLAVE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_MIIMON] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_UPDELAY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_DOWNDELAY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_USE_CARRIER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_ARP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_ARP_IP_TARGET] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_arp_ip_target_type_system },
- [IFLA_BOND_ARP_VALIDATE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_PRIMARY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_PRIMARY_RESELECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_FAIL_OVER_MAC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_RESEND_IGMP] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_NUM_PEER_NOTIF] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_ALL_SLAVES_ACTIVE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_MIN_LINKS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_LP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_PACKETS_PER_SLAVE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BOND_AD_LACP_RATE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_AD_SELECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_AD_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bond_ad_info_type_system },
- [IFLA_BOND_AD_ACTOR_SYS_PRIO] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BOND_AD_USER_PORT_KEY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BOND_AD_ACTOR_SYSTEM] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BOND_PEER_NOTIF_DELAY] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_bridge_types[] = {
- [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_ROOT_ID] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_bridge_id) },
- [IFLA_BR_BRIDGE_ID] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_bridge_id) },
- [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BR_VLAN_STATS_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_STATS_ENABLED] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_IGMP_VERSION] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MCAST_MLD_VERSION] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BR_MULTI_BOOLOPT] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct br_boolopt_multi) },
-};
-
-static const NLType rtnl_link_info_data_can_types[] = {
- [IFLA_CAN_BITTIMING] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_bittiming) },
- [IFLA_CAN_BITTIMING_CONST] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_bittiming_const) },
- [IFLA_CAN_CLOCK] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_clock) },
- [IFLA_CAN_STATE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_CAN_CTRLMODE] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_ctrlmode) },
- [IFLA_CAN_RESTART_MS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_CAN_RESTART] = { .type = NETLINK_TYPE_U32 },
- [IFLA_CAN_BERR_COUNTER] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_berr_counter) },
- [IFLA_CAN_DATA_BITTIMING] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_bittiming) },
- [IFLA_CAN_DATA_BITTIMING_CONST] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_bittiming_const) },
- [IFLA_CAN_TERMINATION] = { .type = NETLINK_TYPE_U16 },
- [IFLA_CAN_TERMINATION_CONST] = { .type = NETLINK_TYPE_BINARY }, /* size = termination_const_cnt * sizeof(u16) */
- [IFLA_CAN_BITRATE_CONST] = { .type = NETLINK_TYPE_BINARY }, /* size = bitrate_const_cnt * sizeof(u32) */
- [IFLA_CAN_DATA_BITRATE_CONST] = { .type = NETLINK_TYPE_BINARY }, /* size = data_bitrate_const_cnt * sizeof(u32) */
- [IFLA_CAN_BITRATE_MAX] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_geneve_types[] = {
- [IFLA_GENEVE_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GENEVE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
- [IFLA_GENEVE_TTL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_TOS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GENEVE_COLLECT_METADATA] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_GENEVE_REMOTE6] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [IFLA_GENEVE_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_LABEL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GENEVE_TTL_INHERIT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GENEVE_DF] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_link_info_data_gre_types[] = {
- [IFLA_GRE_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_IFLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_OFLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_IKEY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_OKEY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_GRE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_GRE_TTL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_TOS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_PMTUDISC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_ENCAP_LIMIT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_FLOWINFO] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_GRE_COLLECT_METADATA] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_GRE_IGNORE_DF] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_FWMARK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_ERSPAN_INDEX] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GRE_ERSPAN_VER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_ERSPAN_DIR] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GRE_ERSPAN_HWID] = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_link_info_data_ipoib_types[] = {
- [IFLA_IPOIB_PKEY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPOIB_MODE] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPOIB_UMCAST] = { .type = NETLINK_TYPE_U16 },
+static const NLAPolicySet rtnl_link_policy_set;
+
+static const NLAPolicy rtnl_link_info_data_bareudp_policies[] = {
+ [IFLA_BAREUDP_PORT] = BUILD_POLICY(U16),
+ [IFLA_BAREUDP_ETHERTYPE] = BUILD_POLICY(U16),
+ [IFLA_BAREUDP_SRCPORT_MIN] = BUILD_POLICY(U16),
+ [IFLA_BAREUDP_MULTIPROTO_MODE] = BUILD_POLICY(FLAG),
+};
+
+static const NLAPolicy rtnl_link_info_data_batadv_policies[] = {
+ [IFLA_BATADV_ALGO_NAME] = BUILD_POLICY_WITH_SIZE(STRING, 20),
+};
+
+static const NLAPolicy rtnl_bond_arp_ip_target_policies[] = {
+ [BOND_ARP_TARGETS_0] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_1] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_2] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_3] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_4] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_5] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_6] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_7] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_8] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_9] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_10] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_11] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_12] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_13] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_14] = BUILD_POLICY(U32),
+ [BOND_ARP_TARGETS_15] = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_bond_arp_ip_target);
+
+static const NLAPolicy rtnl_bond_ad_info_policies[] = {
+ [IFLA_BOND_AD_INFO_AGGREGATOR] = BUILD_POLICY(U16),
+ [IFLA_BOND_AD_INFO_NUM_PORTS] = BUILD_POLICY(U16),
+ [IFLA_BOND_AD_INFO_ACTOR_KEY] = BUILD_POLICY(U16),
+ [IFLA_BOND_AD_INFO_PARTNER_KEY] = BUILD_POLICY(U16),
+ [IFLA_BOND_AD_INFO_PARTNER_MAC] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+};
+
+DEFINE_POLICY_SET(rtnl_bond_ad_info);
+
+static const NLAPolicy rtnl_link_info_data_bond_policies[] = {
+ [IFLA_BOND_MODE] = BUILD_POLICY(U8),
+ [IFLA_BOND_ACTIVE_SLAVE] = BUILD_POLICY(U32),
+ [IFLA_BOND_MIIMON] = BUILD_POLICY(U32),
+ [IFLA_BOND_UPDELAY] = BUILD_POLICY(U32),
+ [IFLA_BOND_DOWNDELAY] = BUILD_POLICY(U32),
+ [IFLA_BOND_USE_CARRIER] = BUILD_POLICY(U8),
+ [IFLA_BOND_ARP_INTERVAL] = BUILD_POLICY(U32),
+ [IFLA_BOND_ARP_IP_TARGET] = BUILD_POLICY_NESTED(rtnl_bond_arp_ip_target),
+ [IFLA_BOND_ARP_VALIDATE] = BUILD_POLICY(U32),
+ [IFLA_BOND_ARP_ALL_TARGETS] = BUILD_POLICY(U32),
+ [IFLA_BOND_PRIMARY] = BUILD_POLICY(U32),
+ [IFLA_BOND_PRIMARY_RESELECT] = BUILD_POLICY(U8),
+ [IFLA_BOND_FAIL_OVER_MAC] = BUILD_POLICY(U8),
+ [IFLA_BOND_XMIT_HASH_POLICY] = BUILD_POLICY(U8),
+ [IFLA_BOND_RESEND_IGMP] = BUILD_POLICY(U32),
+ [IFLA_BOND_NUM_PEER_NOTIF] = BUILD_POLICY(U8),
+ [IFLA_BOND_ALL_SLAVES_ACTIVE] = BUILD_POLICY(U8),
+ [IFLA_BOND_MIN_LINKS] = BUILD_POLICY(U32),
+ [IFLA_BOND_LP_INTERVAL] = BUILD_POLICY(U32),
+ [IFLA_BOND_PACKETS_PER_SLAVE] = BUILD_POLICY(U32),
+ [IFLA_BOND_AD_LACP_RATE] = BUILD_POLICY(U8),
+ [IFLA_BOND_AD_SELECT] = BUILD_POLICY(U8),
+ [IFLA_BOND_AD_INFO] = BUILD_POLICY_NESTED(rtnl_bond_ad_info),
+ [IFLA_BOND_AD_ACTOR_SYS_PRIO] = BUILD_POLICY(U16),
+ [IFLA_BOND_AD_USER_PORT_KEY] = BUILD_POLICY(U16),
+ [IFLA_BOND_AD_ACTOR_SYSTEM] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [IFLA_BOND_TLB_DYNAMIC_LB] = BUILD_POLICY(U8),
+ [IFLA_BOND_PEER_NOTIF_DELAY] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_bridge_policies[] = {
+ [IFLA_BR_FORWARD_DELAY] = BUILD_POLICY(U32),
+ [IFLA_BR_HELLO_TIME] = BUILD_POLICY(U32),
+ [IFLA_BR_MAX_AGE] = BUILD_POLICY(U32),
+ [IFLA_BR_AGEING_TIME] = BUILD_POLICY(U32),
+ [IFLA_BR_STP_STATE] = BUILD_POLICY(U32),
+ [IFLA_BR_PRIORITY] = BUILD_POLICY(U16),
+ [IFLA_BR_VLAN_FILTERING] = BUILD_POLICY(U8),
+ [IFLA_BR_VLAN_PROTOCOL] = BUILD_POLICY(U16),
+ [IFLA_BR_GROUP_FWD_MASK] = BUILD_POLICY(U16),
+ [IFLA_BR_ROOT_ID] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_bridge_id)),
+ [IFLA_BR_BRIDGE_ID] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_bridge_id)),
+ [IFLA_BR_ROOT_PORT] = BUILD_POLICY(U16),
+ [IFLA_BR_ROOT_PATH_COST] = BUILD_POLICY(U32),
+ [IFLA_BR_TOPOLOGY_CHANGE] = BUILD_POLICY(U8),
+ [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = BUILD_POLICY(U8),
+ [IFLA_BR_HELLO_TIMER] = BUILD_POLICY(U64),
+ [IFLA_BR_TCN_TIMER] = BUILD_POLICY(U64),
+ [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = BUILD_POLICY(U64),
+ [IFLA_BR_GC_TIMER] = BUILD_POLICY(U64),
+ [IFLA_BR_GROUP_ADDR] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [IFLA_BR_FDB_FLUSH] = BUILD_POLICY(FLAG),
+ [IFLA_BR_MCAST_ROUTER] = BUILD_POLICY(U8),
+ [IFLA_BR_MCAST_SNOOPING] = BUILD_POLICY(U8),
+ [IFLA_BR_MCAST_QUERY_USE_IFADDR] = BUILD_POLICY(U8),
+ [IFLA_BR_MCAST_QUERIER] = BUILD_POLICY(U8),
+ [IFLA_BR_MCAST_HASH_ELASTICITY] = BUILD_POLICY(U32),
+ [IFLA_BR_MCAST_HASH_MAX] = BUILD_POLICY(U32),
+ [IFLA_BR_MCAST_LAST_MEMBER_CNT] = BUILD_POLICY(U32),
+ [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = BUILD_POLICY(U32),
+ [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = BUILD_POLICY(U64),
+ [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = BUILD_POLICY(U64),
+ [IFLA_BR_MCAST_QUERIER_INTVL] = BUILD_POLICY(U64),
+ [IFLA_BR_MCAST_QUERY_INTVL] = BUILD_POLICY(U64),
+ [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = BUILD_POLICY(U64),
+ [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = BUILD_POLICY(U64),
+ [IFLA_BR_NF_CALL_IPTABLES] = BUILD_POLICY(U8),
+ [IFLA_BR_NF_CALL_IP6TABLES] = BUILD_POLICY(U8),
+ [IFLA_BR_NF_CALL_ARPTABLES] = BUILD_POLICY(U8),
+ [IFLA_BR_VLAN_DEFAULT_PVID] = BUILD_POLICY(U16),
+ [IFLA_BR_VLAN_STATS_ENABLED] = BUILD_POLICY(U8),
+ [IFLA_BR_MCAST_STATS_ENABLED] = BUILD_POLICY(U8),
+ [IFLA_BR_MCAST_IGMP_VERSION] = BUILD_POLICY(U8),
+ [IFLA_BR_MCAST_MLD_VERSION] = BUILD_POLICY(U8),
+ [IFLA_BR_VLAN_STATS_PER_PORT] = BUILD_POLICY(U8),
+ [IFLA_BR_MULTI_BOOLOPT] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct br_boolopt_multi)),
+};
+
+static const NLAPolicy rtnl_link_info_data_can_policies[] = {
+ [IFLA_CAN_BITTIMING] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_bittiming)),
+ [IFLA_CAN_BITTIMING_CONST] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_bittiming_const)),
+ [IFLA_CAN_CLOCK] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_clock)),
+ [IFLA_CAN_STATE] = BUILD_POLICY(U32),
+ [IFLA_CAN_CTRLMODE] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_ctrlmode)),
+ [IFLA_CAN_RESTART_MS] = BUILD_POLICY(U32),
+ [IFLA_CAN_RESTART] = BUILD_POLICY(U32),
+ [IFLA_CAN_BERR_COUNTER] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_berr_counter)),
+ [IFLA_CAN_DATA_BITTIMING] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_bittiming)),
+ [IFLA_CAN_DATA_BITTIMING_CONST] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_bittiming_const)),
+ [IFLA_CAN_TERMINATION] = BUILD_POLICY(U16),
+ [IFLA_CAN_TERMINATION_CONST] = BUILD_POLICY(BINARY), /* size = termination_const_cnt * sizeof(u16) */
+ [IFLA_CAN_BITRATE_CONST] = BUILD_POLICY(BINARY), /* size = bitrate_const_cnt * sizeof(u32) */
+ [IFLA_CAN_DATA_BITRATE_CONST] = BUILD_POLICY(BINARY), /* size = data_bitrate_const_cnt * sizeof(u32) */
+ [IFLA_CAN_BITRATE_MAX] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_geneve_policies[] = {
+ [IFLA_GENEVE_ID] = BUILD_POLICY(U32),
+ [IFLA_GENEVE_REMOTE] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+ [IFLA_GENEVE_TTL] = BUILD_POLICY(U8),
+ [IFLA_GENEVE_TOS] = BUILD_POLICY(U8),
+ [IFLA_GENEVE_PORT] = BUILD_POLICY(U16),
+ [IFLA_GENEVE_COLLECT_METADATA] = BUILD_POLICY(FLAG),
+ [IFLA_GENEVE_REMOTE6] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [IFLA_GENEVE_UDP_CSUM] = BUILD_POLICY(U8),
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = BUILD_POLICY(U8),
+ [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = BUILD_POLICY(U8),
+ [IFLA_GENEVE_LABEL] = BUILD_POLICY(U32),
+ [IFLA_GENEVE_TTL_INHERIT] = BUILD_POLICY(U8),
+ [IFLA_GENEVE_DF] = BUILD_POLICY(U8),
+};
+
+static const NLAPolicy rtnl_link_info_data_gre_policies[] = {
+ [IFLA_GRE_LINK] = BUILD_POLICY(U32),
+ [IFLA_GRE_IFLAGS] = BUILD_POLICY(U16),
+ [IFLA_GRE_OFLAGS] = BUILD_POLICY(U16),
+ [IFLA_GRE_IKEY] = BUILD_POLICY(U32),
+ [IFLA_GRE_OKEY] = BUILD_POLICY(U32),
+ [IFLA_GRE_LOCAL] = BUILD_POLICY(IN_ADDR),
+ [IFLA_GRE_REMOTE] = BUILD_POLICY(IN_ADDR),
+ [IFLA_GRE_TTL] = BUILD_POLICY(U8),
+ [IFLA_GRE_TOS] = BUILD_POLICY(U8),
+ [IFLA_GRE_PMTUDISC] = BUILD_POLICY(U8),
+ [IFLA_GRE_ENCAP_LIMIT] = BUILD_POLICY(U8),
+ [IFLA_GRE_FLOWINFO] = BUILD_POLICY(U32),
+ [IFLA_GRE_FLAGS] = BUILD_POLICY(U32),
+ [IFLA_GRE_ENCAP_TYPE] = BUILD_POLICY(U16),
+ [IFLA_GRE_ENCAP_FLAGS] = BUILD_POLICY(U16),
+ [IFLA_GRE_ENCAP_SPORT] = BUILD_POLICY(U16),
+ [IFLA_GRE_ENCAP_DPORT] = BUILD_POLICY(U16),
+ [IFLA_GRE_COLLECT_METADATA] = BUILD_POLICY(FLAG),
+ [IFLA_GRE_IGNORE_DF] = BUILD_POLICY(U8),
+ [IFLA_GRE_FWMARK] = BUILD_POLICY(U32),
+ [IFLA_GRE_ERSPAN_INDEX] = BUILD_POLICY(U32),
+ [IFLA_GRE_ERSPAN_VER] = BUILD_POLICY(U8),
+ [IFLA_GRE_ERSPAN_DIR] = BUILD_POLICY(U8),
+ [IFLA_GRE_ERSPAN_HWID] = BUILD_POLICY(U16),
+};
+
+static const NLAPolicy rtnl_link_info_data_ipoib_policies[] = {
+ [IFLA_IPOIB_PKEY] = BUILD_POLICY(U16),
+ [IFLA_IPOIB_MODE] = BUILD_POLICY(U16),
+ [IFLA_IPOIB_UMCAST] = BUILD_POLICY(U16),
};
/* IFLA_IPTUN_ attributes are used in ipv4/ipip.c, ipv6/ip6_tunnel.c, and ipv6/sit.c. And unfortunately,
* IFLA_IPTUN_FLAGS is used with different types, ugh... */
-#define DEFINE_IPTUN_TYPES(name, flags_type) \
- static const NLType rtnl_link_info_data_##name##_types[] = { \
- [IFLA_IPTUN_LINK] = { .type = NETLINK_TYPE_U32 }, \
- [IFLA_IPTUN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, \
- [IFLA_IPTUN_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, \
- [IFLA_IPTUN_TTL] = { .type = NETLINK_TYPE_U8 }, \
- [IFLA_IPTUN_TOS] = { .type = NETLINK_TYPE_U8 }, \
- [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NETLINK_TYPE_U8 }, \
- [IFLA_IPTUN_FLOWINFO] = { .type = NETLINK_TYPE_U32 }, \
- [IFLA_IPTUN_FLAGS] = { .type = flags_type }, \
- [IFLA_IPTUN_PROTO] = { .type = NETLINK_TYPE_U8 }, \
- [IFLA_IPTUN_PMTUDISC] = { .type = NETLINK_TYPE_U8 }, \
- [IFLA_IPTUN_6RD_PREFIX] = { .type = NETLINK_TYPE_IN_ADDR, \
- .size = sizeof(struct in6_addr) }, \
- [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NETLINK_TYPE_U32 }, \
- [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, \
- [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NETLINK_TYPE_U16 }, \
- [IFLA_IPTUN_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, \
- [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NETLINK_TYPE_U16 }, \
- [IFLA_IPTUN_ENCAP_SPORT] = { .type = NETLINK_TYPE_U16 }, \
- [IFLA_IPTUN_ENCAP_DPORT] = { .type = NETLINK_TYPE_U16 }, \
- [IFLA_IPTUN_COLLECT_METADATA] = { .type = NETLINK_TYPE_FLAG }, \
- [IFLA_IPTUN_FWMARK] = { .type = NETLINK_TYPE_U32 }, \
+#define DEFINE_IPTUN_TYPES(name, flags_type) \
+ static const NLAPolicy rtnl_link_info_data_##name##_policies[] = { \
+ [IFLA_IPTUN_LINK] = BUILD_POLICY(U32), \
+ [IFLA_IPTUN_LOCAL] = BUILD_POLICY(IN_ADDR), \
+ [IFLA_IPTUN_REMOTE] = BUILD_POLICY(IN_ADDR), \
+ [IFLA_IPTUN_TTL] = BUILD_POLICY(U8), \
+ [IFLA_IPTUN_TOS] = BUILD_POLICY(U8), \
+ [IFLA_IPTUN_ENCAP_LIMIT] = BUILD_POLICY(U8), \
+ [IFLA_IPTUN_FLOWINFO] = BUILD_POLICY(U32), \
+ [IFLA_IPTUN_FLAGS] = BUILD_POLICY(flags_type), \
+ [IFLA_IPTUN_PROTO] = BUILD_POLICY(U8), \
+ [IFLA_IPTUN_PMTUDISC] = BUILD_POLICY(U8), \
+ [IFLA_IPTUN_6RD_PREFIX] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)), \
+ [IFLA_IPTUN_6RD_RELAY_PREFIX] = BUILD_POLICY(U32), \
+ [IFLA_IPTUN_6RD_PREFIXLEN] = BUILD_POLICY(U16), \
+ [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = BUILD_POLICY(U16), \
+ [IFLA_IPTUN_ENCAP_TYPE] = BUILD_POLICY(U16), \
+ [IFLA_IPTUN_ENCAP_FLAGS] = BUILD_POLICY(U16), \
+ [IFLA_IPTUN_ENCAP_SPORT] = BUILD_POLICY(U16), \
+ [IFLA_IPTUN_ENCAP_DPORT] = BUILD_POLICY(U16), \
+ [IFLA_IPTUN_COLLECT_METADATA] = BUILD_POLICY(FLAG), \
+ [IFLA_IPTUN_FWMARK] = BUILD_POLICY(U32), \
}
-DEFINE_IPTUN_TYPES(iptun, NETLINK_TYPE_U32); /* for ipip and ip6tnl */
-DEFINE_IPTUN_TYPES(sit, NETLINK_TYPE_U16); /* for sit */
+DEFINE_IPTUN_TYPES(iptun, U32); /* for ipip and ip6tnl */
+DEFINE_IPTUN_TYPES(sit, U16); /* for sit */
-static const NLType rtnl_link_info_data_ipvlan_types[] = {
- [IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 },
- [IFLA_IPVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_link_info_data_macsec_types[] = {
- [IFLA_MACSEC_SCI] = { .type = NETLINK_TYPE_U64 },
- [IFLA_MACSEC_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_MACSEC_ICV_LEN] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_CIPHER_SUITE] = { .type = NETLINK_TYPE_U64 },
- [IFLA_MACSEC_WINDOW] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACSEC_ENCODING_SA] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_ENCRYPT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_PROTECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_INC_SCI] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_ES] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_SCB] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_REPLAY_PROTECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_VALIDATION] = { .type = NETLINK_TYPE_U8 },
- [IFLA_MACSEC_OFFLOAD] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_macvlan_macaddr_types[] = {
- [IFLA_MACVLAN_MACADDR] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_macvlan_macaddr);
-
-static const NLType rtnl_link_info_data_macvlan_types[] = {
- [IFLA_MACVLAN_MODE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_MACVLAN_MACADDR_MODE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACVLAN_MACADDR_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_macvlan_macaddr_type_system },
- [IFLA_MACVLAN_MACADDR_COUNT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACVLAN_BC_QUEUE_LEN] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_tun_types[] = {
- [IFLA_TUN_OWNER] = { .type = NETLINK_TYPE_U32 },
- [IFLA_TUN_GROUP] = { .type = NETLINK_TYPE_U32 },
- [IFLA_TUN_TYPE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_TUN_PI] = { .type = NETLINK_TYPE_U8 },
- [IFLA_TUN_VNET_HDR] = { .type = NETLINK_TYPE_U8 },
- [IFLA_TUN_PERSIST] = { .type = NETLINK_TYPE_U8 },
- [IFLA_TUN_MULTI_QUEUE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_TUN_NUM_QUEUES] = { .type = NETLINK_TYPE_U32 },
- [IFLA_TUN_NUM_DISABLED_QUEUES] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_veth_types[] = {
- [VETH_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-};
-
-static const NLType rtnl_vlan_qos_map_types[] = {
- [IFLA_VLAN_QOS_MAPPING] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vlan_qos_mapping) },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_vlan_qos_map);
-
-static const NLType rtnl_link_info_data_vlan_types[] = {
- [IFLA_VLAN_ID] = { .type = NETLINK_TYPE_U16 },
- [IFLA_VLAN_FLAGS] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vlan_flags) },
- [IFLA_VLAN_EGRESS_QOS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
- [IFLA_VLAN_INGRESS_QOS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vlan_qos_map_type_system },
- [IFLA_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 },
-};
-
-static const NLType rtnl_link_info_data_vrf_types[] = {
- [IFLA_VRF_TABLE] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_vti_types[] = {
- [IFLA_VTI_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VTI_IKEY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VTI_OKEY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VTI_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_VTI_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFLA_VTI_FWMARK] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_link_info_data_vxcan_types[] = {
- [VXCAN_INFO_PEER] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
-};
-
-static const NLType rtnl_link_info_data_vxlan_types[] = {
- [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
- [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in_addr) },
- [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_AGEING] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_LIMIT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_PORT_RANGE] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vxlan_port_range) },
- [IFLA_VXLAN_PROXY] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_RSC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_L2MISS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_L3MISS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_VXLAN_GROUP6] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [IFLA_VXLAN_LOCAL6] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [IFLA_VXLAN_UDP_CSUM] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_REMCSUM_TX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_REMCSUM_RX] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_GBP] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_VXLAN_COLLECT_METADATA] = { .type = NETLINK_TYPE_U8 },
- [IFLA_VXLAN_LABEL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VXLAN_GPE] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_VXLAN_TTL_INHERIT] = { .type = NETLINK_TYPE_FLAG },
- [IFLA_VXLAN_DF] = { .type = NETLINK_TYPE_U8 },
-};
-
-static const NLType rtnl_link_info_data_xfrm_types[] = {
- [IFLA_XFRM_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_XFRM_IF_ID] = { .type = NETLINK_TYPE_U32 }
+static const NLAPolicy rtnl_link_info_data_ipvlan_policies[] = {
+ [IFLA_IPVLAN_MODE] = BUILD_POLICY(U16),
+ [IFLA_IPVLAN_FLAGS] = BUILD_POLICY(U16),
+};
+
+static const NLAPolicy rtnl_link_info_data_macsec_policies[] = {
+ [IFLA_MACSEC_SCI] = BUILD_POLICY(U64),
+ [IFLA_MACSEC_PORT] = BUILD_POLICY(U16),
+ [IFLA_MACSEC_ICV_LEN] = BUILD_POLICY(U8),
+ [IFLA_MACSEC_CIPHER_SUITE] = BUILD_POLICY(U64),
+ [IFLA_MACSEC_WINDOW] = BUILD_POLICY(U32),
+ [IFLA_MACSEC_ENCODING_SA] = BUILD_POLICY(U8),
+ [IFLA_MACSEC_ENCRYPT] = BUILD_POLICY(U8),
+ [IFLA_MACSEC_PROTECT] = BUILD_POLICY(U8),
+ [IFLA_MACSEC_INC_SCI] = BUILD_POLICY(U8),
+ [IFLA_MACSEC_ES] = BUILD_POLICY(U8),
+ [IFLA_MACSEC_SCB] = BUILD_POLICY(U8),
+ [IFLA_MACSEC_REPLAY_PROTECT] = BUILD_POLICY(U8),
+ [IFLA_MACSEC_VALIDATION] = BUILD_POLICY(U8),
+ [IFLA_MACSEC_OFFLOAD] = BUILD_POLICY(U8),
+};
+
+static const NLAPolicy rtnl_macvlan_macaddr_policies[] = {
+ [IFLA_MACVLAN_MACADDR] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+};
+
+DEFINE_POLICY_SET(rtnl_macvlan_macaddr);
+
+static const NLAPolicy rtnl_link_info_data_macvlan_policies[] = {
+ [IFLA_MACVLAN_MODE] = BUILD_POLICY(U32),
+ [IFLA_MACVLAN_FLAGS] = BUILD_POLICY(U16),
+ [IFLA_MACVLAN_MACADDR_MODE] = BUILD_POLICY(U32),
+ [IFLA_MACVLAN_MACADDR_DATA] = BUILD_POLICY_NESTED(rtnl_macvlan_macaddr),
+ [IFLA_MACVLAN_MACADDR_COUNT] = BUILD_POLICY(U32),
+ [IFLA_MACVLAN_BC_QUEUE_LEN] = BUILD_POLICY(U32),
+ [IFLA_MACVLAN_BC_QUEUE_LEN_USED] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_tun_policies[] = {
+ [IFLA_TUN_OWNER] = BUILD_POLICY(U32),
+ [IFLA_TUN_GROUP] = BUILD_POLICY(U32),
+ [IFLA_TUN_TYPE] = BUILD_POLICY(U8),
+ [IFLA_TUN_PI] = BUILD_POLICY(U8),
+ [IFLA_TUN_VNET_HDR] = BUILD_POLICY(U8),
+ [IFLA_TUN_PERSIST] = BUILD_POLICY(U8),
+ [IFLA_TUN_MULTI_QUEUE] = BUILD_POLICY(U8),
+ [IFLA_TUN_NUM_QUEUES] = BUILD_POLICY(U32),
+ [IFLA_TUN_NUM_DISABLED_QUEUES] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_veth_policies[] = {
+ [VETH_INFO_PEER] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+};
+
+static const NLAPolicy rtnl_vlan_qos_map_policies[] = {
+ [IFLA_VLAN_QOS_MAPPING] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vlan_qos_mapping)),
+};
+
+DEFINE_POLICY_SET(rtnl_vlan_qos_map);
+
+static const NLAPolicy rtnl_link_info_data_vlan_policies[] = {
+ [IFLA_VLAN_ID] = BUILD_POLICY(U16),
+ [IFLA_VLAN_FLAGS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vlan_flags)),
+ [IFLA_VLAN_EGRESS_QOS] = BUILD_POLICY_NESTED(rtnl_vlan_qos_map),
+ [IFLA_VLAN_INGRESS_QOS] = BUILD_POLICY_NESTED(rtnl_vlan_qos_map),
+ [IFLA_VLAN_PROTOCOL] = BUILD_POLICY(U16),
+};
+
+static const NLAPolicy rtnl_link_info_data_vrf_policies[] = {
+ [IFLA_VRF_TABLE] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_vti_policies[] = {
+ [IFLA_VTI_LINK] = BUILD_POLICY(U32),
+ [IFLA_VTI_IKEY] = BUILD_POLICY(U32),
+ [IFLA_VTI_OKEY] = BUILD_POLICY(U32),
+ [IFLA_VTI_LOCAL] = BUILD_POLICY(IN_ADDR),
+ [IFLA_VTI_REMOTE] = BUILD_POLICY(IN_ADDR),
+ [IFLA_VTI_FWMARK] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_link_info_data_vxcan_policies[] = {
+ [VXCAN_INFO_PEER] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+};
+
+static const NLAPolicy rtnl_link_info_data_vxlan_policies[] = {
+ [IFLA_VXLAN_ID] = BUILD_POLICY(U32),
+ [IFLA_VXLAN_GROUP] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+ [IFLA_VXLAN_LINK] = BUILD_POLICY(U32),
+ [IFLA_VXLAN_LOCAL] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in_addr)),
+ [IFLA_VXLAN_TTL] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_TOS] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_LEARNING] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_AGEING] = BUILD_POLICY(U32),
+ [IFLA_VXLAN_LIMIT] = BUILD_POLICY(U32),
+ [IFLA_VXLAN_PORT_RANGE] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vxlan_port_range)),
+ [IFLA_VXLAN_PROXY] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_RSC] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_L2MISS] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_L3MISS] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_PORT] = BUILD_POLICY(U16),
+ [IFLA_VXLAN_GROUP6] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [IFLA_VXLAN_LOCAL6] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [IFLA_VXLAN_UDP_CSUM] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_REMCSUM_TX] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_REMCSUM_RX] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_GBP] = BUILD_POLICY(FLAG),
+ [IFLA_VXLAN_REMCSUM_NOPARTIAL] = BUILD_POLICY(FLAG),
+ [IFLA_VXLAN_COLLECT_METADATA] = BUILD_POLICY(U8),
+ [IFLA_VXLAN_LABEL] = BUILD_POLICY(U32),
+ [IFLA_VXLAN_GPE] = BUILD_POLICY(FLAG),
+ [IFLA_VXLAN_TTL_INHERIT] = BUILD_POLICY(FLAG),
+ [IFLA_VXLAN_DF] = BUILD_POLICY(U8),
+};
+
+static const NLAPolicy rtnl_link_info_data_xfrm_policies[] = {
+ [IFLA_XFRM_LINK] = BUILD_POLICY(U32),
+ [IFLA_XFRM_IF_ID] = BUILD_POLICY(U32)
};
-
-static const NLTypeSystemUnionElement rtnl_link_info_data_type_systems[] = {
- { .name = "bareudp", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_bareudp), },
- { .name = "batadv", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_batadv), },
- { .name = "bond", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_bond), },
- { .name = "bridge", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_bridge), },
+
+static const NLAPolicySetUnionElement rtnl_link_info_data_policy_set_union_elements[] = {
+ BUILD_UNION_ELEMENT_BY_STRING("bareudp", rtnl_link_info_data_bareudp),
+ BUILD_UNION_ELEMENT_BY_STRING("batadv", rtnl_link_info_data_batadv),
+ BUILD_UNION_ELEMENT_BY_STRING("bond", rtnl_link_info_data_bond),
+ BUILD_UNION_ELEMENT_BY_STRING("bridge", rtnl_link_info_data_bridge),
/*
- { .name = "caif", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_caif), },
+ BUILD_UNION_ELEMENT_BY_STRING("caif", rtnl_link_info_data_caif),
*/
- { .name = "can", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_can), },
- { .name = "erspan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre), },
- { .name = "geneve", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_geneve), },
- { .name = "gre", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre), },
- { .name = "gretap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre), },
+ BUILD_UNION_ELEMENT_BY_STRING("can", rtnl_link_info_data_can),
+ BUILD_UNION_ELEMENT_BY_STRING("erspan", rtnl_link_info_data_gre),
+ BUILD_UNION_ELEMENT_BY_STRING("geneve", rtnl_link_info_data_geneve),
+ BUILD_UNION_ELEMENT_BY_STRING("gre", rtnl_link_info_data_gre),
+ BUILD_UNION_ELEMENT_BY_STRING("gretap", rtnl_link_info_data_gre),
/*
- { .name = "gtp", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gtp), },
- { .name = "hsr", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_hsr), },
+ BUILD_UNION_ELEMENT_BY_STRING("gtp", rtnl_link_info_data_gtp),
+ BUILD_UNION_ELEMENT_BY_STRING("hsr", rtnl_link_info_data_hsr),
*/
- { .name = "ip6erspan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre), },
- { .name = "ip6gre", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre), },
- { .name = "ip6gretap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_gre), },
- { .name = "ip6tnl", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_iptun), },
- { .name = "ipoib", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipoib), },
- { .name = "ipip", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_iptun), },
- { .name = "ipvlan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvlan), },
- { .name = "ipvtap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ipvlan), },
- { .name = "macsec", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_macsec), },
- { .name = "macvlan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_macvlan), },
- { .name = "macvtap", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_macvlan), },
+ BUILD_UNION_ELEMENT_BY_STRING("ip6erspan", rtnl_link_info_data_gre),
+ BUILD_UNION_ELEMENT_BY_STRING("ip6gre", rtnl_link_info_data_gre),
+ BUILD_UNION_ELEMENT_BY_STRING("ip6gretap", rtnl_link_info_data_gre),
+ BUILD_UNION_ELEMENT_BY_STRING("ip6tnl", rtnl_link_info_data_iptun),
+ BUILD_UNION_ELEMENT_BY_STRING("ipoib", rtnl_link_info_data_ipoib),
+ BUILD_UNION_ELEMENT_BY_STRING("ipip", rtnl_link_info_data_iptun),
+ BUILD_UNION_ELEMENT_BY_STRING("ipvlan", rtnl_link_info_data_ipvlan),
+ BUILD_UNION_ELEMENT_BY_STRING("ipvtap", rtnl_link_info_data_ipvlan),
+ BUILD_UNION_ELEMENT_BY_STRING("macsec", rtnl_link_info_data_macsec),
+ BUILD_UNION_ELEMENT_BY_STRING("macvlan", rtnl_link_info_data_macvlan),
+ BUILD_UNION_ELEMENT_BY_STRING("macvtap", rtnl_link_info_data_macvlan),
/*
- { .name = "ppp", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_ppp), },
- { .name = "rmnet", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_rmnet), },
+ BUILD_UNION_ELEMENT_BY_STRING("ppp", rtnl_link_info_data_ppp),
+ BUILD_UNION_ELEMENT_BY_STRING("rmnet", rtnl_link_info_data_rmnet),
*/
- { .name = "sit", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_sit), },
- { .name = "tun", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_tun), },
- { .name = "veth", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_veth), },
- { .name = "vlan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vlan), },
- { .name = "vrf", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vrf), },
- { .name = "vti", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vti), },
- { .name = "vti6", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vti), },
- { .name = "vxcan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vxcan), },
- { .name = "vxlan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_vxlan), },
+ BUILD_UNION_ELEMENT_BY_STRING("sit", rtnl_link_info_data_sit),
+ BUILD_UNION_ELEMENT_BY_STRING("tun", rtnl_link_info_data_tun),
+ BUILD_UNION_ELEMENT_BY_STRING("veth", rtnl_link_info_data_veth),
+ BUILD_UNION_ELEMENT_BY_STRING("vlan", rtnl_link_info_data_vlan),
+ BUILD_UNION_ELEMENT_BY_STRING("vrf", rtnl_link_info_data_vrf),
+ BUILD_UNION_ELEMENT_BY_STRING("vti", rtnl_link_info_data_vti),
+ BUILD_UNION_ELEMENT_BY_STRING("vti6", rtnl_link_info_data_vti),
+ BUILD_UNION_ELEMENT_BY_STRING("vxcan", rtnl_link_info_data_vxcan),
+ BUILD_UNION_ELEMENT_BY_STRING("vxlan", rtnl_link_info_data_vxlan),
/*
- { .name = "wwan", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_wwan), },
+ BUILD_UNION_ELEMENT_BY_STRING("wwan", rtnl_link_info_data_wwan),
*/
- { .name = "xfrm", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_link_info_data_xfrm), },
-};
-
-DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(rtnl_link_info_data, IFLA_INFO_KIND);
-
-static const struct NLType rtnl_bridge_port_types[] = {
- [IFLA_BRPORT_STATE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_COST] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRPORT_PRIORITY] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_MODE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_GUARD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PROTECT] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_FAST_LEAVE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_LEARNING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PROXYARP] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_LEARNING_SYNC] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_ROOT_ID] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_BRIDGE_ID] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_DESIGNATED_PORT] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_DESIGNATED_COST] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_ID] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_NO] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_TOPOLOGY_CHANGE_ACK] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_CONFIG_PENDING] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MESSAGE_AGE_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BRPORT_FORWARD_DELAY_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BRPORT_HOLD_TIMER] = { .type = NETLINK_TYPE_U64 },
- [IFLA_BRPORT_FLUSH] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_PAD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MCAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_VLAN_TUNNEL] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_BCAST_FLOOD] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_ISOLATED] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_BACKUP_PORT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRPORT_MRP_RING_OPEN] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MRP_IN_OPEN] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRPORT_MCAST_EHT_HOSTS_CNT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLTypeSystemUnionElement rtnl_link_info_slave_data_type_systems[] = {
- { .name = "bridge", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_bridge_port), },
-};
-
-DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(rtnl_link_info_slave_data, IFLA_INFO_SLAVE_KIND);
-
-static const NLType rtnl_link_info_types[] = {
- [IFLA_INFO_KIND] = { .type = NETLINK_TYPE_STRING },
- [IFLA_INFO_DATA] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_link_info_data_type_system_union },
+ BUILD_UNION_ELEMENT_BY_STRING("xfrm", rtnl_link_info_data_xfrm),
+};
+
+DEFINE_POLICY_SET_UNION(rtnl_link_info_data, IFLA_INFO_KIND);
+
+static const struct NLAPolicy rtnl_bridge_port_policies[] = {
+ [IFLA_BRPORT_STATE] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_COST] = BUILD_POLICY(U32),
+ [IFLA_BRPORT_PRIORITY] = BUILD_POLICY(U16),
+ [IFLA_BRPORT_MODE] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_GUARD] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_PROTECT] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_FAST_LEAVE] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_LEARNING] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_UNICAST_FLOOD] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_PROXYARP] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_LEARNING_SYNC] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_PROXYARP_WIFI] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_ROOT_ID] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_BRIDGE_ID] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_DESIGNATED_PORT] = BUILD_POLICY(U16),
+ [IFLA_BRPORT_DESIGNATED_COST] = BUILD_POLICY(U16),
+ [IFLA_BRPORT_ID] = BUILD_POLICY(U16),
+ [IFLA_BRPORT_NO] = BUILD_POLICY(U16),
+ [IFLA_BRPORT_TOPOLOGY_CHANGE_ACK] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_CONFIG_PENDING] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_MESSAGE_AGE_TIMER] = BUILD_POLICY(U64),
+ [IFLA_BRPORT_FORWARD_DELAY_TIMER] = BUILD_POLICY(U64),
+ [IFLA_BRPORT_HOLD_TIMER] = BUILD_POLICY(U64),
+ [IFLA_BRPORT_FLUSH] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_MULTICAST_ROUTER] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_PAD] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_MCAST_FLOOD] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_MCAST_TO_UCAST] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_VLAN_TUNNEL] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_BCAST_FLOOD] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_GROUP_FWD_MASK] = BUILD_POLICY(U16),
+ [IFLA_BRPORT_NEIGH_SUPPRESS] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_ISOLATED] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_BACKUP_PORT] = BUILD_POLICY(U32),
+ [IFLA_BRPORT_MRP_RING_OPEN] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_MRP_IN_OPEN] = BUILD_POLICY(U8),
+ [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = BUILD_POLICY(U32),
+ [IFLA_BRPORT_MCAST_EHT_HOSTS_CNT] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicySetUnionElement rtnl_link_info_slave_data_policy_set_union_elements[] = {
+ BUILD_UNION_ELEMENT_BY_STRING("bridge", rtnl_bridge_port),
+};
+
+DEFINE_POLICY_SET_UNION(rtnl_link_info_slave_data, IFLA_INFO_SLAVE_KIND);
+
+static const NLAPolicy rtnl_link_info_policies[] = {
+ [IFLA_INFO_KIND] = BUILD_POLICY(STRING),
+ [IFLA_INFO_DATA] = BUILD_POLICY_NESTED_UNION_BY_STRING(rtnl_link_info_data),
/* TODO: Currently IFLA_INFO_XSTATS is used only when IFLA_INFO_KIND is "can". In the future,
- * when multiple kinds of netdevs use this attribute, then convert its type to NETLINK_TYPE_UNION. */
- [IFLA_INFO_XSTATS] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct can_device_stats) },
- [IFLA_INFO_SLAVE_KIND] = { .type = NETLINK_TYPE_STRING },
- [IFLA_INFO_SLAVE_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system_union = &rtnl_link_info_slave_data_type_system_union },
+ * when multiple kinds of netdevs use this attribute, convert its type to NETLINK_TYPE_UNION. */
+ [IFLA_INFO_XSTATS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct can_device_stats)),
+ [IFLA_INFO_SLAVE_KIND] = BUILD_POLICY(STRING),
+ [IFLA_INFO_SLAVE_DATA] = BUILD_POLICY_NESTED_UNION_BY_STRING(rtnl_link_info_slave_data),
};
-DEFINE_TYPE_SYSTEM(rtnl_link_info);
+DEFINE_POLICY_SET(rtnl_link_info);
-static const struct NLType rtnl_inet_types[] = {
- [IFLA_INET_CONF] = { .type = NETLINK_TYPE_BINARY }, /* size = IPV4_DEVCONF_MAX * 4 */
+static const struct NLAPolicy rtnl_inet_policies[] = {
+ [IFLA_INET_CONF] = BUILD_POLICY(BINARY), /* size = IPV4_DEVCONF_MAX * 4 */
};
-DEFINE_TYPE_SYSTEM(rtnl_inet);
+DEFINE_POLICY_SET(rtnl_inet);
-static const struct NLType rtnl_inet6_types[] = {
- [IFLA_INET6_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_INET6_CONF] = { .type = NETLINK_TYPE_BINARY }, /* size = DEVCONF_MAX * sizeof(s32) */
- [IFLA_INET6_STATS] = { .type = NETLINK_TYPE_BINARY }, /* size = IPSTATS_MIB_MAX * sizeof(u64) */
+static const struct NLAPolicy rtnl_inet6_policies[] = {
+ [IFLA_INET6_FLAGS] = BUILD_POLICY(U32),
+ [IFLA_INET6_CONF] = BUILD_POLICY(BINARY), /* size = DEVCONF_MAX * sizeof(s32) */
+ [IFLA_INET6_STATS] = BUILD_POLICY(BINARY), /* size = IPSTATS_MIB_MAX * sizeof(u64) */
[IFLA_INET6_MCAST] = {}, /* unused. */
- [IFLA_INET6_CACHEINFO] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_cacheinfo) },
- [IFLA_INET6_ICMP6STATS] = { .type = NETLINK_TYPE_BINARY }, /* size = ICMP6_MIB_MAX * sizeof(u64) */
- [IFLA_INET6_TOKEN] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [IFLA_INET6_ADDR_GEN_MODE] = { .type = NETLINK_TYPE_U8 },
+ [IFLA_INET6_CACHEINFO] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_cacheinfo)),
+ [IFLA_INET6_ICMP6STATS] = BUILD_POLICY(BINARY), /* size = ICMP6_MIB_MAX * sizeof(u64) */
+ [IFLA_INET6_TOKEN] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [IFLA_INET6_ADDR_GEN_MODE] = BUILD_POLICY(U8),
};
-DEFINE_TYPE_SYSTEM(rtnl_inet6);
+DEFINE_POLICY_SET(rtnl_inet6);
-static const NLTypeSystemUnionElement rtnl_prot_info_type_systems[] = {
- { .protocol = AF_BRIDGE, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_bridge_port), },
- { .protocol = AF_INET6, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_inet6), },
+static const NLAPolicySetUnionElement rtnl_prot_info_policy_set_union_elements[] = {
+ BUILD_UNION_ELEMENT_BY_FAMILY(AF_BRIDGE, rtnl_bridge_port),
+ BUILD_UNION_ELEMENT_BY_FAMILY(AF_INET6, rtnl_inet6),
};
-DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(rtnl_prot_info);
+DEFINE_POLICY_SET_UNION(rtnl_prot_info, 0);
-static const NLType rtnl_af_spec_unspec_types[] = {
- [AF_INET] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_inet_type_system },
- [AF_INET6] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_inet6_type_system },
+static const NLAPolicy rtnl_af_spec_unspec_policies[] = {
+ [AF_INET] = BUILD_POLICY_NESTED(rtnl_inet),
+ [AF_INET6] = BUILD_POLICY_NESTED(rtnl_inet6),
};
-static const NLType rtnl_bridge_vlan_tunnel_info_types[] = {
- [IFLA_BRIDGE_VLAN_TUNNEL_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NETLINK_TYPE_U16 },
+static const NLAPolicy rtnl_bridge_vlan_tunnel_info_policies[] = {
+ [IFLA_BRIDGE_VLAN_TUNNEL_ID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_VLAN_TUNNEL_VID] = BUILD_POLICY(U16),
+ [IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = BUILD_POLICY(U16),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_vlan_tunnel_info);
+DEFINE_POLICY_SET(rtnl_bridge_vlan_tunnel_info);
-static const NLType rtnl_bridge_mrp_instance_types[] = {
- [IFLA_BRIDGE_MRP_INSTANCE_RING_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INSTANCE_PRIO] = { .type = NETLINK_TYPE_U16 },
+static const NLAPolicy rtnl_bridge_mrp_instance_policies[] = {
+ [IFLA_BRIDGE_MRP_INSTANCE_RING_ID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INSTANCE_P_IFINDEX] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INSTANCE_S_IFINDEX] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INSTANCE_PRIO] = BUILD_POLICY(U16),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_instance);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_instance);
-static const NLType rtnl_bridge_mrp_port_state_types[] = {
- [IFLA_BRIDGE_MRP_PORT_STATE_STATE] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_port_state_policies[] = {
+ [IFLA_BRIDGE_MRP_PORT_STATE_STATE] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_port_state);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_port_state);
-static const NLType rtnl_bridge_mrp_port_role_types[] = {
- [IFLA_BRIDGE_MRP_PORT_ROLE_ROLE] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_port_role_policies[] = {
+ [IFLA_BRIDGE_MRP_PORT_ROLE_ROLE] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_port_role);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_port_role);
-static const NLType rtnl_bridge_mrp_ring_state_types[] = {
- [IFLA_BRIDGE_MRP_RING_STATE_RING_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_RING_STATE_STATE] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_ring_state_policies[] = {
+ [IFLA_BRIDGE_MRP_RING_STATE_RING_ID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_RING_STATE_STATE] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_ring_state);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_ring_state);
-static const NLType rtnl_bridge_mrp_ring_role_types[] = {
- [IFLA_BRIDGE_MRP_RING_ROLE_RING_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_RING_ROLE_ROLE] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_ring_role_policies[] = {
+ [IFLA_BRIDGE_MRP_RING_ROLE_RING_ID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_RING_ROLE_ROLE] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_ring_role);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_ring_role);
-static const NLType rtnl_bridge_mrp_start_test_types[] = {
- [IFLA_BRIDGE_MRP_START_TEST_RING_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_START_TEST_PERIOD] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_START_TEST_MONITOR] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_start_test_policies[] = {
+ [IFLA_BRIDGE_MRP_START_TEST_RING_ID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_START_TEST_PERIOD] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_START_TEST_MONITOR] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_start_test);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_start_test);
-static const NLType rtnl_bridge_mrp_info_types[] = {
- [IFLA_BRIDGE_MRP_INFO_RING_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_P_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_S_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_PRIO] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRIDGE_MRP_INFO_RING_STATE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_RING_ROLE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_TEST_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_TEST_MAX_MISS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_TEST_MONITOR] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_I_IFINDEX] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_IN_STATE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_IN_ROLE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_IN_TEST_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_INFO_IN_TEST_MAX_MISS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_info_policies[] = {
+ [IFLA_BRIDGE_MRP_INFO_RING_ID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_P_IFINDEX] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_S_IFINDEX] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_PRIO] = BUILD_POLICY(U16),
+ [IFLA_BRIDGE_MRP_INFO_RING_STATE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_RING_ROLE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_TEST_INTERVAL] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_TEST_MAX_MISS] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_TEST_MONITOR] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_I_IFINDEX] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_IN_STATE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_IN_ROLE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_IN_TEST_INTERVAL] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_INFO_IN_TEST_MAX_MISS] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_info);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_info);
-static const NLType rtnl_bridge_mrp_in_role_types[] = {
- [IFLA_BRIDGE_MRP_IN_ROLE_RING_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_IN_ROLE_IN_ID] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRIDGE_MRP_IN_ROLE_ROLE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_IN_ROLE_I_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_in_role_policies[] = {
+ [IFLA_BRIDGE_MRP_IN_ROLE_RING_ID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_IN_ROLE_IN_ID] = BUILD_POLICY(U16),
+ [IFLA_BRIDGE_MRP_IN_ROLE_ROLE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_IN_ROLE_I_IFINDEX] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_in_role);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_in_role);
-static const NLType rtnl_bridge_mrp_in_state_types[] = {
- [IFLA_BRIDGE_MRP_IN_STATE_IN_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_IN_STATE_STATE] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_in_state_policies[] = {
+ [IFLA_BRIDGE_MRP_IN_STATE_IN_ID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_IN_STATE_STATE] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_in_state);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_in_state);
-static const NLType rtnl_bridge_mrp_start_in_test_types[] = {
- [IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_mrp_start_in_test_policies[] = {
+ [IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp_start_in_test);
+DEFINE_POLICY_SET(rtnl_bridge_mrp_start_in_test);
-static const NLType rtnl_bridge_mrp_types[] = {
- [IFLA_BRIDGE_MRP_INSTANCE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_instance_type_system },
- [IFLA_BRIDGE_MRP_PORT_STATE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_port_state_type_system },
- [IFLA_BRIDGE_MRP_PORT_ROLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_port_role_type_system },
- [IFLA_BRIDGE_MRP_RING_STATE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_ring_state_type_system },
- [IFLA_BRIDGE_MRP_RING_ROLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_ring_role_type_system },
- [IFLA_BRIDGE_MRP_START_TEST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_start_test_type_system },
- [IFLA_BRIDGE_MRP_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_info_type_system },
- [IFLA_BRIDGE_MRP_IN_ROLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_in_role_type_system },
- [IFLA_BRIDGE_MRP_IN_STATE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_in_state_type_system },
- [IFLA_BRIDGE_MRP_START_IN_TEST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_start_in_test_type_system },
+static const NLAPolicy rtnl_bridge_mrp_policies[] = {
+ [IFLA_BRIDGE_MRP_INSTANCE] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_instance),
+ [IFLA_BRIDGE_MRP_PORT_STATE] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_port_state),
+ [IFLA_BRIDGE_MRP_PORT_ROLE] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_port_role),
+ [IFLA_BRIDGE_MRP_RING_STATE] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_ring_state),
+ [IFLA_BRIDGE_MRP_RING_ROLE] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_ring_role),
+ [IFLA_BRIDGE_MRP_START_TEST] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_start_test),
+ [IFLA_BRIDGE_MRP_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_info),
+ [IFLA_BRIDGE_MRP_IN_ROLE] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_in_role),
+ [IFLA_BRIDGE_MRP_IN_STATE] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_in_state),
+ [IFLA_BRIDGE_MRP_START_IN_TEST] = BUILD_POLICY_NESTED(rtnl_bridge_mrp_start_in_test),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_mrp);
+DEFINE_POLICY_SET(rtnl_bridge_mrp);
-static const NLType rtnl_bridge_cfm_mep_create_types[] = {
- [IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_mep_create_policies[] = {
+ [IFLA_BRIDGE_CFM_MEP_CREATE_INSTANCE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_MEP_CREATE_DOMAIN] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_MEP_CREATE_DIRECTION] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_MEP_CREATE_IFINDEX] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_mep_create);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_mep_create);
-static const NLType rtnl_bridge_cfm_mep_delete_types[] = {
- [IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_mep_delete_policies[] = {
+ [IFLA_BRIDGE_CFM_MEP_DELETE_INSTANCE] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_mep_delete);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_mep_delete);
-static const NLType rtnl_bridge_cfm_mep_config_types[] = {
- [IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_mep_config_policies[] = {
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_INSTANCE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_UNICAST_MAC] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_MDLEVEL] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_MEPID] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_mep_config);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_mep_config);
-static const NLType rtnl_bridge_cfm_cc_config_types[] = {
- [IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID] = { .type = NETLINK_TYPE_BINARY, .size = CFM_MAID_LENGTH },
+static const NLAPolicy rtnl_bridge_cfm_cc_config_policies[] = {
+ [IFLA_BRIDGE_CFM_CC_CONFIG_INSTANCE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_CONFIG_ENABLE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_INTERVAL] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_CONFIG_EXP_MAID] = BUILD_POLICY_WITH_SIZE(BINARY, CFM_MAID_LENGTH),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_config);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_config);
-static const NLType rtnl_bridge_cfm_cc_peer_mep_types[] = {
- [IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_PEER_MEPID] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_cc_peer_mep_policies[] = {
+ [IFLA_BRIDGE_CFM_CC_PEER_MEP_INSTANCE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_PEER_MEPID] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_peer_mep);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_peer_mep);
-static const NLType rtnl_bridge_cfm_cc_rdi_types[] = {
- [IFLA_BRIDGE_CFM_CC_RDI_INSTANCE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_RDI_RDI] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_cc_rdi_policies[] = {
+ [IFLA_BRIDGE_CFM_CC_RDI_INSTANCE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_RDI_RDI] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_rdi);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_rdi);
-static const NLType rtnl_bridge_cfm_cc_ccm_tx_types[] = {
- [IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
- [IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE] = { .type = NETLINK_TYPE_U8 },
+static const NLAPolicy rtnl_bridge_cfm_cc_ccm_tx_policies[] = {
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_INSTANCE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_DMAC] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_SEQ_NO_UPDATE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_PERIOD] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_IF_TLV_VALUE] = BUILD_POLICY(U8),
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_PORT_TLV_VALUE] = BUILD_POLICY(U8),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_ccm_tx);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_ccm_tx);
-static const NLType rtnl_bridge_cfm_mep_status_types[] = {
- [IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_mep_status_policies[] = {
+ [IFLA_BRIDGE_CFM_MEP_STATUS_INSTANCE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_MEP_STATUS_OPCODE_UNEXP_SEEN] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_MEP_STATUS_VERSION_UNEXP_SEEN] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_MEP_STATUS_RX_LEVEL_LOW_SEEN] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_mep_status);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_mep_status);
-static const NLType rtnl_bridge_cfm_cc_peer_status_types[] = {
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN] = { .type = NETLINK_TYPE_U32 },
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_bridge_cfm_cc_peer_status_policies[] = {
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_INSTANCE] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_PEER_MEPID] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_CCM_DEFECT] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_RDI] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_PORT_TLV_VALUE] = BUILD_POLICY(U8),
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_IF_TLV_VALUE] = BUILD_POLICY(U8),
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEEN] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_TLV_SEEN] = BUILD_POLICY(U32),
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_SEQ_UNEXP_SEEN] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm_cc_peer_status);
+DEFINE_POLICY_SET(rtnl_bridge_cfm_cc_peer_status);
-static const NLType rtnl_bridge_cfm_types[] = {
- [IFLA_BRIDGE_CFM_MEP_CREATE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_create_type_system },
- [IFLA_BRIDGE_CFM_MEP_DELETE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_delete_type_system },
- [IFLA_BRIDGE_CFM_MEP_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_config_type_system },
- [IFLA_BRIDGE_CFM_CC_CONFIG] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_config_type_system },
- [IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_peer_mep_type_system },
- [IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_peer_mep_type_system },
- [IFLA_BRIDGE_CFM_CC_RDI] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_rdi_type_system },
- [IFLA_BRIDGE_CFM_CC_CCM_TX] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_ccm_tx_type_system },
- [IFLA_BRIDGE_CFM_MEP_CREATE_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_create_type_system },
- [IFLA_BRIDGE_CFM_MEP_CONFIG_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_config_type_system },
- [IFLA_BRIDGE_CFM_CC_CONFIG_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_config_type_system },
- [IFLA_BRIDGE_CFM_CC_RDI_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_rdi_type_system },
- [IFLA_BRIDGE_CFM_CC_CCM_TX_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_ccm_tx_type_system },
- [IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_peer_mep_type_system },
- [IFLA_BRIDGE_CFM_MEP_STATUS_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_mep_status_type_system },
- [IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_cc_peer_status_type_system },
+static const NLAPolicy rtnl_bridge_cfm_policies[] = {
+ [IFLA_BRIDGE_CFM_MEP_CREATE] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_create),
+ [IFLA_BRIDGE_CFM_MEP_DELETE] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_delete),
+ [IFLA_BRIDGE_CFM_MEP_CONFIG] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_config),
+ [IFLA_BRIDGE_CFM_CC_CONFIG] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_config),
+ [IFLA_BRIDGE_CFM_CC_PEER_MEP_ADD] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_peer_mep),
+ [IFLA_BRIDGE_CFM_CC_PEER_MEP_REMOVE] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_peer_mep),
+ [IFLA_BRIDGE_CFM_CC_RDI] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_rdi),
+ [IFLA_BRIDGE_CFM_CC_CCM_TX] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_ccm_tx),
+ [IFLA_BRIDGE_CFM_MEP_CREATE_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_create),
+ [IFLA_BRIDGE_CFM_MEP_CONFIG_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_config),
+ [IFLA_BRIDGE_CFM_CC_CONFIG_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_config),
+ [IFLA_BRIDGE_CFM_CC_RDI_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_rdi),
+ [IFLA_BRIDGE_CFM_CC_CCM_TX_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_ccm_tx),
+ [IFLA_BRIDGE_CFM_CC_PEER_MEP_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_peer_mep),
+ [IFLA_BRIDGE_CFM_MEP_STATUS_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_mep_status),
+ [IFLA_BRIDGE_CFM_CC_PEER_STATUS_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_cfm_cc_peer_status),
};
-DEFINE_TYPE_SYSTEM(rtnl_bridge_cfm);
+DEFINE_POLICY_SET(rtnl_bridge_cfm);
-static const NLType rtnl_af_spec_bridge_types[] = {
- [IFLA_BRIDGE_FLAGS] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRIDGE_MODE] = { .type = NETLINK_TYPE_U16 },
- [IFLA_BRIDGE_VLAN_INFO] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct bridge_vlan_info) },
- [IFLA_BRIDGE_VLAN_TUNNEL_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_vlan_tunnel_info_type_system },
- [IFLA_BRIDGE_MRP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_mrp_type_system },
- [IFLA_BRIDGE_CFM] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_bridge_cfm_type_system },
+static const NLAPolicy rtnl_af_spec_bridge_policies[] = {
+ [IFLA_BRIDGE_FLAGS] = BUILD_POLICY(U16),
+ [IFLA_BRIDGE_MODE] = BUILD_POLICY(U16),
+ [IFLA_BRIDGE_VLAN_INFO] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct bridge_vlan_info)),
+ [IFLA_BRIDGE_VLAN_TUNNEL_INFO] = BUILD_POLICY_NESTED(rtnl_bridge_vlan_tunnel_info),
+ [IFLA_BRIDGE_MRP] = BUILD_POLICY_NESTED(rtnl_bridge_mrp),
+ [IFLA_BRIDGE_CFM] = BUILD_POLICY_NESTED(rtnl_bridge_cfm),
};
-static const NLTypeSystemUnionElement rtnl_af_spec_type_systems[] = {
- { .protocol = AF_UNSPEC, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_af_spec_unspec), },
- { .protocol = AF_BRIDGE, .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_af_spec_bridge), },
+static const NLAPolicySetUnionElement rtnl_af_spec_policy_set_union_elements[] = {
+ BUILD_UNION_ELEMENT_BY_FAMILY(AF_UNSPEC, rtnl_af_spec_unspec),
+ BUILD_UNION_ELEMENT_BY_FAMILY(AF_BRIDGE, rtnl_af_spec_bridge),
};
-DEFINE_TYPE_SYSTEM_UNION_MATCH_PROTOCOL(rtnl_af_spec);
+DEFINE_POLICY_SET_UNION(rtnl_af_spec, 0);
-static const NLType rtnl_prop_list_types[] = {
- [IFLA_ALT_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
+static const NLAPolicy rtnl_prop_list_policies[] = {
+ [IFLA_ALT_IFNAME] = BUILD_POLICY_WITH_SIZE(STRING, ALTIFNAMSIZ - 1),
};
-DEFINE_TYPE_SYSTEM(rtnl_prop_list);
+DEFINE_POLICY_SET(rtnl_prop_list);
-static const NLType rtnl_vf_vlan_list_types[] = {
- [IFLA_VF_VLAN_INFO] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_vlan_info) },
+static const NLAPolicy rtnl_vf_vlan_list_policies[] = {
+ [IFLA_VF_VLAN_INFO] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_vlan_info)),
};
-DEFINE_TYPE_SYSTEM(rtnl_vf_vlan_list);
+DEFINE_POLICY_SET(rtnl_vf_vlan_list);
-static const NLType rtnl_vf_info_types[] = {
- [IFLA_VF_MAC] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_mac) },
- [IFLA_VF_VLAN] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_vlan) },
- [IFLA_VF_VLAN_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_list_type_system },
- [IFLA_VF_TX_RATE] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_tx_rate) },
- [IFLA_VF_SPOOFCHK] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_spoofchk) },
- [IFLA_VF_RATE] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_rate) },
- [IFLA_VF_LINK_STATE] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_link_state) },
- [IFLA_VF_RSS_QUERY_EN] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_rss_query_en) },
- [IFLA_VF_TRUST] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_trust) },
- [IFLA_VF_IB_NODE_GUID] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_guid) },
- [IFLA_VF_IB_PORT_GUID] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_vf_guid) },
+static const NLAPolicy rtnl_vf_info_policies[] = {
+ [IFLA_VF_MAC] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_mac)),
+ [IFLA_VF_VLAN] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_vlan)),
+ [IFLA_VF_VLAN_LIST] = BUILD_POLICY_NESTED(rtnl_vf_vlan_list),
+ [IFLA_VF_TX_RATE] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_tx_rate)),
+ [IFLA_VF_SPOOFCHK] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_spoofchk)),
+ [IFLA_VF_RATE] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_rate)),
+ [IFLA_VF_LINK_STATE] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_link_state)),
+ [IFLA_VF_RSS_QUERY_EN] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_rss_query_en)),
+ [IFLA_VF_TRUST] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_trust)),
+ [IFLA_VF_IB_NODE_GUID] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_guid)),
+ [IFLA_VF_IB_PORT_GUID] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_vf_guid)),
};
-DEFINE_TYPE_SYSTEM(rtnl_vf_info);
+DEFINE_POLICY_SET(rtnl_vf_info);
-static const NLType rtnl_vfinfo_list_types[] = {
- [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_info_type_system },
+static const NLAPolicy rtnl_vfinfo_list_policies[] = {
+ [IFLA_VF_INFO] = BUILD_POLICY_NESTED(rtnl_vf_info),
};
-DEFINE_TYPE_SYSTEM(rtnl_vfinfo_list);
+DEFINE_POLICY_SET(rtnl_vfinfo_list);
-static const NLType rtnl_vf_port_types[] = {
- [IFLA_PORT_VF] = { .type = NETLINK_TYPE_U32 },
- [IFLA_PORT_PROFILE] = { .type = NETLINK_TYPE_STRING },
- [IFLA_PORT_VSI_TYPE] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct ifla_port_vsi) },
- [IFLA_PORT_INSTANCE_UUID] = { .type = NETLINK_TYPE_BINARY, .size = PORT_UUID_MAX },
- [IFLA_PORT_HOST_UUID] = { .type = NETLINK_TYPE_BINARY, .size = PORT_UUID_MAX },
- [IFLA_PORT_REQUEST] = { .type = NETLINK_TYPE_U8 },
- [IFLA_PORT_RESPONSE] = { .type = NETLINK_TYPE_U16 },
+static const NLAPolicy rtnl_vf_port_policies[] = {
+ [IFLA_PORT_VF] = BUILD_POLICY(U32),
+ [IFLA_PORT_PROFILE] = BUILD_POLICY(STRING),
+ [IFLA_PORT_VSI_TYPE] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct ifla_port_vsi)),
+ [IFLA_PORT_INSTANCE_UUID] = BUILD_POLICY_WITH_SIZE(BINARY, PORT_UUID_MAX),
+ [IFLA_PORT_HOST_UUID] = BUILD_POLICY_WITH_SIZE(BINARY, PORT_UUID_MAX),
+ [IFLA_PORT_REQUEST] = BUILD_POLICY(U8),
+ [IFLA_PORT_RESPONSE] = BUILD_POLICY(U16),
};
-DEFINE_TYPE_SYSTEM(rtnl_vf_port);
+DEFINE_POLICY_SET(rtnl_vf_port);
-static const NLType rtnl_vf_ports_types[] = {
- [IFLA_VF_PORT] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_port_type_system },
+static const NLAPolicy rtnl_vf_ports_policies[] = {
+ [IFLA_VF_PORT] = BUILD_POLICY_NESTED(rtnl_vf_port),
};
-DEFINE_TYPE_SYSTEM(rtnl_vf_ports);
+DEFINE_POLICY_SET(rtnl_vf_ports);
-static const NLType rtnl_xdp_types[] = {
- [IFLA_XDP_FD] = { .type = NETLINK_TYPE_S32 },
- [IFLA_XDP_ATTACHED] = { .type = NETLINK_TYPE_U8 },
- [IFLA_XDP_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_XDP_PROG_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_XDP_DRV_PROG_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_XDP_SKB_PROG_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_XDP_HW_PROG_ID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_XDP_EXPECTED_FD] = { .type = NETLINK_TYPE_S32 },
+static const NLAPolicy rtnl_xdp_policies[] = {
+ [IFLA_XDP_FD] = BUILD_POLICY(S32),
+ [IFLA_XDP_ATTACHED] = BUILD_POLICY(U8),
+ [IFLA_XDP_FLAGS] = BUILD_POLICY(U32),
+ [IFLA_XDP_PROG_ID] = BUILD_POLICY(U32),
+ [IFLA_XDP_DRV_PROG_ID] = BUILD_POLICY(U32),
+ [IFLA_XDP_SKB_PROG_ID] = BUILD_POLICY(U32),
+ [IFLA_XDP_HW_PROG_ID] = BUILD_POLICY(U32),
+ [IFLA_XDP_EXPECTED_FD] = BUILD_POLICY(S32),
};
-DEFINE_TYPE_SYSTEM(rtnl_xdp);
+DEFINE_POLICY_SET(rtnl_xdp);
-static const NLType rtnl_proto_down_reason_types[] = {
- [IFLA_PROTO_DOWN_REASON_MASK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_PROTO_DOWN_REASON_VALUE] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_proto_down_reason_policies[] = {
+ [IFLA_PROTO_DOWN_REASON_MASK] = BUILD_POLICY(U32),
+ [IFLA_PROTO_DOWN_REASON_VALUE] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_proto_down_reason);
+DEFINE_POLICY_SET(rtnl_proto_down_reason);
-static const NLType rtnl_link_types[] = {
- [IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR },
- [IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR },
- [IFLA_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
- [IFLA_MTU] = { .type = NETLINK_TYPE_U32 },
- [IFLA_LINK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_QDISC] = { .type = NETLINK_TYPE_STRING },
- [IFLA_STATS] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct rtnl_link_stats) },
+static const NLAPolicy rtnl_link_policies[] = {
+ [IFLA_ADDRESS] = BUILD_POLICY(ETHER_ADDR),
+ [IFLA_BROADCAST] = BUILD_POLICY(ETHER_ADDR),
+ [IFLA_IFNAME] = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ - 1),
+ [IFLA_MTU] = BUILD_POLICY(U32),
+ [IFLA_LINK] = BUILD_POLICY(U32),
+ [IFLA_QDISC] = BUILD_POLICY(STRING),
+ [IFLA_STATS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rtnl_link_stats)),
[IFLA_COST] = { /* Not used. */ },
[IFLA_PRIORITY] = { /* Not used. */ },
- [IFLA_MASTER] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_MASTER] = BUILD_POLICY(U32),
[IFLA_WIRELESS] = { /* Used only by wext. */ },
- [IFLA_PROTINFO] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_prot_info_type_system_union },
- [IFLA_TXQLEN] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MAP] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct rtnl_link_ifmap) },
- [IFLA_WEIGHT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_OPERSTATE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_LINKMODE] = { .type = NETLINK_TYPE_U8 },
- [IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system },
- [IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 },
- [IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 },
- [IFLA_NUM_VF] = { .type = NETLINK_TYPE_U32 },
- [IFLA_VFINFO_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vfinfo_list_type_system },
- [IFLA_STATS64] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct rtnl_link_stats64) },
- [IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_ports_type_system },
- [IFLA_PORT_SELF] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_port_type_system },
- [IFLA_AF_SPEC] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_af_spec_type_system_union },
- [IFLA_GROUP] = { .type = NETLINK_TYPE_U32 },
- [IFLA_NET_NS_FD] = { .type = NETLINK_TYPE_U32 },
- [IFLA_EXT_MASK] = { .type = NETLINK_TYPE_U32 },
- [IFLA_PROMISCUITY] = { .type = NETLINK_TYPE_U32 },
- [IFLA_NUM_TX_QUEUES] = { .type = NETLINK_TYPE_U32 },
- [IFLA_NUM_RX_QUEUES] = { .type = NETLINK_TYPE_U32 },
- [IFLA_CARRIER] = { .type = NETLINK_TYPE_U8 },
- [IFLA_PHYS_PORT_ID] = { .type = NETLINK_TYPE_BINARY, .size = MAX_PHYS_ITEM_ID_LEN },
- [IFLA_CARRIER_CHANGES] = { .type = NETLINK_TYPE_U32 },
- [IFLA_PHYS_SWITCH_ID] = { .type = NETLINK_TYPE_BINARY, .size = MAX_PHYS_ITEM_ID_LEN },
- [IFLA_LINK_NETNSID] = { .type = NETLINK_TYPE_S32 },
- [IFLA_PHYS_PORT_NAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
- [IFLA_PROTO_DOWN] = { .type = NETLINK_TYPE_U8 },
- [IFLA_GSO_MAX_SEGS] = { .type = NETLINK_TYPE_U32 },
- [IFLA_GSO_MAX_SIZE] = { .type = NETLINK_TYPE_U32 },
- [IFLA_XDP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_xdp_type_system },
- [IFLA_EVENT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_NEW_NETNSID] = { .type = NETLINK_TYPE_S32 },
- [IFLA_TARGET_NETNSID] = { .type = NETLINK_TYPE_S32 },
- [IFLA_CARRIER_UP_COUNT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_CARRIER_DOWN_COUNT] = { .type = NETLINK_TYPE_U32 },
- [IFLA_NEW_IFINDEX] = { .type = NETLINK_TYPE_S32 },
- [IFLA_MIN_MTU] = { .type = NETLINK_TYPE_U32 },
- [IFLA_MAX_MTU] = { .type = NETLINK_TYPE_U32 },
- [IFLA_PROP_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_prop_list_type_system },
- [IFLA_ALT_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = ALTIFNAMSIZ - 1 },
- [IFLA_PERM_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR },
- [IFLA_PROTO_DOWN_REASON] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_proto_down_reason_type_system },
- [IFLA_PARENT_DEV_NAME] = { .type = NETLINK_TYPE_STRING, },
- [IFLA_PARENT_DEV_BUS_NAME] = { .type = NETLINK_TYPE_STRING, },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_link);
+ [IFLA_PROTINFO] = BUILD_POLICY_NESTED_UNION_BY_FAMILY(rtnl_prot_info),
+ [IFLA_TXQLEN] = BUILD_POLICY(U32),
+ [IFLA_MAP] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rtnl_link_ifmap)),
+ [IFLA_WEIGHT] = BUILD_POLICY(U32),
+ [IFLA_OPERSTATE] = BUILD_POLICY(U8),
+ [IFLA_LINKMODE] = BUILD_POLICY(U8),
+ [IFLA_LINKINFO] = BUILD_POLICY_NESTED(rtnl_link_info),
+ [IFLA_NET_NS_PID] = BUILD_POLICY(U32),
+ [IFLA_IFALIAS] = BUILD_POLICY_WITH_SIZE(STRING, IFALIASZ - 1),
+ [IFLA_NUM_VF] = BUILD_POLICY(U32),
+ [IFLA_VFINFO_LIST] = BUILD_POLICY_NESTED(rtnl_vfinfo_list),
+ [IFLA_STATS64] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rtnl_link_stats64)),
+ [IFLA_VF_PORTS] = BUILD_POLICY_NESTED(rtnl_vf_ports),
+ [IFLA_PORT_SELF] = BUILD_POLICY_NESTED(rtnl_vf_port),
+ [IFLA_AF_SPEC] = BUILD_POLICY_NESTED_UNION_BY_FAMILY(rtnl_af_spec),
+ [IFLA_GROUP] = BUILD_POLICY(U32),
+ [IFLA_NET_NS_FD] = BUILD_POLICY(U32),
+ [IFLA_EXT_MASK] = BUILD_POLICY(U32),
+ [IFLA_PROMISCUITY] = BUILD_POLICY(U32),
+ [IFLA_NUM_TX_QUEUES] = BUILD_POLICY(U32),
+ [IFLA_NUM_RX_QUEUES] = BUILD_POLICY(U32),
+ [IFLA_CARRIER] = BUILD_POLICY(U8),
+ [IFLA_PHYS_PORT_ID] = BUILD_POLICY_WITH_SIZE(BINARY, MAX_PHYS_ITEM_ID_LEN),
+ [IFLA_CARRIER_CHANGES] = BUILD_POLICY(U32),
+ [IFLA_PHYS_SWITCH_ID] = BUILD_POLICY_WITH_SIZE(BINARY, MAX_PHYS_ITEM_ID_LEN),
+ [IFLA_LINK_NETNSID] = BUILD_POLICY(S32),
+ [IFLA_PHYS_PORT_NAME] = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ - 1),
+ [IFLA_PROTO_DOWN] = BUILD_POLICY(U8),
+ [IFLA_GSO_MAX_SEGS] = BUILD_POLICY(U32),
+ [IFLA_GSO_MAX_SIZE] = BUILD_POLICY(U32),
+ [IFLA_XDP] = BUILD_POLICY_NESTED(rtnl_xdp),
+ [IFLA_EVENT] = BUILD_POLICY(U32),
+ [IFLA_NEW_NETNSID] = BUILD_POLICY(S32),
+ [IFLA_TARGET_NETNSID] = BUILD_POLICY(S32),
+ [IFLA_CARRIER_UP_COUNT] = BUILD_POLICY(U32),
+ [IFLA_CARRIER_DOWN_COUNT] = BUILD_POLICY(U32),
+ [IFLA_NEW_IFINDEX] = BUILD_POLICY(S32),
+ [IFLA_MIN_MTU] = BUILD_POLICY(U32),
+ [IFLA_MAX_MTU] = BUILD_POLICY(U32),
+ [IFLA_PROP_LIST] = BUILD_POLICY_NESTED(rtnl_prop_list),
+ [IFLA_ALT_IFNAME] = BUILD_POLICY_WITH_SIZE(STRING, ALTIFNAMSIZ - 1),
+ [IFLA_PERM_ADDRESS] = BUILD_POLICY(ETHER_ADDR),
+ [IFLA_PROTO_DOWN_REASON] = BUILD_POLICY_NESTED(rtnl_proto_down_reason),
+ [IFLA_PARENT_DEV_NAME] = BUILD_POLICY(STRING),
+ [IFLA_PARENT_DEV_BUS_NAME] = BUILD_POLICY(STRING),
+};
+
+DEFINE_POLICY_SET(rtnl_link);
/* IFA_FLAGS was defined in kernel 3.14, but we still support older
* kernels where IFA_MAX is lower. */
-static const NLType rtnl_address_types[] = {
- [IFA_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFA_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFA_LABEL] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
- [IFA_BROADCAST] = { .type = NETLINK_TYPE_IN_ADDR },
- [IFA_ANYCAST] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [IFA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct ifa_cacheinfo) },
- [IFA_MULTICAST] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [IFA_FLAGS] = { .type = NETLINK_TYPE_U32 },
- [IFA_RT_PRIORITY] = { .type = NETLINK_TYPE_U32 },
- [IFA_TARGET_NETNSID] = { .type = NETLINK_TYPE_S32 },
+static const NLAPolicy rtnl_address_policies[] = {
+ [IFA_ADDRESS] = BUILD_POLICY(IN_ADDR),
+ [IFA_LOCAL] = BUILD_POLICY(IN_ADDR),
+ [IFA_LABEL] = BUILD_POLICY_WITH_SIZE(STRING, IFNAMSIZ - 1),
+ [IFA_BROADCAST] = BUILD_POLICY(IN_ADDR),
+ [IFA_ANYCAST] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [IFA_CACHEINFO] = BUILD_POLICY_WITH_SIZE(CACHE_INFO, sizeof(struct ifa_cacheinfo)),
+ [IFA_MULTICAST] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [IFA_FLAGS] = BUILD_POLICY(U32),
+ [IFA_RT_PRIORITY] = BUILD_POLICY(U32),
+ [IFA_TARGET_NETNSID] = BUILD_POLICY(S32),
};
-DEFINE_TYPE_SYSTEM(rtnl_address);
+DEFINE_POLICY_SET(rtnl_address);
/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
-static const NLType rtnl_route_metrics_types[] = {
- [RTAX_MTU] = { .type = NETLINK_TYPE_U32 },
- [RTAX_WINDOW] = { .type = NETLINK_TYPE_U32 },
- [RTAX_RTT] = { .type = NETLINK_TYPE_U32 },
- [RTAX_RTTVAR] = { .type = NETLINK_TYPE_U32 },
- [RTAX_SSTHRESH] = { .type = NETLINK_TYPE_U32 },
- [RTAX_CWND] = { .type = NETLINK_TYPE_U32 },
- [RTAX_ADVMSS] = { .type = NETLINK_TYPE_U32 },
- [RTAX_REORDERING] = { .type = NETLINK_TYPE_U32 },
- [RTAX_HOPLIMIT] = { .type = NETLINK_TYPE_U32 },
- [RTAX_INITCWND] = { .type = NETLINK_TYPE_U32 },
- [RTAX_FEATURES] = { .type = NETLINK_TYPE_U32 },
- [RTAX_RTO_MIN] = { .type = NETLINK_TYPE_U32 },
- [RTAX_INITRWND] = { .type = NETLINK_TYPE_U32 },
- [RTAX_QUICKACK] = { .type = NETLINK_TYPE_U32 },
- [RTAX_CC_ALGO] = { .type = NETLINK_TYPE_U32 },
- [RTAX_FASTOPEN_NO_COOKIE] = { .type = NETLINK_TYPE_U32 },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_route_metrics);
-
-static const NLType rtnl_route_types[] = {
- [RTA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
- [RTA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
- [RTA_IIF] = { .type = NETLINK_TYPE_U32 },
- [RTA_OIF] = { .type = NETLINK_TYPE_U32 },
- [RTA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR },
- [RTA_PRIORITY] = { .type = NETLINK_TYPE_U32 },
- [RTA_PREFSRC] = { .type = NETLINK_TYPE_IN_ADDR }, /* 6? */
- [RTA_METRICS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_metrics_type_system },
- [RTA_MULTIPATH] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct rtnexthop) },
- [RTA_FLOW] = { .type = NETLINK_TYPE_U32 }, /* 6? */
- [RTA_CACHEINFO] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct rta_cacheinfo) },
- [RTA_TABLE] = { .type = NETLINK_TYPE_U32 },
- [RTA_MARK] = { .type = NETLINK_TYPE_U32 },
- [RTA_MFC_STATS] = { .type = NETLINK_TYPE_U64 },
- [RTA_VIA] = { /* See struct rtvia */ },
- [RTA_NEWDST] = { .type = NETLINK_TYPE_U32 },
- [RTA_PREF] = { .type = NETLINK_TYPE_U8 },
- [RTA_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
+static const NLAPolicy rtnl_route_metrics_policies[] = {
+ [RTAX_MTU] = BUILD_POLICY(U32),
+ [RTAX_WINDOW] = BUILD_POLICY(U32),
+ [RTAX_RTT] = BUILD_POLICY(U32),
+ [RTAX_RTTVAR] = BUILD_POLICY(U32),
+ [RTAX_SSTHRESH] = BUILD_POLICY(U32),
+ [RTAX_CWND] = BUILD_POLICY(U32),
+ [RTAX_ADVMSS] = BUILD_POLICY(U32),
+ [RTAX_REORDERING] = BUILD_POLICY(U32),
+ [RTAX_HOPLIMIT] = BUILD_POLICY(U32),
+ [RTAX_INITCWND] = BUILD_POLICY(U32),
+ [RTAX_FEATURES] = BUILD_POLICY(U32),
+ [RTAX_RTO_MIN] = BUILD_POLICY(U32),
+ [RTAX_INITRWND] = BUILD_POLICY(U32),
+ [RTAX_QUICKACK] = BUILD_POLICY(U32),
+ [RTAX_CC_ALGO] = BUILD_POLICY(U32),
+ [RTAX_FASTOPEN_NO_COOKIE] = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_route_metrics);
+
+static const NLAPolicy rtnl_route_policies[] = {
+ [RTA_DST] = BUILD_POLICY(IN_ADDR),
+ [RTA_SRC] = BUILD_POLICY(IN_ADDR),
+ [RTA_IIF] = BUILD_POLICY(U32),
+ [RTA_OIF] = BUILD_POLICY(U32),
+ [RTA_GATEWAY] = BUILD_POLICY(IN_ADDR),
+ [RTA_PRIORITY] = BUILD_POLICY(U32),
+ [RTA_PREFSRC] = BUILD_POLICY(IN_ADDR),
+ [RTA_METRICS] = BUILD_POLICY_NESTED(rtnl_route_metrics),
+ [RTA_MULTIPATH] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rtnexthop)),
+ [RTA_FLOW] = BUILD_POLICY(U32),
+ [RTA_CACHEINFO] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct rta_cacheinfo)),
+ [RTA_TABLE] = BUILD_POLICY(U32),
+ [RTA_MARK] = BUILD_POLICY(U32),
+ [RTA_MFC_STATS] = BUILD_POLICY(U64),
+ [RTA_VIA] = BUILD_POLICY(BINARY), /* See struct rtvia */
+ [RTA_NEWDST] = BUILD_POLICY(U32),
+ [RTA_PREF] = BUILD_POLICY(U8),
+ [RTA_ENCAP_TYPE] = BUILD_POLICY(U16),
[RTA_ENCAP] = { .type = NETLINK_TYPE_NESTED }, /* Multiple type systems i.e. LWTUNNEL_ENCAP_MPLS/LWTUNNEL_ENCAP_IP/LWTUNNEL_ENCAP_ILA etc... */
- [RTA_EXPIRES] = { .type = NETLINK_TYPE_U32 },
- [RTA_UID] = { .type = NETLINK_TYPE_U32 },
- [RTA_TTL_PROPAGATE] = { .type = NETLINK_TYPE_U8 },
- [RTA_IP_PROTO] = { .type = NETLINK_TYPE_U8 },
- [RTA_SPORT] = { .type = NETLINK_TYPE_U16 },
- [RTA_DPORT] = { .type = NETLINK_TYPE_U16 },
- [RTA_NH_ID] = { .type = NETLINK_TYPE_U32 },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_route);
-
-static const NLType rtnl_neigh_types[] = {
- [NDA_DST] = { .type = NETLINK_TYPE_IN_ADDR },
- [NDA_LLADDR] = { .type = NETLINK_TYPE_ETHER_ADDR },
- [NDA_CACHEINFO] = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) },
- [NDA_PROBES] = { .type = NETLINK_TYPE_U32 },
- [NDA_VLAN] = { .type = NETLINK_TYPE_U16 },
- [NDA_PORT] = { .type = NETLINK_TYPE_U16 },
- [NDA_VNI] = { .type = NETLINK_TYPE_U32 },
- [NDA_IFINDEX] = { .type = NETLINK_TYPE_U32 },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_neigh);
-
-static const NLType rtnl_addrlabel_types[] = {
- [IFAL_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) },
- [IFAL_LABEL] = { .type = NETLINK_TYPE_U32 },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_addrlabel);
-
-static const NLType rtnl_routing_policy_rule_types[] = {
- [FRA_DST] = { .type = NETLINK_TYPE_IN_ADDR },
- [FRA_SRC] = { .type = NETLINK_TYPE_IN_ADDR },
- [FRA_IIFNAME] = { .type = NETLINK_TYPE_STRING },
- [FRA_GOTO] = { .type = NETLINK_TYPE_U32 },
- [FRA_PRIORITY] = { .type = NETLINK_TYPE_U32 },
- [FRA_FWMARK] = { .type = NETLINK_TYPE_U32 },
- [FRA_FLOW] = { .type = NETLINK_TYPE_U32 },
- [FRA_TUN_ID] = { .type = NETLINK_TYPE_U64 },
- [FRA_SUPPRESS_IFGROUP] = { .type = NETLINK_TYPE_U32 },
- [FRA_SUPPRESS_PREFIXLEN] = { .type = NETLINK_TYPE_U32 },
- [FRA_TABLE] = { .type = NETLINK_TYPE_U32 },
- [FRA_FWMASK] = { .type = NETLINK_TYPE_U32 },
- [FRA_OIFNAME] = { .type = NETLINK_TYPE_STRING },
- [FRA_PAD] = { .type = NETLINK_TYPE_U32 },
- [FRA_L3MDEV] = { .type = NETLINK_TYPE_U8 },
- [FRA_UID_RANGE] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct fib_rule_uid_range) },
- [FRA_PROTOCOL] = { .type = NETLINK_TYPE_U8 },
- [FRA_IP_PROTO] = { .type = NETLINK_TYPE_U8 },
- [FRA_SPORT_RANGE] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct fib_rule_port_range) },
- [FRA_DPORT_RANGE] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct fib_rule_port_range) },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_routing_policy_rule);
-
-static const NLType rtnl_nexthop_types[] = {
- [NHA_ID] = { .type = NETLINK_TYPE_U32 },
+ [RTA_EXPIRES] = BUILD_POLICY(U32),
+ [RTA_UID] = BUILD_POLICY(U32),
+ [RTA_TTL_PROPAGATE] = BUILD_POLICY(U8),
+ [RTA_IP_PROTO] = BUILD_POLICY(U8),
+ [RTA_SPORT] = BUILD_POLICY(U16),
+ [RTA_DPORT] = BUILD_POLICY(U16),
+ [RTA_NH_ID] = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_route);
+
+static const NLAPolicy rtnl_neigh_policies[] = {
+ [NDA_DST] = BUILD_POLICY(IN_ADDR),
+ [NDA_LLADDR] = BUILD_POLICY(ETHER_ADDR),
+ [NDA_CACHEINFO] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct nda_cacheinfo)),
+ [NDA_PROBES] = BUILD_POLICY(U32),
+ [NDA_VLAN] = BUILD_POLICY(U16),
+ [NDA_PORT] = BUILD_POLICY(U16),
+ [NDA_VNI] = BUILD_POLICY(U32),
+ [NDA_IFINDEX] = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_neigh);
+
+static const NLAPolicy rtnl_addrlabel_policies[] = {
+ [IFAL_ADDRESS] = BUILD_POLICY_WITH_SIZE(IN_ADDR, sizeof(struct in6_addr)),
+ [IFAL_LABEL] = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_addrlabel);
+
+static const NLAPolicy rtnl_routing_policy_rule_policies[] = {
+ [FRA_DST] = BUILD_POLICY(IN_ADDR),
+ [FRA_SRC] = BUILD_POLICY(IN_ADDR),
+ [FRA_IIFNAME] = BUILD_POLICY(STRING),
+ [FRA_GOTO] = BUILD_POLICY(U32),
+ [FRA_PRIORITY] = BUILD_POLICY(U32),
+ [FRA_FWMARK] = BUILD_POLICY(U32),
+ [FRA_FLOW] = BUILD_POLICY(U32),
+ [FRA_TUN_ID] = BUILD_POLICY(U64),
+ [FRA_SUPPRESS_IFGROUP] = BUILD_POLICY(U32),
+ [FRA_SUPPRESS_PREFIXLEN] = BUILD_POLICY(U32),
+ [FRA_TABLE] = BUILD_POLICY(U32),
+ [FRA_FWMASK] = BUILD_POLICY(U32),
+ [FRA_OIFNAME] = BUILD_POLICY(STRING),
+ [FRA_PAD] = BUILD_POLICY(U32),
+ [FRA_L3MDEV] = BUILD_POLICY(U8),
+ [FRA_UID_RANGE] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct fib_rule_uid_range)),
+ [FRA_PROTOCOL] = BUILD_POLICY(U8),
+ [FRA_IP_PROTO] = BUILD_POLICY(U8),
+ [FRA_SPORT_RANGE] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct fib_rule_port_range)),
+ [FRA_DPORT_RANGE] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct fib_rule_port_range)),
+};
+
+DEFINE_POLICY_SET(rtnl_routing_policy_rule);
+
+static const NLAPolicy rtnl_nexthop_policies[] = {
+ [NHA_ID] = BUILD_POLICY(U32),
[NHA_GROUP] = { /* array of struct nexthop_grp */ },
- [NHA_GROUP_TYPE] = { .type = NETLINK_TYPE_U16 },
- [NHA_BLACKHOLE] = { .type = NETLINK_TYPE_FLAG },
- [NHA_OIF] = { .type = NETLINK_TYPE_U32 },
- [NHA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR },
- [NHA_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 },
+ [NHA_GROUP_TYPE] = BUILD_POLICY(U16),
+ [NHA_BLACKHOLE] = BUILD_POLICY(FLAG),
+ [NHA_OIF] = BUILD_POLICY(U32),
+ [NHA_GATEWAY] = BUILD_POLICY(IN_ADDR),
+ [NHA_ENCAP_TYPE] = BUILD_POLICY(U16),
[NHA_ENCAP] = { .type = NETLINK_TYPE_NESTED },
- [NHA_GROUPS] = { .type = NETLINK_TYPE_FLAG },
- [NHA_MASTER] = { .type = NETLINK_TYPE_U32 },
- [NHA_FDB] = { .type = NETLINK_TYPE_FLAG },
+ [NHA_GROUPS] = BUILD_POLICY(FLAG),
+ [NHA_MASTER] = BUILD_POLICY(U32),
+ [NHA_FDB] = BUILD_POLICY(FLAG),
};
-DEFINE_TYPE_SYSTEM(rtnl_nexthop);
+DEFINE_POLICY_SET(rtnl_nexthop);
-static const NLType rtnl_tca_option_data_cake_types[] = {
- [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
- [TCA_CAKE_DIFFSERV_MODE] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_ATM] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_FLOW_MODE] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_OVERHEAD] = { .type = NETLINK_TYPE_S32 },
- [TCA_CAKE_RTT] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_TARGET] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_AUTORATE] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_MEMORY] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_NAT] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_RAW] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_WASH] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_MPU] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_INGRESS] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_ACK_FILTER] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_SPLIT_GSO] = { .type = NETLINK_TYPE_U32 },
- [TCA_CAKE_FWMARK] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_tca_option_data_cake_policies[] = {
+ [TCA_CAKE_BASE_RATE64] = BUILD_POLICY(U64),
+ [TCA_CAKE_DIFFSERV_MODE] = BUILD_POLICY(U32),
+ [TCA_CAKE_ATM] = BUILD_POLICY(U32),
+ [TCA_CAKE_FLOW_MODE] = BUILD_POLICY(U32),
+ [TCA_CAKE_OVERHEAD] = BUILD_POLICY(S32),
+ [TCA_CAKE_RTT] = BUILD_POLICY(U32),
+ [TCA_CAKE_TARGET] = BUILD_POLICY(U32),
+ [TCA_CAKE_AUTORATE] = BUILD_POLICY(U32),
+ [TCA_CAKE_MEMORY] = BUILD_POLICY(U32),
+ [TCA_CAKE_NAT] = BUILD_POLICY(U32),
+ [TCA_CAKE_RAW] = BUILD_POLICY(U32),
+ [TCA_CAKE_WASH] = BUILD_POLICY(U32),
+ [TCA_CAKE_MPU] = BUILD_POLICY(U32),
+ [TCA_CAKE_INGRESS] = BUILD_POLICY(U32),
+ [TCA_CAKE_ACK_FILTER] = BUILD_POLICY(U32),
+ [TCA_CAKE_SPLIT_GSO] = BUILD_POLICY(U32),
+ [TCA_CAKE_FWMARK] = BUILD_POLICY(U32),
};
-static const NLType rtnl_tca_option_data_codel_types[] = {
- [TCA_CODEL_TARGET] = { .type = NETLINK_TYPE_U32 },
- [TCA_CODEL_LIMIT] = { .type = NETLINK_TYPE_U32 },
- [TCA_CODEL_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [TCA_CODEL_ECN] = { .type = NETLINK_TYPE_U32 },
- [TCA_CODEL_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_tca_option_data_codel_policies[] = {
+ [TCA_CODEL_TARGET] = BUILD_POLICY(U32),
+ [TCA_CODEL_LIMIT] = BUILD_POLICY(U32),
+ [TCA_CODEL_INTERVAL] = BUILD_POLICY(U32),
+ [TCA_CODEL_ECN] = BUILD_POLICY(U32),
+ [TCA_CODEL_CE_THRESHOLD] = BUILD_POLICY(U32),
};
-static const NLType rtnl_tca_option_data_drr_types[] = {
- [TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_tca_option_data_drr_policies[] = {
+ [TCA_DRR_QUANTUM] = BUILD_POLICY(U32),
};
-static const NLType rtnl_tca_option_data_ets_quanta_types[] = {
- [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32, },
+static const NLAPolicy rtnl_tca_option_data_ets_quanta_policies[] = {
+ [TCA_ETS_QUANTA_BAND] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(rtnl_tca_option_data_ets_quanta);
+DEFINE_POLICY_SET(rtnl_tca_option_data_ets_quanta);
-static const NLType rtnl_tca_option_data_ets_prio_types[] = {
- [TCA_ETS_PRIOMAP_BAND] = { .type = NETLINK_TYPE_U8, },
+static const NLAPolicy rtnl_tca_option_data_ets_prio_policies[] = {
+ [TCA_ETS_PRIOMAP_BAND] = BUILD_POLICY(U8),
};
-DEFINE_TYPE_SYSTEM(rtnl_tca_option_data_ets_prio);
+DEFINE_POLICY_SET(rtnl_tca_option_data_ets_prio);
-static const NLType rtnl_tca_option_data_ets_types[] = {
- [TCA_ETS_NBANDS] = { .type = NETLINK_TYPE_U8 },
- [TCA_ETS_NSTRICT] = { .type = NETLINK_TYPE_U8 },
- [TCA_ETS_QUANTA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_quanta_type_system },
- [TCA_ETS_PRIOMAP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_prio_type_system },
- [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_tca_option_data_ets_policies[] = {
+ [TCA_ETS_NBANDS] = BUILD_POLICY(U8),
+ [TCA_ETS_NSTRICT] = BUILD_POLICY(U8),
+ [TCA_ETS_QUANTA] = BUILD_POLICY_NESTED(rtnl_tca_option_data_ets_quanta),
+ [TCA_ETS_PRIOMAP] = BUILD_POLICY_NESTED(rtnl_tca_option_data_ets_prio),
+ [TCA_ETS_QUANTA_BAND] = BUILD_POLICY(U32),
};
-static const NLType rtnl_tca_option_data_fq_types[] = {
- [TCA_FQ_PLIMIT] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_FLOW_PLIMIT] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_QUANTUM] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_INITIAL_QUANTUM] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_RATE_ENABLE] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_FLOW_DEFAULT_RATE] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_FLOW_MAX_RATE] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_BUCKETS_LOG] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_FLOW_REFILL_DELAY] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_LOW_RATE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_ORPHAN_MASK] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_tca_option_data_fq_policies[] = {
+ [TCA_FQ_PLIMIT] = BUILD_POLICY(U32),
+ [TCA_FQ_FLOW_PLIMIT] = BUILD_POLICY(U32),
+ [TCA_FQ_QUANTUM] = BUILD_POLICY(U32),
+ [TCA_FQ_INITIAL_QUANTUM] = BUILD_POLICY(U32),
+ [TCA_FQ_RATE_ENABLE] = BUILD_POLICY(U32),
+ [TCA_FQ_FLOW_DEFAULT_RATE] = BUILD_POLICY(U32),
+ [TCA_FQ_FLOW_MAX_RATE] = BUILD_POLICY(U32),
+ [TCA_FQ_BUCKETS_LOG] = BUILD_POLICY(U32),
+ [TCA_FQ_FLOW_REFILL_DELAY] = BUILD_POLICY(U32),
+ [TCA_FQ_LOW_RATE_THRESHOLD] = BUILD_POLICY(U32),
+ [TCA_FQ_CE_THRESHOLD] = BUILD_POLICY(U32),
+ [TCA_FQ_ORPHAN_MASK] = BUILD_POLICY(U32),
};
-static const NLType rtnl_tca_option_data_fq_codel_types[] = {
- [TCA_FQ_CODEL_TARGET] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_LIMIT] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_INTERVAL] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_ECN] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_FLOWS] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_QUANTUM] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_CE_THRESHOLD] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_DROP_BATCH_SIZE] = { .type = NETLINK_TYPE_U32 },
- [TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_tca_option_data_fq_codel_policies[] = {
+ [TCA_FQ_CODEL_TARGET] = BUILD_POLICY(U32),
+ [TCA_FQ_CODEL_LIMIT] = BUILD_POLICY(U32),
+ [TCA_FQ_CODEL_INTERVAL] = BUILD_POLICY(U32),
+ [TCA_FQ_CODEL_ECN] = BUILD_POLICY(U32),
+ [TCA_FQ_CODEL_FLOWS] = BUILD_POLICY(U32),
+ [TCA_FQ_CODEL_QUANTUM] = BUILD_POLICY(U32),
+ [TCA_FQ_CODEL_CE_THRESHOLD] = BUILD_POLICY(U32),
+ [TCA_FQ_CODEL_DROP_BATCH_SIZE] = BUILD_POLICY(U32),
+ [TCA_FQ_CODEL_MEMORY_LIMIT] = BUILD_POLICY(U32),
};
-static const NLType rtnl_tca_option_data_fq_pie_types[] = {
- [TCA_FQ_PIE_LIMIT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_gred_types[] = {
- [TCA_GRED_DPS] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct tc_gred_sopt) },
-};
-
-static const NLType rtnl_tca_option_data_hhf_types[] = {
- [TCA_HHF_BACKLOG_LIMIT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_htb_types[] = {
- [TCA_HTB_PARMS] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct tc_htb_opt) },
- [TCA_HTB_INIT] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct tc_htb_glob) },
- [TCA_HTB_CTAB] = { .type = NETLINK_TYPE_BINARY, .size = TC_RTAB_SIZE },
- [TCA_HTB_RTAB] = { .type = NETLINK_TYPE_BINARY, .size = TC_RTAB_SIZE },
- [TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 },
- [TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 },
-};
-
-static const NLType rtnl_tca_option_data_pie_types[] = {
- [TCA_PIE_LIMIT] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_qfq_types[] = {
- [TCA_QFQ_WEIGHT] = { .type = NETLINK_TYPE_U32 },
- [TCA_QFQ_LMAX] = { .type = NETLINK_TYPE_U32 },
-};
-
-static const NLType rtnl_tca_option_data_sfb_types[] = {
- [TCA_SFB_PARMS] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct tc_sfb_qopt) },
+static const NLAPolicy rtnl_tca_option_data_fq_pie_policies[] = {
+ [TCA_FQ_PIE_LIMIT] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_tca_option_data_gred_policies[] = {
+ [TCA_GRED_DPS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_gred_sopt)),
+};
+
+static const NLAPolicy rtnl_tca_option_data_hhf_policies[] = {
+ [TCA_HHF_BACKLOG_LIMIT] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_tca_option_data_htb_policies[] = {
+ [TCA_HTB_PARMS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_htb_opt)),
+ [TCA_HTB_INIT] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_htb_glob)),
+ [TCA_HTB_CTAB] = BUILD_POLICY_WITH_SIZE(BINARY, TC_RTAB_SIZE),
+ [TCA_HTB_RTAB] = BUILD_POLICY_WITH_SIZE(BINARY, TC_RTAB_SIZE),
+ [TCA_HTB_RATE64] = BUILD_POLICY(U64),
+ [TCA_HTB_CEIL64] = BUILD_POLICY(U64),
+};
+
+static const NLAPolicy rtnl_tca_option_data_pie_policies[] = {
+ [TCA_PIE_LIMIT] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_tca_option_data_qfq_policies[] = {
+ [TCA_QFQ_WEIGHT] = BUILD_POLICY(U32),
+ [TCA_QFQ_LMAX] = BUILD_POLICY(U32),
+};
+
+static const NLAPolicy rtnl_tca_option_data_sfb_policies[] = {
+ [TCA_SFB_PARMS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_sfb_qopt)),
};
-static const NLType rtnl_tca_option_data_tbf_types[] = {
- [TCA_TBF_PARMS] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct tc_tbf_qopt) },
- [TCA_TBF_RTAB] = { .type = NETLINK_TYPE_BINARY, .size = TC_RTAB_SIZE },
- [TCA_TBF_PTAB] = { .type = NETLINK_TYPE_BINARY, .size = TC_RTAB_SIZE },
- [TCA_TBF_RATE64] = { .type = NETLINK_TYPE_U64 },
- [TCA_TBF_PRATE64] = { .type = NETLINK_TYPE_U64 },
- [TCA_TBF_BURST] = { .type = NETLINK_TYPE_U32 },
- [TCA_TBF_PBURST] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy rtnl_tca_option_data_tbf_policies[] = {
+ [TCA_TBF_PARMS] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct tc_tbf_qopt)),
+ [TCA_TBF_RTAB] = BUILD_POLICY_WITH_SIZE(BINARY, TC_RTAB_SIZE),
+ [TCA_TBF_PTAB] = BUILD_POLICY_WITH_SIZE(BINARY, TC_RTAB_SIZE),
+ [TCA_TBF_RATE64] = BUILD_POLICY(U64),
+ [TCA_TBF_PRATE64] = BUILD_POLICY(U64),
+ [TCA_TBF_BURST] = BUILD_POLICY(U32),
+ [TCA_TBF_PBURST] = BUILD_POLICY(U32),
};
-static const NLTypeSystemUnionElement rtnl_tca_option_data_type_systems[] = {
- { .name = "cake", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_cake), },
- { .name = "codel", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_codel), },
- { .name = "drr", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_drr), },
- { .name = "ets", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_ets), },
- { .name = "fq", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_fq), },
- { .name = "fq_codel", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_fq_codel), },
- { .name = "fq_pie", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_fq_pie), },
- { .name = "gred", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_gred), },
- { .name = "hhf", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_hhf), },
- { .name = "htb", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_htb), },
- { .name = "pie", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_pie), },
- { .name = "qfq", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_qfq), },
- { .name = "sfb", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_sfb), },
- { .name = "tbf", .type_system = TYPE_SYSTEM_FROM_TYPE(rtnl_tca_option_data_tbf), },
-};
-
-DEFINE_TYPE_SYSTEM_UNION_MATCH_SIBLING(rtnl_tca_option_data, TCA_KIND);
-
-static const NLType rtnl_tca_types[] = {
- [TCA_KIND] = { .type = NETLINK_TYPE_STRING },
- [TCA_OPTIONS] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_tca_option_data_type_system_union },
- [TCA_INGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
- [TCA_EGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_tca);
-
-static const NLType rtnl_mdb_types[] = {
- [MDBA_SET_ENTRY] = { .type = NETLINK_TYPE_BINARY, .size = sizeof(struct br_port_msg) },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl_mdb);
-
-static const NLType rtnl_types[] = {
- [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_SETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_NEWLINKPROP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_DELLINKPROP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_GETLINKPROP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
- [RTM_NEWADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
- [RTM_DELADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
- [RTM_GETADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) },
- [RTM_NEWROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
- [RTM_DELROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
- [RTM_GETROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) },
- [RTM_NEWNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
- [RTM_DELNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
- [RTM_GETNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) },
- [RTM_NEWADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
- [RTM_DELADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
- [RTM_GETADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) },
- [RTM_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
- [RTM_DELRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
- [RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct fib_rule_hdr) },
- [RTM_NEWNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
- [RTM_DELNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
- [RTM_GETNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
- [RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_NEWTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_DELTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_GETTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
- [RTM_NEWMDB] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
- [RTM_DELMDB] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
- [RTM_GETMDB] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_mdb_type_system, .size = sizeof(struct br_port_msg) },
-};
-
-DEFINE_TYPE_SYSTEM(rtnl);
-
-const NLType *rtnl_get_type(uint16_t nlmsg_type) {
- return type_system_get_type(&rtnl_type_system, nlmsg_type);
+static const NLAPolicySetUnionElement rtnl_tca_option_data_policy_set_union_elements[] = {
+ BUILD_UNION_ELEMENT_BY_STRING("cake", rtnl_tca_option_data_cake),
+ BUILD_UNION_ELEMENT_BY_STRING("codel", rtnl_tca_option_data_codel),
+ BUILD_UNION_ELEMENT_BY_STRING("drr", rtnl_tca_option_data_drr),
+ BUILD_UNION_ELEMENT_BY_STRING("ets", rtnl_tca_option_data_ets),
+ BUILD_UNION_ELEMENT_BY_STRING("fq", rtnl_tca_option_data_fq),
+ BUILD_UNION_ELEMENT_BY_STRING("fq_codel", rtnl_tca_option_data_fq_codel),
+ BUILD_UNION_ELEMENT_BY_STRING("fq_pie", rtnl_tca_option_data_fq_pie),
+ BUILD_UNION_ELEMENT_BY_STRING("gred", rtnl_tca_option_data_gred),
+ BUILD_UNION_ELEMENT_BY_STRING("hhf", rtnl_tca_option_data_hhf),
+ BUILD_UNION_ELEMENT_BY_STRING("htb", rtnl_tca_option_data_htb),
+ BUILD_UNION_ELEMENT_BY_STRING("pie", rtnl_tca_option_data_pie),
+ BUILD_UNION_ELEMENT_BY_STRING("qfq", rtnl_tca_option_data_qfq),
+ BUILD_UNION_ELEMENT_BY_STRING("sfb", rtnl_tca_option_data_sfb),
+ BUILD_UNION_ELEMENT_BY_STRING("tbf", rtnl_tca_option_data_tbf),
+};
+
+DEFINE_POLICY_SET_UNION(rtnl_tca_option_data, TCA_KIND);
+
+static const NLAPolicy rtnl_tca_policies[] = {
+ [TCA_KIND] = BUILD_POLICY(STRING),
+ [TCA_OPTIONS] = BUILD_POLICY_NESTED_UNION_BY_STRING(rtnl_tca_option_data),
+ [TCA_INGRESS_BLOCK] = BUILD_POLICY(U32),
+ [TCA_EGRESS_BLOCK] = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_tca);
+
+static const NLAPolicy rtnl_mdb_policies[] = {
+ [MDBA_SET_ENTRY] = BUILD_POLICY_WITH_SIZE(BINARY, sizeof(struct br_port_msg)),
+};
+
+DEFINE_POLICY_SET(rtnl_mdb);
+
+static const NLAPolicy rtnl_policies[] = {
+ [RTM_NEWLINK] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+ [RTM_DELLINK] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+ [RTM_GETLINK] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+ [RTM_SETLINK] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+ [RTM_NEWLINKPROP] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+ [RTM_DELLINKPROP] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+ [RTM_GETLINKPROP] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
+ [RTM_NEWADDR] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_address, sizeof(struct ifaddrmsg)),
+ [RTM_DELADDR] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_address, sizeof(struct ifaddrmsg)),
+ [RTM_GETADDR] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_address, sizeof(struct ifaddrmsg)),
+ [RTM_NEWROUTE] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_route, sizeof(struct rtmsg)),
+ [RTM_DELROUTE] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_route, sizeof(struct rtmsg)),
+ [RTM_GETROUTE] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_route, sizeof(struct rtmsg)),
+ [RTM_NEWNEIGH] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_neigh, sizeof(struct ndmsg)),
+ [RTM_DELNEIGH] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_neigh, sizeof(struct ndmsg)),
+ [RTM_GETNEIGH] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_neigh, sizeof(struct ndmsg)),
+ [RTM_NEWADDRLABEL] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_addrlabel, sizeof(struct ifaddrlblmsg)),
+ [RTM_DELADDRLABEL] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_addrlabel, sizeof(struct ifaddrlblmsg)),
+ [RTM_GETADDRLABEL] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_addrlabel, sizeof(struct ifaddrlblmsg)),
+ [RTM_NEWRULE] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_routing_policy_rule, sizeof(struct fib_rule_hdr)),
+ [RTM_DELRULE] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_routing_policy_rule, sizeof(struct fib_rule_hdr)),
+ [RTM_GETRULE] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_routing_policy_rule, sizeof(struct fib_rule_hdr)),
+ [RTM_NEWNEXTHOP] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nexthop, sizeof(struct nhmsg)),
+ [RTM_DELNEXTHOP] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nexthop, sizeof(struct nhmsg)),
+ [RTM_GETNEXTHOP] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nexthop, sizeof(struct nhmsg)),
+ [RTM_NEWQDISC] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+ [RTM_DELQDISC] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+ [RTM_GETQDISC] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+ [RTM_NEWTCLASS] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+ [RTM_DELTCLASS] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+ [RTM_GETTCLASS] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_tca, sizeof(struct tcmsg)),
+ [RTM_NEWMDB] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
+ [RTM_DELMDB] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
+ [RTM_GETMDB] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
+};
+
+DEFINE_POLICY_SET(rtnl);
+
+const NLAPolicy *rtnl_get_policy(uint16_t nlmsg_type) {
+ return policy_set_get_policy(&rtnl_policy_set, nlmsg_type);
}
#include "netlink-internal.h"
#include "netlink-types-internal.h"
-static const NLType empty_types[1] = {
+static const NLAPolicy empty_policies[1] = {
/* fake array to avoid .types==NULL, which denotes invalid type-systems */
};
-DEFINE_TYPE_SYSTEM(empty);
+DEFINE_POLICY_SET(empty);
-static const NLType error_types[] = {
- [NLMSGERR_ATTR_MSG] = { .type = NETLINK_TYPE_STRING },
- [NLMSGERR_ATTR_OFFS] = { .type = NETLINK_TYPE_U32 },
+static const NLAPolicy error_policies[] = {
+ [NLMSGERR_ATTR_MSG] = BUILD_POLICY(STRING),
+ [NLMSGERR_ATTR_OFFS] = BUILD_POLICY(U32),
};
-DEFINE_TYPE_SYSTEM(error);
+DEFINE_POLICY_SET(error);
-static const NLType basic_types[] = {
- [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system },
- [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
+static const NLAPolicy basic_policies[] = {
+ [NLMSG_DONE] = BUILD_POLICY_NESTED(empty),
+ [NLMSG_ERROR] = BUILD_POLICY_NESTED_WITH_SIZE(error, sizeof(struct nlmsgerr)),
};
-DEFINE_TYPE_SYSTEM(basic);
+DEFINE_POLICY_SET(basic);
-uint16_t type_get_type(const NLType *type) {
- assert(type);
- return type->type;
+NLAType policy_get_type(const NLAPolicy *policy) {
+ return ASSERT_PTR(policy)->type;
}
-size_t type_get_size(const NLType *type) {
- assert(type);
- return type->size;
+size_t policy_get_size(const NLAPolicy *policy) {
+ return ASSERT_PTR(policy)->size;
}
-const NLTypeSystem *type_get_type_system(const NLType *nl_type) {
- assert(nl_type);
- assert(nl_type->type == NETLINK_TYPE_NESTED);
- assert(nl_type->type_system);
- return nl_type->type_system;
+const NLAPolicySet *policy_get_policy_set(const NLAPolicy *policy) {
+ assert(policy);
+ assert(policy->type == NETLINK_TYPE_NESTED);
+
+ return ASSERT_PTR(policy->policy_set);
}
-const NLTypeSystemUnion *type_get_type_system_union(const NLType *nl_type) {
- assert(nl_type);
- assert(nl_type->type == NETLINK_TYPE_UNION);
- assert(nl_type->type_system_union);
- return nl_type->type_system_union;
+const NLAPolicySetUnion *policy_get_policy_set_union(const NLAPolicy *policy) {
+ assert(policy);
+ assert(IN_SET(policy->type, NETLINK_TYPE_NESTED_UNION_BY_STRING, NETLINK_TYPE_NESTED_UNION_BY_FAMILY));
+
+ return ASSERT_PTR(policy->policy_set_union);
}
-int type_system_root_get_type_system_and_header_size(
+int netlink_get_policy_set_and_header_size(
sd_netlink *nl,
uint16_t type,
- const NLTypeSystem **ret_type_system,
+ const NLAPolicySet **ret_policy_set,
size_t *ret_header_size) {
- const NLType *nl_type;
+ const NLAPolicy *policy;
assert(nl);
if (IN_SET(type, NLMSG_DONE, NLMSG_ERROR))
- nl_type = type_system_get_type(&basic_type_system, type);
+ policy = policy_set_get_policy(&basic_policy_set, type);
else
switch (nl->protocol) {
case NETLINK_ROUTE:
- nl_type = rtnl_get_type(type);
+ policy = rtnl_get_policy(type);
break;
case NETLINK_NETFILTER:
- nl_type = nfnl_get_type(type);
+ policy = nfnl_get_policy(type);
break;
case NETLINK_GENERIC:
- return genl_get_type_system_and_header_size(nl, type, ret_type_system, ret_header_size);
+ return genl_get_policy_set_and_header_size(nl, type, ret_policy_set, ret_header_size);
default:
return -EOPNOTSUPP;
}
- if (!nl_type)
+ if (!policy)
return -EOPNOTSUPP;
- if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
+ if (policy_get_type(policy) != NETLINK_TYPE_NESTED)
return -EOPNOTSUPP;
- if (ret_type_system)
- *ret_type_system = type_get_type_system(nl_type);
+ if (ret_policy_set)
+ *ret_policy_set = policy_get_policy_set(policy);
if (ret_header_size)
- *ret_header_size = type_get_size(nl_type);
+ *ret_header_size = policy_get_size(policy);
return 0;
}
-const NLType *type_system_get_type(const NLTypeSystem *type_system, uint16_t type) {
- const NLType *nl_type;
+const NLAPolicy *policy_set_get_policy(const NLAPolicySet *policy_set, uint16_t attr_type) {
+ const NLAPolicy *policy;
- assert(type_system);
- assert(type_system->types);
+ assert(policy_set);
+ assert(policy_set->policies);
- if (type >= type_system->count)
+ if (attr_type >= policy_set->count)
return NULL;
- nl_type = &type_system->types[type];
+ policy = &policy_set->policies[attr_type];
- if (nl_type->type == NETLINK_TYPE_UNSPEC)
+ if (policy->type == NETLINK_TYPE_UNSPEC)
return NULL;
- return nl_type;
+ return policy;
}
-const NLTypeSystem *type_system_get_type_system(const NLTypeSystem *type_system, uint16_t type) {
- const NLType *nl_type;
+const NLAPolicySet *policy_set_get_policy_set(const NLAPolicySet *policy_set, uint16_t attr_type) {
+ const NLAPolicy *policy;
- nl_type = type_system_get_type(type_system, type);
- if (!nl_type)
+ policy = policy_set_get_policy(policy_set, attr_type);
+ if (!policy)
return NULL;
- return type_get_type_system(nl_type);
+ return policy_get_policy_set(policy);
}
-const NLTypeSystemUnion *type_system_get_type_system_union(const NLTypeSystem *type_system, uint16_t type) {
- const NLType *nl_type;
+const NLAPolicySetUnion *policy_set_get_policy_set_union(const NLAPolicySet *policy_set, uint16_t attr_type) {
+ const NLAPolicy *policy;
- nl_type = type_system_get_type(type_system, type);
- if (!nl_type)
+ policy = policy_set_get_policy(policy_set, attr_type);
+ if (!policy)
return NULL;
- return type_get_type_system_union(nl_type);
+ return policy_get_policy_set_union(policy);
}
-NLMatchType type_system_union_get_match_type(const NLTypeSystemUnion *type_system_union) {
- assert(type_system_union);
- return type_system_union->match_type;
-}
+uint16_t policy_set_union_get_match_attribute(const NLAPolicySetUnion *policy_set_union) {
+ assert(policy_set_union->match_attribute != 0);
-uint16_t type_system_union_get_match_attribute(const NLTypeSystemUnion *type_system_union) {
- assert(type_system_union);
- assert(type_system_union->match_type == NL_MATCH_SIBLING);
- return type_system_union->match_attribute;
+ return policy_set_union->match_attribute;
}
-const NLTypeSystem *type_system_union_get_type_system_by_string(const NLTypeSystemUnion *type_system_union, const char *key) {
- assert(type_system_union);
- assert(type_system_union->elements);
- assert(type_system_union->match_type == NL_MATCH_SIBLING);
- assert(key);
+const NLAPolicySet *policy_set_union_get_policy_set_by_string(const NLAPolicySetUnion *policy_set_union, const char *string) {
+ assert(policy_set_union);
+ assert(policy_set_union->elements);
+ assert(string);
- for (size_t i = 0; i < type_system_union->count; i++)
- if (streq(type_system_union->elements[i].name, key))
- return &type_system_union->elements[i].type_system;
+ for (size_t i = 0; i < policy_set_union->count; i++)
+ if (streq(policy_set_union->elements[i].string, string))
+ return &policy_set_union->elements[i].policy_set;
return NULL;
}
-const NLTypeSystem *type_system_union_get_type_system_by_protocol(const NLTypeSystemUnion *type_system_union, uint16_t protocol) {
- assert(type_system_union);
- assert(type_system_union->elements);
- assert(type_system_union->match_type == NL_MATCH_PROTOCOL);
+const NLAPolicySet *policy_set_union_get_policy_set_by_family(const NLAPolicySetUnion *policy_set_union, int family) {
+ assert(policy_set_union);
+ assert(policy_set_union->elements);
- for (size_t i = 0; i < type_system_union->count; i++)
- if (type_system_union->elements[i].protocol == protocol)
- return &type_system_union->elements[i].type_system;
+ for (size_t i = 0; i < policy_set_union->count; i++)
+ if (policy_set_union->elements[i].family == family)
+ return &policy_set_union->elements[i].policy_set;
return NULL;
}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <errno.h>
+
#include "sd-netlink.h"
-enum {
- NETLINK_TYPE_UNSPEC,
+typedef enum NLAType {
+ NETLINK_TYPE_UNSPEC, /* NLA_UNSPEC */
+ NETLINK_TYPE_BINARY, /* NLA_BINARY */
+ NETLINK_TYPE_FLAG, /* NLA_FLAG */
NETLINK_TYPE_U8, /* NLA_U8 */
NETLINK_TYPE_U16, /* NLA_U16 */
NETLINK_TYPE_U32, /* NLA_U32 */
NETLINK_TYPE_S32, /* NLA_S32 */
NETLINK_TYPE_S64, /* NLA_S64 */
NETLINK_TYPE_STRING, /* NLA_STRING */
- NETLINK_TYPE_FLAG, /* NLA_FLAG */
+ NETLINK_TYPE_BITFIELD32, /* NLA_BITFIELD32 */
+ NETLINK_TYPE_REJECT, /* NLA_REJECT */
NETLINK_TYPE_IN_ADDR,
NETLINK_TYPE_ETHER_ADDR,
NETLINK_TYPE_CACHE_INFO,
- NETLINK_TYPE_NESTED, /* NLA_NESTED */
- NETLINK_TYPE_UNION,
NETLINK_TYPE_SOCKADDR,
- NETLINK_TYPE_BINARY,
- NETLINK_TYPE_BITFIELD32, /* NLA_BITFIELD32 */
- NETLINK_TYPE_REJECT, /* NLA_REJECT */
-};
-
-typedef enum NLMatchType {
- NL_MATCH_SIBLING,
- NL_MATCH_PROTOCOL,
-} NLMatchType;
-
-typedef struct NLTypeSystemUnion NLTypeSystemUnion;
-typedef struct NLTypeSystem NLTypeSystem;
-typedef struct NLType NLType;
-
-const NLType *rtnl_get_type(uint16_t nlmsg_type);
-const NLType *nfnl_get_type(uint16_t nlmsg_type);
-const NLTypeSystem *genl_get_type_system_by_name(const char *name);
-int genl_get_type_system_and_header_size(
+ NETLINK_TYPE_NESTED, /* NLA_NESTED */
+ NETLINK_TYPE_NESTED_UNION_BY_STRING,
+ NETLINK_TYPE_NESTED_UNION_BY_FAMILY,
+ _NETLINK_TYPE_MAX,
+ _NETLINK_TYPE_INVALID = -EINVAL,
+} NLAType;
+
+typedef struct NLAPolicy NLAPolicy;
+typedef struct NLAPolicySet NLAPolicySet;
+typedef struct NLAPolicySetUnion NLAPolicySetUnion;
+
+const NLAPolicy *rtnl_get_policy(uint16_t nlmsg_type);
+const NLAPolicy *nfnl_get_policy(uint16_t nlmsg_type);
+const NLAPolicySet *genl_get_policy_set_by_name(const char *name);
+int genl_get_policy_set_and_header_size(
sd_netlink *nl,
uint16_t id,
- const NLTypeSystem **ret_type_system,
+ const NLAPolicySet **ret_policy_set,
size_t *ret_header_size);
-uint16_t type_get_type(const NLType *type);
-size_t type_get_size(const NLType *type);
-const NLTypeSystem *type_get_type_system(const NLType *type);
-const NLTypeSystemUnion *type_get_type_system_union(const NLType *type);
+NLAType policy_get_type(const NLAPolicy *policy);
+size_t policy_get_size(const NLAPolicy *policy);
+const NLAPolicySet *policy_get_policy_set(const NLAPolicy *policy);
+const NLAPolicySetUnion *policy_get_policy_set_union(const NLAPolicy *policy);
-int type_system_root_get_type_system_and_header_size(
+int netlink_get_policy_set_and_header_size(
sd_netlink *nl,
uint16_t type,
- const NLTypeSystem **ret_type_system,
+ const NLAPolicySet **ret_policy_set,
size_t *ret_header_size);
-const NLType *type_system_get_type(const NLTypeSystem *type_system, uint16_t type);
-const NLTypeSystem *type_system_get_type_system(const NLTypeSystem *type_system, uint16_t type);
-const NLTypeSystemUnion *type_system_get_type_system_union(const NLTypeSystem *type_system, uint16_t type);
-NLMatchType type_system_union_get_match_type(const NLTypeSystemUnion *type_system_union);
-uint16_t type_system_union_get_match_attribute(const NLTypeSystemUnion *type_system_union);
-const NLTypeSystem *type_system_union_get_type_system_by_string(const NLTypeSystemUnion *type_system_union, const char *key);
-const NLTypeSystem *type_system_union_get_type_system_by_protocol(const NLTypeSystemUnion *type_system_union, uint16_t protocol);
+const NLAPolicy *policy_set_get_policy(const NLAPolicySet *policy_set, uint16_t attr_type);
+const NLAPolicySet *policy_set_get_policy_set(const NLAPolicySet *type_system, uint16_t attr_type);
+const NLAPolicySetUnion *policy_set_get_policy_set_union(const NLAPolicySet *type_system, uint16_t attr_type);
+uint16_t policy_set_union_get_match_attribute(const NLAPolicySetUnion *policy_set_union);
+const NLAPolicySet *policy_set_union_get_policy_set_by_string(const NLAPolicySetUnion *type_system_union, const char *string);
+const NLAPolicySet *policy_set_union_get_policy_set_by_family(const NLAPolicySetUnion *type_system_union, int family);
int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret);
-static inline bool rtnl_message_type_is_neigh(uint16_t type) {
- return IN_SET(type, RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH);
-}
-
-static inline bool rtnl_message_type_is_route(uint16_t type) {
- return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE);
-}
-
-static inline bool rtnl_message_type_is_nexthop(uint16_t type) {
- return IN_SET(type, RTM_NEWNEXTHOP, RTM_GETNEXTHOP, RTM_DELNEXTHOP);
-}
-
-static inline bool rtnl_message_type_is_link(uint16_t type) {
- return IN_SET(type,
- RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK,
- RTM_NEWLINKPROP, RTM_DELLINKPROP, RTM_GETLINKPROP);
-}
-
-static inline bool rtnl_message_type_is_addr(uint16_t type) {
- return IN_SET(type, RTM_NEWADDR, RTM_GETADDR, RTM_DELADDR);
-}
-
-static inline bool rtnl_message_type_is_addrlabel(uint16_t type) {
- return IN_SET(type, RTM_NEWADDRLABEL, RTM_DELADDRLABEL, RTM_GETADDRLABEL);
-}
-
-static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) {
- return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE);
-}
-
-static inline bool rtnl_message_type_is_traffic_control(uint16_t type) {
- return IN_SET(type,
- RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC,
- RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS);
-}
-
-static inline bool rtnl_message_type_is_mdb(uint16_t type) {
- return IN_SET(type, RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB);
-}
-
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
int rtnl_set_link_properties(
sd_netlink **rtnl,
#include "macro.h"
#include "netlink-genl.h"
#include "netlink-internal.h"
-#include "netlink-util.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "string-util.h"
return r;
r = copy_xattr(fileno(fr), fileno(fw), COPY_ALL_XATTRS);
if (r < 0)
- return r;
+ log_debug_errno(r, "Failed to copy all xattrs from old to new /etc/locale.gen file, ignoring: %m");
}
if (!write_new) {
if (r < 0)
return bus_log_parse_error(r);
- table = table_new("uid", "user");
+ table = table_new("uid", "user", "linger");
if (!table)
return log_oom();
(void) table_set_align_percent(table, TABLE_HEADER_CELL(0), 100);
for (;;) {
- const char *user;
+ const char *user, *object;
uint32_t uid;
+ int linger;
- r = sd_bus_message_read(reply, "(uso)", &uid, &user, NULL);
+ r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object);
if (r < 0)
return bus_log_parse_error(r);
if (r == 0)
break;
+ r = sd_bus_get_property_trivial(bus,
+ "org.freedesktop.login1",
+ object,
+ "org.freedesktop.login1.User",
+ "Linger",
+ &error,
+ 'b',
+ &linger);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get linger status: %s", bus_error_message(&error, r));
+
r = table_add_many(table,
TABLE_UID, (uid_t) uid,
- TABLE_STRING, user);
+ TABLE_STRING, user,
+ TABLE_BOOLEAN, linger);
if (r < 0)
return table_log_add_error(r);
}
return r;
if (manager_add_user_by_uid(m, uid, &u) >= 0)
- user_start(u, /* want_user_instance= */ true);
+ user_start(u);
} else {
User *u;
log_struct(LOG_INFO,
LOG_MESSAGE("System shutdown has been cancelled"),
"ACTION=%s", handle_action_to_string(a->handle),
- "MESSAGE_ID=" SD_MESSAGE_LOGIND_SHUTDOWN_CANCELED_STR,
+ "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_CANCELED_STR,
username ? "OPERATOR=%s" : NULL, username);
utmp_wall("System shutdown has been cancelled",
#include "alloc-util.h"
#include "bus-util.h"
+#include "daemon-util.h"
#include "fd-util.h"
#include "logind-session-dbus.h"
#include "logind-session-device.h"
}
void session_device_free(SessionDevice *sd) {
- int r;
-
assert(sd);
/* Make sure to remove the pushed fd. */
- if (sd->pushed_fd) {
- r = sd_notifyf(false,
- "FDSTOREREMOVE=1\n"
- "FDNAME=session-%s-device-%u-%u",
- sd->session->id, major(sd->dev), minor(sd->dev));
- if (r < 0)
- log_warning_errno(r, "Failed to remove file descriptor from the store, ignoring: %m");
- }
+ if (sd->pushed_fd)
+ (void) notify_remove_fd_warnf("session-%s-device-%u-%u", sd->session->id, major(sd->dev), minor(sd->dev));
session_device_stop(sd);
session_device_notify(sd, SESSION_DEVICE_RELEASE);
}
int session_device_save(SessionDevice *sd) {
- _cleanup_free_ char *m = NULL;
const char *id;
int r;
id = sd->session->id;
assert(*(id + strcspn(id, "-\n")) == '\0');
- r = asprintf(&m, "FDSTORE=1\n"
- "FDNAME=session-%s-device-%u-%u\n",
- id, major(sd->dev), minor(sd->dev));
- if (r < 0)
- return r;
-
- r = sd_pid_notify_with_fds(0, false, m, &sd->fd, 1);
+ r = notify_push_fdf(sd->fd, "session-%s-device-%u-%u", id, major(sd->dev), minor(sd->dev));
if (r < 0)
return r;
after = strv_new("systemd-logind.service",
s->user->runtime_dir_service,
!uid_is_system(s->user->user_record->uid) ? "systemd-user-sessions.service" : STRV_IGNORE,
- s->class != SESSION_BACKGROUND ? s->user->service : STRV_IGNORE);
+ s->user->service);
if (!after)
return log_oom();
description,
/* These two have StopWhenUnneeded= set, hence add a dep towards them */
STRV_MAKE(s->user->runtime_dir_service,
- s->class != SESSION_BACKGROUND ? s->user->service : NULL),
+ s->user->service),
after,
user_record_home_directory(s->user->user_record),
properties,
if (s->started)
return 0;
- r = user_start(s->user, /* want_user_instance= */ s->class != SESSION_BACKGROUND);
+ r = user_start(s->user);
if (r < 0)
return r;
return 0;
}
-int user_start(User *u, bool want_user_instance) {
+int user_start(User *u) {
assert(u);
if (u->started && !u->stopping)
(void) user_update_slice(u);
/* Start user@UID.service */
- if (want_user_instance)
- user_start_service(u);
+ user_start_service(u);
if (!u->started) {
if (!dual_timestamp_is_set(&u->timestamp))
bool user_may_gc(User *u, bool drop_not_started);
void user_add_to_gc_queue(User *u);
-int user_start(User *u, bool need_user_instance);
+int user_start(User *u);
int user_stop(User *u, bool force);
int user_finalize(User *u);
UserState user_get_state(User *u);
log_struct(level,
LOG_MESSAGE("%s", l),
"ACTION=%s", handle_action_to_string(m->scheduled_shutdown_action->handle),
- "MESSAGE_ID=" SD_MESSAGE_LOGIND_SHUTDOWN_STR,
+ "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_SCHEDULED_STR,
username ? "OPERATOR=%s" : NULL, username);
if (m->enable_wall_messages)
static int manager_attach_fds(Manager *m) {
_cleanup_strv_free_ char **fdnames = NULL;
- int r, n;
+ int n;
/* Upon restart, PID1 will send us back all fds of session devices that we previously opened. Each
* file descriptor is associated with a given session. The session ids are passed through FDNAMES. */
if (deliver_fd(m, fdnames[i], fd) >= 0)
continue;
- /* Hmm, we couldn't deliver the fd to any session device object? If so, let's close the fd */
- safe_close(fd);
-
- /* Remove from fdstore as well */
- r = sd_notifyf(false,
- "FDSTOREREMOVE=1\n"
- "FDNAME=%s", fdnames[i]);
- if (r < 0)
- log_warning_errno(r, "Failed to remove file descriptor from the store, ignoring: %m");
+ /* Hmm, we couldn't deliver the fd to any session device object? If so, let's close the fd
+ * and remove it from fdstore. */
+ close_and_notify_warn(fd, fdnames[i]);
}
return 0;
int r;
Seat *seat;
Session *session;
+ User *user;
Button *button;
Inhibitor *inhibitor;
HASHMAP_FOREACH(seat, m->seats)
(void) seat_start(seat);
- /* Users are started by respective sessions */
+ HASHMAP_FOREACH(user, m->users)
+ (void) user_start(user);
+
HASHMAP_FOREACH(session, m->sessions)
(void) session_start(session, NULL, NULL);
static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit, const char *field) {
uint64_t val;
int r;
+ bool is_cpu_weight;
+ is_cpu_weight = streq(field, "CPUWeight");
if (isempty(limit))
return PAM_SUCCESS;
- r = cg_weight_parse(limit, &val);
+ r = is_cpu_weight ? cg_cpu_weight_parse(limit, &val) : cg_weight_parse(limit, &val);
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", field, "t", val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
- } else if (streq(field, "CPUWeight"))
+ } else if (is_cpu_weight)
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit);
else
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit);
* does the PAM session registration early for new connections, and registers a pty only
* much later (this is because it doesn't know yet if it needs one at all, as whether to
* register a pty or not is negotiated much later in the protocol). */
- } else if (streq(tty, "systemd")) {
- if (isempty(class))
- class = "user";
- tty = NULL;
+
} else
/* Chop off leading /dev prefix that some clients specify, but others do not. */
tty = skip_dev_prefix(tty);
if (r < 0)
return r;
+ if (endswith(sd_bus_message_get_member(message), "WithFlags")) {
+ uint64_t raw_flags;
+
+ r = sd_bus_message_read(message, "t", &raw_flags);
+ if (r < 0)
+ return r;
+
+ if ((raw_flags & ~_MACHINE_COPY_FLAGS_MASK_PUBLIC) != 0)
+ return -EINVAL;
+
+ if (raw_flags & MACHINE_COPY_REPLACE)
+ copy_flags |= COPY_REPLACE;
+ }
+
if (!path_is_absolute(src))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute.");
SD_BUS_NO_RESULT,
bus_machine_method_copy,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CopyFromWithFlags",
+ SD_BUS_ARGS("s", source, "s", destination, "t", flags),
+ SD_BUS_NO_RESULT,
+ bus_machine_method_copy,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CopyToWithFlags",
+ SD_BUS_ARGS("s", source, "s", destination, "t", flags),
+ SD_BUS_NO_RESULT,
+ bus_machine_method_copy,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("OpenRootDirectory",
SD_BUS_NO_ARGS,
SD_BUS_RESULT("h", fd),
#include "bus-util.h"
#include "machine.h"
+typedef enum {
+ MACHINE_COPY_REPLACE = 1 << 0, /* Public API via DBUS, do not change */
+ _MACHINE_COPY_FLAGS_MASK_PUBLIC = MACHINE_COPY_REPLACE,
+} MachineCopyFlags;
+
extern const BusObjectImplementation machine_object;
char *machine_bus_path(Machine *s);
#include "locale-util.h"
#include "log.h"
#include "logs-show.h"
+#include "machine-dbus.h"
#include "macro.h"
#include "main-func.h"
#include "mkdir.h"
return 0;
}
+static const char *select_copy_method(bool copy_from, bool force) {
+ if (force)
+ return copy_from ? "CopyFromMachineWithFlags" : "CopyToMachineWithFlags";
+ else
+ return copy_from ? "CopyFromMachine" : "CopyToMachine";
+}
+
static int copy_files(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
bus,
&m,
bus_machine_mgr,
- copy_from ? "CopyFromMachine" : "CopyToMachine");
+ select_copy_method(copy_from, arg_force));
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
+ if (arg_force) {
+ r = sd_bus_message_append(m, "t", MACHINE_COPY_REPLACE);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
/* This is a slow operation, hence turn off any method call timeouts */
r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
if (r < 0)
SD_BUS_NO_RESULT,
method_copy_machine,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CopyFromMachineWithFlags",
+ SD_BUS_ARGS("s", name, "s", source, "s", destination, "t", flags),
+ SD_BUS_NO_RESULT,
+ method_copy_machine,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CopyToMachineWithFlags",
+ SD_BUS_ARGS("s", name, "s", source, "s", destination, "t", flags),
+ SD_BUS_NO_RESULT,
+ method_copy_machine,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("OpenMachineRootDirectory",
SD_BUS_ARGS("s", name),
SD_BUS_RESULT("h", fd),
if (r < 0)
return r;
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
return -EBUSY;
}
return link_get_l2tp_local_address(link, t, ret);
HASHMAP_FOREACH(link, netdev->manager->links_by_index) {
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
continue;
if (link_get_l2tp_local_address(link, t, ret) >= 0)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, conditions)
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, conditions)
Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, conditions)
+Match.Credential, config_parse_net_condition, CONDITION_CREDENTIAL, offsetof(NetDev, conditions)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, conditions)
Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(NetDev, conditions)
NetDev.Description, config_parse_string, 0, offsetof(NetDev, description)
Tun.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
Tun.User, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(TunTap, user_name)
Tun.Group, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(TunTap, group_name)
+Tun.KeepCarrier, config_parse_bool, 0, offsetof(TunTap, keep_fd)
Tap.OneQueue, config_parse_warn_compat, DISABLED_LEGACY, 0
Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue)
Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info)
Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr)
Tap.User, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(TunTap, user_name)
Tap.Group, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(TunTap, group_name)
+Tap.KeepCarrier, config_parse_bool, 0, offsetof(TunTap, keep_fd)
Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode)
Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy)
Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate)
assert_not_reached();
}
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
return -EBUSY;
SET_FOREACH(a, link->addresses) {
#include "networkd-manager.h"
#include "networkd-queue.h"
#include "networkd-setlink.h"
+#include "networkd-sriov.h"
#include "nlmon.h"
#include "path-lookup.h"
#include "siphash24.h"
return;
}
+ if (NETDEV_VTABLE(netdev) && NETDEV_VTABLE(netdev)->drop)
+ NETDEV_VTABLE(netdev)->drop(netdev);
+
netdev->state = NETDEV_STATE_LINGER;
log_netdev_debug(netdev, "netdev removed");
return 0;
}
+static bool link_is_ready_to_create_stacked_netdev_one(Link *link, bool allow_unmanaged) {
+ assert(link);
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED))
+ return false;
+
+ if (!link->network)
+ return allow_unmanaged;
+
+ if (link->set_link_messages > 0)
+ return false;
+
+ /* If stacked netdevs are created before the underlying interface being activated, then
+ * the activation policy for the netdevs are ignored. See issue #22593. */
+ if (!link->activated)
+ return false;
+
+ return true;
+}
+
+static bool link_is_ready_to_create_stacked_netdev(Link *link) {
+ return check_ready_for_all_sr_iov_ports(link, /* allow_unmanaged = */ false,
+ link_is_ready_to_create_stacked_netdev_one);
+}
+
static int netdev_is_ready_to_create(NetDev *netdev, Link *link) {
assert(netdev);
if (netdev->state != NETDEV_STATE_LOADING)
return false;
- if (link) {
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
- return false;
-
- if (link->set_link_messages > 0)
- return false;
-
- /* If stacked netdevs are created before the underlying interface being activated, then
- * the activation policy for the netdevs are ignored. See issue #22593. */
- if (!link->activated)
- return false;
- }
+ if (link && !link_is_ready_to_create_stacked_netdev(link))
+ return false;
if (NETDEV_VTABLE(netdev)->is_ready_to_create)
return NETDEV_VTABLE(netdev)->is_ready_to_create(netdev, link);
config_item_perf_lookup, network_netdev_gperf_lookup,
CONFIG_PARSE_WARN,
netdev_raw,
+ NULL,
NULL);
if (r < 0)
return r; /* config_parse_many() logs internally. */
NETDEV_VTABLE(netdev)->sections,
config_item_perf_lookup, network_netdev_gperf_lookup,
CONFIG_PARSE_WARN,
- netdev, NULL);
+ netdev, NULL, NULL);
if (r < 0)
return r; /* config_parse_many() logs internally. */
* to be set != 0. */
void (*init)(NetDev *n);
+ /* This is called when the interface is removed. */
+ void (*drop)(NetDev *n);
+
/* This should free all kind-specific variables. It should be
* idempotent. */
void (*done)(NetDev *n);
#include <linux/if_tun.h>
#include "alloc-util.h"
+#include "daemon-util.h"
#include "fd-util.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "socket-util.h"
#include "tuntap.h"
#include "user-util.h"
#define TUN_DEV "/dev/net/tun"
-static int netdev_fill_tuntap_message(NetDev *netdev, struct ifreq *ifr) {
- TunTap *t;
-
+static TunTap* TUNTAP(NetDev *netdev) {
assert(netdev);
- assert(netdev->ifname);
- assert(ifr);
-
- if (netdev->kind == NETDEV_KIND_TAP) {
- t = TAP(netdev);
- ifr->ifr_flags |= IFF_TAP;
- } else {
- t = TUN(netdev);
- ifr->ifr_flags |= IFF_TUN;
+
+ switch (netdev->kind) {
+ case NETDEV_KIND_TAP:
+ return TAP(netdev);
+ case NETDEV_KIND_TUN:
+ return TUN(netdev);
+ default:
+ return NULL;
}
+}
- if (!t->packet_info)
- ifr->ifr_flags |= IFF_NO_PI;
+static void *close_fd_ptr(void *p) {
+ safe_close(PTR_TO_FD(p));
+ return NULL;
+}
- if (t->multi_queue)
- ifr->ifr_flags |= IFF_MULTI_QUEUE;
+DEFINE_PRIVATE_HASH_OPS_FULL(named_fd_hash_ops, char, string_hash_func, string_compare_func, free, void, close_fd_ptr);
- if (t->vnet_hdr)
- ifr->ifr_flags |= IFF_VNET_HDR;
+int manager_add_tuntap_fd(Manager *m, int fd, const char *name) {
+ _cleanup_free_ char *tuntap_name = NULL;
+ const char *p;
+ int r;
+
+ assert(m);
+ assert(fd >= 0);
+ assert(name);
+
+ p = startswith(name, "tuntap-");
+ if (!p)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Received unknown fd (%s).", name);
+
+ if (!ifname_valid(p))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Received tuntap fd with invalid name (%s).", p);
- strncpy(ifr->ifr_name, netdev->ifname, IFNAMSIZ-1);
+ tuntap_name = strdup(p);
+ if (!tuntap_name)
+ return log_oom_debug();
+ r = hashmap_ensure_put(&m->tuntap_fds_by_name, &named_fd_hash_ops, tuntap_name, FD_TO_PTR(fd));
+ if (r < 0)
+ return log_debug_errno(r, "Failed to store tuntap fd: %m");
+
+ TAKE_PTR(tuntap_name);
return 0;
}
-static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) {
+void manager_clear_unmanaged_tuntap_fds(Manager *m) {
+ char *name;
+ void *p;
+
+ assert(m);
+
+ while ((p = hashmap_steal_first_key_and_value(m->tuntap_fds_by_name, (void**) &name))) {
+ close_and_notify_warn(PTR_TO_FD(p), name);
+ name = mfree(name);
+ }
+}
+
+static int tuntap_take_fd(NetDev *netdev) {
+ _cleanup_free_ char *name = NULL;
+ void *p;
+ int r;
+
+ assert(netdev);
+ assert(netdev->manager);
+
+ r = link_get_by_name(netdev->manager, netdev->ifname, NULL);
+ if (r < 0)
+ return r;
+
+ p = hashmap_remove2(netdev->manager->tuntap_fds_by_name, netdev->ifname, (void**) &name);
+ if (!p)
+ return -ENOENT;
+
+ log_netdev_debug(netdev, "Found file descriptor in fd store.");
+ return PTR_TO_FD(p);
+}
+
+static int netdev_create_tuntap(NetDev *netdev) {
_cleanup_close_ int fd = -1;
- TunTap *t = NULL;
- const char *user;
- const char *group;
- uid_t uid;
- gid_t gid;
+ struct ifreq ifr = {};
+ TunTap *t;
int r;
assert(netdev);
- assert(ifr);
+ t = TUNTAP(netdev);
+ assert(t);
- fd = open(TUN_DEV, O_RDWR|O_CLOEXEC);
+ fd = TAKE_FD(t->fd);
if (fd < 0)
- return log_netdev_error_errno(netdev, errno, "Failed to open tun dev: %m");
-
- if (ioctl(fd, TUNSETIFF, ifr) < 0)
- return log_netdev_error_errno(netdev, errno, "TUNSETIFF failed on tun dev: %m");
+ fd = tuntap_take_fd(netdev);
+ if (fd < 0)
+ fd = open(TUN_DEV, O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+ return log_netdev_error_errno(netdev, errno, "Failed to open " TUN_DEV ": %m");
if (netdev->kind == NETDEV_KIND_TAP)
- t = TAP(netdev);
+ ifr.ifr_flags |= IFF_TAP;
else
- t = TUN(netdev);
+ ifr.ifr_flags |= IFF_TUN;
- assert(t);
+ if (!t->packet_info)
+ ifr.ifr_flags |= IFF_NO_PI;
+
+ if (t->multi_queue)
+ ifr.ifr_flags |= IFF_MULTI_QUEUE;
+
+ if (t->vnet_hdr)
+ ifr.ifr_flags |= IFF_VNET_HDR;
+
+ strncpy(ifr.ifr_name, netdev->ifname, IFNAMSIZ-1);
+
+ if (ioctl(fd, TUNSETIFF, &ifr) < 0)
+ return log_netdev_error_errno(netdev, errno, "TUNSETIFF failed: %m");
if (t->user_name) {
- user = t->user_name;
+ const char *user = t->user_name;
+ uid_t uid;
r = get_user_creds(&user, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name);
if (ioctl(fd, TUNSETOWNER, uid) < 0)
- return log_netdev_error_errno(netdev, errno, "TUNSETOWNER failed on tun dev: %m");
+ return log_netdev_error_errno(netdev, errno, "TUNSETOWNER failed: %m");
}
if (t->group_name) {
- group = t->group_name;
+ const char *group = t->group_name;
+ gid_t gid;
r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name);
if (ioctl(fd, TUNSETGROUP, gid) < 0)
- return log_netdev_error_errno(netdev, errno, "TUNSETGROUP failed on tun dev: %m");
+ return log_netdev_error_errno(netdev, errno, "TUNSETGROUP failed: %m");
}
if (ioctl(fd, TUNSETPERSIST, 1) < 0)
- return log_netdev_error_errno(netdev, errno, "TUNSETPERSIST failed on tun dev: %m");
+ return log_netdev_error_errno(netdev, errno, "TUNSETPERSIST failed: %m");
+
+ if (t->keep_fd) {
+ t->fd = TAKE_FD(fd);
+ (void) notify_push_fdf(t->fd, "tuntap-%s", netdev->ifname);
+ }
return 0;
}
-static int netdev_create_tuntap(NetDev *netdev) {
- struct ifreq ifr = {};
- int r;
+static void tuntap_init(NetDev *netdev) {
+ TunTap *t;
- r = netdev_fill_tuntap_message(netdev, &ifr);
- if (r < 0)
- return r;
+ assert(netdev);
+ t = TUNTAP(netdev);
+ assert(t);
- return netdev_tuntap_add(netdev, &ifr);
+ t->fd = -1;
}
-static void tuntap_done(NetDev *netdev) {
- TunTap *t = NULL;
+static void tuntap_drop(NetDev *netdev) {
+ TunTap *t;
assert(netdev);
+ t = TUNTAP(netdev);
+ assert(t);
- if (netdev->kind == NETDEV_KIND_TUN)
- t = TUN(netdev);
- else
- t = TAP(netdev);
+ t->fd = close_and_notify_warn(t->fd, netdev->ifname);
+}
+static void tuntap_done(NetDev *netdev) {
+ TunTap *t;
+
+ assert(netdev);
+ t = TUNTAP(netdev);
assert(t);
+ t->fd = safe_close(t->fd);
t->user_name = mfree(t->user_name);
t->group_name = mfree(t->group_name);
}
.object_size = sizeof(TunTap),
.sections = NETDEV_COMMON_SECTIONS "Tun\0",
.config_verify = tuntap_verify,
+ .init = tuntap_init,
+ .drop = tuntap_drop,
.done = tuntap_done,
.create = netdev_create_tuntap,
.create_type = NETDEV_CREATE_INDEPENDENT,
.object_size = sizeof(TunTap),
.sections = NETDEV_COMMON_SECTIONS "Tap\0",
.config_verify = tuntap_verify,
+ .init = tuntap_init,
+ .drop = tuntap_drop,
.done = tuntap_done,
.create = netdev_create_tuntap,
.create_type = NETDEV_CREATE_INDEPENDENT,
struct TunTap {
NetDev meta;
+ int fd;
char *user_name;
char *group_name;
bool multi_queue;
bool packet_info;
bool vnet_hdr;
+ bool keep_fd;
};
DEFINE_NETDEV_CAST(TUN, TunTap);
DEFINE_NETDEV_CAST(TAP, TunTap);
extern const NetDevVTable tun_vtable;
extern const NetDevVTable tap_vtable;
+
+int manager_add_tuntap_fd(Manager *m, int fd, const char *name);
+void manager_clear_unmanaged_tuntap_fds(Manager *m);
a->broadcast.s_addr = a->in_addr.in.s_addr | htobe32(UINT32_C(0xffffffff) >> a->prefixlen);
}
-static struct ifa_cacheinfo *address_set_cinfo(const Address *a, struct ifa_cacheinfo *cinfo) {
+static void address_set_cinfo(Manager *m, const Address *a, struct ifa_cacheinfo *cinfo) {
usec_t now_usec;
+ assert(m);
assert(a);
assert(cinfo);
- now_usec = now(CLOCK_BOOTTIME);
+ assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &now_usec) >= 0);
*cinfo = (struct ifa_cacheinfo) {
- .ifa_valid = MIN(usec_sub_unsigned(a->lifetime_valid_usec, now_usec) / USEC_PER_SEC, UINT32_MAX),
- .ifa_prefered = MIN(usec_sub_unsigned(a->lifetime_preferred_usec, now_usec) / USEC_PER_SEC, UINT32_MAX),
+ .ifa_valid = usec_to_sec(a->lifetime_valid_usec, now_usec),
+ .ifa_prefered = usec_to_sec(a->lifetime_preferred_usec, now_usec),
};
-
- return cinfo;
}
-static void address_set_lifetime(Address *a, const struct ifa_cacheinfo *cinfo) {
+static void address_set_lifetime(Manager *m, Address *a, const struct ifa_cacheinfo *cinfo) {
usec_t now_usec;
+ assert(m);
assert(a);
assert(cinfo);
- now_usec = now(CLOCK_BOOTTIME);
-
- if (cinfo->ifa_valid == UINT32_MAX)
- a->lifetime_valid_usec = USEC_INFINITY;
- else
- a->lifetime_valid_usec = usec_add(cinfo->ifa_valid * USEC_PER_SEC, now_usec);
+ assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &now_usec) >= 0);
- if (cinfo->ifa_prefered == UINT32_MAX)
- a->lifetime_preferred_usec = USEC_INFINITY;
- else
- a->lifetime_preferred_usec = usec_add(cinfo->ifa_prefered * USEC_PER_SEC, now_usec);
+ a->lifetime_valid_usec = sec_to_usec(cinfo->ifa_valid, now_usec);
+ a->lifetime_preferred_usec = sec_to_usec(cinfo->ifa_prefered, now_usec);
}
static uint32_t address_prefix(const Address *a) {
/* IPv6LL address may be in the tentative state, and in that case networkd has not received it.
* So, we need to dump all IPv6 addresses. */
- if (link_may_have_ipv6ll(link))
+ if (link_may_have_ipv6ll(link, /* check_multicast = */ false))
return 0;
r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_GETADDR, link->ifindex, AF_INET6);
return 1;
}
-static int address_configure(const Address *address, Link *link, Request *req) {
+static int address_configure(const Address *address, const struct ifa_cacheinfo *c, Link *link, Request *req) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(address);
assert(IN_SET(address->family, AF_INET, AF_INET6));
+ assert(c);
assert(link);
assert(link->ifindex > 0);
assert(link->manager);
return r;
}
- r = sd_netlink_message_append_cache_info(m, IFA_CACHEINFO,
- address_set_cinfo(address, &(struct ifa_cacheinfo) {}));
+ r = sd_netlink_message_append_cache_info(m, IFA_CACHEINFO, c);
if (r < 0)
return r;
}
static int address_process_request(Request *req, Link *link, Address *address) {
+ struct ifa_cacheinfo c;
int r;
assert(req);
if (!address_is_ready_to_configure(link, address))
return 0;
- r = address_configure(address, link, req);
+ address_set_cinfo(link->manager, address, &c);
+ if (c.ifa_valid == 0) {
+ log_link_debug(link, "Refuse to configure %s address %s, as its valid lifetime is zero.",
+ network_config_source_to_string(address->source),
+ IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
+ address_cancel_requesting(address);
+ return 1;
+ }
+
+ r = address_configure(address, &c, link, req);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to configure address: %m");
/* update flags and etc. */
address->flags = tmp->flags;
address->scope = tmp->scope;
- address_set_lifetime(address, &cinfo);
+ address_set_lifetime(m, address, &cinfo);
address_enter_configured(address);
log_address_debug(address, "Received updated", link);
} else {
- address_set_lifetime(tmp, &cinfo);
+ address_set_lifetime(m, tmp, &cinfo);
address_enter_configured(tmp);
log_address_debug(tmp, "Received new", link);
r = address_add(link, tmp);
if (r < 0) {
log_link_warning_errno(link, r, "Failed to remember foreign address %s, ignoring: %m",
- IN_ADDR_PREFIX_TO_STRING(tmp->family, &tmp->in_addr, tmp->prefixlen));
+ IN_ADDR_PREFIX_TO_STRING(tmp->family, &tmp->in_addr, tmp->prefixlen));
return 0;
}
#include <linux/if_arp.h>
#include "bus-error.h"
+#include "bus-locator.h"
#include "dhcp-identifier.h"
#include "dhcp-internal.h"
#include "dhcp6-internal.h"
m->product_uuid_requested = false;
- r = sd_bus_call_method_async(
+ r = bus_call_method_async(
m->bus,
NULL,
- "org.freedesktop.hostname1",
- "/org/freedesktop/hostname1",
- "org.freedesktop.hostname1",
+ bus_hostname,
"GetProductUUID",
get_product_uuid_handler,
m,
const struct in_addr *br_addresses;
struct in_addr ipv4address;
uint32_t lifetime_sec;
- usec_t lifetime_usec;
+ usec_t lifetime_usec, now_usec;
int r;
assert(link);
assert(uplink);
+ assert(uplink->manager);
assert(uplink->dhcp_lease);
r = sd_dhcp_lease_get_address(uplink->dhcp_lease, &ipv4address);
if (r < 0)
return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m");
- lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(CLOCK_BOOTTIME));
+ assert_se(sd_event_now(uplink->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
+ lifetime_usec = sec_to_usec(lifetime_sec, now_usec);
r = sd_dhcp_lease_get_6rd(uplink->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, &br_addresses, NULL);
if (r < 0)
union in_addr_union server_address;
const struct in_addr *br_addresses;
uint32_t lifetime_sec;
- usec_t lifetime_usec;
+ usec_t lifetime_usec, now_usec;
Link *link;
int r;
assert(uplink);
+ assert(uplink->manager);
assert(uplink->dhcp_lease);
r = sd_dhcp_lease_get_address(uplink->dhcp_lease, &ipv4address);
if (r < 0)
return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m");
- lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(CLOCK_BOOTTIME));
+ assert_se(sd_event_now(uplink->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
+ lifetime_usec = sec_to_usec(lifetime_sec, now_usec);
r = sd_dhcp_lease_get_server_identifier(uplink->dhcp_lease, &server_address.in);
if (r < 0)
for (sd_dhcp6_lease_reset_pd_prefix_iter(uplink->dhcp6_lease);;) {
uint32_t lifetime_preferred_sec, lifetime_valid_sec;
- usec_t lifetime_preferred_usec, lifetime_valid_usec;
struct in6_addr pd_prefix;
uint8_t pd_prefix_len;
if (r < 0)
return r;
- lifetime_preferred_usec = usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec);
- lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec);
-
r = dhcp_pd_assign_subnet_prefix(link, &pd_prefix, pd_prefix_len,
- lifetime_preferred_usec, lifetime_valid_usec,
+ sec_to_usec(lifetime_preferred_sec, timestamp_usec),
+ sec_to_usec(lifetime_valid_sec, timestamp_usec),
/* is_uplink = */ link == uplink);
if (r < 0)
return r;
/* First, logs acquired prefixes and request unreachable routes. */
for (sd_dhcp6_lease_reset_pd_prefix_iter(uplink->dhcp6_lease);;) {
uint32_t lifetime_preferred_sec, lifetime_valid_sec;
- usec_t lifetime_valid_usec;
struct in6_addr pd_prefix;
uint8_t pd_prefix_len;
if (r < 0)
return log_link_error_errno(uplink, r, "Failed to mask DHCPv6 delegated prefix: %m");
- lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec);
-
r = dhcp_pd_prefix_add(uplink, &pd_prefix, pd_prefix_len);
if (r < 0)
return r;
- r = dhcp6_request_unreachable_route(uplink, &pd_prefix, pd_prefix_len, lifetime_valid_usec, &server_address);
+ r = dhcp6_request_unreachable_route(uplink, &pd_prefix, pd_prefix_len,
+ sec_to_usec(lifetime_valid_sec, timestamp_usec),
+ &server_address);
if (r < 0)
return r;
}
assert(link);
- if (!link->network)
- return false;
-
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
- return false;
-
- if (link->set_flags_messages > 0)
+ if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
return false;
if (!link_has_carrier(link))
int r;
assert(link);
+ assert(link->manager);
assert(link->network);
assert(link->dhcp_lease);
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
uint32_t lifetime_sec;
+ usec_t now_usec;
r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime_sec);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
- lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(CLOCK_BOOTTIME));
+ assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
+ lifetime_usec = sec_to_usec(lifetime_sec, now_usec);
} else
lifetime_usec = USEC_INFINITY;
if (link->ipv4ll) {
log_link_debug(link, "DHCP client is stopped. Acquiring IPv4 link-local address");
+ if (in4_addr_is_set(&link->network->ipv4ll_start_address)) {
+ r = sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set IPv4 link-local start address: %m");
+ }
+
r = sd_ipv4ll_start(link->ipv4ll);
if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
if (link->ipv4ll && !sd_ipv4ll_is_running(link->ipv4ll)) {
log_link_debug(link, "Problems acquiring DHCP lease, acquiring IPv4 link-local address");
+ if (in4_addr_is_set(&link->network->ipv4ll_start_address)) {
+ r = sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set IPv4 link-local start address: %m");
+ }
+
r = sd_ipv4ll_start(link->ipv4ll);
if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
* If neither is set or a failure occurs, return false, which is the default for this flag.
*/
r = link->network->dhcp_broadcast;
- if (r < 0 && link->sd_device && sd_device_get_property_value(link->sd_device, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
+ if (r < 0 && link->dev && sd_device_get_property_value(link->dev, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
r = parse_boolean(val);
if (r < 0)
log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
}
int dhcp4_update_mac(Link *link) {
+ bool restart;
int r;
assert(link);
if (!link->dhcp_client)
return 0;
- r = sd_dhcp_client_set_mac(link->dhcp_client, link->hw_addr.bytes,
+ restart = sd_dhcp_client_is_running(link->dhcp_client);
+
+ r = sd_dhcp_client_stop(link->dhcp_client);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_client_set_mac(link->dhcp_client,
+ link->hw_addr.bytes,
link->bcast_addr.length > 0 ? link->bcast_addr.bytes : NULL,
link->hw_addr.length, link->iftype);
if (r < 0)
return r;
- return dhcp4_set_client_identifier(link);
+ r = dhcp4_set_client_identifier(link);
+ if (r < 0)
+ return r;
+
+ if (restart) {
+ r = sd_dhcp_client_start(link->dhcp_client);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
}
int dhcp4_start(Link *link) {
assert(link);
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
return 0;
r = dhcp4_configure_duid(link);
break;
r = dhcp6_request_address(link, &server_address, &ip6_addr,
- usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec),
- usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec));
+ sec_to_usec(lifetime_preferred_sec, timestamp_usec),
+ sec_to_usec(lifetime_valid_sec, timestamp_usec));
if (r < 0)
return r;
}
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix delegation hint: %m");
}
+ r = sd_dhcp6_client_set_rapid_commit(client, link->network->dhcp6_use_rapid_commit);
+ if (r < 0)
+ return log_link_debug_errno(link, r,
+ "DHCPv6 CLIENT: Failed to %s rapid commit: %m",
+ enable_disable(link->network->dhcp6_use_rapid_commit));
+
link->dhcp6_client = TAKE_PTR(client);
return 0;
assert(link);
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
return 0;
r = dhcp_configure_duid(link, link_get_dhcp6_duid(link));
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <linux/if_arp.h>
+
#include "sd-dhcp-client.h"
#include "sd-ipv4acd.h"
+#include "ipvlan.h"
#include "networkd-address.h"
#include "networkd-dhcp4.h"
#include "networkd-ipv4acd.h"
#include "networkd-link.h"
#include "networkd-manager.h"
+bool link_ipv4acd_supported(Link *link) {
+ assert(link);
+
+ if (link->flags & IFF_LOOPBACK)
+ return false;
+
+ /* ARPHRD_INFINIBAND seems to potentially support IPv4ACD.
+ * But currently sd-ipv4acd only supports ARPHRD_ETHER. */
+ if (link->iftype != ARPHRD_ETHER)
+ return false;
+
+ if (link->hw_addr.length != ETH_ALEN)
+ return false;
+
+ if (ether_addr_is_null(&link->hw_addr.ether))
+ return false;
+
+ if (streq_ptr(link->kind, "vrf"))
+ return false;
+
+ /* L3 or L3S mode do not support ARP. */
+ if (IN_SET(link_get_ipvlan_mode(link), NETDEV_IPVLAN_MODE_L3, NETDEV_IPVLAN_MODE_L3S))
+ return false;
+
+ return true;
+}
+
static int static_ipv4acd_address_remove(Link *link, Address *address, bool on_conflict) {
int r;
if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4))
return 0;
- if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
+ if (!link_ipv4acd_supported(link))
return 0;
/* Currently, only static and DHCP4 addresses are supported. */
typedef struct Address Address;
typedef struct Link Link;
+bool link_ipv4acd_supported(Link *link);
int ipv4acd_configure(Address *address);
int ipv4acd_update_mac(Link *link);
int ipv4acd_start(Link *link);
#include "netif-util.h"
#include "networkd-address.h"
+#include "networkd-ipv4acd.h"
#include "networkd-ipv4ll.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-queue.h"
#include "parse-util.h"
+bool link_ipv4ll_enabled(Link *link) {
+ assert(link);
+
+ if (!link_ipv4acd_supported(link))
+ return false;
+
+ if (!link->network)
+ return false;
+
+ if (link->network->bond)
+ return false;
+
+ return link->network->link_local & ADDRESS_FAMILY_IPV4;
+}
+
static int address_new_from_ipv4ll(Link *link, Address **ret) {
_cleanup_(address_freep) Address *address = NULL;
struct in_addr addr;
if (r < 0)
return r;
- if (link->sd_device &&
- net_get_unique_predictable_data(link->sd_device, true, &seed) >= 0) {
+ if (link->dev &&
+ net_get_unique_predictable_data(link->dev, true, &seed) >= 0) {
r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
if (r < 0)
return r;
return 0;
}
+
+int config_parse_ipv4ll_address(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ union in_addr_union a;
+ struct in_addr *ipv4ll_address = ASSERT_PTR(data);
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ *ipv4ll_address = (struct in_addr) {};
+ return 0;
+ }
+
+ r = in_addr_from_string(AF_INET, rvalue, &a);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+ if (!in4_addr_is_link_local_dynamic(&a.in)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Specified address cannot be used as an IPv4 link local address, ignoring assignment: %s",
+ rvalue);
+ return 0;
+ }
+
+ *ipv4ll_address = a.in;
+ return 0;
+}
typedef struct Link Link;
+bool link_ipv4ll_enabled(Link *link);
+
int ipv4ll_configure(Link *link);
int ipv4ll_update_mac(Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll);
+CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll_address);
return link->network->link_local & ADDRESS_FAMILY_IPV6;
}
-bool link_may_have_ipv6ll(Link *link) {
+bool link_may_have_ipv6ll(Link *link, bool check_multicast) {
assert(link);
/*
if (!link->network)
return false;
+ if (check_multicast && !FLAGS_SET(link->flags, IFF_MULTICAST) && link->network->multicast <= 0)
+ return false;
+
ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) {
if (a->family != AF_INET6)
continue;
} IPv6LinkLocalAddressGenMode;
bool link_ipv6ll_enabled(Link *link);
-bool link_may_have_ipv6ll(Link *link);
+bool link_may_have_ipv6ll(Link *link, bool check_multicast);
IPv6LinkLocalAddressGenMode link_get_ipv6ll_addrgen_mode(Link *link);
int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalAddressGenMode mode);
assert(link);
assert(ret);
- r = net_get_type_string(link->sd_device, link->iftype, &type);
+ r = net_get_type_string(link->dev, link->iftype, &type);
if (r == -ENOMEM)
return r;
w = json_variant_unref(w);
- r = device_build_json(link->sd_device, &w);
+ r = device_build_json(link->dev, &w);
if (r < 0)
return r;
#include "format-util.h"
#include "fs-util.h"
#include "glyph-util.h"
-#include "ipvlan.h"
#include "missing_network.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "strv.h"
#include "tc.h"
#include "tmpfile-util.h"
+#include "tuntap.h"
#include "udev-util.h"
#include "util.h"
#include "vrf.h"
-bool link_ipv4ll_enabled(Link *link) {
+bool link_ipv6_enabled(Link *link) {
assert(link);
- if (link->flags & IFF_LOOPBACK)
- return false;
-
- if (!link->network)
+ if (!socket_ipv6_is_supported())
return false;
if (link->iftype == ARPHRD_CAN)
return false;
- if (link->hw_addr.length != ETH_ALEN)
- return false;
-
- if (ether_addr_is_null(&link->hw_addr.ether))
- return false;
-
- /* ARPHRD_INFINIBAND seems to potentially support IPv4LL.
- * But currently sd-ipv4ll and sd-ipv4acd only support ARPHRD_ETHER. */
- if (link->iftype != ARPHRD_ETHER)
- return false;
-
- if (streq_ptr(link->kind, "vrf"))
- return false;
-
- /* L3 or L3S mode do not support ARP. */
- if (IN_SET(link_get_ipvlan_mode(link), NETDEV_IPVLAN_MODE_L3, NETDEV_IPVLAN_MODE_L3S))
- return false;
-
- if (link->network->bond)
- return false;
-
- return link->network->link_local & ADDRESS_FAMILY_IPV4;
-}
-
-bool link_ipv6_enabled(Link *link) {
- assert(link);
-
- if (!socket_ipv6_is_supported())
+ if (!link->network)
return false;
if (link->network->bond)
return false;
- if (link->iftype == ARPHRD_CAN)
- return false;
-
- /* DHCPv6 client will not be started if no IPv6 link-local address is configured. */
- if (link_ipv6ll_enabled(link))
+ if (link_may_have_ipv6ll(link, /* check_multicast = */ false))
return true;
if (network_has_static_ipv6_configurations(link->network))
return false;
}
-bool link_is_ready_to_configure(Link *link, bool allow_unmanaged) {
+static bool link_is_ready_to_configure_one(Link *link, bool allow_unmanaged) {
assert(link);
- if (!link->network) {
- if (!allow_unmanaged)
- return false;
-
- return link_has_carrier(link);
- }
-
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED))
return false;
+ if (!link->network)
+ return allow_unmanaged;
+
if (!link->network->configure_without_carrier) {
if (link->set_flags_messages > 0)
return false;
return true;
}
+bool link_is_ready_to_configure(Link *link, bool allow_unmanaged) {
+ return check_ready_for_all_sr_iov_ports(link, allow_unmanaged, link_is_ready_to_configure_one);
+}
+
void link_ntp_settings_clear(Link *link) {
link->ntp = strv_free(link->ntp);
}
link_free_engines(link);
+ set_free(link->sr_iov_virt_port_ifindices);
free(link->ifname);
strv_free(link->alternative_names);
free(link->kind);
unlink_and_free(link->lldp_file);
unlink_and_free(link->state_file);
- sd_device_unref(link->sd_device);
+ sd_device_unref(link->dev);
netdev_unref(link->netdev);
hashmap_free(link->bound_to_links);
log_link_debug(link, "Acquiring DHCPv4 lease.");
} else if (link->ipv4ll) {
+ if (in4_addr_is_set(&link->network->ipv4ll_start_address)) {
+ r = sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not set IPv4 link-local start address: %m");
+ }
+
r = sd_ipv4ll_start(link->ipv4ll);
if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
link_free_bound_to_list(link);
link_free_bound_by_list(link);
+ link_clear_sr_iov_ifindices(link);
+
link_drop_from_master(link);
if (link->state_file)
r = net_match_config(
&network->match,
- link->sd_device,
+ link->dev,
&link->hw_addr,
&link->permanent_hw_addr,
link->driver,
if (r == 0)
continue;
- if (network->match.ifname && link->sd_device) {
+ if (network->match.ifname && link->dev) {
uint8_t name_assign_type = NET_NAME_UNKNOWN;
const char *attr;
- if (sd_device_get_sysattr_value(link->sd_device, "name_assign_type", &attr) >= 0)
+ if (sd_device_get_sysattr_value(link->dev, "name_assign_type", &attr) >= 0)
(void) safe_atou8(attr, &name_assign_type);
warn = name_assign_type == NET_NAME_ENUM;
}
static int link_initialized(Link *link, sd_device *device) {
+ int r;
+
assert(link);
assert(device);
/* Always replace with the new sd_device object. As the sysname (and possibly other properties
* or sysattrs) may be outdated. */
- sd_device_ref(device);
- sd_device_unref(link->sd_device);
- link->sd_device = device;
+ device_unref_and_replace(link->dev, device);
+
+ r = link_set_sr_iov_ifindices(link);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to manage SR-IOV PF and VF ports, ignoring: %m");
/* Do not ignore unamanaged state case here. If an interface is renamed after being once
* configured, and the corresponding .network file has Name= in [Match] section, then the
return link_initialized(link, device);
}
-int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata) {
- sd_device_action_t action;
- Manager *m = userdata;
- Link *link = NULL;
+int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t action) {
int r, ifindex;
+ Link *link;
assert(m);
assert(device);
- r = sd_device_get_action(device, &action);
+ r = sd_device_get_ifindex(device, &ifindex);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to get ifindex: %m");
+
+ r = link_get_by_index(m, ifindex, &link);
if (r < 0) {
- log_device_debug_errno(device, r, "Failed to get udev action, ignoring device: %m");
+ /* This error is not critical, as the corresponding rtnl message may be received later. */
+ log_device_debug_errno(device, r, "Failed to get link from ifindex %i, ignoring: %m", ifindex);
return 0;
}
- /* Ignore the "remove" uevent — let's remove a device only if rtnetlink says so. All other uevents
- * are "positive" events in some form, i.e. inform us about a changed or new network interface, that
- * still exists — and we are interested in that. */
- if (action == SD_DEVICE_REMOVE)
- return 0;
-
- r = sd_device_get_ifindex(device, &ifindex);
- if (r < 0) {
- log_device_debug_errno(device, r, "Ignoring udev %s event for device without ifindex or with invalid ifindex: %m",
- device_action_to_string(action));
+ /* Let's unref the sd-device object assigned to the corresponding Link object, but keep the Link
+ * object here. It will be removed only when rtnetlink says so. */
+ if (action == SD_DEVICE_REMOVE) {
+ link->dev = sd_device_unref(link->dev);
return 0;
}
r = device_is_renaming(device);
- if (r < 0) {
- log_device_debug_errno(device, r, "Failed to determine the device is renamed or not, ignoring '%s' uevent: %m",
- device_action_to_string(action));
- return 0;
- }
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to determine if the device is renaming or not: %m");
if (r > 0) {
- log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed.");
- return 0;
- }
-
- r = link_get_by_index(m, ifindex, &link);
- if (r < 0) {
- log_device_debug_errno(device, r, "Failed to get link from ifindex %i, ignoring: %m", ifindex);
+ log_device_debug(device, "Device is renaming, waiting for the interface to be renamed.");
+ /* TODO:
+ * What happens when a device is initialized, then soon renamed after that? When we detect
+ * such, maybe we should cancel or postpone all queued requests for the interface. */
return 0;
}
int ifindex;
int master_ifindex;
int dsa_master_ifindex;
+ int sr_iov_phys_port_ifindex;
+ Set *sr_iov_virt_port_ifindices;
+
char *ifname;
char **alternative_names;
char *kind;
uint32_t min_mtu;
uint32_t max_mtu;
uint32_t original_mtu;
- sd_device *sd_device;
+ sd_device *dev;
char *driver;
/* link-local addressing */
bool link_ipv6_enabled(Link *link);
int link_ipv6ll_gained(Link *link);
-bool link_ipv4ll_enabled(Link *link);
-
int link_stop_engines(Link *link, bool may_keep_dhcp);
const char* link_state_to_string(LinkState s) _const_;
int link_reconfigure(Link *link, bool force);
int link_reconfigure_after_sleep(Link *link);
-int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata);
+int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t action);
int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
int link_flags_to_string_alloc(uint32_t flags, char **ret);
#include <linux/nexthop.h>
#include <linux/nl80211.h>
-#include "sd-daemon.h"
#include "sd-netlink.h"
#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-locator.h"
#include "bus-log-control-api.h"
#include "bus-polkit.h"
#include "bus-util.h"
#include "conf-parser.h"
+#include "daemon-util.h"
#include "def.h"
+#include "device-private.h"
+#include "device-util.h"
#include "dns-domain.h"
#include "fd-util.h"
#include "fileio.h"
#include "sysctl-util.h"
#include "tclass.h"
#include "tmpfile-util.h"
+#include "tuntap.h"
#include "udev-util.h"
/* use 128 MB for receive socket kernel queue. */
return 0;
}
+static int manager_process_uevent(sd_device_monitor *monitor, sd_device *device, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+ sd_device_action_t action;
+ const char *s;
+ int r;
+
+ assert(device);
+
+ r = sd_device_get_action(device, &action);
+ if (r < 0)
+ return log_device_warning_errno(device, r, "Failed to get udev action, ignoring: %m");
+
+ r = sd_device_get_subsystem(device, &s);
+ if (r < 0)
+ return log_device_warning_errno(device, r, "Failed to get subsystem, ignoring: %m");
+
+ if (streq(s, "net"))
+ r = manager_udev_process_link(m, device, action);
+ else if (streq(s, "ieee80211"))
+ r = manager_udev_process_wiphy(m, device, action);
+ else if (streq(s, "rfkill"))
+ r = manager_udev_process_rfkill(m, device, action);
+ else {
+ log_device_debug(device, "Received device with unexpected subsystem \"%s\", ignoring.", s);
+ return 0;
+ }
+ if (r < 0)
+ log_device_warning_errno(device, r, "Failed to process \"%s\" uevent, ignoring: %m",
+ device_action_to_string(action));
+
+ return 0;
+}
+
static int manager_connect_udev(Manager *m) {
int r;
r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "net", NULL);
if (r < 0)
- return log_error_errno(r, "Could not add device monitor filter: %m");
+ return log_error_errno(r, "Could not add device monitor filter for net subsystem: %m");
+
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "ieee80211", NULL);
+ if (r < 0)
+ return log_error_errno(r, "Could not add device monitor filter for ieee80211 subsystem: %m");
+
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(m->device_monitor, "rfkill", NULL);
+ if (r < 0)
+ return log_error_errno(r, "Could not add device monitor filter for rfkill subsystem: %m");
r = sd_device_monitor_attach_event(m->device_monitor, m->event);
if (r < 0)
return log_error_errno(r, "Failed to attach event to device monitor: %m");
- r = sd_device_monitor_start(m->device_monitor, manager_udev_process_link, m);
+ r = sd_device_monitor_start(m->device_monitor, manager_process_uevent, m);
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
return 0;
}
-static int systemd_netlink_fd(void) {
- int n, fd, rtnl_fd = -EINVAL;
+static int manager_listen_fds(Manager *m, int *ret_rtnl_fd) {
+ _cleanup_strv_free_ char **names = NULL;
+ int n, rtnl_fd = -1;
- n = sd_listen_fds(true);
- if (n <= 0)
+ assert(m);
+ assert(ret_rtnl_fd);
+
+ n = sd_listen_fds_with_names(/* unset_environment = */ true, &names);
+ if (n < 0)
+ return n;
+
+ if (strv_length(names) != (size_t) n)
return -EINVAL;
- for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++)
+ for (int i = 0; i < n; i++) {
+ int fd = i + SD_LISTEN_FDS_START;
+
if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) {
- if (rtnl_fd >= 0)
- return -EINVAL;
+ if (rtnl_fd >= 0) {
+ log_debug("Received multiple netlink socket, ignoring.");
+ safe_close(fd);
+ continue;
+ }
rtnl_fd = fd;
+ continue;
}
- return rtnl_fd;
+ if (manager_add_tuntap_fd(m, fd, names[i]) >= 0)
+ continue;
+
+ if (m->test_mode)
+ safe_close(fd);
+ else
+ close_and_notify_warn(fd, names[i]);
+ }
+
+ *ret_rtnl_fd = rtnl_fd;
+ return 0;
}
static int manager_connect_genl(Manager *m) {
return sd_netlink_attach_filter(manager->rtnl, ELEMENTSOF(filter), filter);
}
-static int manager_connect_rtnl(Manager *m) {
- int fd, r;
+static int manager_connect_rtnl(Manager *m, int fd) {
+ _unused_ _cleanup_close_ int fd_close = fd;
+ int r;
assert(m);
- fd = systemd_netlink_fd();
+ /* This takes input fd. */
+
if (fd < 0)
r = sd_netlink_open(&m->rtnl);
else
r = sd_netlink_open_fd(&m->rtnl, fd);
if (r < 0)
return r;
+ TAKE_FD(fd_close);
/* Bump receiver buffer, but only if we are not called via socket activation, as in that
* case systemd sets the receive buffer size for us, and the value in the .socket unit
}
int manager_setup(Manager *m) {
+ _cleanup_close_ int rtnl_fd = -1;
int r;
assert(m);
if (r < 0)
return r;
- r = manager_connect_rtnl(m);
+ r = manager_listen_fds(m, &rtnl_fd);
+ if (r < 0)
+ return r;
+
+ r = manager_connect_rtnl(m, TAKE_FD(rtnl_fd));
if (r < 0)
return r;
m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
+ m->tuntap_fds_by_name = hashmap_free(m->tuntap_fds_by_name);
+
m->wiphy_by_name = hashmap_free(m->wiphy_by_name);
m->wiphy_by_index = hashmap_free_with_destructor(m->wiphy_by_index, wiphy_free);
if (r < 0)
return r;
+ manager_clear_unmanaged_tuntap_fds(m);
+
r = network_load(m, &m->networks);
if (r < 0)
return r;
return 0;
}
- r = sd_bus_call_method_async(
+ r = bus_call_method_async(
m->bus,
NULL,
- "org.freedesktop.hostname1",
- "/org/freedesktop/hostname1",
- "org.freedesktop.hostname1",
+ bus_hostname,
"SetHostname",
set_hostname_handler,
m,
return 0;
}
- r = sd_bus_call_method_async(
+ r = bus_call_method_async(
m->bus,
NULL,
- "org.freedesktop.timedate1",
- "/org/freedesktop/timedate1",
- "org.freedesktop.timedate1",
+ bus_timedate,
"SetTimezone",
set_timezone_handler,
m,
FirewallContext *fw_ctx;
OrderedSet *request_queue;
+
+ Hashmap *tuntap_fds_by_name;
};
int manager_new(Manager **ret, bool test_mode);
if (!link->network)
return false;
- if (!link_may_have_ipv6ll(link))
+ if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
return false;
assert(link->network->ipv6_accept_ra >= 0);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
- lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+ lifetime_usec = sec16_to_usec(lifetime_sec, timestamp_usec);
r = sd_ndisc_router_get_address(rt, &gateway);
if (r < 0)
if (lifetime_preferred_sec > lifetime_valid_sec)
return 0;
- lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec);
- lifetime_preferred_usec = usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec);
+ lifetime_valid_usec = sec_to_usec(lifetime_valid_sec, timestamp_usec);
+ lifetime_preferred_usec = sec_to_usec(lifetime_preferred_sec, timestamp_usec);
r = ndisc_generate_addresses(link, &prefix, prefixlen, &addresses);
if (r < 0)
route->family = AF_INET6;
route->flags = RTM_F_PREFIX;
route->dst_prefixlen = prefixlen;
- route->lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+ route->lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6);
if (r < 0)
route->gw_family = AF_INET6;
route->dst.in6 = dst;
route->dst_prefixlen = prefixlen;
- route->lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+ route->lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
r = ndisc_request_route(TAKE_PTR(route), link, rt);
if (r < 0)
if (lifetime_sec == 0)
return 0;
- lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+ lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
if (n < 0)
if (lifetime_sec == 0)
return 0;
- lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+ lifetime_usec = sec_to_usec(lifetime_sec, timestamp_usec);
r = sd_ndisc_router_dnssl_get_domains(rt, &l);
if (r < 0)
assert(link);
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
return 0;
r = ndisc_configure(link);
void ndisc_vacuum(Link *link) {
NDiscRDNSS *r;
NDiscDNSSL *d;
- usec_t time_now;
+ usec_t now_usec;
assert(link);
+ assert(link->manager);
/* Removes all RDNSS and DNSSL entries whose validity time has passed */
- time_now = now(CLOCK_BOOTTIME);
+ assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
SET_FOREACH(r, link->ndisc_rdnss)
- if (r->lifetime_usec < time_now)
+ if (r->lifetime_usec < now_usec)
free(set_remove(link->ndisc_rdnss, r));
SET_FOREACH(d, link->ndisc_dnssl)
- if (d->lifetime_usec < time_now)
+ if (d->lifetime_usec < now_usec)
free(set_remove(link->ndisc_dnssl, d));
}
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions)
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions)
Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions)
+Match.Credential, config_parse_net_condition, CONDITION_CREDENTIAL, offsetof(Network, conditions)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions)
Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(Network, conditions)
Link.MACAddress, config_parse_hw_addr, 0, offsetof(Network, hw_addr)
Network.LinkLocalAddressing, config_parse_link_local_address_family, 0, offsetof(Network, link_local)
Network.IPv6LinkLocalAddressGenerationMode, config_parse_ipv6_link_local_address_gen_mode, 0, offsetof(Network, ipv6ll_address_gen_mode)
Network.IPv6StableSecretAddress, config_parse_in_addr_non_null, AF_INET6, offsetof(Network, ipv6ll_stable_secret)
+Network.IPv4LLStartAddress, config_parse_ipv4ll_address, 0, offsetof(Network, ipv4ll_start_address)
Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route)
Network.DefaultRouteOnDevice, config_parse_bool, 0, offsetof(Network, default_route_on_device)
Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode)
DHCPv6.IAID, config_parse_iaid, AF_INET6, 0
DHCPv6.DUIDType, config_parse_duid_type, 0, offsetof(Network, dhcp6_duid)
DHCPv6.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, dhcp6_duid)
+DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_use_rapid_commit)
IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_gateway)
IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_route_prefix)
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
DHCP.RouteTable, config_parse_dhcp_or_ra_route_table, AF_INET, 0
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
-DHCP.RapidCommit, config_parse_warn_compat, DISABLED_LEGACY, 0
+DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_use_rapid_commit)
DHCP.ForceDHCPv6PDOtherInformation, config_parse_warn_compat, DISABLED_LEGACY, 0
DHCPv4.UseDomainName, config_parse_dhcp_use_domains, AF_INET, 0
DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
DHCPv6.RouteMetric, config_parse_dhcp_or_ra_route_metric, AF_INET6, 0
-DHCPv6.RapidCommit, config_parse_warn_compat, DISABLED_LEGACY, 0
DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_warn_compat, DISABLED_LEGACY, 0
DHCPv6PrefixDelegation.SubnetId, config_parse_dhcp_pd_subnet_id, 0, offsetof(Network, dhcp_pd_subnet_id)
DHCPv6PrefixDelegation.Announce, config_parse_bool, 0, offsetof(Network, dhcp_pd_announce)
.dhcp6_use_dns = true,
.dhcp6_use_hostname = true,
.dhcp6_use_ntp = true,
+ .dhcp6_use_rapid_commit = true,
.dhcp6_duid.type = _DUID_TYPE_INVALID,
.dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID,
config_item_perf_lookup, network_network_gperf_lookup,
CONFIG_PARSE_WARN,
network,
- &network->stats_by_path);
+ &network->stats_by_path,
+ NULL);
if (r < 0)
return r; /* config_parse_many() logs internally. */
bool dhcp6_use_hostname;
bool dhcp6_use_ntp;
bool dhcp6_use_ntp_set;
+ bool dhcp6_use_rapid_commit;
DHCPUseDomains dhcp6_use_domains;
bool dhcp6_use_domains_set;
uint32_t dhcp6_iaid;
AddressFamily link_local;
IPv6LinkLocalAddressGenMode ipv6ll_address_gen_mode;
struct in6_addr ipv6ll_stable_secret;
+ struct in_addr ipv4ll_start_address;
bool ipv4ll_route;
/* IPv6 RA support */
bool link_radv_enabled(Link *link) {
assert(link);
- if (!link_may_have_ipv6ll(link))
+ if (!link_may_have_ipv6ll(link, /* check_multicast = */ true))
return false;
if (link->hw_addr.length != ETH_ALEN)
assert(link);
assert(link->network);
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
return false;
if (in6_addr_is_null(&link->ipv6ll_address))
return 1;
}
-static int route_configure(const Route *route, Link *link, Request *req) {
+static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link, Request *req) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
if (r < 0)
return r;
- if (route->lifetime_usec != USEC_INFINITY) {
- r = sd_netlink_message_append_u32(m, RTA_EXPIRES,
- MIN(DIV_ROUND_UP(usec_sub_unsigned(route->lifetime_usec, now(CLOCK_BOOTTIME)), USEC_PER_SEC), UINT32_MAX));
+ if (lifetime_sec != UINT32_MAX) {
+ r = sd_netlink_message_append_u32(m, RTA_EXPIRES, lifetime_sec);
if (r < 0)
return r;
}
if (r < 0)
return false;
if (r > 0) {
- if (!link_is_ready_to_configure(l, true))
+ if (!link_is_ready_to_configure(l, /* allow_unmanaged = */ true) ||
+ !link_has_carrier(l))
return false;
m->ifindex = l->ifindex;
assert(req);
assert(link);
+ assert(link->manager);
assert(route);
r = route_is_ready_to_configure(route, link);
}
}
- r = route_configure(route, link, req);
+ usec_t now_usec;
+ assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
+ uint32_t sec = usec_to_sec(route->lifetime_usec, now_usec);
+ if (sec == 0) {
+ log_link_debug(link, "Refuse to configure %s route with zero lifetime.",
+ network_config_source_to_string(route->source));
+
+ if (converted)
+ for (size_t i = 0; i < converted->n; i++) {
+ Route *existing;
+
+ assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0);
+ route_cancel_requesting(existing);
+ }
+ else
+ route_cancel_requesting(route);
+ }
+
+ r = route_configure(route, sec, link, req);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to configure route: %m");
#include "networkd-manager.h"
#include "networkd-queue.h"
#include "networkd-setlink.h"
+#include "networkd-sriov.h"
+#include "networkd-wiphy.h"
static int get_link_default_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return link_getlink_handler_internal(rtnl, m, link, "Failed to sync link information");
return r;
if (link->network->use_bpdu >= 0) {
- r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, link->network->use_bpdu);
+ r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, !link->network->use_bpdu);
if (r < 0)
return r;
}
}
if (link->network->allow_port_to_be_root >= 0) {
- r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, link->network->allow_port_to_be_root);
+ r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, !link->network->allow_port_to_be_root);
if (r < 0)
return r;
}
assert(link->network);
if (link->network->keep_master) {
+ /* When KeepMaster=yes, BatmanAdvanced=, Bond=, Bridge=, and VRF= are ignored. */
link->master_set = true;
return 0;
- }
-
- link->master_set = false;
- if (link->network->batadv || link->network->bond || link->network->bridge || link->network->vrf)
+ } else if (link->network->batadv || link->network->bond || link->network->bridge || link->network->vrf) {
+ link->master_set = false;
return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MASTER,
link_set_master_handler,
NULL);
- else
+
+ } else if (link->master_ifindex != 0) {
+ /* Unset master only when it is set. */
+ link->master_set = false;
return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MASTER,
link_unset_master_handler,
NULL);
+
+ } else {
+ /* Nothing we need to do. */
+ link->master_set = true;
+ return 0;
+ }
}
int link_request_to_set_mtu(Link *link, uint32_t mtu) {
r = sd_netlink_message_get_errno(m);
if (r == -ENETDOWN && up && link_up_dsa_slave(link) > 0)
log_link_message_debug_errno(link, m, r, "Could not bring up dsa slave, retrying again after dsa master becomes up");
- else if (r < 0) {
- const char *error_msg;
-
- error_msg = up ?
- (on_activate ? "Could not bring up interface" : "Could not bring up interface, ignoring") :
- (on_activate ? "Could not bring down interface" : "Could not bring down interface, ignoring");
-
- log_link_message_warning_errno(link, m, r, error_msg);
- if (on_activate)
- return 0;
- }
+ else if (r < 0)
+ log_link_message_warning_errno(link, m, r, up ?
+ "Could not bring up interface, ignoring" :
+ "Could not bring down interface, ignoring");
r = link_call_getlink(link, get_link_update_flag_handler);
if (r < 0) {
return request_call_netlink_async(link->manager->rtnl, m, req);
}
-static bool link_is_ready_to_activate(Link *link) {
+static bool link_is_ready_to_activate_one(Link *link, bool allow_unmanaged) {
assert(link);
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED))
return false;
+ if (!link->network)
+ return allow_unmanaged;
+
if (link->set_link_messages > 0)
return false;
return true;
}
+ static bool link_is_ready_to_activate(Link *link, bool up) {
+ assert(link);
+
+ if (!check_ready_for_all_sr_iov_ports(link, /* allow_unmanaged = */ false,
+ link_is_ready_to_activate_one))
+ return false;
+
+ if (up && link_rfkilled(link) > 0)
+ return false;
+
+ return true;
+}
+
static int link_process_activation(Request *req, Link *link, void *userdata) {
bool up = PTR_TO_INT(userdata);
int r;
assert(req);
assert(link);
- if (!link_is_ready_to_activate(link))
+ if (!link_is_ready_to_activate(link, up))
return 0;
r = link_up_or_down(link, up, req);
if (!link->activated)
return false;
+ if (up && link_rfkilled(link) > 0)
+ return false;
+
return true;
}
/* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright © 2020 VMware, Inc. */
+#include "device-enumerator-private.h"
+#include "dirent-util.h"
+#include "fd-util.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-queue.h"
return 0;
}
+
+static int find_ifindex_from_pci_dev_port(sd_device *pci_dev, const char *dev_port) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
+ int ifindex, r;
+
+ assert(pci_dev);
+ assert(dev_port);
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_parent(e, pci_dev);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_subsystem(e, "net", true);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_sysattr(e, "dev_port", dev_port, true);
+ if (r < 0)
+ return r;
+
+ dev = sd_device_enumerator_get_device_first(e);
+ if (!dev)
+ return -ENODEV; /* no device found */
+
+ if (sd_device_enumerator_get_device_next(e))
+ return -ENXIO; /* multiple devices found */
+
+ r = sd_device_get_ifindex(dev, &ifindex);
+ if (r < 0)
+ return r;
+
+ assert(ifindex > 0);
+ return ifindex;
+}
+
+static int manager_update_sr_iov_ifindices(Manager *manager, int phys_port_ifindex, int virt_port_ifindex) {
+ Link *phys_link = NULL, *virt_link = NULL;
+ int r;
+
+ assert(manager);
+ assert(phys_port_ifindex > 0);
+ assert(virt_port_ifindex > 0);
+
+ /* This sets ifindices only when both interfaces are already managed by us. */
+
+ r = link_get_by_index(manager, phys_port_ifindex, &phys_link);
+ if (r < 0)
+ return r;
+
+ r = link_get_by_index(manager, virt_port_ifindex, &virt_link);
+ if (r < 0)
+ return r;
+
+ /* update VF ifindex in PF */
+ r = set_ensure_put(&phys_link->sr_iov_virt_port_ifindices, NULL, INT_TO_PTR(virt_port_ifindex));
+ if (r < 0)
+ return r;
+
+ log_link_debug(phys_link,
+ "Found SR-IOV VF port %s(%i).",
+ virt_link ? virt_link->ifname : "n/a", virt_port_ifindex);
+
+ /* update PF ifindex in VF */
+ if (virt_link->sr_iov_phys_port_ifindex > 0 && virt_link->sr_iov_phys_port_ifindex != phys_port_ifindex) {
+ Link *old_phys_link;
+
+ if (link_get_by_index(manager, virt_link->sr_iov_phys_port_ifindex, &old_phys_link) >= 0)
+ set_remove(old_phys_link->sr_iov_virt_port_ifindices, INT_TO_PTR(virt_port_ifindex));
+ }
+
+ virt_link->sr_iov_phys_port_ifindex = phys_port_ifindex;
+
+ log_link_debug(virt_link,
+ "Found SR-IOV PF port %s(%i).",
+ phys_link ? phys_link->ifname : "n/a", phys_port_ifindex);
+
+ return 0;
+}
+
+static int link_set_sr_iov_phys_port(Link *link) {
+ _cleanup_(sd_device_unrefp) sd_device *pci_physfn_dev = NULL;
+ const char *dev_port;
+ sd_device *pci_dev;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+
+ if (link->sr_iov_phys_port_ifindex > 0)
+ return 0;
+
+ if (!link->dev)
+ return -ENODEV;
+
+ r = sd_device_get_sysattr_value(link->dev, "dev_port", &dev_port);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_parent_with_subsystem_devtype(link->dev, "pci", NULL, &pci_dev);
+ if (r < 0)
+ return r;
+
+ r = sd_device_new_child(&pci_physfn_dev, pci_dev, "physfn");
+ if (r < 0)
+ return r;
+
+ r = find_ifindex_from_pci_dev_port(pci_physfn_dev, dev_port);
+ if (r < 0)
+ return r;
+
+ return manager_update_sr_iov_ifindices(link->manager, r, link->ifindex);
+}
+
+static int link_set_sr_iov_virt_ports(Link *link) {
+ const char *dev_port, *pci_syspath;
+ _cleanup_closedir_ DIR *dir = NULL;
+ sd_device *pci_dev;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+
+ set_clear(link->sr_iov_virt_port_ifindices);
+
+ if (!link->dev)
+ return -ENODEV;
+
+ r = sd_device_get_sysattr_value(link->dev, "dev_port", &dev_port);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_parent_with_subsystem_devtype(link->dev, "pci", NULL, &pci_dev);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_syspath(pci_dev, &pci_syspath);
+ if (r < 0)
+ return r;
+
+ dir = opendir(pci_syspath);
+ if (!dir)
+ return -errno;
+
+ FOREACH_DIRENT_ALL(de, dir, break) {
+ _cleanup_(sd_device_unrefp) sd_device *pci_virtfn_dev = NULL;
+
+ if (de->d_type != DT_LNK)
+ continue;
+
+ /* Accept name prefixed with "virtfn", but refuse "virtfn" itself. */
+ if (isempty(startswith(de->d_name, "virtfn")))
+ continue;
+
+ if (sd_device_new_child(&pci_virtfn_dev, pci_dev, de->d_name) < 0)
+ continue;
+
+ if (find_ifindex_from_pci_dev_port(pci_virtfn_dev, dev_port) < 0)
+ continue;
+
+ if (manager_update_sr_iov_ifindices(link->manager, link->ifindex, r) < 0)
+ continue;
+ }
+
+ return 0;
+}
+
+int link_set_sr_iov_ifindices(Link *link) {
+ int r;
+
+ assert(link);
+
+ r = link_set_sr_iov_phys_port(link);
+ if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
+ return r;
+
+ r = link_set_sr_iov_virt_ports(link);
+ if (r < 0 && !ERRNO_IS_DEVICE_ABSENT(r))
+ return r;
+
+ return 0;
+}
+
+void link_clear_sr_iov_ifindices(Link *link) {
+ void *v;
+
+ assert(link);
+ assert(link->manager);
+
+ if (link->sr_iov_phys_port_ifindex > 0) {
+ Link *phys_link;
+
+ if (link_get_by_index(link->manager, link->sr_iov_phys_port_ifindex, &phys_link) >= 0)
+ set_remove(phys_link->sr_iov_virt_port_ifindices, INT_TO_PTR(link->ifindex));
+
+ link->sr_iov_phys_port_ifindex = 0;
+ }
+
+ while ((v = set_steal_first(link->sr_iov_virt_port_ifindices))) {
+ Link *virt_link;
+
+ if (link_get_by_index(link->manager, PTR_TO_INT(v), &virt_link) >= 0)
+ virt_link->sr_iov_phys_port_ifindex = 0;
+ }
+}
+
+bool check_ready_for_all_sr_iov_ports(
+ Link *link,
+ bool allow_unmanaged, /* for the main target */
+ bool (check_one)(Link *link, bool allow_unmanaged)) {
+
+ Link *phys_link;
+ void *v;
+
+ assert(link);
+ assert(link->manager);
+ assert(check_one);
+
+ /* Some drivers make VF ports become down when their PF port becomes down, and may fail to configure
+ * VF ports. Also, when a VF port becomes up/down, its PF port and other VF ports may become down.
+ * See issue #23315. */
+
+ /* First, check the main target. */
+ if (!check_one(link, allow_unmanaged))
+ return false;
+
+ /* If this is a VF port, then also check the PF port. */
+ if (link->sr_iov_phys_port_ifindex > 0) {
+ if (link_get_by_index(link->manager, link->sr_iov_phys_port_ifindex, &phys_link) < 0 ||
+ !check_one(phys_link, /* allow_unmanaged = */ true))
+ return false;
+ } else
+ phys_link = link;
+
+ /* Also check all VF ports. */
+ SET_FOREACH(v, phys_link->sr_iov_virt_port_ifindices) {
+ int ifindex = PTR_TO_INT(v);
+ Link *virt_link;
+
+ if (ifindex == link->ifindex)
+ continue; /* The main target link is a VF port, and its state is already checked. */
+
+ if (link_get_by_index(link->manager, ifindex, &virt_link) < 0)
+ return false;
+
+ if (!check_one(virt_link, /* allow_unmanaged = */ true))
+ return false;
+ }
+
+ return true;
+}
typedef struct Link Link;
int link_request_sr_iov_vfs(Link *link);
+
+int link_set_sr_iov_ifindices(Link *link);
+void link_clear_sr_iov_ifindices(Link *link);
+
+bool check_ready_for_all_sr_iov_ports(
+ Link *link,
+ bool allow_unmanaged, /* for the main target */
+ bool (check_one)(Link *link, bool allow_unmanaged));
NETWORK_CONFIG_STATE_REMOVING = 1 << 5, /* e.g. address_remove() is called, but no response is received yet */
} NetworkConfigState;
+static inline usec_t sec16_to_usec(uint16_t sec, usec_t timestamp_usec) {
+ return sec == UINT16_MAX ? USEC_INFINITY : usec_add(timestamp_usec, sec * USEC_PER_SEC);
+}
+
+static inline usec_t sec_to_usec(uint32_t sec, usec_t timestamp_usec) {
+ return sec == UINT32_MAX ? USEC_INFINITY : usec_add(timestamp_usec, sec * USEC_PER_SEC);
+}
+
+static inline uint32_t usec_to_sec(usec_t usec, usec_t now_usec) {
+ return MIN(DIV_ROUND_UP(usec_sub_unsigned(usec, now_usec), USEC_PER_SEC), UINT32_MAX);
+}
+
CONFIG_PARSER_PROTOTYPE(config_parse_link_local_address_family);
CONFIG_PARSER_PROTOTYPE(config_parse_address_family_with_kernel);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_masquerade);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <net/if_arp.h>
#include <linux/nl80211.h>
+#include "device-private.h"
#include "device-util.h"
#include "networkd-manager.h"
#include "networkd-wiphy.h"
#include "parse-util.h"
+#include "path-util.h"
+#include "udev-util.h"
#include "wifi-util.h"
Wiphy *wiphy_free(Wiphy *w) {
hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
}
+ sd_device_unref(w->dev);
+ sd_device_unref(w->rfkill);
+
free(w->name);
return mfree(w);
}
-static int wiphy_new(Manager *manager, uint32_t index, Wiphy **ret) {
+static int wiphy_new(Manager *manager, sd_netlink_message *message, Wiphy **ret) {
_cleanup_(wiphy_freep) Wiphy *w = NULL;
+ _cleanup_free_ char *name = NULL;
+ uint32_t index;
int r;
assert(manager);
+ assert(message);
+
+ r = sd_netlink_message_read_u32(message, NL80211_ATTR_WIPHY, &index);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_read_string_strdup(message, NL80211_ATTR_WIPHY_NAME, &name);
+ if (r < 0)
+ return r;
w = new(Wiphy, 1);
if (!w)
return -ENOMEM;
*w = (Wiphy) {
+ .manager = manager,
.index = index,
+ .name = TAKE_PTR(name),
};
r = hashmap_ensure_put(&manager->wiphy_by_index, NULL, UINT32_TO_PTR(w->index), w);
if (r < 0)
return r;
- w->manager = manager;
+ r = hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
+ if (r < 0)
+ return r;
+
+ log_wiphy_debug(w, "Saved new wiphy: index=%"PRIu32, w->index);
if (ret)
*ret = w;
return 0;
}
+static int link_get_wiphy(Link *link, Wiphy **ret) {
+ _cleanup_(sd_device_unrefp) sd_device *phy = NULL;
+ const char *s;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+
+ if (link->iftype != ARPHRD_ETHER)
+ return -EOPNOTSUPP;
+
+ if (!link->dev)
+ return -ENODEV;
+
+ r = sd_device_get_devtype(link->dev, &s);
+ if (r < 0)
+ return r;
+
+ if (!streq_ptr(s, "wlan"))
+ return -EOPNOTSUPP;
+
+ r = sd_device_new_child(&phy, link->dev, "phy80211");
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_sysname(phy, &s);
+ if (r < 0)
+ return r;
+
+ /* TODO:
+ * Maybe, it is better to cache the found Wiphy object in the Link object.
+ * To support that, we need to investigate what happens when the _phy_ is renamed. */
+
+ return wiphy_get_by_name(link->manager, s, ret);
+}
+
+static int rfkill_get_state(sd_device *dev) {
+ int r;
+
+ assert(dev);
+
+ /* The previous values may be outdated. Let's clear cache and re-read the values. */
+ device_clear_sysattr_cache(dev);
+
+ r = device_get_sysattr_bool(dev, "soft");
+ if (r < 0 && r != -ENOENT)
+ return r;
+ if (r > 0)
+ return RFKILL_SOFT;
+
+ r = device_get_sysattr_bool(dev, "hard");
+ if (r < 0 && r != -ENOENT)
+ return r;
+ if (r > 0)
+ return RFKILL_HARD;
+
+ return RFKILL_UNBLOCKED;
+}
+
+static int wiphy_rfkilled(Wiphy *w) {
+ int r;
+
+ assert(w);
+
+ if (!udev_available()) {
+ if (w->rfkill_state != RFKILL_UNBLOCKED) {
+ log_wiphy_debug(w, "Running in container, assuming the radio transmitter is unblocked.");
+ w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
+ }
+ return false;
+ }
+
+ if (!w->rfkill) {
+ if (w->rfkill_state != RFKILL_UNBLOCKED) {
+ log_wiphy_debug(w, "No rfkill device found, assuming the radio transmitter is unblocked.");
+ w->rfkill_state = RFKILL_UNBLOCKED; /* To suppress the above log message, cache the state. */
+ }
+ return false;
+ }
+
+ r = rfkill_get_state(w->rfkill);
+ if (r < 0)
+ return log_wiphy_debug_errno(w, r, "Could not get rfkill state: %m");
+
+ if (w->rfkill_state != r)
+ switch (r) {
+ case RFKILL_UNBLOCKED:
+ log_wiphy_debug(w, "The radio transmitter is unblocked.");
+ break;
+ case RFKILL_SOFT:
+ log_wiphy_debug(w, "The radio transmitter is turned off by software.");
+ break;
+ case RFKILL_HARD:
+ log_wiphy_debug(w, "The radio transmitter is forced off by something outside of the driver's control.");
+ break;
+ default:
+ assert_not_reached();
+ }
+
+ w->rfkill_state = r; /* Cache the state to suppress the above log messages. */
+ return r != RFKILL_UNBLOCKED;
+}
+
+int link_rfkilled(Link *link) {
+ Wiphy *w;
+ int r;
+
+ assert(link);
+
+ r = link_get_wiphy(link, &w);
+ if (r < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_DEVICE_ABSENT(r))
+ return false; /* Typically, non-wifi interface or running in container */
+ return log_link_debug_errno(link, r, "Could not get phy: %m");
+ }
+
+ return wiphy_rfkilled(w);
+}
+
static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) {
const char *name;
int r;
if (r < 0)
return r;
- if (streq_ptr(w->name, name))
+ if (streq(w->name, name))
return 0;
- if (w->name)
- hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
+ log_wiphy_debug(w, "Wiphy name change detected, renamed to %s.", name);
+
+ hashmap_remove_value(w->manager->wiphy_by_name, w->name, w);
r = free_and_strdup(&w->name, name);
if (r < 0)
return r;
- return hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
+ r = hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w);
+ if (r < 0)
+ return r;
+
+ return 1; /* updated */
}
-static int wiphy_update(Wiphy *w, sd_netlink_message *message) {
+static int wiphy_update_device(Wiphy *w) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
int r;
assert(w);
- assert(message);
+ assert(w->name);
+
+ if (!udev_available())
+ return 0;
+
+ w->dev = sd_device_unref(w->dev);
+
+ r = sd_device_new_from_subsystem_sysname(&dev, "ieee80211", w->name);
+ if (r < 0)
+ return r;
+
+ if (DEBUG_LOGGING) {
+ const char *s = NULL;
+
+ (void) sd_device_get_syspath(dev, &s);
+ log_wiphy_debug(w, "Found device: %s", strna(s));
+ }
+
+ w->dev = TAKE_PTR(dev);
+ return 0;
+}
+
+static int wiphy_update_rfkill(Wiphy *w) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *rfkill;
+ int r;
+
+ assert(w);
+
+ if (!udev_available())
+ return 0;
+
+ w->rfkill = sd_device_unref(w->rfkill);
+
+ if (!w->dev)
+ return 0;
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_subsystem(e, "rfkill", true);
+ if (r < 0)
+ return r;
- r = wiphy_update_name(w, message);
+ r = sd_device_enumerator_add_match_parent(e, w->dev);
if (r < 0)
- return log_wiphy_debug_errno(w, r, "Failed to update wiphy name: %m");
+ return r;
+
+ rfkill = sd_device_enumerator_get_device_first(e);
+ if (!rfkill)
+ /* rfkill device may not detected by the kernel yet, and may appear later. */
+ return -ENODEV;
+
+ if (sd_device_enumerator_get_device_next(e))
+ return -ENXIO; /* multiple devices found */
+
+ w->rfkill = sd_device_ref(rfkill);
+
+ if (DEBUG_LOGGING) {
+ const char *s = NULL;
+
+ (void) sd_device_get_syspath(rfkill, &s);
+ log_wiphy_debug(w, "Found rfkill device: %s", strna(s));
+ }
+
+ return 0;
+}
+
+static int wiphy_update(Wiphy *w) {
+ int r;
+
+ assert(w);
+
+ r = wiphy_update_device(w);
+ if (r < 0) {
+ if (ERRNO_IS_DEVICE_ABSENT(r))
+ log_wiphy_debug_errno(w, r, "Failed to update wiphy device, ignoring: %m");
+ else
+ return log_wiphy_warning_errno(w, r, "Failed to update wiphy device: %m");
+ }
+
+ r = wiphy_update_rfkill(w);
+ if (r < 0) {
+ if (ERRNO_IS_DEVICE_ABSENT(r))
+ log_wiphy_debug_errno(w, r, "Failed to update rfkill device, ignoring: %m");
+ else
+ return log_wiphy_warning_errno(w, r, "Failed to update rfkill device: %m");
+ }
return 0;
}
switch (cmd) {
case NL80211_CMD_NEW_WIPHY: {
- bool is_new = !w;
if (!w) {
- r = wiphy_new(manager, index, &w);
+ r = wiphy_new(manager, message, &w);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to save new wiphy, ignoring: %m");
+ return 0;
+ }
+ } else {
+ r = wiphy_update_name(w, message);
if (r < 0) {
- log_warning_errno(r, "Failed to allocate wiphy, ignoring: %m");
+ log_wiphy_warning_errno(w, r, "Failed to update wiphy name, ignoring: %m");
return 0;
}
+ if (r == 0)
+ return 0;
}
- r = wiphy_update(w, message);
- if (r < 0) {
+ r = wiphy_update(w);
+ if (r < 0)
log_wiphy_warning_errno(w, r, "Failed to update wiphy, ignoring: %m");
- return 0;
- }
- log_wiphy_debug(w, "Received %s phy.", is_new ? "new" : "updated");
break;
}
case NL80211_CMD_DEL_WIPHY:
return 0;
}
+
+int manager_udev_process_wiphy(Manager *m, sd_device *device, sd_device_action_t action) {
+ const char *name;
+ Wiphy *w;
+ int r;
+
+ assert(m);
+ assert(device);
+
+ r = sd_device_get_sysname(device, &name);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to get sysname: %m");
+
+ r = wiphy_get_by_name(m, name, &w);
+ if (r < 0) {
+ /* This error is not critical, as the corresponding genl message may be received later. */
+ log_device_debug_errno(device, r, "Failed to get Wiphy object, ignoring: %m");
+ return 0;
+ }
+
+ return device_unref_and_replace(w->dev, action == SD_DEVICE_REMOVE ? NULL : device);
+}
+
+int manager_udev_process_rfkill(Manager *m, sd_device *device, sd_device_action_t action) {
+ _cleanup_free_ char *parent_path = NULL, *parent_name = NULL;
+ const char *s;
+ Wiphy *w;
+ int r;
+
+ assert(m);
+ assert(device);
+
+ r = sd_device_get_syspath(device, &s);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to get syspath: %m");
+
+ /* Do not use sd_device_get_parent() here, as this might be a 'remove' uevent. */
+ r = path_extract_directory(s, &parent_path);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to get parent syspath: %m");
+
+ r = path_extract_filename(parent_path, &parent_name);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to get parent name: %m");
+
+ r = wiphy_get_by_name(m, parent_name, &w);
+ if (r < 0) {
+ /* This error is not critical, as the corresponding genl message may be received later. */
+ log_device_debug_errno(device, r, "Failed to get Wiphy object: %m");
+ return 0;
+ }
+
+ return device_unref_and_replace(w->rfkill, action == SD_DEVICE_REMOVE ? NULL : device);
+}
#include "macro.h"
+typedef struct Link Link;
typedef struct Manager Manager;
+/* The following values are different from the ones defined in linux/rfkill.h. */
+typedef enum RFKillState {
+ RFKILL_UNKNOWN,
+ RFKILL_UNBLOCKED,
+ RFKILL_SOFT,
+ RFKILL_HARD,
+ _RFKILL_STATE_MAX,
+ _RFKILL_STATE_INVALID = -EINVAL,
+} RFKillState;
+
typedef struct Wiphy {
Manager *manager;
uint32_t index;
char *name;
+
+ sd_device *dev;
+ sd_device *rfkill;
+ RFKillState rfkill_state;
} Wiphy;
Wiphy *wiphy_free(Wiphy *w);
int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret);
int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret);
+int link_rfkilled(Link *link);
+
int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager);
+int manager_udev_process_wiphy(Manager *m, sd_device *device, sd_device_action_t action);
+int manager_udev_process_rfkill(Manager *m, sd_device *device, sd_device_action_t action);
#define log_wiphy_full_errno_zerook(w, level, error, ...) \
({ \
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return false;
- if (IN_SET(qdisc->parent, TC_H_ROOT, TC_H_CLSACT)) /* TC_H_CLSACT == TC_H_INGRESS */
- return true;
+ /* TC_H_CLSACT == TC_H_INGRESS */
+ if (!IN_SET(qdisc->parent, TC_H_ROOT, TC_H_CLSACT) &&
+ link_find_tclass(link, qdisc->parent, NULL) < 0)
+ return false;
+
+ if (QDISC_VTABLE(qdisc) &&
+ QDISC_VTABLE(qdisc)->is_ready &&
+ QDISC_VTABLE(qdisc)->is_ready(qdisc, link) <= 0)
+ return false;
- return link_find_tclass(link, qdisc->parent, NULL) >= 0;
+ return true;
}
static int qdisc_process_request(Request *req, Link *link, QDisc *qdisc) {
int (*init)(QDisc *qdisc);
int (*fill_message)(Link *link, QDisc *qdisc, sd_netlink_message *m);
int (*verify)(QDisc *qdisc);
+ int (*is_ready)(QDisc *qdisc, Link *link);
} QDiscVTable;
extern const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX];
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "macro.h"
+#include "networkd-link.h"
#include "parse-util.h"
#include "string-util.h"
#include "teql.h"
return free_and_replace(qdisc->tca_kind, tca_kind);
}
+static int trivial_link_equalizer_is_ready(QDisc *qdisc, Link *link) {
+ Link *teql;
+
+ assert(qdisc);
+ assert(qdisc->tca_kind);
+ assert(link);
+ assert(link->manager);
+
+ if (link_get_by_name(link->manager, qdisc->tca_kind, &teql) < 0)
+ return false;
+
+ return link_is_ready_to_configure(teql, /* allow_unmanaged = */ true);
+}
+
const QDiscVTable teql_vtable = {
.object_size = sizeof(TrivialLinkEqualizer),
.verify = trivial_link_equalizer_verify,
+ .is_ready = trivial_link_equalizer_is_ready,
};
int config_parse_trivial_link_equalizer_id(
SD_BUS_PARAM(fd),
bus_method_dump_by_fd,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_SIGNAL_WITH_NAMES("Killed",
+ "ss",
+ SD_BUS_PARAM(cgroup)
+ SD_BUS_PARAM(reason),
+ 0),
SD_BUS_VTABLE_END
};
r = cg_path_get_owner_uid(message.path, &cg_uid);
if (r < 0) {
- log_debug("Failed to get cgroup %s owner uid: %m", message.path);
+ log_debug_errno(r, "Failed to get cgroup %s owner uid: %m", message.path);
continue;
}
if (r < 0)
log_notice_errno(r, "Failed to kill any cgroup(s) based on swap: %m");
else {
- if (selected && r > 0)
+ if (selected && r > 0) {
log_notice("Killed %s due to memory used (%"PRIu64") / total (%"PRIu64") and "
"swap used (%"PRIu64") / total (%"PRIu64") being more than "
PERMYRIAD_AS_PERCENT_FORMAT_STR,
m->system_context.mem_used, m->system_context.mem_total,
m->system_context.swap_used, m->system_context.swap_total,
PERMYRIAD_AS_PERCENT_FORMAT_VAL(m->swap_used_limit_permyriad));
+
+ /* send dbus signal */
+ (void) sd_bus_emit_signal(m->bus,
+ "/org/freedesktop/oom1",
+ "org.freedesktop.oom1.Manager",
+ "Killed",
+ "ss",
+ selected,
+ "memory-used");
+ }
return 0;
}
}
* it. In either case, go through the event loop again and select a new candidate if
* pressure is still high. */
m->mem_pressure_post_action_delay_start = usec_now;
- if (selected && r > 0)
+ if (selected && r > 0) {
log_notice("Killed %s due to memory pressure for %s being %lu.%02lu%% > %lu.%02lu%%"
" for > %s with reclaim activity",
selected, t->path,
LOADAVG_INT_SIDE(t->memory_pressure.avg10), LOADAVG_DECIMAL_SIDE(t->memory_pressure.avg10),
LOADAVG_INT_SIDE(t->mem_pressure_limit), LOADAVG_DECIMAL_SIDE(t->mem_pressure_limit),
FORMAT_TIMESPAN(m->default_mem_pressure_duration_usec, USEC_PER_SEC));
+
+ /* send dbus signal */
+ (void) sd_bus_emit_signal(m->bus,
+ "/org/freedesktop/oom1",
+ "org.freedesktop.oom1.Manager",
+ "Killed",
+ "ss",
+ selected,
+ "memory-pressure");
+ }
return 0;
}
}
static const char *arg_node = NULL;
static char *arg_root = NULL;
static char *arg_image = NULL;
-static char *arg_definitions = NULL;
+static char **arg_definitions = NULL;
static bool arg_discard = true;
static bool arg_can_factory_reset = false;
static int arg_factory_reset = -1;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
-STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
struct Partition {
char *definition_path;
+ char **drop_in_files;
sd_id128_t type_uuid;
sd_id128_t current_uuid, new_uuid;
free(p->current_label);
free(p->new_label);
free(p->definition_path);
+ strv_free(p->drop_in_files);
if (p->current_partition)
fdisk_unref_partition(p->current_partition);
return 0;
}
-static int partition_read_definition(Partition *p, const char *path) {
+static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
ConfigTableItem table[] = {
{ "Partition", "Type", config_parse_type, 0, &p->type_uuid },
{}
};
int r;
+ _cleanup_free_ char *filename = NULL;
+ const char* dropin_dirname;
- r = config_parse(NULL, path, NULL,
- "Partition\0",
- config_item_table_lookup, table,
- CONFIG_PARSE_WARN,
- p,
- NULL);
+ r = path_extract_filename(path, &filename);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from path '%s': %m", path);;
+
+ dropin_dirname = strjoina(filename, ".d");
+
+ r = config_parse_many(
+ STRV_MAKE_CONST(path),
+ conf_file_dirs,
+ dropin_dirname,
+ "Partition\0",
+ config_item_table_lookup, table,
+ CONFIG_PARSE_WARN,
+ p,
+ NULL,
+ &p->drop_in_files);
if (r < 0)
return r;
static int context_read_definitions(
Context *context,
- const char *directory,
+ char **directories,
const char *root) {
_cleanup_strv_free_ char **files = NULL;
Partition *last = NULL;
int r;
+ const char *const *dirs;
assert(context);
- if (directory)
- r = conf_files_list_strv(&files, ".conf", NULL, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) STRV_MAKE(directory));
- else
- r = conf_files_list_strv(&files, ".conf", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) CONF_PATHS_STRV("repart.d"));
+ dirs = (const char* const*) (directories ?: CONF_PATHS_STRV("repart.d"));
+
+ r = conf_files_list_strv(&files, ".conf", directories ? NULL : root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, dirs);
if (r < 0)
return log_error_errno(r, "Failed to enumerate *.conf files: %m");
if (!p->definition_path)
return log_oom();
- r = partition_read_definition(p, *f);
+ r = partition_read_definition(p, *f, dirs);
if (r < 0)
return r;
if (*backing_fd < 0) {
/* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */
- *backing_fd = fcntl(fdisk_get_devfd(c), F_DUPFD_CLOEXEC, 3);
+ *backing_fd = fd_reopen(fdisk_get_devfd(c), O_RDONLY|O_CLOEXEC);
if (*backing_fd < 0)
- return log_error_errno(errno, "Failed to duplicate fdisk fd: %m");
- }
+ return log_error_errno(*backing_fd, "Failed to duplicate fdisk fd: %m");
- /* Tell udev not to interfere while we are processing the device */
- if (flock(fdisk_get_devfd(c), arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
- return log_error_errno(errno, "Failed to lock block device: %m");
+ /* Tell udev not to interfere while we are processing the device */
+ if (flock(*backing_fd, arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
+ return log_error_errno(errno, "Failed to lock block device: %m");
+ }
/* The offsets/sizes libfdisk returns to us will be in multiple of the sector size of the
* device. This is typically 512, and sometimes 4096. Let's query libfdisk once for it, and then use
secsz = fdisk_get_sector_size(c);
/* Insist on a power of two, and that it's a multiple of 512, i.e. the traditional sector size. */
- if (secsz < 512 || secsz != 1UL << log2u64(secsz))
- return log_error_errno(errno, "Sector size %lu is not a power of two larger than 512? Refusing.", secsz);
+ if (secsz < 512 || !ISPOWEROF2(secsz))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size %lu is not a power of two larger than 512? Refusing.", secsz);
/* Use at least 4K, and ensure it's a multiple of the sector size, regardless if that is smaller or
* larger */
_cleanup_(table_unrefp) Table *t = NULL;
uint64_t sum_padding = 0, sum_size = 0;
int r;
+ const size_t dropin_files_col = 13;
+ bool no_dropin_files = true;
if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) {
log_info("Empty partition table.");
return 0;
}
- t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity");
+ t = table_new("type", "label", "uuid", "file", "node", "offset", "old size", "raw size", "size", "old padding", "raw padding", "padding", "activity", "drop-in files");
if (!t)
return log_oom();
if (!DEBUG_LOGGING) {
if (arg_json_format_flags & JSON_FORMAT_OFF)
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
- (size_t) 8, (size_t) 11);
+ (size_t) 8, (size_t) 11, dropin_files_col);
else
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
- (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10, (size_t) 12);
+ (size_t) 5, (size_t) 6, (size_t) 7, (size_t) 9, (size_t) 10, (size_t) 12,
+ dropin_files_col);
}
(void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100);
TABLE_UINT64, p->current_padding == UINT64_MAX ? 0 : p->current_padding,
TABLE_UINT64, p->new_padding,
TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL,
- TABLE_STRING, activity ?: "unchanged");
+ TABLE_STRING, activity ?: "unchanged",
+ TABLE_STRV, p->drop_in_files);
if (r < 0)
return table_log_add_error(r);
+
+ no_dropin_files = no_dropin_files && strv_isempty(p->drop_in_files);
}
if ((arg_json_format_flags & JSON_FORMAT_OFF) && (sum_padding > 0 || sum_size > 0)) {
TABLE_EMPTY,
TABLE_EMPTY,
TABLE_STRING, b,
+ TABLE_EMPTY,
TABLE_EMPTY);
if (r < 0)
return table_log_add_error(r);
}
+ if (no_dropin_files) {
+ r = table_hide_column_from_display(t, dropin_files_col);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set columns to display: %m");
+ }
+
return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
}
(void) context_dump_partitions(context, node);
- putc('\n', stdout);
-
- if (arg_json_format_flags & JSON_FORMAT_OFF)
+ if (arg_json_format_flags & JSON_FORMAT_OFF) {
+ putc('\n', stdout);
(void) context_dump_partition_bar(context, node);
- putc('\n', stdout);
+ putc('\n', stdout);
+ }
+
fflush(stdout);
}
arg_pretty = r;
break;
- case ARG_DEFINITIONS:
- r = parse_path_argument(optarg, false, &arg_definitions);
+ case ARG_DEFINITIONS: {
+ _cleanup_free_ char *path = NULL;
+ r = parse_path_argument(optarg, false, &path);
if (r < 0)
return r;
+ if (strv_consume(&arg_definitions, TAKE_PTR(path)) < 0)
+ return log_oom();
break;
+ }
case ARG_SIZE: {
uint64_t parsed, rounded;
if (!context)
return log_oom();
+ strv_uniq(arg_definitions);
+
r = context_read_definitions(context, arg_definitions, arg_root);
if (r < 0)
return r;
"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager --json=help
"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager --json=pretty
"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager --json=short
+
+echo "### Testing drop-in overrides ###"
+
+mkdir -p "$D/definitions-overrides"
+
+cat >"$D/definitions-overrides/root.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
+EOF
+
+mkdir -p "$D/definitions-overrides/root.conf.d"
+
+cat >"$D/definitions-overrides/root.conf.d/override1.conf" <<EOF
+[Partition]
+Label=label1
+SizeMaxBytes=32M
+EOF
+
+cat >"$D/definitions-overrides/root.conf.d/override2.conf" <<EOF
+[Partition]
+Label=label2
+EOF
+
+rm -f test-drop-in-image
+
+JSON_OUTPUT=$("$repart" --definitions="$D/definitions-overrides" --dry-run=yes --empty=create --size=100M --json=pretty test-drop-in-image)
+
+diff <(echo "$JSON_OUTPUT") - <<EOF
+[
+ {
+ "type" : "swap",
+ "label" : "label2",
+ "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+ "file" : "root.conf",
+ "node" : "test-drop-in-image1",
+ "offset" : 1048576,
+ "old_size" : 0,
+ "raw_size" : 33554432,
+ "old_padding" : 0,
+ "raw_padding" : 0,
+ "activity" : "create",
+ "drop-in_files" : [
+ "$D/definitions-overrides/root.conf.d/override1.conf",
+ "$D/definitions-overrides/root.conf.d/override2.conf"
+ ]
+ }
+]
+EOF
+
+echo "### Testing list of definitions directories ###"
+
+mkdir -p "$D/definitions1"
+
+cat >"$D/definitions1/root1.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=32M
+UUID=7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0
+Label=label1
+EOF
+
+mkdir -p "$D/definitions2"
+
+cat >"$D/definitions2/root2.conf" <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=32M
+UUID=837c3d67-21b3-478e-be82-7e7f83bf96d3
+Label=label2
+EOF
+
+rm -f test-definitions
+
+JSON_OUTPUT=$("$repart" --definitions="$D/definitions1" --definitions="$D/definitions2" --dry-run=yes --empty=create --size=100M --json=pretty test-definitions)
+
+diff <(echo "$JSON_OUTPUT") - <<EOF
+[
+ {
+ "type" : "swap",
+ "label" : "label1",
+ "uuid" : "7b93d1f2-595d-4ce3-b0b9-837fbd9e63b0",
+ "file" : "root1.conf",
+ "node" : "test-definitions1",
+ "offset" : 1048576,
+ "old_size" : 0,
+ "raw_size" : 33554432,
+ "old_padding" : 0,
+ "raw_padding" : 0,
+ "activity" : "create"
+ },
+ {
+ "type" : "swap",
+ "label" : "label2",
+ "uuid" : "837c3d67-21b3-478e-be82-7e7f83bf96d3",
+ "file" : "root2.conf",
+ "node" : "test-definitions2",
+ "offset" : 34603008,
+ "old_size" : 0,
+ "raw_size" : 33554432,
+ "old_padding" : 0,
+ "raw_padding" : 0,
+ "activity" : "create"
+ }
+]
+EOF
if (!arg_now)
return 0;
- r = sd_bus_call_method(
+ r = bus_call_method(
bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
+ bus_systemd_mgr,
method,
&error,
&reply,
-# The "trusted" profile for services, i.e. no restrictions are applied
+# The "trusted" profile for services, i.e. no restrictions are applied apart from a private /tmp
[Service]
MountAPIVFS=yes
+PrivateTmp=yes
BindPaths=/run
BindReadOnlyPaths=/etc/machine-id
BindReadOnlyPaths=/etc/resolv.conf
assert(s);
- dot = strchr(s, '.');
+ dot = strrchr(s, '.');
if (dot) {
_cleanup_free_ char *iface = NULL;
return 1;
}
-int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) {
+int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p, usec_t ts, unsigned max_rr) {
unsigned ancount = 0;
DnsCacheItem *i;
- usec_t t;
int r;
assert(cache);
assert(p);
-
- t = now(CLOCK_BOOTTIME);
+ assert(p->protocol == DNS_PROTOCOL_MDNS);
HASHMAP_FOREACH(i, cache->by_key)
LIST_FOREACH(by_key, j, i) {
/* RFC6762 7.1: Don't append records with less than half the TTL remaining
* as known answers. */
- if (usec_sub_unsigned(j->until, t) < j->rr->ttl * USEC_PER_SEC / 2)
+ if (usec_sub_unsigned(j->until, ts) < j->rr->ttl * USEC_PER_SEC / 2)
continue;
r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL);
- if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) {
- /* For mDNS, if we're unable to stuff all known answers into the given packet,
- * allocate a new one, push the RR into that one and link it to the current one.
- */
+ if (r == -EMSGSIZE) {
+ if (max_rr == 0)
+ /* If max_rr == 0, do not allocate more packets. */
+ goto finalize;
+
+ /* If we're unable to stuff all known answers into the given packet, allocate
+ * a new one, push the RR into that one and link it to the current one. */
DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
ancount = 0;
return r;
ancount++;
+ if (max_rr > 0 && ancount >= max_rr) {
+ DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
+ ancount = 0;
+
+ r = dns_packet_new_query(&p->more, p->protocol, 0, true);
+ if (r < 0)
+ return r;
+
+ p = p->more;
+
+ max_rr = UINT_MAX;
+ }
}
+finalize:
DNS_PACKET_HEADER(p)->ancount = htobe16(ancount);
return 0;
unsigned dns_cache_size(DnsCache *cache);
-int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p);
+int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p, usec_t ts, unsigned max_rr);
if (has_search_domains && dns_name_is_single_label(domain))
return DNS_SCOPE_YES_BASE + 1;
+ /* If ResolveUnicastSingleLabel=yes and the query is single-label, then bump match result
+ to prevent LLMNR monopoly among candidates. */
+ if (s->manager->resolve_unicast_single_label && dns_name_is_single_label(domain))
+ return DNS_SCOPE_YES_BASE + 1;
+
/* Let's return the number of labels in the best matching result */
if (n_best >= 0) {
assert(n_best <= DNS_SCOPE_YES_END - DNS_SCOPE_YES_BASE);
return;
}
- assert(dns_question_size(p->question) == 1);
+ if (dns_question_size(p->question) != 1)
+ return (void) log_debug("Received LLMNR query without question or multiple questions, ignoring.");
+
key = dns_question_first_key(p->question);
r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
}
int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) {
- usec_t jitter;
int r;
assert(scope);
if (scope->conflict_event_source)
return 0;
- random_bytes(&jitter, sizeof(jitter));
- jitter %= LLMNR_JITTER_INTERVAL_USEC;
-
r = sd_event_add_time_relative(
scope->manager->event,
&scope->conflict_event_source,
CLOCK_BOOTTIME,
- jitter,
- LLMNR_JITTER_INTERVAL_USEC,
+ random_u64_range(LLMNR_JITTER_INTERVAL_USEC),
+ 0,
on_conflict_dispatch, scope);
if (r < 0)
return log_debug_errno(r, "Failed to add conflict dispatch event: %m");
&scope->announce_event_source,
CLOCK_BOOTTIME,
MDNS_ANNOUNCE_DELAY,
- MDNS_JITTER_RANGE_USEC,
+ 0,
on_announcement_timeout, scope);
if (r < 0)
return log_debug_errno(r, "Failed to schedule second announcement: %m");
return 0;
}
+static int dns_transaction_setup_timeout(
+ DnsTransaction *t,
+ usec_t timeout_usec /* relative */,
+ usec_t next_usec /* CLOCK_BOOTTIME */) {
+
+ int r;
+
+ assert(t);
+
+ dns_transaction_stop_timeout(t);
+
+ r = sd_event_add_time_relative(
+ t->scope->manager->event,
+ &t->timeout_event_source,
+ CLOCK_BOOTTIME,
+ timeout_usec, 0,
+ on_transaction_timeout, t);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
+
+ t->next_attempt_after = next_usec;
+ t->state = DNS_TRANSACTION_PENDING;
+ return 0;
+}
+
static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
assert(t);
assert(t->scope);
return DNS_TIMEOUT_USEC;
case DNS_PROTOCOL_MDNS:
- assert(t->n_attempts > 0);
if (t->probing)
return MDNS_PROBING_INTERVAL_USEC;
- else
- return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
+
+ /* See RFC 6762 Section 5.1 suggests that timeout should be a few seconds. */
+ assert(t->n_attempts > 0);
+ return (1 << (t->n_attempts - 1)) * USEC_PER_SEC;
case DNS_PROTOCOL_LLMNR:
return t->scope->resend_timeout;
return 0;
}
- if (t->n_attempts >= dns_transaction_attempts_max(t->scope->protocol, t->probing)) {
+ if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
DnsTransactionState result;
if (t->scope->protocol == DNS_PROTOCOL_LLMNR)
return 1;
}
+static int dns_packet_append_zone(DnsPacket *p, DnsTransaction *t, DnsResourceKey *k, unsigned *nscount) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ bool tentative;
+ int r;
+
+ assert(p);
+ assert(t);
+ assert(k);
+
+ if (k->type != DNS_TYPE_ANY)
+ return 0;
+
+ r = dns_zone_lookup(&t->scope->zone, k, t->scope->link->ifindex, &answer, NULL, &tentative);
+ if (r < 0)
+ return r;
+
+ return dns_packet_append_answer(p, answer, nscount);
+}
+
static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- bool add_known_answers = false;
- DnsResourceKey *tkey;
_cleanup_set_free_ Set *keys = NULL;
- unsigned qdcount;
- unsigned nscount = 0;
+ unsigned qdcount, ancount = 0 /* avoid false maybe-uninitialized warning */, nscount;
+ bool add_known_answers = false;
usec_t ts;
int r;
/* Discard any previously prepared packet, so we can start over and coalesce again */
t->sent = dns_packet_unref(t->sent);
+ /* First, create a dummy packet to calculate packet size. */
r = dns_packet_new_query(&p, t->scope->protocol, 0, false);
if (r < 0)
return r;
if (dns_key_is_shared(dns_transaction_key(t)))
add_known_answers = true;
- if (dns_transaction_key(t)->type == DNS_TYPE_ANY) {
- r = set_ensure_put(&keys, &dns_resource_key_hash_ops, dns_transaction_key(t));
- if (r < 0)
- return r;
- }
+ r = dns_packet_append_zone(p, t, dns_transaction_key(t), NULL);
+ if (r < 0)
+ return r;
+
+ /* Save appended keys */
+ r = set_ensure_put(&keys, &dns_resource_key_hash_ops, dns_transaction_key(t));
+ if (r < 0)
+ return r;
/*
* For mDNS, we want to coalesce as many open queries in pending transactions into one single
assert_se(sd_event_now(t->scope->manager->event, CLOCK_BOOTTIME, &ts) >= 0);
- LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) {
-
- /* Skip ourselves */
- if (other == t)
- continue;
+ for (bool restart = true; restart;) {
+ restart = false;
+ LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) {
+ size_t saved_packet_size;
+ bool append = false;
- if (other->state != DNS_TRANSACTION_PENDING)
- continue;
-
- if (other->next_attempt_after > ts)
- continue;
-
- if (qdcount >= UINT16_MAX)
- break;
+ /* Skip ourselves */
+ if (other == t)
+ continue;
- r = dns_packet_append_key(p, dns_transaction_key(other), 0, NULL);
+ if (other->state != DNS_TRANSACTION_PENDING)
+ continue;
- /*
- * If we can't stuff more questions into the packet, just give up.
- * One of the 'other' transactions will fire later and take care of the rest.
- */
- if (r == -EMSGSIZE)
- break;
+ if (other->next_attempt_after > ts)
+ continue;
- if (r < 0)
- return r;
+ if (!set_contains(keys, dns_transaction_key(other))) {
+ r = dns_packet_append_key(p, dns_transaction_key(other), 0, &saved_packet_size);
+ /* If we can't stuff more questions into the packet, just give up.
+ * One of the 'other' transactions will fire later and take care of the rest. */
+ if (r == -EMSGSIZE)
+ break;
+ if (r < 0)
+ return r;
- r = dns_transaction_prepare(other, ts);
- if (r <= 0)
- continue;
+ r = dns_packet_append_zone(p, t, dns_transaction_key(other), NULL);
+ if (r == -EMSGSIZE)
+ break;
+ if (r < 0)
+ return r;
- ts += transaction_get_resend_timeout(other);
+ append = true;
+ }
- r = sd_event_add_time(
- other->scope->manager->event,
- &other->timeout_event_source,
- CLOCK_BOOTTIME,
- ts, 0,
- on_transaction_timeout, other);
- if (r < 0)
- return r;
+ r = dns_transaction_prepare(other, ts);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ if (append)
+ dns_packet_truncate(p, saved_packet_size);
- (void) sd_event_source_set_description(other->timeout_event_source, "dns-transaction-timeout");
+ /* In this case, not only this transaction, but multiple transactions may be
+ * freed. Hence, we need to restart the loop. */
+ restart = true;
+ break;
+ }
- other->state = DNS_TRANSACTION_PENDING;
- other->next_attempt_after = ts;
+ usec_t timeout = transaction_get_resend_timeout(other);
+ r = dns_transaction_setup_timeout(other, timeout, usec_add(ts, timeout));
+ if (r < 0)
+ return r;
- qdcount++;
+ if (dns_key_is_shared(dns_transaction_key(other)))
+ add_known_answers = true;
- if (dns_key_is_shared(dns_transaction_key(other)))
- add_known_answers = true;
+ if (append) {
+ r = set_ensure_put(&keys, &dns_resource_key_hash_ops, dns_transaction_key(other));
+ if (r < 0)
+ return r;
+ }
- if (dns_transaction_key(other)->type == DNS_TYPE_ANY) {
- r = set_ensure_put(&keys, &dns_resource_key_hash_ops, dns_transaction_key(other));
- if (r < 0)
- return r;
+ qdcount++;
+ if (qdcount >= UINT16_MAX)
+ break;
}
}
- DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount);
-
/* Append known answer section if we're asking for any shared record */
if (add_known_answers) {
- r = dns_cache_export_shared_to_packet(&t->scope->cache, p);
+ r = dns_cache_export_shared_to_packet(&t->scope->cache, p, ts, 0);
if (r < 0)
return r;
+
+ ancount = be16toh(DNS_PACKET_HEADER(p)->ancount);
}
- SET_FOREACH(tkey, keys) {
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- bool tentative;
+ /* Then, create actual packet. */
+ p = dns_packet_unref(p);
+ r = dns_packet_new_query(&p, t->scope->protocol, 0, false);
+ if (r < 0)
+ return r;
- r = dns_zone_lookup(&t->scope->zone, tkey, t->scope->link->ifindex, &answer, NULL, &tentative);
+ /* Questions */
+ DnsResourceKey *k;
+ SET_FOREACH(k, keys) {
+ r = dns_packet_append_key(p, k, 0, NULL);
if (r < 0)
return r;
+ }
+ DNS_PACKET_HEADER(p)->qdcount = htobe16(qdcount);
- r = dns_packet_append_answer(p, answer, &nscount);
+ /* Known answers */
+ if (add_known_answers) {
+ r = dns_cache_export_shared_to_packet(&t->scope->cache, p, ts, ancount);
+ if (r < 0)
+ return r;
+ }
+
+ /* Authorities */
+ nscount = 0;
+ SET_FOREACH(k, keys) {
+ r = dns_packet_append_zone(p, t, k, &nscount);
if (r < 0)
return r;
}
DNS_PACKET_HEADER(p)->nscount = htobe16(nscount);
t->sent = TAKE_PTR(p);
-
return 0;
}
if (!t->initial_jitter_scheduled &&
IN_SET(t->scope->protocol, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS)) {
- usec_t jitter, accuracy;
+ usec_t jitter;
- /* RFC 4795 Section 2.7 suggests all queries should be delayed by a random time from 0 to
- * JITTER_INTERVAL. */
+ /* RFC 4795 Section 2.7 suggests all LLMNR queries should be delayed by a random time from 0 to
+ * JITTER_INTERVAL.
+ * RFC 6762 Section 8.1 suggests initial probe queries should be delayed by a random time from
+ * 0 to 250ms. */
t->initial_jitter_scheduled = true;
+ t->n_attempts = 0;
switch (t->scope->protocol) {
case DNS_PROTOCOL_LLMNR:
jitter = random_u64_range(LLMNR_JITTER_INTERVAL_USEC);
- accuracy = LLMNR_JITTER_INTERVAL_USEC;
break;
case DNS_PROTOCOL_MDNS:
- jitter = usec_add(random_u64_range(MDNS_JITTER_RANGE_USEC), MDNS_JITTER_MIN_USEC);
- accuracy = MDNS_JITTER_RANGE_USEC;
+ if (t->probing)
+ jitter = random_u64_range(MDNS_PROBING_INTERVAL_USEC);
+ else
+ jitter = 0;
break;
default:
assert_not_reached();
}
- assert(!t->timeout_event_source);
-
- r = sd_event_add_time_relative(
- t->scope->manager->event,
- &t->timeout_event_source,
- CLOCK_BOOTTIME,
- jitter, accuracy,
- on_transaction_timeout, t);
+ r = dns_transaction_setup_timeout(t, jitter, ts);
if (r < 0)
return r;
- (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
-
- t->n_attempts = 0;
- t->next_attempt_after = ts;
- t->state = DNS_TRANSACTION_PENDING;
-
log_debug("Delaying %s transaction %" PRIu16 " for " USEC_FMT "us.",
dns_protocol_to_string(t->scope->protocol),
t->id,
return dns_transaction_go(t);
}
- ts += transaction_get_resend_timeout(t);
-
- r = sd_event_add_time(
- t->scope->manager->event,
- &t->timeout_event_source,
- CLOCK_BOOTTIME,
- ts, 0,
- on_transaction_timeout, t);
+ usec_t timeout = transaction_get_resend_timeout(t);
+ r = dns_transaction_setup_timeout(t, timeout, usec_add(ts, timeout));
if (r < 0)
return r;
- (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout");
-
- t->state = DNS_TRANSACTION_PENDING;
- t->next_attempt_after = ts;
-
return 1;
}
/* LLMNR Jitter interval, see RFC 4795 Section 7 */
#define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
-/* mDNS Jitter interval, see RFC 6762 Section 5.2 */
-#define MDNS_JITTER_MIN_USEC (20 * USEC_PER_MSEC)
-#define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC)
-
/* mDNS probing interval, see RFC 6762 Section 8.1 */
#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC)
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
-/* Maximum attempts to send MDNS requests is one except for probe requests, see RFC 6762 Section 8.1
- * RFC 6762 differentiates between normal (single-shot/continuous) and probe requests.
- * It therefore makes sense to attempt each normal query only once with no retries.
- * Otherwise we'd be sending out three attempts for even a normal query. */
-#define MDNS_TRANSACTION_ATTEMPTS_MAX 1
-
-#define MDNS_PROBE_TRANSACTION_ATTEMPTS_MAX 3
-
-static inline unsigned dns_transaction_attempts_max(DnsProtocol p, bool probing) {
-
- switch (p) {
-
- case DNS_PROTOCOL_LLMNR:
- return LLMNR_TRANSACTION_ATTEMPTS_MAX;
-
- case DNS_PROTOCOL_MDNS:
- if (probing)
- return MDNS_PROBE_TRANSACTION_ATTEMPTS_MAX;
- else
- return MDNS_TRANSACTION_ATTEMPTS_MAX;
+/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */
+#define MDNS_TRANSACTION_ATTEMPTS_MAX 3
- case DNS_PROTOCOL_DNS:
- return DNS_TRANSACTION_ATTEMPTS_MAX;
-
- default:
- return 0;
- }
-}
+#define TRANSACTION_ATTEMPTS_MAX(p) (((p) == DNS_PROTOCOL_LLMNR) ? \
+ LLMNR_TRANSACTION_ATTEMPTS_MAX : \
+ (((p) == DNS_PROTOCOL_MDNS) ? \
+ MDNS_TRANSACTION_ATTEMPTS_MAX : \
+ DNS_TRANSACTION_ATTEMPTS_MAX))
config_item_perf_lookup, resolved_dnssd_gperf_lookup,
CONFIG_PARSE_WARN,
service,
+ NULL,
NULL);
if (r < 0)
return r;
if (r < 0)
return r;
- assert(r > 0);
-
if (proposed_rrs_cmp(remote, r, our, size) > 0)
return 1;
}
/* All the questions in the query had a QU bit set, RFC 6762, section 5.4 */
- DNS_QUESTION_FOREACH_ITEM(item, p->question) {
+ DNS_QUESTION_FOREACH_ITEM(item, p->question)
if (!FLAGS_SET(item->flags, DNS_QUESTION_WANTS_UNICAST_REPLY))
return false;
- }
+
return true;
}
if (r < 0)
return log_debug_errno(r, "Failed to extract resource records from incoming packet: %m");
- assert_return((dns_question_size(p->question) > 0), -EINVAL);
+ if (dns_question_size(p->question) <= 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Received mDNS query without question, ignoring.");
unicast_reply = mdns_should_reply_using_unicast(p);
if (unicast_reply && !sender_on_local_subnet(s, p)) {
}
}
- LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
- r = dns_answer_match_key(p->answer, t->key, NULL);
- if (r < 0)
- log_debug_errno(r, "Failed to match resource key, ignoring: %m");
- else if (r > 0) /* This packet matches the transaction, let's pass it on as reply */
+ for (bool match = true; match;) {
+ match = false;
+ LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
+ if (t->state != DNS_TRANSACTION_PENDING)
+ continue;
+
+ r = dns_answer_match_key(p->answer, dns_transaction_key(t), NULL);
+ if (r <= 0) {
+ if (r < 0)
+ log_debug_errno(r, "Failed to match resource key, ignoring: %m");
+ continue;
+ }
+
+ /* This packet matches the transaction, let's pass it on as reply */
dns_transaction_process_reply(t, p, false);
+
+ /* The dns_transaction_process_reply() -> dns_transaction_complete() ->
+ * dns_query_candidate_stop() may free multiple transactions. Hence, restart
+ * the loop. */
+ match = true;
+ break;
+ }
}
dns_cache_put(&scope->cache, scope->manager->enable_cache, NULL, DNS_PACKET_RCODE(p), p->answer, NULL, false, _DNSSEC_RESULT_INVALID, UINT32_MAX, p->family, &p->sender);
return bus_log_create_error(r);
}
- r = bus_append_unit_property_assignment_many(m, t, properties);
- if (r < 0)
- return r;
-
- return 0;
+ return bus_append_unit_property_assignment_many(m, t, properties);
}
static int transient_cgroup_set_properties(sd_bus_message *m) {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <linux/blkpg.h>
#include <sys/file.h>
+#include <sys/ioctl.h>
#include <unistd.h>
+#include "sd-device.h"
+
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
+#include "device-util.h"
#include "devnum-util.h"
#include "dirent-util.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "missing_magic.h"
return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
}
+
+int fd_get_whole_disk(int fd, bool backing, dev_t *ret) {
+ dev_t devt;
+ struct stat st;
+ int r;
+
+ assert(ret);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (S_ISBLK(st.st_mode))
+ devt = st.st_rdev;
+ else if (!backing)
+ return -ENOTBLK;
+ else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
+ return -ENOTBLK;
+ else if (major(st.st_dev) != 0)
+ devt = st.st_dev;
+ else {
+ _cleanup_close_ int regfd = -1;
+
+ /* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
+ * handing, to get the backing device node. */
+
+ regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (regfd < 0)
+ return regfd;
+
+ r = btrfs_get_block_device_fd(regfd, &devt);
+ if (r == -ENOTTY)
+ return -ENOTBLK;
+ if (r < 0)
+ return r;
+ }
+
+ return block_get_whole_disk(devt, ret);
+}
+
+int path_get_whole_disk(const char *path, bool backing, dev_t *ret) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_CLOEXEC|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ return fd_get_whole_disk(fd, backing, ret);
+}
+
+int block_device_add_partition(int fd, const char *name, int nr, uint64_t start, uint64_t size) {
+ assert(fd >= 0);
+ assert(name);
+ assert(nr > 0);
+
+ struct blkpg_partition bp = {
+ .pno = nr,
+ .start = start,
+ .length = size,
+ };
+
+ struct blkpg_ioctl_arg ba = {
+ .op = BLKPG_ADD_PARTITION,
+ .data = &bp,
+ .datalen = sizeof(bp),
+ };
+
+ if (strlen(name) >= sizeof(bp.devname))
+ return -EINVAL;
+
+ strcpy(bp.devname, name);
+
+ return RET_NERRNO(ioctl(fd, BLKPG, &ba));
+}
+
+int block_device_remove_partition(int fd, const char *name, int nr) {
+ assert(fd >= 0);
+ assert(name);
+ assert(nr > 0);
+
+ struct blkpg_partition bp = {
+ .pno = nr,
+ };
+
+ struct blkpg_ioctl_arg ba = {
+ .op = BLKPG_DEL_PARTITION,
+ .data = &bp,
+ .datalen = sizeof(bp),
+ };
+
+ if (strlen(name) >= sizeof(bp.devname))
+ return -EINVAL;
+
+ strcpy(bp.devname, name);
+
+ return RET_NERRNO(ioctl(fd, BLKPG, &ba));
+}
+
+int block_device_remove_all_partitions(int fd) {
+ struct stat stat;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *part;
+ int r, k = 0;
+
+ if (fstat(fd, &stat) < 0)
+ return -errno;
+
+ r = sd_device_new_from_devnum(&dev, 'b', stat.st_rdev);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_parent(e, dev);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_subsystem(e, "block", true);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_property(e, "DEVTYPE", "partition");
+ if (r < 0)
+ return r;
+
+ FOREACH_DEVICE(e, part) {
+ const char *v, *devname;
+ int nr;
+
+ r = sd_device_get_devname(part, &devname);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_property_value(part, "PARTN", &v);
+ if (r < 0)
+ return r;
+
+ r = safe_atoi(v, &nr);
+ if (r < 0)
+ return r;
+
+ r = block_device_remove_partition(fd, devname, nr);
+ if (r == -ENODEV) {
+ log_debug("Kernel removed partition %s before us, ignoring", devname);
+ continue;
+ }
+ if (r < 0) {
+ log_debug_errno(r, "Failed to remove partition %s: %m", devname);
+ k = k ?: r;
+ continue;
+ }
+
+ log_debug("Removed partition %s", devname);
+ }
+
+ return k;
+}
int fd_is_encrypted(int fd);
int path_is_encrypted(const char *path);
+
+int fd_get_whole_disk(int fd, bool backing, dev_t *ret);
+int path_get_whole_disk(const char *path, bool backing, dev_t *ret);
+
+int block_device_add_partition(int fd, const char *name, int nr, uint64_t start, uint64_t size);
+int block_device_remove_partition(int fd, const char *name, int nr);
+int block_device_remove_all_partitions(int fd);
.interface = "org.freedesktop.timedate1"
};
+const BusLocator* const bus_hostname = &(BusLocator){
+ .destination = "org.freedesktop.hostname1",
+ .path = "/org/freedesktop/hostname1",
+ .interface = "org.freedesktop.hostname1"
+};
+
/* Shorthand flavors of the sd-bus convenience helpers with destination,path,interface strings encapsulated
* within a single struct. */
int bus_call_method_async(
extern const BusLocator* const bus_resolve_mgr;
extern const BusLocator* const bus_systemd_mgr;
extern const BusLocator* const bus_timedate;
+extern const BusLocator* const bus_hostname;
/* Shorthand flavors of the sd-bus convenience helpers with destination,path,interface strings encapsulated
* within a single struct. */
bus_print_property_value(name, expected_value, flags, s);
- } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
+ } else if (STR_IN_SET(name, "CPUWeight", "StartupCPUWeight") && u == CGROUP_WEIGHT_IDLE)
+ bus_print_property_value(name, expected_value, flags, "idle");
+
+ else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
(STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
(STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) ||
(STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == UINT64_MAX) ||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "bus-locator.h"
#include "bus-unit-procs.h"
#include "glyph-util.h"
#include "hashmap.h"
prefix = strempty(prefix);
- r = sd_bus_call_method(
+ r = bus_call_method(
bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
+ bus_systemd_mgr,
"GetUnitProcesses",
error,
&reply,
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_weight_parse);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string);
DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "CPUWeight",
- "StartupCPUWeight",
- "IOWeight",
+ "StartupCPUWeight"))
+ return bus_append_cg_cpu_weight_parse(m, field, eq);
+
+ if (STR_IN_SET(field, "IOWeight",
"StartupIOWeight"))
return bus_append_cg_weight_parse(m, field, eq);
if (streq(field, "TimeoutStopSec"))
return bus_append_parse_sec_rename(m, field, eq);
+ /* Scope units don't have execution context but we still want to allow setting these two,
+ * so let's handle them separately. */
+ if (STR_IN_SET(field, "User", "Group"))
+ return bus_append_string(m, field, eq);
+
return 0;
}
case UNIT_TARGET:
case UNIT_DEVICE:
case UNIT_SWAP:
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Not supported unit type");
+ break;
default:
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Invalid unit type");
+ assert_not_reached();
}
r = bus_append_unit_property(m, field, eq);
return 0;
}
+int cg_cpu_weight_parse(const char *s, uint64_t *ret) {
+ if (streq_ptr(s, "idle"))
+ return *ret = CGROUP_WEIGHT_IDLE;
+ return cg_weight_parse(s, ret);
+}
+
int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
uint64_t u;
int r;
bool cg_is_hybrid_wanted(void);
int cg_weight_parse(const char *s, uint64_t *ret);
+int cg_cpu_weight_parse(const char *s, uint64_t *ret);
int cg_cpu_shares_parse(const char *s, uint64_t *ret);
int cg_blkio_weight_parse(const char *s, uint64_t *ret);
#include "cgroup-util.h"
#include "condition.h"
#include "cpu-set-util.h"
+#include "creds-util.h"
#include "efi-api.h"
#include "env-file.h"
#include "env-util.h"
return false;
}
+static int condition_test_credential(Condition *c, char **env) {
+ int (*gd)(const char **ret);
+ int r;
+
+ assert(c);
+ assert(c->parameter);
+ assert(c->type == CONDITION_CREDENTIAL);
+
+ /* For now we'll do a very simple existence check and are happy with either a regular or an encrypted
+ * credential. Given that we check the syntax of the argument we have the option to later maybe allow
+ * contents checks too without breaking compatibility, but for now let's be minimalistic. */
+
+ if (!credential_name_valid(c->parameter)) /* credentials with invalid names do not exist */
+ return false;
+
+ FOREACH_POINTER(gd, get_credentials_dir, get_encrypted_credentials_dir) {
+ _cleanup_free_ char *j = NULL;
+ const char *cd;
+
+ r = gd(&cd);
+ if (r == -ENXIO) /* no env var set */
+ continue;
+ if (r < 0)
+ return r;
+
+ j = path_join(cd, c->parameter);
+ if (!j)
+ return -ENOMEM;
+
+ if (laccess(j, F_OK) >= 0)
+ return true; /* yay! */
+ if (errno != ENOENT)
+ return -errno;
+
+ /* not found in this dir */
+ }
+
+ return false;
+}
+
typedef enum {
/* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest
* should be listed first. */
[CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
[CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
[CONDITION_KERNEL_VERSION] = condition_test_kernel_version,
+ [CONDITION_CREDENTIAL] = condition_test_credential,
[CONDITION_VIRTUALIZATION] = condition_test_virtualization,
[CONDITION_SECURITY] = condition_test_security,
[CONDITION_CAPABILITY] = condition_test_capability,
[CONDITION_HOST] = "ConditionHost",
[CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
[CONDITION_KERNEL_VERSION] = "ConditionKernelVersion",
+ [CONDITION_CREDENTIAL] = "ConditionCredential",
[CONDITION_SECURITY] = "ConditionSecurity",
[CONDITION_CAPABILITY] = "ConditionCapability",
[CONDITION_AC_POWER] = "ConditionACPower",
[CONDITION_HOST] = "AssertHost",
[CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine",
[CONDITION_KERNEL_VERSION] = "AssertKernelVersion",
+ [CONDITION_CREDENTIAL] = "AssertCredential",
[CONDITION_SECURITY] = "AssertSecurity",
[CONDITION_CAPABILITY] = "AssertCapability",
[CONDITION_AC_POWER] = "AssertACPower",
CONDITION_HOST,
CONDITION_KERNEL_COMMAND_LINE,
CONDITION_KERNEL_VERSION,
+ CONDITION_CREDENTIAL,
CONDITION_SECURITY,
CONDITION_CAPABILITY,
CONDITION_AC_POWER,
return 1;
}
-static int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st) {
+int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st) {
_cleanup_free_ struct stat *st_copy = NULL;
_cleanup_free_ char *path_copy = NULL;
int r;
const void *table,
ConfigParseFlags flags,
void *userdata,
- Hashmap **ret_stats_by_path) {
+ Hashmap **ret_stats_by_path,
+ char ***ret_dropin_files) {
_cleanup_strv_free_ char **files = NULL;
int r;
if (r < 0)
return r;
- return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
+ r = config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
+ if (r < 0)
+ return r;
+
+ if (ret_dropin_files)
+ *ret_dropin_files = TAKE_PTR(files);
+
+ return 0;
}
-static int config_get_stats_by_path_one(
+static int dropins_get_stats_by_path(
const char* conf_file,
const char* const* conf_file_dirs,
- Hashmap *stats_by_path) {
+ Hashmap **stats_by_path) {
_cleanup_strv_free_ char **files = NULL;
_cleanup_free_ char *dropin_dirname = NULL;
- struct stat st;
int r;
assert(conf_file);
assert(conf_file_dirs);
assert(stats_by_path);
- /* Unlike config_parse(), this does not support stream. */
-
r = path_extract_filename(conf_file, &dropin_dirname);
if (r < 0)
return r;
if (r < 0)
return r;
- /* First read the main config file. */
- r = RET_NERRNO(stat(conf_file, &st));
- if (r >= 0) {
- r = hashmap_put_stats_by_path(&stats_by_path, conf_file, &st);
- if (r < 0)
- return r;
- } else if (r != -ENOENT)
- return r;
-
- /* Then read all the drop-ins. */
STRV_FOREACH(fn, files) {
+ struct stat st;
+
if (stat(*fn, &st) < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
- r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
+ r = hashmap_put_stats_by_path(stats_by_path, *fn, &st);
if (r < 0)
return r;
}
const char *root,
unsigned flags,
const char* const* dirs,
+ bool check_dropins,
Hashmap **ret) {
_cleanup_hashmap_free_ Hashmap *stats_by_path = NULL;
assert(dirs);
assert(ret);
+ /* Unlike config_parse(), this does not support stream. */
+
r = conf_files_list_strv(&files, suffix, root, flags, dirs);
if (r < 0)
return r;
- stats_by_path = hashmap_new(&path_hash_ops_free_free);
- if (!stats_by_path)
- return -ENOMEM;
-
STRV_FOREACH(f, files) {
- r = config_get_stats_by_path_one(*f, dirs, stats_by_path);
+ struct stat st;
+
+ /* First read the main config file. */
+ if (stat(*f, &st) < 0) {
+ if (errno == ENOENT)
+ continue;
+
+ return -errno;
+ }
+
+ r = hashmap_put_stats_by_path(&stats_by_path, *f, &st);
+ if (r < 0)
+ return r;
+
+ if (!check_dropins)
+ continue;
+
+ /* Then read all the drop-ins if requested. */
+ r = dropins_get_stats_by_path(*f, dirs, &stats_by_path);
if (r < 0)
return r;
}
const void *table,
ConfigParseFlags flags,
void *userdata,
- Hashmap **ret_stats_by_path); /* possibly NULL */
+ Hashmap **ret_stats_by_path, /* possibly NULL */
+ char ***ret_drop_in_files); /* possibly NULL */
int config_get_stats_by_path(
const char *suffix,
const char *root,
unsigned flags,
const char* const* dirs,
+ bool check_dropins,
Hashmap **ret);
+int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st);
bool stats_by_path_equal(Hashmap *a, Hashmap *b);
typedef struct ConfigSection {
return 1;
}
+static int fd_copy_tree_generic(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ dev_t original_device,
+ unsigned depth_left,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags,
+ HardlinkContext *hardlink_context,
+ const char *display_path,
+ copy_progress_path_t progress_path,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata);
+
static int fd_copy_regular(
int df,
const char *from,
if (r > 0)
continue;
}
+ }
- q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, child_display_path, progress_path, progress_bytes, userdata);
- } else if (S_ISREG(buf.st_mode))
- q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags, hardlink_context, progress_bytes, userdata);
- else if (S_ISLNK(buf.st_mode))
- q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
- else if (S_ISFIFO(buf.st_mode))
- q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags, hardlink_context);
- else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
- q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags, hardlink_context);
- else
- q = -EOPNOTSUPP;
+ q = fd_copy_tree_generic(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, child_display_path, progress_path, progress_bytes, userdata);
if (q == -EINTR) /* Propagate SIGINT/SIGTERM up instantly */
return q;
return r;
}
+static int fd_copy_leaf(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags,
+ HardlinkContext *hardlink_context,
+ const char *display_path,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
+ int r;
+
+ if (S_ISREG(st->st_mode))
+ r = fd_copy_regular(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, progress_bytes, userdata);
+ else if (S_ISLNK(st->st_mode))
+ r = fd_copy_symlink(df, from, st, dt, to, override_uid, override_gid, copy_flags);
+ else if (S_ISFIFO(st->st_mode))
+ r = fd_copy_fifo(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context);
+ else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode) || S_ISSOCK(st->st_mode))
+ r = fd_copy_node(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context);
+ else
+ r = -EOPNOTSUPP;
+
+ return r;
+}
+
+static int fd_copy_tree_generic(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ dev_t original_device,
+ unsigned depth_left,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags,
+ HardlinkContext *hardlink_context,
+ const char *display_path,
+ copy_progress_path_t progress_path,
+ copy_progress_bytes_t progress_bytes,
+ void *userdata) {
+ int r;
+
+ if (S_ISDIR(st->st_mode))
+ return fd_copy_directory(df, from, st, dt, to, original_device, depth_left-1, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_path, progress_bytes, userdata);
+
+ r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
+ /* We just tried to copy a leaf node of the tree. If it failed because the node already exists *and* the COPY_REPLACE flag has been provided, we should unlink the node and re-copy. */
+ if (r == -EEXIST && (copy_flags & COPY_REPLACE)) {
+ /* This codepath is us trying to address an error to copy, if the unlink fails, lets just return the original error. */
+ if (unlinkat(dt, to, 0) < 0)
+ return r;
+
+ r = fd_copy_leaf(df, from, st, dt, to, override_uid, override_gid, copy_flags, hardlink_context, display_path, progress_bytes, userdata);
+ }
+
+ return r;
+}
+
int copy_tree_at_full(
int fdf,
const char *from,
if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
- if (S_ISREG(st.st_mode))
- r = fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL, progress_bytes, userdata);
- else if (S_ISDIR(st.st_mode))
- r = fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
- else if (S_ISLNK(st.st_mode))
- r = fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
- else if (S_ISFIFO(st.st_mode))
- r = fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
- else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
- r = fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags, NULL);
- else
- return -EOPNOTSUPP;
+ r = fd_copy_tree_generic(fdf, from, &st, fdt, to, st.st_dev, COPY_DEPTH_MAX, override_uid, override_gid, copy_flags, NULL, NULL, progress_path, progress_bytes, userdata);
if (r < 0)
return r;
(char**) ret, ret_size);
}
+int get_credential_user_password(const char *username, char **ret_password, bool *ret_is_hashed) {
+ _cleanup_(erase_and_freep) char *creds_password = NULL;
+ _cleanup_free_ char *cn = NULL;
+ int r;
+
+ /* Try to pick up the password for this account via the credentials logic */
+ cn = strjoin("passwd.hashed-password.", username);
+ if (!cn)
+ return -ENOMEM;
+
+ r = read_credential(cn, (void**) &creds_password, NULL);
+ if (r == -ENOENT) {
+ free(cn);
+ cn = strjoin("passwd.plaintext-password.", username);
+ if (!cn)
+ return -ENOMEM;
+
+ r = read_credential(cn, (void**) &creds_password, NULL);
+ if (r < 0)
+ log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
+ else
+ *ret_is_hashed = false;
+ } else if (r < 0)
+ log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
+ else
+ *ret_is_hashed = true;
+
+ *ret_password = TAKE_PTR(creds_password);
+
+ return r;
+}
+
#if HAVE_OPENSSL
#define CREDENTIAL_HOST_SECRET_SIZE 4096
int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *ret_size);
+int get_credential_user_password(const char *username, char **ret_password, bool *ret_is_hashed);
+
/* The four modes we support: keyed only by on-disk key, only by TPM2 HMAC key, and by the combination of
* both, as well as one with a fixed zero length key if TPM2 is missing (the latter of course provides no
* authenticity or confidentiality, but is still useful for integrity protection, and makes things simpler
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "daemon-util.h"
+#include "fd-util.h"
+#include "log.h"
+#include "string-util.h"
+
+static int notify_remove_fd_warn(const char *name) {
+ int r;
+
+ assert(name);
+
+ r = sd_notifyf(/* unset_environment = */ false,
+ "FDSTOREREMOVE=1\n"
+ "FDNAME=%s", name);
+ if (r < 0)
+ return log_warning_errno(r,
+ "Failed to remove file descriptor \"%s\" from the store, ignoring: %m",
+ name);
+
+ return 0;
+}
+
+int notify_remove_fd_warnf(const char *format, ...) {
+ _cleanup_free_ char *p = NULL;
+ va_list ap;
+ int r;
+
+ assert(format);
+
+ va_start(ap, format);
+ r = vasprintf(&p, format, ap);
+ va_end(ap);
+ if (r < 0)
+ return log_oom();
+
+ return notify_remove_fd_warn(p);
+}
+
+int close_and_notify_warn(int fd, const char *name) {
+ if (name)
+ (void) notify_remove_fd_warn(name);
+
+ return safe_close(fd);
+}
+
+static int notify_push_fd(int fd, const char *name) {
+ _cleanup_free_ char *state = NULL;
+
+ assert(fd >= 0);
+ assert(name);
+
+ state = strjoin("FDSTORE=1\n"
+ "FDNAME=", name);
+ if (!state)
+ return -ENOMEM;
+
+ return sd_pid_notify_with_fds(0, /* unset_environment = */ false, state, &fd, 1);
+}
+
+int notify_push_fdf(int fd, const char *format, ...) {
+ _cleanup_free_ char *name = NULL;
+ va_list ap;
+ int r;
+
+ assert(fd >= 0);
+ assert(format);
+
+ va_start(ap, format);
+ r = vasprintf(&name, format, ap);
+ va_end(ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ return notify_push_fd(fd, name);
+}
#include "sd-daemon.h"
+#include "macro.h"
+
#define NOTIFY_READY "READY=1\n" "STATUS=Processing requests..."
#define NOTIFY_STOPPING "STOPPING=1\n" "STATUS=Shutting down..."
if (*p)
(void) sd_notify(false, *p);
}
+
+int notify_remove_fd_warnf(const char *format, ...) _printf_(1, 2);
+int close_and_notify_warn(int fd, const char *name);
+int notify_push_fdf(int fd, const char *format, ...) _printf_(2, 3);
#include <valgrind/memcheck.h>
#endif
-#include <linux/blkpg.h>
#include <linux/dm-ioctl.h>
#include <linux/loop.h>
#include <sys/file.h>
log_debug("Unexpected partition flag %llu set on %s!", bit, node);
}
}
-
-static int ioctl_partition_remove(int fd, const char *name, int nr) {
- assert(fd >= 0);
- assert(name);
- assert(nr > 0);
-
- struct blkpg_partition bp = {
- .pno = nr,
- };
-
- struct blkpg_ioctl_arg ba = {
- .op = BLKPG_DEL_PARTITION,
- .data = &bp,
- .datalen = sizeof(bp),
- };
-
- if (strlen(name) >= sizeof(bp.devname))
- return -EINVAL;
-
- strcpy(bp.devname, name);
-
- return RET_NERRNO(ioctl(fd, BLKPG, &ba));
-}
#endif
static void dissected_partition_done(int fd, DissectedPartition *p) {
if (p->node && p->partno > 0 && !p->relinquished) {
int r;
- r = ioctl_partition_remove(fd, p->node, p->partno);
+ r = block_device_remove_partition(fd, p->node, p->partno);
if (r < 0)
log_debug_errno(r, "BLKPG_DEL_PARTITION failed, ignoring: %m");
}
}
#if HAVE_BLKID
-static int ioctl_partition_add(
- int fd,
- const char *name,
- int nr,
- uint64_t start,
- uint64_t size) {
-
- assert(fd >= 0);
- assert(name);
- assert(nr > 0);
-
- struct blkpg_partition bp = {
- .pno = nr,
- .start = start,
- .length = size,
- };
-
- struct blkpg_ioctl_arg ba = {
- .op = BLKPG_ADD_PARTITION,
- .data = &bp,
- .datalen = sizeof(bp),
- };
-
- if (strlen(name) >= sizeof(bp.devname))
- return -EINVAL;
-
- strcpy(bp.devname, name);
-
- return RET_NERRNO(ioctl(fd, BLKPG, &ba));
-}
-
static int make_partition_devname(
const char *whole_devname,
int nr,
* Kernel returns EBUSY if there's already a partition by that number or an overlapping
* partition already existent. */
- r = ioctl_partition_add(fd, node, nr, (uint64_t) start * 512, (uint64_t) size * 512);
+ r = block_device_add_partition(fd, node, nr, (uint64_t) start * 512, (uint64_t) size * 512);
if (r < 0) {
if (r != -EBUSY)
return log_debug_errno(r, "BLKPG_ADD_PARTITION failed: %m");
if (!PARTITION_DESIGNATOR_VERSIONED(designator) ||
strverscmp_improved(m->partitions[designator].label, label) >= 0) {
- r = ioctl_partition_remove(fd, node, nr);
+ r = block_device_remove_partition(fd, node, nr);
if (r < 0)
log_debug_errno(r, "BLKPG_DEL_PARTITION failed, ignoring: %m");
continue;
/* First one wins */
if (m->partitions[PARTITION_XBOOTLDR].found) {
- r = ioctl_partition_remove(fd, node, nr);
+ r = block_device_remove_partition(fd, node, nr);
if (r < 0)
log_debug_errno(r, "BLKPG_DEL_PARTITION failed, ignoring: %m");
continue;
return 0;
}
+int efi_stub_get_features(uint64_t *ret) {
+ _cleanup_free_ void *v = NULL;
+ size_t s;
+ int r;
+
+ assert(ret);
+
+ if (!is_efi_boot()) {
+ *ret = 0;
+ return 0;
+ }
+
+ r = efi_get_variable(EFI_LOADER_VARIABLE(StubFeatures), NULL, &v, &s);
+ if (r == -ENOENT) {
+ _cleanup_free_ char *info = NULL;
+
+ /* The new (v252+) StubFeatures variable is not supported, let's see if it's systemd-stub at all */
+ r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubInfo), &info);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return r;
+
+ /* Variable not set, definitely means not systemd-stub */
+
+ } else if (first_word(info, "systemd-stub")) {
+
+ /* An older systemd-stub version. Let's hardcode the feature set, since it was pretty
+ * static in all its versions. */
+
+ *ret = EFI_STUB_FEATURE_REPORT_BOOT_PARTITION;
+ return 0;
+ }
+
+ /* No features supported */
+ *ret = 0;
+ return 0;
+ }
+ if (r < 0)
+ return r;
+
+ if (s != sizeof(uint64_t))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "StubFeatures EFI variable doesn't have the right size.");
+
+ memcpy(ret, v, sizeof(uint64_t));
+ return 0;
+}
+
int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
_cleanup_free_ char *v = NULL;
static struct stat cache_stat = {};
int efi_loader_get_entries(char ***ret);
int efi_loader_get_features(uint64_t *ret);
+int efi_stub_get_features(uint64_t *ret);
int efi_loader_get_config_timeout_one_shot(usec_t *ret);
int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat);
return -EOPNOTSUPP;
}
+static inline int efi_stub_get_features(uint64_t *ret) {
+ return -EOPNOTSUPP;
+}
+
static inline int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
return -EOPNOTSUPP;
}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
+#include "architecture.h"
#include "env-util.h"
#include "extension-release.h"
#include "log.h"
const char *host_sysext_scope,
char **extension_release) {
- const char *extension_release_id = NULL, *extension_release_sysext_level = NULL;
+ const char *extension_release_id = NULL, *extension_release_sysext_level = NULL, *extension_architecture = NULL;
assert(name);
assert(!isempty(host_os_release_id));
}
}
+ /* When the architecture field is present and not '_any' it must match the host - for now just look at uname but in
+ * the future we could check if the kernel also supports 32 bit or binfmt has a translator set up for the architecture */
+ extension_architecture = strv_env_pairs_get(extension_release, "ARCHITECTURE");
+ if (!isempty(extension_architecture) && !streq(extension_architecture, "_any") &&
+ !streq(architecture_to_string(uname_architecture()), extension_architecture)) {
+ log_debug("Extension '%s' is for architecture '%s', but deployed on top of '%s'.",
+ name, extension_architecture, architecture_to_string(uname_architecture()));
+ return 0;
+ }
+
extension_release_id = strv_env_pairs_get(extension_release, "ID");
if (isempty(extension_release_id)) {
- log_debug("Extension '%s' does not contain ID in extension-release but requested to match '%s'",
+ log_debug("Extension '%s' does not contain ID in extension-release but requested to match '%s' or be '_any'",
name, host_os_release_id);
return 0;
}
+ /* A sysext with no host OS dependency (static binaries or scripts) can match
+ * '_any' host OS, and VERSION_ID or SYSEXT_LEVEL are not required anywhere */
+ if (streq(extension_release_id, "_any")) {
+ log_debug("Extension '%s' matches '_any' OS.", name);
+ return 1;
+ }
+
if (!streq(host_os_release_id, extension_release_id)) {
log_debug("Extension '%s' is for OS '%s', but deployed on top of '%s'.",
name, extension_release_id, host_os_release_id);
"Failed to resolve path %s%s%s: %m",
path,
root ? " under directory " : "",
- root ?: "");
+ strempty(root));
r = verify_esp(p, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags);
if (r < 0)
"Failed to resolve path %s%s%s: %m",
path,
root ? " under directory " : "",
- root ?: "");
+ strempty(root));
if (!path_is_valid(p) || !path_is_absolute(p))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to resolve path %s%s%s: %m",
dir,
root ? " under directory " : "",
- root ?: "");
+ strempty(root));
r = verify_esp(p, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid,
flags | VERIFY_ESP_SEARCHING);
"Failed to resolve path %s%s%s: %m",
path,
root ? " under directory " : "",
- root ?: "");
+ strempty(root));
r = verify_xbootldr(p, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid);
if (r < 0)
"Failed to resolve path %s%s%s: %m",
path,
root ? " under directory " : "",
- root ?: "");
+ strempty(root));
if (!path_is_valid(p) || !path_is_absolute(p))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
return log_error_errno(r,
"Failed to resolve path /boot%s%s: %m",
root ? " under directory " : "",
- root ?: "");
+ strempty(root));
r = verify_xbootldr(p, true, unprivileged_mode, ret_uuid, ret_devid);
if (r >= 0)
#include "time-util.h"
#define NFT_SYSTEMD_DNAT_MAP_NAME "map_port_ipport"
-#define NFT_SYSTEMD_TABLE_NAME "io.systemd.nat"
+#define NFT_SYSTEMD_TABLE_NAME "io.systemd.nat"
#define NFT_SYSTEMD_MASQ_SET_NAME "masq_saddr"
#define NFNL_DEFAULT_TIMEOUT_USECS (1ULL * USEC_PER_SEC)
#define UDP_DPORT_OFFSET 2
-static int nfnl_netlink_sendv(
- sd_netlink *nfnl,
- sd_netlink_message *messages[static 1],
- size_t msgcount) {
+static sd_netlink_message **netlink_message_unref_many(sd_netlink_message **m) {
+ if (!m)
+ return NULL;
+
+ /* This does not free array. The end of the array must be NULL. */
- _cleanup_free_ uint32_t *serial = NULL;
+ for (sd_netlink_message **p = m; *p; p++)
+ *p = sd_netlink_message_unref(*p);
+
+ return m;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(sd_netlink_message**, netlink_message_unref_many);
+
+static int nfnl_open_expr_container(sd_netlink_message *m, const char *name) {
int r;
- assert(nfnl);
- assert(messages);
- assert(msgcount > 0);
+ assert(m);
+ assert(name);
- r = sd_netlink_sendv(nfnl, messages, msgcount, &serial);
+ r = sd_netlink_message_open_array(m, NFTA_LIST_ELEM);
if (r < 0)
return r;
- r = 0;
- for (size_t i = 1; i < msgcount - 1; i++) {
- int tmp;
-
- /* If message is an error, this returns embedded errno */
- tmp = sd_netlink_read(nfnl, serial[i], NFNL_DEFAULT_TIMEOUT_USECS, NULL);
- if (tmp < 0 && r == 0)
- r = tmp;
- }
-
- return r;
+ return sd_netlink_message_open_container_union(m, NFTA_EXPR_DATA, name);
}
-static int nfnl_add_open_expr_container(sd_netlink_message *m, const char *name) {
+static int nfnl_close_expr_container(sd_netlink_message *m) {
int r;
- r = sd_netlink_message_open_array(m, NFTA_LIST_ELEM);
- if (r < 0)
- return r;
+ assert(m);
- r = sd_netlink_message_append_string(m, NFTA_EXPR_NAME, name);
+ r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
if (r < 0)
return r;
- return sd_netlink_message_open_container_union(m, NFTA_EXPR_DATA, name);
+ return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
}
-static int nfnl_add_expr_fib(sd_netlink_message *m, uint32_t nft_fib_flags,
- enum nft_fib_result result,
- enum nft_registers dreg) {
+static int nfnl_add_expr_fib(
+ sd_netlink_message *m,
+ uint32_t nft_fib_flags,
+ enum nft_fib_result result,
+ enum nft_registers dreg) {
+
int r;
- r = nfnl_add_open_expr_container(m, "fib");
+ assert(m);
+
+ r = nfnl_open_expr_container(m, "fib");
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_FIB_FLAGS, htobe32(nft_fib_flags));
if (r < 0)
return r;
+
r = sd_netlink_message_append_u32(m, NFTA_FIB_RESULT, htobe32(result));
- if (r < 0)
- return r;
- r = sd_netlink_message_append_u32(m, NFTA_FIB_DREG, htobe32(dreg));
if (r < 0)
return r;
- r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
+ r = sd_netlink_message_append_u32(m, NFTA_FIB_DREG, htobe32(dreg));
if (r < 0)
return r;
- return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+ return nfnl_close_expr_container(m);
}
-static int nfnl_add_expr_meta(sd_netlink_message *m, enum nft_meta_keys key,
- enum nft_registers dreg) {
+static int nfnl_add_expr_meta(
+ sd_netlink_message *m,
+ enum nft_meta_keys key,
+ enum nft_registers dreg) {
+
int r;
- r = nfnl_add_open_expr_container(m, "meta");
+ assert(m);
+
+ r = nfnl_open_expr_container(m, "meta");
if (r < 0)
return r;
if (r < 0)
return r;
- r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
- if (r < 0)
- return r;
-
- return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+ return nfnl_close_expr_container(m);
}
-static int nfnl_add_expr_payload(sd_netlink_message *m, enum nft_payload_bases pb,
- uint32_t offset, uint32_t len, enum nft_registers dreg) {
+static int nfnl_add_expr_payload(
+ sd_netlink_message *m,
+ enum nft_payload_bases pb,
+ uint32_t offset,
+ uint32_t len,
+ enum nft_registers dreg) {
+
int r;
- r = nfnl_add_open_expr_container(m, "payload");
+ assert(m);
+
+ r = nfnl_open_expr_container(m, "payload");
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_DREG, htobe32(dreg));
if (r < 0)
return r;
+
r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_BASE, htobe32(pb));
if (r < 0)
return r;
+
r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_OFFSET, htobe32(offset));
if (r < 0)
return r;
+
r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_LEN, htobe32(len));
if (r < 0)
return r;
- r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
- if (r < 0)
- return r;
- return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+ return nfnl_close_expr_container(m);
}
-static int nfnl_add_expr_lookup_set_data(sd_netlink_message *m, const char *set_name,
- enum nft_registers sreg) {
- int r;
-
- r = nfnl_add_open_expr_container(m, "lookup");
- if (r < 0)
- return r;
-
- r = sd_netlink_message_append_string(m, NFTA_LOOKUP_SET, set_name);
- if (r < 0)
- return r;
-
- return sd_netlink_message_append_u32(m, NFTA_LOOKUP_SREG, htobe32(sreg));
-}
+static int nfnl_add_expr_lookup(
+ sd_netlink_message *m,
+ const char *set_name,
+ enum nft_registers sreg,
+ enum nft_registers dreg) {
-static int nfnl_add_expr_lookup_set(sd_netlink_message *m, const char *set_name,
- enum nft_registers sreg) {
int r;
- r = nfnl_add_expr_lookup_set_data(m, set_name, sreg);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
- if (r < 0)
- return r;
- return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
-}
-
-static int nfnl_add_expr_lookup_map(sd_netlink_message *m, const char *set_name,
- enum nft_registers sreg, enum nft_registers dreg) {
- int r;
+ assert(m);
+ assert(set_name);
- r = nfnl_add_expr_lookup_set_data(m, set_name, sreg);
+ r = nfnl_open_expr_container(m, "lookup");
if (r < 0)
return r;
- r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_DREG, htobe32(dreg));
+ r = sd_netlink_message_append_string(m, NFTA_LOOKUP_SET, set_name);
if (r < 0)
return r;
- r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
+ r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_SREG, htobe32(sreg));
if (r < 0)
return r;
- return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
-}
-
-static int nfnl_add_expr_data(sd_netlink_message *m, int attr, const void *data, uint32_t dlen) {
- int r;
-
- r = sd_netlink_message_open_container(m, attr);
- if (r < 0)
- return r;
- r = sd_netlink_message_append_data(m, NFTA_DATA_VALUE, data, dlen);
- if (r < 0)
- return r;
+ if (dreg != 0) {
+ r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_DREG, htobe32(dreg));
+ if (r < 0)
+ return r;
+ }
- return sd_netlink_message_close_container(m); /* attr */
+ return nfnl_close_expr_container(m);
}
-static int nfnl_add_expr_cmp_data(sd_netlink_message *m, const void *data, uint32_t dlen) {
- return nfnl_add_expr_data(m, NFTA_CMP_DATA, data, dlen);
-}
+static int nfnl_add_expr_cmp(
+ sd_netlink_message *m,
+ enum nft_cmp_ops cmp_op,
+ enum nft_registers sreg,
+ const void *data,
+ size_t dlen) {
-static int nfnl_add_expr_cmp(sd_netlink_message *m, enum nft_cmp_ops cmp_op,
- enum nft_registers sreg, const void *data, uint32_t dlen) {
int r;
- r = nfnl_add_open_expr_container(m, "cmp");
+ assert(m);
+ assert(data);
+
+ r = nfnl_open_expr_container(m, "cmp");
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_CMP_OP, htobe32(cmp_op));
if (r < 0)
return r;
+
r = sd_netlink_message_append_u32(m, NFTA_CMP_SREG, htobe32(sreg));
if (r < 0)
return r;
- r = nfnl_add_expr_cmp_data(m, data, dlen);
+ r = sd_netlink_message_append_container_data(m, NFTA_CMP_DATA, NFTA_DATA_VALUE, data, dlen);
if (r < 0)
return r;
- r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
- if (r < 0)
- return r;
- return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+ return nfnl_close_expr_container(m);
}
-static int nfnl_add_expr_bitwise(sd_netlink_message *m,
- enum nft_registers sreg,
- enum nft_registers dreg,
- const void *and,
- const void *xor, uint32_t len) {
+static int nfnl_add_expr_bitwise(
+ sd_netlink_message *m,
+ enum nft_registers sreg,
+ enum nft_registers dreg,
+ const void *and,
+ const void *xor,
+ uint32_t len) {
+
int r;
- r = nfnl_add_open_expr_container(m, "bitwise");
+ assert(m);
+ assert(and);
+ assert(xor);
+
+ r = nfnl_open_expr_container(m, "bitwise");
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_BITWISE_SREG, htobe32(sreg));
if (r < 0)
return r;
+
r = sd_netlink_message_append_u32(m, NFTA_BITWISE_DREG, htobe32(dreg));
if (r < 0)
return r;
+
r = sd_netlink_message_append_u32(m, NFTA_BITWISE_LEN, htobe32(len));
if (r < 0)
return r;
- r = nfnl_add_expr_data(m, NFTA_BITWISE_MASK, and, len);
+ r = sd_netlink_message_append_container_data(m, NFTA_BITWISE_MASK, NFTA_DATA_VALUE, and, len);
if (r < 0)
return r;
- r = nfnl_add_expr_data(m, NFTA_BITWISE_XOR, xor, len);
+ r = sd_netlink_message_append_container_data(m, NFTA_BITWISE_XOR, NFTA_DATA_VALUE, xor, len);
if (r < 0)
return r;
- r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
- if (r < 0)
- return r;
- return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
+ return nfnl_close_expr_container(m);
}
-static int nfnl_add_expr_dnat(sd_netlink_message *m,
- int family,
- enum nft_registers areg,
- enum nft_registers preg) {
+static int nfnl_add_expr_dnat(
+ sd_netlink_message *m,
+ int family,
+ enum nft_registers areg,
+ enum nft_registers preg) {
+
int r;
- r = nfnl_add_open_expr_container(m, "nat");
+ assert(m);
+
+ r = nfnl_open_expr_container(m, "nat");
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_NAT_REG_ADDR_MIN, htobe32(areg));
if (r < 0)
return r;
+
r = sd_netlink_message_append_u32(m, NFTA_NAT_REG_PROTO_MIN, htobe32(preg));
- if (r < 0)
- return r;
- r = sd_netlink_message_close_container(m);
if (r < 0)
return r;
- return sd_netlink_message_close_container(m);
+ return nfnl_close_expr_container(m);
}
static int nfnl_add_expr_masq(sd_netlink_message *m) {
return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
}
-static int sd_nfnl_message_new_masq_rule(sd_netlink *nfnl, sd_netlink_message **ret, int family,
- const char *chain) {
+static int sd_nfnl_message_new_masq_rule(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *chain) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
/* -t nat -A POSTROUTING -p protocol -s source/pflen -o out_interface -d destination/pflen -j MASQUERADE */
+ assert(nfnl);
+ assert(ret);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(chain);
+
r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain);
if (r < 0)
return r;
return r;
/* 1st statement: use reg1 content to make lookup in @masq_saddr set. */
- r = nfnl_add_expr_lookup_set(m, NFT_SYSTEMD_MASQ_SET_NAME, NFT_REG32_01);
+ r = nfnl_add_expr_lookup(m, NFT_SYSTEMD_MASQ_SET_NAME, NFT_REG32_01, 0);
if (r < 0)
return r;
r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */
if (r < 0)
return r;
+
*ret = TAKE_PTR(m);
return 0;
}
-static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_message **ret, int family,
- const char *chain) {
+static int sd_nfnl_message_new_dnat_rule_pre(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *chain) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
enum nft_registers proto_reg;
uint32_t local = RTN_LOCAL;
/* -t nat -A PREROUTING -p protocol --dport local_port -i in_interface -s source/pflen
* -d destination/pflen -j DNAT --to-destination remote_addr:remote_port */
+ assert(nfnl);
+ assert(ret);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(chain);
+
r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain);
if (r < 0)
return r;
return r;
/* 3rd statement: lookup 'l4proto . dport', e.g. 'tcp . 22' as key and
- * store address and port for the dnat mapping in REG1/REG2.
- */
- r = nfnl_add_expr_lookup_map(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
+ * store address and port for the dnat mapping in REG1/REG2. */
+ r = nfnl_add_expr_lookup(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
if (r < 0)
return r;
r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */
if (r < 0)
return r;
+
*ret = TAKE_PTR(m);
return 0;
}
-static int sd_nfnl_message_new_dnat_rule_out(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *chain) {
- static const uint32_t zero = 0, one = 1;
+static int sd_nfnl_message_new_dnat_rule_out(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *chain) {
+ static const uint32_t zero = 0, one = 1;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
enum nft_registers proto_reg;
int r;
+ assert(nfnl);
+ assert(ret);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(chain);
+
r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain);
if (r < 0)
return r;
* the new destination ip and port number.
*
* reg1 and reg2 are clobbered and will then contain the new
- * address/port number.
- */
- r = nfnl_add_expr_lookup_map(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
+ * address/port number. */
+ r = nfnl_add_expr_lookup(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
if (r < 0)
return r;
r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */
if (r < 0)
return r;
+
*ret = TAKE_PTR(m);
return 0;
}
-static int nft_new_set(struct sd_netlink *nfnl,
- sd_netlink_message **ret,
- int family, const char *set_name,
- uint32_t set_id,
- uint32_t flags, uint32_t type, uint32_t klen) {
+static int nft_new_set(
+ struct sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *set_name,
+ uint32_t set_id,
+ uint32_t flags,
+ uint32_t type,
+ uint32_t klen) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
+ assert(nfnl);
+ assert(ret);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(set_name);
+
r = sd_nfnl_nft_message_new_set(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name, set_id, klen);
if (r < 0)
return r;
return r;
}
-static int nft_new_map(struct sd_netlink *nfnl,
- sd_netlink_message **ret,
- int family, const char *set_name, uint32_t set_id,
- uint32_t flags, uint32_t type, uint32_t klen, uint32_t dtype, uint32_t dlen) {
+static int nft_new_map(
+ struct sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *set_name,
+ uint32_t set_id,
+ uint32_t flags,
+ uint32_t type,
+ uint32_t klen,
+ uint32_t dtype,
+ uint32_t dlen) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
+ assert(nfnl);
+ assert(ret);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(set_name);
+
r = nft_new_set(nfnl, &m, family, set_name, set_id, flags | NFT_SET_MAP, type, klen);
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, NFTA_SET_DATA_LEN, htobe32(dlen));
if (r < 0)
return r;
+
*ret = TAKE_PTR(m);
return 0;
}
-static int nft_add_element(sd_netlink *nfnl, sd_netlink_message **ret,
- int family, const char *set_name,
- const void *key, uint32_t klen,
- const void *data, uint32_t dlen) {
+static int nft_add_element(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *set_name,
+ const void *key,
+ uint32_t klen,
+ const void *data,
+ uint32_t dlen) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
+ assert(nfnl);
+ assert(ret);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(set_name);
+ assert(key);
+ assert(data);
+
/*
* Ideally there would be an API that provides:
*
* This replicated here and each element gets added to the set
* one-by-one.
*/
- r = sd_nfnl_nft_message_new_setelems_begin(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name);
+ r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ true, family, NFT_SYSTEMD_TABLE_NAME, set_name);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
if (r < 0)
return r;
- r = sd_nfnl_nft_message_add_setelem(m, 0, key, klen, data, dlen);
+ r = sd_nfnl_nft_message_append_setelem(m, 0, key, klen, data, dlen, 0);
if (r < 0)
return r;
/* could theoretically append more set elements to add here */
- r = sd_nfnl_nft_message_add_setelem_end(m);
+
+ r = sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
if (r < 0)
return r;
+
*ret = TAKE_PTR(m);
return 0;
}
-static int nft_del_element(sd_netlink *nfnl,
- sd_netlink_message **ret, int family, const char *set_name,
- const void *key, uint32_t klen,
- const void *data, uint32_t dlen) {
+static int nft_del_element(
+ sd_netlink *nfnl,
+ sd_netlink_message **ret,
+ int family,
+ const char *set_name,
+ const void *key,
+ uint32_t klen,
+ const void *data,
+ uint32_t dlen) {
+
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
- r = sd_nfnl_nft_message_del_setelems_begin(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name);
+ assert(nfnl);
+ assert(ret);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(set_name);
+ assert(key);
+ assert(data);
+
+ r = sd_nfnl_nft_message_new_setelems(nfnl, &m, /* add = */ false, family, NFT_SYSTEMD_TABLE_NAME, set_name);
if (r < 0)
return r;
- r = sd_nfnl_nft_message_add_setelem(m, 0, key, klen, data, dlen);
+ r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
+ if (r < 0)
+ return r;
+
+ r = sd_nfnl_nft_message_append_setelem(m, 0, key, klen, data, dlen, 0);
if (r < 0)
return r;
- r = sd_nfnl_nft_message_add_setelem_end(m);
+ r = sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
if (r < 0)
return r;
+
*ret = TAKE_PTR(m);
return 0;
}
/* This is needed so 'nft' userspace tool can properly format the contents
* of the set/map when someone uses 'nft' to inspect their content.
*
- * The values cannot be changed, they are part of the nft tool type identifier ABI.
- */
+ * The values cannot be changed, they are part of the nft tool type identifier ABI. */
#define TYPE_BITS 6
enum nft_key_types {
- TYPE_IPADDR = 7,
- TYPE_IP6ADDR = 8,
+ TYPE_IPADDR = 7,
+ TYPE_IP6ADDR = 8,
TYPE_INET_PROTOCOL = 12,
- TYPE_INET_SERVICE = 13,
+ TYPE_INET_SERVICE = 13,
};
static uint32_t concat_types2(enum nft_key_types a, enum nft_key_types b) {
return type;
}
-/* enough space to hold netlink messages for table skeleton */
-#define NFT_INIT_MSGS 16
static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
- sd_netlink_message *batch[NFT_INIT_MSGS] = {};
- size_t msgcnt = 0, i, ip_type_size;
+ sd_netlink_message *messages[10] = {};
+ _unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages;
+ size_t msgcnt = 0, ip_type_size;
uint32_t set_id = 0;
int ip_type, r;
+ assert(nfnl);
assert(IN_SET(family, AF_INET, AF_INET6));
- r = sd_nfnl_message_batch_begin(nfnl, &batch[msgcnt]);
- if (r < 0)
- goto out_unref;
-
- msgcnt++;
- assert(msgcnt < NFT_INIT_MSGS);
/* Set F_EXCL so table add fails if the table already exists. */
- r = sd_nfnl_nft_message_new_table(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME);
+ r = sd_nfnl_nft_message_new_table(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME);
if (r < 0)
- goto out_unref;
-
- msgcnt++;
- assert(msgcnt < NFT_INIT_MSGS);
+ return r;
- r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME,
+ r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME,
"prerouting", "nat",
NF_INET_PRE_ROUTING, NF_IP_PRI_NAT_DST + 1);
if (r < 0)
- goto out_unref;
+ return r;
- msgcnt++;
- assert(msgcnt < NFT_INIT_MSGS);
- r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME,
+ r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME,
"output", "nat",
NF_INET_LOCAL_OUT, NF_IP_PRI_NAT_DST + 1);
if (r < 0)
- goto out_unref;
+ return r;
- msgcnt++;
- assert(msgcnt < NFT_INIT_MSGS);
- r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME,
+ r = sd_nfnl_nft_message_new_basechain(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_TABLE_NAME,
"postrouting", "nat",
NF_INET_POST_ROUTING, NF_IP_PRI_NAT_SRC + 1);
if (r < 0)
- goto out_unref;
+ return r;
if (family == AF_INET) {
ip_type_size = sizeof(uint32_t);
ip_type_size = sizeof(struct in6_addr);
ip_type = TYPE_IP6ADDR;
}
- msgcnt++;
- assert(msgcnt < NFT_INIT_MSGS);
/* set to store ip address ranges we should masquerade for */
- r = nft_new_set(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_MASQ_SET_NAME, ++set_id, NFT_SET_INTERVAL, ip_type, ip_type_size);
+ r = nft_new_set(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_MASQ_SET_NAME, ++set_id, NFT_SET_INTERVAL, ip_type, ip_type_size);
if (r < 0)
- goto out_unref;
+ return r;
/*
* map to store ip address:port pair to dnat to. elements in concatenation
* Example: ip protocol . tcp daddr is sizeof(uint32_t) + sizeof(uint32_t), not
* sizeof(uint8_t) + sizeof(uint16_t).
*/
- msgcnt++;
- assert(msgcnt < NFT_INIT_MSGS);
- r = nft_new_map(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_DNAT_MAP_NAME, ++set_id, 0,
+ r = nft_new_map(nfnl, &messages[msgcnt++], family, NFT_SYSTEMD_DNAT_MAP_NAME, ++set_id, 0,
concat_types2(TYPE_INET_PROTOCOL, TYPE_INET_SERVICE), sizeof(uint32_t) * 2,
concat_types2(ip_type, TYPE_INET_SERVICE), ip_type_size + sizeof(uint32_t));
if (r < 0)
- goto out_unref;
-
- msgcnt++;
- assert(msgcnt < NFT_INIT_MSGS);
- r = sd_nfnl_message_new_dnat_rule_pre(nfnl, &batch[msgcnt], family, "prerouting");
- if (r < 0)
- goto out_unref;
+ return r;
- msgcnt++;
- assert(msgcnt < NFT_INIT_MSGS);
- r = sd_nfnl_message_new_dnat_rule_out(nfnl, &batch[msgcnt], family, "output");
+ r = sd_nfnl_message_new_dnat_rule_pre(nfnl, &messages[msgcnt++], family, "prerouting");
if (r < 0)
- goto out_unref;
+ return r;
- msgcnt++;
- r = sd_nfnl_message_new_masq_rule(nfnl, &batch[msgcnt], family, "postrouting");
+ r = sd_nfnl_message_new_dnat_rule_out(nfnl, &messages[msgcnt++], family, "output");
if (r < 0)
- goto out_unref;
+ return r;
- msgcnt++;
- assert(msgcnt < NFT_INIT_MSGS);
- r = sd_nfnl_message_batch_end(nfnl, &batch[msgcnt]);
+ r = sd_nfnl_message_new_masq_rule(nfnl, &messages[msgcnt++], family, "postrouting");
if (r < 0)
- goto out_unref;
-
- msgcnt++;
- assert(msgcnt <= NFT_INIT_MSGS);
- r = nfnl_netlink_sendv(nfnl, batch, msgcnt);
- if (r == -EEXIST)
- r = 0;
+ return r;
-out_unref:
- for (i = 0; i < msgcnt; i++)
- sd_netlink_message_unref(batch[i]);
+ assert(msgcnt < ELEMENTSOF(messages));
+ r = sd_nfnl_call_batch(nfnl, messages, msgcnt, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
+ if (r < 0 && r != -EEXIST)
+ return r;
- return r;
+ return 0;
}
int fw_nftables_init(FirewallContext *ctx) {
_cleanup_(sd_netlink_unrefp) sd_netlink *nfnl = NULL;
int r;
+ assert(ctx);
+ assert(!ctx->nfnl);
+
r = sd_nfnl_socket_open(&nfnl);
if (r < 0)
return r;
}
void fw_nftables_exit(FirewallContext *ctx) {
+ assert(ctx);
+
ctx->nfnl = sd_netlink_unref(ctx->nfnl);
}
-static int nft_message_add_setelem_iprange(sd_netlink_message *m,
- const union in_addr_union *source,
- unsigned int prefixlen) {
+static int nft_message_append_setelem_iprange(
+ sd_netlink_message *m,
+ const union in_addr_union *source,
+ unsigned int prefixlen) {
+
uint32_t mask, start, end;
unsigned int nplen;
int r;
+ assert(m);
+ assert(source);
assert(prefixlen <= 32);
+
nplen = 32 - prefixlen;
mask = (1U << nplen) - 1U;
mask = htobe32(~mask);
start = source->in.s_addr & mask;
- r = sd_nfnl_nft_message_add_setelem(m, 0, &start, sizeof(start), NULL, 0);
+ r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
if (r < 0)
return r;
- r = sd_nfnl_nft_message_add_setelem_end(m);
+ r = sd_nfnl_nft_message_append_setelem(m, 0, &start, sizeof(start), NULL, 0, 0);
if (r < 0)
return r;
end = 0U;
end = htobe32(end);
- r = sd_nfnl_nft_message_add_setelem(m, 1, &end, sizeof(end), NULL, 0);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(NFT_SET_ELEM_INTERVAL_END));
+ r = sd_nfnl_nft_message_append_setelem(m, 1, &end, sizeof(end), NULL, 0, NFT_SET_ELEM_INTERVAL_END);
if (r < 0)
return r;
- r = sd_nfnl_nft_message_add_setelem_end(m);
- if (r < 0)
- return r;
-
- return 0;
+ return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
}
-static int nft_message_add_setelem_ip6range(
+static int nft_message_append_setelem_ip6range(
sd_netlink_message *m,
const union in_addr_union *source,
unsigned int prefixlen) {
union in_addr_union start, end;
int r;
- r = in_addr_prefix_range(AF_INET6, source, prefixlen, &start, &end);
- if (r < 0)
- return r;
+ assert(m);
+ assert(source);
- r = sd_nfnl_nft_message_add_setelem(m, 0, &start.in6, sizeof(start.in6), NULL, 0);
+ r = in_addr_prefix_range(AF_INET6, source, prefixlen, &start, &end);
if (r < 0)
return r;
- r = sd_nfnl_nft_message_add_setelem_end(m);
+ r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
if (r < 0)
return r;
- r = sd_nfnl_nft_message_add_setelem(m, 1, &end.in6, sizeof(end.in6), NULL, 0);
+ r = sd_nfnl_nft_message_append_setelem(m, 0, &start.in6, sizeof(start.in6), NULL, 0, 0);
if (r < 0)
return r;
- r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(NFT_SET_ELEM_INTERVAL_END));
+ r = sd_nfnl_nft_message_append_setelem(m, 1, &end.in6, sizeof(end.in6), NULL, 0, NFT_SET_ELEM_INTERVAL_END);
if (r < 0)
return r;
- return sd_nfnl_nft_message_add_setelem_end(m);
+ return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
}
-#define NFT_MASQ_MSGS 3
-
static int fw_nftables_add_masquerade_internal(
- FirewallContext *ctx,
+ sd_netlink *nfnl,
bool add,
int af,
const union in_addr_union *source,
unsigned int source_prefixlen) {
- sd_netlink_message *transaction[NFT_MASQ_MSGS] = {};
- size_t tsize;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
+ assert(nfnl);
+ assert(IN_SET(af, AF_INET, AF_INET6));
+
if (!source || source_prefixlen == 0)
return -EINVAL;
if (af == AF_INET6 && source_prefixlen < 8)
return -EINVAL;
- r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]);
+ r = sd_nfnl_nft_message_new_setelems(nfnl, &m, add, af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME);
if (r < 0)
return r;
- tsize = 1;
- if (add)
- r = sd_nfnl_nft_message_new_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME);
- else
- r = sd_nfnl_nft_message_del_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME);
- if (r < 0)
- goto out_unref;
if (af == AF_INET)
- r = nft_message_add_setelem_iprange(transaction[tsize], source, source_prefixlen);
+ r = nft_message_append_setelem_iprange(m, source, source_prefixlen);
else
- r = nft_message_add_setelem_ip6range(transaction[tsize], source, source_prefixlen);
- if (r < 0)
- goto out_unref;
-
- ++tsize;
- assert(tsize < NFT_MASQ_MSGS);
- r = sd_nfnl_message_batch_end(ctx->nfnl, &transaction[tsize]);
+ r = nft_message_append_setelem_ip6range(m, source, source_prefixlen);
if (r < 0)
return r;
- ++tsize;
- r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize);
-
-out_unref:
- while (tsize > 0)
- sd_netlink_message_unref(transaction[--tsize]);
- return r < 0 ? r : 0;
+ return sd_nfnl_call_batch(nfnl, &m, 1, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
}
int fw_nftables_add_masquerade(
int r;
+ assert(ctx);
+ assert(ctx->nfnl);
+ assert(IN_SET(af, AF_INET, AF_INET6));
+
if (!socket_ipv6_is_supported() && af == AF_INET6)
return -EOPNOTSUPP;
- r = fw_nftables_add_masquerade_internal(ctx, add, af, source, source_prefixlen);
+ r = fw_nftables_add_masquerade_internal(ctx->nfnl, add, af, source, source_prefixlen);
if (r != -ENOENT)
return r;
if (r < 0)
return r;
- return fw_nftables_add_masquerade_internal(ctx, add, af, source, source_prefixlen);
+ return fw_nftables_add_masquerade_internal(ctx->nfnl, add, af, source, source_prefixlen);
}
-#define NFT_DNAT_MSGS 4
-
static int fw_nftables_add_local_dnat_internal(
- FirewallContext *ctx,
+ sd_netlink *nfnl,
bool add,
int af,
int protocol,
uint16_t remote_port,
const union in_addr_union *previous_remote) {
- sd_netlink_message *transaction[NFT_DNAT_MSGS] = {};
+ sd_netlink_message *messages[3] = {};
+ _unused_ _cleanup_(netlink_message_unref_manyp) sd_netlink_message **unref = messages;
static bool ipv6_supported = true;
uint32_t data[5], key[2], dlen;
- size_t tsize;
+ size_t msgcnt = 0;
int r;
+ assert(nfnl);
assert(add || !previous_remote);
+ assert(IN_SET(af, AF_INET, AF_INET6));
if (!ipv6_supported && af == AF_INET6)
return -EOPNOTSUPP;
data[4] = htobe16(remote_port);
}
- r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]);
- if (r < 0)
- return r;
-
- tsize = 1;
/* If a previous remote is set, remove its entry */
if (add && previous_remote && !in_addr_equal(af, previous_remote, remote)) {
if (af == AF_INET)
else
memcpy(data, &previous_remote->in6, sizeof(previous_remote->in6));
- r = nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
+ r = nft_del_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
if (r < 0)
- goto out_unref;
-
- tsize++;
+ return r;
}
if (af == AF_INET)
else
memcpy(data, &remote->in6, sizeof(remote->in6));
- assert(tsize < NFT_DNAT_MSGS);
if (add)
- r = nft_add_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
+ r = nft_add_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
else
- r = nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
+ r = nft_del_element(nfnl, &messages[msgcnt++], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, dlen);
if (r < 0)
- goto out_unref;
-
- tsize++;
- assert(tsize < NFT_DNAT_MSGS);
-
- r = sd_nfnl_message_batch_end(ctx->nfnl, &transaction[tsize]);
- if (r < 0)
- goto out_unref;
-
- tsize++;
- assert(tsize <= NFT_DNAT_MSGS);
+ return r;
- r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize);
+ assert(msgcnt < ELEMENTSOF(messages));
+ r = sd_nfnl_call_batch(nfnl, messages, msgcnt, NFNL_DEFAULT_TIMEOUT_USECS, NULL);
if (r == -EOVERFLOW && af == AF_INET6) {
/* The current implementation of DNAT in systemd requires kernel's
* fdb9c405e35bdc6e305b9b4e20ebc141ed14fc81 (v5.8), and the older kernel returns
* -EOVERFLOW. Let's treat the error as -EOPNOTSUPP. */
log_debug_errno(r, "The current implementation of IPv6 DNAT in systemd requires kernel 5.8 or newer, ignoring: %m");
ipv6_supported = false;
- r = -EOPNOTSUPP;
+ return -EOPNOTSUPP;
}
+ if (r < 0)
+ return r;
-out_unref:
- while (tsize > 0)
- sd_netlink_message_unref(transaction[--tsize]);
-
- return r < 0 ? r : 0;
+ return 0;
}
int fw_nftables_add_local_dnat(
int r;
+ assert(ctx);
+ assert(ctx->nfnl);
+ assert(IN_SET(af, AF_INET, AF_INET6));
+
if (!socket_ipv6_is_supported() && af == AF_INET6)
return -EOPNOTSUPP;
- r = fw_nftables_add_local_dnat_internal(ctx, add, af, protocol, local_port, remote, remote_port, previous_remote);
+ r = fw_nftables_add_local_dnat_internal(ctx->nfnl, add, af, protocol, local_port, remote, remote_port, previous_remote);
if (r != -ENOENT)
return r;
return r;
/* table created anew; previous address already gone */
- return fw_nftables_add_local_dnat_internal(ctx, add, af, protocol, local_port, remote, remote_port, NULL);
+ return fw_nftables_add_local_dnat_internal(ctx->nfnl, add, af, protocol, local_port, remote, remote_port, NULL);
}
"DefaultDependencies=no\n"
"BindsTo=%%i.mount\n"
"Conflicts=shutdown.target\n"
- "After=%%i.mount\n"
+ "After=systemd-repart.service %%i.mount\n"
"Before=shutdown.target%s%s\n",
program_invocation_short_name,
target ? " " : "",
return 0;
}
-bool hwdb_validate(sd_hwdb *hwdb) {
+bool hwdb_should_reload(sd_hwdb *hwdb) {
bool found = false;
const char* p;
struct stat st;
#include "sd-hwdb.h"
-bool hwdb_validate(sd_hwdb *hwdb);
+bool hwdb_should_reload(sd_hwdb *hwdb);
int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat);
int hwdb_query(const char *modalias, const char *root);
#include <errno.h>
#include <locale.h>
-#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/types.h>
#include "json-internal.h"
#include "json.h"
#include "macro.h"
+#include "math-util.h"
#include "memory-util.h"
#include "string-table.h"
#include "string-util.h"
return json_variant_unsigned(v) == 0 ? JSON_VARIANT_MAGIC_ZERO_UNSIGNED : v;
case JSON_VARIANT_REAL:
- DISABLE_WARNING_FLOAT_EQUAL;
- return json_variant_real(v) == 0.0 ? JSON_VARIANT_MAGIC_ZERO_REAL : v;
- REENABLE_WARNING;
+ return iszero_safe(json_variant_real(v)) ? JSON_VARIANT_MAGIC_ZERO_REAL : v;
case JSON_VARIANT_STRING:
return isempty(json_variant_string(v)) ? JSON_VARIANT_MAGIC_EMPTY_STRING : v;
assert_return(ret, -EINVAL);
- DISABLE_WARNING_FLOAT_EQUAL;
- if (d == 0.0) {
- *ret = JSON_VARIANT_MAGIC_ZERO_REAL;
+ r = fpclassify(d);
+ switch (r) {
+ case FP_NAN:
+ case FP_INFINITE:
+ /* JSON doesn't know NaN, +Infinity or -Infinity. Let's silently convert to 'null'. */
+ *ret = JSON_VARIANT_MAGIC_NULL;
return 0;
- }
- REENABLE_WARNING;
- /* JSON doesn't know NaN, +Infinity or -Infinity. Let's silently convert to 'null'. */
- if (isnan(d) || isinf(d)) {
- *ret = JSON_VARIANT_MAGIC_NULL;
+ case FP_ZERO:
+ *ret = JSON_VARIANT_MAGIC_ZERO_REAL;
return 0;
}
converted = (int64_t) v->value.real;
- DISABLE_WARNING_FLOAT_EQUAL;
- if ((double) converted == v->value.real)
+ if (fp_equal((double) converted, v->value.real))
return converted;
- REENABLE_WARNING;
log_debug("Real %g requested as integer, and cannot be converted losslessly, returning 0.", v->value.real);
return 0;
converted = (uint64_t) v->value.real;
- DISABLE_WARNING_FLOAT_EQUAL;
- if ((double) converted == v->value.real)
+ if (fp_equal((double) converted, v->value.real))
return converted;
- REENABLE_WARNING;
log_debug("Real %g requested as unsigned integer, and cannot be converted losslessly, returning 0.", v->value.real);
return 0;
if (rt == JSON_VARIANT_UNSIGNED && type == JSON_VARIANT_REAL)
return (uint64_t) (double) v->value.unsig == v->value.unsig;
- DISABLE_WARNING_FLOAT_EQUAL;
-
/* Any real that can be converted losslessly to an integer and back may also be considered an integer */
if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_INTEGER)
- return (double) (int64_t) v->value.real == v->value.real;
+ return fp_equal((double) (int64_t) v->value.real, v->value.real);
if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_UNSIGNED)
- return (double) (uint64_t) v->value.real == v->value.real;
-
- REENABLE_WARNING;
+ return fp_equal((double) (uint64_t) v->value.real, v->value.real);
return false;
}
return json_variant_unsigned(a) == json_variant_unsigned(b);
case JSON_VARIANT_REAL:
- DISABLE_WARNING_FLOAT_EQUAL;
- return json_variant_real(a) == json_variant_real(b);
- REENABLE_WARNING;
+ return fp_equal(json_variant_real(a), json_variant_real(b));
case JSON_VARIANT_BOOLEAN:
return json_variant_boolean(a) == json_variant_boolean(b);
d->relinquished = true;
}
+void loop_device_unrelinquish(LoopDevice *d) {
+ assert(d);
+ d->relinquished = false;
+}
+
int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret) {
_cleanup_close_ int loop_fd = -1;
_cleanup_free_ char *p = NULL;
DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref);
void loop_device_relinquish(LoopDevice *d);
+void loop_device_unrelinquish(LoopDevice *d);
int loop_device_refresh_size(LoopDevice *d, uint64_t offset, uint64_t size);
'creds-util.h',
'cryptsetup-util.c',
'cryptsetup-util.h',
+ 'daemon-util.c',
'daemon-util.h',
'data-fd-util.c',
'data-fd-util.h',
#include <sys/statvfs.h>
#include <unistd.h>
#include <linux/loop.h>
+#if WANT_LINUX_FS_H
#include <linux/fs.h>
+#endif
#include "alloc-util.h"
#include "chase-symlinks.h"
{ "v249", NAMING_V249 },
{ "v250", NAMING_V250 },
{ "v251", NAMING_V251 },
+ { "v252", NAMING_V252 },
/* … add more schemes here, as the logic to name devices is updated … */
EXTRA_NET_NAMING_MAP
NAMING_REPLACE_STRICTLY = 1 << 12, /* Use udev_replace_ifname() for NAME= rule */
NAMING_XEN_VIF = 1 << 13, /* Generate names for Xen netfront devices */
NAMING_BRIDGE_MULTIFUNCTION_SLOT = 1 << 14, /* Use PCI hotplug slot information associated with bridge, but only if PCI device is multifunction */
+ NAMING_DEVICETREE_ALIASES = 1 << 15, /* Generate names from devicetree aliases */
/* And now the masks that combine the features above */
NAMING_V238 = 0,
NAMING_V249 = NAMING_V247 | NAMING_SLOT_FUNCTION_ID | NAMING_16BIT_INDEX | NAMING_REPLACE_STRICTLY,
NAMING_V250 = NAMING_V249 | NAMING_XEN_VIF,
NAMING_V251 = NAMING_V250 | NAMING_BRIDGE_MULTIFUNCTION_SLOT,
+ NAMING_V252 = NAMING_V251 | NAMING_DEVICETREE_ALIASES,
EXTRA_NET_NAMING_SCHEMES
int (*sym_pcre2_get_error_message)(int, PCRE2_UCHAR *, PCRE2_SIZE);
int (*sym_pcre2_match)(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, pcre2_match_data *, pcre2_match_context *);
PCRE2_SIZE* (*sym_pcre2_get_ovector_pointer)(pcre2_match_data *);
+#endif
int dlopen_pcre2(void) {
+#if HAVE_PCRE2
/* So here's something weird: PCRE2 actually renames the symbols exported by the library via C
* macros, so that the exported symbols carry a suffix "_8" but when used from C the suffix is
* gone. In the argument list below we ignore this mangling. Surprisingly (at least to me), we
DLSYM_ARG(pcre2_get_error_message),
DLSYM_ARG(pcre2_match),
DLSYM_ARG(pcre2_get_ovector_pointer));
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
+#endif
}
+int pattern_compile_and_log(const char *pattern, PatternCompileCase case_, pcre2_code **ret) {
+#if HAVE_PCRE2
+ PCRE2_SIZE erroroffset;
+ pcre2_code *p;
+ unsigned flags = 0;
+ int errorcode, r;
+
+ assert(pattern);
+
+ r = dlopen_pcre2();
+ if (r < 0)
+ return r;
+
+ if (case_ == PATTERN_COMPILE_CASE_INSENSITIVE)
+ flags = PCRE2_CASELESS;
+ else if (case_ == PATTERN_COMPILE_CASE_AUTO) {
+ _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
+ bool has_case;
+ _cleanup_(sym_pcre2_code_freep) pcre2_code *cs = NULL;
+
+ md = sym_pcre2_match_data_create(1, NULL);
+ if (!md)
+ return log_oom();
+
+ r = pattern_compile_and_log("[[:upper:]]", PATTERN_COMPILE_CASE_SENSITIVE, &cs);
+ if (r < 0)
+ return r;
+
+ r = sym_pcre2_match(cs, (PCRE2_SPTR8) pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
+ has_case = r >= 0;
+
+ flags = !has_case * PCRE2_CASELESS;
+ }
+
+ log_debug("Doing case %s matching based on %s",
+ flags & PCRE2_CASELESS ? "insensitive" : "sensitive",
+ case_ != PATTERN_COMPILE_CASE_AUTO ? "request" : "pattern casing");
+
+ p = sym_pcre2_compile((PCRE2_SPTR8) pattern,
+ PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
+ if (!p) {
+ unsigned char buf[LINE_MAX];
+
+ r = sym_pcre2_get_error_message(errorcode, buf, sizeof buf);
+
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Bad pattern \"%s\": %s", pattern,
+ r < 0 ? "unknown error" : (char *)buf);
+ }
+
+ if (ret)
+ *ret = p;
+
+ return 0;
#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
+#endif
+}
-int dlopen_pcre2(void) {
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
- "PCRE2 support is not compiled in.");
+int pattern_matches_and_log(pcre2_code *compiled_pattern, const char *message, size_t size, size_t *ret_ovec) {
+#if HAVE_PCRE2
+ _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
+ int r;
+
+ assert(compiled_pattern);
+ assert(message);
+ /* pattern_compile_and_log() must be called before this function is called and that function already
+ * dlopens pcre2 so we can assert on it being available here. */
+ assert(pcre2_dl);
+
+ md = sym_pcre2_match_data_create(1, NULL);
+ if (!md)
+ return log_oom();
+
+ r = sym_pcre2_match(compiled_pattern,
+ (const unsigned char *)message,
+ size,
+ 0, /* start at offset 0 in the subject */
+ 0, /* default options */
+ md,
+ NULL);
+ if (r == PCRE2_ERROR_NOMATCH)
+ return false;
+ if (r < 0) {
+ unsigned char buf[LINE_MAX];
+
+ r = sym_pcre2_get_error_message(r, buf, sizeof(buf));
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Pattern matching failed: %s",
+ r < 0 ? "unknown error" : (char*) buf);
+ }
+
+ if (ret_ovec) {
+ ret_ovec[0] = sym_pcre2_get_ovector_pointer(md)[0];
+ ret_ovec[1] = sym_pcre2_get_ovector_pointer(md)[1];
+ }
+
+ return true;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PCRE2 support is not compiled in.");
+#endif
}
+
+void *pattern_free(pcre2_code *p) {
+#if HAVE_PCRE2
+ if (!p)
+ return NULL;
+
+ assert(pcre2_dl);
+ sym_pcre2_code_free(p);
+ return NULL;
+#else
+ assert(p == NULL);
+ return NULL;
#endif
+}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(pcre2_match_data*, sym_pcre2_match_data_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(pcre2_code*, sym_pcre2_code_free, NULL);
+#else
+
+typedef struct {} pcre2_code;
+
#endif
+typedef enum {
+ PATTERN_COMPILE_CASE_AUTO,
+ PATTERN_COMPILE_CASE_SENSITIVE,
+ PATTERN_COMPILE_CASE_INSENSITIVE,
+ _PATTERN_COMPILE_CASE_MAX,
+ _PATTERN_COMPILE_CASE_INVALID = -EINVAL,
+} PatternCompileCase;
+
+int pattern_compile_and_log(const char *pattern, PatternCompileCase case_, pcre2_code **ret);
+int pattern_matches_and_log(pcre2_code *compiled_pattern, const char *message, size_t size, size_t *ret_ovec);
+void *pattern_free(pcre2_code *p);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pattern_free);
+
int dlopen_pcre2(void);
#include <syslog.h>
#include <unistd.h>
+#include "sd-device.h"
+
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
#include "conf-parser.h"
#include "def.h"
+#include "device-util.h"
#include "devnum-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "hexdecoct.h"
+#include "id128-util.h"
#include "log.h"
#include "macro.h"
#include "path-util.h"
#include "sleep-config.h"
+#include "siphash24.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "strv.h"
#include "time-util.h"
+#define BATTERY_LOW_CAPACITY_LEVEL 5
+#define DISCHARGE_RATE_FILEPATH "/var/lib/systemd/sleep/battery_discharge_percentage_rate_per_hour"
+#define BATTERY_DISCHARGE_RATE_HASH_KEY SD_ID128_MAKE(5f,9a,20,18,38,76,46,07,8d,36,58,0b,bb,c4,e0,63)
+
+static void *CAPACITY_TO_PTR(int capacity) {
+ assert(capacity >= 0);
+ assert(capacity <= 100);
+ return INT_TO_PTR(capacity + 1);
+}
+
+static int PTR_TO_CAPACITY(void *p) {
+ int capacity = PTR_TO_INT(p) - 1;
+ assert(capacity >= 0);
+ assert(capacity <= 100);
+ return capacity;
+}
+
int parse_sleep_config(SleepConfig **ret_sleep_config) {
_cleanup_(free_sleep_configp) SleepConfig *sc = NULL;
int allow_suspend = -1, allow_hibernate = -1,
return 0;
}
+/* Get the list of batteries */
+static int battery_enumerator_new(sd_device_enumerator **ret) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ int r;
+
+ assert(ret);
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_subsystem(e, "power_supply", /* match= */ true);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_property(e, "POWER_SUPPLY_TYPE", "Battery");
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(e);
+
+ return 0;
+}
+
+static int get_capacity_by_name(Hashmap *capacities_by_name, const char *name) {
+ void *p;
+
+ assert(capacities_by_name);
+ assert(name);
+
+ p = hashmap_get(capacities_by_name, name);
+ if (!p)
+ return -ENOENT;
+
+ return PTR_TO_CAPACITY(p);
+}
+
+/* Battery percentage capacity fetched from capacity file and if in range 0-100 then returned */
+static int read_battery_capacity_percentage(sd_device *dev) {
+ const char *power_supply_capacity;
+ int battery_capacity, r;
+
+ assert(dev);
+
+ r = sd_device_get_property_value(dev, "POWER_SUPPLY_CAPACITY", &power_supply_capacity);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to read battery capacity: %m");
+
+ r = safe_atoi(power_supply_capacity, &battery_capacity);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to parse battery capacity: %m");
+
+ if (battery_capacity < 0 || battery_capacity > 100)
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ERANGE), "Invalid battery capacity");
+
+ return battery_capacity;
+}
+
+/* If battery percentage capacity is less than equal to 5% return success */
+int battery_is_low(void) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
+ int r;
+
+ /* We have not used battery capacity_level since value is set to full
+ * or Normal in case acpi is not working properly. In case of no battery
+ * 0 will be returned and system will be suspended for 1st cycle then hibernated */
+
+ r = battery_enumerator_new(&e);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+
+ FOREACH_DEVICE(e, dev) {
+ r = read_battery_capacity_percentage(dev);
+ if (r < 0) {
+ log_device_debug_errno(dev, r, "Failed to get battery capacity, ignoring: %m");
+ continue;
+ }
+ if (r > BATTERY_LOW_CAPACITY_LEVEL)
+ return false;
+ }
+
+ return true;
+}
+
+/* Store current capacity of each battery before suspension and timestamp */
+int fetch_batteries_capacity_by_name(Hashmap **ret) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ _cleanup_(hashmap_freep) Hashmap *batteries_capacity_by_name = NULL;
+ sd_device *dev;
+ int r;
+
+ assert(ret);
+
+ batteries_capacity_by_name = hashmap_new(&string_hash_ops_free);
+ if (!batteries_capacity_by_name)
+ return log_oom_debug();
+
+ r = battery_enumerator_new(&e);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+
+ FOREACH_DEVICE(e, dev) {
+ _cleanup_free_ char *battery_name_copy = NULL;
+ const char *battery_name;
+ int battery_capacity;
+
+ battery_capacity = r = read_battery_capacity_percentage(dev);
+ if (r < 0) {
+ log_device_debug_errno(dev, r, "Failed to get battery capacity, ignoring: %m");
+ continue;
+ }
+
+ r = sd_device_get_property_value(dev, "POWER_SUPPLY_NAME", &battery_name);
+ if (r < 0) {
+ log_device_debug_errno(dev, r, "Failed to read battery name, ignoring: %m");
+ continue;
+ }
+
+ battery_name_copy = strdup(battery_name);
+ if (!battery_name_copy)
+ return log_oom_debug();
+
+ r = hashmap_put(batteries_capacity_by_name, battery_name_copy, CAPACITY_TO_PTR(battery_capacity));
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to store battery capacity: %m");
+
+ TAKE_PTR(battery_name_copy);
+ }
+
+ *ret = TAKE_PTR(batteries_capacity_by_name);
+
+ return 0;
+}
+
+/* Read file path and return hash of value in that file */
+static int get_battery_identifier(sd_device *dev, const char *property, struct siphash *state) {
+ const char *x;
+ int r;
+
+ assert(dev);
+ assert(property);
+ assert(state);
+
+ r = sd_device_get_property_value(dev, property, &x);
+ if (r == -ENOENT)
+ log_device_debug_errno(dev, r, "battery device property %s is unavailable, ignoring: %m", property);
+ else if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to read battery device property %s: %m", property);
+ else if (isempty(x))
+ log_device_debug(dev, "battery device property '%s' is null.", property);
+ else
+ siphash24_compress_string(x, state);
+
+ return 0;
+}
+
+/* Read system and battery identifier from specific location and generate hash of it */
+static int get_system_battery_identifier_hash(sd_device *dev, uint64_t *ret) {
+ struct siphash state;
+ sd_id128_t machine_id, product_id;
+ int r;
+
+ assert(ret);
+ assert(dev);
+
+ siphash24_init(&state, BATTERY_DISCHARGE_RATE_HASH_KEY.bytes);
+
+ get_battery_identifier(dev, "POWER_SUPPLY_MANUFACTURER", &state);
+ get_battery_identifier(dev, "POWER_SUPPLY_MODEL_NAME", &state);
+ get_battery_identifier(dev, "POWER_SUPPLY_SERIAL_NUMBER", &state);
+
+ r = sd_id128_get_machine(&machine_id);
+ if (r == -ENOENT)
+ log_debug_errno(r, "machine ID is unavailable: %m");
+ else if (r < 0)
+ return log_debug_errno(r, "Failed to get machine ID: %m");
+ else
+ siphash24_compress(&machine_id, sizeof(sd_id128_t), &state);
+
+ r = id128_get_product(&product_id);
+ if (r == -ENOENT)
+ log_debug_errno(r, "product_id does not exist: %m");
+ else if (r < 0)
+ return log_debug_errno(r, "Failed to get product ID: %m");
+ else
+ siphash24_compress(&product_id, sizeof(sd_id128_t), &state);
+
+ *ret = siphash24_finalize(&state);
+
+ return 0;
+}
+
+/* battery percentage discharge rate per hour is in range 1-199 then return success */
+static bool battery_discharge_rate_is_valid(int battery_discharge_rate) {
+ return battery_discharge_rate > 0 && battery_discharge_rate < 200;
+}
+
+/* Battery percentage discharge rate per hour is read from specific file. It is stored along with system
+ * and battery identifier hash to maintain the integrity of discharge rate value */
+static int get_battery_discharge_rate(sd_device *dev, int *ret) {
+ _cleanup_fclose_ FILE *f = NULL;
+ uint64_t current_hash_id;
+ const char *p;
+ int r;
+
+ assert(dev);
+ assert(ret);
+
+ f = fopen(DISCHARGE_RATE_FILEPATH, "re");
+ if (!f)
+ return log_debug_errno(errno, "Failed to read discharge rate from " DISCHARGE_RATE_FILEPATH ": %m");
+
+ r = get_system_battery_identifier_hash(dev, ¤t_hash_id);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to generate system battery identifier hash: %m");
+
+ for (;;) {
+ _cleanup_free_ char *stored_hash_id = NULL, *stored_discharge_rate = NULL, *line = NULL;
+ uint64_t hash_id;
+ int discharge_rate;
+
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read discharge rate from " DISCHARGE_RATE_FILEPATH ": %m");
+ if (r == 0)
+ break;
+
+ p = line;
+ r = extract_many_words(&p, NULL, 0, &stored_hash_id, &stored_discharge_rate, NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse hash_id and discharge_rate read from " DISCHARGE_RATE_FILEPATH ": %m");
+ if (r != 2)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid number of items fetched from " DISCHARGE_RATE_FILEPATH);
+
+ r = safe_atou64(stored_hash_id, &hash_id);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse hash ID read from " DISCHARGE_RATE_FILEPATH " location: %m");
+
+ if (current_hash_id != hash_id)
+ /* matching device not found, move to next line */
+ continue;
+
+ r = safe_atoi(stored_discharge_rate, &discharge_rate);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to parse discharge rate read from " DISCHARGE_RATE_FILEPATH ": %m");
+
+ if (!battery_discharge_rate_is_valid(discharge_rate))
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ERANGE), "Invalid battery discharge percentage rate per hour: %m");
+
+ *ret = discharge_rate;
+ return 0; /* matching device found, exit iteration */
+ }
+
+ return -ENOENT;
+}
+
+/* Write battery percentage discharge rate per hour along with system and battery identifier hash to file */
+static int put_battery_discharge_rate(int estimated_battery_discharge_rate, uint64_t system_hash_id, bool trunc) {
+ int r;
+
+ if (!battery_discharge_rate_is_valid(estimated_battery_discharge_rate))
+ return log_debug_errno(SYNTHETIC_ERRNO(ERANGE),
+ "Invalid battery discharge rate %d%% per hour: %m",
+ estimated_battery_discharge_rate);
+
+ r = write_string_filef(
+ DISCHARGE_RATE_FILEPATH,
+ WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_MKDIR_0755 | (trunc ? WRITE_STRING_FILE_TRUNCATE : 0),
+ "%"PRIu64" %d",
+ system_hash_id,
+ estimated_battery_discharge_rate);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to update %s: %m", DISCHARGE_RATE_FILEPATH);
+
+ log_debug("Estimated discharge rate %d%% per hour successfully saved to %s", estimated_battery_discharge_rate, DISCHARGE_RATE_FILEPATH);
+
+ return 0;
+}
+
+/* Estimate battery discharge rate using stored previous and current capacity over timestamp difference */
+int estimate_battery_discharge_rate_per_hour(
+ Hashmap *last_capacity,
+ Hashmap *current_capacity,
+ usec_t before_timestamp,
+ usec_t after_timestamp) {
+
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
+ bool trunc = true;
+ int r;
+
+ assert(last_capacity);
+ assert(current_capacity);
+ assert(before_timestamp < after_timestamp);
+
+ r = battery_enumerator_new(&e);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+
+ FOREACH_DEVICE(e, dev) {
+ int battery_last_capacity, battery_current_capacity, battery_discharge_rate;
+ const char *battery_name;
+ uint64_t system_hash_id;
+
+ r = sd_device_get_property_value(dev, "POWER_SUPPLY_NAME", &battery_name);
+ if (r < 0) {
+ log_device_debug_errno(dev, r, "Failed to read battery name, ignoring: %m");
+ continue;
+ }
+
+ battery_last_capacity = get_capacity_by_name(last_capacity, battery_name);
+ if (battery_last_capacity < 0)
+ continue;
+
+ battery_current_capacity = get_capacity_by_name(current_capacity, battery_name);
+ if (battery_current_capacity < 0)
+ continue;
+
+ if (battery_current_capacity >= battery_last_capacity) {
+ log_device_debug(dev, "Battery was not discharged during suspension");
+ continue;
+ }
+
+ r = get_system_battery_identifier_hash(dev, &system_hash_id);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to generate system battery identifier hash: %m");
+
+ log_device_debug(dev,
+ "%d%% was discharged in %s. Estimating discharge rate...",
+ battery_last_capacity - battery_current_capacity,
+ FORMAT_TIMESPAN(after_timestamp - before_timestamp, USEC_PER_SEC));
+
+ battery_discharge_rate = (battery_last_capacity - battery_current_capacity) * USEC_PER_HOUR / (after_timestamp - before_timestamp);
+ r = put_battery_discharge_rate(battery_discharge_rate, system_hash_id, trunc);
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to update battery discharge rate, ignoring: %m");
+ else
+ trunc = false;
+ }
+
+ return 0;
+}
+
+/* calculate the suspend interval for each battery and then return the sum of it */
+int get_total_suspend_interval(Hashmap *last_capacity, usec_t *ret) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ usec_t total_suspend_interval = 0;
+ sd_device *dev;
+ int r;
+
+ assert(last_capacity);
+ assert(ret);
+
+ r = battery_enumerator_new(&e);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+
+ FOREACH_DEVICE(e, dev) {
+ int battery_last_capacity, previous_discharge_rate = 0;
+ const char *battery_name;
+ usec_t suspend_interval;
+
+ r = sd_device_get_property_value(dev, "POWER_SUPPLY_NAME", &battery_name);
+ if (r < 0) {
+ log_device_debug_errno(dev, r, "Failed to read battery name, ignoring: %m");
+ continue;
+ }
+
+ battery_last_capacity = PTR_TO_CAPACITY(hashmap_get(last_capacity, battery_name));
+ if (battery_last_capacity <= 0)
+ continue;
+
+ r = get_battery_discharge_rate(dev, &previous_discharge_rate);
+ if (r < 0) {
+ log_device_debug_errno(dev, r, "Failed to get discharge rate, ignoring: %m");
+ continue;
+ }
+
+ if (previous_discharge_rate == 0)
+ continue;
+
+ if (battery_last_capacity * 2 <= previous_discharge_rate) {
+ log_device_debug(dev, "Current battery capacity percentage too low compared to discharge rate");
+ continue;
+ }
+ suspend_interval = battery_last_capacity * USEC_PER_HOUR / previous_discharge_rate;
+
+ total_suspend_interval = usec_add(total_suspend_interval, suspend_interval);
+ }
+ /* The previous discharge rate is stored in per hour basis so converted to minutes.
+ * Subtracted 30 minutes from the result to keep a buffer of 30 minutes before battery gets critical */
+ total_suspend_interval = usec_sub_unsigned(total_suspend_interval, 30 * USEC_PER_MINUTE);
+ if (total_suspend_interval == 0)
+ return -ENOENT;
+
+ *ret = total_suspend_interval;
+
+ return 0;
+}
+
int can_sleep_state(char **types) {
_cleanup_free_ char *text = NULL;
int r;
#pragma once
#include <linux/fiemap.h>
+
+#include "hashmap.h"
#include "time-util.h"
typedef enum SleepOperation {
int can_sleep(SleepOperation operation);
int can_sleep_disk(char **types);
int can_sleep_state(char **types);
+int battery_is_low(void);
+int get_total_suspend_interval(Hashmap *last_capacity, usec_t *ret);
+int fetch_batteries_capacity_by_name(Hashmap **ret_current_capacity);
+int estimate_battery_discharge_rate_per_hour(
+ Hashmap *last_capacity,
+ Hashmap *current_capacity,
+ usec_t before_timestamp,
+ usec_t after_timestamp);
const char* sleep_operation_to_string(SleepOperation s) _const_;
SleepOperation sleep_operation_from_string(const char *s) _pure_;
return 0;
}
-int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
- return 0;
-}
-
-int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) {
+int mac_smack_fix_full(int atfd, const char *inode_path, const char *label_path, LabelFixFlags flags) {
return 0;
}
return 0;
}
+#define TPM2_CREDIT_RANDOM_FLAG_PATH "/run/systemd/tpm-rng-credited"
+
static int tpm2_credit_random(ESYS_CONTEXT *c) {
size_t rps, done = 0;
TSS2_RC rc;
+ usec_t t;
int r;
assert(c);
* but likely better. Note that we don't trust the TPM RNG very much, hence do not actually credit
* any entropy. */
+ if (access(TPM2_CREDIT_RANDOM_FLAG_PATH, F_OK) < 0) {
+ if (errno != ENOENT)
+ log_debug_errno(errno, "Failed to detect if '" TPM2_CREDIT_RANDOM_FLAG_PATH "' exists, ignoring: %m");
+ } else {
+ log_debug("Not adding TPM2 entropy to the kernel random pool again.");
+ return 0; /* Already done */
+ }
+
+ t = now(CLOCK_MONOTONIC);
+
for (rps = random_pool_size(); rps > 0;) {
_cleanup_(Esys_Freep) TPM2B_DIGEST *buffer = NULL;
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Zero-sized entropy returned from TPM.");
- r = random_write_entropy(-1, buffer->buffer, buffer->size, false);
+ r = random_write_entropy(-1, buffer->buffer, buffer->size, /* credit= */ false);
if (r < 0)
return log_error_errno(r, "Failed wo write entropy to kernel: %m");
rps = LESS_BY(rps, buffer->size);
}
- log_debug("Added %zu bytes of entropy to the kernel random pool.", done);
+ log_debug("Added %zu bytes of TPM2 entropy to the kernel random pool in %s.", done, FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - t, 0));
+
+ r = touch(TPM2_CREDIT_RANDOM_FLAG_PATH);
+ if (r < 0)
+ log_debug_errno(r, "Failed to touch '" TPM2_CREDIT_RANDOM_FLAG_PATH "', ignoring: %m");
+
return 0;
}
.type = TPM2_ALG_ECC,
.nameAlg = TPM2_ALG_SHA256,
.objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters = {
- .eccDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .curveID = TPM2_ECC_NIST_P256,
- .kdf.scheme = TPM2_ALG_NULL,
+ .parameters.eccDetail = {
+ .symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
},
+ .scheme.scheme = TPM2_ALG_NULL,
+ .curveID = TPM2_ECC_NIST_P256,
+ .kdf.scheme = TPM2_ALG_NULL,
},
},
};
.type = TPM2_ALG_RSA,
.nameAlg = TPM2_ALG_SHA256,
.objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters = {
- .rsaDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .keyBits = 2048,
+ .parameters.rsaDetail = {
+ .symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
},
+ .scheme.scheme = TPM2_ALG_NULL,
+ .keyBits = 2048,
},
},
};
return 0;
}
-static void tpm2_pcr_mask_to_selecion(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret) {
+static void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret) {
assert(ret);
/* We only do 24bit here, as that's what PC TPMs are supposed to support */
*ret = (TPML_PCR_SELECTION) {
.count = 1,
- .pcrSelections[0].hash = bank,
- .pcrSelections[0].sizeofSelect = 3,
- .pcrSelections[0].pcrSelect[0] = mask & 0xFF,
- .pcrSelections[0].pcrSelect[1] = (mask >> 8) & 0xFF,
- .pcrSelections[0].pcrSelect[2] = (mask >> 16) & 0xFF,
+ .pcrSelections[0] = {
+ .hash = bank,
+ .sizeofSelect = 3,
+ .pcrSelect[0] = mask & 0xFF,
+ .pcrSelect[1] = (mask >> 8) & 0xFF,
+ .pcrSelect[2] = (mask >> 16) & 0xFF,
+ }
};
}
* actually measure into them, or only into a suboptimal bank. If so, the PCRs should be all zero or
* all 0xFF. Detect that, so that we can warn and maybe pick a better bank. */
- tpm2_pcr_mask_to_selecion(mask, bank, &selection);
+ tpm2_pcr_mask_to_selection(mask, bank, &selection);
rc = sym_Esys_PCR_Read(
c,
static int tpm2_make_encryption_session(
ESYS_CONTEXT *c,
- ESYS_TR tpmKey,
+ ESYS_TR primary,
ESYS_TR *ret_session) {
static const TPMT_SYM_DEF symmetric = {
.algorithm = TPM2_ALG_AES,
- .keyBits = {
- .aes = 128,
- },
- .mode = {
- .aes = TPM2_ALG_CFB,
- },
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
};
const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
TPMA_SESSION_CONTINUESESSION;
* recover the salt, which is then used for key derivation. */
rc = sym_Esys_StartAuthSession(
c,
- tpmKey,
+ primary,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
static int tpm2_make_pcr_session(
ESYS_CONTEXT *c,
- ESYS_TR tpmKey,
+ ESYS_TR primary,
ESYS_TR parent_session,
+ TPM2_SE session_type,
uint32_t pcr_mask,
uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
bool use_pin,
static const TPMT_SYM_DEF symmetric = {
.algorithm = TPM2_ALG_AES,
- .keyBits = {
- .aes = 128
- },
- .mode = {
- .aes = TPM2_ALG_CFB,
- }
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
};
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
TPML_PCR_SELECTION pcr_selection;
if (r == 0)
log_notice("Selected TPM2 PCRs are not initialized on this system, most likely due to a firmware issue. PCR policy is effectively not enforced. Proceeding anyway.");
- tpm2_pcr_mask_to_selecion(pcr_mask, pcr_bank, &pcr_selection);
+ tpm2_pcr_mask_to_selection(pcr_mask, pcr_bank, &pcr_selection);
} else {
TPMI_ALG_HASH h;
if (r < 0)
return r;
- tpm2_pcr_mask_to_selecion(pcr_mask, h, &pcr_selection);
+ tpm2_pcr_mask_to_selection(pcr_mask, h, &pcr_selection);
}
rc = sym_Esys_StartAuthSession(
c,
- tpmKey,
+ primary,
ESYS_TR_NONE,
parent_session,
ESYS_TR_NONE,
ESYS_TR_NONE,
NULL,
- TPM2_SE_POLICY,
+ session_type,
&symmetric,
TPM2_ALG_SHA256,
&session);
if (r < 0)
goto finish;
- r = tpm2_make_pcr_session(c.esys_context, primary, session, pcr_mask, UINT16_MAX, !!pin, NULL,
- &policy_digest, &pcr_bank);
+ r = tpm2_make_pcr_session(
+ c.esys_context,
+ primary,
+ session,
+ TPM2_SE_TRIAL,
+ pcr_mask,
+ /* pcr_bank= */ UINT16_MAX,
+ !!pin,
+ /* ret_session= */ NULL,
+ &policy_digest,
+ &pcr_bank);
if (r < 0)
goto finish;
.type = TPM2_ALG_KEYEDHASH,
.nameAlg = TPM2_ALG_SHA256,
.objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
- .parameters = {
- .keyedHashDetail = {
- .scheme.scheme = TPM2_ALG_NULL,
- },
- },
- .unique = {
- .keyedHash = {
- .size = 32,
- },
- },
+ .parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
+ .unique.keyedHash.size = 32,
.authPolicy = *policy_digest,
},
};
if (r < 0)
goto finish;
- r = tpm2_make_pcr_session(c.esys_context, primary, hmac_session, pcr_mask, pcr_bank, !!pin, &session,
- &policy_digest, NULL);
+ r = tpm2_make_pcr_session(
+ c.esys_context,
+ primary,
+ hmac_session,
+ TPM2_SE_POLICY,
+ pcr_mask,
+ pcr_bank,
+ !!pin,
+ &session,
+ &policy_digest,
+ /* ret_pcr_bank= */ NULL);
if (r < 0)
goto finish;
static const char* const resolve_name_timing_table[_RESOLVE_NAME_TIMING_MAX] = {
[RESOLVE_NAME_NEVER] = "never",
- [RESOLVE_NAME_LATE] = "late",
+ [RESOLVE_NAME_LATE] = "late",
[RESOLVE_NAME_EARLY] = "early",
};
int on_ac_power(void) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- bool found_offline = false, found_online = false;
+ bool found_offline = false, found_online = false, found_battery = false;
sd_device *d;
int r;
* for defined power source types. Also see:
* https://docs.kernel.org/admin-guide/abi-testing.html#abi-file-testing-sysfs-class-power */
if (streq(val, "Battery")) {
+ found_battery = true;
log_device_debug(d, "The power supply is battery, ignoring.");
continue;
}
log_debug("Found at least one online non-battery power supply, system is running on AC power.");
else if (!found_offline)
log_debug("Found no offline non-battery power supply, assuming system is running on AC power.");
+ else if (!found_battery)
+ log_debug("Found no battery, assuming system is running on AC power.");
else
log_debug("All non-battery power supplies are offline, assuming system is running with battery.");
- return found_online || !found_offline;
+ return found_online || !found_offline || !found_battery;
}
bool udev_available(void) {
if (hr->memory_max != UINT64_MAX)
printf(" Memory Max: %s\n", FORMAT_BYTES(hr->memory_max));
- if (hr->cpu_weight != UINT64_MAX)
+ if (hr->cpu_weight == CGROUP_WEIGHT_IDLE)
+ printf(" CPU Weight: %s\n", "idle");
+ else if (hr->cpu_weight != UINT64_MAX)
printf(" CPU Weight: %" PRIu64 "\n", hr->cpu_weight);
if (hr->io_weight != UINT64_MAX)
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "def.h"
+#include "errno-util.h"
#include "exec-util.h"
#include "fd-util.h"
#include "fileio.h"
}
int main(int argc, char *argv[]) {
+ static const char* const dirs[] = {
+ SYSTEM_SHUTDOWN_PATH,
+ NULL
+ };
_cleanup_free_ char *cgroup = NULL;
char *arguments[3];
int cmd, r;
- static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL};
/* The log target defaults to console, but the original systemd process will pass its log target in through a
* command line argument, which will override this default. Also, ensure we'll never log to the journal or
umask(0022);
if (getpid_cached() != 1) {
- log_error("Not executed by init (PID 1).");
- r = -EPERM;
+ r = log_error_errno(SYNTHETIC_ERRNO(EPERM), "Not executed by init (PID 1).");
goto error;
}
else if (streq(arg_verb, "exit"))
cmd = 0; /* ignored, just checking that arg_verb is valid */
else {
- log_error("Unknown action '%s'.", arg_verb);
- r = -EINVAL;
+ r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown action '%s'.", arg_verb);
goto error;
}
(void) watchdog_ping();
- /* Let's trim the cgroup tree on each iteration so
- that we leave an empty cgroup tree around, so that
- container managers get a nice notify event when we
- are down */
+ /* Let's trim the cgroup tree on each iteration so that we leave an empty cgroup tree around,
+ * so that container managers get a nice notify event when we are down */
if (cgroup)
(void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false);
continue;
}
- /* If in this iteration we didn't manage to
- * unmount/deactivate anything, we simply give up */
+ /* If in this iteration we didn't manage to unmount/deactivate anything, we simply give up */
if (!changed) {
log_info("Cannot finalize remaining%s%s%s%s%s continuing.",
need_umount ? " file systems," : "",
need_md_detach ? " MD devices," : "");
}
- /* We're done with the watchdog. Note that the watchdog is explicitly not
- * stopped here. It remains active to guard against any issues during the
- * rest of the shutdown sequence. */
+ /* We're done with the watchdog. Note that the watchdog is explicitly not stopped here. It remains
+ * active to guard against any issues during the rest of the shutdown sequence. */
watchdog_free_device();
- arguments[0] = NULL;
+ arguments[0] = NULL; /* Filled in by execute_directories(), when needed */
arguments[1] = arg_verb;
arguments[2] = NULL;
(void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
need_dm_detach ? " DM devices," : "",
need_md_detach ? " MD devices," : "");
- /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
- * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
- * sync'ed things already once above, but we did some more work since then which might have caused IO, hence
- * let's do it once more. Do not remove this sync, data corruption will result. */
+ /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need
+ * to be sync'ed explicitly in advance. So let's do this here, but not needlessly slow down
+ * containers. Note that we sync'ed things already once above, but we did some more work since then
+ * which might have caused IO, hence let's do it once more. Do not remove this sync, data corruption
+ * will result. */
if (!in_container)
sync_with_progress();
/* Child */
execv(args[0], (char * const *) args);
+ log_debug_errno(errno, "Failed to execute '" KEXEC "' binary, proceeding with reboot(RB_KEXEC): %m");
/* execv failed (kexec binary missing?), so try simply reboot(RB_KEXEC) */
(void) reboot(cmd);
}
(void) reboot(cmd);
- if (errno == EPERM && in_container) {
- /* If we are in a container, and we lacked
- * CAP_SYS_BOOT just exit, this will kill our
+ if (ERRNO_IS_PRIVILEGE(errno) && in_container) {
+ /* If we are in a container, and we lacked CAP_SYS_BOOT just exit, this will kill our
* container for good. */
log_info("Exiting container.");
return EXIT_SUCCESS;
}
static int execute_s2h(const SleepConfig *sleep_config) {
- _cleanup_close_ int tfd = -1;
- struct itimerspec ts = {};
+ _cleanup_hashmap_free_ Hashmap *last_capacity = NULL, *current_capacity = NULL;
int r;
assert(sleep_config);
- tfd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK|TFD_CLOEXEC);
- if (tfd < 0)
- return log_error_errno(errno, "Error creating timerfd: %m");
+ while (battery_is_low() == 0) {
+ _cleanup_close_ int tfd = -1;
+ struct itimerspec ts = {};
+ usec_t suspend_interval = sleep_config->hibernate_delay_sec, before_timestamp = 0, after_timestamp = 0, total_suspend_interval;
+ bool woken_by_timer;
- log_debug("Set timerfd wake alarm for %s",
- FORMAT_TIMESPAN(sleep_config->hibernate_delay_sec, USEC_PER_SEC));
+ tfd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (tfd < 0)
+ return log_error_errno(errno, "Error creating timerfd: %m");
- timespec_store(&ts.it_value, sleep_config->hibernate_delay_sec);
+ /* Store current battery capacity and current time before suspension */
+ r = fetch_batteries_capacity_by_name(&last_capacity);
+ if (r >= 0)
+ before_timestamp = now(CLOCK_BOOTTIME);
+ else if (r == -ENOENT)
+ /* In case of no battery, system suspend interval will be set to HibernateDelaySec=. */
+ log_debug_errno(r, "Suspend Interval value set to %s: %m", FORMAT_TIMESPAN(suspend_interval, USEC_PER_SEC));
+ else
+ return log_error_errno(r, "Error fetching battery capacity percentage: %m");
+
+ r = get_total_suspend_interval(last_capacity, &total_suspend_interval);
+ if (r < 0)
+ log_debug_errno(r, "Failed to estimate suspend interval using previous discharge rate, ignoring: %m");
+ else
+ suspend_interval = total_suspend_interval;
- r = timerfd_settime(tfd, 0, &ts, NULL);
- if (r < 0)
- return log_error_errno(errno, "Error setting hibernate timer: %m");
+ log_debug("Set timerfd wake alarm for %s", FORMAT_TIMESPAN(suspend_interval, USEC_PER_SEC));
+ /* Wake alarm for system with or without battery to hibernate or estimate discharge rate whichever is applicable */
+ timespec_store(&ts.it_value, suspend_interval);
- r = execute(sleep_config, SLEEP_SUSPEND, NULL);
- if (r < 0)
- return r;
+ if (timerfd_settime(tfd, 0, &ts, NULL) < 0)
+ return log_error_errno(errno, "Error setting battery estimate timer: %m");
- r = fd_wait_for_event(tfd, POLLIN, 0);
- if (r < 0)
- return log_error_errno(r, "Error polling timerfd: %m");
- if (!FLAGS_SET(r, POLLIN)) /* We woke up before the alarm time, we are done. */
- return 0;
+ r = execute(sleep_config, SLEEP_SUSPEND, NULL);
+ if (r < 0)
+ return r;
- tfd = safe_close(tfd);
+ r = fd_wait_for_event(tfd, POLLIN, 0);
+ if (r < 0)
+ return log_error_errno(r, "Error polling timerfd: %m");
+ /* Store fd_wait status */
+ woken_by_timer = FLAGS_SET(r, POLLIN);
- /* If woken up after alarm time, hibernate */
- log_debug("Attempting to hibernate after waking from %s timer",
- FORMAT_TIMESPAN(sleep_config->hibernate_delay_sec, USEC_PER_SEC));
+ r = fetch_batteries_capacity_by_name(¤t_capacity);
+ if (r < 0) {
+ /* In case of no battery or error while getting charge level, no need to measure
+ * discharge rate. Instead system should wakeup if it is manual wakeup or
+ * hibernate if this is a timer wakeup. */
+ log_debug_errno(r, "Battery capacity percentage unavailable, cannot estimate discharge rate: %m");
+ if (!woken_by_timer)
+ return 0;
+ break;
+ }
+
+ after_timestamp = now(CLOCK_BOOTTIME);
+ log_debug("Attempting to estimate battery discharge rate after wakeup from %s sleep", FORMAT_TIMESPAN(after_timestamp - before_timestamp, USEC_PER_HOUR));
+
+ if (after_timestamp != before_timestamp) {
+ r = estimate_battery_discharge_rate_per_hour(last_capacity, current_capacity, before_timestamp, after_timestamp);
+ if (r < 0)
+ log_warning_errno(r, "Failed to estimate and update battery discharge rate, ignoring: %m");
+ } else
+ log_debug("System woke up too early to estimate discharge rate");
+
+ if (!woken_by_timer)
+ /* Return as manual wakeup done. This also will return in case battery was charged during suspension */
+ return 0;
+ }
+ log_debug("Attempting to hibernate");
r = execute(sleep_config, SLEEP_HIBERNATE, NULL);
if (r < 0) {
log_notice("Couldn't hibernate, will try to suspend again.");
#HibernateState=disk
#HybridSleepMode=suspend platform shutdown
#HybridSleepState=disk
-#HibernateDelaySec=180min
+#HibernateDelaySec=120min
#include "sd-bus.h"
+#include "bus-locator.h"
#include "bus-util.h"
#include "bus-error.h"
#include "def.h"
log_info("Reloading system manager configuration");
- r = sd_bus_message_new_method_call(
+ r = bus_message_new_method_call(
bus,
&m,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
+ bus_systemd_mgr,
"Reload");
if (r < 0)
return bus_log_create_error(r);
log_info("Starting "SPECIAL_DEFAULT_TARGET);
/* Start this unit only if we can replace basic.target with it */
- r = sd_bus_call_method(bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "StartUnit",
- &error,
- NULL,
- "ss", SPECIAL_DEFAULT_TARGET, "isolate");
+ r = bus_call_method(
+ bus,
+ bus_systemd_mgr,
+ "StartUnit",
+ &error,
+ NULL,
+ "ss", SPECIAL_DEFAULT_TARGET, "isolate");
if (r < 0)
return log_error_errno(r, "Failed to start "SPECIAL_DEFAULT_TARGET": %s", bus_error_message(&error, r));
#include <sys/types.h>
#include "conf-files.h"
+#include "creds-util.h"
#include "def.h"
#include "errno-util.h"
#include "fd-util.h"
static char **arg_prefixes = NULL;
static bool arg_cat_config = false;
+static bool arg_strict = false;
static PagerFlags arg_pager_flags = 0;
STATIC_DESTRUCTOR_REGISTER(arg_prefixes, strv_freep);
if (strv_isempty(arg_prefixes))
return true;
- STRV_FOREACH(i, arg_prefixes) {
- const char *t;
-
- t = path_startswith(*i, "/proc/sys/");
- if (!t)
- t = *i;
-
- if (path_startswith(p, t))
- return true;
- }
-
- return false;
+ return path_startswith_strv(p, arg_prefixes);
}
static Option *option_new(
return TAKE_PTR(o);
}
-static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_failure) {
+static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_failure, bool ignore_enoent) {
int r;
r = sysctl_write(key, value);
if (r < 0) {
- /* If the sysctl is not available in the kernel or we are running with reduced privileges and
- * cannot write it, then log about the issue, and proceed without failing. (EROFS is treated
- * as a permission problem here, since that's how container managers usually protected their
- * sysctls.) In all other cases log an error and make the tool fail. */
- if (ignore_failure || r == -EROFS || ERRNO_IS_PRIVILEGE(r))
+ /* Proceed without failing if ignore_failure is true.
+ * If the sysctl is not available in the kernel or we are running with reduced privileges and
+ * cannot write it, then log about the issue, and proceed without failing. Unless strict mode
+ * (arg_strict = true) is enabled, in which case we should fail. (EROFS is treated as a
+ * permission problem here, since that's how container managers usually protected their
+ * sysctls.)
+ * In all other cases log an error and make the tool fail. */
+ if (ignore_failure || (!arg_strict && (r == -EROFS || ERRNO_IS_PRIVILEGE(r))))
log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
- else if (r == -ENOENT)
+ else if (ignore_enoent && r == -ENOENT)
log_warning_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
else
return log_error_errno(r, "Couldn't write '%s' to '%s': %m", value, key);
return 0;
}
-static int apply_all(OrderedHashmap *sysctl_options) {
- Option *option;
- int r = 0;
+static int apply_glob_option_with_prefix(OrderedHashmap *sysctl_options, Option *option, const char *prefix) {
+ _cleanup_strv_free_ char **paths = NULL;
+ _cleanup_free_ char *pattern = NULL;
+ int r, k;
- ORDERED_HASHMAP_FOREACH(option, sysctl_options) {
- int k;
+ assert(sysctl_options);
+ assert(option);
- /* Ignore "negative match" options, they are there only to exclude stuff from globs. */
- if (!option->value)
+ if (prefix) {
+ _cleanup_free_ char *key = NULL;
+
+ r = path_glob_can_match(option->key, prefix, &key);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if the glob '%s' matches prefix '%s': %m",
+ option->key, prefix);
+ if (r == 0) {
+ log_debug("The glob '%s' does not match prefix '%s'.", option->key, prefix);
+ return 0;
+ }
+
+ log_debug("The glob '%s' is prefixed with '%s': '%s'", option->key, prefix, key);
+
+ if (!string_is_glob(key)) {
+ /* The prefixed pattern is not glob anymore. Let's skip to call glob(). */
+ if (ordered_hashmap_contains(sysctl_options, key)) {
+ log_debug("Not setting %s (explicit setting exists).", key);
+ return 0;
+ }
+
+ return sysctl_write_or_warn(key, option->value,
+ /* ignore_failure = */ option->ignore_failure,
+ /* ignore_enoent = */ true);
+ }
+
+ pattern = path_join("/proc/sys", key);
+ } else
+ pattern = path_join("/proc/sys", option->key);
+ if (!pattern)
+ return log_oom();
+
+ r = glob_extend(&paths, pattern, GLOB_NOCHECK);
+ if (r < 0) {
+ if (r == -ENOENT) {
+ log_debug("No match for glob: %s", option->key);
+ return 0;
+ }
+ if (option->ignore_failure || ERRNO_IS_PRIVILEGE(r)) {
+ log_debug_errno(r, "Failed to resolve glob '%s', ignoring: %m", option->key);
+ return 0;
+ } else
+ return log_error_errno(r, "Couldn't resolve glob '%s': %m", option->key);
+ }
+
+ STRV_FOREACH(s, paths) {
+ const char *key;
+
+ assert_se(key = path_startswith(*s, "/proc/sys"));
+
+ if (ordered_hashmap_contains(sysctl_options, key)) {
+ log_debug("Not setting %s (explicit setting exists).", key);
continue;
+ }
- if (string_is_glob(option->key)) {
- _cleanup_strv_free_ char **paths = NULL;
- _cleanup_free_ char *pattern = NULL;
+ k = sysctl_write_or_warn(key, option->value,
+ /* ignore_failure = */ option->ignore_failure,
+ /* ignore_enoent = */ !arg_strict);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
- pattern = path_join("/proc/sys", option->key);
- if (!pattern)
- return log_oom();
+ return r;
+}
- k = glob_extend(&paths, pattern, GLOB_NOCHECK);
- if (k < 0) {
- if (option->ignore_failure || ERRNO_IS_PRIVILEGE(k))
- log_debug_errno(k, "Failed to resolve glob '%s', ignoring: %m",
- option->key);
- else {
- log_error_errno(k, "Couldn't resolve glob '%s': %m",
- option->key);
- if (r == 0)
- r = k;
- }
+static int apply_glob_option(OrderedHashmap *sysctl_options, Option *option) {
+ int r = 0, k;
- } else if (strv_isempty(paths))
- log_debug("No match for glob: %s", option->key);
+ if (strv_isempty(arg_prefixes))
+ return apply_glob_option_with_prefix(sysctl_options, option, NULL);
- STRV_FOREACH(s, paths) {
- const char *key;
+ STRV_FOREACH(i, arg_prefixes) {
+ k = apply_glob_option_with_prefix(sysctl_options, option, *i);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
- assert_se(key = path_startswith(*s, "/proc/sys"));
+ return r;
+}
- if (!test_prefix(key))
- continue;
+static int apply_all(OrderedHashmap *sysctl_options) {
+ Option *option;
+ int r = 0;
- if (ordered_hashmap_contains(sysctl_options, key)) {
- log_debug("Not setting %s (explicit setting exists).", key);
- continue;
- }
+ ORDERED_HASHMAP_FOREACH(option, sysctl_options) {
+ int k;
- k = sysctl_write_or_warn(key, option->value, option->ignore_failure);
- if (r == 0)
- r = k;
- }
+ /* Ignore "negative match" options, they are there only to exclude stuff from globs. */
+ if (!option->value)
+ continue;
- } else {
- k = sysctl_write_or_warn(option->key, option->value, option->ignore_failure);
- if (r == 0)
- r = k;
- }
+ if (string_is_glob(option->key))
+ k = apply_glob_option(sysctl_options, option);
+ else
+ k = sysctl_write_or_warn(option->key, option->value,
+ /* ignore_failure = */ option->ignore_failure,
+ /* ignore_enoent = */ !arg_strict);
+ if (k < 0 && r >= 0)
+ r = k;
}
return r;
!test_prefix(p))
continue;
- if (ordered_hashmap_ensure_allocated(sysctl_options, &option_hash_ops) < 0)
- return log_oom();
-
existing = ordered_hashmap_get(*sysctl_options, p);
if (existing) {
if (streq_ptr(value, existing->value)) {
if (!new_option)
return log_oom();
- k = ordered_hashmap_put(*sysctl_options, new_option->key, new_option);
+ k = ordered_hashmap_ensure_put(sysctl_options, &option_hash_ops, new_option->key, new_option);
if (k < 0)
return log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", p);
return r;
}
+static int read_credential_lines(OrderedHashmap **sysctl_options) {
+ _cleanup_free_ char *j = NULL;
+ const char *d;
+ int r;
+
+ r = get_credentials_dir(&d);
+ if (r == -ENXIO)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to get credentials directory: %m");
+
+ j = path_join(d, "sysctl.extra");
+ if (!j)
+ return log_oom();
+
+ (void) parse_file(sysctl_options, j, /* ignore_enoent= */ true);
+ return 0;
+}
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
ARG_CAT_CONFIG,
ARG_PREFIX,
ARG_NO_PAGER,
+ ARG_STRICT,
};
static const struct option options[] = {
{ "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
{ "prefix", required_argument, NULL, ARG_PREFIX },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "strict", no_argument, NULL, ARG_STRICT },
{}
};
break;
case ARG_PREFIX: {
+ const char *s;
char *p;
/* We used to require people to specify absolute paths
* sysctl name available. */
sysctl_normalize(optarg);
- if (path_startswith(optarg, "/proc/sys"))
- p = strdup(optarg);
- else
- p = path_join("/proc/sys", optarg);
+ s = path_startswith(optarg, "/proc/sys");
+ p = strdup(s ?: optarg);
if (!p)
return log_oom();
arg_pager_flags |= PAGER_DISABLE;
break;
+ case ARG_STRICT:
+ arg_strict = true;
+ break;
+
case '?':
return -EINVAL;
if (k < 0 && r == 0)
r = k;
}
+
+ k = read_credential_lines(&sysctl_options);
+ if (k < 0 && r == 0)
+ r = k;
}
k = apply_all(sysctl_options);
#include "sd-login.h"
#include "bus-error.h"
+#include "bus-locator.h"
#include "format-table.h"
#include "locale-util.h"
#include "set.h"
return c;
}
-static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
+static void output_legend(const char *type, size_t n_items) {
+ const char *on, *off;
+
+ assert(type);
+
+ on = n_items > 0 ? ansi_highlight() : ansi_highlight_red();
+ off = ansi_normal();
+
+ printf("\n%s%zu %ss listed.%s\n", on, n_items, type, off);
+ if (!arg_all)
+ printf("Pass --all to see loaded but inactive %ss, too.\n", type);
+}
+
+static int output_units_list(const UnitInfo *unit_infos, size_t c) {
_cleanup_(table_unrefp) Table *table = NULL;
- unsigned job_count = 0;
+ size_t job_count = 0;
int r;
table = table_new("", "unit", "load", "active", "sub", "job", "description");
"SUB = The low-level unit activation state, values depend on unit type.");
if (job_count > 0)
puts("JOB = Pending job for the unit.\n");
- on = ansi_highlight();
- off = ansi_normal();
- } else {
- on = ansi_highlight_red();
- off = ansi_normal();
}
+ on = records > 0 ? ansi_highlight() : ansi_highlight_red();
+ off = ansi_normal();
+
if (arg_all || strv_contains(arg_states, "inactive"))
printf("%s%zu loaded units listed.%s\n"
"To show all installed unit files use 'systemctl list-unit-files'.\n",
return strcmp(a->type, b->type);
}
-static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
+static int output_sockets_list(struct socket_info *socket_infos, size_t cs) {
_cleanup_(table_unrefp) Table *table = NULL;
- const char *on, *off;
int r;
+ assert(socket_infos || cs == 0);
+
table = table_new("listen", "type", "unit", "activates");
if (!table)
return log_oom();
(void) table_set_empty_string(table, "-");
- if (cs) {
- for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
- _cleanup_free_ char *j = NULL;
- const char *path;
-
- if (s->machine) {
- j = strjoin(s->machine, ":", s->path);
- if (!j)
- return log_oom();
- path = j;
- } else
- path = s->path;
-
- r = table_add_many(table,
- TABLE_STRING, path,
- TABLE_STRING, s->type,
- TABLE_STRING, s->id);
- if (r < 0)
- return table_log_add_error(r);
-
- if (strv_isempty(s->triggered))
- r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
- else if (strv_length(s->triggered) == 1)
- r = table_add_cell(table, NULL, TABLE_STRING, s->triggered[0]);
- else
- /* This should never happen, currently our socket units can only trigger a
- * single unit. But let's handle this anyway, who knows what the future
- * brings? */
- r = table_add_cell(table, NULL, TABLE_STRV, s->triggered);
- if (r < 0)
- return table_log_add_error(r);
+ for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
+ _cleanup_free_ char *j = NULL;
+ const char *path;
- }
+ if (s->machine) {
+ j = strjoin(s->machine, ":", s->path);
+ if (!j)
+ return log_oom();
+ path = j;
+ } else
+ path = s->path;
- on = ansi_highlight();
- off = ansi_normal();
- } else {
- on = ansi_highlight_red();
- off = ansi_normal();
+ r = table_add_many(table,
+ TABLE_STRING, path,
+ TABLE_STRING, s->type,
+ TABLE_STRING, s->id);
+ if (r < 0)
+ return table_log_add_error(r);
+
+ if (strv_isempty(s->triggered))
+ r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+ else if (strv_length(s->triggered) == 1)
+ r = table_add_cell(table, NULL, TABLE_STRING, s->triggered[0]);
+ else
+ /* This should never happen, currently our socket units can only trigger a
+ * single unit. But let's handle this anyway, who knows what the future
+ * brings? */
+ r = table_add_cell(table, NULL, TABLE_STRV, s->triggered);
+ if (r < 0)
+ return table_log_add_error(r);
}
r = output_table(table);
if (r < 0)
return r;
- if (arg_legend != 0) {
- printf("\n%s%u sockets listed.%s\n", on, cs, off);
- if (!arg_all)
- printf("Pass --all to see loaded but inactive sockets, too.\n");
- }
+ if (arg_legend != 0)
+ output_legend("socket", cs);
return 0;
}
_cleanup_strv_free_ char **sockets_with_suffix = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
_cleanup_free_ struct socket_info *socket_infos = NULL;
- unsigned cs = 0;
+ size_t cs = 0;
int r, n;
sd_bus *bus;
return strcmp(a->id, b->id);
}
-static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
+static int output_timers_list(struct timer_info *timer_infos, size_t n) {
_cleanup_(table_unrefp) Table *table = NULL;
- const char *on, *off;
int r;
assert(timer_infos || n == 0);
return table_log_add_error(r);
}
- if (n > 0) {
- on = ansi_highlight();
- off = ansi_normal();
- } else {
- on = ansi_highlight_red();
- off = ansi_normal();
- }
-
r = output_table(table);
if (r < 0)
return r;
- if (arg_legend != 0) {
- printf("\n%s%u timers listed.%s\n", on, n, off);
- if (!arg_all)
- printf("Pass --all to see loaded but inactive timers, too.\n");
- }
+ if (arg_legend != 0)
+ output_legend("timer", n);
return 0;
}
_cleanup_strv_free_ char **timers_with_suffix = NULL;
_cleanup_free_ struct timer_info *timer_infos = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
- int n, c = 0;
dual_timestamp nw;
+ size_t c = 0;
sd_bus *bus;
- int r;
+ int n, r;
r = acquire_bus(BUS_MANAGER, &bus);
if (r < 0)
return r;
}
+
+struct automount_info {
+ const char *machine;
+ const char *id;
+ char *what;
+ char *where;
+ usec_t timeout_idle_usec;
+ bool mounted;
+};
+
+static int automount_info_compare(const struct automount_info *a, const struct automount_info *b) {
+ int r;
+
+ assert(a);
+ assert(b);
+
+ r = strcasecmp_ptr(a->machine, b->machine);
+ if (r != 0)
+ return r;
+
+ return strcmp(a->where, b->where);
+}
+
+static int collect_automount_info(sd_bus* bus, const UnitInfo* info, struct automount_info *ret_info) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *mount = NULL, *mount_path = NULL, *where = NULL, *what = NULL, *state = NULL;
+ usec_t timeout_idle_usec;
+ BusLocator locator;
+ int r;
+
+ assert(bus);
+ assert(info);
+ assert(ret_info);
+
+ locator = (BusLocator) {
+ .destination = "org.freedesktop.systemd1",
+ .path = info->unit_path,
+ .interface = "org.freedesktop.systemd1.Automount",
+ };
+
+ r = bus_get_property_string(bus, &locator, "Where", &error, &where);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get automount target: %s", bus_error_message(&error, r));
+
+ r = bus_get_property_trivial(bus, &locator, "TimeoutIdleUSec", &error, 't', &timeout_idle_usec);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get idle timeout: %s", bus_error_message(&error, r));
+
+ r = unit_name_from_path(where, ".mount", &mount);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name from path: %m");
+
+ mount_path = unit_dbus_path_from_name(mount);
+ if (!mount_path)
+ return log_oom();
+
+ locator.path = mount_path;
+ locator.interface = "org.freedesktop.systemd1.Mount";
+
+ r = bus_get_property_string(bus, &locator, "What", &error, &what);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get mount source: %s", bus_error_message(&error, r));
+
+ locator.interface = "org.freedesktop.systemd1.Unit";
+
+ r = bus_get_property_string(bus, &locator, "ActiveState", &error, &state);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get mount state: %s", bus_error_message(&error, r));
+
+ *ret_info = (struct automount_info) {
+ .machine = info->machine,
+ .id = info->id,
+ .what = TAKE_PTR(what),
+ .where = TAKE_PTR(where),
+ .timeout_idle_usec = timeout_idle_usec,
+ .mounted = streq_ptr(state, "active"),
+ };
+
+ return 0;
+}
+
+static int output_automounts_list(struct automount_info *infos, size_t n_infos) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ int r;
+
+ assert(infos || n_infos == 0);
+
+ table = table_new("what", "where", "mounted", "idle timeout", "unit");
+ if (!table)
+ return log_oom();
+
+ table_set_header(table, arg_legend != 0);
+ if (arg_full)
+ table_set_width(table, 0);
+
+ (void) table_set_empty_string(table, "-");
+
+ for (struct automount_info *info = infos; info < infos + n_infos; info++) {
+ _cleanup_free_ char *j = NULL;
+ const char *unit;
+
+ if (info->machine) {
+ j = strjoin(info->machine, ":", info->id);
+ if (!j)
+ return log_oom();
+ unit = j;
+ } else
+ unit = info->id;
+
+ r = table_add_many(table,
+ TABLE_STRING, info->what,
+ TABLE_STRING, info->where,
+ TABLE_BOOLEAN, info->mounted,
+ TABLE_TIMESPAN_MSEC, info->timeout_idle_usec,
+ TABLE_STRING, unit);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ r = output_table(table);
+ if (r < 0)
+ return r;
+
+ if (arg_legend != 0)
+ output_legend("automount", n_infos);
+
+ return 0;
+}
+
+int verb_list_automounts(int argc, char *argv[], void *userdata) {
+ _cleanup_(message_set_freep) Set *replies = NULL;
+ _cleanup_strv_free_ char **machines = NULL, **automounts = NULL;
+ _cleanup_free_ UnitInfo *unit_infos = NULL;
+ _cleanup_free_ struct automount_info *automount_infos = NULL;
+ size_t c = 0;
+ int r, n;
+ sd_bus *bus;
+
+ r = acquire_bus(BUS_MANAGER, &bus);
+ if (r < 0)
+ return r;
+
+ pager_open(arg_pager_flags);
+
+ r = expand_unit_names(bus, strv_skip(argv, 1), ".automount", &automounts, NULL);
+ if (r < 0)
+ return r;
+
+ if (argc == 1 || automounts) {
+ n = get_unit_list_recursive(bus, automounts, &unit_infos, &replies, &machines);
+ if (n < 0)
+ return n;
+
+ for (const UnitInfo *u = unit_infos; u < unit_infos + n; u++) {
+ if (!endswith(u->id, ".automount"))
+ continue;
+
+ if (!GREEDY_REALLOC(automount_infos, c + 1)) {
+ r = log_oom();
+ goto cleanup;
+ }
+
+ r = collect_automount_info(bus, u, &automount_infos[c]);
+ if (r < 0)
+ goto cleanup;
+
+ c++;
+ }
+
+ typesafe_qsort(automount_infos, c, automount_info_compare);
+ }
+
+ output_automounts_list(automount_infos, c);
+
+ cleanup:
+ assert(c == 0 || automount_infos);
+ for (struct automount_info *info = automount_infos; info < automount_infos + c; info++) {
+ free(info->what);
+ free(info->where);
+ }
+
+ return r;
+}
int verb_list_units(int argc, char *argv[], void *userdata);
int verb_list_sockets(int argc, char *argv[], void *userdata);
int verb_list_timers(int argc, char *argv[], void *userdata);
+int verb_list_automounts(int argc, char *argv[], void *userdata);
usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next);
int unit_get_dependencies(sd_bus *bus, const char *name, char ***ret) {
_cleanup_strv_free_ char **deps = NULL;
- static const struct bus_properties_map map[_DEPENDENCY_MAX][6] = {
+ static const struct bus_properties_map map[_DEPENDENCY_MAX][7] = {
[DEPENDENCY_FORWARD] = {
{ "Requires", "as", NULL, 0 },
{ "Requisite", "as", NULL, 0 },
{ "Wants", "as", NULL, 0 },
{ "ConsistsOf", "as", NULL, 0 },
{ "BindsTo", "as", NULL, 0 },
+ { "Upholds", "as", NULL, 0 },
{}
},
[DEPENDENCY_REVERSE] = {
{ "WantedBy", "as", NULL, 0 },
{ "PartOf", "as", NULL, 0 },
{ "BoundBy", "as", NULL, 0 },
+ { "UpheldBy", "as", NULL, 0 },
{}
},
[DEPENDENCY_AFTER] = {
"%5$sQuery or send control commands to the system manager.%6$s\n"
"\n%3$sUnit Commands:%4$s\n"
" list-units [PATTERN...] List units currently in memory\n"
+ " list-automounts [PATTERN...] List automount units currently in memory,\n"
+ " ordered by path\n"
" list-sockets [PATTERN...] List socket units currently in memory,\n"
" ordered by address\n"
" list-timers [PATTERN...] List timer units currently in memory,\n"
static const Verb verbs[] = {
{ "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, verb_list_units },
{ "list-unit-files", VERB_ANY, VERB_ANY, 0, verb_list_unit_files },
+ { "list-automounts", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_automounts },
{ "list-sockets", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_sockets },
{ "list-timers", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_timers },
{ "list-jobs", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_jobs },
#endif
#ifndef _SD_ARRAY_STATIC
-# if __STDC_VERSION__ >= 199901L
+# if __STDC_VERSION__ >= 199901L && !defined(__cplusplus)
# define _SD_ARRAY_STATIC static
# else
# define _SD_ARRAY_STATIC
int sd_device_new_from_ifname(sd_device **ret, const char *ifname);
int sd_device_new_from_ifindex(sd_device **ret, int ifindex);
+int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix);
+
int sd_device_get_parent(sd_device *child, sd_device **ret);
int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret);
int sd_device_monitor_detach_event(sd_device_monitor *m);
sd_event *sd_device_monitor_get_event(sd_device_monitor *m);
sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m);
+int sd_device_monitor_set_description(sd_device_monitor *m, const char *description);
+int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret);
int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata);
int sd_device_monitor_stop(sd_device_monitor *m);
int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret);
int sd_dhcp_client_set_mac(
sd_dhcp_client *client,
- const uint8_t *addr,
+ const uint8_t *hw_addr,
const uint8_t *bcast_addr,
size_t addr_len,
uint16_t arp_type);
int request);
int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
sd_dhcp6_option *v);
+int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable);
int sd_dhcp6_client_get_lease(
sd_dhcp6_client *client,
/* Hey! If you add a new message here, you *must* also update the message catalog with an appropriate explanation */
-/* And if you add a new ID here, make sure to generate a random one with "systemd-id128 new". Do not use any other IDs,
- * and do not count them up manually. */
-
-#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
-#define SD_MESSAGE_JOURNAL_START_STR SD_ID128_MAKE_STR(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
-#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
-#define SD_MESSAGE_JOURNAL_STOP_STR SD_ID128_MAKE_STR(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
-#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
-#define SD_MESSAGE_JOURNAL_DROPPED_STR SD_ID128_MAKE_STR(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
-#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
-#define SD_MESSAGE_JOURNAL_MISSED_STR SD_ID128_MAKE_STR(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
-#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
-#define SD_MESSAGE_JOURNAL_USAGE_STR SD_ID128_MAKE_STR(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
-
-#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
-#define SD_MESSAGE_COREDUMP_STR SD_ID128_MAKE_STR(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
-#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
-#define SD_MESSAGE_TRUNCATED_CORE_STR SD_ID128_MAKE_STR(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
-#define SD_MESSAGE_BACKTRACE SD_ID128_MAKE(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
-#define SD_MESSAGE_BACKTRACE_STR SD_ID128_MAKE_STR(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
-
-#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
-#define SD_MESSAGE_SESSION_START_STR SD_ID128_MAKE_STR(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
-#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
-#define SD_MESSAGE_SESSION_STOP_STR SD_ID128_MAKE_STR(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
-#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
-#define SD_MESSAGE_SEAT_START_STR SD_ID128_MAKE_STR(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
-#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
-#define SD_MESSAGE_SEAT_STOP_STR SD_ID128_MAKE_STR(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
-#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
-#define SD_MESSAGE_MACHINE_START_STR SD_ID128_MAKE_STR(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
-#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
-#define SD_MESSAGE_MACHINE_STOP_STR SD_ID128_MAKE_STR(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
-
-#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
-#define SD_MESSAGE_TIME_CHANGE_STR SD_ID128_MAKE_STR(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
-#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
-#define SD_MESSAGE_TIMEZONE_CHANGE_STR SD_ID128_MAKE_STR(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
-
-#define SD_MESSAGE_TAINTED SD_ID128_MAKE(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b)
-#define SD_MESSAGE_TAINTED_STR SD_ID128_MAKE_STR(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b)
-#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
-#define SD_MESSAGE_STARTUP_FINISHED_STR SD_ID128_MAKE_STR(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
-#define SD_MESSAGE_USER_STARTUP_FINISHED \
- SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
-#define SD_MESSAGE_USER_STARTUP_FINISHED_STR \
- SD_ID128_MAKE_STR(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
-
-#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
-#define SD_MESSAGE_SLEEP_START_STR SD_ID128_MAKE_STR(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
-#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
-#define SD_MESSAGE_SLEEP_STOP_STR SD_ID128_MAKE_STR(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
-
-#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
-#define SD_MESSAGE_SHUTDOWN_STR SD_ID128_MAKE_STR(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
-
-#define SD_MESSAGE_FACTORY_RESET SD_ID128_MAKE(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c)
-#define SD_MESSAGE_FACTORY_RESET_STR SD_ID128_MAKE_STR(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c)
-
-/* The messages below are actually about jobs, not really about units, the macros are misleadingly named. Moreover
- * SD_MESSAGE_UNIT_FAILED is not actually about a failing unit but about a failed start job. A job either finishes with
- * SD_MESSAGE_UNIT_STARTED or with SD_MESSAGE_UNIT_FAILED hence. */
-#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
-#define SD_MESSAGE_UNIT_STARTING_STR SD_ID128_MAKE_STR(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
-#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
-#define SD_MESSAGE_UNIT_STARTED_STR SD_ID128_MAKE_STR(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
-#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
-#define SD_MESSAGE_UNIT_FAILED_STR SD_ID128_MAKE_STR(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
-#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
-#define SD_MESSAGE_UNIT_STOPPING_STR SD_ID128_MAKE_STR(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
-#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
-#define SD_MESSAGE_UNIT_STOPPED_STR SD_ID128_MAKE_STR(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
-#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
-#define SD_MESSAGE_UNIT_RELOADING_STR SD_ID128_MAKE_STR(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
-#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
-#define SD_MESSAGE_UNIT_RELOADED_STR SD_ID128_MAKE_STR(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
-
-#define SD_MESSAGE_UNIT_RESTART_SCHEDULED SD_ID128_MAKE(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3)
-#define SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR \
- SD_ID128_MAKE_STR(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3)
-
-#define SD_MESSAGE_UNIT_RESOURCES SD_ID128_MAKE(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
-#define SD_MESSAGE_UNIT_RESOURCES_STR SD_ID128_MAKE_STR(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
-
-#define SD_MESSAGE_UNIT_SUCCESS SD_ID128_MAKE(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
-#define SD_MESSAGE_UNIT_SUCCESS_STR SD_ID128_MAKE_STR(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
-#define SD_MESSAGE_UNIT_SKIPPED SD_ID128_MAKE(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73)
-#define SD_MESSAGE_UNIT_SKIPPED_STR SD_ID128_MAKE_STR(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73)
-#define SD_MESSAGE_UNIT_FAILURE_RESULT SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
-#define SD_MESSAGE_UNIT_FAILURE_RESULT_STR \
- SD_ID128_MAKE_STR(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
-
-#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
-#define SD_MESSAGE_SPAWN_FAILED_STR SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
-
-#define SD_MESSAGE_UNIT_PROCESS_EXIT SD_ID128_MAKE(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
-#define SD_MESSAGE_UNIT_PROCESS_EXIT_STR SD_ID128_MAKE_STR(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
-
-#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
-#define SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR \
- SD_ID128_MAKE_STR(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
-
-#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
-#define SD_MESSAGE_OVERMOUNTING_STR SD_ID128_MAKE_STR(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
-
-#define SD_MESSAGE_UNIT_OOMD_KILL SD_ID128_MAKE(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed)
-#define SD_MESSAGE_UNIT_OOMD_KILL_STR SD_ID128_MAKE_STR(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed)
-
-#define SD_MESSAGE_UNIT_OUT_OF_MEMORY SD_ID128_MAKE(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef)
-#define SD_MESSAGE_UNIT_OUT_OF_MEMORY_STR SD_ID128_MAKE_STR(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef)
-
-#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
-#define SD_MESSAGE_LID_OPENED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
-#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
-#define SD_MESSAGE_LID_CLOSED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
-#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
-#define SD_MESSAGE_SYSTEM_DOCKED_STR SD_ID128_MAKE_STR(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
-#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
-#define SD_MESSAGE_SYSTEM_UNDOCKED_STR SD_ID128_MAKE_STR(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
-#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
-#define SD_MESSAGE_POWER_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
-#define SD_MESSAGE_POWER_KEY_LONG_PRESS SD_ID128_MAKE(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b)
-#define SD_MESSAGE_POWER_KEY_LONG_PRESS_STR \
- SD_ID128_MAKE_STR(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b)
-#define SD_MESSAGE_REBOOT_KEY SD_ID128_MAKE(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
-#define SD_MESSAGE_REBOOT_KEY_STR SD_ID128_MAKE_STR(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
-#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS SD_ID128_MAKE(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75)
-#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS_STR \
- SD_ID128_MAKE_STR(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75)
-#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
-#define SD_MESSAGE_SUSPEND_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
-#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS SD_ID128_MAKE(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8)
-#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS_STR \
- SD_ID128_MAKE_STR(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8)
-#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
-#define SD_MESSAGE_HIBERNATE_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
-#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS \
- SD_ID128_MAKE(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45)
-#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS_STR \
- SD_ID128_MAKE_STR(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45)
-
-#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
-#define SD_MESSAGE_INVALID_CONFIGURATION_STR \
- SD_ID128_MAKE_STR(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
-
-#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
-#define SD_MESSAGE_DNSSEC_FAILURE_STR SD_ID128_MAKE_STR(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
-#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED \
- SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
-#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR \
- SD_ID128_MAKE_STR(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
-#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
-#define SD_MESSAGE_DNSSEC_DOWNGRADE_STR SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
-
-#define SD_MESSAGE_UNSAFE_USER_NAME SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
-#define SD_MESSAGE_UNSAFE_USER_NAME_STR SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
-
-#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE \
- SD_ID128_MAKE(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
-#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR \
- SD_ID128_MAKE_STR(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
-#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE \
- SD_ID128_MAKE(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa)
-#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE_STR \
- SD_ID128_MAKE_STR(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa)
-
-#define SD_MESSAGE_NOBODY_USER_UNSUITABLE SD_ID128_MAKE(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
-#define SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR \
- SD_ID128_MAKE_STR(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
-
-#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED \
- SD_ID128_MAKE(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
-#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR \
- SD_ID128_MAKE_STR(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
-
-#define SD_MESSAGE_TIME_SYNC SD_ID128_MAKE(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
-#define SD_MESSAGE_TIME_SYNC_STR SD_ID128_MAKE_STR(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
-
-#define SD_MESSAGE_LOGIND_SHUTDOWN SD_ID128_MAKE(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
-#define SD_MESSAGE_LOGIND_SHUTDOWN_STR SD_ID128_MAKE_STR(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
-
-#define SD_MESSAGE_LOGIND_SHUTDOWN_CANCELED \
- SD_ID128_MAKE(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
-#define SD_MESSAGE_LOGIND_SHUTDOWN_CANCELED_STR \
- SD_ID128_MAKE_STR(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
+/* And if you add a new ID here, make sure to generate a random one with "systemd-id128 new". Do not use any
+ * other IDs, and do not count them up manually. */
+
+#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
+#define SD_MESSAGE_JOURNAL_START_STR SD_ID128_MAKE_STR(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
+#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
+#define SD_MESSAGE_JOURNAL_STOP_STR SD_ID128_MAKE_STR(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
+#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
+#define SD_MESSAGE_JOURNAL_DROPPED_STR SD_ID128_MAKE_STR(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
+#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
+#define SD_MESSAGE_JOURNAL_MISSED_STR SD_ID128_MAKE_STR(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06)
+#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
+#define SD_MESSAGE_JOURNAL_USAGE_STR SD_ID128_MAKE_STR(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6)
+
+#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
+#define SD_MESSAGE_COREDUMP_STR SD_ID128_MAKE_STR(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1)
+#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
+#define SD_MESSAGE_TRUNCATED_CORE_STR SD_ID128_MAKE_STR(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37)
+#define SD_MESSAGE_BACKTRACE SD_ID128_MAKE(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
+#define SD_MESSAGE_BACKTRACE_STR SD_ID128_MAKE_STR(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95)
+
+#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
+#define SD_MESSAGE_SESSION_START_STR SD_ID128_MAKE_STR(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66)
+#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
+#define SD_MESSAGE_SESSION_STOP_STR SD_ID128_MAKE_STR(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
+#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
+#define SD_MESSAGE_SEAT_START_STR SD_ID128_MAKE_STR(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
+#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
+#define SD_MESSAGE_SEAT_STOP_STR SD_ID128_MAKE_STR(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
+#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
+#define SD_MESSAGE_MACHINE_START_STR SD_ID128_MAKE_STR(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
+#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
+#define SD_MESSAGE_MACHINE_STOP_STR SD_ID128_MAKE_STR(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
+
+#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
+#define SD_MESSAGE_TIME_CHANGE_STR SD_ID128_MAKE_STR(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
+#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
+#define SD_MESSAGE_TIMEZONE_CHANGE_STR SD_ID128_MAKE_STR(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
+
+#define SD_MESSAGE_TAINTED SD_ID128_MAKE(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b)
+#define SD_MESSAGE_TAINTED_STR SD_ID128_MAKE_STR(50,87,6a,9d,b0,0f,4c,40,bd,e1,a2,ad,38,1c,3a,1b)
+#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
+#define SD_MESSAGE_STARTUP_FINISHED_STR SD_ID128_MAKE_STR(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff)
+#define SD_MESSAGE_USER_STARTUP_FINISHED SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
+#define SD_MESSAGE_USER_STARTUP_FINISHED_STR SD_ID128_MAKE_STR(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1)
+
+#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
+#define SD_MESSAGE_SLEEP_START_STR SD_ID128_MAKE_STR(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28)
+#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
+#define SD_MESSAGE_SLEEP_STOP_STR SD_ID128_MAKE_STR(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14)
+
+#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
+#define SD_MESSAGE_SHUTDOWN_STR SD_ID128_MAKE_STR(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40)
+
+#define SD_MESSAGE_FACTORY_RESET SD_ID128_MAKE(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c)
+#define SD_MESSAGE_FACTORY_RESET_STR SD_ID128_MAKE_STR(c1,4a,af,76,ec,28,4a,5f,a1,f1,05,f8,8d,fb,06,1c)
+
+/* The messages below are actually about jobs, not really about units, the macros are misleadingly named.
+ * Moreover SD_MESSAGE_UNIT_FAILED is not actually about a failing unit but about a failed start job. A job
+ * either finishes with SD_MESSAGE_UNIT_STARTED or with SD_MESSAGE_UNIT_FAILED hence. */
+#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
+#define SD_MESSAGE_UNIT_STARTING_STR SD_ID128_MAKE_STR(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5)
+#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
+#define SD_MESSAGE_UNIT_STARTED_STR SD_ID128_MAKE_STR(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf)
+#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
+#define SD_MESSAGE_UNIT_FAILED_STR SD_ID128_MAKE_STR(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d)
+#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
+#define SD_MESSAGE_UNIT_STOPPING_STR SD_ID128_MAKE_STR(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f)
+#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
+#define SD_MESSAGE_UNIT_STOPPED_STR SD_ID128_MAKE_STR(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86)
+#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
+#define SD_MESSAGE_UNIT_RELOADING_STR SD_ID128_MAKE_STR(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25)
+#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
+#define SD_MESSAGE_UNIT_RELOADED_STR SD_ID128_MAKE_STR(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54)
+
+#define SD_MESSAGE_UNIT_RESTART_SCHEDULED SD_ID128_MAKE(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3)
+#define SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR SD_ID128_MAKE_STR(5e,b0,34,94,b6,58,48,70,a5,36,b3,37,29,08,09,b3)
+
+#define SD_MESSAGE_UNIT_RESOURCES SD_ID128_MAKE(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
+#define SD_MESSAGE_UNIT_RESOURCES_STR SD_ID128_MAKE_STR(ae,8f,7b,86,6b,03,47,b9,af,31,fe,1c,80,b1,27,c0)
+
+#define SD_MESSAGE_UNIT_SUCCESS SD_ID128_MAKE(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
+#define SD_MESSAGE_UNIT_SUCCESS_STR SD_ID128_MAKE_STR(7a,d2,d1,89,f7,e9,4e,70,a3,8c,78,13,54,91,24,48)
+#define SD_MESSAGE_UNIT_SKIPPED SD_ID128_MAKE(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73)
+#define SD_MESSAGE_UNIT_SKIPPED_STR SD_ID128_MAKE_STR(0e,42,84,a0,ca,ca,4b,fc,81,c0,bb,67,86,97,26,73)
+#define SD_MESSAGE_UNIT_FAILURE_RESULT SD_ID128_MAKE(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
+#define SD_MESSAGE_UNIT_FAILURE_RESULT_STR SD_ID128_MAKE_STR(d9,b3,73,ed,55,a6,4f,eb,82,42,e0,2d,be,79,a4,9c)
+
+#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
+#define SD_MESSAGE_SPAWN_FAILED_STR SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7)
+
+#define SD_MESSAGE_UNIT_PROCESS_EXIT SD_ID128_MAKE(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
+#define SD_MESSAGE_UNIT_PROCESS_EXIT_STR SD_ID128_MAKE_STR(98,e3,22,20,3f,7a,4e,d2,90,d0,9f,e0,3c,09,fe,15)
+
+#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
+#define SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR SD_ID128_MAKE_STR(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e)
+
+#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
+#define SD_MESSAGE_OVERMOUNTING_STR SD_ID128_MAKE_STR(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7)
+
+#define SD_MESSAGE_UNIT_OOMD_KILL SD_ID128_MAKE(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed)
+#define SD_MESSAGE_UNIT_OOMD_KILL_STR SD_ID128_MAKE_STR(d9,89,61,1b,15,e4,4c,9d,bf,31,e3,c8,12,56,e4,ed)
+
+#define SD_MESSAGE_UNIT_OUT_OF_MEMORY SD_ID128_MAKE(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef)
+#define SD_MESSAGE_UNIT_OUT_OF_MEMORY_STR SD_ID128_MAKE_STR(fe,6f,aa,94,e7,77,46,63,a0,da,52,71,78,91,d8,ef)
+
+#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
+#define SD_MESSAGE_LID_OPENED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f)
+#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
+#define SD_MESSAGE_LID_CLOSED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70)
+#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
+#define SD_MESSAGE_SYSTEM_DOCKED_STR SD_ID128_MAKE_STR(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff)
+#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
+#define SD_MESSAGE_SYSTEM_UNDOCKED_STR SD_ID128_MAKE_STR(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53)
+#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
+#define SD_MESSAGE_POWER_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71)
+#define SD_MESSAGE_POWER_KEY_LONG_PRESS SD_ID128_MAKE(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b)
+#define SD_MESSAGE_POWER_KEY_LONG_PRESS_STR SD_ID128_MAKE_STR(3e,01,17,10,1e,b2,43,c1,b9,a5,0d,b3,49,4a,b1,0b)
+#define SD_MESSAGE_REBOOT_KEY SD_ID128_MAKE(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
+#define SD_MESSAGE_REBOOT_KEY_STR SD_ID128_MAKE_STR(9f,a9,d2,c0,12,13,4e,c3,85,45,1f,fe,31,6f,97,d0)
+#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS SD_ID128_MAKE(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75)
+#define SD_MESSAGE_REBOOT_KEY_LONG_PRESS_STR SD_ID128_MAKE_STR(f1,c5,9a,58,c9,d9,43,66,89,65,c3,37,ca,ec,59,75)
+#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
+#define SD_MESSAGE_SUSPEND_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72)
+#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS SD_ID128_MAKE(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8)
+#define SD_MESSAGE_SUSPEND_KEY_LONG_PRESS_STR SD_ID128_MAKE_STR(bf,da,f6,d3,12,ab,40,07,bc,1f,e4,0a,15,df,78,e8)
+#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
+#define SD_MESSAGE_HIBERNATE_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73)
+#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS SD_ID128_MAKE(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45)
+#define SD_MESSAGE_HIBERNATE_KEY_LONG_PRESS_STR SD_ID128_MAKE_STR(16,78,36,df,6f,7f,42,8e,98,14,72,27,b2,dc,89,45)
+
+#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
+#define SD_MESSAGE_INVALID_CONFIGURATION_STR SD_ID128_MAKE_STR(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01)
+
+#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
+#define SD_MESSAGE_DNSSEC_FAILURE_STR SD_ID128_MAKE_STR(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
+#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR SD_ID128_MAKE_STR(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
+#define SD_MESSAGE_DNSSEC_DOWNGRADE_STR SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
+
+#define SD_MESSAGE_UNSAFE_USER_NAME SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
+#define SD_MESSAGE_UNSAFE_USER_NAME_STR SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
+
+#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE SD_ID128_MAKE(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
+#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR SD_ID128_MAKE_STR(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
+#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE SD_ID128_MAKE(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa)
+#define SD_MESSAGE_DEVICE_PATH_NOT_SUITABLE_STR SD_ID128_MAKE_STR(01,01,90,13,8f,49,4e,29,a0,ef,66,69,74,95,31,aa)
+
+#define SD_MESSAGE_NOBODY_USER_UNSUITABLE SD_ID128_MAKE(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
+#define SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR SD_ID128_MAKE_STR(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
+
+#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED SD_ID128_MAKE(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
+#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR SD_ID128_MAKE_STR(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
+
+#define SD_MESSAGE_TIME_SYNC SD_ID128_MAKE(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
+#define SD_MESSAGE_TIME_SYNC_STR SD_ID128_MAKE_STR(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37)
+
+#define SD_MESSAGE_SHUTDOWN_SCHEDULED SD_ID128_MAKE(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
+#define SD_MESSAGE_SHUTDOWN_SCHEDULED_STR SD_ID128_MAKE_STR(9e,70,66,27,9d,c8,40,3d,a7,9c,e4,b1,a6,90,64,b2)
+
+#define SD_MESSAGE_SHUTDOWN_CANCELED SD_ID128_MAKE(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
+#define SD_MESSAGE_SHUTDOWN_CANCELED_STR SD_ID128_MAKE_STR(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
_SD_END_DECLARATIONS;
int sd_netlink_attach_filter(sd_netlink *nl, size_t len, struct sock_filter *filter);
/* message */
-int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data);
-int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned short type, char * const *data);
-int sd_netlink_message_append_flag(sd_netlink_message *m, unsigned short type);
-int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uint8_t data);
-int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data);
-int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data);
-int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data);
-int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data);
-int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data);
-int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data);
-int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data);
-int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len);
-int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data);
-int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data);
-int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, unsigned short type, const struct sockaddr_in *data);
-int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, unsigned short type, const struct sockaddr_in6 *data);
-int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data);
-int sd_netlink_message_append_cache_info(sd_netlink_message *m, unsigned short type, const struct ifa_cacheinfo *info);
-
-int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type);
-int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned short type, const char *key);
+int sd_netlink_message_append_string(sd_netlink_message *m, uint16_t attr_type, const char *data);
+int sd_netlink_message_append_strv(sd_netlink_message *m, uint16_t attr_type, char * const *data);
+int sd_netlink_message_append_flag(sd_netlink_message *m, uint16_t attr_type);
+int sd_netlink_message_append_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t data);
+int sd_netlink_message_append_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t data);
+int sd_netlink_message_append_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t data);
+int sd_netlink_message_append_u64(sd_netlink_message *m, uint16_t attr_type, uint64_t data);
+int sd_netlink_message_append_s8(sd_netlink_message *m, uint16_t attr_type, int8_t data);
+int sd_netlink_message_append_s16(sd_netlink_message *m, uint16_t attr_type, int16_t data);
+int sd_netlink_message_append_s32(sd_netlink_message *m, uint16_t attr_type, int32_t data);
+int sd_netlink_message_append_s64(sd_netlink_message *m, uint16_t attr_type, int64_t data);
+int sd_netlink_message_append_data(sd_netlink_message *m, uint16_t attr_type, const void *data, size_t len);
+int sd_netlink_message_append_container_data(
+ sd_netlink_message *m,
+ uint16_t container_type,
+ uint16_t attr_type,
+ const void *data,
+ size_t len);
+int sd_netlink_message_append_in_addr(sd_netlink_message *m, uint16_t attr_type, const struct in_addr *data);
+int sd_netlink_message_append_in6_addr(sd_netlink_message *m, uint16_t attr_type, const struct in6_addr *data);
+int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in *data);
+int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, uint16_t attr_type, const struct sockaddr_in6 *data);
+int sd_netlink_message_append_ether_addr(sd_netlink_message *m, uint16_t attr_type, const struct ether_addr *data);
+int sd_netlink_message_append_cache_info(sd_netlink_message *m, uint16_t attr_type, const struct ifa_cacheinfo *info);
+
+int sd_netlink_message_open_container(sd_netlink_message *m, uint16_t attr_type);
+int sd_netlink_message_open_container_union(sd_netlink_message *m, uint16_t attr_type, const char *key);
int sd_netlink_message_close_container(sd_netlink_message *m);
-int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data);
-int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data);
-int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data);
-int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data);
-int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data);
-int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret);
-int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data);
-int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data);
-int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data);
-int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data);
-int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info);
-int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data);
-int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data);
-int sd_netlink_message_has_flag(sd_netlink_message *m, unsigned short type);
-int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type);
-int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type);
+int sd_netlink_message_read(sd_netlink_message *m, uint16_t attr_type, size_t size, void *data);
+int sd_netlink_message_read_data(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data);
+int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data);
+int sd_netlink_message_read_string_strdup(sd_netlink_message *m, uint16_t attr_type, char **data);
+int sd_netlink_message_read_string(sd_netlink_message *m, uint16_t attr_type, const char **data);
+int sd_netlink_message_read_strv(sd_netlink_message *m, uint16_t container_type, uint16_t attr_type, char ***ret);
+int sd_netlink_message_read_u8(sd_netlink_message *m, uint16_t attr_type, uint8_t *data);
+int sd_netlink_message_read_u16(sd_netlink_message *m, uint16_t attr_type, uint16_t *data);
+int sd_netlink_message_read_u32(sd_netlink_message *m, uint16_t attr_type, uint32_t *data);
+int sd_netlink_message_read_ether_addr(sd_netlink_message *m, uint16_t attr_type, struct ether_addr *data);
+int sd_netlink_message_read_cache_info(sd_netlink_message *m, uint16_t attr_type, struct ifa_cacheinfo *info);
+int sd_netlink_message_read_in_addr(sd_netlink_message *m, uint16_t attr_type, struct in_addr *data);
+int sd_netlink_message_read_in6_addr(sd_netlink_message *m, uint16_t attr_type, struct in6_addr *data);
+int sd_netlink_message_has_flag(sd_netlink_message *m, uint16_t attr_type);
+int sd_netlink_message_enter_container(sd_netlink_message *m, uint16_t attr_type);
+int sd_netlink_message_enter_array(sd_netlink_message *m, uint16_t attr_type);
int sd_netlink_message_exit_container(sd_netlink_message *m);
int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type);
break;
}
- r = fflush_and_check(passwd);
+ r = fflush_sync_and_check(passwd);
if (r < 0)
return log_debug_errno(r, "Failed to flush %s: %m", passwd_tmp);
ORDERED_HASHMAP_FOREACH(i, todo_uids) {
_cleanup_(erase_and_freep) char *creds_password = NULL;
- _cleanup_free_ char *cn = NULL;
+ bool is_hashed;
struct spwd n = {
.sp_namp = i->name,
.sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */
};
- /* Try to pick up the password for this account via the credentials logic */
- cn = strjoin("passwd.hashed-password.", i->name);
- if (!cn)
- return -ENOMEM;
-
- r = read_credential(cn, (void**) &creds_password, NULL);
- if (r == -ENOENT) {
- _cleanup_(erase_and_freep) char *plaintext_password = NULL;
-
- free(cn);
- cn = strjoin("passwd.plaintext-password.", i->name);
- if (!cn)
- return -ENOMEM;
+ r = get_credential_user_password(i->name, &creds_password, &is_hashed);
+ if (r < 0)
+ log_debug_errno(r, "Couldn't read password credential for user '%s', ignoring: %m", i->name);
- r = read_credential(cn, (void**) &plaintext_password, NULL);
+ if (creds_password && !is_hashed) {
+ _cleanup_(erase_and_freep) char* plaintext_password = TAKE_PTR(creds_password);
+ r = hash_password(plaintext_password, &creds_password);
if (r < 0)
- log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
- else {
- r = hash_password(plaintext_password, &creds_password);
- if (r < 0)
- return log_debug_errno(r, "Failed to hash password: %m");
- }
- } else if (r < 0)
- log_debug_errno(r, "Couldn't read credential '%s', ignoring: %m", cn);
+ return log_debug_errno(r, "Failed to hash password: %m");
+ }
if (creds_password)
n.sp_pwdp = creds_password;
return 0;
}
-static int gid_is_ok(gid_t gid) {
+static int gid_is_ok(gid_t gid, bool check_with_uid) {
struct group *g;
struct passwd *p;
return 0;
/* Avoid reusing gids that are already used by a different user */
- if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
+ if (check_with_uid && ordered_hashmap_get(todo_uids, UID_TO_PTR(gid)))
return 0;
if (hashmap_contains(database_by_gid, GID_TO_PTR(gid)))
return 0;
- if (hashmap_contains(database_by_uid, UID_TO_PTR(gid)))
+ if (check_with_uid && hashmap_contains(database_by_uid, UID_TO_PTR(gid)))
return 0;
if (!arg_root) {
if (!IN_SET(errno, 0, ENOENT))
return -errno;
- errno = 0;
- p = getpwuid((uid_t) gid);
- if (p)
- return 0;
- if (!IN_SET(errno, 0, ENOENT))
- return -errno;
+ if (check_with_uid) {
+ errno = 0;
+ p = getpwuid((uid_t) gid);
+ if (p)
+ return 0;
+ if (!IN_SET(errno, 0, ENOENT))
+ return -errno;
+ }
}
return 1;
/* Try to use the suggested numeric GID */
if (i->gid_set) {
- r = gid_is_ok(i->gid);
+ r = gid_is_ok(i->gid, false);
if (r < 0)
return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
if (i->id_set_strict) {
/* Try to reuse the numeric uid, if there's one */
if (!i->gid_set && i->uid_set) {
- r = gid_is_ok((gid_t) i->uid);
+ r = gid_is_ok((gid_t) i->uid, true);
if (r < 0)
return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
if (r > 0) {
if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c))
log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
else {
- r = gid_is_ok(c);
+ r = gid_is_ok(c, true);
if (r < 0)
return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
else if (r > 0) {
if (r < 0)
return log_error_errno(r, "No free group ID available for %s.", i->name);
- r = gid_is_ok(search_uid);
+ r = gid_is_ok(search_uid, true);
if (r < 0)
return log_error_errno(r, "Failed to verify GID " GID_FMT ": %m", i->gid);
else if (r > 0)
switch (i->type) {
case ADD_USER: {
- Item *j;
+ Item *j = NULL;
+
+ if (!i->gid_set)
+ j = ordered_hashmap_get(groups, i->group_name ?: i->name);
- j = ordered_hashmap_get(groups, i->group_name ?: i->name);
if (j && j->todo_group) {
/* When a group with the target name is already in queue,
* use the information about the group and do not create
/* Use (argument):n, where n==1 for the first positional arg */
r = parse_line("(argument)", pos, *arg);
else
- r = read_config_file(*arg, false);
+ r = read_config_file(*arg, /* ignore_enoent= */ false);
if (r < 0)
return r;
log_debug("Reading config file \"%s\"%s", *f, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
/* Just warn, ignore result otherwise */
- (void) read_config_file(*f, true);
+ (void) read_config_file(*f, /* ignore_enoent= */ true);
}
return 0;
}
+static int read_credential_lines(void) {
+ _cleanup_free_ char *j = NULL;
+ const char *d;
+ int r;
+
+ r = get_credentials_dir(&d);
+ if (r == -ENXIO)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to get credentials directory: %m");
+
+ j = path_join(d, "sysusers.extra");
+ if (!j)
+ return log_oom();
+
+ (void) read_config_file(j, /* ignore_enoent= */ true);
+ return 0;
+}
+
static int run(int argc, char *argv[]) {
#ifndef STANDALONE
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
assert(!arg_image);
#endif
- /* If command line arguments are specified along with --replace, read all
- * configuration files and insert the positional arguments at the specified
- * place. Otherwise, if command line arguments are specified, execute just
- * them, and finally, without --replace= or any positional arguments, just
- * read configuration and execute it.
- */
+ /* If command line arguments are specified along with --replace, read all configuration files and
+ * insert the positional arguments at the specified place. Otherwise, if command line arguments are
+ * specified, execute just them, and finally, without --replace= or any positional arguments, just
+ * read configuration and execute it. */
if (arg_replace || optind >= argc)
r = read_config_files(argv + optind);
else
if (r < 0)
return r;
- /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
- * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
- * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
- * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
- * /etc. */
+ r = read_credential_lines();
+ if (r < 0)
+ return r;
+
+ /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our
+ * detection whether the names or UID/GID area already used otherwise doesn't get confused. After
+ * all, even though nss-systemd synthesizes these users/groups, they should still appear in
+ * /etc/passwd and /etc/group, as the synthesizing logic is merely supposed to be fallback for cases
+ * where we run with a completely unpopulated /etc. */
if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0)
return log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
[files('test-macro.c')],
+ [files('test-math-util.c'),
+ [],
+ [libm]],
+
[files('test-mkdir.c')],
[files('test-json.c'),
[files('test-exec-util.c')],
+ [files('test-execve.c')],
+
[files('test-hexdecoct.c')],
[files('test-alloc-util.c')],
#include "condition.h"
#include "cpu-set-util.h"
#include "efi-loader.h"
+#include "env-util.h"
#include "errno-util.h"
+#include "fs-util.h"
#include "hostname-util.h"
#include "id128-util.h"
#include "ima-util.h"
#include "macro.h"
#include "nulstr-util.h"
#include "os-util.h"
+#include "path-util.h"
#include "process-util.h"
#include "psi-util.h"
+#include "rm-rf.h"
#include "selinux-util.h"
#include "set.h"
#include "smack-util.h"
#include "string-util.h"
#include "strv.h"
#include "tests.h"
+#include "tmpfile-util.h"
#include "tomoyo-util.h"
#include "udev-util.h"
#include "uid-alloc-range.h"
condition_free(condition);
}
+TEST(condition_test_credential) {
+ _cleanup_(rm_rf_physical_and_freep) char *n1 = NULL, *n2 = NULL;
+ _cleanup_free_ char *d1 = NULL, *d2 = NULL, *j = NULL;
+ Condition *condition;
+
+ assert_se(free_and_strdup(&d1, getenv("CREDENTIALS_DIRECTORY")) >= 0);
+ assert_se(free_and_strdup(&d2, getenv("ENCRYPTED_CREDENTIALS_DIRECTORY")) >= 0);
+
+ assert_se(unsetenv("CREDENTIALS_DIRECTORY") >= 0);
+ assert_se(unsetenv("ENCRYPTED_CREDENTIALS_DIRECTORY") >= 0);
+
+ condition = condition_new(CONDITION_CREDENTIAL, "definitelymissing", /* trigger= */ false, /* negate= */ false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ /* invalid */
+ condition = condition_new(CONDITION_CREDENTIAL, "..", /* trigger= */ false, /* negate= */ false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ assert_se(mkdtemp_malloc(NULL, &n1) >= 0);
+ assert_se(mkdtemp_malloc(NULL, &n2) >= 0);
+
+ assert_se(setenv("CREDENTIALS_DIRECTORY", n1, /* overwrite= */ true) >= 0);
+ assert_se(setenv("ENCRYPTED_CREDENTIALS_DIRECTORY", n2, /* overwrite= */ true) >= 0);
+
+ condition = condition_new(CONDITION_CREDENTIAL, "stillmissing", /* trigger= */ false, /* negate= */ false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ assert_se(j = path_join(n1, "existing"));
+ assert_se(touch(j) >= 0);
+ assert_se(j);
+ condition = condition_new(CONDITION_CREDENTIAL, "existing", /* trigger= */ false, /* negate= */ false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) > 0);
+ condition_free(condition);
+ free(j);
+
+ assert_se(j = path_join(n2, "existing-encrypted"));
+ assert_se(touch(j) >= 0);
+ assert_se(j);
+ condition = condition_new(CONDITION_CREDENTIAL, "existing-encrypted", /* trigger= */ false, /* negate= */ false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) > 0);
+ condition_free(condition);
+
+ assert_se(set_unset_env("CREDENTIALS_DIRECTORY", d1, /* overwrite= */ true) >= 0);
+ assert_se(set_unset_env("ENCRYPTED_CREDENTIALS_DIRECTORY", d2, /* overwrite= */ true) >= 0);
+}
+
#if defined(__i386__) || defined(__x86_64__)
TEST(condition_test_cpufeature) {
Condition *condition;
unlink(fn_copy);
}
+static bool read_file_and_streq(const char* filepath, const char* expected_contents) {
+ _cleanup_free_ char *buf = NULL;
+
+ assert_se(read_full_file(filepath, &buf, NULL) == 0);
+ return streq(buf, expected_contents);
+}
+
+TEST(copy_tree_replace_file) {
+ _cleanup_free_ char *src = NULL, *dst = NULL;
+
+ assert_se(tempfn_random("/tmp/test-copy_file.XXXXXX", NULL, &src) >= 0);
+ assert_se(tempfn_random("/tmp/test-copy_file.XXXXXX", NULL, &dst) >= 0);
+
+ assert_se(write_string_file(src, "bar bar", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(dst, "foo foo foo", WRITE_STRING_FILE_CREATE) == 0);
+
+ /* The file exists- now overwrite original contents, and test the COPY_REPLACE flag. */
+
+ assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK) == -EEXIST);
+
+ assert_se(read_file_and_streq(dst, "foo foo foo\n"));
+
+ assert_se(copy_tree(src, dst, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE) == 0);
+
+ assert_se(read_file_and_streq(dst, "bar bar\n"));
+}
+
+TEST(copy_tree_replace_dirs) {
+ _cleanup_free_ char *src_path1 = NULL, *src_path2 = NULL, *dst_path1 = NULL, *dst_path2 = NULL;
+ _cleanup_(rm_rf_physical_and_freep) char *src_directory = NULL, *dst_directory = NULL;
+ const char *file1 = "foo_file", *file2 = "bar_file";
+
+ /* Create the random source/destination directories */
+ assert_se(mkdtemp_malloc("/tmp/dirXXXXXX", &src_directory) >= 0);
+ assert_se(mkdtemp_malloc("/tmp/dirXXXXXX", &dst_directory) >= 0);
+
+ /* Construct the source/destination filepaths (should have different dir name, but same file names within) */
+ assert_se(src_path1 = path_join(src_directory, file1));
+ assert_se(src_path2 = path_join(src_directory, file2));
+ assert_se(dst_path1 = path_join(dst_directory, file1));
+ assert_se(dst_path2 = path_join(dst_directory, file2));
+
+ /* Populate some data to differentiate the files. */
+ assert_se(write_string_file(src_path1, "src file 1", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(src_path2, "src file 2", WRITE_STRING_FILE_CREATE) == 0);
+
+ assert_se(write_string_file(dst_path1, "dest file 1", WRITE_STRING_FILE_CREATE) == 0);
+ assert_se(write_string_file(dst_path2, "dest file 2", WRITE_STRING_FILE_CREATE) == 0);
+
+ /* Copying without COPY_REPLACE should fail because the destination file already exists. */
+ assert_se(copy_tree(src_directory, dst_directory, UID_INVALID, GID_INVALID, COPY_REFLINK) == -EEXIST);
+
+ {
+ assert_se(read_file_and_streq(src_path1, "src file 1\n"));
+ assert_se(read_file_and_streq(src_path2, "src file 2\n"));
+ assert_se(read_file_and_streq(dst_path1, "dest file 1\n"));
+ assert_se(read_file_and_streq(dst_path2, "dest file 2\n"));
+ }
+
+ assert_se(copy_tree(src_directory, dst_directory, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE) == 0);
+
+ {
+ assert_se(read_file_and_streq(src_path1, "src file 1\n"));
+ assert_se(read_file_and_streq(src_path2, "src file 2\n"));
+ assert_se(read_file_and_streq(dst_path1, "src file 1\n"));
+ assert_se(read_file_and_streq(dst_path2, "src file 2\n"));
+ }
+}
+
TEST(copy_file_fd) {
char in_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
char out_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "exec-util.h"
+#include "fd-util.h"
+#include "log.h"
+#include "main-func.h"
+#include "strv.h"
+#include "tests.h"
+
+/* This program can be used to call programs through fexecve / execveat(…, "", …, AT_EMPTY_PATH),
+ * when compiled with -Dfexecve=true, and the fallback paths, when -Dfexecve=false.
+ *
+ * Example:
+ * $ strace -e execveat build/test-execve /bin/grep Name /proc/self/status
+ * execveat(3, "", ["/bin/grep", "Name", "/proc/self/status"], NULL, AT_EMPTY_PATH) = 0
+ * Name: 3
+ *
+ * FIXME: use the new kernel api to set COMM properly when the kernel makes that available.
+ * C.f. ceedbf8185fc7593366679f02d31da63af8c4bd1.
+ */
+
+static int run(int argc, char **argv) {
+ _cleanup_close_ int fd;
+ char **args = strv_skip(argv, 1);
+ int r;
+
+ test_setup_logging(LOG_DEBUG);
+
+ args = !strv_isempty(args) ? args : STRV_MAKE("/bin/true");
+
+ fd = open(args[0], O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "open(%s) failed: %m", args[0]);
+
+ r = fexecve_or_execve(fd, args[0], args, NULL);
+ assert_se(r < 0);
+ return log_error_errno(r, "fexecve_or_execve(%s) failed: %m", args[0]);
+}
+
+DEFINE_MAIN_FUNCTION(run);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <float.h>
-#include <math.h>
#include "alloc-util.h"
#include "escape.h"
#include "fileio.h"
#include "json-internal.h"
#include "json.h"
+#include "math-util.h"
#include "string-util.h"
#include "strv.h"
#include "tests.h"
d = va_arg(ap, double);
- assert_se(fabsl(d - v.real) < 1e-10 ||
- fabsl((d - v.real) / v.real) < 1e-10);
+ assert_se(fabs(d - v.real) < 1e-10 ||
+ fabs((d - v.real) / v.real) < 1e-10);
} else if (t == JSON_TOKEN_INTEGER) {
int64_t i;
/* has thisisaverylongproperty */
p = json_variant_by_key(v, "thisisaverylongproperty");
- assert_se(p && json_variant_type(p) == JSON_VARIANT_REAL && fabsl(json_variant_real(p) - 1.27) < 0.001);
+ assert_se(p && json_variant_type(p) == JSON_VARIANT_REAL && fabs(json_variant_real(p) - 1.27) < 0.001);
}
static void test_zeroes(JsonVariant *v) {
assert_se(json_variant_integer(w) == 0);
assert_se(json_variant_unsigned(w) == 0U);
- DISABLE_WARNING_FLOAT_EQUAL;
- assert_se(json_variant_real(w) == 0.0L);
- REENABLE_WARNING;
+ assert_se(iszero_safe(json_variant_real(w)));
assert_se(json_variant_is_integer(w));
assert_se(json_variant_is_unsigned(w));
const double delta = 0.0001;
assert_se(json_variant_is_array(v));
- assert_se(json_variant_elements(v) == 9);
- assert_se(fabsl((double) 1.0 - ((double) DBL_MIN / json_variant_real(json_variant_by_index(v, 0)))) <= delta);
- assert_se(fabsl((double) 1.0 - ((double) DBL_MAX / json_variant_real(json_variant_by_index(v, 1)))) <= delta);
+ assert_se(json_variant_elements(v) == 11);
+ assert_se(fabs(1.0 - (DBL_MIN / json_variant_real(json_variant_by_index(v, 0)))) <= delta);
+ assert_se(fabs(1.0 - (DBL_MAX / json_variant_real(json_variant_by_index(v, 1)))) <= delta);
assert_se(json_variant_is_null(json_variant_by_index(v, 2))); /* nan is not supported by json → null */
assert_se(json_variant_is_null(json_variant_by_index(v, 3))); /* +inf is not supported by json → null */
assert_se(json_variant_is_null(json_variant_by_index(v, 4))); /* -inf is not supported by json → null */
assert_se(json_variant_is_null(json_variant_by_index(v, 5)) ||
- fabsl((double) 1.0 - ((double) HUGE_VAL / json_variant_real(json_variant_by_index(v, 5)))) <= delta); /* HUGE_VAL might be +inf, but might also be something else */
+ fabs(1.0 - (HUGE_VAL / json_variant_real(json_variant_by_index(v, 5)))) <= delta); /* HUGE_VAL might be +inf, but might also be something else */
assert_se(json_variant_is_real(json_variant_by_index(v, 6)) &&
json_variant_is_integer(json_variant_by_index(v, 6)) &&
json_variant_integer(json_variant_by_index(v, 6)) == 0);
assert_se(json_variant_is_real(json_variant_by_index(v, 8)) &&
json_variant_is_integer(json_variant_by_index(v, 8)) &&
json_variant_integer(json_variant_by_index(v, 8)) == -10);
+ assert_se(json_variant_is_real(json_variant_by_index(v, 9)) &&
+ !json_variant_is_integer(json_variant_by_index(v, 9)));
+ assert_se(fabs(1.0 - (DBL_MIN / 2 / json_variant_real(json_variant_by_index(v, 9)))) <= delta);
+ assert_se(json_variant_is_real(json_variant_by_index(v, 10)) &&
+ !json_variant_is_integer(json_variant_by_index(v, 10)));
+ assert_se(fabs(1.0 - (-DBL_MIN / 2 / json_variant_real(json_variant_by_index(v, 10)))) <= delta);
}
TEST(float) {
JSON_BUILD_REAL(HUGE_VAL),
JSON_BUILD_REAL(0),
JSON_BUILD_REAL(10),
- JSON_BUILD_REAL(-10))) >= 0);
+ JSON_BUILD_REAL(-10),
+ JSON_BUILD_REAL(DBL_MIN / 2),
+ JSON_BUILD_REAL(-DBL_MIN / 2))) >= 0);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(!p);
}
+TEST(ISPOWEROF2) {
+ uint64_t u;
+ int64_t i;
+
+ /* First, test constant expressions */
+ assert_se(!ISPOWEROF2(-2));
+ assert_se(!ISPOWEROF2(-1));
+ assert_se(!ISPOWEROF2(0));
+ assert_se(ISPOWEROF2(1));
+ assert_se(ISPOWEROF2(2));
+ assert_se(!ISPOWEROF2(3));
+ assert_se(ISPOWEROF2(4));
+ assert_se(!ISPOWEROF2(5));
+ assert_se(!ISPOWEROF2(6));
+ assert_se(!ISPOWEROF2(7));
+ assert_se(ISPOWEROF2(8));
+ assert_se(!ISPOWEROF2(9));
+ assert_se(!ISPOWEROF2(1022));
+ assert_se(ISPOWEROF2(1024));
+ assert_se(!ISPOWEROF2(1025));
+ assert_se(!ISPOWEROF2(UINT64_C(0xffffffff)));
+ assert_se(ISPOWEROF2(UINT64_C(0x100000000)));
+ assert_se(!ISPOWEROF2(UINT64_C(0x100000001)));
+
+ /* Then, test dynamic expressions, and if they are side-effect free */
+ i = -2;
+ assert_se(!ISPOWEROF2(i++));
+ assert_se(i == -1);
+ assert_se(!ISPOWEROF2(i++));
+ assert_se(i == 0);
+ assert_se(!ISPOWEROF2(i++));
+ assert_se(i == 1);
+ assert_se(ISPOWEROF2(i++));
+ assert_se(i == 2);
+ assert_se(ISPOWEROF2(i++));
+ assert_se(i == 3);
+ assert_se(!ISPOWEROF2(i++));
+ assert_se(i == 4);
+ assert_se(ISPOWEROF2(i++));
+ assert_se(i == 5);
+ assert_se(!ISPOWEROF2(i));
+
+ u = 0;
+ assert_se(!ISPOWEROF2(u++));
+ assert_se(u == 1);
+ assert_se(ISPOWEROF2(u++));
+ assert_se(u == 2);
+ assert_se(ISPOWEROF2(u++));
+ assert_se(u == 3);
+ assert_se(!ISPOWEROF2(u++));
+ assert_se(u == 4);
+ assert_se(ISPOWEROF2(u++));
+ assert_se(u == 5);
+ assert_se(!ISPOWEROF2(u));
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <float.h>
+
+#include "math-util.h"
+#include "tests.h"
+
+TEST(iszero_safe) {
+ /* zeros */
+ assert_se(iszero_safe(0.0));
+ assert_se(iszero_safe(-0.0));
+ assert_se(iszero_safe(0e0));
+ assert_se(iszero_safe(-0e0));
+ assert_se(iszero_safe(0e+0));
+ assert_se(iszero_safe(0e-0));
+ assert_se(iszero_safe(-0e-0));
+ assert_se(iszero_safe(-0e000));
+ assert_se(iszero_safe(0e000));
+
+ /* non-zero normal values */
+ assert_se(!iszero_safe(42.0));
+ assert_se(!iszero_safe(M_PI));
+ assert_se(!iszero_safe(DBL_MAX));
+ assert_se(!iszero_safe(-DBL_MAX));
+ assert_se(!iszero_safe(DBL_MIN));
+ assert_se(!iszero_safe(-DBL_MIN));
+ assert_se(!iszero_safe(1 / DBL_MAX));
+
+ /* subnormal values */
+ assert_se(!iszero_safe(DBL_MIN / 2));
+ assert_se(!iszero_safe(-DBL_MIN / 42));
+ assert_se(!iszero_safe(1 / DBL_MAX / 2));
+
+ /* too small values which cannot be in subnormal form */
+ assert_se( iszero_safe(DBL_MIN / DBL_MAX));
+ assert_se( iszero_safe(DBL_MIN / -DBL_MAX));
+ assert_se( iszero_safe(-DBL_MIN / DBL_MAX));
+ assert_se( iszero_safe(-DBL_MIN / -DBL_MAX));
+
+ /* NaN or infinity */
+ assert_se(!iszero_safe(NAN));
+ assert_se(!iszero_safe(INFINITY));
+ assert_se(!iszero_safe(-INFINITY));
+ assert_se(!iszero_safe(1 / NAN));
+
+ /* inverse of infinity */
+ assert_se( iszero_safe(1 / INFINITY));
+ assert_se( iszero_safe(1 / -INFINITY));
+ assert_se( iszero_safe(-1 / INFINITY));
+ assert_se( iszero_safe(-1 / -INFINITY));
+ assert_se( iszero_safe(42 / -INFINITY));
+ assert_se( iszero_safe(-42 / -INFINITY));
+ assert_se( iszero_safe(DBL_MIN / INFINITY));
+ assert_se( iszero_safe(DBL_MIN / -INFINITY));
+ assert_se( iszero_safe(DBL_MAX / INFINITY / 2));
+ assert_se( iszero_safe(DBL_MAX / -INFINITY * DBL_MAX));
+ assert_se(!iszero_safe(DBL_MAX * 2 / INFINITY));
+
+ /* infinity / infinity is NaN */
+ assert_se(!iszero_safe(INFINITY / INFINITY));
+ assert_se(!iszero_safe(INFINITY * 2 / INFINITY));
+ assert_se(!iszero_safe(INFINITY / DBL_MAX / INFINITY));
+}
+
+TEST(fp_equal) {
+ /* normal values */
+ assert_se( fp_equal(0.0, -0e0));
+ assert_se( fp_equal(3.0, 3));
+ assert_se(!fp_equal(3.000001, 3));
+ assert_se( fp_equal(M_PI, M_PI));
+ assert_se(!fp_equal(M_PI, -M_PI));
+ assert_se( fp_equal(DBL_MAX, DBL_MAX));
+ assert_se(!fp_equal(DBL_MAX, -DBL_MAX));
+ assert_se(!fp_equal(-DBL_MAX, DBL_MAX));
+ assert_se( fp_equal(-DBL_MAX, -DBL_MAX));
+ assert_se( fp_equal(DBL_MIN, DBL_MIN));
+ assert_se(!fp_equal(DBL_MIN, -DBL_MIN));
+ assert_se(!fp_equal(-DBL_MIN, DBL_MIN));
+ assert_se( fp_equal(-DBL_MIN, -DBL_MIN));
+
+ /* subnormal values */
+ assert_se( fp_equal(DBL_MIN / 10, DBL_MIN / 10));
+ assert_se(!fp_equal(DBL_MIN / 10, -DBL_MIN / 10));
+ assert_se(!fp_equal(-DBL_MIN / 10, DBL_MIN / 10));
+ assert_se( fp_equal(-DBL_MIN / 10, -DBL_MIN / 10));
+ assert_se(!fp_equal(DBL_MIN / 10, DBL_MIN / 15));
+ assert_se(!fp_equal(DBL_MIN / 10, DBL_MIN / 15));
+
+ /* subnormal difference */
+ assert_se(!fp_equal(DBL_MIN / 10, DBL_MIN + DBL_MIN / 10));
+ assert_se( fp_equal(3.0, 3.0 + DBL_MIN / 2)); /* 3.0 + DBL_MIN / 2 is truncated to 3.0 */
+
+ /* too small values */
+ assert_se( fp_equal(DBL_MIN / DBL_MAX, -DBL_MIN / DBL_MAX));
+
+ /* NaN or infinity */
+ assert_se(!fp_equal(NAN, NAN));
+ assert_se(!fp_equal(NAN, 0));
+ assert_se(!fp_equal(NAN, INFINITY));
+ assert_se(!fp_equal(INFINITY, INFINITY));
+ assert_se(!fp_equal(INFINITY, -INFINITY));
+ assert_se(!fp_equal(-INFINITY, INFINITY));
+ assert_se(!fp_equal(-INFINITY, -INFINITY));
+
+ /* inverse of infinity */
+ assert_se( fp_equal(0, 1 / INFINITY));
+ assert_se( fp_equal(42 / INFINITY, 1 / -INFINITY));
+ assert_se(!fp_equal(42 / INFINITY, INFINITY / INFINITY));
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
TEST(fd_is_mount_point) {
_cleanup_close_ int fd = -1;
+ int r;
fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
assert_se(fd >= 0);
* the system is borked. Let's allow for it to be missing though. */
assert_se(IN_SET(fd_is_mount_point(fd, "root", 0), -ENOENT, 0));
assert_se(IN_SET(fd_is_mount_point(fd, "root/", 0), -ENOENT, 0));
+
+ safe_close(fd);
+ fd = open("/proc", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
+ assert_se(fd >= 0);
+
+ assert_se(fd_is_mount_point(fd, NULL, 0) > 0);
+ assert_se(fd_is_mount_point(fd, "", 0) == -EINVAL);
+ assert_se(fd_is_mount_point(fd, "version", 0) == 0);
+
+ safe_close(fd);
+ fd = open("/proc/version", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ assert_se(fd >= 0);
+
+ r = fd_is_mount_point(fd, NULL, 0);
+ assert_se(IN_SET(r, 0, -ENOTDIR)); /* on old kernels we can't determine if regular files are mount points if we have no directory fd */
+ assert_se(fd_is_mount_point(fd, "", 0) == -EINVAL);
}
static int intro(void) {
assert_se(streq_ptr(path_startswith_strv("/foo2/bar", STRV_MAKE("/foo/quux", "", "/zzz")), NULL));
}
+static void test_path_glob_can_match_one(const char *pattern, const char *prefix, const char *expected) {
+ _cleanup_free_ char *result = NULL;
+
+ log_debug("%s(%s, %s, %s)", __func__, pattern, prefix, strnull(expected));
+
+ assert_se(path_glob_can_match(pattern, prefix, &result) == !!expected);
+ assert_se(streq_ptr(result, expected));
+}
+
+TEST(path_glob_can_match) {
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/foo/hoge/aaa/bbb", NULL);
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/foo/hoge/aaa", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/foo/hoge", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/foo", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/hoge/aaa", "/", "/foo/hoge/aaa");
+
+ test_path_glob_can_match_one("/foo/*/aaa", "/foo/hoge/aaa/bbb", NULL);
+ test_path_glob_can_match_one("/foo/*/aaa", "/foo/hoge/aaa", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/*/aaa", "/foo/hoge", "/foo/hoge/aaa");
+ test_path_glob_can_match_one("/foo/*/aaa", "/foo", "/foo/*/aaa");
+ test_path_glob_can_match_one("/foo/*/aaa", "/", "/foo/*/aaa");
+
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx/yyy/aaa/bbb", NULL);
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx/yyy/aaa", "/foo/xxx/yyy/aaa");
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx/yyy", "/foo/xxx/yyy/aaa");
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo/xxx", "/foo/xxx/*/aaa");
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/foo", "/foo/*/*/aaa");
+ test_path_glob_can_match_one("/foo/*/*/aaa", "/", "/foo/*/*/aaa");
+
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/aaa/bbb/ccc", NULL);
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/aaa/bbb", "/foo/xxx/aaa/bbb");
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/ccc", NULL);
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx/aaa", "/foo/xxx/aaa/*");
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo/xxx", "/foo/xxx/aaa/*");
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/foo", "/foo/*/aaa/*");
+ test_path_glob_can_match_one("/foo/*/aaa/*", "/", "/foo/*/aaa/*");
+}
+
TEST(print_MAX) {
log_info("PATH_MAX=%zu\n"
"FILENAME_MAX=%zu\n"
#include "chattr-util.h"
#include "conf-files.h"
#include "copy.h"
+#include "creds-util.h"
#include "def.h"
#include "devnum-util.h"
#include "dirent-util.h"
#include "format-util.h"
#include "fs-util.h"
#include "glob-util.h"
+#include "hexdecoct.h"
#include "io-util.h"
#include "label.h"
#include "log.h"
char *path;
char *argument;
+ void *binary_argument; /* set if binary data, in which case it takes precedence over 'argument' */
+ size_t binary_argument_size;
char **xattrs;
#if HAVE_ACL
acl_t acl_access;
return set_contains(unix_sockets, fn);
}
+/* Accessors for the argument in binary format */
+static const void* item_binary_argument(const Item *i) {
+ assert(i);
+ return i->binary_argument ?: i->argument;
+}
+
+static size_t item_binary_argument_size(const Item *i) {
+ assert(i);
+ return i->binary_argument ? i->binary_argument_size : strlen_ptr(i->argument);
+}
+
static DIR* xopendirat_nomod(int dirfd, const char *path) {
DIR *dir;
int r;
assert(i);
- assert(i->argument);
-
- p = i->argument;
+ assert_se(p = i->argument);
for (;;) {
_cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL;
return fd_set_attribute(item, fd, path, NULL);
}
+static int write_argument_data(Item *i, int fd, const char *path) {
+ int r;
+
+ assert(i);
+ assert(fd >= 0);
+ assert(path);
+
+ if (item_binary_argument_size(i) == 0)
+ return 0;
+
+ assert(item_binary_argument(i));
+
+ log_debug("Writing to \"%s\".", path);
+
+ r = loop_write(fd, item_binary_argument(i), item_binary_argument_size(i), /* do_poll= */ false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write file \"%s\": %m", path);
+
+ return 0;
+}
+
static int write_one_file(Item *i, const char *path) {
_cleanup_close_ int fd = -1, dir_fd = -1;
_cleanup_free_ char *bn = NULL;
assert(i);
assert(path);
- assert(i->argument);
assert(i->type == WRITE_FILE);
r = path_extract_filename(path, &bn);
}
/* 'w' is allowed to write into any kind of files. */
- log_debug("Writing to \"%s\".", path);
- r = loop_write(fd, i->argument, strlen(i->argument), false);
+ r = write_argument_data(i, fd, path);
if (r < 0)
- return log_error_errno(r, "Failed to write file \"%s\": %m", path);
+ return r;
return fd_set_perms(i, fd, path, NULL);
}
path);
st = &stbuf;
- } else {
-
- log_debug("\"%s\" has been created.", path);
-
- if (i->argument) {
- log_debug("Writing to \"%s\".", path);
-
- r = loop_write(fd, i->argument, strlen(i->argument), false);
- if (r < 0)
- return log_error_errno(r, "Failed to write file \"%s\": %m", path);
- }
+ } else if (item_binary_argument(i)) {
+ r = write_argument_data(i, fd, path);
+ if (r < 0)
+ return r;
}
return fd_set_perms(i, fd, path, st);
log_debug("\"%s\" has been created.", path);
- if (i->argument) {
- log_debug("Writing to \"%s\".", path);
-
- r = loop_write(fd, i->argument, strlen(i->argument), false);
+ if (item_binary_argument(i)) {
+ r = write_argument_data(i, fd, path);
if (r < 0)
- return log_error_errno(erofs ? -EROFS : r, "Failed to write file %s: %m", path);
+ return r;
}
return fd_set_perms(i, fd, path, st);
assert(i);
free(i->path);
free(i->argument);
+ free(i->binary_argument);
strv_free(i->xattrs);
#if HAVE_ACL
if (takes_ownership(a->type) && takes_ownership(b->type))
/* check if the items are the same */
- return streq_ptr(a->argument, b->argument) &&
+ return memcmp_nn(item_binary_argument(a), item_binary_argument_size(a),
+ item_binary_argument(b), item_binary_argument_size(b)) == 0 &&
a->uid_set == b->uid_set &&
a->uid == b->uid &&
ItemArray *existing;
OrderedHashmap *h;
int r, pos;
- bool append_or_force = false, boot = false, allow_failure = false, try_replace = false;
+ bool append_or_force = false, boot = false, allow_failure = false, try_replace = false, unbase64 = false, from_cred = false;
assert(fname);
assert(line >= 1);
allow_failure = true;
else if (action[pos] == '=' && !try_replace)
try_replace = true;
+ else if (action[pos] == '~' && !unbase64)
+ unbase64 = true;
+ else if (action[pos] == '^' && !from_cred)
+ from_cred = true;
else {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action);
break;
case CREATE_SYMLINK:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for symlink targets.");
+ }
+
if (!i.argument) {
i.argument = path_join("/usr/share/factory", i.path);
if (!i.argument)
break;
case COPY_FILES:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for copy sources.");
+ }
+
if (!i.argument) {
i.argument = path_join("/usr/share/factory", i.path);
if (!i.argument)
return log_oom();
-
} else if (!path_is_absolute(i.argument)) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Source path '%s' is not absolute.", i.argument);
case CREATE_CHAR_DEVICE:
case CREATE_BLOCK_DEVICE:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for device node creation.");
+ }
+
if (!i.argument) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Device file requires argument.");
case SET_XATTR:
case RECURSIVE_SET_XATTR:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for extended attributes.");
+ }
if (!i.argument) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
case SET_ACL:
case RECURSIVE_SET_ACL:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for ACLs.");
+ }
if (!i.argument) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
case SET_ATTRIBUTE:
case RECURSIVE_SET_ATTRIBUTE:
+ if (unbase64) {
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "base64 decoding not supported for file attributes.");
+ }
if (!i.argument) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
if (!should_include_path(i.path))
return 0;
- r = specifier_expansion_from_arg(specifier_table, &i);
- if (r == -ENXIO)
- return log_unresolvable_specifier(fname, line);
- if (r < 0) {
- if (IN_SET(r, -EINVAL, -EBADSLT))
- *invalid_config = true;
- return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m");
+ if (!unbase64) {
+ /* Do specifier expansion except if base64 mode is enabled */
+ r = specifier_expansion_from_arg(specifier_table, &i);
+ if (r == -ENXIO)
+ return log_unresolvable_specifier(fname, line);
+ if (r < 0) {
+ if (IN_SET(r, -EINVAL, -EBADSLT))
+ *invalid_config = true;
+ return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m");
+ }
+ }
+
+ if (from_cred) {
+ if (!i.argument)
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Reading from credential requested, but no credential name specified.");
+ if (!credential_name_valid(i.argument))
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Credential name not valid: %s", i.argument);
+
+ r = read_credential(i.argument, &i.binary_argument, &i.binary_argument_size);
+ if (IN_SET(r, -ENXIO, -ENOENT)) {
+ /* Silently skip over lines that have no credentials passed */
+ log_syntax(NULL, LOG_INFO, fname, line, 0, "Credential '%s' not specified, skipping line.", i.argument);
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read credential '%s': %m", i.argument);
+ }
+
+ /* If base64 decoding is requested, do so now */
+ if (unbase64 && item_binary_argument(&i)) {
+ _cleanup_free_ void *data = NULL;
+ size_t data_size = 0;
+
+ r = unbase64mem(item_binary_argument(&i), item_binary_argument_size(&i), &data, &data_size);
+ if (r < 0)
+ return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to base64 decode specified argument '%s': %m", i.argument);
+
+ free_and_replace(i.binary_argument, data);
+ i.binary_argument_size = data_size;
}
if (!empty_or_root(arg_root)) {
return 1;
}
-static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
+static int read_config_file(
+ char **config_dirs,
+ const char *fn,
+ bool ignore_enoent,
+ bool *invalid_config) {
+
_cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
_cleanup_fclose_ FILE *_f = NULL;
_cleanup_free_ char *pp = NULL;
return 0;
}
+static int read_credential_lines(bool *invalid_config) {
+ _cleanup_free_ char *j = NULL;
+ const char *d;
+ int r;
+
+ r = get_credentials_dir(&d);
+ if (r == -ENXIO)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to get credentials directory: %m");
+
+ j = path_join(d, "tmpfiles.extra");
+ if (!j)
+ return log_oom();
+
+ (void) read_config_file(/* config_dirs= */ NULL, j, /* ignore_enoent= */ true, invalid_config);
+ return 0;
+}
+
static int link_parent(ItemArray *a) {
const char *path;
char *prefix;
if (r < 0)
return r;
+ r = read_credential_lines(&invalid_config);
+ if (r < 0)
+ return r;
+
/* Let's now link up all child/parent relationships */
ORDERED_HASHMAP_FOREACH(a, items) {
r = link_parent(a);
/* Take care to not iterate beyond the last valid track as specified in
* the TOC, but also avoid going beyond the TOC length, just in case
* the last track number is invalidly large */
- for (size_t i = 4; i + 8 < len && num_tracks > 0; i += 8, --num_tracks) {
+ for (size_t i = 4; i + 8 <= len && num_tracks > 0; i += 8, --num_tracks) {
bool is_data_track;
uint32_t block;
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(LinkConfig, conditions)
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions)
Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(LinkConfig, conditions)
+Match.Credential, config_parse_net_condition, CONDITION_CREDENTIAL, offsetof(LinkConfig, conditions)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions)
Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(LinkConfig, conditions)
Link.Description, config_parse_string, 0, offsetof(LinkConfig, description)
"Link\0"
"SR-IOV\0",
config_item_perf_lookup, link_config_gperf_lookup,
- CONFIG_PARSE_WARN, config, &stats_by_path);
+ CONFIG_PARSE_WARN, config, &stats_by_path,
+ NULL);
if (r < 0)
return r; /* config_parse_many() logs internally. */
assert(ctx);
- r = config_get_stats_by_path(".link", NULL, 0, NETWORK_DIRS, &stats_by_path);
+ r = config_get_stats_by_path(".link", NULL, 0, NETWORK_DIRS, /* check_dropins = */ true, &stats_by_path);
if (r < 0) {
- log_warning_errno(r, "Failed to get stats of .link files: %m");
+ log_warning_errno(r, "Failed to get stats of .link files, ignoring: %m");
return true;
}
#include "gpt.h"
#include "parse-util.h"
#include "string-util.h"
+#include "strv.h"
#include "strxcpyx.h"
#include "udev-builtin.h"
blkid_encode_string(value, s, sizeof(s));
udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s);
+ } else if (STR_IN_SET(name, "FSSIZE", "FSLASTBLOCK", "FSBLOCKSIZE")) {
+ strscpyl(s, sizeof(s), "ID_FS_", name + 2, NULL);
+ udev_builtin_add_property(dev, test, s, value);
+
} else if (streq(name, "PTTYPE")) {
udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value);
blkid_probe_set_superblocks_flags(pr,
BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
+#ifdef BLKID_SUBLKS_FSINFO
+ BLKID_SUBLKS_FSINFO |
+#endif
BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION);
if (noraid)
}
/* called every couple of seconds during event activity; 'true' if config has changed */
-static bool builtin_hwdb_validate(void) {
- return hwdb_validate(hwdb);
+static bool builtin_hwdb_should_reload(void) {
+ if (hwdb_should_reload(hwdb)) {
+ log_debug("hwdb needs reloading.");
+ return true;
+ }
+
+ return false;
}
const UdevBuiltin udev_builtin_hwdb = {
.cmd = builtin_hwdb,
.init = builtin_hwdb_init,
.exit = builtin_hwdb_exit,
- .validate = builtin_hwdb_validate,
+ .should_reload = builtin_hwdb_should_reload,
.help = "Hardware database",
};
if (!ctx)
return -ENOMEM;
- log_debug("Load module index");
+ log_debug("Loading kernel module index.");
kmod_set_log_fn(ctx, udev_kmod_log, NULL);
kmod_load_resources(ctx);
return 0;
/* called on udev shutdown and reload request */
static void builtin_kmod_exit(void) {
- log_debug("Unload module index");
+ log_debug("Unload kernel module index.");
ctx = kmod_unref(ctx);
}
/* called every couple of seconds during event activity; 'true' if config has changed */
-static bool builtin_kmod_validate(void) {
- log_debug("Validate module index");
+static bool builtin_kmod_should_reload(void) {
if (!ctx)
return false;
- return (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK);
+
+ if (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) {
+ log_debug("Kernel module index needs reloading.");
+ return true;
+ }
+
+ return false;
}
const UdevBuiltin udev_builtin_kmod = {
.cmd = builtin_kmod,
.init = builtin_kmod_init,
.exit = builtin_kmod_exit,
- .validate = builtin_kmod_validate,
+ .should_reload = builtin_kmod_should_reload,
.help = "Kernel module loader",
.run_once = false,
};
NET_XENVIF,
NET_PLATFORM,
NET_NETDEVSIM,
+ NET_DEVICETREE,
} NetNameType;
typedef struct NetNames {
char xen_slot[ALTIFNAMSIZ];
char platform_path[ALTIFNAMSIZ];
char netdevsim_path[ALTIFNAMSIZ];
+ char devicetree_onboard[ALTIFNAMSIZ];
} NetNames;
/* skip intermediate virtio devices */
return r;
/* Get physical function's pci device. */
- physfn_syspath = strjoina(syspath, "/physfn");
- r = sd_device_new_from_syspath(&physfn_pcidev, physfn_syspath);
+ r = sd_device_new_child(&physfn_pcidev, pcidev, "physfn");
if (r < 0)
return r;
return -errno;
FOREACH_DIRENT_ALL(de, dir, break) {
- _cleanup_free_ char *virtfn_link_file = NULL, *virtfn_pci_syspath = NULL;
- const char *n;
+ _cleanup_(sd_device_unrefp) sd_device *virtfn_pcidev = NULL;
+ const char *n, *s;
n = startswith(de->d_name, "virtfn");
- if (!n)
+ if (isempty(n))
continue;
- virtfn_link_file = path_join(physfn_syspath, de->d_name);
- if (!virtfn_link_file)
- return -ENOMEM;
+ if (sd_device_new_child(&virtfn_pcidev, physfn_pcidev, de->d_name) < 0)
+ continue;
- if (chase_symlinks(virtfn_link_file, NULL, 0, &virtfn_pci_syspath, NULL) < 0)
+ if (sd_device_get_syspath(virtfn_pcidev, &s) < 0)
continue;
- if (streq(syspath, virtfn_pci_syspath)) {
+ if (streq(s, syspath)) {
char *suffix;
suffix = strjoin("v", n);
return 0;
}
+static int dev_devicetree_onboard(sd_device *dev, NetNames *names) {
+ _cleanup_(sd_device_unrefp) sd_device *aliases_dev = NULL, *ofnode_dev = NULL, *devicetree_dev = NULL;
+ const char *alias, *ofnode_path, *ofnode_syspath, *devicetree_syspath;
+ sd_device *parent;
+ int r;
+
+ if (!naming_scheme_has(NAMING_DEVICETREE_ALIASES))
+ return 0;
+
+ /* check if our direct parent has an of_node */
+ r = sd_device_get_parent(dev, &parent);
+ if (r < 0)
+ return r;
+
+ r = sd_device_new_child(&ofnode_dev, parent, "of_node");
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_syspath(ofnode_dev, &ofnode_syspath);
+ if (r < 0)
+ return r;
+
+ /* /proc/device-tree should be a symlink to /sys/firmware/devicetree/base. */
+ r = sd_device_new_from_path(&devicetree_dev, "/proc/device-tree");
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_syspath(devicetree_dev, &devicetree_syspath);
+ if (r < 0)
+ return r;
+
+ /*
+ * Example paths:
+ * devicetree_syspath = /sys/firmware/devicetree/base
+ * ofnode_syspath = /sys/firmware/devicetree/base/soc/ethernet@deadbeef
+ * ofnode_path = soc/ethernet@deadbeef
+ */
+ ofnode_path = path_startswith(ofnode_syspath, devicetree_syspath);
+ if (!ofnode_path)
+ return -ENOENT;
+
+ /* Get back our leading / to match the contents of the aliases */
+ ofnode_path--;
+ assert(path_is_absolute(ofnode_path));
+
+ r = sd_device_new_child(&aliases_dev, devicetree_dev, "aliases");
+ if (r < 0)
+ return r;
+
+ FOREACH_DEVICE_SYSATTR(aliases_dev, alias) {
+ const char *alias_path, *alias_index, *conflict;
+ unsigned i;
+
+ alias_index = startswith(alias, "ethernet");
+ if (!alias_index)
+ continue;
+
+ if (sd_device_get_sysattr_value(aliases_dev, alias, &alias_path) < 0)
+ continue;
+
+ if (!path_equal(ofnode_path, alias_path))
+ continue;
+
+ /* If there's no index, we default to 0... */
+ if (isempty(alias_index)) {
+ i = 0;
+ conflict = "ethernet0";
+ } else {
+ r = safe_atou(alias_index, &i);
+ if (r < 0)
+ return log_device_debug_errno(dev, r,
+ "Could not get index of alias %s: %m", alias);
+ conflict = "ethernet";
+ }
+
+ /* ...but make sure we don't have an alias conflict */
+ if (i == 0 && sd_device_get_sysattr_value(aliases_dev, conflict, NULL) >= 0)
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
+ "Ethernet alias conflict: ethernet and ethernet0 both exist");
+
+ xsprintf(names->devicetree_onboard, "d%u", i);
+ names->type = NET_DEVICETREE;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
static int names_pci(sd_device *dev, const LinkInfo *info, NetNames *names) {
_cleanup_(sd_device_unrefp) sd_device *physfn_pcidev = NULL;
_cleanup_free_ char *virtfn_suffix = NULL;
ieee_oui(dev, &info, test);
}
+ /* get devicetree aliases; only ethernet supported for now */
+ if (streq(prefix, "en") && dev_devicetree_onboard(dev, &names) >= 0 &&
+ names.type == NET_DEVICETREE) {
+ char str[ALTIFNAMSIZ];
+
+ if (snprintf_ok(str, sizeof str, "%s%s", prefix, names.devicetree_onboard))
+ udev_builtin_add_property(dev, test, "ID_NET_NAME_ONBOARD", str);
+ }
+
/* get path names for Linux on System z network devices */
if (names_ccw(dev, &names) >= 0 && names.type == NET_CCW) {
char str[ALTIFNAMSIZ];
log_debug("Unloaded link configuration context.");
}
-static bool builtin_net_setup_link_validate(void) {
- log_debug("Check if link configuration needs reloading.");
+static bool builtin_net_setup_link_should_reload(void) {
if (!ctx)
return false;
- return link_config_should_reload(ctx);
+ if (link_config_should_reload(ctx)) {
+ log_debug("Link configuration context needs reloading.");
+ return true;
+ }
+
+ return false;
}
const UdevBuiltin udev_builtin_net_setup_link = {
.cmd = builtin_net_setup_link,
.init = builtin_net_setup_link_init,
.exit = builtin_net_setup_link_exit,
- .validate = builtin_net_setup_link_validate,
+ .should_reload = builtin_net_setup_link_should_reload,
.help = "Configure network link",
.run_once = false,
};
initialized = false;
}
-bool udev_builtin_validate(void) {
+bool udev_builtin_should_reload(void) {
for (UdevBuiltinCommand i = 0; i < _UDEV_BUILTIN_MAX; i++)
- if (builtins[i] && builtins[i]->validate && builtins[i]->validate())
+ if (builtins[i] && builtins[i]->should_reload && builtins[i]->should_reload())
return true;
return false;
}
const char *help;
int (*init)(void);
void (*exit)(void);
- bool (*validate)(void);
+ bool (*should_reload)(void);
bool run_once;
} UdevBuiltin;
bool udev_builtin_run_once(UdevBuiltinCommand cmd);
int udev_builtin_run(sd_device *dev, sd_netlink **rtnl, UdevBuiltinCommand cmd, const char *command, bool test);
void udev_builtin_list(void);
-bool udev_builtin_validate(void);
+bool udev_builtin_should_reload(void);
int udev_builtin_add_property(sd_device *dev, bool test, const char *key, const char *val);
int udev_builtin_hwdb_lookup(sd_device *dev, const char *prefix, const char *modalias,
const char *filter, bool test);
#include "alloc-util.h"
#include "architecture.h"
#include "conf-files.h"
+#include "conf-parser.h"
#include "def.h"
#include "device-private.h"
#include "device-util.h"
};
struct UdevRules {
- usec_t dirs_ts_usec;
ResolveNameTiming resolve_name_timing;
Hashmap *known_users;
Hashmap *known_groups;
+ Hashmap *stats_by_path;
UdevRuleFile *current_file;
LIST_HEAD(UdevRuleFile, rule_files);
};
hashmap_free_free_key(rules->known_users);
hashmap_free_free_key(rules->known_groups);
+ hashmap_free(rules->stats_by_path);
return mfree(rules);
}
UdevRuleFile *rule_file;
bool ignore_line = false;
unsigned line_nr = 0;
+ struct stat st;
int r;
f = fopen(filename, "re");
if (errno == ENOENT)
return 0;
- return -errno;
+ return log_warning_errno(errno, "Failed to open %s, ignoring: %m", filename);
}
- (void) fd_warn_permissions(filename, fileno(f));
+ if (fstat(fileno(f), &st) < 0)
+ return log_warning_errno(errno, "Failed to stat %s, ignoring: %m", filename);
- if (null_or_empty_fd(fileno(f))) {
+ if (null_or_empty(&st)) {
log_debug("Skipping empty file: %s", filename);
return 0;
}
+ r = hashmap_put_stats_by_path(&rules->stats_by_path, filename, &st);
+ if (r < 0)
+ return log_warning_errno(errno, "Failed to save stat for %s, ignoring: %m", filename);
+
+ (void) fd_warn_permissions(filename, fileno(f));
+
log_debug("Reading rules file: %s", filename);
name = strdup(filename);
if (!rules)
return -ENOMEM;
- (void) udev_rules_check_timestamp(rules);
-
r = conf_files_list_strv(&files, ".rules", NULL, 0, RULES_DIRS);
if (r < 0)
return log_debug_errno(r, "Failed to enumerate rules files: %m");
return 0;
}
-bool udev_rules_check_timestamp(UdevRules *rules) {
+bool udev_rules_should_reload(UdevRules *rules) {
+ _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL;
+ int r;
+
if (!rules)
- return false;
+ return true;
+
+ r = config_get_stats_by_path(".rules", NULL, 0, RULES_DIRS, /* check_dropins = */ false, &stats_by_path);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to get stats of udev rules, ignoring: %m");
+ return true;
+ }
+
+ if (!stats_by_path_equal(rules->stats_by_path, stats_by_path)) {
+ log_debug("Udev rules need reloading");
+ return true;
+ }
- return paths_check_timestamp(RULES_DIRS, &rules->dirs_ts_usec, true);
+ return false;
}
static bool token_match_string(UdevRuleToken *token, const char *str) {
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
+#include "alloc-util.h"
#include "hashmap.h"
#include "time-util.h"
#include "udev-util.h"
int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing);
UdevRules *udev_rules_free(UdevRules *rules);
DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free);
+#define udev_rules_free_and_replace(a, b) free_and_replace_full(a, b, udev_rules_free)
-bool udev_rules_check_timestamp(UdevRules *rules);
+bool udev_rules_should_reload(UdevRules *rules);
int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event,
usec_t timeout_usec,
int timeout_signal,
const char *device,
bool backing) {
- _cleanup_close_ int fd = -1;
- dev_t devt, whole_devt;
- struct stat st;
+ dev_t devt;
int r;
assert(devnos);
assert(*devnos || *n_devnos == 0);
assert(device);
- fd = open(device, O_CLOEXEC|O_PATH);
- if (fd < 0)
- return log_error_errno(errno, "Failed to open '%s': %m", device);
-
- if (fstat(fd, &st) < 0)
- return log_error_errno(errno, "Failed to stat '%s': %m", device);
-
- if (S_ISBLK(st.st_mode))
- devt = st.st_rdev;
- else if (!backing)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Not a block device: %s", device);
- else if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
- return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Not a block device, regular file or directory: %s", device);
- else if (major(st.st_dev) != 0)
- devt = st.st_dev;
- else {
- _cleanup_close_ int regfd = -1;
-
- /* If major(st.st_dev) is zero, this might mean we are backed by btrfs, which needs special
- * handing, to get the backing device node. */
-
- regfd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
- if (regfd < 0)
- return log_error_errno(regfd, "Failed to open '%s': %m", device);
-
- r = btrfs_get_block_device_fd(regfd, &devt);
- if (r == -ENOTTY)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Path '%s' not backed by block device.", device);
- if (r < 0)
- return log_error_errno(r, "Failed to acquire btrfs backing device of '%s': %m", device);
- }
-
- r = block_get_whole_disk(devt, &whole_devt);
+ r = path_get_whole_disk(device, backing, &devt);
if (r < 0)
return log_error_errno(r, "Failed to find whole block device for '%s': %m", device);
- if (typesafe_bsearch(&whole_devt, *devnos, *n_devnos, devt_compare_func)) {
- log_debug("Device %u:%u already listed for locking, ignoring.", major(whole_devt), minor(whole_devt));
+ if (typesafe_bsearch(&devt, *devnos, *n_devnos, devt_compare_func)) {
+ log_debug("Device %u:%u already listed for locking, ignoring.", major(devt), minor(devt));
return 0;
}
if (!GREEDY_REALLOC(*devnos, *n_devnos + 1))
return log_oom();
- (*devnos)[(*n_devnos)++] = whole_devt;
+ (*devnos)[(*n_devnos)++] = devt;
/* Immediately sort again, to ensure the binary search above will work for the next device we add */
typesafe_qsort(*devnos, *n_devnos, devt_compare_func);
}
/* reload requested, HUP signal received, rules changed, builtin changed */
-static void manager_reload(Manager *manager) {
+static void manager_reload(Manager *manager, bool force) {
+ _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
+ usec_t now_usec;
+ int r;
+
assert(manager);
+ assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &now_usec) >= 0);
+ if (!force && now_usec < usec_add(manager->last_usec, 3 * USEC_PER_SEC))
+ /* check for changed config, every 3 seconds at most */
+ return;
+ manager->last_usec = now_usec;
+
+ /* Reload SELinux label database, to make the child inherit the up-to-date database. */
+ mac_selinux_maybe_reload();
+
+ /* Nothing changed. It is not necessary to reload. */
+ if (!udev_rules_should_reload(manager->rules) && !udev_builtin_should_reload())
+ return;
+
sd_notify(false,
"RELOADING=1\n"
"STATUS=Flushing configuration...");
manager_kill_workers(manager, false);
- manager->rules = udev_rules_free(manager->rules);
+
udev_builtin_exit();
+ udev_builtin_init();
+
+ r = udev_rules_load(&rules, arg_resolve_name_timing);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read udev rules, using the previously loaded rules, ignoring: %m");
+ else
+ udev_rules_free_and_replace(manager->rules, rules);
notify_ready();
}
if (r < 0)
return r;
+ (void) sd_device_monitor_set_description(worker_monitor, "worker");
+
/* allow the main daemon netlink address to send devices to the worker */
r = device_monitor_allow_unicast_sender(worker_monitor, manager->monitor);
if (r < 0)
}
static int event_queue_start(Manager *manager) {
- usec_t usec;
int r;
assert(manager);
if (!manager->events || manager->exit || manager->stop_exec_queue)
return 0;
- assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &usec) >= 0);
- /* check for changed config, every 3 seconds at most */
- if (manager->last_usec == 0 ||
- usec > usec_add(manager->last_usec, 3 * USEC_PER_SEC)) {
- if (udev_rules_check_timestamp(manager->rules) ||
- udev_builtin_validate())
- manager_reload(manager);
-
- manager->last_usec = usec;
- }
-
r = event_source_disable(manager->kill_workers_event);
if (r < 0)
log_warning_errno(r, "Failed to disable event source for cleaning up idle workers, ignoring: %m");
- udev_builtin_init();
-
- if (!manager->rules) {
- r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
- if (r < 0)
- return log_warning_errno(r, "Failed to read udev rules: %m");
- }
-
- /* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
- * and, until the next SELinux policy changes, we safe further reloads in future children */
- mac_selinux_maybe_reload();
+ manager_reload(manager, /* force = */ false);
LIST_FOREACH(event, event, manager->events) {
if (event->state != EVENT_QUEUED)
case UDEV_CTRL_START_EXEC_QUEUE:
log_debug("Received udev control message (START_EXEC_QUEUE)");
manager->stop_exec_queue = false;
- event_queue_start(manager);
+ /* It is not necessary to call event_queue_start() here, as it will be called in on_post() if necessary. */
break;
case UDEV_CTRL_RELOAD:
log_debug("Received udev control message (RELOAD)");
- manager_reload(manager);
+ manager_reload(manager, /* force = */ true);
break;
case UDEV_CTRL_SET_ENV: {
_unused_ _cleanup_free_ char *old_val = NULL;
assert(manager);
- manager_reload(manager);
+ manager_reload(manager, /* force = */ true);
return 1;
}
if (!hashmap_isempty(manager->workers)) {
/* There are idle workers */
- (void) event_reset_time(manager->event, &manager->kill_workers_event, CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + 3 * USEC_PER_SEC, USEC_PER_SEC,
- on_kill_workers_event, manager, 0, "kill-workers-event", false);
+ (void) event_reset_time_relative(manager->event, &manager->kill_workers_event,
+ CLOCK_MONOTONIC, 3 * USEC_PER_SEC, USEC_PER_SEC,
+ on_kill_workers_event, manager,
+ 0, "kill-workers-event", false);
return 1;
}
log_warning_errno(r, "Failed to set receive buffer size for device monitor, ignoring: %m");
}
+ (void) sd_device_monitor_set_description(manager->monitor, "manager");
+
r = device_monitor_enable_receiving(manager->monitor);
if (r < 0)
return log_error_errno(r, "Failed to bind netlink socket: %m");
if (r < 0)
return log_error_errno(r, "Failed to create post event source: %m");
+ manager->last_usec = now(CLOCK_MONOTONIC);
+
udev_builtin_init();
r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
- if (!manager->rules)
+ if (r < 0)
return log_error_errno(r, "Failed to read udev rules: %m");
r = udev_rules_apply_static_dev_perms(manager->rules);
if (r < 0)
- log_error_errno(r, "Failed to apply permissions on static device nodes: %m");
+ log_warning_errno(r, "Failed to apply permissions on static device nodes, ignoring: %m");
notify_ready();
. "${TEST_BASE_DIR:?}/test-functions"
test_append_files() {
- image_install -o evemu-device evemu-event crond crontab
+ image_install -o evemu-device evemu-event
}
do_test "$@"
set -e
TEST_DESCRIPTION="test credentials"
-NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} --set-credential=mynspawncredential:strangevalue"
-QEMU_OPTIONS="${QEMU_OPTIONS:-} -fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue"
-KERNEL_APPEND="${KERNEL_APPEND:-} systemd.set_credential=kernelcmdlinecred:uff rd.systemd.import_credentials=no"
+
+NSPAWN_CREDS=(
+ "--set-credential=mynspawncredential:strangevalue"
+)
+NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} ${NSPAWN_CREDS[*]}"
+
+QEMU_CREDS=(
+ "-fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue"
+ "-smbios type=11,value=io.systemd.credential:smbioscredential=magicdata"
+ "-smbios type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh"
+ "-smbios type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK"
+ "-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg=="
+)
+QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}"
+
+KERNEL_CREDS=(
+ "systemd.set_credential=kernelcmdlinecred:uff"
+ "systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest"
+ "systemd.set_credential=login.motd:hello"
+ "systemd.set_credential=login.issue:welcome"
+ "rd.systemd.import_credentials=no"
+)
+KERNEL_APPEND="${KERNEL_APPEND:-} ${KERNEL_CREDS[*]}"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
install_dmevent
generate_module_dependencies
inst_binary tpm2_pcrextend
-
- # On Ubuntu, cryptsetup does not link against libgcc_s.so.1
- if get_bool "$LOOKS_LIKE_DEBIAN"; then
- inst_library "/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/libgcc_s.so.1"
- fi
}
TEST_70_TPM_DEVICE="tpm-tis"
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+all setup run clean clean-again:
+ @TEST_BASE_DIR=../ ./test.sh --$@
+
+.PHONY: all setup run clean clean-again
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+TEST_DESCRIPTION="Sysctl-related tests"
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+do_test "$@"
[Match]
+Architecture=
+Credential=
+Driver=
+Firmware=
+Host=
+KernelCommandLine=
+KernelVersion=
+Kind=
MACAddress=
-PermanentMACAddress=
OriginalName=
Path=
-Driver=
-Type=
-Kind=
+PermanentMACAddress=
Property=
-Host=
+Type=
Virtualization=
-KernelCommandLine=
-KernelVersion=
-Architecture=
-Firmware=
[Link]
Description=
MACAddressPolicy=
SourceMACAddress=
[Match]
Architecture=
+Credential=
Firmware=
Host=
+KernelCommandLine=
KernelVersion=
Virtualization=
-KernelCommandLine=
[GENEVE]
DestinationPort=
TTL=
Group=
PacketInfo=
VNetHeader=
+KeepCarrier=
[IPVLAN]
Mode=
Flags=
VNetHeader=
Group=
User=
+KeepCarrier=
[NetDev]
Kind=
MACAddress=
ProxyARPWiFi=
MulticastRouter=
[Match]
-KernelVersion=
-Type=
-Kind=
-Driver=
Architecture=
-Firmware=
-Path=
-WLANInterfaceType=
-SSID=
BSSID=
-Name=
-Property=
-Virtualization=
-KernelCommandLine=
+Credential=
+Driver=
+Firmware=
Host=
+KernelCommandLine=
+KernelVersion=
+Kind=
MACAddress=
+Name=
+Path=
PermanentMACAddress=
+Property=
+SSID=
+Type=
+Virtualization=
+WLANInterfaceType=
[Link]
ActivationPolicy=
RequiredForOnline=
L2TP=
MACsec=
LinkLocalAddressing=
+IPv4LLStartAddress=
IPv6LinkLocalAddressGenerationMode=
IPv6StableSecretAddress=
ConfigureWithoutCarrier=
AmbientCapabilities=
AssertACPower=
AssertArchitecture=
+AssertCPUPressure=
AssertCapability=
AssertControlGroupController=
-AssertCPUPressure=
+AssertCredential=
AssertDirectoryNotEmpty=
AssertFileIsExecutable=
AssertFileNotEmpty=
ConditionArchitecture=
ConditionCapability=
ConditionControlGroupController=
+ConditionCredential=
ConditionCPUPressure=
ConditionDirectoryNotEmpty=
ConditionFileIsExecutable=
InputKey=
InvertRule=
KernelCommandLine=
+Credential=
KernelVersion=
Key=
Kind=
AssertCPUs=
AssertCapability=
AssertControlGroupController=
+AssertCredential=
AssertDirectoryNotEmpty=
AssertEnvironment=
AssertFileIsExecutable=
ConditionFirmware=
ConditionCapability=
ConditionControlGroupController=
+ConditionCredential=
ConditionDirectoryNotEmpty=
ConditionEnvironment=
ConditionFileIsExecutable=
[Partitions]
RootSize=3G
-[Packages]
+[Content]
BuildPackages=
audit-libs-devel
bzip2-devel
self.assertEqual(self.read_attr('port2', 'brport/path_cost'), '555')
self.assertEqual(self.read_attr('port2', 'brport/multicast_fast_leave'), '1')
self.assertEqual(self.read_attr('port2', 'brport/unicast_flood'), '1')
- self.assertEqual(self.read_attr('port2', 'brport/bpdu_guard'), '1')
- self.assertEqual(self.read_attr('port2', 'brport/root_block'), '1')
+ self.assertEqual(self.read_attr('port2', 'brport/bpdu_guard'), '0')
+ self.assertEqual(self.read_attr('port2', 'brport/root_block'), '0')
class ClientTestBase(NetworkdTestingUtilities):
"""Provide common methods for testing networkd against servers."""
chmod +x "$initdir/opt/script1.sh"
echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file"
mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw" -noappend
+
+ export initdir="$TESTDIR/app-nodistro"
+ mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system"
+ ( echo "ID=_any"
+ echo "ARCHITECTURE=_any" ) >"$initdir/usr/lib/extension-release.d/extension-release.app-nodistro"
+ echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/app-nodistro.raw" -noappend
)
}
# Install the library itself and create necessary symlinks
inst_library "$file"
done < <(find /lib*/multipath -type f)
-
- if get_bool "$LOOKS_LIKE_ARCH"; then
- # On Arch the multipath libraries are not linked against libgcc_s.so.1,
- # but it's still required at runtime
- inst_library "/lib64/libgcc_s.so.1"
- fi
}
install_lvm() {
LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath "$i")" inst_libs "$i"
done
+ # Install libgcc_s.so if available, since it's dlopen()ed by libpthread
+ # and might cause unexpected failures during pthread_exit()/pthread_cancel()
+ # if not present
+ # See: https://github.com/systemd/systemd/pull/23858
+ while read -r libgcc_s; do
+ [[ -e "$libgcc_s" ]] && inst_library "$libgcc_s"
+ done < <(ldconfig -p | awk '/\/libgcc_s.so.1$/ { print $4 }')
+
local lib path
# A number of dependencies is now optional via dlopen, so the install
# script will not pick them up, since it looks at linkage.
inst_simple "$reallib" "$reallib"
inst_dir "${dest%/*}"
[[ -d "${dest%/*}" ]] && dest="$(readlink -f "${dest%/*}")/${dest##*/}"
+ ddebug "Creating symlink $reallib -> $dest"
ln -sfn -- "$(convert_abs_rel "${dest}" "${reallib}")" "${initdir}/${dest}"
else
inst_simple "$src" "$dest"
[Match]
Name=bridge99
+[Link]
+MTUBytes=9000
+
[Network]
Address=192.168.0.15/24
Gateway=192.168.0.1
DHCP=ipv4
LinkLocalAddressing=yes
IPv6AcceptRA=no
+IPv4LLStartAddress=169.254.133.11
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-[QDisc]
-Parent=clsact
-
-[HierarchyTokenBucket]
-Parent=root
-Handle=0002
-DefaultClass=30
-RateToQuantum=20
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0030
-Priority=1
-QuantumBytes=4000
-MTUBytes=1700
-OverheadBytes=100
-Rate=1M
-BufferBytes=123456
-CeilRate=0.5M
-CeilBufferBytes=123457
-
-[NetworkEmulator]
-Parent=2:30
-Handle=0030
-DelaySec=50ms
-DelayJitterSec=10ms
-LossRate=20%
-PacketLimit=100
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0031
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[TrivialLinkEqualizer]
-Parent=2:31
-Handle=0031
-Id=1
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0032
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[FairQueueing]
-Parent=2:32
-Handle=0032
-PacketLimit=1000
-FlowLimit=200
-QuantumBytes=1500
-InitialQuantumBytes=13000
-MaximumRate=1M
-Buckets=512
-OrphanMask=511
-Pacing=yes
-CEThresholdSec=100ms
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0033
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[ControlledDelay]
-Parent=2:33
-Handle=0033
-PacketLimit=2000
-TargetSec=10ms
-IntervalSec=50ms
-ECN=yes
-CEThresholdSec=100ms
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0034
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[FairQueueingControlledDelay]
-Parent=2:34
-Handle=0034
-PacketLimit=20480
-MemoryLimitBytes=64M
-Flows=2048
-TargetSec=10ms
-IntervalSec=200ms
-QuantumBytes=1400
-ECN=yes
-CEThresholdSec=100ms
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0035
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[TokenBucketFilter]
-Parent=2:35
-Handle=0035
-Rate=1G
-BurstBytes=5000
-LatencySec=70msec
-PeakRate=100G
-MTUBytes=1000000
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0036
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[StochasticFairnessQueueing]
-Parent=2:36
-Handle=0036
-PerturbPeriodSec=5sec
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0037
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[PFIFO]
-Parent=2:37
-Handle=0037
-PacketLimit=100000
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0038
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[GenericRandomEarlyDetection]
-Parent=2:38
-Handle=0038
-VirtualQueues=12
-DefaultVirtualQueue=10
-GenericRIO=yes
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:0039
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[StochasticFairBlue]
-Parent=2:39
-Handle=0039
-PacketLimit=200000
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:003a
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[BFIFO]
-Parent=2:3a
-Handle=003a
-LimitBytes=1000000
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:003b
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[PFIFOHeadDrop]
-Parent=2:3b
-Handle=003b
-PacketLimit=1023
-
-[HierarchyTokenBucketClass]
-Parent=root
-ClassId=0002:003c
-Priority=1
-Rate=1M
-CeilRate=0.5M
-
-[PFIFOFast]
-Parent=2:3c
-Handle=003c
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[QDisc]
+Parent=clsact
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[ControlledDelay]
+Parent=root
+Handle=0033
+PacketLimit=2000
+TargetSec=10ms
+IntervalSec=50ms
+ECN=yes
+CEThresholdSec=100ms
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[FairQueueing]
+Parent=root
+Handle=0032
+PacketLimit=1000
+FlowLimit=200
+QuantumBytes=1500
+InitialQuantumBytes=13000
+MaximumRate=1M
+Buckets=512
+OrphanMask=511
+Pacing=yes
+CEThresholdSec=100ms
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[FairQueueingControlledDelay]
+Parent=root
+Handle=0034
+PacketLimit=20480
+MemoryLimitBytes=64M
+Flows=2048
+TargetSec=10ms
+IntervalSec=200ms
+QuantumBytes=1400
+ECN=yes
+CEThresholdSec=100ms
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[GenericRandomEarlyDetection]
+Parent=root
+Handle=0038
+VirtualQueues=12
+DefaultVirtualQueue=10
+GenericRIO=yes
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[HierarchyTokenBucket]
+Parent=root
+Handle=0002
+DefaultClass=30
+RateToQuantum=20
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0037
+Priority=1
+Rate=1M
+CeilRate=0.5M
+QuantumBytes=4000
+MTUBytes=1700
+OverheadBytes=100
+BufferBytes=123456
+CeilBufferBytes=123457
+
+[PFIFO]
+Parent=2:37
+Handle=0037
+PacketLimit=100000
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:003a
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[BFIFO]
+Parent=2:3a
+Handle=003a
+LimitBytes=1000000
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:003b
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[PFIFOHeadDrop]
+Parent=2:3b
+Handle=003b
+PacketLimit=1023
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:003c
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[PFIFOFast]
+Parent=2:3c
+Handle=003c
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.4/16
+
+[QDisc]
+Parent=ingress
NetworkEmulatorDelayJitterSec=10ms
NetworkEmulatorLossRate=20%
NetworkEmulatorPacketLimit=100
-
-[TrafficControlQueueingDiscipline]
-Parent=ingress
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[NetworkEmulator]
+Parent=root
+Handle=0030
+DelaySec=50ms
+DelayJitterSec=10ms
+LossRate=20%
+PacketLimit=100
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
-Name=test1
+Name=dummy98
[Network]
IPv6AcceptRA=no
-Address=10.1.2.4/16
+Address=10.1.2.3/16
[QuickFairQueueing]
Parent=root
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[StochasticFairBlue]
+Parent=root
+Handle=0039
+PacketLimit=200000
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[StochasticFairnessQueueing]
+Parent=root
+Handle=0036
+PerturbPeriodSec=5sec
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[TokenBucketFilter]
+Parent=root
+Handle=0035
+Rate=1G
+BurstBytes=5000
+LatencySec=70msec
+PeakRate=100G
+MTUBytes=1000000
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[TrivialLinkEqualizer]
+Parent=root
+Handle=0031
+Id=1
# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
-Name=tap99
+Name=testtap99
Kind=tap
[Tap]
MultiQueue=true
PacketInfo=true
VNetHeader=true
+KeepCarrier=yes
# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
-Name=tun99
+Name=testtun99
Kind=tun
[Tun]
MultiQueue=true
PacketInfo=true
VNetHeader=true
+KeepCarrier=yes
[Network]
Address=192.168.124.1/24
+Address=fe80::1/64
+Address=169.254.11.1/24
IPv6AcceptRA=no
[Match]
Name=dummy98
+[Link]
+MTUBytes=9000
+
[Network]
Bridge=bridge99
[Match]
Name=test1
+[Link]
+MTUBytes=9000
+
[Network]
Bridge=bridge99
Name=xfrm98 xfrm99
Name=vxlan98
Name=hogehogehogehogehogehoge
+Name=testtun99
+Name=testtap99
[Network]
LinkLocalAddressing=yes
import itertools
import os
import pathlib
+import psutil
import re
import shutil
import signal
networkd_bin = shutil.which('systemd-networkd', path=which_paths)
resolved_bin = shutil.which('systemd-resolved', path=which_paths)
+timesyncd_bin = shutil.which('systemd-timesyncd', path=which_paths)
udevd_bin = shutil.which('systemd-udevd', path=which_paths)
wait_online_bin = shutil.which('systemd-networkd-wait-online', path=which_paths)
networkctl_bin = shutil.which('networkctl', path=which_paths)
resolvectl_bin = shutil.which('resolvectl', path=which_paths)
timedatectl_bin = shutil.which('timedatectl', path=which_paths)
+udevadm_bin = shutil.which('udevadm', path=which_paths)
use_valgrind = False
+valgrind_cmd = ''
enable_debug = True
env = {}
wait_online_env = {}
def touch(path):
pathlib.Path(path).touch()
-def check_output(*command, text=True, **kwargs):
+def check_output(*command, **kwargs):
# This checks the result and returns stdout (and stderr) on success.
command = command[0].split() + list(command[1:])
- return subprocess.run(command, check=True, universal_newlines=text, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs).stdout.rstrip()
-
-def call(*command, text=True, **kwargs):
+ ret = subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
+ if ret.returncode == 0:
+ return ret.stdout.rstrip()
+ # When returncode != 0, print stdout and stderr, then trigger CalledProcessError.
+ print(ret.stdout)
+ ret.check_returncode()
+
+def call(*command, **kwargs):
# This returns returncode. stdout and stderr are merged and shown in console
command = command[0].split() + list(command[1:])
- return subprocess.run(command, check=False, universal_newlines=text, stderr=subprocess.STDOUT, **kwargs).returncode
+ return subprocess.run(command, check=False, universal_newlines=True, stderr=subprocess.STDOUT, **kwargs).returncode
-def call_quiet(*command, text=True, **kwargs):
+def call_quiet(*command, **kwargs):
command = command[0].split() + list(command[1:])
- return subprocess.run(command, check=False, universal_newlines=text, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs).returncode
+ return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, **kwargs).returncode
-def run(*command, text=True, **kwargs):
+def run(*command, **kwargs):
# This returns CompletedProcess instance.
command = command[0].split() + list(command[1:])
- return subprocess.run(command, check=False, universal_newlines=text, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
+ return subprocess.run(command, check=False, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
-def is_module_available(module_name):
- lsmod_output = check_output('lsmod')
- module_re = re.compile(rf'^{re.escape(module_name)}\b', re.MULTILINE)
- return module_re.search(lsmod_output) or call_quiet('modprobe', module_name) == 0
+def is_module_available(*module_names):
+ for module_name in module_names:
+ lsmod_output = check_output('lsmod')
+ module_re = re.compile(rf'^{re.escape(module_name)}\b', re.MULTILINE)
+ if not module_re.search(lsmod_output) and call_quiet('modprobe', module_name) != 0:
+ return False
+ return True
-def expectedFailureIfModuleIsNotAvailable(module_name):
+def expectedFailureIfModuleIsNotAvailable(*module_names):
def f(func):
- return func if is_module_available(module_name) else unittest.expectedFailure(func)
+ return func if is_module_available(*module_names) else unittest.expectedFailure(func)
return f
return f
-def expectedFailureIfCAKEIsNotAvailable():
- def f(func):
- call_quiet('ip link add dummy98 type dummy')
- rc = call_quiet('tc qdisc add dev dummy98 parent root cake')
- remove_link('dummy98')
- return func if rc == 0 else unittest.expectedFailure(func)
-
- return f
-
-def expectedFailureIfPIEIsNotAvailable():
- def f(func):
- call_quiet('ip link add dummy98 type dummy')
- rc = call_quiet('tc qdisc add dev dummy98 parent root pie')
- remove_link('dummy98')
- return func if rc == 0 else unittest.expectedFailure(func)
-
- return f
-
-def expectedFailureIfHHFIsNotAvailable():
- def f(func):
- call_quiet('ip link add dummy98 type dummy')
- rc = call_quiet('tc qdisc add dev dummy98 parent root hhf')
- remove_link('dummy98')
- return func if rc == 0 else unittest.expectedFailure(func)
-
- return f
-
-def expectedFailureIfETSIsNotAvailable():
- def f(func):
- call_quiet('ip link add dummy98 type dummy')
- rc = call_quiet('tc qdisc add dev dummy98 parent root ets bands 10')
- remove_link('dummy98')
- return func if rc == 0 else unittest.expectedFailure(func)
-
- return f
-
-def expectedFailureIfFQPIEIsNotAvailable():
- def f(func):
- call_quiet('ip link add dummy98 type dummy')
- rc = call_quiet('tc qdisc add dev dummy98 parent root fq_pie')
- remove_link('dummy98')
- return func if rc == 0 else unittest.expectedFailure(func)
-
- return f
+def udev_reload():
+ check_output(*udevadm_cmd, 'control', '--reload')
def copy_network_unit(*units, copy_dropins=True):
"""
When a drop-in file is specified, its unit file is also copied in automatically.
"""
+ has_link = False
mkdir_p(network_unit_dir)
for unit in units:
if copy_dropins and os.path.exists(os.path.join(networkd_ci_temp_dir, unit + '.d')):
cp_r(os.path.join(networkd_ci_temp_dir, unit + '.d'), os.path.join(network_unit_dir, unit + '.d'))
+
if unit.endswith('.conf'):
dropin = unit
unit = os.path.dirname(dropin).rstrip('.d')
dropindir = os.path.join(network_unit_dir, unit + '.d')
mkdir_p(dropindir)
cp(os.path.join(networkd_ci_temp_dir, dropin), dropindir)
+
cp(os.path.join(networkd_ci_temp_dir, unit), network_unit_dir)
+ if unit.endswith('.link'):
+ has_link = True
+
+ if has_link:
+ udev_reload()
+
def remove_network_unit(*units):
"""
Remove previously copied unit files from the testbed.
Drop-ins will be removed automatically.
"""
+ has_link = False
for unit in units:
rm_f(os.path.join(network_unit_dir, unit))
rm_rf(os.path.join(network_unit_dir, unit + '.d'))
+ if unit.endswith('.link') or unit.endswith('.link.d'):
+ has_link = True
+
+ if has_link:
+ udev_reload()
+
def clear_network_units():
+ has_link = False
+ if os.path.exists(network_unit_dir):
+ units = os.listdir(network_unit_dir)
+ for unit in units:
+ if unit.endswith('.link') or unit.endswith('.link.d'):
+ has_link = True
+
rm_rf(network_unit_dir)
+ if has_link:
+ udev_reload()
+
def copy_networkd_conf_dropin(*dropins):
"""Copy networkd.conf dropin files into the testbed."""
mkdir_p(networkd_conf_dropin_dir)
rm_rf(udev_rules_dir)
def save_active_units():
- for u in ['systemd-udevd-kernel.socket', 'systemd-udevd-control.socket', 'systemd-udevd.service',
- 'systemd-networkd.socket', 'systemd-networkd.service',
- 'systemd-resolved.service',
+ for u in ['systemd-networkd.socket', 'systemd-networkd.service',
+ 'systemd-resolved.service', 'systemd-timesyncd.service',
'firewalld.service']:
if call(f'systemctl is-active --quiet {u}') == 0:
call(f'systemctl stop {u}')
def restore_active_units():
if 'systemd-networkd.socket' in active_units:
call('systemctl stop systemd-networkd.socket systemd-networkd.service')
- if 'systemd-udevd-kernel.socket' in active_units or 'systemd-udevd-control.socket' in active_units:
- call('systemctl stop systemd-udevd-kernel.socket systemd-udevd-control.socket systemd-udevd.service')
for u in active_units:
call(f'systemctl restart {u}')
+def create_unit_dropin(unit, contents):
+ mkdir_p(f'/run/systemd/system/{unit}.d')
+ with open(f'/run/systemd/system/{unit}.d/00-override.conf', mode='w', encoding='utf-8') as f:
+ f.write('\n'.join(contents))
+
+def create_service_dropin(service, command, reload_command=None, additional_settings=None):
+ drop_in = [
+ '[Service]',
+ 'ExecStart=',
+ f'ExecStart=!!{valgrind_cmd}{command}',
+ ]
+ if reload_command:
+ drop_in += [
+ 'ExecReload=',
+ f'ExecReload={valgrind_cmd}{reload_command}',
+ ]
+ if enable_debug:
+ drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
+ if asan_options:
+ drop_in += [f'Environment=ASAN_OPTIONS="{asan_options}"']
+ if lsan_options:
+ drop_in += [f'Environment=LSAN_OPTIONS="{lsan_options}"']
+ if ubsan_options:
+ drop_in += [f'Environment=UBSAN_OPTIONS="{ubsan_options}"']
+ if asan_options or lsan_options or ubsan_options:
+ drop_in += ['SystemCallFilter=']
+ if use_valgrind or asan_options or lsan_options or ubsan_options:
+ drop_in += ['MemoryDenyWriteExecute=no']
+ if use_valgrind:
+ drop_in += [
+ 'Environment=SYSTEMD_MEMPOOL=0',
+ 'PrivateTmp=yes',
+ ]
+ if with_coverage:
+ drop_in += [
+ 'ProtectSystem=no',
+ 'ProtectHome=no',
+ ]
+ if additional_settings:
+ drop_in += additional_settings
+
+ create_unit_dropin(f'{service}.service', drop_in)
+
def link_exists(link):
return os.path.exists(os.path.join('/sys/class/net', link, 'ifindex'))
def flush_l2tp_tunnels():
tids = []
- output = check_output('ip l2tp show tunnel')
- for line in output.splitlines():
+ ret = run('ip l2tp show tunnel')
+ if ret.returncode != 0:
+ return # l2tp may not be supported
+ for line in ret.stdout.splitlines():
words = line.split()
if words[0] == 'Tunnel':
tid = words[1].rstrip(',')
def save_timezone():
global saved_timezone
- r = run('timedatectl show --value --property Timezone')
+ r = run(*timedatectl_cmd, 'show', '--value', '--property', 'Timezone', env=env)
if r.returncode == 0:
saved_timezone = r.stdout.rstrip()
print(f'### Saved timezone: {saved_timezone}')
def restore_timezone():
if saved_timezone:
- call(f'timedatectl set-timezone {saved_timezone}')
+ call(*timedatectl_cmd, 'set-timezone', f'{saved_timezone}', env=env)
def read_link_attr(*args):
with open(os.path.join('/sys/class/net', *args), encoding='utf-8') as f:
check_output('systemctl start systemd-networkd')
def restart_networkd(show_logs=True):
- stop_networkd(show_logs)
- start_networkd()
+ if show_logs:
+ invocation_id = check_output('systemctl show systemd-networkd.service -p InvocationID --value')
+ check_output('systemctl restart systemd-networkd.service')
+ if show_logs:
+ print(check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id))
+
+def networkd_pid():
+ return int(check_output('systemctl show --value -p MainPID systemd-networkd.service'))
def networkctl_reconfigure(*links):
check_output(*networkctl_cmd, 'reconfigure', *links, env=env)
flush_links()
# 5. stop networkd
- stop_networkd(show_logs=True)
+ stop_networkd()
# 6. remove configs
clear_network_units()
save_routing_policy_rules()
save_timezone()
- drop_in = [
- '[Unit]',
- 'StartLimitIntervalSec=0',
- '[Service]',
- 'Restart=no',
- 'ExecStart=',
- 'ExecReload=',
- ]
- if use_valgrind:
- drop_in += [
- 'ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + networkd_bin,
- f'ExecReload=valgrind {networkctl_bin} reload',
- 'PrivateTmp=yes'
- ]
- else:
- drop_in += [
- 'ExecStart=!!' + networkd_bin,
- f'ExecReload={networkctl_bin} reload',
- ]
- if enable_debug:
- drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
- if asan_options:
- drop_in += ['Environment=ASAN_OPTIONS="' + asan_options + '"']
- if lsan_options:
- drop_in += ['Environment=LSAN_OPTIONS="' + lsan_options + '"']
- if ubsan_options:
- drop_in += ['Environment=UBSAN_OPTIONS="' + ubsan_options + '"']
- if asan_options or lsan_options or ubsan_options:
- drop_in += ['SystemCallFilter=']
- if use_valgrind or asan_options or lsan_options or ubsan_options:
- drop_in += ['MemoryDenyWriteExecute=no']
- if with_coverage:
- drop_in += [
- 'ProtectSystem=no',
- 'ProtectHome=no',
+ create_service_dropin('systemd-networkd', networkd_bin,
+ f'{networkctl_bin} reload',
+ ['[Service]', 'Restart=no', '[Unit]', 'StartLimitIntervalSec=0'])
+ create_service_dropin('systemd-resolved', resolved_bin)
+ create_service_dropin('systemd-timesyncd', timesyncd_bin)
+
+ # TODO: also run udevd with sanitizers, valgrind, or coverage
+ #create_service_dropin('systemd-udevd', udevd_bin,
+ # f'{udevadm_bin} control --reload --timeout 0')
+ create_unit_dropin(
+ 'systemd-udevd.service',
+ [
+ '[Service]',
+ 'ExecStart=',
+ f'ExecStart=!!{udevd_bin}',
+ 'ExecReload=',
+ f'ExecReload={udevadm_bin} control --reload --timeout 0',
]
-
- mkdir_p('/run/systemd/system/systemd-networkd.service.d')
- with open('/run/systemd/system/systemd-networkd.service.d/00-override.conf', mode='w', encoding='utf-8') as f:
- f.write('\n'.join(drop_in))
-
- drop_in = [
- '[Service]',
- 'Restart=no',
- 'ExecStart=',
- ]
- if use_valgrind:
- drop_in += ['ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + resolved_bin]
- else:
- drop_in += ['ExecStart=!!' + resolved_bin]
- if enable_debug:
- drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
- if asan_options:
- drop_in += ['Environment=ASAN_OPTIONS="' + asan_options + '"']
- if lsan_options:
- drop_in += ['Environment=LSAN_OPTIONS="' + lsan_options + '"']
- if ubsan_options:
- drop_in += ['Environment=UBSAN_OPTIONS="' + ubsan_options + '"']
- if asan_options or lsan_options or ubsan_options:
- drop_in += ['SystemCallFilter=']
- if use_valgrind or asan_options or lsan_options or ubsan_options:
- drop_in += ['MemoryDenyWriteExecute=no']
- if with_coverage:
- drop_in += [
- 'ProtectSystem=no',
- 'ProtectHome=no',
+ )
+ create_unit_dropin(
+ 'systemd-networkd.socket',
+ [
+ '[Unit]',
+ 'StartLimitIntervalSec=0',
]
-
- mkdir_p('/run/systemd/system/systemd-resolved.service.d')
- with open('/run/systemd/system/systemd-resolved.service.d/00-override.conf', mode='w', encoding='utf-8') as f:
- f.write('\n'.join(drop_in))
-
- drop_in = [
- '[Service]',
- 'ExecStart=',
- 'ExecStart=!!' + udevd_bin,
- ]
-
- mkdir_p('/run/systemd/system/systemd-udevd.service.d')
- with open('/run/systemd/system/systemd-udevd.service.d/00-override.conf', mode='w', encoding='utf-8') as f:
- f.write('\n'.join(drop_in))
+ )
check_output('systemctl daemon-reload')
print(check_output('systemctl cat systemd-networkd.service'))
print(check_output('systemctl cat systemd-resolved.service'))
+ print(check_output('systemctl cat systemd-timesyncd.service'))
print(check_output('systemctl cat systemd-udevd.service'))
check_output('systemctl restart systemd-resolved.service')
+ check_output('systemctl restart systemd-timesyncd.service')
check_output('systemctl restart systemd-udevd.service')
def tearDownModule():
restore_timezone()
rm_rf('/run/systemd/system/systemd-networkd.service.d')
+ rm_rf('/run/systemd/system/systemd-networkd.socket.d')
rm_rf('/run/systemd/system/systemd-resolved.service.d')
+ rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
rm_rf('/run/systemd/system/systemd-udevd.service.d')
check_output('systemctl daemon-reload')
+ check_output('systemctl restart systemd-udevd.service')
restore_active_units()
class Utilities():
try:
check_output(*args, env=wait_online_env)
except subprocess.CalledProcessError as e:
- print(e.stdout) # show logs only on failure
+ # show detailed status on failure
for link in links_with_operstate:
name = link.split(':')[0]
if link_exists(name):
@expectedFailureIfAlternativeNameIsNotAvailable()
def test_altname(self):
copy_network_unit('26-netdev-link-local-addressing-yes.network', '12-dummy.netdev', '12-dummy.link')
- check_output('udevadm control --reload')
start_networkd()
self.wait_online(['dummy98:degraded'])
self.assertRegex(output, 'link/ether 12:34:56:78:9a:bf')
self.assertRegex(output, 'mtu 1800')
- def test_tun(self):
- copy_network_unit('25-tun.netdev')
+ def test_tuntap(self):
+ copy_network_unit('25-tun.netdev', '25-tap.netdev', '26-netdev-link-local-addressing-yes.network')
start_networkd()
- self.wait_online(['tun99:off'], setup_state='unmanaged')
+ self.wait_online(['testtun99:degraded', 'testtap99:degraded'])
+
+ pid = networkd_pid()
+ name = psutil.Process(pid).name()[:15]
+
+ output = check_output('ip -d tuntap show')
+ print(output)
+ self.assertRegex(output, f'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
+ self.assertRegex(output, f'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
- output = check_output('ip -d link show tun99')
+ output = check_output('ip -d link show testtun99')
print(output)
# Old ip command does not support IFF_ flags
self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
+ self.assertIn('UP,LOWER_UP', output)
- def test_tap(self):
- copy_network_unit('25-tap.netdev')
- start_networkd()
+ output = check_output('ip -d link show testtap99')
+ print(output)
+ self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
+ self.assertIn('UP,LOWER_UP', output)
+
+ remove_network_unit('26-netdev-link-local-addressing-yes.network')
+
+ restart_networkd()
+ self.wait_online(['testtun99:degraded', 'testtap99:degraded'], setup_state='unmanaged')
- self.wait_online(['tap99:off'], setup_state='unmanaged')
+ pid = networkd_pid()
+ name = psutil.Process(pid).name()[:15]
- output = check_output('ip -d link show tap99')
+ output = check_output('ip -d tuntap show')
+ print(output)
+ self.assertRegex(output, f'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
+ self.assertRegex(output, f'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:{name}\({pid}\)systemd\(1\)$')
+
+ output = check_output('ip -d link show testtun99')
+ print(output)
+ self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
+ self.assertIn('UP,LOWER_UP', output)
+
+ output = check_output('ip -d link show testtap99')
print(output)
- # Old ip command does not support IFF_ flags
self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
+ self.assertIn('UP,LOWER_UP', output)
+
+ clear_network_units()
+ restart_networkd()
+ self.wait_online(['testtun99:off', 'testtap99:off'], setup_state='unmanaged')
+
+ output = check_output('ip -d tuntap show')
+ print(output)
+ self.assertRegex(output, f'(?m)testtap99: tap pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
+ self.assertRegex(output, f'(?m)testtun99: tun pi (multi_queue |)vnet_hdr persist filter *(0x100|)\n\tAttached to processes:$')
+
+ for i in range(10):
+ if i != 0:
+ time.sleep(1)
+ output = check_output('ip -d link show testtun99')
+ print(output)
+ self.assertRegex(output, 'tun (type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
+ if 'NO-CARRIER' in output:
+ break
+ else:
+ self.fail()
+
+ for i in range(10):
+ if i != 0:
+ time.sleep(1)
+ output = check_output('ip -d link show testtap99')
+ print(output)
+ self.assertRegex(output, 'tun (type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
+ if 'NO-CARRIER' in output:
+ break
+ else:
+ self.fail()
@expectedFailureIfModuleIsNotAvailable('vrf')
def test_vrf(self):
print(output)
self.assertIn('inet 192.168.124.1/24 scope global wg99', output)
+ output = check_output('ip -4 address show dev wg99')
+ print(output)
+ self.assertIn('inet 169.254.11.1/24 scope link wg99', output)
+
+ output = check_output('ip -6 address show dev wg99')
+ print(output)
+ self.assertIn('inet6 fe80::1/64 scope link', output)
+
output = check_output('ip -4 address show dev wg98')
print(output)
self.assertIn('inet 192.168.123.123/24 scope global wg98', output)
def tearDown(self):
tear_down_common()
- @expectedFailureIfModuleIsNotAvailable('l2tp_eth')
+ @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_netlink')
def test_l2tp_udp(self):
copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
'25-l2tp-udp.netdev', '25-l2tp.network')
self.assertRegex(output, "Peer session 18, tunnel 11")
self.assertRegex(output, "interface name: l2tp-ses2")
- @expectedFailureIfModuleIsNotAvailable('l2tp_ip')
+ @expectedFailureIfModuleIsNotAvailable('l2tp_eth', 'l2tp_ip', 'l2tp_netlink')
def test_l2tp_ip(self):
copy_network_unit('11-dummy.netdev', '25-l2tp-dummy.network',
'25-l2tp-ip.netdev', '25-l2tp.network')
self.assertRegex(output, 'Search Domains: one')
def test_keep_configuration_static(self):
- check_output('systemctl stop systemd-networkd.socket')
- check_output('systemctl stop systemd-networkd.service')
-
check_output('ip link add name dummy98 type dummy')
check_output('ip address add 10.1.2.3/16 dev dummy98')
check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
print(output)
self.assertEqual(output, '')
- def test_qdisc(self):
- copy_network_unit('25-qdisc-clsact-and-htb.network', '12-dummy.netdev',
- '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')
- check_output('modprobe sch_teql max_equalizers=2')
+class NetworkdTCTests(unittest.TestCase, Utilities):
+
+ def setUp(self):
+ setup_common()
+
+ def tearDown(self):
+ tear_down_common()
+
+ @expectedFailureIfModuleIsNotAvailable('sch_cake')
+ def test_qdisc_cake(self):
+ copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable', 'test1:routable'])
+ self.wait_online(['dummy98:routable'])
- output = check_output('tc qdisc show dev test1')
+ output = check_output('tc qdisc show dev dummy98')
print(output)
- self.assertRegex(output, 'qdisc netem')
- self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
- self.assertRegex(output, 'qdisc ingress')
+ self.assertIn('qdisc cake 3a: root', output)
+ self.assertIn('bandwidth 500Mbit', output)
+ self.assertIn('autorate-ingress', output)
+ self.assertIn('diffserv8', output)
+ self.assertIn('dual-dsthost', output)
+ self.assertIn(' nat', output)
+ self.assertIn(' wash', output)
+ self.assertIn(' split-gso', output)
+ self.assertIn(' raw', output)
+ self.assertIn(' atm', output)
+ self.assertIn('overhead 128', output)
+ self.assertIn('mpu 20', output)
+ self.assertIn('fwmark 0xff00', output)
+
+ @expectedFailureIfModuleIsNotAvailable('sch_codel')
+ def test_qdisc_codel(self):
+ copy_network_unit('25-qdisc-codel.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
- self.assertRegex(output, 'qdisc clsact')
+ self.assertRegex(output, 'qdisc codel 33: root')
+ self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
- self.assertRegex(output, 'qdisc htb 2: root')
- self.assertRegex(output, r'default (0x30|30)')
+ @expectedFailureIfModuleIsNotAvailable('sch_drr')
+ def test_qdisc_drr(self):
+ copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
- self.assertRegex(output, 'qdisc netem 30: parent 2:30')
- self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
- self.assertRegex(output, 'qdisc fq_codel')
- self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn')
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'qdisc drr 2: root')
+ output = check_output('tc class show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
+
+ @expectedFailureIfModuleIsNotAvailable('sch_ets')
+ def test_qdisc_ets(self):
+ copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
+
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+
+ self.assertRegex(output, 'qdisc ets 3a: root')
+ self.assertRegex(output, 'bands 10 strict 3')
+ self.assertRegex(output, 'quanta 1 2 3 4 5')
+ self.assertRegex(output, 'priomap 3 4 5 6 7')
- self.assertRegex(output, 'qdisc teql1 31: parent 2:31')
+ @expectedFailureIfModuleIsNotAvailable('sch_fq')
+ def test_qdisc_fq(self):
+ copy_network_unit('25-qdisc-fq.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
- self.assertRegex(output, 'qdisc fq 32: parent 2:32')
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'qdisc fq 32: root')
self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
self.assertRegex(output, 'quantum 1500')
self.assertRegex(output, 'initial_quantum 13000')
self.assertRegex(output, 'maxrate 1Mbit')
- self.assertRegex(output, 'qdisc codel 33: parent 2:33')
- self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
+ @expectedFailureIfModuleIsNotAvailable('sch_fq_codel')
+ def test_qdisc_fq_codel(self):
+ copy_network_unit('25-qdisc-fq_codel.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
- self.assertRegex(output, 'qdisc fq_codel 34: parent 2:34')
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'qdisc fq_codel 34: root')
self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn')
- self.assertRegex(output, 'qdisc tbf 35: parent 2:35')
- self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
+ @expectedFailureIfModuleIsNotAvailable('sch_fq_pie')
+ def test_qdisc_fq_pie(self):
+ copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
- self.assertRegex(output, 'qdisc sfq 36: parent 2:36')
- self.assertRegex(output, 'perturb 5sec')
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
- self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
- self.assertRegex(output, 'limit 100000p')
+ self.assertRegex(output, 'qdisc fq_pie 3a: root')
+ self.assertRegex(output, 'limit 200000p')
+
+ @expectedFailureIfModuleIsNotAvailable('sch_gred')
+ def test_qdisc_gred(self):
+ copy_network_unit('25-qdisc-gred.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
- self.assertRegex(output, 'qdisc gred 38: parent 2:38')
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'qdisc gred 38: root')
self.assertRegex(output, 'vqs 12 default 10 grio')
- self.assertRegex(output, 'qdisc sfb 39: parent 2:39')
- self.assertRegex(output, 'limit 200000')
+ @expectedFailureIfModuleIsNotAvailable('sch_hhf')
+ def test_qdisc_hhf(self):
+ copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
+
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'qdisc hhf 3a: root')
+ self.assertRegex(output, 'limit 1022p')
+
+ @expectedFailureIfModuleIsNotAvailable('sch_htb')
+ def test_qdisc_htb_fifo(self):
+ copy_network_unit('25-qdisc-htb-fifo.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
+
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'qdisc htb 2: root')
+ self.assertRegex(output, r'default (0x30|30)')
+
+ self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
+ self.assertRegex(output, 'limit 100000p')
self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a')
self.assertRegex(output, 'limit 1000000')
output = check_output('tc -d class show dev dummy98')
print(output)
- self.assertRegex(output, 'class htb 2:30 root leaf 30:')
- self.assertRegex(output, 'class htb 2:31 root leaf 31:')
- self.assertRegex(output, 'class htb 2:32 root leaf 32:')
- self.assertRegex(output, 'class htb 2:33 root leaf 33:')
- self.assertRegex(output, 'class htb 2:34 root leaf 34:')
- self.assertRegex(output, 'class htb 2:35 root leaf 35:')
- self.assertRegex(output, 'class htb 2:36 root leaf 36:')
self.assertRegex(output, 'class htb 2:37 root leaf 37:')
- self.assertRegex(output, 'class htb 2:38 root leaf 38:')
- self.assertRegex(output, 'class htb 2:39 root leaf 39:')
self.assertRegex(output, 'class htb 2:3a root leaf 3a:')
self.assertRegex(output, 'class htb 2:3b root leaf 3b:')
self.assertRegex(output, 'class htb 2:3c root leaf 3c:')
self.assertRegex(output, 'burst 123456')
self.assertRegex(output, 'cburst 123457')
- def test_qdisc2(self):
- copy_network_unit('25-qdisc-drr.network', '12-dummy.netdev',
- '25-qdisc-qfq.network', '11-dummy.netdev')
+ @expectedFailureIfModuleIsNotAvailable('sch_ingress')
+ def test_qdisc_ingress(self):
+ copy_network_unit('25-qdisc-clsact.network', '12-dummy.netdev',
+ '25-qdisc-ingress.network', '11-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable', 'test1:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
- self.assertRegex(output, 'qdisc drr 2: root')
- output = check_output('tc class show dev dummy98')
- print(output)
- self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
+ self.assertRegex(output, 'qdisc clsact')
output = check_output('tc qdisc show dev test1')
print(output)
- self.assertRegex(output, 'qdisc qfq 2: root')
- output = check_output('tc class show dev test1')
- print(output)
- self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
- self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
+ self.assertRegex(output, 'qdisc ingress')
- @expectedFailureIfCAKEIsNotAvailable()
- def test_qdisc_cake(self):
- copy_network_unit('25-qdisc-cake.network', '12-dummy.netdev')
+ @expectedFailureIfModuleIsNotAvailable('sch_netem')
+ def test_qdisc_netem(self):
+ copy_network_unit('25-qdisc-netem.network', '12-dummy.netdev',
+ '25-qdisc-netem-compat.network', '11-dummy.netdev')
start_networkd()
- self.wait_online(['dummy98:routable'])
+ self.wait_online(['dummy98:routable', 'test1:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
- self.assertIn('qdisc cake 3a: root', output)
- self.assertIn('bandwidth 500Mbit', output)
- self.assertIn('autorate-ingress', output)
- self.assertIn('diffserv8', output)
- self.assertIn('dual-dsthost', output)
- self.assertIn(' nat', output)
- self.assertIn(' wash', output)
- self.assertIn(' split-gso', output)
- self.assertIn(' raw', output)
- self.assertIn(' atm', output)
- self.assertIn('overhead 128', output)
- self.assertIn('mpu 20', output)
- self.assertIn('fwmark 0xff00', output)
+ self.assertRegex(output, 'qdisc netem 30: root')
+ self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
- @expectedFailureIfPIEIsNotAvailable()
+ output = check_output('tc qdisc show dev test1')
+ print(output)
+ self.assertRegex(output, 'qdisc netem [0-9a-f]*: root')
+ self.assertRegex(output, 'limit 100 delay 50(.0)?ms 10(.0)?ms loss 20%')
+
+ @expectedFailureIfModuleIsNotAvailable('sch_pie')
def test_qdisc_pie(self):
copy_network_unit('25-qdisc-pie.network', '12-dummy.netdev')
start_networkd()
self.assertRegex(output, 'qdisc pie 3a: root')
self.assertRegex(output, 'limit 200000')
- @expectedFailureIfHHFIsNotAvailable()
- def test_qdisc_hhf(self):
- copy_network_unit('25-qdisc-hhf.network', '12-dummy.netdev')
+ @expectedFailureIfModuleIsNotAvailable('sch_qfq')
+ def test_qdisc_qfq(self):
+ copy_network_unit('25-qdisc-qfq.network', '12-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
- self.assertRegex(output, 'qdisc hhf 3a: root')
- self.assertRegex(output, 'limit 1022p')
+ self.assertRegex(output, 'qdisc qfq 2: root')
+ output = check_output('tc class show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
+ self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
- @expectedFailureIfETSIsNotAvailable()
- def test_qdisc_ets(self):
- copy_network_unit('25-qdisc-ets.network', '12-dummy.netdev')
+ @expectedFailureIfModuleIsNotAvailable('sch_sfb')
+ def test_qdisc_sfb(self):
+ copy_network_unit('25-qdisc-sfb.network', '12-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
+ self.assertRegex(output, 'qdisc sfb 39: root')
+ self.assertRegex(output, 'limit 200000')
- self.assertRegex(output, 'qdisc ets 3a: root')
- self.assertRegex(output, 'bands 10 strict 3')
- self.assertRegex(output, 'quanta 1 2 3 4 5')
- self.assertRegex(output, 'priomap 3 4 5 6 7')
+ @expectedFailureIfModuleIsNotAvailable('sch_sfq')
+ def test_qdisc_sfq(self):
+ copy_network_unit('25-qdisc-sfq.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_online(['dummy98:routable'])
- @expectedFailureIfFQPIEIsNotAvailable()
- def test_qdisc_fq_pie(self):
- copy_network_unit('25-qdisc-fq_pie.network', '12-dummy.netdev')
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'qdisc sfq 36: root')
+ self.assertRegex(output, 'perturb 5sec')
+
+ @expectedFailureIfModuleIsNotAvailable('sch_tbf')
+ def test_qdisc_tbf(self):
+ copy_network_unit('25-qdisc-tbf.network', '12-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
+ self.assertRegex(output, 'qdisc tbf 35: root')
+ self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
- self.assertRegex(output, 'qdisc fq_pie 3a: root')
- self.assertRegex(output, 'limit 200000p')
+ @expectedFailureIfModuleIsNotAvailable('sch_teql')
+ def test_qdisc_teql(self):
+ call_quiet('rmmod sch_teql')
+
+ copy_network_unit('25-qdisc-teql.network', '12-dummy.netdev')
+ start_networkd()
+ self.wait_links('dummy98')
+ check_output('modprobe sch_teql max_equalizers=2')
+ self.wait_online(['dummy98:routable'])
+
+ output = check_output('tc qdisc show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'qdisc teql1 31: root')
+class NetworkWaitOnlineTests(unittest.TestCase, Utilities):
+
+ def setUp(self):
+ setup_common()
+
+ def tearDown(self):
+ tear_down_common()
+
+ @expectedFailureIfModuleIsNotAvailable('sch_netem')
def test_wait_online_ipv4(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-with-ipv6-prefix.network', '25-dhcp-client-ipv4-ipv6ra-prefix-client-with-delay.network')
start_networkd()
self.wait_address('veth99', r'192.168.5.[0-9]+', ipv='-4', timeout_sec=1)
+ @expectedFailureIfModuleIsNotAvailable('sch_netem')
def test_wait_online_ipv6(self):
copy_network_unit('25-veth.netdev', '25-ipv6-prefix-with-delay.network', '25-ipv6ra-prefix-client-with-static-ipv4-address.network')
start_networkd()
self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
- self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '1')
- self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '1')
+ self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
+ self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
def test_bridge_property(self):
copy_network_unit('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
start_networkd()
self.wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
+ output = check_output('ip -d link show bridge99')
+ print(output)
+ self.assertIn('mtu 9000 ', output)
+
output = check_output('ip -d link show test1')
print(output)
- self.assertRegex(output, 'master')
- self.assertRegex(output, 'bridge')
+ self.assertIn('master bridge99 ', output)
+ self.assertIn('bridge_slave', output)
+ self.assertIn('mtu 9000 ', output)
output = check_output('ip -d link show dummy98')
print(output)
- self.assertRegex(output, 'master')
- self.assertRegex(output, 'bridge')
+ self.assertIn('master bridge99 ', output)
+ self.assertIn('bridge_slave', output)
+ self.assertIn('mtu 9000 ', output)
output = check_output('ip addr show bridge99')
print(output)
- self.assertRegex(output, '192.168.0.15/24')
+ self.assertIn('192.168.0.15/24', output)
output = check_output('bridge -d link show dummy98')
print(output)
self.check_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress', '1', allow_enoent=True)
self.check_bridge_port_attr('bridge99', 'dummy98', 'learning', '0')
self.check_bridge_port_attr('bridge99', 'dummy98', 'priority', '23')
- self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '1')
- self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '1')
+ self.check_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard', '0')
+ self.check_bridge_port_attr('bridge99', 'dummy98', 'root_block', '0')
output = check_output('bridge -d link show test1')
print(output)
check_output('ip address add 192.168.0.16/24 dev bridge99')
output = check_output('ip addr show bridge99')
print(output)
- self.assertRegex(output, '192.168.0.16/24')
+ self.assertIn('192.168.0.16/24', output)
# for issue #6088
print('### ip -6 route list table all dev bridge99')
self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
remove_link('test1')
-
self.wait_operstate('bridge99', 'degraded-carrier')
- remove_link('dummy98')
+ output = check_output('ip -d link show bridge99')
+ print(output)
+ self.assertIn('mtu 9000 ', output)
+
+ output = check_output('ip -d link show dummy98')
+ print(output)
+ self.assertIn('master bridge99 ', output)
+ self.assertIn('bridge_slave', output)
+ self.assertIn('mtu 9000 ', output)
+ remove_link('dummy98')
self.wait_operstate('bridge99', 'no-carrier')
+ output = check_output('ip -d link show bridge99')
+ print(output)
+ # When no carrier, the kernel may reset the MTU
+ self.assertIn('NO-CARRIER', output)
+
output = check_output('ip address show bridge99')
print(output)
- self.assertRegex(output, 'NO-CARRIER')
- self.assertNotRegex(output, '192.168.0.15/24')
- self.assertRegex(output, '192.168.0.16/24') # foreign address is kept
+ self.assertNotIn('192.168.0.15/24', output)
+ self.assertIn('192.168.0.16/24', output) # foreign address is kept
print('### ip -6 route list table all dev bridge99')
output = check_output('ip -6 route list table all dev bridge99')
print(output)
self.assertRegex(output, 'ff00::/8 table local (proto kernel )?metric 256 (linkdown )?pref medium')
+ check_output('ip link add dummy98 type dummy')
+ self.wait_online(['dummy98:enslaved', 'bridge99:routable'])
+
+ output = check_output('ip -d link show bridge99')
+ print(output)
+ self.assertIn('mtu 9000 ', output)
+
+ output = check_output('ip -d link show dummy98')
+ print(output)
+ self.assertIn('master bridge99 ', output)
+ self.assertIn('bridge_slave', output)
+ self.assertIn('mtu 9000 ', output)
+
def test_bridge_configure_without_carrier(self):
copy_network_unit('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
'11-dummy.netdev')
@expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
def test_sriov_udev(self):
copy_network_unit('25-sriov.link', '25-sriov-udev.network')
- call('udevadm control --reload')
call('modprobe netdevsim')
with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
f.write('[Link]\nSR-IOVVirtualFunctions=4\n')
- call('udevadm control --reload')
- call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+ udev_reload()
+ call(*udevadm_cmd, 'trigger', '--action=add', '--settle', '/sys/devices/netdevsim99/net/eni99np1')
output = check_output('ip link show dev eni99np1')
print(output)
with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
f.write('[Link]\nSR-IOVVirtualFunctions=\n')
- call('udevadm control --reload')
- call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+ udev_reload()
+ call(*udevadm_cmd, 'trigger', '--action=add', '--settle', '/sys/devices/netdevsim99/net/eni99np1')
output = check_output('ip link show dev eni99np1')
print(output)
with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
f.write('[Link]\nSR-IOVVirtualFunctions=2\n')
- call('udevadm control --reload')
- call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+ udev_reload()
+ call(*udevadm_cmd, 'trigger', '--action=add', '--settle', '/sys/devices/netdevsim99/net/eni99np1')
output = check_output('ip link show dev eni99np1')
print(output)
with open(os.path.join(network_unit_dir, '25-sriov.link'), mode='a', encoding='utf-8') as f:
f.write('[Link]\nSR-IOVVirtualFunctions=\n')
- call('udevadm control --reload')
- call('udevadm trigger --action add --settle /sys/devices/netdevsim99/net/eni99np1')
+ udev_reload()
+ call(*udevadm_cmd, 'trigger', '--action=add', '--settle', '/sys/devices/netdevsim99/net/eni99np1')
output = check_output('ip link show dev eni99np1')
print(output)
print(output)
self.assertRegex(output, 'token :: dev veth99')
+ print('## dnsmasq log')
+ output = read_dnsmasq_log_file()
+ print(output)
+ self.assertIn('DHCPSOLICIT(veth-peer)', output)
+ self.assertNotIn('DHCPADVERTISE(veth-peer)', output)
+ self.assertNotIn('DHCPREQUEST(veth-peer)', output)
+ self.assertIn('DHCPREPLY(veth-peer)', output)
+ self.assertIn('sent size: 0 option: 14 rapid-commit', output)
+
+ with open(os.path.join(network_unit_dir, '25-dhcp-client-ipv6-only.network'), mode='a', encoding='utf-8') as f:
+ f.write('\n[DHCPv6]\nRapidCommit=no\n')
+
+ stop_dnsmasq()
+ start_dnsmasq()
+
+ networkctl_reload()
+ self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ # checking address
+ output = check_output('ip address show dev veth99 scope global')
+ print(output)
+ self.assertRegex(output, r'inet6 2600::[0-9a-f:]*/128 scope global dynamic noprefixroute')
+ self.assertNotIn('192.168.5', output)
+
+ # checking semi-static route
+ output = check_output('ip -6 route list dev veth99 2001:1234:5:9fff:ff:ff:ff:ff')
+ print(output)
+ self.assertRegex(output, 'via fe80::1034:56ff:fe78:9abd')
+
+ print('## dnsmasq log')
+ output = read_dnsmasq_log_file()
+ print(output)
+ self.assertIn('DHCPSOLICIT(veth-peer)', output)
+ self.assertIn('DHCPADVERTISE(veth-peer)', output)
+ self.assertIn('DHCPREQUEST(veth-peer)', output)
+ self.assertIn('DHCPREPLY(veth-peer)', output)
+ self.assertNotIn('rapid-commit', output)
+
def test_dhcp_client_ipv4_only(self):
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client-ipv4-only.network')
output = check_output('ip -4 address show dev veth99')
print(output)
self.assertNotIn('192.168.5.', output)
- self.assertRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
+ self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
start_dnsmasq()
print('Wait for a DHCP lease to be acquired and the IPv4LL address to be dropped')
stop_dnsmasq()
print('Wait for the DHCP lease to be expired and an IPv4LL address to be acquired')
self.wait_address_dropped('veth99', r'inet 192\.168\.5\.\d+/24 metric 1024 brd 192\.168\.5\.255 scope global dynamic', ipv='-4', timeout_sec=130)
- self.wait_address('veth99', r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
+ self.wait_address('veth99', r'inet 169\.254\.133\.11/16 metric 2048 brd 169\.254\.255\.255 scope link', scope='link', ipv='-4')
output = check_output('ip -4 address show dev veth99')
print(output)
self.assertNotIn('192.168.5.', output)
- self.assertRegex(output, r'inet 169\.254\.\d+\.\d+/16 metric 2048 brd 169\.254\.255\.255 scope link')
+ self.assertIn('inet 169.254.133.11/16 metric 2048 brd 169.254.255.255 scope link', output)
def test_dhcp_client_use_dns(self):
def check(self, ipv4, ipv6):
def test_mtu_link(self):
copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', copy_dropins=False)
- # must reload udev because it only picks up new files after 3 second delay
- call('udevadm control --reload')
# note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
self.check_mtu('1600', reset=False)
def test_mtu_link_ipv6_mtu(self):
''' set ipv6 mtu and set device mtu via link file '''
copy_network_unit('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
- # must reload udev because it only picks up new files after 3 second delay
- call('udevadm control --reload')
self.check_mtu('1600', '1550', reset=False)
parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin')
parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin')
+ parser.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest='timesyncd_bin')
parser.add_argument('--udevd', help='Path to systemd-udevd', dest='udevd_bin')
parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin')
parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin')
parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin')
parser.add_argument('--timedatectl', help='Path to timedatectl', dest='timedatectl_bin')
+ parser.add_argument('--udevadm', help='Path to udevadm', dest='udevadm_bin')
parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
ns, unknown_args = parser.parse_known_args(namespace=unittest)
if ns.build_dir:
- if ns.networkd_bin or ns.resolved_bin or ns.udevd_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin:
- print('WARNING: --networkd, --resolved, --wait-online, --networkctl, --resolvectl, or --timedatectl options are ignored when --build-dir is specified.')
+ if ns.networkd_bin or ns.resolved_bin or ns.timesyncd_bin or ns.udevd_bin or \
+ ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin or ns.udevadm_bin:
+ print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
+ timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
udevd_bin = os.path.join(ns.build_dir, 'systemd-udevd')
wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
+ udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
else:
if ns.networkd_bin:
networkd_bin = ns.networkd_bin
if ns.resolved_bin:
resolved_bin = ns.resolved_bin
+ if ns.timesyncd_bin:
+ timesyncd_bin = ns.timesyncd_bin
if ns.udevd_bin:
udevd_bin = ns.udevd_bin
if ns.wait_online_bin:
resolvectl_bin = ns.resolvectl_bin
if ns.timedatectl_bin:
timedatectl_bin = ns.timedatectl_bin
+ if ns.udevadm_bin:
+ udevadm_bin = ns.udevadm_bin
use_valgrind = ns.use_valgrind
enable_debug = ns.enable_debug
with_coverage = ns.with_coverage
if use_valgrind:
- networkctl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', networkctl_bin]
- resolvectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', resolvectl_bin]
- timedatectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', timedatectl_bin]
- wait_online_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', wait_online_bin]
- else:
- networkctl_cmd = [networkctl_bin]
- resolvectl_cmd = [resolvectl_bin]
- timedatectl_cmd = [timedatectl_bin]
- wait_online_cmd = [wait_online_bin]
+ # Do not forget the trailing space.
+ valgrind_cmd = 'valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all '
+
+ networkctl_cmd = valgrind_cmd.split() + [networkctl_bin]
+ resolvectl_cmd = valgrind_cmd.split() + [resolvectl_bin]
+ timedatectl_cmd = valgrind_cmd.split() + [timedatectl_bin]
+ udevadm_cmd = valgrind_cmd.split() + [udevadm_bin]
+ wait_online_cmd = valgrind_cmd.split() + [wait_online_bin]
if asan_options:
env.update({'ASAN_OPTIONS': asan_options})
env.update({'LSAN_OPTIONS': lsan_options})
if ubsan_options:
env.update({'UBSAN_OPTIONS': ubsan_options})
+ if use_valgrind:
+ env.update({'SYSTEMD_MEMPOOL': '0'})
wait_online_env = env.copy()
if enable_debug:
label = 'valid_symlink-deep'
test_content('f= {} - - - - ' + label, label, user=user, subpath='/deep/1/2', path_cb=valid_symlink)
+def test_base64():
+ test_line('f~ /tmp/base64-test - - - - UGlmZgpQYWZmClB1ZmYgCg==', user=False, returncode=0)
+
+ with open("/tmp/base64-test", mode='r') as f:
+ d = f.read()
+
+ assert d == "Piff\nPaff\nPuff \n"
+
if __name__ == '__main__':
test_invalids(user=False)
test_invalids(user=True)
test_hard_cleanup(user=False)
test_hard_cleanup(user=True)
+
+ test_base64()
# utility functions for shell tests
assert_true() {(
- local rc
-
set +ex
+ local rc
+
"$@"
rc=$?
if [[ $rc -ne 0 ]]; then
)}
assert_rc() {(
- local rc exp="${1?}"
-
set +ex
+ local rc exp="${1?}"
+
shift
"$@"
rc=$?
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+# shellcheck source=test/units/assert.sh
+. "$(dirname "$0")"/assert.sh
+
+wait_service_active() {(
+ set +ex
+ for (( i = 0; i < 20; i++ )); do
+ if (( i != 0 )); then sleep 0.5; fi
+ if systemctl --quiet is-active "${1?}"; then
+ return 0
+ fi
+ done
+ return 1
+)}
+
+wait_service_inactive() {(
+ set +ex
+ for (( i = 0; i < 20; i++ )); do
+ if (( i != 0 )); then sleep 0.5; fi
+ systemctl --quiet is-active "${1?}"
+ if [[ "$?" == "3" ]]; then
+ return 0
+ fi
+ done
+ return 1
+)}
+
+mkdir -p /run/systemd/system
+cat >/run/systemd/system/both.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+cat >/run/systemd/system/on-add.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+cat >/run/systemd/system/on-change.service <<EOF
+[Service]
+ExecStart=sleep 1000
+EOF
+
+systemctl daemon-reload
+
+mkdir -p /run/udev/rules.d/
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
+SUBSYSTEM=="net", KERNEL=="dummy9?", OPTIONS="log_level=debug"
+SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-add.service"
+SUBSYSTEM=="net", KERNEL=="dummy9?", ACTION=="change", TAG+="systemd", ENV{SYSTEMD_WANTS}+="both.service", ENV{SYSTEMD_WANTS}+="on-change.service"
+EOF
+
+udevadm control --reload
+
+# StopWhenUnneeded=no
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+systemctl stop both.service on-add.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+udevadm info /sys/class/net/dummy99
+wait_service_active both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_active on-change.service
+systemctl stop both.service on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+assert_rc 3 systemctl --quiet is-active both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes
+cat >/run/systemd/system/both.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=sleep 1000
+Type=simple
+EOF
+
+cat >/run/systemd/system/on-add.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=sleep 1000
+Type=simple
+EOF
+
+cat >/run/systemd/system/on-change.service <<EOF
+[Unit]
+StopWhenUnneeded=yes
+
+[Service]
+ExecStart=echo changed
+RemainAfterExit=true
+Type=oneshot
+EOF
+
+systemctl daemon-reload
+
+# StopWhenUnneeded=yes (single device, only add event)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+wait_service_inactive on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes (single device, add and change event)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+wait_service_inactive on-add.service
+wait_service_active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_inactive on-change.service
+
+# StopWhenUnneeded=yes (multiple devices, only add events)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link add dummy98 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link del dummy98
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
+wait_service_inactive both.service
+wait_service_inactive on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+# StopWhenUnneeded=yes (multiple devices, add and change events)
+ip link add dummy99 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy99
+wait_service_active both.service
+wait_service_active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+ip link add dummy98 type dummy
+udevadm wait --settle --timeout=30 /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+assert_rc 3 systemctl --quiet is-active on-change.service
+
+udevadm trigger --action=change --settle /sys/class/net/dummy99
+assert_rc 0 systemctl --quiet is-active both.service
+assert_rc 0 systemctl --quiet is-active on-add.service
+wait_service_active on-change.service
+
+ip link del dummy98
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy98
+assert_rc 0 systemctl --quiet is-active both.service
+wait_service_inactive on-add.service
+assert_rc 0 systemctl --quiet is-active on-change.service
+
+ip link del dummy99
+udevadm wait --settle --timeout=30 --removed /sys/class/net/dummy99
+wait_service_inactive both.service
+assert_rc 3 systemctl --quiet is-active on-add.service
+wait_service_inactive on-change.service
+
+# cleanup
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+
+rm -f /run/systemd/system/on-add.service
+rm -f /run/systemd/system/on-change.service
+systemctl daemon-reload
+
+exit 0
set -eux
set -o pipefail
+test_scope_unpriv_delegation() {
+ useradd test ||:
+ trap "userdel -r test" RETURN
+
+ systemd-run --uid=test -p User=test -p Delegate=yes --slice workload.slice --unit workload0.scope --scope \
+ test -w /sys/fs/cgroup/workload.slice/workload0.scope -a \
+ -w /sys/fs/cgroup/workload.slice/workload0.scope/cgroup.procs -a \
+ -w /sys/fs/cgroup/workload.slice/workload0.scope/cgroup.subtree_control
+}
+
if grep -q cgroup2 /proc/filesystems ; then
systemd-run --wait --unit=test0.service -p "DynamicUser=1" -p "Delegate=" \
test -w /sys/fs/cgroup/system.slice/test0.service/ -a \
# And now check again, "io" should have vanished
grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+ # Check that unprivileged delegation works for scopes
+ test_scope_unpriv_delegation
+
else
echo "Skipping TEST-19-DELEGATE, as the kernel doesn't actually support cgroup v2" >&2
fi
cleanup_session() (
set +ex
+ local uid s
+
+ uid=$(id -u logind-test-user)
+
+ loginctl disable-linger logind-test-user
+
systemctl stop getty@tty2.service
- rm -rf /run/systemd/system/getty@tty2.service.d
- systemctl daemon-reload
- pkill -u "$(id -u logind-test-user)"
+ for s in $(loginctl --no-legend list-sessions | awk '$3 == "logind-test-user" { print $1 }'); do
+ echo "INFO: stopping session $s"
+ loginctl terminate-session "$s"
+ done
+
+ loginctl terminate-user logind-test-user
+
+ if ! timeout 30 bash -c "while loginctl --no-legend | grep -q logind-test-user; do sleep 1; done"; then
+ echo "WARNING: session for logind-test-user still active, ignoring."
+ fi
+
+ pkill -u "$uid"
sleep 1
- pkill -KILL -u "$(id -u logind-test-user)"
+ pkill -KILL -u "$uid"
+
+ if ! timeout 30 bash -c "while systemctl is-active --quiet user@${uid}.service; do sleep 1; done"; then
+ echo "WARNING: user@${uid}.service is still active, ignoring."
+ fi
+
+ if ! timeout 30 bash -c "while systemctl is-active --quiet user-runtime-dir@${uid}.service; do sleep 1; done"; then
+ echo "WARNING: user-runtime-dir@${uid}.service is still active, ignoring."
+ fi
+
+ if ! timeout 30 bash -c "while systemctl is-active --quiet user-${uid}.slice; do sleep 1; done"; then
+ echo "WARNING: user-${uid}.slice is still active, ignoring."
+ fi
+
+ rm -rf /run/systemd/system/getty@tty2.service.d
+ systemctl daemon-reload
return 0
)
return 1
fi
- session=$(loginctl --no-legend | grep "logind-test-user" | awk '{ print $1 }')
+ session=$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')
if [[ -z "$session" ]]; then
echo "no session found for user logind-test-user" >&2
return 1
return 1
fi
- leader_pid=$(loginctl session-status "$session" | grep "Leader:" | awk '{ print $2 }')
+ leader_pid=$(loginctl session-status "$session" | awk '$1 == "Leader:" { print $2 }')
if [[ -z "$leader_pid" ]]; then
echo "cannot found leader process for session $session" >&2
return 1
Type=simple
ExecStart=
ExecStart=-/sbin/agetty --autologin logind-test-user --noclear %I $TERM
+Restart=no
EOF
systemctl daemon-reload
udevadm info "$dev"
# trigger logind and activate session
- loginctl activate "$(loginctl --no-legend | grep "logind-test-user" | awk '{ print $1 }')"
+ loginctl activate "$(loginctl --no-legend | awk '$3 == "logind-test-user" { print $1 }')"
# check ACL
sleep 1
teardown_lock_idle_action() (
set +eux
- cleanup_session
-
rm -f /run/systemd/logind.conf.d/idle-action-lock.conf
systemctl restart systemd-logind.service
+ cleanup_session
+
return 0
)
fi
}
-teardown_cron() (
- set +ex
-
- pkill -u "$(id -u logind-test-user)"
- sleep 1
- pkill -KILL -u "$(id -u logind-test-user)"
- pkill crond
- crontab -r -u logind-test-user
-
- return 0
-)
+test_session_properties() {
+ local s
-test_no_user_instance_for_cron() {
- if ! command -v crond || ! command -v crontab ; then
- echo "Skipping test for background cron sessions because cron is missing."
+ if [[ ! -c /dev/tty2 ]]; then
+ echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
return
fi
- trap teardown_cron RETURN
-
- # Setup cron
- crond -s -n &
- # Install crontab for the test user that runs sleep every minute. But let's sleep for
- # 65 seconds to make sure there is overlap between two consecutive runs, i.e. we have
- # always a cron session running.
- crontab -u logind-test-user - <<EOF
-RANDOM_DELAY=0
-* * * * * /bin/sleep 65
-EOF
-
- # Let's wait (at most one interval plus 10s to accommodate for slow machines) for at least one session
- # of the test user
- timeout 70 bash -c "while ! loginctl --no-legend list-sessions | grep -q logind-test-user; do sleep 1; done"
-
- # Check that all sessions of test user have class=background and no user instance was started
- # for the test user.
- while read -r s _; do
- assert_eq "$(loginctl --property Class --value show-session "$s")" "background"
- done < <(loginctl --no-legend list-sessions | grep logind-test-user)
+ trap cleanup_session RETURN
+ create_session
- assert_eq "$(systemctl --property ActiveState --value show user@"$(id -u logind-test-user)".service)" "inactive"
- assert_eq "$(systemctl --property SubState --value show user@"$(id -u logind-test-user)".service)" "dead"
+ s=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }')
+ /usr/lib/systemd/tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}"
}
-test_session_properties() {
- local s
-
+test_list_users() {
if [[ ! -c /dev/tty2 ]]; then
echo "/dev/tty2 does not exist, skipping test ${FUNCNAME[0]}."
return
trap cleanup_session RETURN
create_session
- s=$(loginctl list-sessions --no-legend | awk '$3 == "logind-test-user" { print $1 }')
- /usr/lib/systemd/tests/manual/test-session-properties "/org/freedesktop/login1/session/_3${s?}"
+ assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $1 }')" "$(id -ru logind-test-user)"
+ assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" no
+
+ loginctl enable-linger logind-test-user
+
+ assert_eq "$(loginctl list-users --no-legend | awk '$2 == "logind-test-user" { print $3 }')" yes
}
: >/failed
test_shutdown
test_session
test_lock_idle_action
-test_no_user_instance_for_cron
test_session_properties
+test_list_users
touch /testok
rm /failed
echo "Verity device ${image}.raw not found in /dev/mapper/"
exit 1
fi
-umount "${image_dir}/mount"
-umount "${image_dir}/mount2"
+systemd-dissect --umount "${image_dir}/mount"
+systemd-dissect --umount "${image_dir}/mount2"
systemd-run -P -p RootImage="${image}.raw" cat /usr/lib/os-release | grep -q -F "MARKER=1"
mv "${image}.verity" "${image}.fooverity"
grep -q -F -f "$os_release" "${image_dir}/mount/usr/lib/os-release"
grep -q -F -f "$os_release" "${image_dir}/mount/etc/os-release"
grep -q -F "MARKER=1" "${image_dir}/mount/usr/lib/os-release"
-umount "${image_dir}/mount"
+systemd-dissect --umount "${image_dir}/mount"
# add explicit -p MountAPIVFS=yes once to test the parser
systemd-run -P -p RootImage="${image}.gpt" -p RootHash="${roothash}" -p MountAPIVFS=yes cat /usr/lib/os-release | grep -q -F "MARKER=1"
RemainAfterExit=yes
MountAPIVFS=yes
PrivateTmp=yes
-ExecStart=/bin/sh -c 'systemd-notify --ready; while ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do sleep 0.1; done; mount | grep -F "/dev/mapper/${roothash}-verity" | grep -q -F "nosuid"'
+ExecStart=/bin/sh -c 'systemd-notify --ready; while ! grep -q -F MARKER /tmp/img/usr/lib/os-release; do sleep 0.1; done; mount | grep -e "/dev/mapper/${roothash}-verity" -e "/dev/mapper/loop[0-9]*-verity" | grep -q -F "nosuid"'
EOF
systemctl start testservice-50d.service
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.raw" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P --property ExtensionImages=/usr/share/app-nodistro.raw --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
cat >/run/systemd/system/testservice-50e.service <<EOF
[Service]
MountAPIVFS=yes
systemctl is-active testservice-50e.service
# ExtensionDirectories will set up an overlay
-mkdir -p "${image_dir}/app0" "${image_dir}/app1"
+mkdir -p "${image_dir}/app0" "${image_dir}/app1" "${image_dir}/app-nodistro"
systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; }
systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; }
systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0"
systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1"
+systemd-dissect --mount /usr/share/app-nodistro.raw "${image_dir}/app-nodistro"
systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0"
systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2"
systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1"
+systemd-run -P --property ExtensionDirectories="${image_dir}/app-nodistro" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1"
cat >/run/systemd/system/testservice-50f.service <<EOF
[Service]
MountAPIVFS=yes
EOF
systemctl start testservice-50f.service
systemctl is-active testservice-50f.service
-umount "${image_dir}/app0"
-umount "${image_dir}/app1"
+systemd-dissect --umount "${image_dir}/app0"
+systemd-dissect --umount "${image_dir}/app1"
+
+# Test that an extension consisting of an empty directory under /etc/extensions/ takes precedence
+mkdir -p /var/lib/extensions/
+ln -s /usr/share/app-nodistro.raw /var/lib/extensions/app-nodistro.raw
+systemd-sysext merge
+grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+mkdir -p /etc/extensions/app-nodistro
+systemd-sysext merge
+test ! -e /usr/lib/systemd/system/some_file
+systemd-sysext unmerge
+rmdir /etc/extensions/app-nodistro
+rm /var/lib/extensions/app-nodistro.raw
echo OK >/testok
: >/failed
+# Run a timer for every 15 minutes before setting the current time
+systemd-run --unit test-timer-1 --on-calendar "*:0/15:0" true
+
# Reset host date to current time, 3 days in the past.
date -s "-3 days"
-# Run a timer for every 15 minutes.
-systemd-run --unit test-timer --on-calendar "*:0/15:0" true
+# Run another timer for every 15 minutes, after setting the time
+systemd-run --unit test-timer-2 --on-calendar "*:0/15:0" true
+
+next_elapsed_t1=$(systemctl show test-timer-1.timer -p NextElapseUSecRealtime --value)
+next_elapsed_t1=$(date -d "${next_elapsed_t1}" +%s)
+now=$(date +%s)
+time_delta_t1=$((next_elapsed_t1 - now))
-next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value)
-next_elapsed=$(date -d "${next_elapsed}" +%s)
+next_elapsed_t2=$(systemctl show test-timer-2.timer -p NextElapseUSecRealtime --value)
+next_elapsed_t2=$(date -d "${next_elapsed_t2}" +%s)
now=$(date +%s)
-time_delta=$((next_elapsed - now))
+time_delta_t2=$((next_elapsed_t2 - now))
+
+# Check that the timer will elapse in less than 20 minutes.
+((0 < time_delta_t1 && time_delta_t1 < 1200)) || {
+ echo 'Timer elapse outside of the expected 20 minute window.'
+ echo " next_elapsed_t1=${next_elapsed_t1}"
+ echo " now=${now}"
+ echo " time_delta_t1=${time_delta_t1}"
+ echo ''
+} >>/failed_t1
# Check that the timer will elapse in less than 20 minutes.
-((0 < time_delta && time_delta < 1200)) || {
+((0 < time_delta_t2 && time_delta_t2 < 1200)) || {
echo 'Timer elapse outside of the expected 20 minute window.'
- echo " next_elapsed=${next_elapsed}"
+ echo " next_elapsed_t2=${next_elapsed_t2}"
echo " now=${now}"
- echo " time_delta=${time_delta}"
+ echo " time_delta_t2=${time_delta_t2}"
echo ''
-} >>/failed
+} >>/failed_t2
-if test ! -s /failed ; then
- rm -f /failed
+if test ! -s /failed_t1 && test ! -s /failed_t2; then
+ rm -f /failed_t*
touch /testok
fi
# Verify that passing creds through kernel cmdline works
[ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ]
+ # And that it also works via SMBIOS
+ [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ]
+ [ "$(systemd-creds --system cat binarysmbioscredential)" = "magicbinarydata" ]
+
# If we aren't run in nspawn, we are run in qemu
systemd-detect-virt -q -v
expected_credential=myqemucredential
expected_value=othervalue
+
+ # Verify that writing a sysctl via the kernel cmdline worked
+ [ "$(cat /proc/sys/kernel/domainname)" = "sysctltest" ]
+
+ # Verify that creating a user via sysusers via the kernel cmdline worked
+ grep -q ^credtestuser: /etc/passwd
+
+ # Verify that writing a file via tmpfiles worked
+ [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
+ [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
+ [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
else
echo "qemu_fw_cfg support missing in kernel. Sniff!"
expected_credential=""
# Combine it with a fallback (which should have no effect, given the cred should be passed down)
[ "$(systemd-run -p LoadCredential="$expected_credential" -p SetCredential="$expected_credential":zzz --pipe --wait systemd-creds cat "$expected_credential")" = "$expected_value" ]
+
+ # This should succeed
+ systemd-run -p AssertCredential="$expected_credential" -p Type=oneshot true
+
+ # And this should fail
+ systemd-run -p AssertCredential="undefinedcredential" -p Type=oneshot true && { echo 'unexpected success'; exit 1; }
fi
# Verify that the creds are immutable
set -eux
set -o pipefail
+# shellcheck source=test/units/assert.sh
+. "$(dirname "$0")"/assert.sh
+
+teardown_test_dependencies() (
+ set +eux
+
+ if mountpoint /tmp/deptest; then
+ umount /tmp/deptest
+ fi
+
+ if [[ -n "${LOOP}" ]]; then
+ losetup -d "${LOOP}" || :
+ fi
+ if [[ -n "${LOOP_0}" ]]; then
+ losetup -d "${LOOP_0}" || :
+ fi
+ if [[ -n "${LOOP_1}" ]]; then
+ losetup -d "${LOOP_1}" || :
+ fi
+
+ rm -f /tmp/testsuite-60-dependencies-0.img
+ rm -f /tmp/testsuite-60-dependencies-1.img
+
+ rm -f /run/systemd/system/tmp-deptest.mount
+ systemctl daemon-reload
+
+ return 0
+)
+
+setup_loop() {
+ truncate -s 30m "/tmp/testsuite-60-dependencies-${1?}.img"
+ sfdisk --wipe=always "/tmp/testsuite-60-dependencies-${1?}.img" <<EOF
+label:gpt
+
+name="loop${1?}-part1"
+EOF
+ LOOP=$(losetup -P --show -f "/tmp/testsuite-60-dependencies-${1?}.img")
+ udevadm wait --settle --timeout=10 "${LOOP}p1"
+ udevadm lock --device="${LOOP}p1" mkfs.ext4 -L "partname${1?}-1" "${LOOP}p1"
+}
+
+check_dependencies() {
+ local escaped_0 escaped_1 after
+
+ escaped_0=$(systemd-escape -p "${LOOP_0}p1")
+ escaped_1=$(systemd-escape -p "${LOOP_1}p1")
+
+ if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ fi
+
+ # mount LOOP_0
+ mount -t ext4 "${LOOP_0}p1" /tmp/deptest
+ sleep 1
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_in "local-fs-pre.target" "$after"
+ assert_not_in "remote-fs-pre.target" "$after"
+ assert_not_in "network.target" "$after"
+ assert_in "${escaped_0}.device" "$after"
+ assert_in "blockdev@${escaped_0}.target" "$after"
+ assert_not_in "${escaped_1}.device" "$after"
+ assert_not_in "blockdev@${escaped_1}.target" "$after"
+ umount /tmp/deptest
+
+ if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ fi
+
+ # mount LOOP_1 (using fake _netdev option)
+ mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
+ sleep 1
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ assert_not_in "${escaped_0}.device" "$after"
+ assert_not_in "blockdev@${escaped_0}.target" "$after"
+ assert_in "${escaped_1}.device" "$after"
+ assert_in "blockdev@${escaped_1}.target" "$after"
+ umount /tmp/deptest
+
+ if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ fi
+
+ # mount tmpfs
+ mount -t tmpfs tmpfs /tmp/deptest
+ sleep 1
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_in "local-fs-pre.target" "$after"
+ assert_not_in "remote-fs-pre.target" "$after"
+ assert_not_in "network.target" "$after"
+ assert_not_in "${escaped_0}.device" "$after"
+ assert_not_in "blockdev@${escaped_0}.target" "$after"
+ assert_not_in "${escaped_1}.device" "$after"
+ assert_not_in "blockdev@${escaped_1}.target" "$after"
+ umount /tmp/deptest
+
+ if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
+ after=$(systemctl show --property=After --value tmp-deptest.mount)
+ assert_not_in "local-fs-pre.target" "$after"
+ assert_in "remote-fs-pre.target" "$after"
+ assert_in "network.target" "$after"
+ fi
+}
+
+test_dependencies() {
+ if systemd-detect-virt --quiet --container; then
+ echo "Skipping test_dependencies in container"
+ return
+ fi
+
+ trap teardown_test_dependencies RETURN
+
+ setup_loop 0
+ LOOP_0="${LOOP}"
+ LOOP=
+ setup_loop 1
+ LOOP_1="${LOOP}"
+ LOOP=
+
+ mkdir -p /tmp/deptest
+
+ # without .mount file
+ check_dependencies
+
+ # create .mount file
+ mkdir -p /run/systemd/system
+ cat >/run/systemd/system/tmp-deptest.mount <<EOF
+[Mount]
+Where=/tmp/deptest
+What=192.168.0.1:/tmp/mnt
+Type=nfs
+EOF
+ systemctl daemon-reload
+
+ # with .mount file
+ check_dependencies
+}
+
test_issue_20329() {
local tmpdir unit
tmpdir="$(mktemp -d)"
# Give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units.
timeout 2m bash -c 'while systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; do systemctl daemon-reload; sleep 10; done'
+# test for issue #19983 and #23552.
+test_dependencies
+
# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
test_issue_20329
echo "UUID=deadbeef-dead-dead-beef-222222222222 $mpoint ext4 defaults 0 0" >>/etc/fstab
systemctl daemon-reload
mount "$mpoint"
- systemctl status "$mpoint"
+ timeout 30 bash -c "while ! systemctl -q is-active '$mpoint'; do sleep .2; done"
test -e "$mpoint/test"
umount "$mpoint"
fi
dd if=/dev/zero of="${image_dir}/image" bs=1048576 count=64 || exit 1
+dd if=/dev/zero of="${image_dir}/data" bs=1048576 count=64 || exit 1
loop="$(losetup --show -f "${image_dir}/image")"
if [[ ! -e ${loop} ]]; then
exit 1
fi
+# Do one iteration with a separate data device, to test those branches
+separate_data=1
+
for algorithm in crc32c crc32 sha1 sha256
do
- integritysetup format "${loop}" --batch-mode -I "${algorithm}" || exit 1
- integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" || exit 1
+ if [ "${separate_data}" -eq 1 ]; then
+ data_option="--data-device=${image_dir}/data"
+ else
+ data_option=""
+ fi
+ integritysetup format "${loop}" --batch-mode -I "${algorithm}" "${data_option}" || exit 1
+ integritysetup open -I "${algorithm}" "${loop}" "${DM_NAME}" "${data_option}" || exit 1
mkfs.ext4 -U "${FS_UUID}" "${FULL_DM_DEV_NAME}" || exit 1
# Give userspace time to handle udev events for new FS showing up ...
integritysetup close "${DM_NAME}" || exit 1
# create integritytab, generate units, start service
- build_integrity_tab ${algorithm}
+ if [ "${separate_data}" -eq 1 ]; then
+ data_option=",data-device=${image_dir}/data"
+ else
+ data_option=""
+ fi
+ build_integrity_tab "${algorithm}${data_option}"
# Cause the generator to re-run
systemctl daemon-reload || exit 1
# Check the signature on the FS to ensure we can retrieve it and that is matches
if [ -e "${FULL_DM_DEV_NAME}" ]; then
- if [ "${FULL_DM_DEV_NAME}" != "$(blkid -U "${FS_UUID}")" ]; then
+ # If a separate device is used for the metadata storage, then blkid will return one of the loop devices
+ if [ "${separate_data}" -eq 1 ]; then
+ dev_name="$(integritysetup status ${DM_NAME} | grep '^\s*device:' | awk '{print $2}')"
+ else
+ dev_name="${FULL_DM_DEV_NAME}"
+ fi
+ if [ "${dev_name}" != "$(blkid -U "${FS_UUID}")" ]; then
echo "Failed to locate FS with matching UUID!"
exit 1
fi
exit 1
fi
+ separate_data=0
done
echo OK >/testok
echo -n passphrase >/tmp/passphrase
cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom $img /tmp/passphrase
+# Unlocking via keyfile
+systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto $img
+
# Enroll unlock with default PCR policy
env PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto $img
/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1
# Check failure with wrong PIN
env PIN=123457 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; }
+# Check LUKS2 token plugin unlock (i.e. without specifying tpm2-device=auto)
+if cryptsetup --help | grep -q 'LUKS2 external token plugin support is compiled-in'; then
+ env PIN=123456 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - headless=1
+ /usr/lib/systemd/systemd-cryptsetup detach test-volume
+
+ # Check failure with wrong PIN
+ env PIN=123457 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - headless=1 && { echo 'unexpected success'; exit 1; }
+else
+ echo 'cryptsetup has no LUKS2 token plugin support, skipping'
+fi
+
# Check failure with wrong PCR (and correct PIN)
tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000
env PIN=123456 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; }
tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000
/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && exit 1
+if [[ -e /usr/lib/systemd/sytemd-measure ]]; then
+ echo HALLO > /tmp/tpmdata1
+ echo foobar > /tmp/tpmdata2
+
+ cat >/tmp/result <<EOF
+ 11:sha1=5177e4ad69db92192c10e5f80402bf81bfec8a81
+ 11:sha256=37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13
+ 11:sha384=5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087
+ 11:sha512=961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b
+EOF
+
+ /usr/lib/systemd/systemd-measure calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 | cmp - /tmp/result
+else
+ echo "/usr/lib/systemd/systemd-measure not found, skipping the test case"
+fi
+
echo OK >/testok
exit 0
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-76-SYSCTL
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/assert.sh
+. "$(dirname "$0")"/assert.sh
+
+export SYSTEMD_LOG_LEVEL=debug
+
+echo "foo.bar=42" > /tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
+assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
+
+echo "-foo.foo=42" > /tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
+assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
+
+if ! systemd-detect-virt --quiet --container; then
+ ip link add hoge type dummy
+ udevadm wait /sys/class/net/hoge
+
+ cat >/tmp/foo.conf <<EOF
+net.ipv4.conf.*.drop_gratuitous_arp=1
+net.ipv4.*.*.bootp_relay=1
+net.ipv4.aaa.*.disable_policy=1
+EOF
+
+ echo 0 > /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp
+ echo 0 > /proc/sys/net/ipv4/conf/hoge/bootp_relay
+ echo 0 > /proc/sys/net/ipv4/conf/hoge/disable_policy
+
+ assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf
+ assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1"
+ assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1"
+ assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0"
+fi
+
+touch /testok
['systemd-tmp.conf', ''],
['tmp.conf', ''],
['x11.conf', ''],
+ ['provision.conf', ''],
]
foreach pair : files
--- /dev/null
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+# See tmpfiles.d(5) for details
+
+# Provision additional login messages from credentials, if they are set. Note
+# that these lines are NOPs if the credentials are not set or if the files
+# already exist.
+f^ /etc/motd.d/50-provision.conf - - - - login.motd
+f^ /etc/issue.d/50-provision.conf - - - - login.issue
+
+# Provision a /etc/hosts file from credentials.
+f^ /etc/hosts - - - - network.hosts
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
-# The official unmodified version of the script can be found at
-# https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh
+set -eux
-set -e
+COVERITY_SCAN_TOOL_BASE="/tmp/coverity-scan-analysis"
+COVERITY_SCAN_PROJECT_NAME="systemd/systemd"
-# Declare build command
-COVERITY_SCAN_BUILD_COMMAND="ninja -C cov-build"
+function coverity_install_script {
+ local platform tool_url tool_archive
-# Environment check
-# Use default values if not set
-SCAN_URL=${SCAN_URL:="https://scan.coverity.com"}
-TOOL_BASE=${TOOL_BASE:="/tmp/coverity-scan-analysis"}
-UPLOAD_URL=${UPLOAD_URL:="https://scan.coverity.com/builds"}
+ platform=$(uname)
+ tool_url="https://scan.coverity.com/download/${platform}"
+ tool_archive="/tmp/cov-analysis-${platform}.tgz"
-# These must be set by environment
-echo -e "\033[33;1mNote: COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN are available on Project Settings page on scan.coverity.com\033[0m"
-[ -z "$COVERITY_SCAN_PROJECT_NAME" ] && echo "ERROR: COVERITY_SCAN_PROJECT_NAME must be set" && exit 1
-[ -z "$COVERITY_SCAN_NOTIFICATION_EMAIL" ] && echo "ERROR: COVERITY_SCAN_NOTIFICATION_EMAIL must be set" && exit 1
-[ -z "$COVERITY_SCAN_BRANCH_PATTERN" ] && echo "ERROR: COVERITY_SCAN_BRANCH_PATTERN must be set" && exit 1
-[ -z "$COVERITY_SCAN_BUILD_COMMAND" ] && echo "ERROR: COVERITY_SCAN_BUILD_COMMAND must be set" && exit 1
-[ -z "$COVERITY_SCAN_TOKEN" ] && echo "ERROR: COVERITY_SCAN_TOKEN must be set" && exit 1
+ set +x # this is supposed to hide COVERITY_SCAN_TOKEN
+ echo -e "\033[33;1mDownloading Coverity Scan Analysis Tool...\033[0m"
+ wget -nv -O "$tool_archive" "$tool_url" --post-data "project=$COVERITY_SCAN_PROJECT_NAME&token=${COVERITY_SCAN_TOKEN:?}"
+ set -x
-# Verify this branch should run
-if [[ "${CURRENT_REF^^}" =~ "${COVERITY_SCAN_BRANCH_PATTERN^^}" ]]; then
- echo -e "\033[33;1mCoverity Scan configured to run on branch ${CURRENT_REF}\033[0m"
-else
- echo -e "\033[33;1mCoverity Scan NOT configured to run on branch ${CURRENT_REF}\033[0m"
- exit 1
-fi
-
-# Verify upload is permitted
-AUTH_RES=`curl -s --form project="$COVERITY_SCAN_PROJECT_NAME" --form token="$COVERITY_SCAN_TOKEN" $SCAN_URL/api/upload_permitted`
-if [ "$AUTH_RES" = "Access denied" ]; then
- echo -e "\033[33;1mCoverity Scan API access denied. Check COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN.\033[0m"
- exit 1
-else
- AUTH=`echo $AUTH_RES | jq .upload_permitted`
- if [ "$AUTH" = "true" ]; then
- echo -e "\033[33;1mCoverity Scan analysis authorized per quota.\033[0m"
- else
- WHEN=`echo $AUTH_RES | jq .next_upload_permitted_at`
- echo -e "\033[33;1mCoverity Scan analysis NOT authorized until $WHEN.\033[0m"
- exit 1
- fi
-fi
-
-TOOL_DIR=`find $TOOL_BASE -type d -name 'cov-analysis*'`
-export PATH="$TOOL_DIR/bin:$PATH"
-
-# Disable CCACHE for cov-build to compilation units correctly
-export CCACHE_DISABLE=1
-
-# FUNCTION DEFINITIONS
-# --------------------
-_help()
-{
- # displays help and exits
- cat <<-EOF
- USAGE: $0 [CMD] [OPTIONS]
-
- CMD
- build Issue Coverity build
- upload Upload coverity archive for analysis
- Note: By default, archive is created from default results directory.
- To provide custom archive or results directory, see --result-dir
- and --tar options below.
-
- OPTIONS
- -h,--help Display this menu and exits
-
- Applicable to build command
- ---------------------------
- -o,--out-dir Specify Coverity intermediate directory (defaults to 'cov-int')
- -t,--tar bool, archive the output to .tgz file (defaults to false)
-
- Applicable to upload command
- ----------------------------
- -d, --result-dir Specify result directory if different from default ('cov-int')
- -t, --tar ARCHIVE Use custom .tgz archive instead of intermediate directory or pre-archived .tgz
- (by default 'analysis-result.tgz'
- EOF
- return;
-}
-
-_pack()
-{
- RESULTS_ARCHIVE=${RESULTS_ARCHIVE:-'analysis-results.tgz'}
-
- echo -e "\033[33;1mTarring Coverity Scan Analysis results...\033[0m"
- tar czf $RESULTS_ARCHIVE $RESULTS_DIR
- SHA=`git rev-parse --short HEAD`
-
- PACKED=true
+ mkdir -p "$COVERITY_SCAN_TOOL_BASE"
+ pushd "$COVERITY_SCAN_TOOL_BASE"
+ tar xzf "$tool_archive"
+ popd
}
+function run_coverity {
+ local results_dir tool_dir results_archive sha response status_code
-_build()
-{
- echo -e "\033[33;1mRunning Coverity Scan Analysis Tool...\033[0m"
- local _cov_build_options=""
- #local _cov_build_options="--return-emit-failures 8 --parse-error-threshold 85"
- eval "${COVERITY_SCAN_BUILD_COMMAND_PREPEND}"
- COVERITY_UNSUPPORTED=1 cov-build --dir $RESULTS_DIR $_cov_build_options sh -c "$COVERITY_SCAN_BUILD_COMMAND"
- cov-import-scm --dir $RESULTS_DIR --scm git --log $RESULTS_DIR/scm_log.txt
+ results_dir="cov-int"
+ tool_dir=$(find "$COVERITY_SCAN_TOOL_BASE" -type d -name 'cov-analysis*')
+ results_archive="analysis-results.tgz"
+ sha=$(git rev-parse --short HEAD)
- if [ $? != 0 ]; then
- echo -e "\033[33;1mCoverity Scan Build failed: $TEXT.\033[0m"
- return 1
- fi
-
- [ -z $TAR ] || [ $TAR = false ] && return 0
+ meson -Dman=false build
+ COVERITY_UNSUPPORTED=1 "$tool_dir/bin/cov-build" --dir "$results_dir" sh -c "ninja -C ./build -v"
+ "$tool_dir/bin/cov-import-scm" --dir "$results_dir" --scm git --log "$results_dir/scm_log.txt"
- if [ "$TAR" = true ]; then
- _pack
- fi
-}
+ tar czf "$results_archive" "$results_dir"
-
-_upload()
-{
- # pack results
- [ -z $PACKED ] || [ $PACKED = false ] && _pack
-
- # Upload results
+ set +x # this is supposed to hide COVERITY_SCAN_TOKEN
echo -e "\033[33;1mUploading Coverity Scan Analysis results...\033[0m"
response=$(curl \
- --silent --write-out "\n%{http_code}\n" \
- --form project=$COVERITY_SCAN_PROJECT_NAME \
- --form token=$COVERITY_SCAN_TOKEN \
- --form email=$COVERITY_SCAN_NOTIFICATION_EMAIL \
- --form file=@$RESULTS_ARCHIVE \
- --form version=$SHA \
- --form description="Travis CI build" \
- $UPLOAD_URL)
+ --silent --write-out "\n%{http_code}\n" \
+ --form project="$COVERITY_SCAN_PROJECT_NAME" \
+ --form token="${COVERITY_SCAN_TOKEN:?}" \
+ --form email="${COVERITY_SCAN_NOTIFICATION_EMAIL:?}" \
+ --form file="@$results_archive" \
+ --form version="$sha" \
+ --form description="Daily build" \
+ https://scan.coverity.com/builds)
printf "\033[33;1mThe response is\033[0m\n%s\n" "$response"
status_code=$(echo "$response" | sed -n '$p')
- # Coverity Scan used to respond with 201 on successfully receiving analysis results.
- # Now for some reason it sends 200 and may change back in the foreseeable future.
- # See https://github.com/pmem/pmdk/commit/7b103fd2dd54b2e5974f71fb65c81ab3713c12c5
if [ "$status_code" != "200" ]; then
- TEXT=$(echo "$response" | sed '$d')
- echo -e "\033[33;1mCoverity Scan upload failed: $TEXT.\033[0m"
- exit 1
+ echo -e "\033[33;1mCoverity Scan upload failed: $(echo "$response" | sed '$d').\033[0m"
+ return 1
fi
-
- echo -e "\n\033[33;1mCoverity Scan Analysis completed successfully.\033[0m"
- exit 0
+ set -x
}
-# PARSE COMMAND LINE OPTIONS
-# --------------------------
-
-case $1 in
- -h|--help)
- _help
- exit 0
- ;;
- build)
- CMD='build'
- TEMP=`getopt -o ho:t --long help,out-dir:,tar -n '$0' -- "$@"`
- _ec=$?
- [[ $_ec -gt 0 ]] && _help && exit $_ec
- shift
- ;;
- upload)
- CMD='upload'
- TEMP=`getopt -o hd:t: --long help,result-dir:tar: -n '$0' -- "$@"`
- _ec=$?
- [[ $_ec -gt 0 ]] && _help && exit $_ec
- shift
- ;;
- *)
- _help && exit 1 ;;
-esac
-
-RESULTS_DIR='cov-int'
-
-eval set -- "$TEMP"
-if [ $? != 0 ] ; then exit 1 ; fi
-
-# extract options and their arguments into variables.
-if [[ $CMD == 'build' ]]; then
- TAR=false
- while true ; do
- case $1 in
- -h|--help)
- _help
- exit 0
- ;;
- -o|--out-dir)
- RESULTS_DIR="$2"
- shift 2
- ;;
- -t|--tar)
- TAR=true
- shift
- ;;
- --) _build; shift ; break ;;
- *) echo "Internal error" ; _help && exit 6 ;;
- esac
- done
-
-elif [[ $CMD == 'upload' ]]; then
- while true ; do
- case $1 in
- -h|--help)
- _help
- exit 0
- ;;
- -d|--result-dir)
- CHANGE_DEFAULT_DIR=true
- RESULTS_DIR="$2"
- shift 2
- ;;
- -t|--tar)
- RESULTS_ARCHIVE="$2"
- [ -z $CHANGE_DEFAULT_DIR ] || [ $CHANGE_DEFAULT_DIR = false ] && PACKED=true
- shift 2
- ;;
- --) _upload; shift ; break ;;
- *) echo "Internal error" ; _help && exit 6 ;;
- esac
- done
-
-fi
+coverity_install_script
+run_coverity
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-# Download and extract coverity tool
-
-set -e
-set -o pipefail
-
-# Environment check
-if [ -z "$COVERITY_SCAN_TOKEN" ]; then
- echo >&2 'ERROR: COVERITY_SCAN_TOKEN must be set'
- exit 1
-fi
-
-# Use default values if not set
-PLATFORM="$(uname)"
-TOOL_BASE="${TOOL_BASE:-/tmp/coverity-scan-analysis}"
-TOOL_ARCHIVE="${TOOL_ARCHIVE:-/tmp/cov-analysis-${PLATFORM}.tgz}"
-TOOL_URL="https://scan.coverity.com/download/${PLATFORM}"
-
-# Make sure wget is installed
-sudo apt-get update && sudo apt-get -y install wget
-
-# Get coverity tool
-if [ ! -d "$TOOL_BASE" ]; then
- # Download Coverity Scan Analysis Tool
- if [ ! -e "$TOOL_ARCHIVE" ]; then
- echo -e "\033[33;1mDownloading Coverity Scan Analysis Tool...\033[0m"
- wget -nv -O "$TOOL_ARCHIVE" "$TOOL_URL" --post-data "project=$COVERITY_SCAN_PROJECT_NAME&token=$COVERITY_SCAN_TOKEN"
- fi
-
- # Extract Coverity Scan Analysis Tool
- echo -e "\033[33;1mExtracting Coverity Scan Analysis Tool...\033[0m"
- mkdir -p "$TOOL_BASE"
- pushd "$TOOL_BASE"
- tar xzf "$TOOL_ARCHIVE"
- popd
-fi
-
-echo -e "\033[33;1mCoverity Scan Analysis Tool can be found at $TOOL_BASE ...\033[0m"
ConditionPathExists=/dev/console
[Service]
-# The '-o' option value tells agetty to replace 'login' arguments with an
-# option to preserve environment (-p), followed by '--' for safety, and then
-# the entered username.
+# The '-o' option value tells agetty to replace 'login' arguments with an option to preserve environment (-p),
+# followed by '--' for safety, and then the entered username.
ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear --keep-baud - 115200,38400,9600 $TERM
Type=idle
Restart=always
Before=rescue.service
[Service]
-# The '-o' option value tells agetty to replace 'login' arguments with an
-# option to preserve environment (-p), followed by '--' for safety, and then
-# the entered username.
-ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear --keep-baud - 115200,38400,9600 $TERM
+# The '-o' option value tells agetty to replace 'login' arguments with an option to preserve environment (-p),
+# followed by '--' for safety, and then the entered username.
+ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear - $TERM
Type=idle
Restart=always
RestartSec=0
DeviceAllow=char-* rw
ExecStart=!!{{ROOTLIBEXECDIR}}/systemd-networkd
ExecReload=networkctl reload
+FileDescriptorStoreMax=512
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-sysctl
TimeoutSec=90s
+LoadCredential=sysctl.extra
Conflicts=shutdown.target
After=systemd-remount-fs.service
Before=sysinit.target shutdown.target systemd-update-done.service
-ConditionNeedsUpdate=/etc
+ConditionNeedsUpdate=|/etc
+ConditionCredential=|sysusers.extra
[Service]
Type=oneshot
LoadCredential=passwd.hashed-password.root
LoadCredential=passwd.plaintext-password.root
LoadCredential=passwd.shell.root
+
+# Also, allow configuring extra sysusers lines via a credential
+LoadCredential=sysusers.extra
ExecStart=systemd-tmpfiles --clean
SuccessExitStatus=DATAERR
IOSchedulingClass=idle
+LoadCredential=tmpfiles.extra
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --prefix=/dev --create --boot
SuccessExitStatus=DATAERR CANTCREAT
+LoadCredential=tmpfiles.extra
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
SuccessExitStatus=DATAERR CANTCREAT
+LoadCredential=tmpfiles.extra
+LoadCredential=login.motd
+LoadCredential=login.issue
+LoadCredential=network.hosts
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
-# Empty file to mask its counterpart for unpriviledged users and thus cancels
+# Empty file to mask its counterpart for unprivileged users and thus cancels
# "After=systemd-user-session.service" ordering constraint so that root can log
# in even if the boot process is not yet finished.