# SPDX-License-Identifier: LGPL-2.1-or-later
+# vi: sw=2 ts=2 et:
apparmor:
- - '**/*apparmor*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*apparmor*'
binfmt:
- - '**/*binfmt*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*binfmt*'
btrfs:
- - '**/*btrfs*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*btrfs*'
build-system:
- - meson_options.txt
- - '**/meson.build'
- - Makefile
- - configure
+ - changed-files:
+ - any-glob-to-any-file: ['meson_options.txt', '**/meson.build', 'Makefile', 'configure']
busctl:
- - '**/*busctl*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*busctl*'
cgls:
- - '**/*cgls*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*cgls*'
cgtop:
- - '**/*cgtop*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*cgtop*'
ci:
- - '.github/*'
+ - changed-files:
+ - any-glob-to-any-file: '.github/*'
coccinelle:
- - coccinelle/*
+ - changed-files:
+ - any-glob-to-any-file: 'coccinelle/*'
coredump:
- - '**/*coredump*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*coredump*'
documentation:
- - NEWS
- - README*
- - docs/*
- - man/*
+ - changed-files:
+ - any-glob-to-any-file: ['NEWS', 'README*', 'docs/*', 'man/*']
env-generator:
- - '**/*environment*generator*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*environment*generator*'
debug-generator:
- - '**/debug-generator*'
+ - changed-files:
+ - any-glob-to-any-file: '**/debug-generator*'
fstab-generator:
- - '**/*fstab-generator*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*fstab-generator*'
growfs:
- - '**/*growfs*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*growfs*'
hwdb:
- - hwdb.d/**/*
+ - changed-files:
+ - any-glob-to-any-file: 'hwdb.d/**/*'
journal:
- - src/journal/*
- - src/libsystemd/sd-journal/*
+ - changed-files:
+ - any-glob-to-any-file: ['src/journal/*', 'src/libsystemd/sd-journal/*']
journal-remote:
- - src/journal-remote/*
+ - changed-files:
+ - any-glob-to-any-file: 'src/journal-remote/*'
+login:
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-login*/**'
+logind:
+ - changed-files:
+ - any-glob-to-any-file: 'src/login/*'
meson:
- - meson_options.txt
- - '**/meson.build'
+ - changed-files:
+ - any-glob-to-any-file: ['meson_options.txt', '**/meson.build']
mkosi:
- - .mkosi/*
- - mkosi.build
+ - changed-files:
+ - any-glob-to-any-file: ['.mkosi/*', 'mkosi.build']
network:
- - src/libsystemd-network/**/*
- - src/network/**/*
+ - changed-files:
+ - any-glob-to-any-file: ['src/libsystemd-network/**/*', 'src/network/**/*']
portable:
- - src/portable/**/*
+ - changed-files:
+ - any-glob-to-any-file: 'src/portable/**/*'
rc-local-generator:
- - src/rc-local-generator/*
+ - changed-files:
+ - any-glob-to-any-file: 'src/rc-local-generator/*'
repart:
- - '**/*repart*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*repart*'
resolve:
- - '**/*resolve*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*resolve*'
rfkill:
- - '**/*rfkill*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*rfkill*'
rpm:
- - src/rpm/*
+ - changed-files:
+ - any-glob-to-any-file: 'src/rpm/*'
run:
- - src/run/*
- - man/systemd-run*
+ - changed-files:
+ - any-glob-to-any-file: ['src/run/*', 'man/systemd-run*']
sd-boot/sd-stub/bootctl:
- - src/boot/**/*
- - man/bootctl*
- - man/systemd-boot.xml
+ - changed-files:
+ - any-glob-to-any-file: ['src/boot/**/*', 'man/bootctl*', 'man/systemd-boot.xml']
sd-bus:
- - '**/sd-bus*/**'
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-bus*/**'
sd-daemon:
- - '**/sd-daemon*/**'
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-daemon*/**'
sd-device:
- - '**/sd-device*/**'
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-device*/**'
sd-event:
- - '**/sd-event*/**'
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-event*/**'
sd-hwdb:
- - '**/sd-hwdb*/**'
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-hwdb*/**'
sd-id128:
- - '**/sd-id128*/**'
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-id128*/**'
sd-netlink:
- - '**/sd-netlink*/**'
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-netlink*/**'
sd-path:
- - '**/sd-path*/**'
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-path*/**'
sd-resolve:
- - '**/sd-resolve*/**'
+ - changed-files:
+ - any-glob-to-any-file: '**/sd-resolve*/**'
selinux:
- - '**/*selinux*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*selinux*'
sleep:
- - '**/*sleep*'
+ - changed-files:
+ - any-glob-to-any-file: ['src/shared/*sleep*', 'src/sleep/*']
smack:
- - '**/*smack*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*smack*'
socket-proxy:
- - '**/*socket-proxy*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*socket-proxy*'
sysv-generator:
- - '**/*sysv-generator*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*sysv-generator*'
systemctl:
- - '**/systemctl*'
+ - changed-files:
+ - any-glob-to-any-file: '**/systemctl*'
sysupdate:
- - '**/*sysupdate*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*sysupdate*'
sysvcompat:
- - '**/*sysv*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*sysv*'
sysctl:
- - '**/*sysctl*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*sysctl*'
timedate:
- - '**/*timedate*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*timedate*'
timesync:
- - '**/*timesync*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*timesync*'
tests:
- - src/test/**/*
- - src/fuzz/**/*
- - test/**/*
- - '**/test-*'
- - .github/workflows/*
+ - changed-files:
+ - any-glob-to-any-file: [
+ 'src/test/**/*',
+ 'src/fuzz/**/*',
+ 'test/**/*',
+ '**/test-*',
+ '.github/workflows/*'
+ ]
tpm2:
- - '**/*tpm2*'
- - '**/*tpm-*'
+ - changed-files:
+ - any-glob-to-any-file: ['**/*tpm2*', '**/*tpm-*']
udev:
- - src/udev/**/*
- - src/libudev/*
- - man/*udev*
+ - changed-files:
+ - any-glob-to-any-file: ['src/udev/**/*', 'src/libudev/*', 'man/*udev*']
uki:
- - '**/ukify*'
+ - changed-files:
+ - any-glob-to-any-file: '**/ukify*'
units:
- - units/**/*
+ - changed-files:
+ - any-glob-to-any-file: 'units/**/*'
userdb:
- - '**/*userdb*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*userdb*'
util-lib:
- - src/fundamental/**/*
- - src/basic/**/*
- - src/shared/**/*
+ - changed-files:
+ - any-glob-to-any-file: ['src/fundamental/**/*', 'src/basic/**/*', 'src/shared/**/*']
vconsole:
- - '**/*vconsole*'
+ - changed-files:
+ - any-glob-to-any-file: '**/*vconsole*'
xdg-autostart:
- - '**/**xdg-autostart-generator*'
+ - changed-files:
+ - any-glob-to-any-file: '**/**xdg-autostart-generator*'
sanitizer: ${{ matrix.sanitizer }}
output-sarif: true
- name: Upload Crash
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
+ uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392
if: failure() && steps.build.outcome == 'success'
with:
name: ${{ matrix.sanitizer }}-${{ matrix.architecture }}-artifacts
path: ./out/artifacts
- name: Upload Sarif
if: always() && steps.build.outcome == 'success'
- uses: github/codeql-action/upload-sarif@407ffafae6a767df3e0230c3df91b6443ae8df75
+ uses: github/codeql-action/upload-sarif@012739e5082ff0c22ca6d6ab32e07c36df03c4a4
with:
# Path to SARIF file relative to the root of the repository
sarif_file: cifuzz-sarif/results.sarif
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- name: Initialize CodeQL
- uses: github/codeql-action/init@407ffafae6a767df3e0230c3df91b6443ae8df75
+ uses: github/codeql-action/init@012739e5082ff0c22ca6d6ab32e07c36df03c4a4
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql-config.yml
- run: sudo -E .github/workflows/unit_tests.sh SETUP
- name: Autobuild
- uses: github/codeql-action/autobuild@407ffafae6a767df3e0230c3df91b6443ae8df75
+ uses: github/codeql-action/autobuild@012739e5082ff0c22ca6d6ab32e07c36df03c4a4
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@407ffafae6a767df3e0230c3df91b6443ae8df75
+ uses: github/codeql-action/analyze@012739e5082ff0c22ca6d6ab32e07c36df03c4a4
echo ${{ github.event.number }} >./${{ env.PULL_REQUEST_METADATA_DIR }}/${{ env.PULL_REQUEST_METADATA_FILE }}
- name: Upload Pull Request Metadata artifact
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
+ uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392
with:
name: ${{ env.PULL_REQUEST_METADATA_FILE }}
path: ${{ env.PULL_REQUEST_METADATA_DIR }}
on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review, closed]
+ paths-ignore:
+ - '.github/labeler.yml'
+ - '.github/workflows/labeler.yml'
+ # Allow testing changes made to the labeler configuration
+ pull_request:
+ paths:
+ - '.github/labeler.yml'
+ - '.github/workflows/labeler.yml'
issue_comment:
types: [created]
pull-requests: write
steps:
+ - name: Repository checkout
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
+ if: github.event_name == 'pull_request'
+
- name: Label PR based on policy in labeler.yml
- uses: actions/labeler@ac9175f8a1f3625fd0d4fb234536d26811351594
- if: github.event_name == 'pull_request_target' && github.event.action != 'closed'
+ uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9
+ if: startsWith(github.event_name, 'pull_request') && github.event.action != 'closed'
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/labeler.yml
- sync-labels: "" # This is a workaround for issue 18671
+ sync-labels: false
- name: Set or remove labels based on systemd development workflow
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
- if: github.event_name == 'pull_request_target' && github.event.action != 'closed' && !github.event.pull_request.draft
+ if: startsWith(github.event_name, 'pull_request') && github.event.action != 'closed' && !github.event.pull_request.draft
with:
script: |
response = await github.rest.issues.listLabelsOnIssue({
- name: Remove specific labels when PR is closed or merged
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
- if: github.event_name == 'pull_request_target' && github.event.action == 'closed'
+ if: startsWith(github.event_name, 'pull_request') && github.event.action == 'closed'
with:
script: |
for (const label of ["please-review",
-meson==1.3.0 \
- --hash=sha256:4ba253ef60e454e23234696119cbafa082a0aead0bd3bbf6991295054795f5dc \
- --hash=sha256:e9f54046ce5b9a1f3024f7a7d52f19f085fd57c9d26a5db0cfcf0750572a8fd8
+meson==1.3.1 \
+ --hash=sha256:6020568bdede1643d4fb41e28215be38eff5d52da28ac7d125457c59e0032ad7 \
+ --hash=sha256:d5223ecca9564d735d36daaba2571abc6c032c8c3a7ffa0674e803ef0c7e0219
ninja==1.11.1.1 \
--hash=sha256:18302d96a5467ea98b68e1cae1ae4b4fb2b2a56a82b955193c637557c7273dbd \
--hash=sha256:185e0641bde601e53841525c4196278e9aaf4463758da6dd1e752c0a0f54136a \
line, and then generate a mount unit for it using a udev generated symlink
based on lo_file_name.
+* teach systemd-nspawn the boot assessment logic: hook up vpick's try counters
+ with success notifications from nspawn payloads. When this is enabled,
+ automatically support reverting back to older OS versin images if newer ones
+ fail to boot.
+
+* implement new "systemd-fsrebind" tool that works like gpt-auto-generator but
+ looks at a root dir and then applies vpick on various dirs/images to pick a
+ root tree, a /usr/ tree, a /home/, a /srv/, a /var/ tree and so on. Dirs
+ could also be btrfs subvols (combine with btrfs auto-snapshort approach for
+ creating versions like these automatically).
+
* remove tomoyo support, it's obsolete and unmaintained apparently
* In .socket units, add ConnectStream=, ConnectDatagram=,
* automatic boot assessment: add one more default success check that just waits
for a bit after boot, and blesses the boot if the system stayed up that long.
-* implement concept of "versioned" resources inside a dir, and write a spec for
- it. Make all tools in systemd, in particular
- RootImage=/RootDirectory=/--image=/--directory= implement this. Idea:
- directories ending in ".v/" indicate a directory with versioned resources in
- them. Versioned resources inside a .v dir are always named in the pattern
- <prefix>_<version>[+<tries-left>[-<tries-done>]].<suffix>
-
-* add support for using this .v/ logic on the root fs itself: in the initrd,
- after mounting the rootfs, look for root-<arch>.v/ in the root fs, and then
- apply the logic, moving the switch root logic there.
-
* systemd-repart: add support for generating ISO9660 images
* systemd-repart: in addition to the existing "factory reset" mode (which
passwords, not just the first. i.e. if there are multiple defined, prefer
unlocked over locked and prefer non-empty over empty.
-* maybe add a tool inspired by the GPT auto discovery spec that runs in the
- initrd and rearranges the rootfs hierarchy via bind mounts, if
- enabled. Specifically in some top-level dir /@auto/ it will look for
- dirs/symlinks/subvolumes that are named after their purpose, and optionally
- encode a version as well as assessment counters, and then mount them into the
- file system tree to boot into, similar to how we do that for the gpt auto
- logic. Maybe then bind mount the original root into /.superior or something
- like that (so that update tools can look there). Further discussion in this
- thread:
- https://lists.freedesktop.org/archives/systemd-devel/2021-November/047059.html
- The GPT dissection logic should automatically enable this tool whenever we
- detect a specially marked root fs (i.e introduce a new generic root gpt type
- for this, that is arch independent). The also implement this in the image
- dissection logic, so that nspawn/RootImage= and so on grok it. Maybe make
- generic enough so that it can also work for ostrees arrangements.
-
-* if a path ending in ".auto.d/" is set for RootDirectory=/RootImage= then do a
- strverscmp() of everything inside that dir and use that. i.e. implement very
- simple version control. Also use this in systemd-nspawn --image= and so on.
-
* homed: while a home dir is not activated generate slightly different NSS
records for it, that reports the home dir as "/" and the shell as some binary
provided by us. Then, when an SSH login happens and SSH permits it our binary
- make user manager instances create and use a user-specific key (the one in
/var/lib is root-only) and add --user switch to systemd-creds to use it
-* add tpm.target or so which is delayed until TPM2 device showed up in case
- firmware indicates there is one.
-
* TPM2: auto-reenroll in cryptsetup, as fallback for hosed firmware upgrades
and such
/* FIXME
* - issues with parsing stuff like
- * * int foo[ELEMENTSOF(bar)] = {};
- * * validchars = UPPERCASE_LETTERS DIGITS;
- * * multiline compound literals (some instances)
- * * compound literals in function calls (some instances)
- * * keywords in macro invocations like FOREACH_DIRENT_ALL(de, d, return -errno)
- * (also, see FIXME in the TEST stuff below)
+ * - validchars = UPPERCASE_LETTERS DIGITS;
+ * - see: https://github.com/coccinelle/coccinelle/issues/341
+ * - keywords in macro invocations like FOREACH_DIRENT_ALL(de, d, return -errno)
+ * - see: https://github.com/coccinelle/coccinelle/issues/340
+ * - also see the FIXME in the TEST() stuff below
*/
/* This file contains parsing hacks for Coccinelle (spatch), to make it happy with some of our more complex
* special cases in which the parser incorrectly infers information that then causes issues in valid code
* later down the line.
*
- * Inspired by a similarly named file [0] from the Coccinelle sources, and the original bultin macros [1].
+ * Inspired by a similarly named file [0] from the Coccinelle sources, and the original builtin macros [1].
*
* [0] https://github.com/coccinelle/coccinelle/blob/master/parsing_c/parsing_hacks.ml
* [1] https://github.com/coccinelle/coccinelle/blob/master/standard.h
/* Coccinelle doesn't know this keyword, so just drop it, since it's not important for any of our rules. */
#define thread_local
-/* Coccinelle fails to get this one from the included headers, so let's just drop it. */
+/* Coccinelle fails to parse these from the included headers, so let's just drop them. */
#define PAM_EXTERN
+#define STACK_OF(x)
/* Mark a couple of iterator explicitly as iterators, otherwise Coccinelle gets a bit confused. Coccinelle
* can usually infer this information automagically, but in these specific cases it needs a bit of help. */
#define LIST_FOREACH(name, i, head) YACFE_ITERATOR
#define ORDERED_HASHMAP_FOREACH(e, h) YACFE_ITERATOR
#define SET_FOREACH(e, s) YACFE_ITERATOR
+#define STRV_FOREACH_BACKWARDS YACFE_ITERATOR
/* Coccinelle really doesn't like multiline macros that are not in the "usual" do { ... } while(0) format, so
* let's help it a little here by providing simplified one-line versions. */
#define CMSG_BUFFER_TYPE(x) union { uint8_t align_check[(size) >= CMSG_SPACE(0) && (size) == CMSG_ALIGN(size) ? 1 : -1]; }
+#define SD_ID128_MAKE(...) ((const sd_id128) {})
# A couple of notes:
#
# 1) Limit this to 10 files at once, as processing the ASTs is _very_ memory hungry - e.g. with 20 files
- # at once one spatch process can take around 2.5 GiB of RAM, which can easily eat up all available RAM
- # when paired together with parallel
+ # at once one spatch process can take around 2.5 GiB of RAM, which can easily eat up all available RAM
+ # when paired together with parallel
#
# 2) Make sure spatch can find our includes via -I <dir>, similarly as we do when compiling stuff.
# Also, include the system include path as well, since we're not kernel and we make use of the stdlib
# (and other libraries).
#
# 3) Make sure to include includes from includes (--recursive-includes), but use them only to get type
- # definitions (--include-headers-for-types) - otherwise we'd start formatting them as well, which might
- # be unwanted, especially for includes we fetch verbatim from third-parties
+ # definitions (--include-headers-for-types) - otherwise we'd start formatting them as well, which might
+ # be unwanted, especially for includes we fetch verbatim from third-parties
#
# 4) Explicitly undefine the SD_BOOT symbol, so Coccinelle ignores includes guarded by #if SD_BOOT
#
--recursive-includes \
--include-headers-for-types \
--undefined SD_BOOT \
+ --undefined ENABLE_DEBUG_HASHMAP \
--macro-file-builtins "coccinelle/parsing_hacks.h" \
--smpl-spacing \
--sp-file "$script" \
@@
- siphash24_compress(&p, sizeof(p), s);
+ siphash24_compress_typesafe(p, s);
+
+@@
+union in_addr_union p;
+expression f, s;
+@@
+- siphash24_compress(&p, FAMILY_ADDRESS_SIZE(f), s);
++ in_addr_hash_func(&p, f, s);
devices sysfs path are actually backed by sysfs. Relaxing this verification
is useful for testing purposes.
+* `$SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC=` — Specifies an extra timespan that the
+ udev manager process waits for a worker process kills slow programs specified
+ by IMPORT{program}=, PROGRAM=, or RUN=, and finalizes the processing event.
+ If the worker process cannot finalize the event within the specified timespan,
+ the worker process is killed by the manager process. Defaults to 10 seconds.
+
`udevadm` and `systemd-hwdb`:
* `SYSTEMD_HWDB_UPDATE_BYPASS=` — If set to "1", execution of hwdb updates is skipped
<term><varname>StopIdleSessionSec=</varname></term>
<listitem><para>Specifies a timeout in seconds, or a time span value after which
- <filename>systemd-logind</filename> checks the idle state of all sessions. Every session that is idle for
- longer then the timeout will be stopped. Defaults to <literal>infinity</literal>
- (<filename>systemd-logind</filename> is not checking the idle state of sessions). For details about the syntax
- of time spans, see
+ <filename>systemd-logind</filename> checks the idle state of all sessions. Every session that is idle
+ for longer than the timeout will be stopped. Note that this option doesn't apply to
+ <literal>greeter</literal> or <literal>lock-screen</literal> sessions. Defaults to
+ <literal>infinity</literal> (<filename>systemd-logind</filename> is not checking the idle state
+ of sessions). For details about the syntax of time spans, see
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
GetDynamicUsers(out a(us) users);
DumpUnitFileDescriptorStore(in s name,
out a(suuutuusu) entries);
+ StartAuxiliaryScope(in s name,
+ in ah pidfds,
+ in t flags,
+ in a(sv) properties,
+ out o job);
signals:
UnitNew(s id,
o unit);
<variablelist class="dbus-method" generated="True" extra-ref="DumpUnitFileDescriptorStore()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="StartAuxiliaryScope()"/>
+
<variablelist class="dbus-signal" generated="True" extra-ref="UnitNew"/>
<variablelist class="dbus-signal" generated="True" extra-ref="UnitRemoved"/>
file descriptors currently in the file descriptor store of the specified unit. This call is equivalent
to <function>DumpFileDescriptorStore()</function> on the
<interfacename>org.freedesktop.systemd1.Service</interfacename>. For further details, see below.</para>
+
+ <para><function>StartAuxiliaryScope()</function> creates a new scope unit from a service where calling
+ process resides. Set of processes that will be migrated to newly created scope is passed in as an array
+ of pidfds. This is useful for creating auxiliary scopes that should contain worker processes and their lifecycle
+ shouldn't be bound to a lifecycle of the service, e.g. they should continue running after the restart
+ of the service. Note that the main PID of the service can not be migrated to an auxiliary scope.
+ Also, <varname>flags</varname> argument must be 0 and is reserved for future extensions.</para>
</refsect2>
<refsect2>
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryHigh = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t TasksCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveTasksMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressBytes = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressPackets = ...;
<!--property MemoryZSwapCurrent is not documented!-->
+ <!--property EffectiveMemoryMax is not documented!-->
+
+ <!--property EffectiveMemoryHigh is not documented!-->
+
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
<!--property TasksCurrent is not documented!-->
+ <!--property EffectiveTasksMax is not documented!-->
+
<!--property IPIngressBytes is not documented!-->
<!--property IPIngressPackets is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
<variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryHigh = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t TasksCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveTasksMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressBytes = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressPackets = ...;
<!--property MemoryZSwapCurrent is not documented!-->
+ <!--property EffectiveMemoryMax is not documented!-->
+
+ <!--property EffectiveMemoryHigh is not documented!-->
+
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
<!--property TasksCurrent is not documented!-->
+ <!--property EffectiveTasksMax is not documented!-->
+
<!--property IPIngressBytes is not documented!-->
<!--property IPIngressPackets is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
<variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryHigh = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t TasksCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveTasksMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressBytes = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressPackets = ...;
<!--property MemoryZSwapCurrent is not documented!-->
+ <!--property EffectiveMemoryMax is not documented!-->
+
+ <!--property EffectiveMemoryHigh is not documented!-->
+
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
<!--property TasksCurrent is not documented!-->
+ <!--property EffectiveTasksMax is not documented!-->
+
<!--property IPIngressBytes is not documented!-->
<!--property IPIngressPackets is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
<variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryHigh = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t TasksCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveTasksMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressBytes = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressPackets = ...;
<!--property MemoryZSwapCurrent is not documented!-->
+ <!--property EffectiveMemoryMax is not documented!-->
+
+ <!--property EffectiveMemoryHigh is not documented!-->
+
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
<!--property TasksCurrent is not documented!-->
+ <!--property EffectiveTasksMax is not documented!-->
+
<!--property IPIngressBytes is not documented!-->
<!--property IPIngressPackets is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
<variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryHigh = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t TasksCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveTasksMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressBytes = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressPackets = ...;
<!--property MemoryZSwapCurrent is not documented!-->
+ <!--property EffectiveMemoryMax is not documented!-->
+
+ <!--property EffectiveMemoryHigh is not documented!-->
+
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
<!--property TasksCurrent is not documented!-->
+ <!--property EffectiveTasksMax is not documented!-->
+
<!--property IPIngressBytes is not documented!-->
<!--property IPIngressPackets is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
<variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t MemoryAvailable = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveMemoryHigh = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t CPUUsageNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly ay EffectiveCPUs = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t TasksCurrent = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t EffectiveTasksMax = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressBytes = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t IPIngressPackets = ...;
<!--property MemoryZSwapCurrent is not documented!-->
+ <!--property EffectiveMemoryMax is not documented!-->
+
+ <!--property EffectiveMemoryHigh is not documented!-->
+
<!--property CPUUsageNSec is not documented!-->
<!--property EffectiveCPUs is not documented!-->
<!--property TasksCurrent is not documented!-->
+ <!--property EffectiveTasksMax is not documented!-->
+
<!--property IPIngressBytes is not documented!-->
<!--property IPIngressPackets is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryMax"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryHigh"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
<variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EffectiveTasksMax"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
<variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
<function>QueueSignalUnit()</function>,
<function>SoftReboot()</function>, and
<function>DumpUnitFileDescriptorStore()</function> were added in version 254.</para>
+ <para><function>StartAuxiliaryScope()</function> was added in version 256.</para>
</refsect2>
<refsect2>
<title>Unit Objects</title>
<varname>MemorySwapCurrent</varname>,
<varname>MemorySwapPeak</varname>, and
<varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+ <para><varname>EffectiveMemoryHigh</varname>,
+ <varname>EffectiveMemoryMax</varname>,
+ <varname>EffectiveTasksMax</varname> were added in version 256.</para>
</refsect2>
<refsect2>
<title>Socket Unit Objects</title>
<varname>MemorySwapCurrent</varname>,
<varname>MemorySwapPeak</varname>, and
<varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+ <para><varname>EffectiveMemoryHigh</varname>,
+ <varname>EffectiveMemoryMax</varname>,
+ <varname>EffectiveTasksMax</varname> were added in version 256.</para>
</refsect2>
<refsect2>
<title>Mount Unit Objects</title>
<varname>MemorySwapCurrent</varname>,
<varname>MemorySwapPeak</varname>, and
<varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+ <para><varname>EffectiveMemoryHigh</varname>,
+ <varname>EffectiveMemoryMax</varname>,
+ <varname>EffectiveTasksMax</varname> were added in version 256.</para>
</refsect2>
<refsect2>
<title>Swap Unit Objects</title>
<varname>MemorySwapCurrent</varname>,
<varname>MemorySwapPeak</varname>, and
<varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+ <para><varname>EffectiveMemoryHigh</varname>,
+ <varname>EffectiveMemoryMax</varname>,
+ <varname>EffectiveTasksMax</varname> were added in version 256.</para>
</refsect2>
<refsect2>
<title>Slice Unit Objects</title>
<varname>MemorySwapCurrent</varname>,
<varname>MemorySwapPeak</varname>, and
<varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+ <para><varname>EffectiveMemoryHigh</varname>,
+ <varname>EffectiveMemoryMax</varname>,
+ <varname>EffectiveTasksMax</varname> were added in version 256.</para>
</refsect2>
<refsect2>
<title>Scope Unit Objects</title>
<varname>MemorySwapCurrent</varname>,
<varname>MemorySwapPeak</varname>, and
<varname>MemoryZSwapCurrent</varname> were added in version 255.</para>
+ <para><varname>EffectiveMemoryHigh</varname>,
+ <varname>EffectiveMemoryMax</varname>,
+ <varname>EffectiveTasksMax</varname> were added in version 256.</para>
</refsect2>
<refsect2>
<title>Job Objects</title>
'systemd-tmpfiles-setup-dev.service',
'systemd-tmpfiles-setup.service'],
''],
+ ['systemd-tpm2-generator', '8', [], ''],
['systemd-tpm2-setup.service',
'8',
['systemd-tpm2-setup', 'systemd-tpm2-setup-early.service'],
'HAVE_LIBCRYPTSETUP'],
['systemd-vmspawn', '1', [], 'ENABLE_VMSPAWN'],
['systemd-volatile-root.service', '8', ['systemd-volatile-root'], ''],
+ ['systemd-vpick', '1', [], ''],
['systemd-xdg-autostart-generator', '8', [], 'ENABLE_XDG_AUTOSTART'],
['systemd', '1', ['init'], ''],
['systemd.automount', '5', [], ''],
['systemd.time', '7', [], ''],
['systemd.timer', '5', [], ''],
['systemd.unit', '5', [], ''],
+ ['systemd.v', '7', [], ''],
['sysupdate.d', '5', [], 'ENABLE_SYSUPDATE'],
['sysusers.d', '5', [], 'ENABLE_SYSUSERS'],
['telinit', '8', [], 'HAVE_SYSV_COMPAT'],
<refsect1>
<title>Description</title>
- <para><filename>systemd-backlight@.service</filename> is a service that restores the display backlight
- brightness at early boot and saves it at shutdown. On disk, the backlight brightness is stored in
- <filename>/var/lib/systemd/backlight/</filename>. During loading, if the udev property
- <option>ID_BACKLIGHT_CLAMP</option> is not set to false, the brightness is clamped to a value of at least
- 1 or 5% of maximum brightness, whichever is greater. The percentage can be adjusted by specifying a
- percentage (needs to be suffixed with <literal>%</literal>, e.g. <literal>30%</literal>) to the property
- <option>ID_BACKLIGHT_CLAMP</option>.</para>
+ <para><filename>systemd-backlight@.service</filename> is a service that restores the brightness of
+ a display backlight or LED (e.g. keyboard backlight) device at early boot, and saves it at shutdown.
+ The brightness is stored in <filename>/var/lib/systemd/backlight/</filename>.</para>
+
+ <para>On restoring brightness of a display backlight device, <command>systemd-backlight</command> reads
+ <varname>ID_BACKLIGHT_CLAMP</varname> udev property, that takes a boolean value or a percentage (needs to
+ be suffixed with <literal>%</literal>, e.g. <literal>30%</literal>). When a percentage is specified, the
+ saved brightness is clamped to a value of at least 1 or the specified percentage of the maximum
+ brightness, whichever is greater. When unset or set to true, the brightness is clamped in the same way
+ with percentage 5%. When false, the saved brightness will not be clamped, and loaded as is.</para>
+
+ <para>On restoring brightness of a LED device, <command>systemd-backlight</command> reads
+ <varname>ID_LEDS_CLAMP</varname> udev property, that also takes a boolean value or a percentage. When a
+ percentage is specified, the saved brightness is clamped to the specified percentage of the maximum
+ brightness. When set to true, the brightness is clamped in the same way with percentage 5%. When unset or
+ set to false, the saved brightness will not be clamped, and loaded as is.</para>
</refsect1>
<refsect1>
mounted directly by <command>mount</command> and <citerefentry
project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>. For
details see below.</para>
+
+ <para>In place of the image path a <literal>.v/</literal> versioned directory may be specified, see
+ <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details.</para>
</refsect1>
<refsect1>
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
<member><ulink url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions Specification</ulink></member>
<member><citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>umount</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<term><option>-D</option></term>
<term><option>--directory=</option></term>
- <listitem><para>Directory to use as file system root for the
- container.</para>
+ <listitem><para>Directory to use as file system root for the container.</para>
- <para>If neither <option>--directory=</option>, nor
- <option>--image=</option> is specified the directory is
- determined by searching for a directory named the same as the
- machine name specified with <option>--machine=</option>. See
+ <para>If neither <option>--directory=</option>, nor <option>--image=</option> is specified the
+ directory is determined by searching for a directory named the same as the machine name specified
+ with <option>--machine=</option>. See
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
section "Files and Directories" for the precise search path.</para>
- <para>If neither <option>--directory=</option>,
- <option>--image=</option>, nor <option>--machine=</option>
- are specified, the current directory will
- be used. May not be specified together with
- <option>--image=</option>.</para></listitem>
+ <para>In place of the directory path a <literal>.v/</literal> versioned directory may be specified, see
+ <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details.</para>
+
+ <para>If neither <option>--directory=</option>, <option>--image=</option>, nor
+ <option>--machine=</option> are specified, the current directory will be used. May not be specified
+ together with <option>--image=</option>.</para></listitem>
</varlistentry>
<varlistentry>
<para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
together with <option>--directory=</option>, <option>--template=</option>.</para>
+ <para>In place of the image path a <literal>.v/</literal> versioned directory may be specified, see
+ <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details.</para>
+
<xi:include href="version-info.xml" xpointer="v211"/></listitem>
</varlistentry>
<member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename></member>
<member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.cred</filename></member>
<member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.raw</filename></member>
+ <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.sysext.raw</filename></member>
+ <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.confext.raw</filename></member>
<member><filename><replaceable>ESP</replaceable>/loader/addons/*.addon.efi</filename></member>
<member><filename><replaceable>ESP</replaceable>/loader/credentials/*.cred</filename></member>
</simplelist></para>
details on encrypted credentials. The generated <command>cpio</command> archive is measured into TPM
PCR 12 (if a TPM is present).</para></listitem>
- <listitem><para>Similarly, files <filename><replaceable>foo</replaceable>.efi.extra.d/*.raw</filename>
- are packed up in a <command>cpio</command> archive and placed in the <filename>/.extra/sysext/</filename>
- directory in the initrd file hierarchy. This is supposed to be used to pass additional system extension
- images to the initrd. See
+ <listitem><para>Similarly, files
+ <filename><replaceable>foo</replaceable>.efi.extra.d/*.sysext.raw</filename> are packed up in a
+ <command>cpio</command> archive and placed in the <filename>/.extra/sysext/</filename> directory in the
+ initrd file hierarchy. This is supposed to be used to pass additional system extension images to the
+ initrd. See
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
details on system extension images. The generated <command>cpio</command> archive containing these
system extension images is measured into TPM PCR 13 (if a TPM is present).</para></listitem>
+ <!-- Note: the actual suffix we look for for sysexts is just *.raw (not *.sysext.raw), for
+ compatibility reasons with old versions. But we want people to name their system extensions
+ properly, hence we document the *.sysext.raw suffix only. -->
+
+ <listitem><para>Similarly, files
+ <filename><replaceable>foo</replaceable>.efi.extra.d/*.confext.raw</filename> are packed up in a
+ <command>cpio</command> archive and placed in the <filename>/.extra/confext/</filename> directory in
+ the initrd file hierarchy. This is supposed to be used to pass additional configuration extension
+ images to the initrd. See
+ <citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
+ details on configuration extension images. The generated <command>cpio</command> archive containing
+ these system extension images is measured into TPM PCR 12 (if a TPM is present).</para></listitem>
+
<listitem><para>Similarly, files
<filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename> are loaded and verified as
PE binaries, and a <literal>.cmdline</literal> section is parsed from them. Addons are supposed to be
<para>Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means
every type of initrd will be measured two or three times: the initrd embedded in the kernel image will be
- measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials will be measured to both PCR
- 9 and PCR 12; the initrd synthesized from system extensions will be measured to both PCR 4 and PCR
- 9. Let's summarize the OS resources and the PCRs they are measured to:</para>
+ measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials (and the one synthesized
+ from configuration extensions) will be measured to both PCR 9 and PCR 12; the initrd synthesized from
+ system extensions will be measured to both PCR 4 and PCR 9. Let's summarize the OS resources and the PCRs
+ they are measured to:</para>
<table>
<title>OS Resource PCR Summary</title>
<entry>System Extensions (synthesized initrd from companion files)</entry>
<entry>9 + 13</entry>
</row>
+
+ <row>
+ <entry>Configuration Extensions (synthesized initrd from companion files)</entry>
+ <entry>9 + 12</entry>
+ </row>
</tbody>
</tgroup>
</table>
<varlistentry>
<term><varname>StubPcrInitRDSysExts</varname></term>
- <listitem><para>The PCR register index the systemd extensions for the initrd, which are picked up
- from the file system the kernel image is located on. Formatted as decimal ASCII string (e.g.
+ <listitem><para>The PCR register index the system extensions for the initrd, which are picked up from
+ the file system the kernel image is located on. Formatted as decimal ASCII string (e.g.
<literal>13</literal>). This variable is set if a measurement was successfully completed, and remains
unset otherwise.</para>
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>StubPcrInitRDConfExts</varname></term>
+
+ <listitem><para>The PCR register index the configuration extensions for the initrd, which are picked
+ up from the file system the kernel image is located on. Formatted as decimal ASCII string (e.g.
+ <literal>12</literal>). This variable is set if a measurement was successfully completed, and remains
+ unset otherwise.</para>
+
+ <xi:include href="version-info.xml" xpointer="v255"/></listitem>
+ </varlistentry>
</variablelist>
<para>Note that some of the variables above may also be set by the boot loader. The stub will only set
</varlistentry>
<varlistentry>
- <term><filename>/.extra/sysext/*.raw</filename></term>
- <listitem><para>System extension image files (suffix <literal>.raw</literal>) that are placed next to
- the unified kernel image (as described above) are copied into the
+ <term><filename>/.extra/sysext/*.sysext.raw</filename></term>
+ <listitem><para>System extension image files (suffix <literal>.sysext.raw</literal>) that are placed
+ next to the unified kernel image (as described above) are copied into the
<filename>/.extra/sysext/</filename> directory in the initrd execution environment.</para>
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><filename>/.extra/confext/*.confext.raw</filename></term>
+ <listitem><para>Configuration extension image files (suffix <literal>.confext.raw</literal>) that are
+ placed next to the unified kernel image (as described above) are copied into the
+ <filename>/.extra/confext/</filename> directory in the initrd execution environment.</para>
+
+ <xi:include href="version-info.xml" xpointer="v255"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><filename>/.extra/tpm2-pcr-signature.json</filename></term>
<listitem><para>The TPM2 PCR signature JSON object included in the <literal>.pcrsig</literal> PE
--- /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" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+<refentry id="systemd-tpm2-generator">
+
+ <refentryinfo>
+ <title>systemd-tpm2-generator</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-tpm2-generator</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-tpm2-generator</refname>
+ <refpurpose>Generator for inserting TPM2 synchronization point in the boot process</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/usr/lib/systemd/system-generators/systemd-tpm2-generator</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>systemd-tpm2-generator</filename> is a generator that adds a <varname>Wants=</varname>
+ dependency from <filename>sysinit.target</filename> to <filename>tpm2.target</filename> when it detects
+ that the firmware discovered a TPM2 device but the OS kernel so far did
+ not. <filename>tpm2.target</filename> is supposed to act as synchronization point for all services that
+ require TPM2 device access. See
+ <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details.</para>
+
+ <para>The <option>systemd.tpm2_wait=</option> kernel command line option may be used to override
+ behaviour of the generator. It accepts a boolean value: if true then <filename>tpm2.target</filename>
+ will be added as synchronization point even if the firmware has not detected a TPM2 device. If false, the
+ target will not be inserted even if firmware reported a device but the OS kernel doesn't expose a device
+ for it yet. The latter might be useful in environments where a suitable TPM2 driver for the available
+ hardware is not available.</para>
+
+ <para><filename>systemd-tpm2-generator</filename> implements
+ <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para><simplelist type="inline">
+ <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+ </simplelist></para>
+ </refsect1>
+</refentry>
--- /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-vpick"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>systemd-vpick</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-vpick</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-vpick</refname>
+ <refpurpose>Resolve paths to <literal>.v/</literal> versioned directories</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>systemd-vpick <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">PATH</arg></command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-vpick</command> resolves a file system path referencing a <literal>.v/</literal>
+ versioned directory to a path to the newest (by version) file contained therein. This tool provides a
+ command line interface for the
+ <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ logic.</para>
+
+ <para>The tool expects a path to a <literal>.v/</literal> directory as argument (either directly, or with
+ a triple underscore pattern as final component). It then determines the newest file contained in that
+ directory, and writes its path to standard output.</para>
+
+ <para>Unless the triple underscore pattern is passed as last component of the path, it is typically
+ necessary to at least specify the <option>--suffix=</option> switch to configure the file suffix to look
+ for.</para>
+
+ <para>If the specified path does not reference a <literal>.v/</literal> path (i.e. neither the final
+ component ends in <literal>.v</literal>, nor the penultimate does or the final one does contain a triple
+ underscore) it specified path is written unmodified to standard output.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>--basename=</option></term>
+ <term><option>-B</option></term>
+
+ <listitem><para>Overrides the "basename" of the files to look for, i.e. the part to the left of the
+ variable part of the filenames. Normally this is derived automatically from the filename of the
+ <literal>.v</literal> component of the specified path, or from the triple underscore pattern in the
+ last component of the specified path.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-V</option></term>
+
+ <listitem><para>Explicitly configures the version to select. If specified, a filename with the
+ specified version string will be looked for, instead of the newest version
+ available.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-A</option></term>
+
+ <listitem><para>Explicitly configures the architecture to select. If specified, a filename with the
+ specified architecture identifier will be looked for. If not specified only filenames with a locally
+ supported architecture are considered, or those without any architecture identifier.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--suffix=</option></term>
+ <term><option>-S</option></term>
+
+ <listitem><para>Configures the suffix of the filenames to consider. For the <literal>.v/</literal>
+ logic it is necessary to specify the suffix to look for, and the <literal>.v/</literal> component
+ must also carry the suffix immediately before <literal>.v</literal> in its name.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--type=</option></term>
+ <term><option>-t</option></term>
+
+ <listitem><para>Configures the inode type to look for in the <literal>.v/</literal> directory. Takes
+ one of <literal>reg</literal>, <literal>dir</literal>, <literal>sock</literal>,
+ <literal>fifo</literal>, <literal>blk</literal>, <literal>chr</literal>, <literal>lnk</literal> as
+ argument, each identifying an inode type. See <citerefentry
+ project='man-pages'><refentrytitle>inode</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details about inode types. If this option is used inodes not matching the specified type are filtered
+ and not taken into consideration.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--print=</option></term>
+ <term><option>-p</option></term>
+
+ <listitem><para>Configures what precisely to write to standard output. If not specified prints the
+ full, resolved path of the newest matching file in the <literal>.v/</literal> directory. This switch can be set to one of the following:</para>
+
+ <itemizedlist>
+ <listitem><para>If set to <literal>filename</literal>, will print only the filename instead of the full path of the resolved file.</para></listitem>
+ <listitem><para>If set to <literal>version</literal>, will print only the version of the resolved file.</para></listitem>
+ <listitem><para>If set to <literal>type</literal>, will print only the inode type of the resolved
+ file (i.e. a string such as <literal>reg</literal> for regular files, or <literal>dir</literal> for
+ directories).</para></listitem>
+ <listitem><para>If set to <literal>arch</literal>, will print only the architecture of the resolved
+ file.</para></listitem>
+ <listitem><para>If set to <literal>tries</literal>, will print only the tries left/tries done of the
+ resolved file.</para></listitem>
+ <listitem><para>If set to <literal>all</literal>, will print all of the above in a simple tabular
+ output.</para></listitem>
+ </itemizedlist>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--resolve=</option></term>
+
+ <listitem><para>Takes a boolean argument. If true the path to the versioned file is fully
+ canonicalized (i.e. symlinks resolved, and redundant path components removed) before it is shown. If
+ false (the default) this is not done, and the path is shown without canonicalization.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>Use a command like the following to automatically pick the newest raw disk image from a
+ <literal>.v/</literal> directory:</para>
+
+ <programlisting>$ systemd-vpick --suffix=.raw --type=reg /var/lib/machines/quux.raw.v/</programlisting>
+
+ <para>This will enumerate all regular files matching
+ <filename>/var/lib/machines/quux.raw.v/quux*.raw</filename>, filter and sort them according to the rules
+ described in
+ <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>, and then
+ write the path to the newest (by version) file to standard output.</para>
+
+ <para>Use a command like the following to automatically pick the newest OS directory tree from a
+ <literal>.v/</literal> directory:</para>
+
+ <programlisting>$ systemd-vpick --type=dir /var/lib/machines/waldo.v/</programlisting>
+
+ <para>This will enumerate all directory inodes matching
+ <filename>/var/lib/machines/waldo.v/waldo*</filename>, filter and sort them according to the rules
+ described in
+ <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>, and then
+ write the path to the newest (by version) directory to standard output.</para>
+
+ <para>For further examples see
+ <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>On success, 0 is returned, a non-zero failure code
+ otherwise.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para><simplelist type="inline">
+ <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+ </simplelist></para>
+ </refsect1>
+</refentry>
<programlisting>BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout</programlisting>
</example>
+ <para>In place of the directory path a <literal>.v/</literal> versioned directory may be specified,
+ see <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details.</para>
+
<xi:include href="system-or-user-ns.xml" xpointer="singular"/></listitem>
</varlistentry>
<citerefentry><refentrytitle>systemd-soft-reboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>),
in case the service is configured to survive it.</para>
+ <para>In place of the image path a <literal>.v/</literal> versioned directory may be specified, see
+ <citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+ details.</para>
+
<xi:include href="system-only.xml" xpointer="singular"/>
<xi:include href="version-info.xml" xpointer="v233"/></listitem>
</listitem>
</varlistentry>
- <varlistentry>
- <term><varname>TTLPropagate=</varname></term>
- <listitem>
- <para>Takes a boolean. When true enables TTL propagation at Label Switched Path (LSP) egress.
- When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v243"/>
- </listitem>
- </varlistentry>
-
<varlistentry>
<term><varname>MTUBytes=</varname></term>
<listitem>
system. If assigned the
special value <literal>infinity</literal>, no memory throttling is applied. This controls the
<literal>memory.high</literal> control group attribute. For details about this control group attribute, see
- <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
+ <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.
+ The effective configuration is reported as <varname>EffectiveMemoryHigh=</varname>
+ (see also <varname>EffectiveMemoryMax=</varname>).</para>
<para>While <varname>StartupMemoryHigh=</varname> applies to the startup and shutdown phases of the system,
<varname>MemoryHigh=</varname> applies to normal runtime of the system, and if the former is not set also to
percentage value may be specified, which is taken relative to the installed physical memory on the system. If
assigned the special value <literal>infinity</literal>, no memory limit is applied. This controls the
<literal>memory.max</literal> control group attribute. For details about this control group attribute, see
- <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
+ <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.
+ The effective configuration is reported as <varname>EffectiveMemoryMax=</varname> (the value is
+ the most stringent limit of the unit and parent slices and it is capped by physical memory).</para>
<para>While <varname>StartupMemoryMax=</varname> applies to the startup and shutdown phases of the system,
<varname>MemoryMax=</varname> applies to normal runtime of the system, and if the former is not set also to
limit is applied. This controls the <literal>pids.max</literal> control group attribute. For
details about this control group attribute, the
<ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#pid">pids controller
- </ulink>.</para>
+ </ulink>.
+ The effective configuration is reported as <varname>EffectiveTasksMax=</varname>.</para>
<para>The system default for this setting may be controlled with
<varname>DefaultTasksMax=</varname> in
<filename>time-set.target</filename>,
<filename>time-sync.target</filename>,
<filename>timers.target</filename>,
+ <filename>tpm2.target</filename>,
<filename>umount.target</filename>,
<filename>usb-gadget.target</filename>,
<!-- slices --><filename>-.slice</filename>,
<xi:include href="version-info.xml" xpointer="v242"/>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><filename>tpm2.target</filename></term>
+ <listitem>
+ <para>This target is started automatically if a TPM2 device is discovered, either by the OS or by
+ the firmware. It acts as synchronization point for services that require TPM2 device access. The
+ target unit is enqueued by
+ <citerefentry><refentrytitle>systemd-tpm2-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ if it detects that the firmware has discovered a TPM2 device but the OS kernel has not activated
+ a driver for it yet. It is also pulled in whenever
+ <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ discovers a TPM2 device. The target unit is ordered after the <filename>/dev/tpmrm0</filename>
+ device node, so that it only becomes active once the TPM2 device is actually accessible. Early
+ boot programs that intend to access the TPM2 device should hence order themselves after this
+ target unit, but not pull it in.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect2>
--- /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.v">
+
+ <refentryinfo>
+ <title>systemd.v</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd.v</refentrytitle>
+ <manvolnum>7</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd.v</refname>
+ <refpurpose>Directory with Versioned Resources</refpurpose>
+ </refnamediv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>In various places systemd components accept paths whose trailing components have the
+ <literal>.v/</literal> suffix, pointing to a directory. These components will then automatically look for
+ suitable files inside the directory, do a version comparison and open the newest file found (by
+ version). Specifically, two expressions are supported:</para>
+
+ <itemizedlist>
+
+ <listitem><para>When looking for files with a suffix <replaceable>.SUFFIX</replaceable>, and a path
+ <filename>…<replaceable>PATH</replaceable>/<replaceable>NAME</replaceable><replaceable>.SUFFIX</replaceable>.v/</filename>
+ is specified, then all files
+ <filename>…<replaceable>PATH</replaceable>/<replaceable>NAME</replaceable><replaceable>.SUFFIX</replaceable>.v/<replaceable>NAME</replaceable>_*<replaceable>.SUFFIX</replaceable></filename>
+ are enumerated, filtered, sorted and the newest file used. The primary sorting key is the
+ <emphasis>variable part</emphasis>, here indicated by the wildcard
+ <literal>*</literal>.</para></listitem>
+
+ <listitem><para>When a path
+ <filename>…<replaceable>PATH</replaceable>.v/<replaceable>NAME</replaceable>___<replaceable>.SUFFIX</replaceable></filename>
+ is specified (i.e. the penultimate component of the path ends in <literal>.v</literal> and the final
+ component contains a triple underscore), then all files
+ <filename>…<replaceable>PATH</replaceable>.v/<replaceable>NAME</replaceable>_*<replaceable>.SUFFIX</replaceable></filename>
+ are enumerated, filtered, sorted and the newest file used (again, by the <emphasis>variable
+ part</emphasis>, here indicated by the wildcard <literal>*</literal>).</para></listitem>
+ </itemizedlist>
+
+ <para>To illustrate this in an example, consider a directory <filename>/var/lib/machines/mymachine.raw.v/</filename>, which is populated with three files:</para>
+
+ <itemizedlist>
+ <listitem><para><filename>mymachine_7.5.13.raw</filename></para></listitem>
+ <listitem><para><filename>mymachine_7.5.14.raw</filename></para></listitem>
+ <listitem><para><filename>mymachine_7.6.0.raw</filename></para></listitem>
+ </itemizedlist>
+
+ <para>Invoke a tool such as <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> with a command line like the following:</para>
+
+ <programlisting># systemd-nspawn --image=/var/lib/machines/mymachine.raw.v --boot</programlisting>
+
+ <para>Then this would automatically be resolved to the equivalent of:</para>
+
+ <programlisting># systemd-nspawn --image=/var/lib/machines/mymachine.raw.v/mymachine_7.6.0.raw --boot</programlisting>
+
+ <para>Much of systemd's functionality that expects a path to a disk image or OS directory hierarchy
+ support the <literal>.v/</literal> versioned directory mechanism, for example
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry> or
+ the <varname>RootDirectory=</varname>/<varname>RootImage=</varname> settings of service files (see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para>
+
+ <para>Use the
+ <citerefentry><refentrytitle>systemd-vpick</refentrytitle><manvolnum>1</manvolnum></citerefentry> tool to
+ resolve <literal>.v/</literal> paths from the command line, for example for usage in shell
+ scripts.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Filtering and Sorting</title>
+
+ <para>The variable part of the filenames in the <literal>.v/</literal> directories are filtered and
+ compared primarily with a version comparison, implementing <ulink
+ url="https://uapi-group.org/specifications/specs/version_format_specification/">Version Format
+ Specification</ulink>. However, additional rules apply:</para>
+
+ <itemizedlist>
+ <listitem><para>If the variable part is suffixed by one or two integer values ("tries left" and "tries
+ done") in the formats <filename>+<replaceable>LEFT</replaceable></filename> or
+ <filename>+<replaceable>LEFT</replaceable>-<replaceable>DONE</replaceable></filename>, then these
+ indicate usage attempt counters. The idea is that each time before a file is attempted to be used, its
+ "tries left" counter is decreased, and the "tries done" counter increased (simply by renaming the
+ file). When the file is successfully used (which for example could mean for an OS image: successfully
+ booted) the counters are removed from the file name, indicating that the file has been validated to
+ work correctly. This mechanism mirrors the boot assessment counters defined by <ulink
+ url="https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/">Automatic Boot Assessment</ulink>. Any filenames
+ with no boot counters or with a non-zero "tries left" counter are sorted before filenames with a zero
+ "tries left" counter.</para></listitem>
+
+ <listitem><para>Preceeding the use counters (if they are specified), an optional CPU architecture
+ identifier may be specified in the filename (separated from the version with an underscore), as defined
+ in the architecture vocabulary of the <varname>ConditionArchitecture=</varname> unit file setting, as
+ documented in
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. Files
+ whose name indicates an architecture not supported locally are filtered and not considered for the
+ version comparison.</para></listitem>
+
+ <listitem><para>The rest of the variable part is the version string.</para></listitem>
+ </itemizedlist>
+
+ <para>Or in other words, the files in the <literal>.v/</literal> directories should follow one of these
+ naming structures:</para>
+
+ <itemizedlist>
+ <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+ <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>_<replaceable>ARCHITECTURE</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+ <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>+<replaceable>LEFT</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+ <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>+<replaceable>LEFT</replaceable>-<replaceable>DONE</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+ <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>_<replaceable>ARCHITECTURE</replaceable>+<replaceable>LEFT</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+ <listitem><para><filename><replaceable>NAME</replaceable>_<replaceable>VERSION</replaceable>_<replaceable>ARCHITECTURE</replaceable>+<replaceable>LEFT</replaceable>-<replaceable>DONE</replaceable><replaceable>.SUFFIX</replaceable></filename></para></listitem>
+ </itemizedlist>
+ </refsect1>
+
+ <refsect1>
+ <title>Example</title>
+
+ <para>Here's a more comprehensive example, further extending the one described above. Consider a
+ directory <filename>/var/lib/machines/mymachine.raw.v/</filename>, which is populated with the following
+ files:</para>
+
+ <itemizedlist>
+ <listitem><para><filename>mymachine_7.5.13.raw</filename></para></listitem>
+ <listitem><para><filename>mymachine_7.5.14_x86-64.raw</filename></para></listitem>
+ <listitem><para><filename>mymachine_7.6.0_arm64.raw</filename></para></listitem>
+ <listitem><para><filename>mymachine_7.7.0_x86-64+0-5.raw</filename></para></listitem>
+ </itemizedlist>
+
+ <para>Now invoke the following command on an x86-64 machine:</para>
+
+ <programlisting>$ systemd-vpick --suffix=.raw /var/lib/machines/mymachine.raw.v/</programlisting>
+
+ <para>This would resolve the specified path to
+ <filename>/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw</filename>. Explanation: even
+ though <filename>mymachine_7.7.0_x86-64+0-5.raw</filename> has the newest version, it is not preferred
+ because its tries left counter is zero. And even though <filename>mymachine_7.6.0_arm64.raw</filename>
+ has the second newest version it is also not considered, in this case because we operate on an x86_64
+ system and the image is intended for arm64 CPUs. Finally, the <filename>mymachine_7.5.13.raw</filename>
+ image is not considered because it is older than <filename>mymachine_7.5.14_x86-64.raw</filename>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para><simplelist type="inline">
+ <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-vpick</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-dissect</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-sysupdate</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ </simplelist></para>
+ </refsect1>
+
+</refentry>
subdir('src/veritysetup')
subdir('src/vmspawn')
subdir('src/volatile-root')
+subdir('src/vpick')
subdir('src/xdg-autostart-generator')
subdir('src/systemd')
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# A S Alam <amanpreet.alam@gmail.com>, 2020, 2021, 2023.
+# A S Alam <aalam@users.noreply.translate.fedoraproject.org>, 2023.
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-14 21:25+0000\n"
-"PO-Revision-Date: 2023-03-14 02:20+0000\n"
-"Last-Translator: A S Alam <amanpreet.alam@gmail.com>\n"
-"Language-Team: Punjabi <https://translate.fedoraproject.org/projects/systemd/master/pa/>\n"
+"PO-Revision-Date: 2023-12-28 15:36+0000\n"
+"Last-Translator: A S Alam <aalam@users.noreply.translate.fedoraproject.org>\n"
+"Language-Team: Punjabi <https://translate.fedoraproject.org/projects/systemd/"
+"master/pa/>\n"
"Language: pa\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 4.15.2\n"
+"X-Generator: Weblate 5.3\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/home/org.freedesktop.home1.policy:13
msgid "Create a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਬਣਾਓ"
#: src/home/org.freedesktop.home1.policy:14
msgid "Authentication is required to create a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਨੂੰ ਬਣਾਉਣ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/home/org.freedesktop.home1.policy:23
msgid "Remove a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਨੂੰ ਹਟਾਓ"
#: src/home/org.freedesktop.home1.policy:24
msgid "Authentication is required to remove a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਨੂੰ ਹਟਾਉਣ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/home/org.freedesktop.home1.policy:33
msgid "Check credentials of a home area"
#: src/home/org.freedesktop.home1.policy:43
msgid "Update a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਨੂੰ ਅੱਪਡੇਟ ਕਰੋ"
#: src/home/org.freedesktop.home1.policy:44
msgid "Authentication is required to update a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/home/org.freedesktop.home1.policy:53
msgid "Resize a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਦਾ ਆਕਾਰ ਬਦਲੋ"
#: src/home/org.freedesktop.home1.policy:54
msgid "Authentication is required to resize a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਦੇ ਆਕਰ ਨੂੰ ਬਦਲਣ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/home/org.freedesktop.home1.policy:63
msgid "Change password of a home area"
-msgstr ""
+msgstr "ਹੋਮ ਖੇਤਰ ਲਈ ਪਾਸਵਰਡ ਬਦਲੋ"
#: src/home/org.freedesktop.home1.policy:64
msgid "Authentication is required to change the password of a user's home area."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਦੇ ਹੋਮ ਖੇਤਰ ਲਈ ਪਾਸਵਰਡ ਨੂੰ ਬਦਲਣ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/home/pam_systemd_home.c:287
#, c-format
#, c-format
msgid "Too frequent login attempts for user %s, try again later."
msgstr ""
+"ਵਰਤੋਂਕਾਰ %s ਲਈ ਬਹੁਤ ਛੇਤੀ ਛੇਤੀ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ਾਂ ਕੀਤੀਆਂ, ਬਾਅਦ ਵਿੱਚ ਕੋਸ਼ਿਸ਼ ਕਰਿਓ।"
#: src/home/pam_systemd_home.c:304
msgid "Password: "
-msgstr ""
+msgstr "ਪਾਸਵਰਡ: "
#: src/home/pam_systemd_home.c:306
#, c-format
msgid "Password incorrect or not sufficient for authentication of user %s."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ %s ਦੀ ਪਰਮਾਣਿਕਤਾ ਲਈ ਪਾਸਵਰਡ ਗਲਤ ਹੈ ਜਾਂ ਕਾਫ਼ੀ ਨਹੀਂ ਹੈ।"
#: src/home/pam_systemd_home.c:307
msgid "Sorry, try again: "
-msgstr ""
+msgstr "ਅਫ਼ਸੋਸ, ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰਿਓ: "
#: src/home/pam_systemd_home.c:329
msgid "Recovery key: "
-msgstr ""
+msgstr "ਰਿਕਵਰੀ ਕੁੰਜੀ: "
#: src/home/pam_systemd_home.c:331
#, c-format
msgid "Password/recovery key incorrect or not sufficient for authentication of user %s."
msgstr ""
+"ਵਰਤੋਂਕਾਰ %s ਦੀ ਪਰਮਾਣਿਕਤਾ ਲਈ ਪਾਸਵਰਡ/ਰਿਕਵਰੀ ਕੁੰਜੀ ਗਲਤ ਹੈ ਜਾਂ ਕਾਫ਼ੀ ਨਹੀਂ ਹੈ।"
#: src/home/pam_systemd_home.c:332
msgid "Sorry, reenter recovery key: "
-msgstr ""
+msgstr "ਅਫ਼ਸੋਸ, ਰਿਕਵਰੀ ਕੁੰਜੀ ਫੇਰ ਭਰਿਓ: "
#: src/home/pam_systemd_home.c:352
#, c-format
#: src/home/pam_systemd_home.c:353 src/home/pam_systemd_home.c:356
msgid "Try again with password: "
-msgstr ""
+msgstr "ਪਾਸਵਰਡ ਨਾਲ ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰਿਓ: "
#: src/home/pam_systemd_home.c:355
#, c-format
#: src/home/pam_systemd_home.c:376
msgid "Security token PIN: "
-msgstr ""
+msgstr "ਸੁਰੱਖਿਆ ਟੋਕਨ ਪਿੰਨ: "
#: src/home/pam_systemd_home.c:393
#, c-format
#, c-format
msgid "Home of user %s is currently locked, please unlock locally first."
msgstr ""
+"%s ਵਰਤੋਂਕਾਰ ਦਾ ਹੋਮ ਇਸ ਵੇਲੇ ਲਾਕ ਹੈ, ਪਹਿਲਾਂ ਇਸ ਨੂੰ ਲੋਕਲ ਰੂਪ ਵਿੱਚ ਅਣ-ਲਾਕ ਕਰੋ।"
#: src/home/pam_systemd_home.c:645
#, c-format
#: src/home/pam_systemd_home.c:868
msgid "User record is blocked, prohibiting access."
-msgstr ""
+msgstr "ਵਰਤੋਂਕਾਰ ਰਿਕਾਰਡ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਈ ਹੈ, ਪਹੁੰਚ ਤੋਂ ਰੋਕਿਆ ਜਾ ਰਿਹਾ ਹੈ।"
#: src/home/pam_systemd_home.c:872
msgid "User record is not valid yet, prohibiting access."
#: src/home/pam_systemd_home.c:893
#, c-format
msgid "Too many logins, try again in %s."
-msgstr ""
+msgstr "ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ ਕੀਤੀਆਂ, %s ਵਿੱਚ ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
#: src/home/pam_systemd_home.c:904
msgid "Password change required."
-msgstr ""
+msgstr "ਪਾਸਵਰਡ ਬਦਲਣ ਦੀ ਲੋੜ ਹੈ।"
#: src/home/pam_systemd_home.c:908
msgid "Password expired, change required."
-msgstr ""
+msgstr "ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ ਪੁੱਗੀ ਹੈ, ਇਹ ਬਦਲਣ ਦੀ ਲੋੜ ਹੈ।"
#: src/home/pam_systemd_home.c:914
msgid "Password is expired, but can't change, refusing login."
msgstr ""
+"ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ ਪੁੱਗੀ, ਪਰ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ, ਲਾਗਇਨ ਤੋਂ ਇਨਕਾਰ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ।"
#: src/home/pam_systemd_home.c:918
msgid "Password will expire soon, please change."
-msgstr ""
+msgstr "ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ ਛੇਤੀ ਹੀ ਪੁੱਗ ਜਾਵੇਗੀ, ਇਸ ਨੂੰ ਬਦਲ ਲਵੋ।"
#: src/hostname/org.freedesktop.hostname1.policy:20
msgid "Set hostname"
-msgstr ""
+msgstr "ਹੋਸਟ ਨਾਂ ਸੈੱਟ ਕਰੋ"
#: src/hostname/org.freedesktop.hostname1.policy:21
msgid "Authentication is required to set the local hostname."
-msgstr ""
+msgstr "ਲੋਕਲ ਹੋਸਟ-ਨਾਂ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/hostname/org.freedesktop.hostname1.policy:30
msgid "Set static hostname"
#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
-msgstr ""
+msgstr "ਮਸ਼ੀਨ ਜਾਣਕਾਰੀ ਸੈੱਟ ਕਰੋ"
#: src/hostname/org.freedesktop.hostname1.policy:42
msgid "Authentication is required to set local machine information."
-msgstr ""
+msgstr "ਲੋਕਲ ਮਸ਼ੀਨ ਜਾਣਕਾਰੀ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਿਕਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/hostname/org.freedesktop.hostname1.policy:51
msgid "Get product UUID"
#: src/hostname/org.freedesktop.hostname1.policy:61
msgid "Get hardware serial number"
-msgstr ""
+msgstr "ਹਾਰਡਵੇਅਰ ਲੜੀ ਨੰਬਰ ਲਵੋ"
#: src/hostname/org.freedesktop.hostname1.policy:62
-#, fuzzy
msgid "Authentication is required to get hardware serial number."
-msgstr "ਸਿਸà¨\9fਮ ਸà©\87ਵਾਵਾà¨\82 à¨\9cਾà¨\82 ਯà©\82ਨਿà¨\9f ਫ਼ਾà¨\87ਲਾà¨\82 ਦਾ à¨\87ੰਤà©\9bਾਮ à¨\95ਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
+msgstr "ਹਾਰਡਵà©\87à¨\85ਰ ਲà©\9cà©\80 ਨੰਬਰ ਲà©\88ਣ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/hostname/org.freedesktop.hostname1.policy:71
msgid "Get system description"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਜਾਣਕਾਰੀ ਲਵੋ"
#: src/hostname/org.freedesktop.hostname1.policy:72
-#, fuzzy
msgid "Authentication is required to get system description."
-msgstr "ਸਿਸà¨\9fਮ ਸà©\87ਵਾਵਾà¨\82 à¨\9cਾà¨\82 ਯà©\82ਨਿà¨\9f ਫ਼ਾà¨\87ਲਾà¨\82 ਦਾ à¨\87ੰਤà©\9bਾਮ à¨\95ਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
+msgstr "ਸਿਸà¨\9fਮ à¨\9cਾਣà¨\95ਾਰà©\80 ਲà©\88ਣ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/import/org.freedesktop.import1.policy:22
msgid "Import a VM or container image"
#: src/locale/org.freedesktop.locale1.policy:22
msgid "Set system locale"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਲੋਕੇਲ ਸੈੱਟ ਕਰੋ"
#: src/locale/org.freedesktop.locale1.policy:23
msgid "Authentication is required to set the system locale."
-msgstr ""
+msgstr "ਸਿਸਟਮ ਲੋਕੇਲ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/locale/org.freedesktop.locale1.policy:33
msgid "Set system keyboard settings"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਕੀਬੋਰਡ ਸੈਟਿੰਗਾਂ ਸੈੱਟ ਕਰੋ"
#: src/locale/org.freedesktop.locale1.policy:34
msgid "Authentication is required to set the system keyboard settings."
-msgstr ""
+msgstr "ਸਿਸਟਮ ਕੀਬੋਰਡ ਸੈਟਿੰਗਾਂ ਸੈੱਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/login/org.freedesktop.login1.policy:22
msgid "Allow applications to inhibit system shutdown"
#: src/login/org.freedesktop.login1.policy:33
msgid "Allow applications to delay system shutdown"
-msgstr ""
+msgstr "ਐਪਲੀਕੇਸ਼ਨਾਂ ਨੂੰ ਸਿਸਟਮ ਬੰਦ ਕਰਨ ਵਿੱਚ ਦੇਰੀ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ"
#: src/login/org.freedesktop.login1.policy:34
msgid "Authentication is required for an application to delay system shutdown."
#: src/login/org.freedesktop.login1.policy:268
msgid "Suspend the system"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਨੂੰ ਸਸਪੈਂਡ"
#: src/login/org.freedesktop.login1.policy:269
msgid "Authentication is required to suspend the system."
#: src/login/org.freedesktop.login1.policy:300
msgid "Hibernate the system"
-msgstr ""
+msgstr "ਸਿਸਟਮ ਨੂੰ ਹਾਈਬਰਨੇਟ"
#: src/login/org.freedesktop.login1.policy:301
msgid "Authentication is required to hibernate the system."
#: src/login/org.freedesktop.login1.policy:342
msgid "Lock or unlock active sessions"
-msgstr ""
+msgstr "ਸਰਗਰਮ ਸ਼ੈਸ਼ਨਾਂ ਨੂੰ ਲਾਕ ਜਾਂ ਅਣ-ਲਾਕ"
#: src/login/org.freedesktop.login1.policy:343
msgid "Authentication is required to lock or unlock active sessions."
#: src/login/org.freedesktop.login1.policy:406
msgid "Change Session"
-msgstr ""
+msgstr "ਸ਼ੈਸ਼ਨ ਨੂੰ ਬਦਲੋ"
#: src/login/org.freedesktop.login1.policy:407
msgid "Authentication is required to change the virtual terminal."
-msgstr ""
+msgstr "ਵਰਚੁਅਲ ਟਰਮੀਨਲ ਨੂੰ ਬਦਲਣ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/machine/org.freedesktop.machine1.policy:22
msgid "Log into a local container"
-msgstr ""
+msgstr "ਲੋਕਲ ਕਨਟੇਨਰ ਵਿੱਚ ਲਾਗਇਨ ਕਰੋ"
#: src/machine/org.freedesktop.machine1.policy:23
msgid "Authentication is required to log into a local container."
-msgstr ""
+msgstr "ਲੋਕਲ ਕਨਟੇਨਰ ਵਿੱਚ ਲਾਗਇਨ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/machine/org.freedesktop.machine1.policy:32
msgid "Log into the local host"
-msgstr ""
+msgstr "ਲੋਕਲ ਹੋਸਟ ਵਿੱਚ ਲਾਗਇਨ ਕਰੋ"
#: src/machine/org.freedesktop.machine1.policy:33
msgid "Authentication is required to log into the local host."
-msgstr ""
+msgstr "ਲੋਕਲ ਹੋਸਟ ਵਿੱਚ ਲਾਗ ਇਨ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ।"
#: src/machine/org.freedesktop.machine1.policy:42
msgid "Acquire a shell in a local container"
SUBSYSTEM=="module", KERNEL=="fuse", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-fs-fuse-connections.mount"
SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sys-kernel-config.mount"
+# Pull in tpm2.target whenever /dev/tpmrm* shows up
+SUBSYSTEM=="tpmrm", KERNEL=="tpmrm[0-9]*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="tpm2.target"
+
LABEL="systemd_end"
p = &image_policy_sysext_strict;
else if (streq(argv[i], "@confext"))
p = &image_policy_confext;
+ else if (streq(argv[i], "@confext-strict"))
+ p = &image_policy_confext_strict;
else if (streq(argv[i], "@container"))
p = &image_policy_container;
else if (streq(argv[i], "@service"))
}
static int find_pci_or_platform_parent(sd_device *device, sd_device **ret) {
- const char *subsystem, *sysname, *value;
sd_device *parent;
+ const char *s;
int r;
assert(device);
if (r < 0)
return r;
- r = sd_device_get_subsystem(parent, &subsystem);
- if (r < 0)
- return r;
+ if (device_in_subsystem(parent, "drm")) {
- r = sd_device_get_sysname(parent, &sysname);
- if (r < 0)
- return r;
-
- if (streq(subsystem, "drm")) {
- const char *c;
+ r = sd_device_get_sysname(parent, &s);
+ if (r < 0)
+ return r;
- c = startswith(sysname, "card");
- if (!c)
+ s = startswith(s, "card");
+ if (!s)
return -ENODATA;
- c += strspn(c, DIGITS);
- if (*c == '-' && !STARTSWITH_SET(c, "-LVDS-", "-Embedded DisplayPort-", "-eDP-"))
+ s += strspn(s, DIGITS);
+ if (*s == '-' && !STARTSWITH_SET(s, "-LVDS-", "-Embedded DisplayPort-", "-eDP-"))
/* A connector DRM device, let's ignore all but LVDS and eDP! */
return -EOPNOTSUPP;
- } else if (streq(subsystem, "pci") &&
- sd_device_get_sysattr_value(parent, "class", &value) >= 0) {
+ } else if (device_in_subsystem(parent, "pci") &&
+ sd_device_get_sysattr_value(parent, "class", &s)) {
+
unsigned long class;
- r = safe_atolu(value, &class);
+ r = safe_atolu(s, &class);
if (r < 0)
- return log_warning_errno(r, "Cannot parse PCI class '%s' of device %s:%s: %m",
- value, subsystem, sysname);
+ return log_device_warning_errno(parent, r, "Cannot parse PCI class '%s': %m", s);
/* Graphics card */
if (class == PCI_CLASS_GRAPHICS_CARD) {
return 0;
}
- } else if (streq(subsystem, "platform")) {
+ } else if (device_in_subsystem(parent, "platform")) {
*ret = parent;
return 0;
}
static int validate_device(sd_device *device) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerate = NULL;
- const char *v, *sysname, *subsystem;
+ const char *v, *sysname;
sd_device *parent;
int r;
if (r < 0)
return log_device_debug_errno(device, r, "Failed to get sysname: %m");
- r = sd_device_get_subsystem(device, &subsystem);
- if (r < 0)
- return log_device_debug_errno(device, r, "Failed to get subsystem: %m");
- if (!streq(subsystem, "backlight"))
- return true;
+ if (!device_in_subsystem(device, "backlight"))
+ return true; /* We assume LED device is always valid. */
r = sd_device_get_sysattr_value(device, "type", &v);
if (r < 0)
if (r < 0)
return log_device_debug_errno(device, r, "Failed to find PCI or platform parent: %m");
- r = sd_device_get_subsystem(parent, &subsystem);
- if (r < 0)
- return log_device_debug_errno(parent, r, "Failed to get subsystem: %m");
-
if (DEBUG_LOGGING) {
- const char *s = NULL;
+ const char *s = NULL, *subsystem = NULL;
(void) sd_device_get_syspath(parent, &s);
- log_device_debug(device, "Found %s parent device: %s", subsystem, strna(s));
+ (void) sd_device_get_subsystem(parent, &subsystem);
+ log_device_debug(device, "Found %s parent device: %s", strna(subsystem), strna(s));
}
r = sd_device_enumerator_new(&enumerate);
if (r < 0)
return log_debug_errno(r, "Failed to add sysattr match: %m");
- if (streq(subsystem, "pci")) {
+ if (device_in_subsystem(parent, "pci")) {
r = has_multiple_graphics_cards();
if (r < 0)
return log_debug_errno(r, "Failed to check if the system has multiple graphics cards: %m");
}
FOREACH_DEVICE(enumerate, other) {
- const char *other_subsystem;
sd_device *other_parent;
/* OK, so there's another backlight device, and it's a platform or firmware device.
return false;
}
- r = sd_device_get_subsystem(other_parent, &other_subsystem);
- if (r < 0) {
- log_device_debug_errno(other_parent, r, "Failed to get subsystem, ignoring: %m");
- continue;
- }
-
- if (streq(other_subsystem, "platform") && streq(subsystem, "pci")) {
+ if (device_in_subsystem(other_parent, "platform") && device_in_subsystem(parent, "pci")) {
/* The other is connected to the platform bus and we are a PCI device, that also means we are out. */
if (DEBUG_LOGGING) {
const char *other_sysname = NULL, *other_type = NULL;
unsigned *brightness) {
unsigned new_brightness, min_brightness;
- const char *subsystem;
- int r;
assert(device);
assert(brightness);
* avoids preserving an unreadably dim screen, which would otherwise force the user to disable
* state restoration. */
- r = sd_device_get_subsystem(device, &subsystem);
- if (r < 0)
- return log_device_warning_errno(device, r, "Failed to get device subsystem: %m");
-
- if (streq(subsystem, "backlight"))
- min_brightness = MAX(1U, (unsigned) ((double) max_brightness * percent / 100));
- else
- min_brightness = 0;
+ min_brightness = (unsigned) ((double) max_brightness * percent / 100);
+ if (device_in_subsystem(device, "backlight"))
+ min_brightness = MAX(1U, min_brightness);
new_brightness = CLAMP(*brightness, min_brightness, max_brightness);
if (new_brightness != *brightness)
return 0;
}
-static bool shall_clamp(sd_device *d, unsigned *ret) {
- const char *s;
+static bool shall_clamp(sd_device *device, unsigned *ret) {
+ const char *property, *s;
+ unsigned default_percent;
int r;
- assert(d);
+ assert(device);
assert(ret);
- r = sd_device_get_property_value(d, "ID_BACKLIGHT_CLAMP", &s);
+ if (device_in_subsystem(device, "backlight")) {
+ property = "ID_BACKLIGHT_CLAMP";
+ default_percent = 5;
+ } else {
+ property = "ID_LEDS_CLAMP";
+ default_percent = 0;
+ }
+
+ r = sd_device_get_property_value(device, property, &s);
if (r < 0) {
if (r != -ENOENT)
- log_device_debug_errno(d, r, "Failed to get ID_BACKLIGHT_CLAMP property, ignoring: %m");
- *ret = 5; /* defaults to 5% */
- return true;
+ log_device_debug_errno(device, r, "Failed to get %s property, ignoring: %m", property);
+ *ret = default_percent;
+ return default_percent > 0;
}
r = parse_boolean(s);
r = parse_percent(s);
if (r < 0) {
- log_device_debug_errno(d, r, "Failed to parse ID_BACKLIGHT_CLAMP property, ignoring: %m");
- *ret = 5;
- return true;
+ log_device_debug_errno(device, r, "Failed to parse %s property, ignoring: %m", property);
+ *ret = default_percent;
+ return default_percent > 0;
}
*ret = r;
}
static int read_brightness(sd_device *device, unsigned max_brightness, unsigned *ret_brightness) {
- const char *subsystem, *value;
+ const char *value;
unsigned brightness;
int r;
assert(device);
assert(ret_brightness);
- r = sd_device_get_subsystem(device, &subsystem);
- if (r < 0)
- return log_device_debug_errno(device, r, "Failed to get subsystem: %m");
-
- if (streq(subsystem, "backlight")) {
+ if (device_in_subsystem(device, "backlight")) {
r = sd_device_get_sysattr_value(device, "actual_brightness", &value);
if (r == -ENOENT) {
log_device_debug_errno(device, r, "Failed to read 'actual_brightness' attribute, "
p[1] = safe_close(p[1]);
}
-void close_many(const int fds[], size_t n_fd) {
- assert(fds || n_fd <= 0);
+void close_many(const int fds[], size_t n_fds) {
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fd; i++)
- safe_close(fds[i]);
+ FOREACH_ARRAY(fd, fds, n_fds)
+ safe_close(*fd);
}
-void close_many_unset(int fds[], size_t n_fd) {
- assert(fds || n_fd <= 0);
+void close_many_unset(int fds[], size_t n_fds) {
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fd; i++)
- fds[i] = safe_close(fds[i]);
+ FOREACH_ARRAY(fd, fds, n_fds)
+ *fd = safe_close(*fd);
}
void close_many_and_free(int *fds, size_t n_fds) {
- assert(fds || n_fds <= 0);
+ assert(fds || n_fds == 0);
close_many(fds, n_fds);
free(fds);
}
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec) {
- int ret = 0, r;
+ int r = 0;
- assert(n_fds == 0 || fds);
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fds; i++) {
- if (fds[i] < 0) /* Skip gracefully over already invalidated fds */
+ FOREACH_ARRAY(fd, fds, n_fds) {
+ if (*fd < 0) /* Skip gracefully over already invalidated fds */
continue;
- r = fd_cloexec(fds[i], cloexec);
- if (r < 0 && ret >= 0) /* Continue going, but return first error */
- ret = r;
- else
- ret = 1; /* report if we did anything */
+ RET_GATHER(r, fd_cloexec(*fd, cloexec));
+
+ if (r >= 0)
+ r = 1; /* report if we did anything */
}
- return ret;
+ return r;
}
-static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
- assert(n_fdset == 0 || fdset);
+static bool fd_in_set(int fd, const int fds[], size_t n_fds) {
+ assert(fd >= 0);
+ assert(fds || n_fds == 0);
- for (size_t i = 0; i < n_fdset; i++) {
- if (fdset[i] < 0)
+ FOREACH_ARRAY(i, fds, n_fds) {
+ if (*i < 0)
continue;
- if (fdset[i] == fd)
+ if (*i == fd)
return true;
}
static int close_all_fds_frugal(const int except[], size_t n_except) {
int max_fd, r = 0;
- assert(n_except == 0 || except);
+ assert(except || n_except == 0);
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
* and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
* spin the CPU for a long time. */
if (max_fd > MAX_FD_LOOP_LIMIT)
return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
- "Refusing to loop over %d potential fds.",
- max_fd);
+ "Refusing to loop over %d potential fds.", max_fd);
for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -EBADF) {
int q;
continue;
q = close_nointr(fd);
- if (q < 0 && q != -EBADF && r >= 0)
- r = q;
+ if (q != -EBADF)
+ RET_GATHER(r, q);
}
return r;
if (fl < 0)
return -errno;
- cloexec = !!(fl & FD_CLOEXEC);
+ cloexec = FLAGS_SET(fl, FD_CLOEXEC);
}
r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
return safe_close(fd);
}
-void close_many(const int fds[], size_t n_fd);
-void close_many_unset(int fds[], size_t n_fd);
+void close_many(const int fds[], size_t n_fds);
+void close_many_unset(int fds[], size_t n_fds);
void close_many_and_free(int *fds, size_t n_fds);
int fclose_nointr(FILE *f);
}
/* If we can't, make sure the queue size is out of bounds, to
- * mark it as overflow */
+ * mark it as overflowed */
for (;;) {
sig_atomic_t c;
__atomic_thread_fence(__ATOMIC_SEQ_CST);
c = n_sigbus_queue;
- if (c > SIGBUS_QUEUE_MAX) /* already overflow */
+ if (c > SIGBUS_QUEUE_MAX) /* already overflowed */
return;
/* OK if we clobber c here, since we either immediately return
if (_likely_(c == 0))
return 0;
- if (_unlikely_(c >= SIGBUS_QUEUE_MAX))
+ if (_unlikely_(c > SIGBUS_QUEUE_MAX))
return -EOVERFLOW;
for (u = 0; u < SIGBUS_QUEUE_MAX; u++) {
#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
#define SPECIAL_TIME_SET_TARGET "time-set.target"
#define SPECIAL_BASIC_TARGET "basic.target"
+#define SPECIAL_TPM2_TARGET "tpm2.target"
/* LSB compatibility */
#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
return NULL;
}
+
+mode_t inode_type_from_string(const char *s) {
+ if (!s)
+ return MODE_INVALID;
+
+ if (streq(s, "reg"))
+ return S_IFREG;
+ if (streq(s, "dir"))
+ return S_IFDIR;
+ if (streq(s, "lnk"))
+ return S_IFLNK;
+ if (streq(s, "chr"))
+ return S_IFCHR;
+ if (streq(s, "blk"))
+ return S_IFBLK;
+ if (streq(s, "fifo"))
+ return S_IFIFO;
+ if (streq(s, "sock"))
+ return S_IFSOCK;
+
+ return MODE_INVALID;
+}
extern const struct hash_ops inode_hash_ops;
const char* inode_type_to_string(mode_t m);
+mode_t inode_type_from_string(const char *s);
return t1[yl];
}
+
+char *strrstr(const char *haystack, const char *needle) {
+ const char *f = NULL;
+ size_t l;
+
+ /* Like strstr() but returns the last rather than the first occurence of "needle" in "haystack". */
+
+ if (!haystack || !needle)
+ return NULL;
+
+ l = strlen(needle);
+
+ /* Special case: for the empty string we return the very last possible occurence, i.e. *after* the
+ * last char, not before. */
+ if (l == 0)
+ return strchr(haystack, 0);
+
+ for (const char *p = haystack; *p; p++)
+ if (strncmp(p, needle, l) == 0)
+ f = p;
+
+ return (char*) f;
+}
*ret = c;
return 1;
}
+
+char *strrstr(const char *haystack, const char *needle);
}
DEFINE_HASH_OPS_FULL(string_strv_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free);
+
+char* strv_endswith(const char *s, char **l) {
+ STRV_FOREACH(i, l) {
+ char *e = endswith(s, *i);
+ if (e)
+ return (char*) e;
+ }
+
+ return NULL;
+}
int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS);
#define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS)
#define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v HASHMAP_DEBUG_SRC_ARGS)
+
+char* strv_endswith(const char *s, char **l);
uint64_t flag;
const char *name;
} stub_flags[] = {
- { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets ESP information" },
- { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" },
- { EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" },
- { EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" },
- { EFI_STUB_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
- { EFI_STUB_FEATURE_CMDLINE_ADDONS, "Pick up .cmdline from addons" },
- { EFI_STUB_FEATURE_CMDLINE_SMBIOS, "Pick up .cmdline from SMBIOS Type 11" },
- { EFI_STUB_FEATURE_DEVICETREE_ADDONS, "Pick up .dtb from addons" },
+ { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets ESP information" },
+ { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" },
+ { EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" },
+ { EFI_STUB_FEATURE_PICK_UP_CONFEXTS, "Picks up configuration extension images from boot partition" },
+ { EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" },
+ { EFI_STUB_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
+ { EFI_STUB_FEATURE_CMDLINE_ADDONS, "Pick up .cmdline from addons" },
+ { EFI_STUB_FEATURE_CMDLINE_SMBIOS, "Pick up .cmdline from SMBIOS Type 11" },
+ { EFI_STUB_FEATURE_DEVICETREE_ADDONS, "Pick up .dtb from addons" },
};
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
const char16_t *dropin_dir,
const char16_t *match_suffix,
+ const char16_t *exclude_suffix,
const char *target_dir_prefix,
uint32_t dir_mode,
uint32_t access_mode,
continue;
if (match_suffix && !endswith_no_case(dirent->FileName, match_suffix))
continue;
+ if (exclude_suffix && endswith_no_case(dirent->FileName, exclude_suffix))
+ continue;
if (!is_ascii(dirent->FileName))
continue;
if (strlen16(dirent->FileName) > 255) /* Max filename size on Linux */
EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
const char16_t *dropin_dir,
const char16_t *match_suffix,
+ const char16_t *exclude_suffix,
const char *target_dir_prefix,
uint32_t dir_mode,
uint32_t access_mode,
EFI_STUB_FEATURE_REPORT_BOOT_PARTITION | /* We set LoaderDevicePartUUID */
EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */
EFI_STUB_FEATURE_PICK_UP_SYSEXTS | /* We pick up system extensions from the boot partition */
+ EFI_STUB_FEATURE_PICK_UP_CONFEXTS | /* We pick up configuration extensions from the boot partition */
EFI_STUB_FEATURE_THREE_PCRS | /* We can measure kernel image, parameters and sysext */
EFI_STUB_FEATURE_RANDOM_SEED | /* We pass a random seed to the kernel */
EFI_STUB_FEATURE_CMDLINE_ADDONS | /* We pick up .cmdline addons */
}
static EFI_STATUS run(EFI_HANDLE image) {
- _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
- size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
+ _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *confext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
+ size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, confext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL;
char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL;
_cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL;
_cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons_global = NULL, *cmdline_addons_uki = NULL;
int sections_measured = -1, parameters_measured = -1;
_cleanup_free_ char *uname = NULL;
- bool sysext_measured = false, m;
+ bool sysext_measured = false, confext_measured = false, m;
uint64_t loader_features = 0;
EFI_STATUS err;
export_variables(loaded_image);
if (pack_cpio(loaded_image,
- NULL,
+ /* dropin_dir= */ NULL,
u".cred",
+ /* exclude_suffix= */ NULL,
".extra/credentials",
/* dir_mode= */ 0500,
/* access_mode= */ 0400,
if (pack_cpio(loaded_image,
u"\\loader\\credentials",
u".cred",
+ /* exclude_suffix= */ NULL,
".extra/global_credentials",
/* dir_mode= */ 0500,
/* access_mode= */ 0400,
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
if (pack_cpio(loaded_image,
- NULL,
- u".raw",
+ /* dropin_dir= */ NULL,
+ u".raw", /* ideally we'd pick up only *.sysext.raw here, but for compat we pick up *.raw instead … */
+ u".confext.raw", /* … but then exclude *.confext.raw again */
".extra/sysext",
/* dir_mode= */ 0555,
/* access_mode= */ 0444,
&m) == EFI_SUCCESS)
sysext_measured = m;
+ if (pack_cpio(loaded_image,
+ /* dropin_dir= */ NULL,
+ u".confext.raw",
+ /* exclude_suffix= */ NULL,
+ ".extra/confext",
+ /* dir_mode= */ 0555,
+ /* access_mode= */ 0444,
+ /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG,
+ u"Configuration extension initrd",
+ &confext_initrd,
+ &confext_initrd_size,
+ &m) == EFI_SUCCESS)
+ confext_measured = m;
+
dt_size = szs[UNIFIED_SECTION_DTB];
dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0;
(void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0);
if (sysext_measured)
(void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM2_PCR_SYSEXTS, 0);
+ if (confext_measured)
+ (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0);
/* If the PCR signature was embedded in the PE image, then let's wrap it in a cpio and also pass it
* to the kernel, so that it can be read from /.extra/tpm2-pcr-signature.json. Note that this section
initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
_cleanup_pages_ Pages initrd_pages = {};
- if (credential_initrd || global_credential_initrd || sysext_initrd || pcrsig_initrd || pcrpkey_initrd) {
+ if (credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
/* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
err = combine_initrd(
initrd_base, initrd_size,
credential_initrd,
global_credential_initrd,
sysext_initrd,
+ confext_initrd,
pcrsig_initrd,
pcrpkey_initrd,
},
credential_initrd_size,
global_credential_initrd_size,
sysext_initrd_size,
+ confext_initrd_size,
pcrsig_initrd_size,
pcrpkey_initrd_size,
},
- 5,
+ 6,
&initrd_pages, &initrd_size);
if (err != EFI_SUCCESS)
return err;
credential_initrd = mfree(credential_initrd);
global_credential_initrd = mfree(global_credential_initrd);
sysext_initrd = mfree(sysext_initrd);
+ confext_initrd = mfree(confext_initrd);
pcrsig_initrd = mfree(pcrsig_initrd);
pcrpkey_initrd = mfree(pcrpkey_initrd);
}
#include "process-util.h"
#include "procfs-util.h"
#include "restrict-ifaces.h"
+#include "set.h"
#include "special.h"
#include "stdio-util.h"
#include "string-table.h"
};
}
+int cgroup_context_add_io_device_weight_dup(CGroupContext *c, CGroupIODeviceWeight *w) {
+ _cleanup_free_ CGroupIODeviceWeight *n = NULL;
+
+ assert(c);
+ assert(w);
+
+ n = new0(CGroupIODeviceWeight, 1);
+ if (!n)
+ return -ENOMEM;
+
+ n->path = strdup(w->path);
+ if (!n->path)
+ return -ENOMEM;
+ n->weight = w->weight;
+
+ LIST_PREPEND(device_weights, c->io_device_weights, TAKE_PTR(n));
+ return 0;
+}
+
+int cgroup_context_add_io_device_limit_dup(CGroupContext *c, CGroupIODeviceLimit *l) {
+ _cleanup_free_ CGroupIODeviceLimit *n = NULL;
+
+ assert(c);
+ assert(l);
+
+ n = new0(CGroupIODeviceLimit, 1);
+ if (!l)
+ return -ENOMEM;
+
+ n->path = strdup(l->path);
+ if (!n->path)
+ return -ENOMEM;
+
+ for (CGroupIOLimitType type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++)
+ n->limits[type] = l->limits[type];
+
+ LIST_PREPEND(device_limits, c->io_device_limits, TAKE_PTR(n));
+ return 0;
+}
+
+int cgroup_context_add_io_device_latency_dup(CGroupContext *c, CGroupIODeviceLatency *l) {
+ _cleanup_free_ CGroupIODeviceLatency *n = NULL;
+
+ assert(c);
+ assert(l);
+
+ n = new0(CGroupIODeviceLatency, 1);
+ if (!n)
+ return -ENOMEM;
+
+ n->path = strdup(l->path);
+ if (!n->path)
+ return -ENOMEM;
+
+ n->target_usec = l->target_usec;
+
+ LIST_PREPEND(device_latencies, c->io_device_latencies, TAKE_PTR(n));
+ return 0;
+}
+
+int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
+ _cleanup_free_ CGroupBlockIODeviceWeight *n = NULL;
+
+ assert(c);
+ assert(w);
+
+ n = new0(CGroupBlockIODeviceWeight, 1);
+ if (!n)
+ return -ENOMEM;
+
+ n->path = strdup(w->path);
+ if (!n->path)
+ return -ENOMEM;
+
+ n->weight = w->weight;
+
+ LIST_PREPEND(device_weights, c->blockio_device_weights, TAKE_PTR(n));
+ return 0;
+}
+
+int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
+ _cleanup_free_ CGroupBlockIODeviceBandwidth *n = NULL;
+
+ assert(c);
+ assert(b);
+
+ n = new0(CGroupBlockIODeviceBandwidth, 1);
+ if (!n)
+ return -ENOMEM;
+
+ *n = (CGroupBlockIODeviceBandwidth) {
+ .rbps = b->rbps,
+ .wbps = b->wbps,
+ };
+
+ LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, TAKE_PTR(n));
+ return 0;
+}
+
+int cgroup_context_add_device_allow_dup(CGroupContext *c, CGroupDeviceAllow *a) {
+ _cleanup_free_ CGroupDeviceAllow *n = NULL;
+
+ assert(c);
+ assert(a);
+
+ n = new0(CGroupDeviceAllow, 1);
+ if (!n)
+ return -ENOMEM;
+
+ n->path = strdup(a->path);
+ if (!n->path)
+ return -ENOMEM;
+
+ n->permissions = a->permissions;
+
+ LIST_PREPEND(device_allow, c->device_allow, TAKE_PTR(n));
+ return 0;
+}
+
+static int cgroup_context_add_socket_bind_item_dup(CGroupContext *c, CGroupSocketBindItem *i, CGroupSocketBindItem *h) {
+ _cleanup_free_ CGroupSocketBindItem *n = NULL;
+
+ assert(c);
+ assert(i);
+
+ n = new0(CGroupSocketBindItem, 1);
+ if (!n)
+ return -ENOMEM;
+
+ *n = (CGroupSocketBindItem) {
+ .address_family = i->address_family,
+ .ip_protocol = i->ip_protocol,
+ .nr_ports = i->nr_ports,
+ .port_min = i->port_min,
+ };
+
+ LIST_PREPEND(socket_bind_items, h, TAKE_PTR(n));
+ return 0;
+}
+
+int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, CGroupSocketBindItem *i) {
+ return cgroup_context_add_socket_bind_item_dup(c, i, c->socket_bind_allow);
+}
+
+int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, CGroupSocketBindItem *i) {
+ return cgroup_context_add_socket_bind_item_dup(c, i, c->socket_bind_deny);
+}
+
+int cgroup_context_copy(CGroupContext *dst, const CGroupContext *src) {
+ struct in_addr_prefix *i;
+ char *iface;
+ int r;
+
+ assert(src);
+ assert(dst);
+
+ dst->cpu_accounting = src->cpu_accounting;
+ dst->io_accounting = src->io_accounting;
+ dst->blockio_accounting = src->blockio_accounting;
+ dst->memory_accounting = src->memory_accounting;
+ dst->tasks_accounting = src->tasks_accounting;
+ dst->ip_accounting = src->ip_accounting;
+
+ dst->memory_oom_group = dst->memory_oom_group;
+
+ dst->cpu_weight = src->cpu_weight;
+ dst->startup_cpu_weight = src->startup_cpu_weight;
+ dst->cpu_quota_per_sec_usec = src->cpu_quota_per_sec_usec;
+ dst->cpu_quota_period_usec = src->cpu_quota_period_usec;
+
+ dst->cpuset_cpus = src->cpuset_cpus;
+ dst->startup_cpuset_cpus = src->startup_cpuset_cpus;
+ dst->cpuset_mems = src->cpuset_mems;
+ dst->startup_cpuset_mems = src->startup_cpuset_mems;
+
+ dst->io_weight = src->io_weight;
+ dst->startup_io_weight = src->startup_io_weight;
+
+ LIST_FOREACH_BACKWARDS(device_weights, w, LIST_FIND_TAIL(device_weights, src->io_device_weights)) {
+ r = cgroup_context_add_io_device_weight_dup(dst, w);
+ if (r < 0)
+ return r;
+ }
+
+ LIST_FOREACH_BACKWARDS(device_limits, l, LIST_FIND_TAIL(device_limits, src->io_device_limits)) {
+ r = cgroup_context_add_io_device_limit_dup(dst, l);
+ if (r < 0)
+ return r;
+ }
+
+ LIST_FOREACH_BACKWARDS(device_latencies, l, LIST_FIND_TAIL(device_latencies, src->io_device_latencies)) {
+ r = cgroup_context_add_io_device_latency_dup(dst, l);
+ if (r < 0)
+ return r;
+ }
+
+ dst->default_memory_min = src->default_memory_min;
+ dst->default_memory_low = src->default_memory_low;
+ dst->default_startup_memory_low = src->default_startup_memory_low;
+ dst->memory_min = src->memory_min;
+ dst->memory_low = src->memory_low;
+ dst->startup_memory_low = src->startup_memory_low;
+ dst->memory_high = src->memory_high;
+ dst->startup_memory_high = src->startup_memory_high;
+ dst->memory_max = src->memory_max;
+ dst->startup_memory_max = src->startup_memory_max;
+ dst->memory_swap_max = src->memory_swap_max;
+ dst->startup_memory_swap_max = src->startup_memory_swap_max;
+ dst->memory_zswap_max = src->memory_zswap_max;
+ dst->startup_memory_zswap_max = src->startup_memory_zswap_max;
+
+ dst->default_memory_min_set = src->default_memory_min_set;
+ dst->default_memory_low_set = src->default_memory_low_set;
+ dst->default_startup_memory_low_set = src->default_startup_memory_low_set;
+ dst->memory_min_set = src->memory_min_set;
+ dst->memory_low_set = src->memory_low_set;
+ dst->startup_memory_low_set = src->startup_memory_low_set;
+ dst->startup_memory_high_set = src->startup_memory_high_set;
+ dst->startup_memory_max_set = src->startup_memory_max_set;
+ dst->startup_memory_swap_max_set = src->startup_memory_swap_max_set;
+ dst->startup_memory_zswap_max_set = src->startup_memory_zswap_max_set;
+
+ SET_FOREACH(i, src->ip_address_allow) {
+ r = in_addr_prefix_add(&dst->ip_address_allow, i);
+ if (r < 0)
+ return r;
+ }
+
+ SET_FOREACH(i, src->ip_address_deny) {
+ r = in_addr_prefix_add(&dst->ip_address_deny, i);
+ if (r < 0)
+ return r;
+ }
+
+ dst->ip_address_allow_reduced = src->ip_address_allow_reduced;
+ dst->ip_address_deny_reduced = src->ip_address_deny_reduced;
+
+ if (!strv_isempty(src->ip_filters_ingress)) {
+ dst->ip_filters_ingress = strv_copy(src->ip_filters_ingress);
+ if (!dst->ip_filters_ingress)
+ return -ENOMEM;
+ }
+
+ if (!strv_isempty(src->ip_filters_egress)) {
+ dst->ip_filters_egress = strv_copy(src->ip_filters_egress);
+ if (!dst->ip_filters_egress)
+ return -ENOMEM;
+ }
+
+ LIST_FOREACH_BACKWARDS(programs, l, LIST_FIND_TAIL(programs, src->bpf_foreign_programs)) {
+ r = cgroup_context_add_bpf_foreign_program_dup(dst, l);
+ if (r < 0)
+ return r;
+ }
+
+ SET_FOREACH(iface, src->restrict_network_interfaces) {
+ r = set_put_strdup(&dst->restrict_network_interfaces, iface);
+ if (r < 0)
+ return r;
+ }
+ dst->restrict_network_interfaces_is_allow_list = src->restrict_network_interfaces_is_allow_list;
+
+ dst->cpu_shares = src->cpu_shares;
+ dst->startup_cpu_shares = src->startup_cpu_shares;
+
+ dst->blockio_weight = src->blockio_weight;
+ dst->startup_blockio_weight = src->startup_blockio_weight;
+
+ LIST_FOREACH_BACKWARDS(device_weights, l, LIST_FIND_TAIL(device_weights, src->blockio_device_weights)) {
+ r = cgroup_context_add_block_io_device_weight_dup(dst, l);
+ if (r < 0)
+ return r;
+ }
+
+ LIST_FOREACH_BACKWARDS(device_bandwidths, l, LIST_FIND_TAIL(device_bandwidths, src->blockio_device_bandwidths)) {
+ r = cgroup_context_add_block_io_device_bandwidth_dup(dst, l);
+ if (r < 0)
+ return r;
+ }
+
+ dst->memory_limit = src->memory_limit;
+
+ dst->device_policy = src->device_policy;
+ LIST_FOREACH_BACKWARDS(device_allow, l, LIST_FIND_TAIL(device_allow, src->device_allow)) {
+ r = cgroup_context_add_device_allow_dup(dst, l);
+ if (r < 0)
+ return r;
+ }
+
+ LIST_FOREACH_BACKWARDS(socket_bind_items, l, LIST_FIND_TAIL(socket_bind_items, src->socket_bind_allow)) {
+ r = cgroup_context_add_socket_bind_item_allow_dup(dst, l);
+ if (r < 0)
+ return r;
+
+ }
+
+ LIST_FOREACH_BACKWARDS(socket_bind_items, l, LIST_FIND_TAIL(socket_bind_items, src->socket_bind_deny)) {
+ r = cgroup_context_add_socket_bind_item_deny_dup(dst, l);
+ if (r < 0)
+ return r;
+ }
+
+ dst->tasks_max = src->tasks_max;
+
+ return 0;
+}
+
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
assert(c);
assert(a);
return r;
}
+static uint64_t unit_get_effective_limit_one(Unit *u, CGroupLimitType type) {
+ CGroupContext *cc;
+
+ assert(u);
+ assert(UNIT_HAS_CGROUP_CONTEXT(u));
+
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
+ switch (type) {
+ case CGROUP_LIMIT_MEMORY_MAX:
+ case CGROUP_LIMIT_MEMORY_HIGH:
+ return physical_memory();
+ case CGROUP_LIMIT_TASKS_MAX:
+ return system_tasks_max();
+ default:
+ assert_not_reached();
+ }
+
+ cc = unit_get_cgroup_context(u);
+ switch (type) {
+ /* Note: on legacy/hybrid hierarchies memory_max stays CGROUP_LIMIT_MAX unless configured
+ * explicitly. Effective value of MemoryLimit= (cgroup v1) is not implemented. */
+ case CGROUP_LIMIT_MEMORY_MAX:
+ return cc->memory_max;
+ case CGROUP_LIMIT_MEMORY_HIGH:
+ return cc->memory_high;
+ case CGROUP_LIMIT_TASKS_MAX:
+ return cgroup_tasks_max_resolve(&cc->tasks_max);
+ default:
+ assert_not_reached();
+ }
+}
+
+int unit_get_effective_limit(Unit *u, CGroupLimitType type, uint64_t *ret) {
+ uint64_t infimum;
+
+ assert(u);
+ assert(ret);
+ assert(type >= 0);
+ assert(type < _CGROUP_LIMIT_TYPE_MAX);
+
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return -EINVAL;
+
+ infimum = unit_get_effective_limit_one(u, type);
+ for (Unit *slice = UNIT_GET_SLICE(u); slice; slice = UNIT_GET_SLICE(slice))
+ infimum = MIN(infimum, unit_get_effective_limit_one(slice, type));
+
+ *ret = infimum;
+ return 0;
+}
+
static int unit_get_io_accounting_raw(Unit *u, uint64_t ret[static _CGROUP_IO_ACCOUNTING_METRIC_MAX]) {
static const char *const field_names[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "rbytes=",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_memory_accounting_metric, CGroupMemoryAccountingMetric);
+
+static const char *const cgroup_limit_type_table[_CGROUP_LIMIT_TYPE_MAX] = {
+ [CGROUP_LIMIT_MEMORY_MAX] = "EffectiveMemoryMax",
+ [CGROUP_LIMIT_MEMORY_HIGH] = "EffectiveMemoryHigh",
+ [CGROUP_LIMIT_TASKS_MAX] = "EffectiveTasksMax",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(cgroup_limit_type, CGroupLimitType);
_CGROUP_PRESSURE_WATCH_INVALID = -EINVAL,
} CGroupPressureWatch;
+/* When adding members make sure to update cgroup_context_copy() accordingly */
struct CGroupContext {
bool cpu_accounting;
bool io_accounting;
_CGROUP_MEMORY_ACCOUNTING_METRIC_INVALID = -EINVAL,
} CGroupMemoryAccountingMetric;
+/* Used for limits whose value sets have infimum */
+typedef enum CGroupLimitType {
+ CGROUP_LIMIT_MEMORY_MAX,
+ CGROUP_LIMIT_MEMORY_HIGH,
+ CGROUP_LIMIT_TASKS_MAX,
+ _CGROUP_LIMIT_TYPE_MAX,
+ _CGROUP_LIMIT_INVALID = -EINVAL,
+} CGroupLimitType;
+
typedef struct Unit Unit;
typedef struct Manager Manager;
typedef enum ManagerState ManagerState;
usec_t cgroup_cpu_adjust_period(usec_t period, usec_t quota, usec_t resolution, usec_t max_period);
void cgroup_context_init(CGroupContext *c);
+int cgroup_context_copy(CGroupContext *dst, const CGroupContext *src);
void cgroup_context_done(CGroupContext *c);
void cgroup_context_dump(Unit *u, FILE* f, const char *prefix);
void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE *f);
int cgroup_context_add_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
int cgroup_context_add_or_update_device_allow(CGroupContext *c, const char *dev, CGroupDevicePermissions p);
int cgroup_context_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
+int cgroup_context_add_io_device_limit_dup(CGroupContext *c, CGroupIODeviceLimit *l);
+int cgroup_context_add_io_device_weight_dup(CGroupContext *c, CGroupIODeviceWeight *w);
+int cgroup_context_add_io_device_latency_dup(CGroupContext *c, CGroupIODeviceLatency *l);
+int cgroup_context_add_block_io_device_weight_dup(CGroupContext *c, CGroupBlockIODeviceWeight *w);
+int cgroup_context_add_block_io_device_bandwidth_dup(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
+int cgroup_context_add_device_allow_dup(CGroupContext *c, CGroupDeviceAllow *a);
+int cgroup_context_add_socket_bind_item_allow_dup(CGroupContext *c, CGroupSocketBindItem *i);
+int cgroup_context_add_socket_bind_item_deny_dup(CGroupContext *c, CGroupSocketBindItem *i);
+
+static inline int cgroup_context_add_bpf_foreign_program_dup(CGroupContext *c, CGroupBPFForeignProgram *p) {
+ return cgroup_context_add_bpf_foreign_program(c, p->attach_type, p->bpffs_path);
+}
void unit_modify_nft_set(Unit *u, bool add);
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret);
int unit_get_ip_accounting(Unit *u, CGroupIPAccountingMetric metric, uint64_t *ret);
+int unit_get_effective_limit(Unit *u, CGroupLimitType type, uint64_t *ret);
int unit_reset_cpu_accounting(Unit *u);
void unit_reset_memory_accounting_last(Unit *u);
const char* cgroup_io_accounting_metric_to_string(CGroupIOAccountingMetric m) _const_;
CGroupIOAccountingMetric cgroup_io_accounting_metric_from_string(const char *s) _pure_;
+const char* cgroup_limit_type_to_string(CGroupLimitType m) _const_;
+CGroupLimitType cgroup_limit_type_from_string(const char *s) _pure_;
+
const char* cgroup_memory_accounting_metric_to_string(CGroupMemoryAccountingMetric m) _const_;
CGroupMemoryAccountingMetric cgroup_memory_accounting_metric_from_string(const char *s) _pure_;
return method_generic_unit_operation(message, userdata, error, bus_service_method_dump_file_descriptor_store, 0);
}
+static int aux_scope_from_message(Manager *m, sd_bus_message *message, Unit **ret_scope, sd_bus_error *error) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ _cleanup_free_ PidRef *pidrefs = NULL;
+ const char *name;
+ Unit *from, *scope;
+ PidRef *main_pid;
+ CGroupContext *cc;
+ size_t n_pids = 0;
+ uint64_t flags;
+ pid_t pid;
+ int r;
+
+ assert(ret_scope);
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r < 0)
+ return r;
+
+ from = manager_get_unit_by_pid(m, pid);
+ if (!from)
+ return sd_bus_error_set(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
+
+ if (!IN_SET(from->type, UNIT_SERVICE, UNIT_SCOPE))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Starting auxiliary scope is supported only for service and scope units, refusing.");
+
+ if (!unit_name_is_valid(from->id, UNIT_NAME_PLAIN))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Auxiliary scope can be started only for non-template service units and scope units, refusing.");
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Invalid name \"%s\" for auxiliary scope.", name);
+
+ if (unit_name_to_type(name) != UNIT_SCOPE)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Name \"%s\" of auxiliary scope doesn't have .scope suffix.", name);
+
+ main_pid = unit_main_pid(from);
+
+ r = sd_bus_message_enter_container(message, 'a', "h");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_(pidref_done) PidRef p = PIDREF_NULL;
+ Unit *unit;
+ int fd;
+
+ r = sd_bus_message_read(message, "h", &fd);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ r = pidref_set_pidfd(&p, fd);
+ if (r < 0) {
+ log_unit_warning_errno(from, r, "Failed to create process reference from PIDFD, ignoring: %m");
+ continue;
+ }
+
+ unit = manager_get_unit_by_pidref(m, &p);
+ if (!unit) {
+ log_unit_warning_errno(from, SYNTHETIC_ERRNO(ENOENT), "Failed to get unit from PIDFD, ingoring: %m");
+ continue;
+ }
+
+ if (!streq(unit->id, from->id)) {
+ log_unit_warning(from, "PID " PID_FMT " is not running in the same service as the calling process, ignoring.", p.pid);
+ continue;
+ }
+
+ if (pidref_equal(main_pid, &p)) {
+ log_unit_warning(from, "Main PID cannot be migrated into auxiliary scope, ignoring.");
+ continue;
+ }
+
+ if (!GREEDY_REALLOC(pidrefs, n_pids+1))
+ return -ENOMEM;
+
+ pidrefs[n_pids++] = TAKE_PIDREF(p);
+ }
+
+ if (n_pids == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "No processes can be migrated to auxiliary scope.");
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read(message, "t", &flags);
+ if (r < 0)
+ return r;
+
+ if (flags != 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
+
+ r = manager_load_unit(m, name, NULL, error, &scope);
+ if (r < 0)
+ return r;
+
+ if (!unit_is_pristine(scope))
+ return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
+ "Unit %s was already loaded or has a fragment file.", name);
+
+ r = unit_set_slice(scope, UNIT_GET_SLICE(from));
+ if (r < 0)
+ return r;
+
+ cc = unit_get_cgroup_context(scope);
+
+ r = cgroup_context_copy(cc, unit_get_cgroup_context(from));
+ if (r < 0)
+ return r;
+
+ r = unit_make_transient(scope);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_set_properties(scope, message, UNIT_RUNTIME, true, error);
+ if (r < 0)
+ return r;
+
+ FOREACH_ARRAY(p, pidrefs, n_pids) {
+ r = unit_pid_attachable(scope, p, error);
+ if (r < 0)
+ return r;
+
+ r = unit_watch_pidref(scope, p, /* exclusive= */ false);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
+ /* Now load the missing bits of the unit we just created */
+ unit_add_to_load_queue(scope);
+ manager_dispatch_load_queue(m);
+
+ *ret_scope = TAKE_PTR(scope);
+
+ return 1;
+}
+
+static int method_start_aux_scope(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = ASSERT_PTR(userdata);
+ Unit *u = NULL; /* avoid false maybe-uninitialized warning */
+ int r;
+
+ assert(message);
+
+ r = mac_selinux_access_check(message, "start", error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_units_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = aux_scope_from_message(m, message, &u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_queue_job(message, u, JOB_START, JOB_REPLACE, 0, error);
+}
+
const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_RESULT("a(suuutuusu)", entries),
method_dump_unit_descriptor_store,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("StartAuxiliaryScope",
+ SD_BUS_ARGS("s", name, "ah", pidfds, "t", flags, "a(sv)", properties),
+ SD_BUS_RESULT("o", job),
+ method_start_aux_scope,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL_WITH_ARGS("UnitNew",
SD_BUS_ARGS("s", id, "o", unit),
return sd_bus_message_append(reply, "t", value);
}
+static int property_get_effective_limit(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint64_t value = CGROUP_LIMIT_MAX;
+ Unit *u = ASSERT_PTR(userdata);
+ ssize_t type;
+
+ assert(bus);
+ assert(reply);
+ assert(property);
+
+ assert_se((type = cgroup_limit_type_from_string(property)) >= 0);
+ (void) unit_get_effective_limit(u, type, &value);
+ return sd_bus_message_append(reply, "t", value);
+}
+
int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_set_free_ Set *pids = NULL;
SD_BUS_PROPERTY("MemorySwapPeak", "t", property_get_memory_accounting, 0, 0),
SD_BUS_PROPERTY("MemoryZSwapCurrent", "t", property_get_memory_accounting, 0, 0),
SD_BUS_PROPERTY("MemoryAvailable", "t", property_get_available_memory, 0, 0),
+ SD_BUS_PROPERTY("EffectiveMemoryMax", "t", property_get_effective_limit, 0, 0),
+ SD_BUS_PROPERTY("EffectiveMemoryHigh", "t", property_get_effective_limit, 0, 0),
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
SD_BUS_PROPERTY("EffectiveCPUs", "ay", property_get_cpuset_cpus, 0, 0),
SD_BUS_PROPERTY("EffectiveMemoryNodes", "ay", property_get_cpuset_mems, 0, 0),
SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
+ SD_BUS_PROPERTY("EffectiveTasksMax", "t", property_get_effective_limit, 0, 0),
SD_BUS_PROPERTY("IPIngressBytes", "t", property_get_ip_counter, 0, 0),
SD_BUS_PROPERTY("IPIngressPackets", "t", property_get_ip_counter, 0, 0),
SD_BUS_PROPERTY("IPEgressBytes", "t", property_get_ip_counter, 0, 0),
#include "strv.h"
#include "terminal-util.h"
#include "utmp-wtmp.h"
+#include "vpick.h"
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
return false;
}
-static int setup_ephemeral(const ExecContext *context, ExecRuntime *runtime) {
+static int setup_ephemeral(
+ const ExecContext *context,
+ ExecRuntime *runtime,
+ char **root_image, /* both input and output! modified if ephemeral logic enabled */
+ char **root_directory) { /* ditto */
+
_cleanup_close_ int fd = -EBADF;
+ _cleanup_free_ char *new_root = NULL;
int r;
+ assert(context);
+ assert(root_image);
+ assert(root_directory);
+
+ if (!*root_image && !*root_directory)
+ return 0;
+
if (!runtime || !runtime->ephemeral_copy)
return 0;
+ assert(runtime->ephemeral_storage_socket[0] >= 0);
+ assert(runtime->ephemeral_storage_socket[1] >= 0);
+
+ new_root = strdup(runtime->ephemeral_copy);
+ if (!new_root)
+ return log_oom_debug();
+
r = posix_lock(runtime->ephemeral_storage_socket[0], LOCK_EX);
if (r < 0)
return log_debug_errno(r, "Failed to lock ephemeral storage socket: %m");
if (fd >= 0)
/* We got an fd! That means ephemeral has already been set up, so nothing to do here. */
return 0;
-
if (fd != -EAGAIN)
return log_debug_errno(fd, "Failed to receive file descriptor queued on ephemeral storage socket: %m");
- log_debug("Making ephemeral snapshot of %s to %s",
- context->root_image ?: context->root_directory, runtime->ephemeral_copy);
+ if (*root_image) {
+ log_debug("Making ephemeral copy of %s to %s", *root_image, new_root);
- if (context->root_image)
- fd = copy_file(context->root_image, runtime->ephemeral_copy, O_EXCL, 0600,
- COPY_LOCK_BSD|COPY_REFLINK|COPY_CRTIME);
- else
- fd = btrfs_subvol_snapshot_at(AT_FDCWD, context->root_directory,
- AT_FDCWD, runtime->ephemeral_copy,
- BTRFS_SNAPSHOT_FALLBACK_COPY |
- BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
- BTRFS_SNAPSHOT_RECURSIVE |
- BTRFS_SNAPSHOT_LOCK_BSD);
- if (fd < 0)
- return log_debug_errno(fd, "Failed to snapshot %s to %s: %m",
- context->root_image ?: context->root_directory, runtime->ephemeral_copy);
+ fd = copy_file(*root_image,
+ new_root,
+ O_EXCL,
+ 0600,
+ COPY_LOCK_BSD|
+ COPY_REFLINK|
+ COPY_CRTIME);
+ if (fd < 0)
+ return log_debug_errno(fd, "Failed to copy image %s to %s: %m",
+ *root_image, new_root);
- if (context->root_image) {
/* A root image might be subject to lots of random writes so let's try to disable COW on it
* which tends to not perform well in combination with lots of random writes.
*
*/
r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
if (r < 0)
- log_debug_errno(fd, "Failed to disable copy-on-write for %s, ignoring: %m", runtime->ephemeral_copy);
+ log_debug_errno(fd, "Failed to disable copy-on-write for %s, ignoring: %m", new_root);
+ } else {
+ assert(*root_directory);
+
+ log_debug("Making ephemeral snapshot of %s to %s", *root_directory, new_root);
+
+ fd = btrfs_subvol_snapshot_at(
+ AT_FDCWD, *root_directory,
+ AT_FDCWD, new_root,
+ BTRFS_SNAPSHOT_FALLBACK_COPY |
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY |
+ BTRFS_SNAPSHOT_RECURSIVE |
+ BTRFS_SNAPSHOT_LOCK_BSD);
+ if (fd < 0)
+ return log_debug_errno(fd, "Failed to snapshot directory %s to %s: %m",
+ *root_directory, new_root);
}
r = send_one_fd(runtime->ephemeral_storage_socket[1], fd, MSG_DONTWAIT);
if (r < 0)
return log_debug_errno(r, "Failed to queue file descriptor on ephemeral storage socket: %m");
+ if (*root_image)
+ free_and_replace(*root_image, new_root);
+ else {
+ assert(*root_directory);
+ free_and_replace(*root_directory, new_root);
+ }
+
return 1;
}
return 0;
}
+static int pick_versions(
+ const ExecContext *context,
+ const ExecParameters *params,
+ char **ret_root_image,
+ char **ret_root_directory) {
+
+ int r;
+
+ assert(context);
+ assert(params);
+ assert(ret_root_image);
+ assert(ret_root_directory);
+
+ if (context->root_image) {
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ context->root_image,
+ &pick_filter_image_raw,
+ PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+ &result);
+ if (r < 0)
+ return r;
+
+ if (!result.path)
+ return log_exec_debug_errno(context, params, SYNTHETIC_ERRNO(ENOENT), "No matching entry in .v/ directory %s found.", context->root_image);
+
+ *ret_root_image = TAKE_PTR(result.path);
+ *ret_root_directory = NULL;
+ return r;
+ }
+
+ if (context->root_directory) {
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ context->root_directory,
+ &pick_filter_image_dir,
+ PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+ &result);
+ if (r < 0)
+ return r;
+
+ if (!result.path)
+ return log_exec_debug_errno(context, params, SYNTHETIC_ERRNO(ENOENT), "No matching entry in .v/ directory %s found.", context->root_directory);
+
+ *ret_root_image = NULL;
+ *ret_root_directory = TAKE_PTR(result.path);
+ return r;
+ }
+
+ *ret_root_image = *ret_root_directory = NULL;
+ return 0;
+}
+
static int apply_mount_namespace(
ExecCommandFlags command_flags,
const ExecContext *context,
_cleanup_strv_free_ char **empty_directories = NULL, **symlinks = NULL,
**read_write_paths_cleanup = NULL;
_cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL,
- *extension_dir = NULL, *host_os_release_stage = NULL;
- const char *root_dir = NULL, *root_image = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL;
+ *extension_dir = NULL, *host_os_release_stage = NULL, *root_image = NULL, *root_dir = NULL;
+ const char *tmp_dir = NULL, *var_tmp_dir = NULL;
char **read_write_paths;
bool needs_sandboxing, setup_os_release_symlink;
BindMount *bind_mounts = NULL;
CLEANUP_ARRAY(bind_mounts, n_bind_mounts, bind_mount_free_many);
if (params->flags & EXEC_APPLY_CHROOT) {
- r = setup_ephemeral(context, runtime);
+ r = pick_versions(
+ context,
+ params,
+ &root_image,
+ &root_dir);
if (r < 0)
return r;
- if (context->root_image)
- root_image = (runtime ? runtime->ephemeral_copy : NULL) ?: context->root_image;
- else
- root_dir = (runtime ? runtime->ephemeral_copy : NULL) ?: context->root_directory;
+ r = setup_ephemeral(
+ context,
+ runtime,
+ &root_image,
+ &root_dir);
+ if (r < 0)
+ return r;
}
r = compile_bind_mounts(context, params, &bind_mounts, &n_bind_mounts, &empty_directories);
#include "tmpfile-util.h"
#include "uid-alloc-range.h"
#include "user-util.h"
+#include "vpick.h"
static enum {
ACTION_DISSECT,
FOREACH_DEVICE(e, d) {
_cleanup_(loop_device_unrefp) LoopDevice *entry_loop = NULL;
- const char *name, *devtype;
- r = sd_device_get_sysname(d, &name);
- if (r < 0) {
- log_warning_errno(r, "Failed to get enumerated device's sysname, skipping: %m");
- continue;
- }
-
- r = sd_device_get_devtype(d, &devtype);
- if (r < 0) {
- log_warning_errno(r, "Failed to get devtype of '%s', skipping: %m", name);
- continue;
- }
-
- if (!streq(devtype, "disk")) /* Filter out partition block devices */
+ if (!device_is_devtype(d, "disk")) /* Filter out partition block devices */
continue;
r = loop_device_open(d, O_RDONLY, LOCK_SH, &entry_loop);
if (r < 0) {
- log_warning_errno(r, "Failed to open loopback block device '%s', skipping: %m", name);
+ log_device_warning_errno(d, r, "Failed to open loopback block device, skipping: %m");
continue;
}
if (r <= 0)
return r;
+ if (arg_image) {
+ r = path_pick_update_warn(
+ &arg_image,
+ &pick_filter_image_raw,
+ PICK_ARCHITECTURE|PICK_TRIES,
+ /* ret_result= */ NULL);
+ if (r < 0)
+ return r;
+ }
+
switch (arg_action) {
case ACTION_UMOUNT:
return action_umount(arg_path);
#define EFI_STUB_FEATURE_CMDLINE_ADDONS (UINT64_C(1) << 5)
#define EFI_STUB_FEATURE_CMDLINE_SMBIOS (UINT64_C(1) << 6)
#define EFI_STUB_FEATURE_DEVICETREE_ADDONS (UINT64_C(1) << 7)
+#define EFI_STUB_FEATURE_PICK_UP_CONFEXTS (UINT64_C(1) << 8)
typedef enum SecureBootMode {
SECURE_BOOT_UNSUPPORTED,
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define SWAP(n) \
- (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+ __builtin_bswap32(n)
# define SWAP64(n) \
- (((n) << 56) \
- | (((n) & 0xff00) << 40) \
- | (((n) & 0xff0000) << 24) \
- | (((n) & 0xff000000) << 8) \
- | (((n) >> 8) & 0xff000000) \
- | (((n) >> 24) & 0xff0000) \
- | (((n) >> 40) & 0xff00) \
- | ((n) >> 56))
+ __builtin_bswap64(n)
#else
# define SWAP(n) (n)
# define SWAP64(n) (n)
#include "proc-cmdline.h"
#include "efivars.h"
+typedef struct KernelHibernateLocation {
+ char *device;
+ uint64_t offset;
+ bool offset_set;
+} KernelHibernateLocation;
+
static KernelHibernateLocation* kernel_hibernate_location_free(KernelHibernateLocation *k) {
if (!k)
return NULL;
DEFINE_TRIVIAL_CLEANUP_FUNC(KernelHibernateLocation*, kernel_hibernate_location_free);
+typedef struct EFIHibernateLocation {
+ char *device;
+
+ sd_id128_t uuid;
+ uint64_t offset;
+
+ char *kernel_version;
+ char *id;
+ char *image_id;
+ char *version_id;
+ char *image_version;
+} EFIHibernateLocation;
+
static EFIHibernateLocation* efi_hibernate_location_free(EFIHibernateLocation *e) {
if (!e)
return NULL;
#include "sd-id128.h"
-typedef struct KernelHibernateLocation {
- char *device;
- uint64_t offset;
- bool offset_set;
-} KernelHibernateLocation;
-
-typedef struct EFIHibernateLocation {
- char *device;
-
- sd_id128_t uuid;
- uint64_t offset;
-
- char *kernel_version;
- char *id;
- char *image_id;
- char *version_id;
- char *image_version;
-} EFIHibernateLocation;
+typedef struct KernelHibernateLocation KernelHibernateLocation;
+typedef struct EFIHibernateLocation EFIHibernateLocation;
typedef struct HibernateInfo {
const char *device;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp-client-id.h"
+
+#include "dhcp-duid-internal.h"
+#include "macro.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+
+/* RFC 2132 section 9.14: its minimum length is 2.
+ * Note, its maximum is not mentioend in the RFC. Hence, 255. */
+#define MIN_CLIENT_ID_LEN 2
+#define MAX_CLIENT_ID_LEN 255
+#define MIN_CLIENT_ID_DATA_LEN (MIN_CLIENT_ID_LEN - sizeof(uint8_t))
+#define MAX_CLIENT_ID_DATA_LEN (MAX_CLIENT_ID_LEN - sizeof(uint8_t))
+
+typedef struct sd_dhcp_client_id {
+ size_t size;
+ union {
+ struct {
+ uint8_t type;
+ union {
+ struct {
+ /* 0: Generic (non-LL) (RFC 2132) */
+ uint8_t data[MAX_CLIENT_ID_DATA_LEN];
+ } _packed_ gen;
+ struct {
+ /* 1: Ethernet Link-Layer (RFC 2132) */
+ uint8_t haddr[ETH_ALEN];
+ } _packed_ eth;
+ struct {
+ /* 2 - 254: ARP/Link-Layer (RFC 2132) */
+ uint8_t haddr[0];
+ } _packed_ ll;
+ struct {
+ /* 255: Node-specific (RFC 4361) */
+ be32_t iaid;
+ struct duid duid;
+ } _packed_ ns;
+ uint8_t data[MAX_CLIENT_ID_DATA_LEN];
+ };
+ } _packed_ id;
+ uint8_t raw[MAX_CLIENT_ID_LEN];
+ };
+} sd_dhcp_client_id;
+
+static inline bool client_id_size_is_valid(size_t size) {
+ return size >= MIN_CLIENT_ID_LEN && size <= MAX_CLIENT_ID_LEN;
+}
+
+static inline bool client_id_data_size_is_valid(size_t size) {
+ return size >= MIN_CLIENT_ID_DATA_LEN && size <= MAX_CLIENT_ID_DATA_LEN;
+}
+
+void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state);
+int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b);
#pragma once
#include "sd-device.h"
+#include "sd-dhcp-duid.h"
#include "sd-id128.h"
#include "ether-addr-util.h"
#include "macro.h"
#include "sparse-endian.h"
-#include "time-util.h"
-#include "unaligned.h"
#define SYSTEMD_PEN 43793
typedef enum DUIDType {
- DUID_TYPE_LLT = 1,
- DUID_TYPE_EN = 2,
- DUID_TYPE_LL = 3,
- DUID_TYPE_UUID = 4,
+ DUID_TYPE_LLT = SD_DUID_TYPE_LLT,
+ DUID_TYPE_EN = SD_DUID_TYPE_EN,
+ DUID_TYPE_LL = SD_DUID_TYPE_LL,
+ DUID_TYPE_UUID = SD_DUID_TYPE_UUID,
_DUID_TYPE_MAX,
- _DUID_TYPE_INVALID = -EINVAL,
- _DUID_TYPE_FORCE_U16 = UINT16_MAX,
+ _DUID_TYPE_INVALID = -EINVAL,
} DUIDType;
/* RFC 8415 section 11.1:
* A DUID consists of a 2-octet type code represented in network byte order, followed by a variable number of
* octets that make up the actual identifier. The length of the DUID (not including the type code) is at
* least 1 octet and at most 128 octets. */
+#define MIN_DUID_DATA_LEN 1
#define MAX_DUID_DATA_LEN 128
+#define MIN_DUID_LEN (sizeof(be16_t) + MIN_DUID_DATA_LEN)
#define MAX_DUID_LEN (sizeof(be16_t) + MAX_DUID_DATA_LEN)
/* https://tools.ietf.org/html/rfc3315#section-9.1 */
/* DUID_TYPE_UUID */
sd_id128_t uuid;
} _packed_ uuid;
- struct {
- uint8_t data[MAX_DUID_DATA_LEN];
- } _packed_ raw;
+ uint8_t data[MAX_DUID_DATA_LEN];
};
} _packed_;
-int dhcp_identifier_set_duid_llt(
- const struct hw_addr_data *hw_addr,
- uint16_t arp_type,
- usec_t t,
- struct duid *ret_duid,
- size_t *ret_len);
-int dhcp_identifier_set_duid_ll(
- const struct hw_addr_data *hw_addr,
- uint16_t arp_type,
- struct duid *ret_duid,
- size_t *ret_len);
-int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid_raw(
- DUIDType duid_type,
- const uint8_t *buf,
- size_t buf_len,
- struct duid *ret_duid,
- size_t *ret_len);
+typedef struct sd_dhcp_duid {
+ size_t size;
+ union {
+ struct duid duid;
+ uint8_t raw[MAX_DUID_LEN];
+ };
+} sd_dhcp_duid;
+
+static inline bool duid_size_is_valid(size_t size) {
+ return size >= MIN_DUID_LEN && size <= MAX_DUID_LEN;
+}
+
+static inline bool duid_data_size_is_valid(size_t size) {
+ return size >= MIN_DUID_DATA_LEN && size <= MAX_DUID_DATA_LEN;
+}
+
+const char *duid_type_to_string(DUIDType t) _const_;
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret);
+
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret);
-
-const char *duid_type_to_string(DUIDType t) _const_;
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <linux/if_infiniband.h>
-#include <net/ethernet.h>
-#include <net/if_arp.h>
-
-#include "dhcp-identifier.h"
-#include "netif-util.h"
-#include "network-common.h"
-#include "siphash24.h"
-#include "sparse-endian.h"
-#include "string-table.h"
-
-#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
-#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
-#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
-
-static const char * const duid_type_table[_DUID_TYPE_MAX] = {
- [DUID_TYPE_LLT] = "DUID-LLT",
- [DUID_TYPE_EN] = "DUID-EN/Vendor",
- [DUID_TYPE_LL] = "DUID-LL",
- [DUID_TYPE_UUID] = "UUID",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
-
-int dhcp_identifier_set_duid_llt(
- const struct hw_addr_data *hw_addr,
- uint16_t arp_type,
- usec_t t,
- struct duid *ret_duid,
- size_t *ret_len) {
-
- uint16_t time_from_2000y;
-
- assert(hw_addr);
- assert(ret_duid);
- assert(ret_len);
-
- if (hw_addr->length == 0)
- return -EOPNOTSUPP;
-
- if (arp_type == ARPHRD_ETHER)
- assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
- else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
- else
- return -EOPNOTSUPP;
-
- if (t < USEC_2000)
- time_from_2000y = 0;
- else
- time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
- unaligned_write_be16(&ret_duid->llt.htype, arp_type);
- unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
- memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length);
-
- *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
-
- return 0;
-}
-
-int dhcp_identifier_set_duid_ll(
- const struct hw_addr_data *hw_addr,
- uint16_t arp_type,
- struct duid *ret_duid,
- size_t *ret_len) {
-
- assert(hw_addr);
- assert(ret_duid);
- assert(ret_len);
-
- if (hw_addr->length == 0)
- return -EOPNOTSUPP;
-
- if (arp_type == ARPHRD_ETHER)
- assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
- else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
- else
- return -EOPNOTSUPP;
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
- unaligned_write_be16(&ret_duid->ll.htype, arp_type);
- memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length);
-
- *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
-
- return 0;
-}
-
-int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len) {
- sd_id128_t machine_id;
- bool test_mode;
- uint64_t hash;
- int r;
-
- assert(ret_duid);
- assert(ret_len);
-
- test_mode = network_test_mode_enabled();
-
- if (!test_mode) {
- r = sd_id128_get_machine(&machine_id);
- if (r < 0)
- return r;
- } else
- /* For tests, especially for fuzzers, reproducibility is important.
- * Hence, use a static and constant machine ID.
- * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
- machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN);
- unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN);
-
- /* a bit of snake-oil perhaps, but no need to expose the machine-id
- * directly; duid->en.id might not be aligned, so we need to copy */
- hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
- memcpy(ret_duid->en.id, &hash, sizeof(hash));
-
- *ret_len = offsetof(struct duid, en.id) + sizeof(hash);
-
- if (test_mode)
- assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
-
- return 0;
-}
-
-int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
- sd_id128_t machine_id;
- int r;
-
- assert(ret_duid);
- assert(ret_len);
-
- r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
- if (r < 0)
- return r;
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
- memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id));
-
- *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
-
- return 0;
-}
-
-int dhcp_identifier_set_duid_raw(
- DUIDType duid_type,
- const uint8_t *buf,
- size_t buf_len,
- struct duid *ret_duid,
- size_t *ret_len) {
-
- assert(buf || buf_len == 0);
- assert(ret_duid);
- assert(ret_len);
-
- if (duid_type < 0 || duid_type > UINT16_MAX)
- return -EINVAL;
-
- if (buf_len > MAX_DUID_DATA_LEN)
- return -EINVAL;
-
- unaligned_write_be16(&ret_duid->type, duid_type);
- memcpy_safe(ret_duid->raw.data, buf, buf_len);
-
- *ret_len = offsetof(struct duid, raw.data) + buf_len;
- return 0;
-}
-
-int dhcp_identifier_set_iaid(
- sd_device *dev,
- const struct hw_addr_data *hw_addr,
- bool legacy_unstable_byteorder,
- void *ret) {
-
- const char *name = NULL;
- uint32_t id32;
- uint64_t id;
-
- assert(hw_addr);
- assert(ret);
-
- if (dev)
- name = net_get_persistent_name(dev);
- if (name)
- id = siphash24(name, strlen(name), HASH_KEY.bytes);
- else
- /* fall back to MAC address if no predictable name available */
- id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
-
- id32 = (id & 0xffffffff) ^ (id >> 32);
-
- if (legacy_unstable_byteorder)
- /* for historical reasons (a bug), the bits were swapped and thus
- * the result was endianness dependent. Preserve that behavior. */
- id32 = bswap_32(id32);
- else
- /* the fixed behavior returns a stable byte order. Since LE is expected
- * to be more common, swap the bytes on LE to give the same as legacy
- * behavior. */
- id32 = be32toh(id32);
-
- unaligned_write_ne32(ret, id32);
- return 0;
-}
#include "sd-dhcp-client.h"
#include "alloc-util.h"
+#include "dhcp-client-id-internal.h"
#include "dhcp-option.h"
#include "list.h"
#include "time-util.h"
char *root_path;
char *captive_portal;
- void *client_id;
- size_t client_id_len;
+ sd_dhcp_client_id client_id;
void *vendor_specific;
size_t vendor_specific_len;
void dhcp_lease_set_timestamp(sd_dhcp_lease *lease, const triple_timestamp *timestamp);
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id *client_id);
#define dhcp_lease_unref_and_replace(a, b) \
unref_and_replace_full(a, b, sd_dhcp_lease_ref, sd_dhcp_lease_unref)
#include "sd-dhcp-server.h"
#include "sd-event.h"
+#include "dhcp-client-id-internal.h"
#include "dhcp-option.h"
#include "network-common.h"
#include "ordered-set.h"
_DHCP_RAW_OPTION_DATA_INVALID,
} DHCPRawOption;
-typedef struct DHCPClientId {
- size_t length;
- uint8_t *data;
-} DHCPClientId;
-
-typedef struct DHCPLease {
- sd_dhcp_server *server;
-
- DHCPClientId client_id;
-
- uint8_t htype; /* e.g. ARPHRD_ETHER */
- uint8_t hlen; /* e.g. ETH_ALEN */
- be32_t address;
- be32_t gateway;
- uint8_t chaddr[16];
- usec_t expiration;
- char *hostname;
-} DHCPLease;
-
struct sd_dhcp_server {
unsigned n_ref;
DHCPMessage *message;
/* options */
- DHCPClientId client_id;
+ sd_dhcp_client_id client_id;
size_t max_optlen;
be32_t server_id;
be32_t requested_ip;
triple_timestamp timestamp;
} DHCPRequest;
-extern const struct hash_ops dhcp_lease_hash_ops;
-
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length, const triple_timestamp *timestamp);
int dhcp_server_send_packet(sd_dhcp_server *server,
DHCPRequest *req, DHCPPacket *packet,
int type, size_t optoffset);
-void client_id_hash_func(const DHCPClientId *p, struct siphash *state);
-int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b);
-
-DHCPLease *dhcp_lease_free(DHCPLease *lease);
-DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, dhcp_lease_free);
-
#define log_dhcp_server_errno(server, error, fmt, ...) \
log_interface_prefix_full_errno( \
"DHCPv4 server: ", \
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp-server-lease.h"
+
+#include "dhcp-client-id-internal.h"
+#include "dhcp-server-internal.h"
+#include "time-util.h"
+
+typedef struct sd_dhcp_server_lease {
+ unsigned n_ref;
+
+ sd_dhcp_server *server;
+
+ sd_dhcp_client_id client_id;
+
+ uint8_t htype; /* e.g. ARPHRD_ETHER */
+ uint8_t hlen; /* e.g. ETH_ALEN */
+ be32_t address;
+ be32_t gateway;
+ uint8_t chaddr[16];
+ usec_t expiration;
+ char *hostname;
+} sd_dhcp_server_lease;
+
+extern const struct hash_ops dhcp_server_lease_hash_ops;
+
+int dhcp_server_put_lease(sd_dhcp_server *server, sd_dhcp_server_lease *lease, bool is_static);
+
+int dhcp_server_set_lease(sd_dhcp_server *server, be32_t address, DHCPRequest *req, usec_t expiration);
+int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server);
+
+sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req);
#include "sd-event.h"
#include "sd-dhcp6-client.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "dhcp6-client-internal.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
DHCP6IA ia_na;
DHCP6IA ia_pd;
DHCP6RequestIA request_ia;
- struct duid duid;
- size_t duid_len;
+ sd_dhcp_duid duid;
be16_t *req_opts;
size_t n_req_opts;
char *fqdn;
}
static int add_lease(sd_dhcp_server *server, const struct in_addr *server_address, uint8_t i) {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
+ _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
int r;
assert(server);
- lease = new(DHCPLease, 1);
+ lease = new(sd_dhcp_server_lease, 1);
if (!lease)
return -ENOMEM;
- *lease = (DHCPLease) {
+ *lease = (sd_dhcp_server_lease) {
+ .n_ref = 1,
.address = htobe32(UINT32_C(10) << 24 | i),
.chaddr = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
.expiration = UINT64_MAX,
.hlen = ETH_ALEN,
.htype = ARPHRD_ETHER,
- .client_id.length = 2,
+ .client_id.size = 2,
};
- lease->client_id.data = new(uint8_t, lease->client_id.length);
- if (!lease->client_id.data)
- return -ENOMEM;
-
- lease->client_id.data[0] = 2;
- lease->client_id.data[1] = i;
-
- lease->server = server; /* This must be set just before hashmap_put(). */
-
- r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
- if (r < 0)
- return r;
+ lease->client_id.raw[0] = 2;
+ lease->client_id.raw[1] = i;
- r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+ r = dhcp_server_put_lease(server, lease, /* is_static = */ false);
if (r < 0)
return r;
sources = files(
'arp-util.c',
- 'dhcp-identifier.c',
'dhcp-network.c',
'dhcp-option.c',
'dhcp-packet.c',
'ndisc-router.c',
'network-common.c',
'network-internal.c',
+ 'sd-dhcp-client-id.c',
'sd-dhcp-client.c',
+ 'sd-dhcp-duid.c',
'sd-dhcp-lease.c',
+ 'sd-dhcp-server-lease.c',
'sd-dhcp-server.c',
'sd-dhcp6-client.c',
'sd-dhcp6-lease.c',
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "dhcp-client-id-internal.h"
+#include "unaligned.h"
+#include "utf8.h"
+
+int sd_dhcp_client_id_clear(sd_dhcp_client_id *client_id) {
+ assert_return(client_id, -EINVAL);
+
+ *client_id = (sd_dhcp_client_id) {};
+ return 0;
+}
+
+int sd_dhcp_client_id_is_set(const sd_dhcp_client_id *client_id) {
+ if (!client_id)
+ return false;
+
+ return client_id_size_is_valid(client_id->size);
+}
+
+int sd_dhcp_client_id_get(const sd_dhcp_client_id *client_id, uint8_t *ret_type, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL);
+ assert_return(ret_type, -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ *ret_type = client_id->id.type;
+ *ret_data = client_id->id.data;
+ *ret_size = client_id->size - offsetof(typeof(client_id->id), data);
+ return 0;
+}
+
+int sd_dhcp_client_id_get_raw(const sd_dhcp_client_id *client_id, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ /* Unlike sd_dhcp_client_id_get(), this returns whole client ID including its type. */
+
+ *ret_data = client_id->raw;
+ *ret_size = client_id->size;
+ return 0;
+}
+
+int sd_dhcp_client_id_set(
+ sd_dhcp_client_id *client_id,
+ uint8_t type,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(client_id, -EINVAL);
+ assert_return(data, -EINVAL);
+ assert_return(client_id_data_size_is_valid(data_size), -EINVAL);
+
+ client_id->id.type = type;
+ memcpy(client_id->id.data, data, data_size);
+
+ client_id->size = offsetof(typeof(client_id->id), data) + data_size;
+ return 0;
+}
+
+int sd_dhcp_client_id_set_raw(
+ sd_dhcp_client_id *client_id,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(client_id, -EINVAL);
+ assert_return(data, -EINVAL);
+ assert_return(client_id_size_is_valid(data_size), -EINVAL);
+
+ /* Unlike sd_dhcp_client_id_set(), this takes whole client ID including its type. */
+
+ memcpy(client_id->raw, data, data_size);
+
+ client_id->size = data_size;
+ return 0;
+}
+
+int sd_dhcp_client_id_set_iaid_duid(
+ sd_dhcp_client_id *client_id,
+ uint32_t iaid,
+ sd_dhcp_duid *duid) {
+
+ assert_return(client_id, -EINVAL);
+ assert_return(duid, -EINVAL);
+ assert_return(sd_dhcp_duid_is_set(duid), -ESTALE);
+
+ client_id->id.type = 255;
+ unaligned_write_be32(&client_id->id.ns.iaid, iaid);
+ memcpy(&client_id->id.ns.duid, &duid->duid, duid->size);
+
+ client_id->size = offsetof(typeof(client_id->id), ns.duid) + duid->size;
+ return 0;
+}
+
+int sd_dhcp_client_id_to_string(const sd_dhcp_client_id *client_id, char **ret) {
+ _cleanup_free_ char *t = NULL;
+ size_t len;
+ int r;
+
+ assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ len = client_id->size - offsetof(typeof(client_id->id), data);
+
+ switch (client_id->id.type) {
+ case 0:
+ if (utf8_is_printable((char *) client_id->id.gen.data, len))
+ r = asprintf(&t, "%.*s", (int) len, client_id->id.gen.data);
+ else
+ r = asprintf(&t, "DATA");
+ break;
+ case 1:
+ if (len == sizeof_field(sd_dhcp_client_id, id.eth))
+ r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
+ client_id->id.eth.haddr[0],
+ client_id->id.eth.haddr[1],
+ client_id->id.eth.haddr[2],
+ client_id->id.eth.haddr[3],
+ client_id->id.eth.haddr[4],
+ client_id->id.eth.haddr[5]);
+ else
+ r = asprintf(&t, "ETHER");
+ break;
+ case 2 ... 254:
+ r = asprintf(&t, "ARP/LL");
+ break;
+ case 255:
+ if (len < sizeof(uint32_t))
+ r = asprintf(&t, "IAID/DUID");
+ else {
+ uint32_t iaid = be32toh(client_id->id.ns.iaid);
+ /* TODO: check and stringify DUID */
+ r = asprintf(&t, "IAID:0x%x/DUID", iaid);
+ }
+ break;
+ }
+ if (r < 0)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(t);
+ return 0;
+}
+
+int sd_dhcp_client_id_to_string_from_raw(const void *data, size_t data_size, char **ret) {
+ sd_dhcp_client_id client_id;
+ int r;
+
+ assert_return(data, -EINVAL);
+ assert_return(client_id_size_is_valid(data_size), -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = sd_dhcp_client_id_set_raw(&client_id, data, data_size);
+ if (r < 0)
+ return r;
+
+ return sd_dhcp_client_id_to_string(&client_id, ret);
+}
+
+void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state) {
+ assert(sd_dhcp_client_id_is_set(client_id));
+ assert(state);
+
+ siphash24_compress_typesafe(client_id->size, state);
+ siphash24_compress(client_id->raw, client_id->size, state);
+}
+
+int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b) {
+ assert(sd_dhcp_client_id_is_set(a));
+ assert(sd_dhcp_client_id_is_set(b));
+
+ return memcmp_nn(a->raw, a->size, b->raw, b->size);
+}
#include "alloc-util.h"
#include "device-util.h"
+#include "dhcp-client-id-internal.h"
#include "dhcp-client-internal.h"
-#include "dhcp-identifier.h"
#include "dhcp-lease-internal.h"
#include "dhcp-network.h"
#include "dhcp-option.h"
#include "utf8.h"
#include "web-util.h"
-#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
#define TRANSIENT_FAILURE_ATTEMPTS 3 /* Arbitrary limit: how many attempts are considered enough to report
* transient failure. */
-typedef struct sd_dhcp_client_id {
- uint8_t type;
- union {
- struct {
- /* 0: Generic (non-LL) (RFC 2132) */
- uint8_t data[MAX_CLIENT_ID_LEN];
- } _packed_ gen;
- struct {
- /* 1: Ethernet Link-Layer (RFC 2132) */
- uint8_t haddr[ETH_ALEN];
- } _packed_ eth;
- struct {
- /* 2 - 254: ARP/Link-Layer (RFC 2132) */
- uint8_t haddr[0];
- } _packed_ ll;
- struct {
- /* 255: Node-specific (RFC 4361) */
- be32_t iaid;
- struct duid duid;
- } _packed_ ns;
- struct {
- uint8_t data[MAX_CLIENT_ID_LEN];
- } _packed_ raw;
- };
-} _packed_ sd_dhcp_client_id;
-
struct sd_dhcp_client {
unsigned n_ref;
struct hw_addr_data bcast_addr;
uint16_t arp_type;
sd_dhcp_client_id client_id;
- size_t client_id_len;
char *hostname;
char *vendor_class_identifier;
char *mudurl;
void *userdata);
static void client_stop(sd_dhcp_client *client, int error);
-int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
- const sd_dhcp_client_id *client_id = data;
- _cleanup_free_ char *t = NULL;
- int r = 0;
-
- assert_return(data, -EINVAL);
- assert_return(len >= 1, -EINVAL);
- assert_return(ret, -EINVAL);
-
- len -= 1;
- if (len > MAX_CLIENT_ID_LEN)
- return -EINVAL;
-
- switch (client_id->type) {
- case 0:
- if (utf8_is_printable((char *) client_id->gen.data, len))
- r = asprintf(&t, "%.*s", (int) len, client_id->gen.data);
- else
- r = asprintf(&t, "DATA");
- break;
- case 1:
- if (len == sizeof_field(sd_dhcp_client_id, eth))
- r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
- client_id->eth.haddr[0],
- client_id->eth.haddr[1],
- client_id->eth.haddr[2],
- client_id->eth.haddr[3],
- client_id->eth.haddr[4],
- client_id->eth.haddr[5]);
- else
- r = asprintf(&t, "ETHER");
- break;
- case 2 ... 254:
- r = asprintf(&t, "ARP/LL");
- break;
- case 255:
- if (len < sizeof(uint32_t))
- r = asprintf(&t, "IAID/DUID");
- else {
- uint32_t iaid = be32toh(client_id->ns.iaid);
- /* TODO: check and stringify DUID */
- r = asprintf(&t, "IAID:0x%x/DUID", iaid);
- }
- break;
- }
- if (r < 0)
- return -ENOMEM;
-
- *ret = TAKE_PTR(t);
- return 0;
-}
-
int dhcp_client_set_state_callback(
sd_dhcp_client *client,
sd_dhcp_client_callback_t cb,
return 0;
}
-int sd_dhcp_client_get_client_id(
- sd_dhcp_client *client,
- uint8_t *ret_type,
- const uint8_t **ret_data,
- size_t *ret_data_len) {
-
+int sd_dhcp_client_get_client_id(sd_dhcp_client *client, const sd_dhcp_client_id **ret) {
assert_return(client, -EINVAL);
+ assert_return(ret, -EINVAL);
- if (client->client_id_len > 0) {
- if (client->client_id_len <= offsetof(sd_dhcp_client_id, raw.data))
- return -EINVAL;
-
- if (ret_type)
- *ret_type = client->client_id.type;
- if (ret_data)
- *ret_data = client->client_id.raw.data;
- if (ret_data_len)
- *ret_data_len = client->client_id_len - offsetof(sd_dhcp_client_id, raw.data);
- return 1;
- }
-
- if (ret_type)
- *ret_type = 0;
- if (ret_data)
- *ret_data = NULL;
- if (ret_data_len)
- *ret_data_len = 0;
+ if (!sd_dhcp_client_id_is_set(&client->client_id))
+ return -ENODATA;
+ *ret = &client->client_id;
return 0;
}
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(data, -EINVAL);
- assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
+ assert_return(client_id_data_size_is_valid(data_len), -EINVAL);
/* For hardware types, log debug message about unexpected data length.
*
"Changing client ID to hardware type %u with unexpected address length %zu",
type, data_len);
- client->client_id.type = type;
- memcpy(&client->client_id.raw.data, data, data_len);
- client->client_id_len = data_len + sizeof (client->client_id.type);
-
- return 0;
+ return sd_dhcp_client_id_set(&client->client_id, type, data, data_len);
}
-/**
- * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid
- * without further modification. Otherwise, if duid_type is supported, DUID
- * is set based on that type. Otherwise, an error is returned.
- */
-static int dhcp_client_set_iaid(
+static int dhcp_client_set_iaid_duid(
sd_dhcp_client *client,
bool iaid_set,
- uint32_t iaid) {
+ uint32_t iaid,
+ sd_dhcp_duid *duid) {
int r;
- assert_return(client, -EINVAL);
- assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
-
- zero(client->client_id);
- client->client_id.type = 255;
-
- if (iaid_set)
- client->client_id.ns.iaid = htobe32(iaid);
- else {
+ if (!iaid_set) {
r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr,
/* legacy_unstable_byteorder = */ true,
- &client->client_id.ns.iaid);
+ &iaid);
if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set IAID: %m");
+ return r;
+
+ iaid = be32toh(iaid);
}
- return 0;
+ return sd_dhcp_client_id_set_iaid_duid(&client->client_id, iaid, duid);
}
int sd_dhcp_client_set_iaid_duid_llt(
uint32_t iaid,
usec_t llt_time) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set_llt(&duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_iaid_duid_ll(
bool iaid_set,
uint32_t iaid) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set_ll(&duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_iaid_duid_en(
bool iaid_set,
uint32_t iaid) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set_en(&duid);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_iaid_duid_uuid(
bool iaid_set,
uint32_t iaid) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set_uuid(&duid);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_iaid_duid_raw(
bool iaid_set,
uint32_t iaid,
uint16_t duid_type,
- const uint8_t *duid,
- size_t duid_len) {
+ const uint8_t *duid_data,
+ size_t duid_data_len) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- assert_return(duid || duid_len == 0, -EINVAL);
+ assert_return(duid_data || duid_data_len == 0, -EINVAL);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set(&duid, duid_type, duid_data, duid_data_len);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_rapid_commit(sd_dhcp_client *client, bool rapid_commit) {
Identifier option is not set */
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_CLIENT_IDENTIFIER,
- client->client_id_len,
- &client->client_id);
+ client->client_id.size,
+ client->client_id.raw);
if (r < 0)
return r;
if (r < 0)
return r;
- if (client->client_id_len > 0) {
- r = dhcp_lease_set_client_id(lease,
- (uint8_t *) &client->client_id,
- client->client_id_len);
+ if (sd_dhcp_client_id_is_set(&client->client_id)) {
+ r = dhcp_lease_set_client_id(lease, &client->client_id);
if (r < 0)
return r;
}
return r;
/* If no client identifier exists, construct an RFC 4361-compliant one */
- if (client->client_id_len == 0) {
+ if (!sd_dhcp_client_id_is_set(&client->client_id)) {
r = sd_dhcp_client_set_iaid_duid_en(client, /* iaid_set = */ false, /* iaid = */ 0);
if (r < 0)
return r;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <linux/if_infiniband.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+
+#include "dhcp-duid-internal.h"
+#include "hexdecoct.h"
+#include "netif-util.h"
+#include "network-common.h"
+#include "siphash24.h"
+#include "string-table.h"
+#include "unaligned.h"
+
+#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
+#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
+#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
+
+static const char * const duid_type_table[_DUID_TYPE_MAX] = {
+ [DUID_TYPE_LLT] = "DUID-LLT",
+ [DUID_TYPE_EN] = "DUID-EN/Vendor",
+ [DUID_TYPE_LL] = "DUID-LL",
+ [DUID_TYPE_UUID] = "UUID",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
+
+int sd_dhcp_duid_clear(sd_dhcp_duid *duid) {
+ assert_return(duid, -EINVAL);
+
+ *duid = (sd_dhcp_duid) {};
+ return 0;
+}
+
+int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid) {
+ if (!duid)
+ return false;
+
+ return duid_size_is_valid(duid->size);
+}
+
+int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ assert_return(ret_type, -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ *ret_type = be16toh(duid->duid.type);
+ *ret_data = duid->duid.data;
+ *ret_size = duid->size - offsetof(struct duid, data);
+ return 0;
+}
+
+int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ /* Unlike sd_dhcp_duid_get(), this returns whole DUID including its type. */
+
+ *ret_data = duid->raw;
+ *ret_size = duid->size;
+ return 0;
+}
+
+int sd_dhcp_duid_set(
+ sd_dhcp_duid *duid,
+ uint16_t duid_type,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(duid, -EINVAL);
+ assert_return(data, -EINVAL);
+ assert_return(duid_data_size_is_valid(data_size), -EINVAL);
+
+ unaligned_write_be16(&duid->duid.type, duid_type);
+ memcpy(duid->duid.data, data, data_size);
+
+ duid->size = offsetof(struct duid, data) + data_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_raw(
+ sd_dhcp_duid *duid,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(duid, -EINVAL);
+ assert_return(data, -EINVAL);
+ assert_return(duid_size_is_valid(data_size), -EINVAL);
+
+ /* Unlike sd_dhcp_duid_set(), this takes whole DUID including its type. */
+
+ memcpy(duid->raw, data, data_size);
+
+ duid->size = data_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_llt(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type,
+ uint64_t usec) {
+
+ uint16_t time_from_2000y;
+
+ assert_return(duid, -EINVAL);
+ assert_return(hw_addr, -EINVAL);
+
+ if (arp_type == ARPHRD_ETHER)
+ assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+ else if (arp_type == ARPHRD_INFINIBAND)
+ assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+ else
+ return -EOPNOTSUPP;
+
+ time_from_2000y = (uint16_t) ((usec_sub_unsigned(usec, USEC_2000) / USEC_PER_SEC) & 0xffffffff);
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LLT);
+ unaligned_write_be16(&duid->duid.llt.htype, arp_type);
+ unaligned_write_be32(&duid->duid.llt.time, time_from_2000y);
+ memcpy(duid->duid.llt.haddr, hw_addr, hw_addr_size);
+
+ duid->size = offsetof(struct duid, llt.haddr) + hw_addr_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_ll(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type) {
+
+ assert_return(duid, -EINVAL);
+ assert_return(hw_addr, -EINVAL);
+
+ if (arp_type == ARPHRD_ETHER)
+ assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+ else if (arp_type == ARPHRD_INFINIBAND)
+ assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+ else
+ return -EOPNOTSUPP;
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LL);
+ unaligned_write_be16(&duid->duid.ll.htype, arp_type);
+ memcpy(duid->duid.ll.haddr, hw_addr, hw_addr_size);
+
+ duid->size = offsetof(struct duid, ll.haddr) + hw_addr_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_en(sd_dhcp_duid *duid) {
+ sd_id128_t machine_id;
+ bool test_mode;
+ uint64_t hash;
+ int r;
+
+ assert_return(duid, -EINVAL);
+
+ test_mode = network_test_mode_enabled();
+
+ if (!test_mode) {
+ r = sd_id128_get_machine(&machine_id);
+ if (r < 0)
+ return r;
+ } else
+ /* For tests, especially for fuzzers, reproducibility is important.
+ * Hence, use a static and constant machine ID.
+ * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
+ machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_EN);
+ unaligned_write_be32(&duid->duid.en.pen, SYSTEMD_PEN);
+
+ /* a bit of snake-oil perhaps, but no need to expose the machine-id
+ * directly; duid->en.id might not be aligned, so we need to copy */
+ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
+ memcpy(duid->duid.en.id, &hash, sizeof(hash));
+
+ duid->size = offsetof(struct duid, en.id) + sizeof(hash);
+
+ if (test_mode)
+ assert_se(memcmp(&duid->duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, duid->size) == 0);
+
+ return 0;
+}
+
+int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid) {
+ sd_id128_t machine_id;
+ int r;
+
+ assert_return(duid, -EINVAL);
+
+ r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
+ if (r < 0)
+ return r;
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_UUID);
+ memcpy(&duid->duid.uuid.uuid, &machine_id, sizeof(machine_id));
+
+ duid->size = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
+ return 0;
+}
+
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret) {
+ _cleanup_free_ char *p = NULL, *x = NULL;
+ const char *t;
+
+ assert(data);
+ assert(duid_data_size_is_valid(data_size));
+ assert(ret);
+
+ x = hexmem(data, data_size);
+ if (!x)
+ return -ENOMEM;
+
+ t = duid_type_to_string(type);
+ if (!t)
+ return asprintf(ret, "%04x:%s", htobe16(type), x);
+
+ p = strjoin(t, ":", x);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
+
+int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret) {
+ uint16_t type;
+ const void *data;
+ size_t data_size;
+ int r;
+
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = sd_dhcp_duid_get(duid, &type, &data, &data_size);
+ if (r < 0)
+ return r;
+
+ return dhcp_duid_to_string_internal(type, data, data_size, ret);
+}
+
+int dhcp_identifier_set_iaid(
+ sd_device *dev,
+ const struct hw_addr_data *hw_addr,
+ bool legacy_unstable_byteorder,
+ void *ret) {
+
+ const char *name = NULL;
+ uint32_t id32;
+ uint64_t id;
+
+ assert(hw_addr);
+ assert(ret);
+
+ if (dev)
+ name = net_get_persistent_name(dev);
+ if (name)
+ id = siphash24(name, strlen(name), HASH_KEY.bytes);
+ else
+ /* fall back to MAC address if no predictable name available */
+ id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
+
+ id32 = (id & 0xffffffff) ^ (id >> 32);
+
+ if (legacy_unstable_byteorder)
+ /* for historical reasons (a bug), the bits were swapped and thus
+ * the result was endianness dependent. Preserve that behavior. */
+ id32 = bswap_32(id32);
+ else
+ /* the fixed behavior returns a stable byte order. Since LE is expected
+ * to be more common, swap the bytes on LE to give the same as legacy
+ * behavior. */
+ id32 = be32toh(id32);
+
+ unaligned_write_ne32(ret, id32);
+ return 0;
+}
free(lease->static_routes);
free(lease->classless_routes);
- free(lease->client_id);
free(lease->vendor_specific);
strv_free(lease->search_domains);
free(lease->sixrd_br_addresses);
_cleanup_fclose_ FILE *f = NULL;
struct in_addr address;
const struct in_addr *addresses;
- const void *client_id, *data;
- size_t client_id_len, data_len;
+ const void *data;
+ size_t data_len;
const char *string;
uint16_t mtu;
_cleanup_free_ sd_dhcp_route **routes = NULL;
if (r >= 0)
fprintf(f, "TIMEZONE=%s\n", string);
- r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
- if (r >= 0) {
+ if (sd_dhcp_client_id_is_set(&lease->client_id)) {
_cleanup_free_ char *client_id_hex = NULL;
- client_id_hex = hexmem(client_id, client_id_len);
+ client_id_hex = hexmem(lease->client_id.raw, lease->client_id.size);
if (!client_id_hex)
return -ENOMEM;
fprintf(f, "CLIENTID=%s\n", client_id_hex);
}
if (client_id_hex) {
- r = unhexmem(client_id_hex, SIZE_MAX, &lease->client_id, &lease->client_id_len);
+ _cleanup_free_ void *data = NULL;
+ size_t data_size;
+
+ r = unhexmem(client_id_hex, SIZE_MAX, &data, &data_size);
if (r < 0)
log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
+
+ r = sd_dhcp_client_id_set_raw(&lease->client_id, data, data_size);
+ if (r < 0)
+ log_debug_errno(r, "Failed to assign client ID, ignoring: %m");
}
if (vendor_specific_hex) {
return 0;
}
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id **ret) {
assert_return(lease, -EINVAL);
- assert_return(client_id, -EINVAL);
- assert_return(client_id_len, -EINVAL);
+ assert_return(ret, -EINVAL);
- if (!lease->client_id)
+ if (!sd_dhcp_client_id_is_set(&lease->client_id))
return -ENODATA;
- *client_id = lease->client_id;
- *client_id_len = lease->client_id_len;
+ *ret = &lease->client_id;
return 0;
}
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) {
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id *client_id) {
assert_return(lease, -EINVAL);
- assert_return(client_id || client_id_len <= 0, -EINVAL);
-
- if (client_id_len <= 0)
- lease->client_id = mfree(lease->client_id);
- else {
- void *p;
- p = memdup(client_id, client_id_len);
- if (!p)
- return -ENOMEM;
+ if (!sd_dhcp_client_id_is_set(client_id))
+ return sd_dhcp_client_id_clear(&lease->client_id);
- free_and_replace(lease->client_id, p);
- lease->client_id_len = client_id_len;
- }
+ lease->client_id = *client_id;
return 0;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "dhcp-server-lease-internal.h"
+
+static sd_dhcp_server_lease* dhcp_server_lease_free(sd_dhcp_server_lease *lease) {
+ if (!lease)
+ return NULL;
+
+ if (lease->server) {
+ hashmap_remove_value(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
+ hashmap_remove_value(lease->server->bound_leases_by_client_id, &lease->client_id, lease);
+ hashmap_remove_value(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address), lease);
+ hashmap_remove_value(lease->server->static_leases_by_client_id, &lease->client_id, lease);
+ }
+
+ free(lease->hostname);
+ return mfree(lease);
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server_lease, sd_dhcp_server_lease, dhcp_server_lease_free);
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ dhcp_server_lease_hash_ops,
+ sd_dhcp_client_id,
+ client_id_hash_func,
+ client_id_compare_func,
+ sd_dhcp_server_lease,
+ sd_dhcp_server_lease_unref);
+
+int dhcp_server_put_lease(sd_dhcp_server *server, sd_dhcp_server_lease *lease, bool is_static) {
+ int r;
+
+ assert(server);
+ assert(lease);
+
+ lease->server = server; /* This must be set before hashmap_put(). */
+
+ r = hashmap_ensure_put(is_static ? &server->static_leases_by_client_id : &server->bound_leases_by_client_id,
+ &dhcp_server_lease_hash_ops, &lease->client_id, lease);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_put(is_static ? &server->static_leases_by_address : &server->bound_leases_by_address,
+ NULL, UINT32_TO_PTR(lease->address), lease);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int dhcp_server_set_lease(sd_dhcp_server *server, be32_t address, DHCPRequest *req, usec_t expiration) {
+ _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
+ int r;
+
+ assert(server);
+ assert(address != 0);
+ assert(req);
+ assert(expiration != 0);
+
+ /* If a lease for the host already exists, update it. */
+ lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
+ if (lease) {
+ if (lease->address != address) {
+ hashmap_remove_value(server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
+ lease->address = address;
+
+ r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+ if (r < 0)
+ return r;
+ }
+
+ lease->expiration = expiration;
+
+ TAKE_PTR(lease);
+ return 0;
+ }
+
+ /* Otherwise, add a new lease. */
+
+ lease = new(sd_dhcp_server_lease, 1);
+ if (!lease)
+ return -ENOMEM;
+
+ *lease = (sd_dhcp_server_lease) {
+ .n_ref = 1,
+ .address = address,
+ .client_id = req->client_id,
+ .htype = req->message->htype,
+ .hlen = req->message->hlen,
+ .gateway = req->message->giaddr,
+ .expiration = expiration,
+ };
+
+ memcpy(lease->chaddr, req->message->chaddr, req->message->hlen);
+
+ if (req->hostname) {
+ lease->hostname = strdup(req->hostname);
+ if (!lease->hostname)
+ return -ENOMEM;
+ }
+
+ r = dhcp_server_put_lease(server, lease, /* is_static = */ false);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(lease);
+ return 0;
+}
+
+int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) {
+ sd_dhcp_server_lease *lease;
+ usec_t time_now;
+ int r;
+
+ assert(server);
+
+ r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now);
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(lease, server->bound_leases_by_client_id)
+ if (lease->expiration < time_now) {
+ log_dhcp_server(server, "CLEAN (0x%x)", be32toh(lease->address));
+ sd_dhcp_server_lease_unref(lease);
+ }
+
+ return 0;
+}
+
+sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req) {
+ sd_dhcp_server_lease *static_lease;
+ sd_dhcp_client_id client_id;
+
+ assert(server);
+ assert(req);
+
+ static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
+ if (static_lease)
+ return static_lease;
+
+ /* when no lease is found based on the client id fall back to chaddr */
+ if (!client_id_data_size_is_valid(req->message->hlen))
+ return NULL;
+
+ if (sd_dhcp_client_id_set(&client_id, /* type = */ 1, req->message->chaddr, req->message->hlen) < 0)
+ return NULL;
+
+ return hashmap_get(server->static_leases_by_client_id, &client_id);
+}
+
+int sd_dhcp_server_set_static_lease(
+ sd_dhcp_server *server,
+ const struct in_addr *address,
+ uint8_t *client_id_raw,
+ size_t client_id_size) {
+
+ _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
+ sd_dhcp_client_id client_id;
+ int r;
+
+ assert_return(server, -EINVAL);
+ assert_return(client_id_raw, -EINVAL);
+ assert_return(client_id_size_is_valid(client_id_size), -EINVAL);
+ assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
+
+ r = sd_dhcp_client_id_set_raw(&client_id, client_id_raw, client_id_size);
+ if (r < 0)
+ return r;
+
+ /* Static lease with an empty or omitted address is a valid entry,
+ * the server removes any static lease with the specified mac address. */
+ if (!address || address->s_addr == 0) {
+ sd_dhcp_server_lease_unref(hashmap_get(server->static_leases_by_client_id, &client_id));
+ return 0;
+ }
+
+ lease = new(sd_dhcp_server_lease, 1);
+ if (!lease)
+ return -ENOMEM;
+
+ *lease = (sd_dhcp_server_lease) {
+ .n_ref = 1,
+ .address = address->s_addr,
+ .client_id = client_id,
+ };
+
+ r = dhcp_server_put_lease(server, lease, /* is_static = */ true);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(lease);
+ return 0;
+}
#include "dhcp-option.h"
#include "dhcp-packet.h"
#include "dhcp-server-internal.h"
+#include "dhcp-server-lease-internal.h"
#include "dns-domain.h"
#include "fd-util.h"
#include "in-addr-util.h"
#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
-DHCPLease *dhcp_lease_free(DHCPLease *lease) {
- if (!lease)
- return NULL;
-
- if (lease->server) {
- hashmap_remove_value(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
- hashmap_remove_value(lease->server->bound_leases_by_client_id, &lease->client_id, lease);
- hashmap_remove_value(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address), lease);
- hashmap_remove_value(lease->server->static_leases_by_client_id, &lease->client_id, lease);
- }
-
- free(lease->client_id.data);
- free(lease->hostname);
- return mfree(lease);
-}
-
/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
* the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
* moreover, the server's own address may be in the pool, and is in that case reserved in order not to
return in4_addr_is_set(&server->relay_target);
}
-void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
- assert(id);
- assert(id->length > 0);
- assert(id->data);
-
- siphash24_compress_typesafe(id->length, state);
- siphash24_compress(id->data, id->length, state);
-}
-
-int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
- int r;
-
- assert(a->length > 0);
- assert(a->data);
- assert(b->length > 0);
- assert(b->data);
-
- r = CMP(a->length, b->length);
- if (r != 0)
- return r;
-
- return memcmp(a->data, b->data, a->length);
-}
-
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
- dhcp_lease_hash_ops,
- DHCPClientId,
- client_id_hash_func,
- client_id_compare_func,
- DHCPLease,
- dhcp_lease_free);
-
static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
assert(server);
break;
case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
- if (len >= 2) {
- uint8_t *data;
-
- data = memdup(option, len);
- if (!data)
- return -ENOMEM;
-
- free_and_replace(req->client_id.data, data);
- req->client_id.length = len;
- }
+ if (client_id_size_is_valid(len))
+ (void) sd_dhcp_client_id_set_raw(&req->client_id, option, len);
break;
case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
if (!req)
return NULL;
- free(req->client_id.data);
free(req->hostname);
return mfree(req);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
+ int r;
+
assert(req);
assert(message);
return -EBADMSG;
/* set client id based on MAC address if client did not send an explicit one */
- if (!req->client_id.data) {
- uint8_t *data;
-
- if (message->hlen == 0)
+ if (!sd_dhcp_client_id_is_set(&req->client_id)) {
+ if (!client_id_data_size_is_valid(message->hlen))
return -EBADMSG;
- data = new0(uint8_t, message->hlen + 1);
- if (!data)
- return -ENOMEM;
-
- data[0] = 0x01;
- memcpy(data + 1, message->chaddr, message->hlen);
-
- req->client_id.length = message->hlen + 1;
- req->client_id.data = data;
+ r = sd_dhcp_client_id_set(&req->client_id, /* type = */ 1, message->chaddr, message->hlen);
+ if (r < 0)
+ return r;
}
if (message->hlen == 0 || memeqzero(message->chaddr, message->hlen)) {
+ uint8_t type;
+ const void *data;
+ size_t size;
+
/* See RFC2131 section 4.1.1.
* hlen and chaddr may not be set for non-ethernet interface.
* Let's try to retrieve it from the client ID. */
- if (!req->client_id.data)
+ if (!sd_dhcp_client_id_is_set(&req->client_id))
return -EBADMSG;
- if (req->client_id.length <= 1 || req->client_id.length > sizeof(message->chaddr) + 1)
+ r = sd_dhcp_client_id_get(&req->client_id, &type, &data, &size);
+ if (r < 0)
+ return r;
+
+ if (type != 1)
return -EBADMSG;
- if (req->client_id.data[0] != 0x01)
+ if (size > sizeof(message->chaddr))
return -EBADMSG;
- message->hlen = req->client_id.length - 1;
- memcpy(message->chaddr, req->client_id.data + 1, message->hlen);
+ memcpy(message->chaddr, data, size);
+ message->hlen = size;
}
if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
return -EBADMSG;
}
-static int prepare_new_lease(DHCPLease **ret_lease, be32_t address, DHCPRequest *req, usec_t expiration) {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
-
- assert(ret_lease);
- assert(address != 0);
- assert(req);
- assert(expiration != 0);
-
- lease = new(DHCPLease, 1);
- if (!lease)
- return -ENOMEM;
-
- *lease = (DHCPLease) {
- .address = address,
- .client_id.length = req->client_id.length,
- .htype = req->message->htype,
- .hlen = req->message->hlen,
- .gateway = req->message->giaddr,
- .expiration = expiration,
- };
- lease->client_id.data = memdup(req->client_id.data, req->client_id.length);
- if (!lease->client_id.data)
- return -ENOMEM;
-
- memcpy(lease->chaddr, req->message->chaddr, req->message->hlen);
-
- if (req->hostname) {
- lease->hostname = strdup(req->hostname);
- if (!lease->hostname)
- return -ENOMEM;
- }
-
- *ret_lease = TAKE_PTR(lease);
-
- return 0;
-}
-
-static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLease *existing_lease, be32_t address) {
+static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
usec_t expiration;
int r;
if (r < 0)
return r;
- if (existing_lease) {
- assert(existing_lease->server);
- assert(existing_lease->address == address);
- existing_lease->expiration = expiration;
-
- } else {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
-
- r = prepare_new_lease(&lease, address, req, expiration);
- if (r < 0)
- return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
-
- lease->server = server; /* This must be set just before hashmap_put(). */
-
- r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
- if (r < 0)
- return log_dhcp_server_errno(server, r, "Could not save lease: %m");
-
- r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
- if (r < 0)
- return log_dhcp_server_errno(server, r, "Could not save lease: %m");
-
- TAKE_PTR(lease);
- }
+ r = dhcp_server_set_lease(server, address, req, expiration);
+ if (r < 0)
+ return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
r = server_send_offer_or_ack(server, req, address, DHCP_ACK);
if (r < 0)
return DHCP_ACK;
}
-static int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) {
- DHCPLease *lease;
- usec_t time_now;
- int r;
-
- assert(server);
-
- r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now);
- if (r < 0)
- return r;
-
- HASHMAP_FOREACH(lease, server->bound_leases_by_client_id)
- if (lease->expiration < time_now) {
- log_dhcp_server(server, "CLEAN (0x%x)", be32toh(lease->address));
- dhcp_lease_free(lease);
- }
-
- return 0;
-}
-
static bool address_available(sd_dhcp_server *server, be32_t address) {
assert(server);
return true;
}
-static int server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req, DHCPLease **ret) {
- DHCPLease *static_lease;
- _cleanup_free_ uint8_t *data = NULL;
-
- assert(server);
- assert(req);
- assert(ret);
-
- static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
- if (static_lease) {
- *ret = static_lease;
- return 0;
- }
-
- /* when no lease is found based on the client id fall back to chaddr */
- data = new(uint8_t, req->message->hlen + 1);
- if (!data)
- return -ENOMEM;
-
- /* set client id type to 1: Ethernet Link-Layer (RFC 2132) */
- data[0] = 0x01;
- memcpy(data + 1, req->message->chaddr, req->message->hlen);
-
- static_lease = hashmap_get(server->static_leases_by_client_id,
- &(DHCPClientId) {
- .length = req->message->hlen + 1,
- .data = data,
- });
-
- *ret = static_lease;
-
- return 0;
-}
-
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length, const triple_timestamp *timestamp) {
_cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
_cleanup_free_ char *error_message = NULL;
- DHCPLease *existing_lease, *static_lease;
+ sd_dhcp_server_lease *existing_lease, *static_lease;
int type, r;
assert(server);
return r;
existing_lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
- r = server_get_static_lease(server, req, &static_lease);
- if (r < 0)
- return r;
+ static_lease = dhcp_server_get_static_lease(server, req);
switch (type) {
return 0;
if (server->rapid_commit && req->rapid_commit)
- return server_ack_request(server, req, existing_lease, address);
+ return server_ack_request(server, req, address);
r = server_send_offer_or_ack(server, req, address, DHCP_OFFER);
if (r < 0)
/* The client requested an address which is different from the static lease. Refuse. */
return server_send_nak_or_ignore(server, init_reboot, req);
- return server_ack_request(server, req, existing_lease, address);
+ return server_ack_request(server, req, address);
}
if (address_is_in_pool(server, address)) {
/* We previously assigned an address, but the client requested another one. Refuse. */
return server_send_nak_or_ignore(server, init_reboot, req);
- return server_ack_request(server, req, existing_lease, address);
+ return server_ack_request(server, req, address);
}
return server_send_nak_or_ignore(server, init_reboot, req);
if (existing_lease->address != req->message->ciaddr)
return 0;
- dhcp_lease_free(existing_lease);
+ sd_dhcp_server_lease_unref(existing_lease);
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
}
int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
- DHCPLease *lease;
+ sd_dhcp_server_lease *lease;
int r = 0;
assert_return(server, -EINVAL);
free_and_replace(server->agent_remote_id, remote_id_dup);
return 0;
}
-
-int sd_dhcp_server_set_static_lease(
- sd_dhcp_server *server,
- const struct in_addr *address,
- uint8_t *client_id,
- size_t client_id_size) {
-
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
- int r;
-
- assert_return(server, -EINVAL);
- assert_return(client_id, -EINVAL);
- assert_return(client_id_size > 0, -EINVAL);
- assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
-
- /* Static lease with an empty or omitted address is a valid entry,
- * the server removes any static lease with the specified mac address. */
- if (!address || address->s_addr == 0) {
- DHCPClientId c;
-
- c = (DHCPClientId) {
- .length = client_id_size,
- .data = client_id,
- };
-
- dhcp_lease_free(hashmap_get(server->static_leases_by_client_id, &c));
- return 0;
- }
-
- lease = new(DHCPLease, 1);
- if (!lease)
- return -ENOMEM;
-
- *lease = (DHCPLease) {
- .address = address->s_addr,
- .client_id.length = client_id_size,
- };
- lease->client_id.data = memdup(client_id, client_id_size);
- if (!lease->client_id.data)
- return -ENOMEM;
-
- lease->server = server; /* This must be set just before hashmap_put(). */
-
- r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
- if (r < 0)
- return r;
- r = hashmap_ensure_put(&server->static_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
- if (r < 0)
- return r;
-
- TAKE_PTR(lease);
- return 0;
-}
#include "alloc-util.h"
#include "device-util.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dns-domain.h"
#include "event-util.h"
#include "fd-util.h"
-#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "iovec-util.h"
static int client_ensure_duid(sd_dhcp6_client *client) {
assert(client);
- if (client->duid_len != 0)
+ if (sd_dhcp_duid_is_set(&client->duid))
return 0;
- return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+ return sd_dhcp6_client_set_duid_en(client);
}
/**
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set_llt(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set_en(&client->duid);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set_uuid(&client->duid);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
assert_return(duid || duid_len == 0, -EINVAL);
- r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set(&client->duid, duid_type, duid, duid_len);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
return 0;
}
-int sd_dhcp6_client_duid_as_string(
- sd_dhcp6_client *client,
- char **duid) {
- _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
- const char *v;
- int r;
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid) {
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ client->duid = *duid;
+ return 0;
+}
+
+int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret) {
assert_return(client, -EINVAL);
- assert_return(client->duid_len > offsetof(struct duid, raw.data), -ENODATA);
- assert_return(duid, -EINVAL);
+ assert_return(ret, -EINVAL);
- v = duid_type_to_string(be16toh(client->duid.type));
- if (v) {
- s = strdup(v);
- if (!s)
- return -ENOMEM;
- } else {
- r = asprintf(&s, "%0x", client->duid.type);
- if (r < 0)
- return -ENOMEM;
- }
+ if (!sd_dhcp_duid_is_set(&client->duid))
+ return -ENODATA;
- t = hexmem(client->duid.raw.data, client->duid_len - offsetof(struct duid, raw.data));
- if (!t)
- return -ENOMEM;
+ *ret = &client->duid;
+ return 0;
+}
- p = strjoin(s, ":", t);
- if (!p)
- return -ENOMEM;
+int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret) {
+ assert_return(client, -EINVAL);
+ assert_return(ret, -EINVAL);
- *duid = TAKE_PTR(p);
+ if (!sd_dhcp_duid_is_set(&client->duid))
+ return -ENODATA;
- return 0;
+ return sd_dhcp_duid_to_string(&client->duid, ret);
}
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
if (r < 0)
return r;
- assert(client->duid_len > 0);
+ assert(sd_dhcp_duid_is_set(&client->duid));
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
- client->duid_len, &client->duid);
+ client->duid.size, &client->duid.duid);
if (r < 0)
return r;
#include "dhcp6-lease-internal.h"
#include "network-common.h"
#include "strv.h"
+#include "unaligned.h"
#define IRT_DEFAULT (1 * USEC_PER_DAY)
#define IRT_MINIMUM (600 * USEC_PER_SEC)
"%s message does not contain client ID. Ignoring.",
dhcp6_message_type_to_string(message->type));
- if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
+ if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"The client ID in %s message does not match. Ignoring.",
dhcp6_message_type_to_string(message->type));
#include "sd-event.h"
#include "alloc-util.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "dhcp-network.h"
#include "dhcp-option.h"
#include "dhcp-packet.h"
switch (code) {
case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
{
+ sd_dhcp_duid duid;
uint32_t iaid;
- struct duid duid;
- size_t duid_len;
- assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0);
+ assert_se(sd_dhcp_duid_set_en(&duid) >= 0);
assert_se(dhcp_identifier_set_iaid(NULL, &hw_addr, /* legacy = */ true, &iaid) >= 0);
- assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
+ assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid.size);
assert_se(len == 19);
assert_se(((uint8_t*) option)[0] == 0xff);
assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0);
- assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0);
+ assert_se(memcmp((uint8_t*) option + 5, &duid.duid, duid.size) == 0);
break;
}
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
}
-static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) {
+static uint64_t client_id_hash_helper(sd_dhcp_client_id *id, uint8_t key[HASH_KEY_SIZE]) {
struct siphash state;
siphash24_init(&state, key);
}
static void test_client_id_hash(void) {
- DHCPClientId a = {
- .length = 4,
+ sd_dhcp_client_id a = {
+ .size = 4,
}, b = {
- .length = 4,
+ .size = 4,
};
uint8_t hash_key[HASH_KEY_SIZE] = {
'0', '1', '2', '3', '4', '5', '6', '7',
log_debug("/* %s */", __func__);
- a.data = (uint8_t*)strdup("abcd");
- b.data = (uint8_t*)strdup("abcd");
+ memcpy(a.raw, "abcd", 4);
+ memcpy(b.raw, "abcd", 4);
assert_se(client_id_compare_func(&a, &b) == 0);
assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
- a.length = 3;
+ a.size = 3;
assert_se(client_id_compare_func(&a, &b) != 0);
- a.length = 4;
+ a.size = 4;
assert_se(client_id_compare_func(&a, &b) == 0);
assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
- b.length = 3;
+ b.size = 3;
assert_se(client_id_compare_func(&a, &b) != 0);
- b.length = 4;
+ b.size = 4;
assert_se(client_id_compare_func(&a, &b) == 0);
assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
- free(b.data);
- b.data = (uint8_t*)strdup("abce");
+ memcpy(b.raw, "abce", 4);
assert_se(client_id_compare_func(&a, &b) != 0);
-
- free(a.data);
- free(b.data);
}
static void test_static_lease(void) {
#include "sd-dhcp6-client.h"
#include "sd-event.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
#include "strv.h"
#include "tests.h"
#include "time-util.h"
+#include "unaligned.h"
#define DHCP6_CLIENT_EVENT_TEST_ADVERTISED 77
#define IA_ID_BYTES \
HASHMAP_FOREACH_KEY(device, syspath, enumerator->devices_by_syspath) {
_cleanup_free_ char *p = NULL;
- const char *subsys;
- if (sd_device_get_subsystem(device, &subsys) < 0)
- continue;
-
- if (!streq(subsys, *prioritized_subsystem))
+ if (!device_in_subsystem(device, *prioritized_subsystem))
continue;
devices[n++] = sd_device_ref(device);
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_monitor, sd_device_monitor, device_monitor_free);
static int check_subsystem_filter(sd_device_monitor *m, sd_device *device) {
- const char *s, *subsystem, *d, *devtype = NULL;
- int r;
+ const char *s, *d;
assert(m);
assert(device);
if (hashmap_isempty(m->subsystem_filter))
return true;
- r = sd_device_get_subsystem(device, &subsystem);
- if (r < 0)
- return r;
-
- r = sd_device_get_devtype(device, &devtype);
- if (r < 0 && r != -ENOENT)
- return r;
-
HASHMAP_FOREACH_KEY(d, s, m->subsystem_filter) {
- if (!streq(s, subsystem))
+ if (!device_in_subsystem(device, s))
continue;
- if (!d || streq_ptr(d, devtype))
- return true;
+ if (d && !device_is_devtype(device, d))
+ continue;
+
+ return true;
}
return false;
return TAKE_PTR(strv);
}
+
+bool device_in_subsystem(sd_device *device, const char *subsystem) {
+ const char *s = NULL;
+
+ assert(device);
+
+ (void) sd_device_get_subsystem(device, &s);
+ return streq_ptr(s, subsystem);
+}
+
+bool device_is_devtype(sd_device *device, const char *devtype) {
+ const char *s = NULL;
+
+ assert(device);
+
+ (void) sd_device_get_devtype(device, &s);
+ return streq_ptr(s, devtype);
+}
int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
char** device_make_log_fields(sd_device *device);
+
+bool device_in_subsystem(sd_device *device, const char *subsystem);
+bool device_is_devtype(sd_device *device, const char *devtype);
int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
_cleanup_free_ char *syspath = NULL;
- const char *t, *subsystem = NULL;
+ const char *t;
dev_t n;
int r;
if (n != devnum)
return -ENXIO;
- r = sd_device_get_subsystem(dev, &subsystem);
- if (r < 0 && r != -ENOENT)
- return r;
- if (streq_ptr(subsystem, "block") != !!S_ISBLK(mode))
+ if (device_in_subsystem(dev, "block") != !!S_ISBLK(mode))
return -ENXIO;
*ret = TAKE_PTR(dev);
return !!device->devtype;
}
-_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) {
- sd_device *parent = NULL;
+_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *device, const char *subsystem, const char *devtype, sd_device **ret) {
int r;
- assert_return(child, -EINVAL);
+ assert_return(device, -EINVAL);
assert_return(subsystem, -EINVAL);
- r = sd_device_get_parent(child, &parent);
- while (r >= 0) {
- const char *parent_subsystem = NULL;
+ for (;;) {
+ r = sd_device_get_parent(device, &device);
+ if (r < 0)
+ return r;
- (void) sd_device_get_subsystem(parent, &parent_subsystem);
- if (streq_ptr(parent_subsystem, subsystem)) {
- const char *parent_devtype = NULL;
+ if (!device_in_subsystem(device, subsystem))
+ continue;
- if (!devtype)
- break;
+ if (devtype && !device_is_devtype(device, devtype))
+ continue;
- (void) sd_device_get_devtype(parent, &parent_devtype);
- if (streq_ptr(parent_devtype, devtype))
- break;
- }
- r = sd_device_get_parent(parent, &parent);
+ if (ret)
+ *ret = device;
+ return 0;
}
-
- if (r < 0)
- return r;
-
- if (ret)
- *ret = parent;
- return 0;
}
_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
_public_ int sd_device_open(sd_device *device, int flags) {
_cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
- const char *devname, *subsystem = NULL;
+ const char *devname;
uint64_t q, diskseq = 0;
struct stat st;
dev_t devnum;
if (r < 0)
return r;
- r = sd_device_get_subsystem(device, &subsystem);
- if (r < 0 && r != -ENOENT)
- return r;
-
fd = open(devname, FLAGS_SET(flags, O_PATH) ? flags : O_CLOEXEC|O_NOFOLLOW|O_PATH);
if (fd < 0)
return -errno;
if (st.st_rdev != devnum)
return -ENXIO;
- if (streq_ptr(subsystem, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode))
+ if (device_in_subsystem(device, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode))
return -ENXIO;
/* If flags has O_PATH, then we cannot check diskseq. Let's return earlier. */
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "device-util.h"
+#include "mountpoint-util.h"
#include "tests.h"
TEST(log_device_full) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
int r;
+ (void) sd_device_new_from_subsystem_sysname(&dev, "net", "lo");
+
for (int level = LOG_ERR; level <= LOG_DEBUG; level++) {
- log_device_full(NULL, level, "test level=%d: %m", level);
+ log_device_full(dev, level, "test level=%d: %m", level);
- r = log_device_full_errno(NULL, level, EUCLEAN, "test level=%d errno=EUCLEAN: %m", level);
+ r = log_device_full_errno(dev, level, EUCLEAN, "test level=%d errno=EUCLEAN: %m", level);
assert_se(r == -EUCLEAN);
- r = log_device_full_errno(NULL, level, 0, "test level=%d errno=0: %m", level);
+ r = log_device_full_errno(dev, level, 0, "test level=%d errno=0: %m", level);
assert_se(r == 0);
- r = log_device_full_errno(NULL, level, SYNTHETIC_ERRNO(ENODATA), "test level=%d errno=S(ENODATA): %m", level);
+ r = log_device_full_errno(dev, level, SYNTHETIC_ERRNO(ENODATA), "test level=%d errno=S(ENODATA): %m", level);
assert_se(r == -ENODATA);
}
}
-DEFINE_TEST_MAIN(LOG_INFO);
+TEST(device_in_subsystem) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ int r;
+
+ r = sd_device_new_from_subsystem_sysname(&dev, "net", "lo");
+ if (r == -ENODEV)
+ return (void) log_tests_skipped("net/lo does not exist");
+ assert_se(r >= 0);
+
+ assert_se(device_in_subsystem(dev, "net"));
+ assert_se(!device_in_subsystem(dev, "disk"));
+ assert_se(!device_in_subsystem(dev, "subsystem"));
+ assert_se(!device_in_subsystem(dev, ""));
+ assert_se(!device_in_subsystem(dev, NULL));
+
+ dev = sd_device_unref(dev);
+
+ assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net") >= 0);
+ assert_se(!device_in_subsystem(dev, "net"));
+ assert_se(!device_in_subsystem(dev, "disk"));
+ assert_se(device_in_subsystem(dev, "subsystem"));
+ assert_se(!device_in_subsystem(dev, ""));
+ assert_se(!device_in_subsystem(dev, NULL));
+
+ dev = sd_device_unref(dev);
+
+ assert_se(sd_device_new_from_syspath(&dev, "/sys/class") >= 0);
+ assert_se(!device_in_subsystem(dev, "net"));
+ assert_se(!device_in_subsystem(dev, "disk"));
+ assert_se(!device_in_subsystem(dev, "subsystem"));
+ assert_se(!device_in_subsystem(dev, ""));
+ assert_se(device_in_subsystem(dev, NULL));
+}
+
+TEST(device_is_devtype) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_add_match_subsystem(e, "disk", true) >= 0);
+
+ FOREACH_DEVICE(e, d) {
+ const char *t;
+
+ assert_se(sd_device_get_devtype(d, &t) >= 0);
+ assert_se(device_is_devtype(d, t));
+ assert_se(!device_is_devtype(d, "hoge"));
+ assert_se(!device_is_devtype(d, ""));
+ assert_se(!device_is_devtype(d, NULL));
+ }
+
+ assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net") >= 0);
+ assert_se(!device_is_devtype(dev, "hoge"));
+ assert_se(!device_is_devtype(dev, ""));
+ assert_se(device_is_devtype(dev, NULL));
+}
+
+static int intro(void) {
+ if (path_is_mount_point("/sys", NULL, 0) <= 0)
+ return log_tests_skipped("/sys is not mounted");
+
+ return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
assert(e);
assert(t);
+ /* If we are already going down, we cannot install the timer.
+ * In such case, the caller needs to call journal_file_post_change() explicitly. */
+ if (IN_SET(sd_event_get_state(e), SD_EVENT_EXITING, SD_EVENT_FINISHED))
+ return 0;
+
r = sd_event_add_time(e, &timer, CLOCK_MONOTONIC, 0, 0, post_change_thunk, f);
if (r < 0)
return r;
f->post_change_timer = TAKE_PTR(timer);
f->post_change_timer_period = t;
- return r;
+ return 1;
}
static int entry_item_cmp(const EntryItem *a, const EntryItem *b) {
};
struct Directory {
+ sd_journal *journal;
char *path;
int wd;
bool is_root;
return 0;
}
+static Directory* directory_free(Directory *d) {
+ if (!d)
+ return NULL;
+
+ if (d->journal) {
+ if (d->wd > 0 &&
+ hashmap_remove_value(d->journal->directories_by_wd, INT_TO_PTR(d->wd), d) &&
+ d->journal->inotify_fd >= 0)
+ (void) inotify_rm_watch(d->journal->inotify_fd, d->wd);
+
+ if (d->path)
+ hashmap_remove_value(d->journal->directories_by_path, d->path, d);
+ }
+
+ if (d->path) {
+ if (d->is_root)
+ log_debug("Root directory %s removed.", d->path);
+ else
+ log_debug("Directory %s removed.", d->path);
+
+ free(d->path);
+ }
+
+ return mfree(d);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Directory*, directory_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ directories_by_path_hash_ops,
+ char,
+ path_hash_func,
+ path_compare,
+ Directory,
+ directory_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ directories_by_wd_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ Directory,
+ directory_free);
+
+static int add_directory_impl(sd_journal *j, const char *path, bool is_root, Directory **ret) {
+ _cleanup_(directory_freep) Directory *m = NULL;
+ Directory *existing;
+ int r;
+
+ assert(j);
+ assert(path);
+ assert(ret);
+
+ existing = hashmap_get(j->directories_by_path, path);
+ if (existing) {
+ if (existing->is_root != is_root) {
+ /* Don't 'downgrade' from root directory */
+ *ret = NULL;
+ return 0;
+ }
+
+ *ret = existing;
+ return 1;
+ }
+
+ m = new(Directory, 1);
+ if (!m)
+ return -ENOMEM;
+
+ *m = (Directory) {
+ .journal = j,
+ .is_root = is_root,
+ .path = strdup(path),
+ .wd = -1,
+ };
+
+ if (!m->path)
+ return -ENOMEM;
+
+ r = hashmap_ensure_put(&j->directories_by_path, &directories_by_path_hash_ops, m->path, m);
+ if (r < 0)
+ return r;
+
+ j->current_invalidate_counter++;
+
+ if (is_root)
+ log_debug("Root directory %s added.", m->path);
+ else
+ log_debug("Directory %s added.", m->path);
+
+ *ret = TAKE_PTR(m);
+ return 1;
+}
+
static int add_directory(sd_journal *j, const char *prefix, const char *dirname);
static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
return;
}
- r = hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m);
- if (r == -EEXIST)
- log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path);
+ r = hashmap_ensure_put(&j->directories_by_wd, &directories_by_wd_hash_ops, INT_TO_PTR(m->wd), m);
if (r < 0) {
- log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path);
- (void) inotify_rm_watch(j->inotify_fd, m->wd);
+ if (r == -EEXIST)
+ log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path);
+ else {
+ log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path);
+ (void) inotify_rm_watch(j->inotify_fd, m->wd);
+ }
m->wd = -1;
}
}
goto fail;
}
- m = hashmap_get(j->directories_by_path, path);
- if (!m) {
- m = new(Directory, 1);
- if (!m) {
- r = -ENOMEM;
- goto fail;
- }
-
- *m = (Directory) {
- .is_root = false,
- .path = path,
- };
-
- if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
- free(m);
- r = -ENOMEM;
- goto fail;
- }
-
- path = NULL; /* avoid freeing in cleanup */
- j->current_invalidate_counter++;
-
- log_debug("Directory %s added.", m->path);
-
- } else if (m->is_root)
- return 0; /* Don't 'downgrade' from root directory */
+ r = add_directory_impl(j, path, /* is_root = */ false, &m);
+ if (r < 0)
+ goto fail;
+ if (r == 0)
+ return 0;
m->last_seen_generation = j->generation;
rewinddir(d);
}
- m = hashmap_get(j->directories_by_path, p);
- if (!m) {
- m = new0(Directory, 1);
- if (!m) {
- r = -ENOMEM;
- goto fail;
- }
-
- m->is_root = true;
-
- m->path = strdup(p);
- if (!m->path) {
- free(m);
- r = -ENOMEM;
- goto fail;
- }
-
- if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
- free(m->path);
- free(m);
- r = -ENOMEM;
- goto fail;
- }
-
- j->current_invalidate_counter++;
-
- log_debug("Root directory %s added.", m->path);
-
- } else if (!m->is_root)
+ r = add_directory_impl(j, p, /* is_root = */ true, &m);
+ if (r < 0)
+ goto fail;
+ if (r == 0)
return 0;
directory_watch(j, m, dirfd(d),
return r;
}
-static void remove_directory(sd_journal *j, Directory *d) {
- assert(j);
-
- if (d->wd > 0) {
- hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
-
- if (j->inotify_fd >= 0)
- (void) inotify_rm_watch(j->inotify_fd, d->wd);
- }
-
- hashmap_remove(j->directories_by_path, d->path);
-
- if (d->is_root)
- log_debug("Root directory %s removed.", d->path);
- else
- log_debug("Directory %s removed.", d->path);
-
- free(d->path);
- free(d);
-}
-
static int add_search_paths(sd_journal *j) {
static const char search_paths[] =
return -errno;
}
- return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
+ return 0;
}
static sd_journal *journal_new(int flags, const char *path, const char *namespace) {
return NULL;
j->files_cache = ordered_hashmap_iterated_cache_new(j->files);
- j->directories_by_path = hashmap_new(&path_hash_ops);
j->mmap = mmap_cache_new();
- if (!j->files_cache || !j->directories_by_path || !j->mmap)
+ if (!j->files_cache || !j->mmap)
return NULL;
return TAKE_PTR(j);
}
_public_ void sd_journal_close(sd_journal *j) {
- Directory *d;
Prioq *p;
if (!j || journal_origin_changed(j))
ordered_hashmap_free_with_destructor(j->files, journal_file_close);
iterated_cache_free(j->files_cache);
- while ((d = hashmap_first(j->directories_by_path)))
- remove_directory(j, d);
-
- while ((d = hashmap_first(j->directories_by_wd)))
- remove_directory(j, d);
-
hashmap_free(j->directories_by_path);
hashmap_free(j->directories_by_wd);
continue;
log_debug("Directory '%s' hasn't been seen in this enumeration, removing.", f->path);
- remove_directory(j, m);
+ directory_free(m);
}
log_debug("Reiteration complete.");
/* Event for a subdirectory */
if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
- remove_directory(j, d);
+ directory_free(d);
} else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && id128_is_valid(e->name)) {
_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
if (m)
- (void) close_nointr(MONITOR_TO_FD(m));
+ (void) close(MONITOR_TO_FD(m));
return NULL;
}
return 0;
}
+int sd_rtnl_message_route_set_tos(sd_netlink_message *m, unsigned char tos) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ rtm->rtm_tos = tos;
+
+ return 0;
+}
+
int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope) {
struct rtmsg *rtm;
#define GET_CONTAINER(m, i) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset))
-#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
-#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
-
int message_new_empty(sd_netlink *nl, sd_netlink_message **ret) {
sd_netlink_message *m;
#include "ordered-set.h"
#include "socket-util.h"
+#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
+#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
+
/* See struct rtvia in rtnetlink.h */
typedef struct RouteVia {
uint16_t family;
sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
if (m)
- (void) close_nointr(MONITOR_TO_FD(m));
+ (void) close(MONITOR_TO_FD(m));
return NULL;
}
if (m->delayed_action)
return log_debug_errno(SYNTHETIC_ERRNO(EALREADY),
- "Action already in progress (%s), ignoring requested %s operation.",
- inhibit_what_to_string(m->delayed_action->inhibit_what),
+ "Action %s already in progress, ignoring requested %s operation.",
+ handle_action_to_string(m->delayed_action->handle),
handle_action_to_string(handle));
inhibit_operation = ASSERT_PTR(handle_action_lookup(handle))->inhibit_what;
return r;
FOREACH_DEVICE(e, d) {
- const char *status, *enabled, *dash, *nn, *subsys;
+ const char *status, *enabled, *dash, *nn;
sd_device *p;
if (sd_device_get_parent(d, &p) < 0)
/* If the parent shares the same subsystem as the
* device we are looking at then it is a connector,
* which is what we are interested in. */
- if (sd_device_get_subsystem(p, &subsys) < 0 || !streq(subsys, "drm"))
+ if (!device_in_subsystem(p, "drm"))
continue;
if (sd_device_get_sysname(d, &nn) < 0)
if (m->action_job && streq(m->action_job, path)) {
assert(m->delayed_action);
- log_info("Operation '%s' finished.", inhibit_what_to_string(m->delayed_action->inhibit_what));
+ log_info("Operation '%s' finished.", handle_action_to_string(m->delayed_action->handle));
/* Tell people that they now may take a lock again */
(void) send_prepare_for(m, m->delayed_action, false);
#include "alloc-util.h"
#include "bus-util.h"
#include "daemon-util.h"
+#include "device-util.h"
#include "fd-util.h"
#include "logind-session-dbus.h"
#include "logind-session-device.h"
}
static int session_device_open(SessionDevice *sd, bool active) {
- int fd, r;
+ _cleanup_close_ int fd = -EBADF;
+ int r;
assert(sd);
assert(sd->type != DEVICE_TYPE_UNKNOWN);
/* Weird legacy DRM semantics might return an error even though we're master. No way to detect
* that so fail at all times and let caller retry in inactive state. */
r = sd_drmsetmaster(fd);
- if (r < 0) {
- (void) close_nointr(fd);
+ if (r < 0)
return r;
- }
} else
/* DRM-Master is granted to the first user who opens a device automatically (ughh,
* racy!). Hence, we just drop DRM-Master in case we were the first. */
break;
}
- return fd;
+ return TAKE_FD(fd);
}
static int session_device_start(SessionDevice *sd) {
}
static DeviceType detect_device_type(sd_device *dev) {
- const char *sysname, *subsystem;
- DeviceType type = DEVICE_TYPE_UNKNOWN;
+ const char *sysname;
- if (sd_device_get_sysname(dev, &sysname) < 0 ||
- sd_device_get_subsystem(dev, &subsystem) < 0)
- return type;
+ if (sd_device_get_sysname(dev, &sysname) < 0)
+ return DEVICE_TYPE_UNKNOWN;
- if (streq(subsystem, "drm")) {
+ if (device_in_subsystem(dev, "drm")) {
if (startswith(sysname, "card"))
- type = DEVICE_TYPE_DRM;
- } else if (streq(subsystem, "input")) {
+ return DEVICE_TYPE_DRM;
+
+ } else if (device_in_subsystem(dev, "input")) {
if (startswith(sysname, "event"))
- type = DEVICE_TYPE_EVDEV;
+ return DEVICE_TYPE_EVDEV;
}
- return type;
+ return DEVICE_TYPE_UNKNOWN;
}
static int session_device_verify(SessionDevice *sd) {
return 0;
}
-int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out) {
+int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **ret) {
SessionDevice *sd;
int r;
assert(s);
- assert(out);
if (!s->seat)
return -EPERM;
- sd = new0(SessionDevice, 1);
+ sd = new(SessionDevice, 1);
if (!sd)
return -ENOMEM;
- sd->session = s;
- sd->dev = dev;
- sd->fd = -EBADF;
- sd->type = DEVICE_TYPE_UNKNOWN;
+ *sd = (SessionDevice) {
+ .session = s,
+ .dev = dev,
+ .fd = -EBADF,
+ .type = DEVICE_TYPE_UNKNOWN,
+ };
r = session_device_verify(sd);
if (r < 0)
LIST_PREPEND(sd_by_device, sd->device->session_devices, sd);
- *out = sd;
+ if (ret)
+ *ret = sd;
+
return 0;
error:
LIST_FIELDS(struct SessionDevice, sd_by_device);
};
-int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out);
+int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **ret);
SessionDevice *session_device_free(SessionDevice *sd);
DEFINE_TRIVIAL_CLEANUP_FUNC(SessionDevice*, session_device_free);
for (const char *p = devices;;) {
_cleanup_free_ char *word = NULL;
- SessionDevice *sd;
dev_t dev;
int k;
k = extract_first_word(&p, &word, NULL, 0);
- if (k == 0)
- break;
- if (k < 0) {
- r = k;
+ if (k <= 0) {
+ RET_GATHER(r, k);
break;
}
k = parse_devnum(word, &dev);
if (k < 0) {
- r = k;
+ RET_GATHER(r, k);
continue;
}
/* The file descriptors for loaded devices will be reattached later. */
- k = session_device_new(s, dev, false, &sd);
- if (k < 0)
- r = k;
+ RET_GATHER(r, session_device_new(s, dev, /* open_device = */ false, /* ret = */ NULL));
}
if (r < 0)
- log_error_errno(r, "Loading session devices for session %s failed: %m", s->id);
-
+ log_error_errno(r, "Failed to load some session devices for session '%s': %m", s->id);
return r;
}
assert(s);
- if (s->manager->stop_idle_session_usec == USEC_INFINITY)
+ if (s->manager->stop_idle_session_usec == USEC_INFINITY || IN_SET(s->class, SESSION_GREETER, SESSION_LOCK_SCREEN))
return 0;
r = sd_event_add_time_relative(
if (sd_device_get_parent(d, &d) < 0)
return 0;
- if (sd_device_get_subsystem(d, &v) < 0 || !streq(v, "block"))
+ if (!device_in_subsystem(d, "block"))
return 0;
}
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "unaligned.h"
static void security_association_clear(SecurityAssociation *sa) {
if (!sa)
if (ioctl(fd, TUNSETIFF, &ifr) < 0)
return log_netdev_error_errno(netdev, errno, "TUNSETIFF failed: %m");
+ if (t->multi_queue) {
+ /* If we don't detach the queue, the kernel will send packets to our queue and they
+ * will be dropped because we never read them, which is especially important in case
+ * of KeepCarrier option which persists open FD. So detach our queue right after
+ * device create/attach to make kernel not send the packets to it. The option is
+ * available for multi-queue devices only.
+ *
+ * See https://github.com/systemd/systemd/pull/30504 for details. */
+ struct ifreq detach_request = { .ifr_flags = IFF_DETACH_QUEUE };
+ if (ioctl(fd, TUNSETQUEUE, &detach_request) < 0)
+ return log_netdev_error_errno(netdev, errno, "TUNSETQUEUE failed: %m");
+ }
+
if (t->user_name) {
const char *user = t->user_name;
uid_t uid;
static void acquire_wlan_link_info(LinkInfo *link) {
_cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
- const char *type = NULL;
int r, k = 0;
assert(link);
- if (link->sd_device)
- (void) sd_device_get_devtype(link->sd_device, &type);
- if (!streq_ptr(type, "wlan"))
+ if (!link->sd_device)
+ return;
+
+ if (!device_is_devtype(link->sd_device, "wlan"))
return;
r = sd_genl_socket_open(&genl);
if (r < 0)
return bus_log_parse_error(r);
- r = sd_dhcp_client_id_to_string(client_id, client_id_sz, &id);
+ r = sd_dhcp_client_id_to_string_from_raw(client_id, client_id_sz, &id);
if (r < 0)
return bus_log_parse_error(r);
}
if (lease) {
- const void *client_id;
- size_t client_id_len;
+ const sd_dhcp_client_id *client_id;
const char *tz;
r = sd_dhcp_lease_get_timezone(lease, &tz);
return table_log_add_error(r);
}
- r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
+ r = sd_dhcp_lease_get_client_id(lease, &client_id);
if (r >= 0) {
_cleanup_free_ char *id = NULL;
- r = sd_dhcp_client_id_to_string(client_id, client_id_len, &id);
+ r = sd_dhcp_client_id_to_string(client_id, &id);
if (r >= 0) {
r = table_add_many(table,
TABLE_FIELD, "DHCP4 Client ID",
return 1;
}
-int address_remove(Address *address) {
+int address_remove(Address *address, Link *link) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- Request *req;
- Link *link;
int r;
assert(address);
assert(IN_SET(address->family, AF_INET, AF_INET6));
- assert(address->link);
- assert(address->link->ifindex > 0);
- assert(address->link->manager);
- assert(address->link->manager->rtnl);
-
- link = address->link;
+ assert(link);
+ assert(link->ifindex > 0);
+ assert(link->manager);
+ assert(link->manager->rtnl);
log_address_debug(address, "Removing", link);
link_ref(link);
address_enter_removing(address);
- if (address_get_request(link, address, &req) >= 0)
- address_enter_removing(req->userdata);
/* The operational state is determined by address state and carrier state. Hence, if we remove
* an address, the operational state may be changed. */
return 0;
}
-int address_remove_and_drop(Address *address) {
- if (!address)
- return 0;
+int address_remove_and_cancel(Address *address, Link *link) {
+ bool waiting = false;
+ Request *req;
+
+ assert(address);
+ assert(link);
+ assert(link->manager);
+
+ /* If the address is remembered by the link, then use the remembered object. */
+ (void) address_get(link, address, &address);
- address_cancel_request(address);
+ /* Cancel the request for the address. If the request is already called but we have not received the
+ * notification about the request, then explicitly remove the address. */
+ if (address_get_request(link, address, &req) >= 0) {
+ waiting = req->waiting_reply;
+ request_detach(link->manager, req);
+ address_cancel_requesting(address);
+ }
- if (address_exists(address))
- return address_remove(address);
+ /* If we know the address will come or already exists, remove it. */
+ if (waiting || (address->link && address_exists(address)))
+ return address_remove(address, link);
- return address_drop(address);
+ return 0;
}
bool link_address_is_dynamic(const Link *link, const Address *address) {
_cleanup_(address_freep) Address *a = NULL;
unsigned char flags, prefixlen;
struct in6_addr address;
- Address *existing;
int ifindex;
/* NETLINK_GET_STRICT_CHK socket option is supported since kernel 4.20. To support
a->prefixlen = prefixlen;
a->flags = flags;
- if (address_get(link, a, &existing) < 0) {
- r = address_add(link, a);
- if (r < 0)
- return r;
-
- existing = TAKE_PTR(a);
- }
-
- r = address_remove(existing);
+ r = address_remove(a, link);
if (r < 0)
return r;
}
if (!address_is_marked(address))
continue;
- RET_GATHER(r, address_remove(address));
+ RET_GATHER(r, address_remove(address, link));
}
return r;
if (!address_exists(address))
continue;
- RET_GATHER(r, address_remove(address));
+ RET_GATHER(r, address_remove(address, link));
}
return r;
return 0;
}
-void address_cancel_request(Address *address) {
- Request req;
-
- assert(address);
- assert(address->link);
-
- if (!address_is_requesting(address))
- return;
-
- req = (Request) {
- .link = address->link,
- .type = REQUEST_TYPE_ADDRESS,
- .userdata = address,
- .hash_func = (hash_func_t) address_hash_func,
- .compare_func = (compare_func_t) address_compare_func,
- };
-
- request_detach(address->link->manager, &req);
- address_cancel_requesting(address);
-}
-
int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
_cleanup_(address_freep) Address *tmp = NULL;
struct ifa_cacheinfo cinfo;
int address_get(Link *link, const Address *in, Address **ret);
int address_get_harder(Link *link, const Address *in, Address **ret);
int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
-int address_remove(Address *address);
-int address_remove_and_drop(Address *address);
+int address_remove(Address *address, Link *link);
+int address_remove_and_cancel(Address *address, Link *link);
int address_dup(const Address *src, Address **ret);
bool address_is_ready(const Address *a);
bool link_check_addresses_ready(Link *link, NetworkConfigSource source);
int manager_get_address(Manager *manager, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret);
bool manager_has_address(Manager *manager, int family, const union in_addr_union *address);
-void address_cancel_request(Address *address);
int link_request_address(
Link *link,
const Address *address,
return 0;
}
-#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
-
int link_update_bridge_vlan(Link *link, sd_netlink_message *m) {
_cleanup_free_ void *data = NULL;
size_t len;
#include "bus-error.h"
#include "bus-locator.h"
-#include "dhcp-identifier.h"
#include "dhcp-option.h"
#include "dhcp6-internal.h"
#include "escape.h"
#include <netinet/in.h>
#include "conf-parser.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "in-addr-util.h"
#include "set.h"
#include "time-util.h"
link_remove_dhcp_pd_subnet_prefix(link, &prefix);
- RET_GATHER(ret, address_remove_and_drop(address));
+ RET_GATHER(ret, address_remove_and_cancel(address, link));
}
}
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-util.h"
-#include "dhcp-server-internal.h"
+#include "dhcp-server-lease-internal.h"
#include "networkd-dhcp-server-bus.h"
#include "networkd-link-bus.h"
#include "networkd-manager.h"
sd_bus_error *error) {
Link *l = ASSERT_PTR(userdata);
sd_dhcp_server *s;
- DHCPLease *lease;
+ sd_dhcp_server_lease *lease;
int r;
assert(reply);
if (r < 0)
return r;
- r = sd_bus_message_append_array(reply, 'y', lease->client_id.data, lease->client_id.length);
+ r = sd_bus_message_append_array(reply, 'y', lease->client_id.raw, lease->client_id.size);
if (r < 0)
return r;
if (only_marked && !address_is_marked(address))
continue;
- RET_GATHER(ret, address_remove_and_drop(address));
+ RET_GATHER(ret, address_remove_and_cancel(address, link));
}
return ret;
if (only_marked && !address_is_marked(address))
continue;
- RET_GATHER(ret, address_remove_and_drop(address));
+ RET_GATHER(ret, address_remove_and_cancel(address, link));
}
return ret;
if (r >= 0)
fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid);
- r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid);
+ r = sd_dhcp6_client_get_duid_as_string(link->dhcp6_client, &duid);
if (r >= 0)
fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid);
else
log_link_debug(link, "Removing address %s, as the ACD client is stopped.", IN4_ADDR_TO_STRING(&address->in_addr.in));
- r = address_remove(address);
+ /* Do not call address_remove_and_cancel() here. Otherwise, the request is cancelled, and the
+ * interface may be in configured state without the address. */
+ r = address_remove(address, link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to remove address %s: %m", IN4_ADDR_TO_STRING(&address->in_addr.in));
static int ipv4ll_address_lost(Link *link) {
_cleanup_(address_freep) Address *address = NULL;
- Address *existing;
int r;
assert(link);
if (r < 0)
return r;
- if (address_get(link, address, &existing) < 0)
- return 0;
-
- if (existing->source != NETWORK_CONFIG_SOURCE_IPV4LL)
- return 0;
-
- if (!address_exists(existing))
- return 0;
-
log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
- return address_remove(existing);
+ return address_remove_and_cancel(address, link);
}
static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
#include <linux/nexthop.h>
-#include "dhcp-server-internal.h"
+#include "dhcp-server-lease-internal.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dns-domain.h"
static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) {
_cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
- DHCPLease *lease;
+ sd_dhcp_server_lease *lease;
int r;
assert(link);
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_BYTE_ARRAY(
"ClientId",
- lease->client_id.data,
- lease->client_id.length),
+ lease->client_id.raw,
+ lease->client_id.size),
JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname),
JSON_BUILD_PAIR_FINITE_USEC(
static int dhcp_server_static_leases_append_json(Link *link, JsonVariant **v) {
_cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
- DHCPLease *lease;
+ sd_dhcp_server_lease *lease;
int r;
assert(link);
JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_BYTE_ARRAY(
"ClientId",
- lease->client_id.data,
- lease->client_id.length),
+ lease->client_id.raw,
+ lease->client_id.size),
JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address)));
if (r < 0)
return r;
#include "bus-util.h"
#include "device-private.h"
#include "device-util.h"
-#include "dhcp-identifier.h"
#include "dhcp-lease-internal.h"
#include "env-file.h"
#include "ethtool-util.h"
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);
if (r < 0)
return log_device_warning_errno(device, r, "Failed to get udev action, ignoring: %m");
- r = sd_device_get_subsystem(device, &s);
- if (r < 0)
- return log_device_warning_errno(device, r, "Failed to get subsystem, ignoring: %m");
-
- if (streq(s, "net"))
+ if (device_in_subsystem(device, "net"))
r = manager_udev_process_link(m, device, action);
- else if (streq(s, "ieee80211"))
+ else if (device_in_subsystem(device, "ieee80211"))
r = manager_udev_process_wiphy(m, device, action);
- else if (streq(s, "rfkill"))
+ else if (device_in_subsystem(device, "rfkill"))
r = manager_udev_process_rfkill(m, device, action);
- else {
- log_device_debug(device, "Received device with unexpected subsystem \"%s\", ignoring.", s);
- return 0;
- }
if (r < 0)
log_device_warning_errno(device, r, "Failed to process \"%s\" uevent, ignoring: %m",
device_action_to_string(action));
#include "sd-netlink.h"
#include "sd-resolve.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "firewall-util.h"
#include "hashmap.h"
#include "networkd-link.h"
if (address->lifetime_valid_usec >= timestamp_usec)
continue; /* the address is still valid */
- r = address_remove_and_drop(address);
+ r = address_remove_and_cancel(address, link);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC address, ignoring: %m"));
}
Route.TCPCongestionControlAlgorithm, config_parse_tcp_congestion, 0, 0
Route.QuickAck, config_parse_route_boolean, 0, 0
Route.FastOpenNoCookie, config_parse_route_boolean, 0, 0
-Route.TTLPropagate, config_parse_route_boolean, 0, 0
+Route.TTLPropagate, config_parse_warn_compat, DISABLED_LEGACY, 0
Route.MultiPathRoute, config_parse_multipath_route, 0, 0
Route.NextHop, config_parse_route_nexthop, 0, 0
NextHop.Id, config_parse_nexthop_id, 0, 0
assert(process);
req = new(Request, 1);
- if (!req) {
- if (free_func)
- free_func(userdata);
+ if (!req)
return -ENOMEM;
- }
*req = (Request) {
.n_ref = 1,
request_process_func_t process,
Request **ret) {
+ int r;
+
assert(netdev);
- return request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
- netdev_ref(netdev), (mfree_func_t) netdev_unref,
- trivial_hash_func, trivial_compare_func,
- process, NULL, NULL, ret);
+ r = request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
+ netdev, (mfree_func_t) netdev_unref,
+ trivial_hash_func, trivial_compare_func,
+ process, NULL, NULL, ret);
+ if (r <= 0)
+ return r;
+
+ netdev_ref(netdev);
+ return 1;
}
int link_queue_request_full(
.quickack = -1,
.fast_open_no_cookie = -1,
.gateway_onlink = -1,
- .ttl_propagate = -1,
};
*ret = TAKE_PTR(route);
return r;
}
- if (route->ttl_propagate >= 0) {
- r = sd_netlink_message_append_u8(m, RTA_TTL_PROPAGATE, route->ttl_propagate);
- if (r < 0)
- return r;
- }
-
r = sd_netlink_message_open_container(m, RTA_METRICS);
if (r < 0)
return r;
n->quickack = r;
else if (streq(lvalue, "FastOpenNoCookie"))
n->fast_open_no_cookie = r;
- else if (streq(lvalue, "TTLPropagate"))
- n->ttl_propagate = r;
else
assert_not_reached();
uint32_t gw_weight;
int quickack;
int fast_open_no_cookie;
- int ttl_propagate;
unsigned char dst_prefixlen;
unsigned char src_prefixlen;
if (!link->dev)
return -ENODEV;
- r = sd_device_get_devtype(link->dev, &s);
- if (r < 0)
- return r;
-
- if (!streq_ptr(s, "wlan"))
+ if (!device_is_devtype(link->dev, "wlan"))
return -EOPNOTSUPP;
r = sd_device_new_child(&phy, link->dev, "phy80211");
#include "umask-util.h"
#include "unit-name.h"
#include "user-util.h"
+#include "vpick.h"
/* The notify socket inside the container it can use to talk to nspawn using the sd_notify(3) protocol */
#define NSPAWN_NOTIFY_SOCKET_PATH "/run/host/notify"
return 0;
}
+static int pick_paths(void) {
+ int r;
+
+ if (arg_directory) {
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+ PickFilter filter = pick_filter_image_dir;
+
+ filter.architecture = arg_architecture;
+
+ r = path_pick_update_warn(
+ &arg_directory,
+ &filter,
+ PICK_ARCHITECTURE|PICK_TRIES,
+ &result);
+ if (r < 0) {
+ /* Accept ENOENT here so that the --template= logic can work */
+ if (r != -ENOENT)
+ return r;
+ } else
+ arg_architecture = result.architecture;
+ }
+
+ if (arg_image) {
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+ PickFilter filter = pick_filter_image_raw;
+
+ filter.architecture = arg_architecture;
+
+ r = path_pick_update_warn(
+ &arg_image,
+ &filter,
+ PICK_ARCHITECTURE|PICK_TRIES,
+ &result);
+ if (r < 0)
+ return r;
+
+ arg_architecture = result.architecture;
+ }
+
+ if (arg_template) {
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+ PickFilter filter = pick_filter_image_dir;
+
+ filter.architecture = arg_architecture;
+
+ r = path_pick_update_warn(
+ &arg_template,
+ &filter,
+ PICK_ARCHITECTURE,
+ &result);
+ if (r < 0)
+ return r;
+
+ arg_architecture = result.architecture;
+ }
+
+ return 0;
+}
+
static int determine_names(void) {
int r;
if (arg_template && !arg_directory && arg_machine) {
- /* If --template= was specified then we should not
- * search for a machine, but instead create a new one
- * in /var/lib/machine. */
+ /* If --template= was specified then we should not search for a machine, but instead create a
+ * new one in /var/lib/machine. */
arg_directory = path_join("/var/lib/machines", arg_machine);
if (!arg_directory)
if (r < 0)
goto finish;
+ r = pick_paths();
+ if (r < 0)
+ goto finish;
+
r = determine_names();
if (r < 0)
goto finish;
* http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml.
*/
enum {
- /* Normal records */
+ /* 0 is reserved */
DNS_TYPE_A = 0x01,
DNS_TYPE_NS,
DNS_TYPE_MD,
DNS_TYPE_NSEC3,
DNS_TYPE_NSEC3PARAM,
DNS_TYPE_TLSA,
-
+ DNS_TYPE_SMIMEA, /* RFC 8162 */
+ /* 0x36 (54) is not assigned */
DNS_TYPE_HIP = 0x37,
DNS_TYPE_NINFO,
DNS_TYPE_RKEY,
DNS_TYPE_CDS,
DNS_TYPE_CDNSKEY,
DNS_TYPE_OPENPGPKEY,
-
+ DNS_TYPE_CSYNC,
+ DNS_TYPE_ZONEMD,
+ DNS_TYPE_SVCB, /* RFC 9460 */
+ DNS_TYPE_HTTPS, /* RFC 9460 */
+ /* 0x42…0x62 (66…98) are not assigned */
DNS_TYPE_SPF = 0x63,
+ DNS_TYPE_UINFO,
+ DNS_TYPE_UID,
+ DNS_TYPE_GID,
+ DNS_TYPE_UNSPEC,
DNS_TYPE_NID,
DNS_TYPE_L32,
DNS_TYPE_L64,
DNS_TYPE_LP,
DNS_TYPE_EUI48,
DNS_TYPE_EUI64,
-
+ /* 0x6e…0xf8 (110…248) are not assigned */
DNS_TYPE_TKEY = 0xF9,
DNS_TYPE_TSIG,
DNS_TYPE_IXFR,
DNS_TYPE_ANY,
DNS_TYPE_URI,
DNS_TYPE_CAA,
+ DNS_TYPE_AVC,
+ DNS_TYPE_DOA,
+ DNS_TYPE_AMTRELAY,
+ DNS_TYPE_RESINFO,
+ /* 0x106…0x7fff (262…32767) are not assigned */
DNS_TYPE_TA = 0x8000,
DNS_TYPE_DLV,
-
+ /* 32770…65279 are not assigned */
+ /* 65280…65534 are for private use */
+ /* 65535 is reserved */
_DNS_TYPE_MAX,
_DNS_TYPE_INVALID = -EINVAL,
};
-assert_cc(DNS_TYPE_SSHFP == 44);
-assert_cc(DNS_TYPE_TLSA == 52);
+assert_cc(DNS_TYPE_SMIMEA == 53);
+assert_cc(DNS_TYPE_HTTPS == 65);
+assert_cc(DNS_TYPE_EUI64 == 109);
+assert_cc(DNS_TYPE_RESINFO == 261);
assert_cc(DNS_TYPE_ANY == 255);
/* DNS record classes, see RFC 1035 */
if (!j->shared_owner)
continue;
+ /* Ignore cached goodby packet. See on_mdns_packet() and RFC 6762 section 10.1. */
+ if (j->rr->ttl <= 1)
+ continue;
+
/* RFC6762 7.1: Don't append records with less than half the TTL remaining
* as known answers. */
if (usec_sub_unsigned(j->until, ts) < j->rr->ttl * USEC_PER_SEC / 2)
* and the reply we might still get from the server will be eaten up instead of resulting in an ICMP
* port unreachable error message. */
- /* Skip the graveyard stuff when we're shutting down, since that requires running event loop */
- if (!t->scope->manager->event || sd_event_get_state(t->scope->manager->event) == SD_EVENT_FINISHED)
+ /* Skip the graveyard stuff when we're shutting down, since that requires running event loop.
+ * Note that this is also called from dns_transaction_free(). In that case, scope may be NULL. */
+ if (!t->scope ||
+ !t->scope->manager ||
+ !t->scope->manager->event ||
+ sd_event_get_state(t->scope->manager->event) == SD_EVENT_FINISHED)
use_graveyard = false;
if (use_graveyard && t->dns_udp_fd >= 0 && t->sent && !t->received) {
if (r < 0)
return r;
- if (socket_ipv6_is_supported()) {
+ if (socket_ipv6_is_enabled()) {
r = manager_llmnr_ipv6_udp_fd(m);
if (r == -EADDRINUSE)
goto eaddrinuse;
if (r < 0)
return r;
- if (socket_ipv6_is_supported()) {
+ if (socket_ipv6_is_enabled()) {
r = manager_mdns_ipv6_fd(m);
if (r == -EADDRINUSE)
goto eaddrinuse;
pid = clone_with_nested_stack(close_func, CLONE_FILES | ((v & NEED_DOUBLE_FORK) ? 0 : SIGCHLD), UINT_TO_PTR(v));
if (pid < 0)
- assert_se(close_nointr(fd) != -EBADF); /* local fallback */
+ safe_close(fd); /* local fallback */
else if (v & NEED_DOUBLE_FORK) {
/* Reap the intermediate child. Key here is that we specify __WCLONE, since we didn't ask for
}
int block_device_is_whole_disk(sd_device *dev) {
- const char *s;
- int r;
-
assert(dev);
- r = sd_device_get_subsystem(dev, &s);
- if (r < 0)
- return r;
-
- if (!streq(s, "block"))
+ if (!device_in_subsystem(dev, "block"))
return -ENOTBLK;
- r = sd_device_get_devtype(dev, &s);
- if (r < 0)
- return r;
-
- return streq(s, "disk");
+ return device_is_devtype(dev, "disk");
}
int block_device_get_whole_disk(sd_device *dev, sd_device **ret) {
bus_print_property_value(name, expected_value, flags, "[not set]");
- else if ((ENDSWITH_SET(name, "MemoryLow", "MemoryMin", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryZSwapMax", "MemoryLimit") &&
+ else if ((ENDSWITH_SET(name, "MemoryLow", "MemoryMin",
+ "MemoryHigh", "MemoryMax",
+ "MemorySwapMax", "MemoryZSwapMax", "MemoryLimit") &&
u == CGROUP_LIMIT_MAX) ||
- (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
+ (endswith(name, "TasksMax") && u == UINT64_MAX) ||
(startswith(name, "Limit") && u == UINT64_MAX) ||
(startswith(name, "DefaultLimit") && u == UINT64_MAX))
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
-#include "bus-wait-for-jobs.h"
-#include "set.h"
-#include "bus-util.h"
#include "bus-internal.h"
-#include "unit-def.h"
+#include "bus-util.h"
+#include "bus-wait-for-jobs.h"
#include "escape.h"
+#include "set.h"
#include "strv.h"
+#include "unit-def.h"
typedef struct BusWaitForJobs {
sd_bus *bus;
sd_bus_slot *slot_disconnected;
} BusWaitForJobs;
+BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d) {
+ if (!d)
+ return NULL;
+
+ set_free(d->jobs);
+
+ sd_bus_slot_unref(d->slot_disconnected);
+ sd_bus_slot_unref(d->slot_job_removed);
+
+ sd_bus_unref(d->bus);
+
+ free(d->name);
+ free(d->result);
+
+ return mfree(d);
+}
+
static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
assert(m);
- log_error("Warning! D-Bus connection terminated.");
+ log_warning("D-Bus connection terminated while waiting for jobs.");
sd_bus_close(sd_bus_message_get_bus(m));
return 0;
}
static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- const char *path, *unit, *result;
BusWaitForJobs *d = ASSERT_PTR(userdata);
- uint32_t id;
- char *found;
+ _cleanup_free_ char *job_found = NULL;
+ const char *path, *unit, *result;
int r;
assert(m);
- r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
+ r = sd_bus_message_read(m, "uoss", /* id = */ NULL, &path, &unit, &result);
if (r < 0) {
bus_log_parse_error(r);
return 0;
}
- found = set_remove(d->jobs, (char*) path);
- if (!found)
+ job_found = set_remove(d->jobs, (char*) path);
+ if (!job_found)
return 0;
- free(found);
-
- (void) free_and_strdup(&d->result, empty_to_null(result));
-
(void) free_and_strdup(&d->name, empty_to_null(unit));
+ (void) free_and_strdup(&d->result, empty_to_null(result));
return 0;
}
-BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d) {
- if (!d)
- return NULL;
-
- set_free(d->jobs);
-
- sd_bus_slot_unref(d->slot_disconnected);
- sd_bus_slot_unref(d->slot_job_removed);
-
- sd_bus_unref(d->bus);
-
- free(d->name);
- free(d->result);
-
- return mfree(d);
-}
-
int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) {
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL;
int r;
.bus = sd_bus_ref(bus),
};
- /* When we are a bus client we match by sender. Direct
- * connections OTOH have no initialized sender field, and
- * hence we ignore the sender then */
+ /* When we are a bus client we match by sender. Direct connections OTOH have no initialized sender
+ * field, and hence we ignore the sender then */
r = sd_bus_match_signal_async(
bus,
&d->slot_job_removed,
}
}
-static int bus_job_get_service_result(BusWaitForJobs *d, char **result) {
+static int bus_job_get_service_result(BusWaitForJobs *d, char **ret) {
_cleanup_free_ char *dbus_path = NULL;
assert(d);
assert(d->name);
- assert(result);
+ assert(ret);
if (!endswith(d->name, ".service"))
return -EINVAL;
"org.freedesktop.systemd1.Service",
"Result",
NULL,
- result);
+ ret);
}
static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) {
- _cleanup_free_ char *service_shell_quoted = NULL;
- const char *systemctl = "systemctl", *journalctl = "journalctl";
static const struct {
const char *result, *explanation;
} explanations[] = {
- { "resources", "of unavailable resources or another system error" },
+ { "resources", "of unavailable resources or another system error" },
{ "protocol", "the service did not take the steps required by its unit configuration" },
- { "timeout", "a timeout was exceeded" },
- { "exit-code", "the control process exited with error code" },
- { "signal", "a fatal signal was delivered to the control process" },
+ { "timeout", "a timeout was exceeded" },
+ { "exit-code", "the control process exited with error code" },
+ { "signal", "a fatal signal was delivered to the control process" },
{ "core-dump", "a fatal signal was delivered causing the control process to dump core" },
- { "watchdog", "the service failed to send watchdog ping" },
- { "start-limit", "start of the service was attempted too often" }
+ { "watchdog", "the service failed to send watchdog ping" },
+ { "start-limit", "start of the service was attempted too often" },
};
+ _cleanup_free_ char *service_shell_quoted = NULL;
+ const char *systemctl = "systemctl", *journalctl = "journalctl";
+
assert(service);
service_shell_quoted = shell_maybe_quote(service, 0);
- if (!strv_isempty((char**) extra_args)) {
+ if (!strv_isempty((char* const*) extra_args)) {
_cleanup_free_ char *t = NULL;
- t = strv_join((char**) extra_args, " ");
+ t = strv_join((char* const*) extra_args, " ");
systemctl = strjoina("systemctl ", t ?: "<args>");
journalctl = strjoina("journalctl ", t ?: "<args>");
}
- if (!isempty(result)) {
- size_t i;
-
- for (i = 0; i < ELEMENTSOF(explanations); ++i)
- if (streq(result, explanations[i].result))
- break;
-
- if (i < ELEMENTSOF(explanations)) {
- log_error("Job for %s failed because %s.\n"
- "See \"%s status %s\" and \"%s -xeu %s\" for details.\n",
- service,
- explanations[i].explanation,
- systemctl,
- service_shell_quoted ?: "<service>",
- journalctl,
- service_shell_quoted ?: "<service>");
- goto finish;
- }
- }
+ if (!isempty(result))
+ FOREACH_ARRAY(i, explanations, ELEMENTSOF(explanations))
+ if (streq(result, i->result)) {
+ log_error("Job for %s failed because %s.\n"
+ "See \"%s status %s\" and \"%s -xeu %s\" for details.\n",
+ service, i->explanation,
+ systemctl, service_shell_quoted ?: "<service>",
+ journalctl, service_shell_quoted ?: "<service>");
+ goto extra;
+ }
log_error("Job for %s failed.\n"
"See \"%s status %s\" and \"%s -xeu %s\" for details.\n",
service,
- systemctl,
- service_shell_quoted ?: "<service>",
- journalctl,
- service_shell_quoted ?: "<service>");
+ systemctl, service_shell_quoted ?: "<service>",
+ journalctl, service_shell_quoted ?: "<service>");
-finish:
+extra:
/* For some results maybe additional explanation is required */
if (streq_ptr(result, "start-limit"))
log_info("To force a start use \"%1$s reset-failed %2$s\"\n"
}
static int check_wait_response(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) {
+ int r;
+
assert(d);
assert(d->name);
assert(d->result);
+ if (streq(d->result, "done")) {
+ if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
+ log_info("Job for %s finished.", d->name);
+
+ return 0;
+ } else if (streq(d->result, "skipped")) {
+ if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
+ log_info("Job for %s was skipped.", d->name);
+
+ return 0;
+ }
+
if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_ERROR)) {
if (streq(d->result, "canceled"))
- log_error("Job for %s canceled.", strna(d->name));
+ log_error("Job for %s canceled.", d->name);
else if (streq(d->result, "timeout"))
- log_error("Job for %s timed out.", strna(d->name));
+ log_error("Job for %s timed out.", d->name);
else if (streq(d->result, "dependency"))
- log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
+ log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", d->name);
else if (streq(d->result, "invalid"))
- log_error("%s is not active, cannot reload.", strna(d->name));
+ log_error("%s is not active, cannot reload.", d->name);
else if (streq(d->result, "assert"))
- log_error("Assertion failed on job for %s.", strna(d->name));
+ log_error("Assertion failed on job for %s.", d->name);
else if (streq(d->result, "unsupported"))
- log_error("Operation on or unit type of %s not supported on this system.", strna(d->name));
+ log_error("Operation on or unit type of %s not supported on this system.", d->name);
else if (streq(d->result, "collected"))
- log_error("Queued job for %s was garbage collected.", strna(d->name));
+ log_error("Queued job for %s was garbage collected.", d->name);
else if (streq(d->result, "once"))
- log_error("Unit %s was started already once and can't be started again.", strna(d->name));
- else if (!STR_IN_SET(d->result, "done", "skipped")) {
-
- if (d->name && endswith(d->name, ".service")) {
- _cleanup_free_ char *result = NULL;
- int q;
-
- q = bus_job_get_service_result(d, &result);
- if (q < 0)
- log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name);
-
- log_job_error_with_service_result(d->name, result, extra_args);
- } else
- log_error("Job failed. See \"journalctl -xe\" for details.");
- }
+ log_error("Unit %s was started already once and can't be started again.", d->name);
+ else if (endswith(d->name, ".service")) {
+ /* Job result is unknown. For services, let's also try Result property. */
+ _cleanup_free_ char *result = NULL;
+
+ r = bus_job_get_service_result(d, &result);
+ if (r < 0)
+ log_debug_errno(r, "Failed to get Result property of unit %s, ignoring: %m",
+ d->name);
+
+ log_job_error_with_service_result(d->name, result, extra_args);
+ } else /* Otherwise we just show a generic message. */
+ log_error("Job failed. See \"journalctl -xe\" for details.");
}
if (STR_IN_SET(d->result, "canceled", "collected"))
return -EOPNOTSUPP;
else if (streq(d->result, "once"))
return -ESTALE;
- else if (streq(d->result, "done")) {
- if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
- log_info("Job for %s finished.", strna(d->name));
- return 0;
- } else if (streq(d->result, "skipped")) {
- if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
- log_info("Job for %s was skipped.", strna(d->name));
- return 0;
- }
- return log_debug_errno(SYNTHETIC_ERRNO(EIO),
- "Unexpected job result, assuming server side newer than us: %s", d->result);
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
+ "Unexpected job result '%s' for unit '%s', assuming server side newer than us.",
+ d->result, d->name);
}
int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) {
if (d->name && d->result) {
q = check_wait_response(d, flags, extra_args);
- /* Return the first error as it is most likely to be
- * meaningful. */
- if (q < 0 && r == 0)
- r = q;
+ /* Return the first error as it is most likely to be meaningful. */
+ RET_GATHER(r, q);
log_full_errno_zerook(LOG_DEBUG, q,
- "Got result %s/%m for job %s", d->result, d->name);
+ "Got result %s/%m for job %s.", d->result, d->name);
}
d->name = mfree(d->name);
#include "macro.h"
+typedef struct BusWaitForJobs BusWaitForJobs;
+
typedef enum WaitJobsFlags {
BUS_WAIT_JOBS_LOG_ERROR = 1 << 0,
BUS_WAIT_JOBS_LOG_SUCCESS = 1 << 1,
} WaitJobsFlags;
-typedef struct BusWaitForJobs BusWaitForJobs;
+BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d);
+DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
-BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d);
int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args);
int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, WaitJobsFlags flags, const char* const* extra_args);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
log_debug_errno(r, "Failed to drop reference to unit %s, ignoring: %m", item->bus_path);
}
- assert_se(hashmap_remove(item->parent->items, item->bus_path) == item);
+ assert_se(hashmap_remove_value(item->parent->items, item->bus_path, item));
if (item->parent->current == item)
item->parent->current = NULL;
assert(m);
- log_error("Warning! D-Bus connection terminated.");
+ log_warning("D-Bus connection terminated while waiting for unit.");
bus_wait_for_units_clear(d);
if (d->ready_callback)
- d->ready_callback(d, false, d->userdata);
+ d->ready_callback(d, BUS_WAIT_FAILURE, d->userdata);
else /* If no ready callback is specified close the connection so that the event loop exits */
sd_bus_close(sd_bus_message_get_bus(m));
typedef void (*bus_wait_for_units_unit_callback)(BusWaitForUnits *d, const char *unit_path, bool good, void *userdata);
int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret);
+
BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d);
+DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForUnits*, bus_wait_for_units_free);
BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d);
void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata);
int bus_wait_for_units_add_unit(BusWaitForUnits *d, const char *unit, BusWaitForUnitsFlags flags, bus_wait_for_units_unit_callback callback, void *userdata);
int bus_wait_for_units_run(BusWaitForUnits *d);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForUnits*, bus_wait_for_units_free);
#include "strv.h"
#include "time-util.h"
#include "utf8.h"
+#include "vpick.h"
#include "xattr-util.h"
static const char* const image_search_path[_IMAGE_CLASS_MAX] = {
"/usr/lib/confexts\0",
};
-/* Inside the initrd, use a slightly different set of search path (i.e. include .extra/sysext in extension
- * search dir) */
+/* Inside the initrd, use a slightly different set of search path (i.e. include .extra/sysext/ and
+ * .extra/confext/ in extension search dir) */
static const char* const image_search_path_initrd[_IMAGE_CLASS_MAX] = {
/* (entries that aren't listed here will get the same search path as for the non initrd-case) */
[IMAGE_SYSEXT] = "/etc/extensions\0" /* only place symlinks here */
"/run/extensions\0" /* and here too */
"/var/lib/extensions\0" /* the main place for images */
- "/.extra/sysext\0" /* put sysext picked up by systemd-stub last, since not trusted */
+ "/.extra/sysext\0", /* put sysext picked up by systemd-stub last, since not trusted */
+
+ [IMAGE_CONFEXT] = "/run/confexts\0" /* only place symlinks here */
+ "/var/lib/confexts\0" /* the main place for images */
+ "/usr/local/lib/confexts\0"
+ "/.extra/confext\0", /* put confext picked up by systemd-stub last, since not trusted */
};
static const char* image_class_suffix_table[_IMAGE_CLASS_MAX] = {
return 0;
}
-static int extract_pretty(
+static int extract_image_basename(
const char *path,
- const char *class_suffix,
- const char *format_suffix,
- char **ret) {
+ const char *class_suffix, /* e.g. ".sysext" (this is an optional suffix) */
+ char **format_suffixes, /* e.g. ".raw" (one of these will be required) */
+ char **ret_basename,
+ char **ret_suffix) {
- _cleanup_free_ char *name = NULL;
+ _cleanup_free_ char *name = NULL, *suffix = NULL;
int r;
assert(path);
- assert(ret);
r = path_extract_filename(path, &name);
if (r < 0)
return r;
- if (format_suffix) {
- char *e = endswith(name, format_suffix);
+ if (format_suffixes) {
+ char *e = strv_endswith(name, format_suffixes);
if (!e) /* Format suffix is required */
return -EINVAL;
+ if (ret_suffix) {
+ suffix = strdup(e);
+ if (!suffix)
+ return -ENOMEM;
+ }
+
*e = 0;
}
if (class_suffix) {
char *e = endswith(name, class_suffix);
- if (e) /* Class suffix is optional */
+ if (e) { /* Class suffix is optional */
+ if (ret_suffix) {
+ _cleanup_free_ char *j = strjoin(e, suffix);
+ if (!j)
+ return -ENOMEM;
+
+ free_and_replace(suffix, j);
+ }
+
*e = 0;
+ }
}
if (!image_name_is_valid(name))
return -EINVAL;
- *ret = TAKE_PTR(name);
+ if (ret_suffix)
+ *ret_suffix = TAKE_PTR(suffix);
+
+ if (ret_basename)
+ *ret_basename = TAKE_PTR(name);
+
return 0;
}
return 0;
if (!pretty) {
- r = extract_pretty(filename, image_class_suffix_to_string(c), NULL, &pretty_buffer);
+ r = extract_image_basename(
+ filename,
+ image_class_suffix_to_string(c),
+ /* format_suffix= */ NULL,
+ &pretty_buffer,
+ /* ret_suffix= */ NULL);
if (r < 0)
return r;
(void) fd_getcrtime_at(dfd, filename, AT_SYMLINK_FOLLOW, &crtime);
if (!pretty) {
- r = extract_pretty(filename, image_class_suffix_to_string(c), ".raw", &pretty_buffer);
+ r = extract_image_basename(
+ filename,
+ image_class_suffix_to_string(c),
+ STRV_MAKE(".raw"),
+ &pretty_buffer,
+ /* ret_suffix= */ NULL);
if (r < 0)
return r;
return 0;
if (!pretty) {
- r = extract_pretty(filename, NULL, NULL, &pretty_buffer);
+ r = extract_image_basename(
+ filename,
+ /* class_suffix= */ NULL,
+ /* format_suffix= */ NULL,
+ &pretty_buffer,
+ /* ret_suffix= */ NULL);
if (r < 0)
return r;
return in_initrd() && image_search_path_initrd[class] ? image_search_path_initrd[class] : image_search_path[class];
}
+static char **make_possible_filenames(ImageClass class, const char *image_name) {
+ _cleanup_strv_free_ char **l = NULL;
+
+ assert(image_name);
+
+ FOREACH_STRING(v_suffix, "", ".v")
+ FOREACH_STRING(format_suffix, "", ".raw") {
+ _cleanup_free_ char *j = NULL;
+ const char *class_suffix;
+
+ class_suffix = image_class_suffix_to_string(class);
+ if (class_suffix) {
+ j = strjoin(image_name, class_suffix, format_suffix, v_suffix);
+ if (!j)
+ return NULL;
+
+ if (strv_consume(&l, TAKE_PTR(j)) < 0)
+ return NULL;
+ }
+
+ j = strjoin(image_name, format_suffix, v_suffix);
+ if (!j)
+ return NULL;
+
+ if (strv_consume(&l, TAKE_PTR(j)) < 0)
+ return NULL;
+ }
+
+ return TAKE_PTR(l);
+}
+
int image_find(ImageClass class,
const char *name,
const char *root,
if (!image_name_is_valid(name))
return -ENOENT;
+ _cleanup_strv_free_ char **names = make_possible_filenames(class, name);
+ if (!names)
+ return -ENOMEM;
+
NULSTR_FOREACH(path, pick_image_search_path(class)) {
_cleanup_free_ char *resolved = NULL;
_cleanup_closedir_ DIR *d = NULL;
* to symlink block devices into the search path. (For now, we disable that when operating
* relative to some root directory.) */
flags = root ? AT_SYMLINK_NOFOLLOW : 0;
- if (fstatat(dirfd(d), name, &st, flags) < 0) {
- _cleanup_free_ char *raw = NULL;
- if (errno != ENOENT)
- return -errno;
+ STRV_FOREACH(n, names) {
+ _cleanup_free_ char *fname_buf = NULL;
+ const char *fname = *n;
- raw = strjoin(name, ".raw");
- if (!raw)
- return -ENOMEM;
+ if (fstatat(dirfd(d), fname, &st, flags) < 0) {
+ if (errno != ENOENT)
+ return -errno;
- if (fstatat(dirfd(d), raw, &st, flags) < 0) {
- if (errno == ENOENT)
+ continue; /* Vanished while we were looking at it */
+ }
+
+ if (endswith(fname, ".raw")) {
+ if (!S_ISREG(st.st_mode)) {
+ log_debug("Ignoring non-regular file '%s' with .raw suffix.", fname);
continue;
+ }
- return -errno;
- }
+ } else if (endswith(fname, ".v")) {
- if (!S_ISREG(st.st_mode))
- continue;
+ if (!S_ISDIR(st.st_mode)) {
+ log_debug("Ignoring non-directory file '%s' with .v suffix.", fname);
+ continue;
+ }
+
+ _cleanup_free_ char *suffix = NULL;
+ suffix = strdup(ASSERT_PTR(startswith(fname, name)));
+ if (!suffix)
+ return -ENOMEM;
+
+ *ASSERT_PTR(endswith(suffix, ".v")) = 0;
+
+ _cleanup_free_ char *vp = path_join(resolved, fname);
+ if (!vp)
+ return -ENOMEM;
+
+ PickFilter filter = {
+ .type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
+ .basename = name,
+ .architecture = _ARCHITECTURE_INVALID,
+ .suffix = suffix,
+ };
+
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+ r = path_pick(root,
+ /* toplevel_fd= */ AT_FDCWD,
+ vp,
+ &filter,
+ PICK_ARCHITECTURE|PICK_TRIES,
+ &result);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to pick versioned image on '%s', skipping: %m", vp);
+ continue;
+ }
+ if (!result.path) {
+ log_debug("Found versioned directory '%s', without matching entry, skipping: %m", vp);
+ continue;
+ }
- r = image_make(class, name, dirfd(d), resolved, raw, &st, ret);
+ /* Refresh the stat data for the discovered target */
+ st = result.st;
- } else {
- if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode))
+ _cleanup_free_ char *bn = NULL;
+ r = path_extract_filename(result.path, &bn);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", result.path);
+ continue;
+ }
+
+ fname_buf = path_join(fname, bn);
+ if (!fname_buf)
+ return log_oom();
+
+ fname = fname_buf;
+
+ } else if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+ log_debug("Ignoring non-directory and non-block device file '%s' without suffix.", fname);
continue;
+ }
- r = image_make(class, name, dirfd(d), resolved, name, &st, ret);
- }
- if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
- continue;
- if (r < 0)
- return r;
+ r = image_make(class, name, dirfd(d), resolved, fname, &st, ret);
+ if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
+ continue;
+ if (r < 0)
+ return r;
- if (ret)
- (*ret)->discoverable = true;
+ if (ret)
+ (*ret)->discoverable = true;
- return 1;
+ return 1;
+ }
}
if (class == IMAGE_MACHINE && streq(name, ".host")) {
if (ret)
(*ret)->discoverable = true;
- return r;
+ return 1;
}
return -ENOENT;
return r;
FOREACH_DIRENT_ALL(de, d, return -errno) {
+ _cleanup_free_ char *pretty = NULL, *fname_buf = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
- _cleanup_free_ char *pretty = NULL;
+ const char *fname = de->d_name;
struct stat st;
int flags;
- if (dot_or_dot_dot(de->d_name))
+ if (dot_or_dot_dot(fname))
continue;
/* As mentioned above, we follow symlinks on this fstatat(), because we want to
* permit people to symlink block devices into the search path. */
flags = root ? AT_SYMLINK_NOFOLLOW : 0;
- if (fstatat(dirfd(d), de->d_name, &st, flags) < 0) {
+ if (fstatat(dirfd(d), fname, &st, flags) < 0) {
if (errno == ENOENT)
continue;
return -errno;
}
- if (S_ISREG(st.st_mode))
- r = extract_pretty(de->d_name, image_class_suffix_to_string(class), ".raw", &pretty);
- else if (S_ISDIR(st.st_mode))
- r = extract_pretty(de->d_name, image_class_suffix_to_string(class), NULL, &pretty);
- else if (S_ISBLK(st.st_mode))
- r = extract_pretty(de->d_name, NULL, NULL, &pretty);
- else {
- log_debug("Skipping directory entry '%s', which is neither regular file, directory nor block device.", de->d_name);
- continue;
- }
- if (r < 0) {
- log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", de->d_name);
+ if (S_ISREG(st.st_mode)) {
+ r = extract_image_basename(
+ fname,
+ image_class_suffix_to_string(class),
+ STRV_MAKE(".raw"),
+ &pretty,
+ /* suffix= */ NULL);
+ if (r < 0) {
+ log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", fname);
+ continue;
+ }
+ } else if (S_ISDIR(st.st_mode)) {
+ const char *v;
+
+ v = endswith(fname, ".v");
+ if (v) {
+ _cleanup_free_ char *suffix = NULL, *nov = NULL;
+
+ nov = strndup(fname, v - fname); /* Chop off the .v */
+ if (!nov)
+ return -ENOMEM;
+
+ r = extract_image_basename(
+ nov,
+ image_class_suffix_to_string(class),
+ STRV_MAKE(".raw", ""),
+ &pretty,
+ &suffix);
+ if (r < 0) {
+ log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like a versioned image.", fname);
+ continue;
+ }
+
+ _cleanup_free_ char *vp = path_join(resolved, fname);
+ if (!vp)
+ return -ENOMEM;
+
+ PickFilter filter = {
+ .type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
+ .basename = pretty,
+ .architecture = _ARCHITECTURE_INVALID,
+ .suffix = suffix,
+ };
+
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+ r = path_pick(root,
+ /* toplevel_fd= */ AT_FDCWD,
+ vp,
+ &filter,
+ PICK_ARCHITECTURE|PICK_TRIES,
+ &result);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to pick versioned image on '%s', skipping: %m", vp);
+ continue;
+ }
+ if (!result.path) {
+ log_debug("Found versioned directory '%s', without matching entry, skipping: %m", vp);
+ continue;
+ }
+
+ /* Refresh the stat data for the discovered target */
+ st = result.st;
+
+ _cleanup_free_ char *bn = NULL;
+ r = path_extract_filename(result.path, &bn);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", result.path);
+ continue;
+ }
+
+ fname_buf = path_join(fname, bn);
+ if (!fname_buf)
+ return log_oom();
+
+ fname = fname_buf;
+ } else {
+ r = extract_image_basename(
+ fname,
+ image_class_suffix_to_string(class),
+ /* format_suffix= */ NULL,
+ &pretty,
+ /* ret_suffix= */ NULL);
+ if (r < 0) {
+ log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", fname);
+ continue;
+ }
+ }
+
+ } else if (S_ISBLK(st.st_mode)) {
+ r = extract_image_basename(
+ fname,
+ /* class_suffix= */ NULL,
+ /* format_suffix= */ NULL,
+ &pretty,
+ /* ret_v_suffix= */ NULL);
+ if (r < 0) {
+ log_debug_errno(r, "Skipping directory entry '%s', which doesn't look like an image.", fname);
+ continue;
+ }
+ } else {
+ log_debug("Skipping directory entry '%s', which is neither regular file, directory nor block device.", fname);
continue;
}
if (hashmap_contains(h, pretty))
continue;
- r = image_make(class, pretty, dirfd(d), resolved, de->d_name, &st, &image);
+ r = image_make(class, pretty, dirfd(d), resolved, fname, &st, &image);
if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
continue;
if (r < 0)
#include "strv.h"
#include "tmpfile-util-label.h"
+typedef struct EditFile {
+ EditFileContext *context;
+ char *path;
+ char *original_path;
+ char **comment_paths;
+ char *temp;
+ unsigned line;
+} EditFile;
+
void edit_file_context_done(EditFileContext *context) {
int r;
#define DROPIN_MARKER_END "### Edits below this comment will be discarded"
typedef struct EditFile EditFile;
-typedef struct EditFileContext EditFileContext;
-
-struct EditFile {
- EditFileContext *context;
- char *path;
- char *original_path;
- char **comment_paths;
- char *temp;
- unsigned line;
-};
-
-struct EditFileContext {
+
+typedef struct EditFileContext {
EditFile *files;
size_t n_files;
const char *marker_start;
bool remove_parent;
bool overwrite_with_origin; /* Always overwrite target with original file. */
bool stdin; /* Read contents from stdin instead of launching an editor. */
-};
+} EditFileContext;
void edit_file_context_done(EditFileContext *context);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
+#include "efi-api.h"
#include "efi-loader.h"
#include "env-util.h"
#include "parse-util.h"
if (cached >= 0)
return cached;
- /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11. Or in
- * other words, if we are running on a TPM enabled UKI.
+ /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11 on a TPM2
+ * chip. Or in other words, if we are running on a TPM enabled UKI. (TPM 1.2 situations are ignored.)
*
* Returns == 0 and > 0 depending on the result of the test. Returns -EREMOTE if we detected a stub
* being used, but it measured things into a different PCR than we are configured for in
if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_FORCE_MEASURE, ignoring: %m");
- if (!is_efi_boot())
+ if (!efi_has_tpm2())
return (cached = 0);
r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
+#include <unistd.h>
#include "sd-daemon.h"
if (!s)
return -ENOMEM;
- for (size_t i = 0; i < n_fds; i++) {
- r = fdset_put(s, fds[i]);
+ FOREACH_ARRAY(fd, fds, n_fds) {
+ r = fdset_put(s, *fd);
if (r < 0)
return r;
}
log_debug("Closing set fd %i (%s)", fd, strna(path));
}
- (void) close_nointr(fd);
+ (void) close(fd);
}
}
.default_flags = PARTITION_POLICY_IGNORE,
};
+const ImagePolicy image_policy_confext_strict = {
+ .n_policies = 1,
+ .policies = {
+ { PARTITION_ROOT, PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
+ },
+ .default_flags = PARTITION_POLICY_IGNORE,
+};
+
const ImagePolicy image_policy_container = {
/* For systemd-nspawn containers we use all partitions, with the exception of swap */
.n_policies = 8,
extern const ImagePolicy image_policy_allow;
extern const ImagePolicy image_policy_deny;
extern const ImagePolicy image_policy_ignore;
-extern const ImagePolicy image_policy_sysext; /* No verity required */
-extern const ImagePolicy image_policy_sysext_strict; /* Signed verity required */
-extern const ImagePolicy image_policy_confext; /* No verity required */
+extern const ImagePolicy image_policy_sysext; /* No verity required */
+extern const ImagePolicy image_policy_sysext_strict; /* Signed verity required */
+extern const ImagePolicy image_policy_confext; /* No verity required */
+extern const ImagePolicy image_policy_confext_strict; /* Signed verity required */
extern const ImagePolicy image_policy_container;
extern const ImagePolicy image_policy_service;
extern const ImagePolicy image_policy_host;
'verbs.c',
'vlan-util.c',
'volatile-util.c',
+ 'vpick.c',
'wall.c',
'watchdog.c',
'web-util.c',
count,
&more,
&capabilities);
+ if (rc == TPM2_RC_VALUE)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENXIO),
+ "Requested TPM2 capability 0x%04" PRIx32 " property 0x%04" PRIx32 " apparently doesn't exist: %s",
+ capability, property, sym_Tss2_RC_Decode(rc));
if (rc != TSS2_RC_SUCCESS)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to get TPM2 capability 0x%04" PRIx32 " property 0x%04" PRIx32 ": %s",
capability, property, sym_Tss2_RC_Decode(rc));
-
if (capabilities->capability != capability)
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"TPM provided wrong capability: 0x%04" PRIx32 " instead of 0x%04" PRIx32 ".",
current_ecc_curve,
TPM2_MAX_ECC_CURVES,
&capability);
+ if (r == -ENXIO) /* If the TPM doesn't support ECC, it might return TPM2_RC_VALUE rather than capability.eccCurves == 0 */
+ break;
if (r < 0)
return r;
return true;
}
static inline void utxent_cleanup(bool *initialized) {
- if (initialized)
+ assert(initialized);
+ if (*initialized)
endutxent();
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/stat.h>
+
+#include "architecture.h"
+#include "chase.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "recurse-dir.h"
+#include "vpick.h"
+
+void pick_result_done(PickResult *p) {
+ assert(p);
+
+ free(p->path);
+ safe_close(p->fd);
+ free(p->version);
+
+ *p = PICK_RESULT_NULL;
+}
+
+static int format_fname(
+ const PickFilter *filter,
+ PickFlags flags,
+ char **ret) {
+
+ _cleanup_free_ char *fn = NULL;
+ int r;
+
+ assert(filter);
+ assert(ret);
+
+ if (FLAGS_SET(flags, PICK_TRIES) || !filter->version) /* Underspecified? */
+ return -ENOEXEC;
+
+ /* The format for names we match goes like this:
+ *
+ * <basename><suffix>
+ * or:
+ * <basename>_<version><suffix>
+ * or:
+ * <basename>_<version>_<architecture><suffix>
+ * or:
+ * <basename>_<architecture><suffix>
+ *
+ * (Note that basename can be empty, in which case the leading "_" is suppressed)
+ *
+ * Examples: foo.raw, foo_1.3-7.raw, foo_1.3-7_x86-64.raw, foo_x86-64.raw
+ *
+ * Why use "_" as separator here? Primarily because it is not used by Semver 2.0. In RPM it is used
+ * for "unsortable" versions, i.e. doesn't show up in "sortable" versions, which we matter for this
+ * usecase here. In Debian the underscore is not allowed (and it uses it itself for separating
+ * fields).
+ *
+ * This is very close to Debian's way to name packages, but allows arbitrary suffixes, and makes the
+ * architecture field redundant.
+ *
+ * Compare with RPM's "NEVRA" concept. Here we have "BVAS" (basename, version, architecture, suffix).
+ */
+
+ if (filter->basename) {
+ fn = strdup(filter->basename);
+ if (!fn)
+ return -ENOMEM;
+ }
+
+ if (filter->version) {
+ if (isempty(fn)) {
+ r = free_and_strdup(&fn, filter->version);
+ if (r < 0)
+ return r;
+ } else if (!strextend(&fn, "_", filter->version))
+ return -ENOMEM;
+ }
+
+ if (FLAGS_SET(flags, PICK_ARCHITECTURE) && filter->architecture >= 0) {
+ const char *as = ASSERT_PTR(architecture_to_string(filter->architecture));
+ if (isempty(fn)) {
+ r = free_and_strdup(&fn, as);
+ if (r < 0)
+ return r;
+ } else if (!strextend(&fn, "_", as))
+ return -ENOMEM;
+ }
+
+ if (filter->suffix && !strextend(&fn, filter->suffix))
+ return -ENOMEM;
+
+ if (!filename_is_valid(fn))
+ return -EINVAL;
+
+ *ret = TAKE_PTR(fn);
+ return 0;
+}
+
+static int errno_from_mode(uint32_t type_mask, mode_t found) {
+ /* Returns the most appropriate error code if we are lookging for an inode of type of those in the
+ * 'type_mask' but found 'found' instead.
+ *
+ * type_mask is a mask of 1U << DT_REG, 1U << DT_DIR, … flags, while found is a S_IFREG, S_IFDIR, …
+ * mode value. */
+
+ if (type_mask == 0) /* type doesn't matter */
+ return 0;
+
+ if (FLAGS_SET(type_mask, UINT32_C(1) << IFTODT(found)))
+ return 0;
+
+ if (type_mask == (UINT32_C(1) << DT_BLK))
+ return -ENOTBLK;
+ if (type_mask == (UINT32_C(1) << DT_DIR))
+ return -ENOTDIR;
+ if (type_mask == (UINT32_C(1) << DT_SOCK))
+ return -ENOTSOCK;
+
+ if (S_ISLNK(found))
+ return -ELOOP;
+ if (S_ISDIR(found))
+ return -EISDIR;
+
+ return -EBADF;
+}
+
+static int pin_choice(
+ const char *toplevel_path,
+ int toplevel_fd,
+ const char *inode_path,
+ int _inode_fd, /* we always take ownership of the fd, even on failure */
+ unsigned tries_left,
+ unsigned tries_done,
+ const PickFilter *filter,
+ PickFlags flags,
+ PickResult *ret) {
+
+ _cleanup_close_ int inode_fd = TAKE_FD(_inode_fd);
+ _cleanup_free_ char *resolved_path = NULL;
+ int r;
+
+ assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
+ assert(inode_path);
+ assert(filter);
+
+ toplevel_path = strempty(toplevel_path);
+
+ if (inode_fd < 0 || FLAGS_SET(flags, PICK_RESOLVE)) {
+ r = chaseat(toplevel_fd,
+ inode_path,
+ CHASE_AT_RESOLVE_IN_ROOT,
+ FLAGS_SET(flags, PICK_RESOLVE) ? &resolved_path : 0,
+ inode_fd < 0 ? &inode_fd : NULL);
+ if (r < 0)
+ return r;
+
+ if (resolved_path)
+ inode_path = resolved_path;
+ }
+
+ struct stat st;
+ if (fstat(inode_fd, &st) < 0)
+ return log_debug_errno(errno, "Failed to stat discovered inode '%s/%s': %m", toplevel_path, inode_path);
+
+ if (filter->type_mask != 0 &&
+ !FLAGS_SET(filter->type_mask, UINT32_C(1) << IFTODT(st.st_mode)))
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(errno_from_mode(filter->type_mask, st.st_mode)),
+ "Inode '%s/%s' has wrong type, found '%s'.",
+ toplevel_path, inode_path,
+ inode_type_to_string(st.st_mode));
+
+ _cleanup_(pick_result_done) PickResult result = {
+ .fd = TAKE_FD(inode_fd),
+ .st = st,
+ .architecture = filter->architecture,
+ .tries_left = tries_left,
+ .tries_done = tries_done,
+ };
+
+ result.path = strdup(inode_path);
+ if (!result.path)
+ return log_oom_debug();
+
+ if (filter->version) {
+ result.version = strdup(filter->version);
+ if (!result.version)
+ return log_oom_debug();
+ }
+
+ *ret = TAKE_PICK_RESULT(result);
+ return 1;
+}
+
+static int parse_tries(const char *s, unsigned *ret_tries_left, unsigned *ret_tries_done) {
+ unsigned left, done;
+ size_t n;
+
+ assert(s);
+ assert(ret_tries_left);
+ assert(ret_tries_done);
+
+ if (s[0] != '+')
+ goto nomatch;
+
+ s++;
+
+ n = strspn(s, DIGITS);
+ if (n == 0)
+ goto nomatch;
+
+ if (s[n] == 0) {
+ if (safe_atou(s, &left) < 0)
+ goto nomatch;
+
+ done = 0;
+ } else if (s[n] == '-') {
+ _cleanup_free_ char *c = NULL;
+
+ c = strndup(s, n);
+ if (!c)
+ return -ENOMEM;
+
+ if (safe_atou(c, &left) < 0)
+ goto nomatch;
+
+ s += n + 1;
+
+ if (!in_charset(s, DIGITS))
+ goto nomatch;
+
+ if (safe_atou(s, &done) < 0)
+ goto nomatch;
+ } else
+ goto nomatch;
+
+ *ret_tries_left = left;
+ *ret_tries_done = done;
+ return 1;
+
+nomatch:
+ *ret_tries_left = *ret_tries_done = UINT_MAX;
+ return 0;
+}
+
+static int make_choice(
+ const char *toplevel_path,
+ int toplevel_fd,
+ const char *inode_path,
+ int _inode_fd, /* we always take ownership of the fd, even on failure */
+ const PickFilter *filter,
+ PickFlags flags,
+ PickResult *ret) {
+
+ static const Architecture local_architectures[] = {
+ /* In order of preference */
+ native_architecture(),
+#ifdef ARCHITECTURE_SECONDARY
+ ARCHITECTURE_SECONDARY,
+#endif
+ _ARCHITECTURE_INVALID, /* accept any arch, as last resort */
+ };
+
+ _cleanup_free_ DirectoryEntries *de = NULL;
+ _cleanup_free_ char *best_version = NULL, *best_filename = NULL, *p = NULL, *j = NULL;
+ _cleanup_close_ int dir_fd = -EBADF, object_fd = -EBADF, inode_fd = TAKE_FD(_inode_fd);
+ const Architecture *architectures;
+ unsigned best_tries_left = UINT_MAX, best_tries_done = UINT_MAX;
+ size_t n_architectures, best_architecture_index = SIZE_MAX;
+ int r;
+
+ assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
+ assert(inode_path);
+ assert(filter);
+
+ toplevel_path = strempty(toplevel_path);
+
+ if (inode_fd < 0) {
+ r = chaseat(toplevel_fd, inode_path, CHASE_AT_RESOLVE_IN_ROOT, NULL, &inode_fd);
+ if (r < 0)
+ return r;
+ }
+
+ /* Maybe the filter is fully specified? Then we can generate the file name directly */
+ r = format_fname(filter, flags, &j);
+ if (r >= 0) {
+ _cleanup_free_ char *object_path = NULL;
+
+ /* Yay! This worked! */
+ p = path_join(inode_path, j);
+ if (!p)
+ return log_oom_debug();
+
+ r = chaseat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, &object_path, &object_fd);
+ if (r < 0) {
+ if (r != -ENOENT)
+ return log_debug_errno(r, "Failed to open '%s/%s': %m", toplevel_path, p);
+
+ *ret = PICK_RESULT_NULL;
+ return 0;
+ }
+
+ return pin_choice(
+ toplevel_path,
+ toplevel_fd,
+ FLAGS_SET(flags, PICK_RESOLVE) ? object_path : p,
+ TAKE_FD(object_fd), /* unconditionally pass ownership of the fd */
+ /* tries_left= */ UINT_MAX,
+ /* tries_done= */ UINT_MAX,
+ filter,
+ flags & ~PICK_RESOLVE,
+ ret);
+
+ } else if (r != -ENOEXEC)
+ return log_debug_errno(r, "Failed to format file name: %m");
+
+ /* Underspecified, so we do our enumeration dance */
+
+ /* Convert O_PATH to a regular directory fd */
+ dir_fd = fd_reopen(inode_fd, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+ if (dir_fd < 0)
+ return log_debug_errno(dir_fd, "Failed to reopen '%s/%s' as directory: %m", toplevel_path, inode_path);
+
+ r = readdir_all(dir_fd, 0, &de);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read directory '%s/%s': %m", toplevel_path, inode_path);
+
+ if (filter->architecture < 0) {
+ architectures = local_architectures;
+ n_architectures = ELEMENTSOF(local_architectures);
+ } else {
+ architectures = &filter->architecture;
+ n_architectures = 1;
+ }
+
+ FOREACH_ARRAY(entry, de->entries, de->n_entries) {
+ unsigned found_tries_done = UINT_MAX, found_tries_left = UINT_MAX;
+ _cleanup_free_ char *chopped = NULL;
+ size_t found_architecture_index = SIZE_MAX;
+ const char *e;
+
+ if (!isempty(filter->basename)) {
+ e = startswith((*entry)->d_name, filter->basename);
+ if (!e)
+ continue;
+
+ if (e[0] != '_')
+ continue;
+
+ e++;
+ } else
+ e = (*entry)->d_name;
+
+ if (!isempty(filter->suffix)) {
+ const char *sfx;
+
+ sfx = endswith(e, filter->suffix);
+ if (!sfx)
+ continue;
+
+ chopped = strndup(e, sfx - e);
+ if (!chopped)
+ return log_oom_debug();
+
+ e = chopped;
+ }
+
+ if (FLAGS_SET(flags, PICK_TRIES)) {
+ char *plus = strrchr(e, '+');
+ if (plus) {
+ r = parse_tries(plus, &found_tries_left, &found_tries_done);
+ if (r < 0)
+ return r;
+ if (r > 0) /* Found and parsed, now chop off */
+ *plus = 0;
+ }
+ }
+
+ if (FLAGS_SET(flags, PICK_ARCHITECTURE)) {
+ char *underscore = strrchr(e, '_');
+ Architecture a;
+
+ a = underscore ? architecture_from_string(underscore + 1) : _ARCHITECTURE_INVALID;
+
+ for (size_t i = 0; i < n_architectures; i++)
+ if (architectures[i] == a) {
+ found_architecture_index = i;
+ break;
+ }
+
+ if (found_architecture_index == SIZE_MAX) { /* No matching arch found */
+ log_debug("Found entry with architecture '%s' which is not what we are looking for, ignoring entry.", a < 0 ? "any" : architecture_to_string(a));
+ continue;
+ }
+
+ /* Chop off architecture from string */
+ if (underscore)
+ *underscore = 0;
+ }
+
+ if (!version_is_valid(e)) {
+ log_debug("Version string '%s' of entry '%s' is invalid, ignoring entry.", e, (*entry)->d_name);
+ continue;
+ }
+
+ if (filter->version && !streq(filter->version, e)) {
+ log_debug("Found entry with version string '%s', but was looking for '%s', ignoring entry.", e, filter->version);
+ continue;
+ }
+
+ if (best_filename) { /* Already found one matching entry? Then figure out the better one */
+ int d = 0;
+
+ /* First, prefer entries with tries left over those without */
+ if (FLAGS_SET(flags, PICK_TRIES))
+ d = CMP(found_tries_left != 0, best_tries_left != 0);
+
+ /* Second, prefer newer versions */
+ if (d == 0)
+ d = strverscmp_improved(e, best_version);
+
+ /* Third, prefer native architectures over secondary architectures */
+ if (d == 0 &&
+ FLAGS_SET(flags, PICK_ARCHITECTURE) &&
+ found_architecture_index != SIZE_MAX && best_architecture_index != SIZE_MAX)
+ d = -CMP(found_architecture_index, best_architecture_index);
+
+ /* Fourth, prefer entries with more tries left */
+ if (FLAGS_SET(flags, PICK_TRIES)) {
+ if (d == 0)
+ d = CMP(found_tries_left, best_tries_left);
+
+ /* Fifth, prefer entries with fewer attempts done so far */
+ if (d == 0)
+ d = -CMP(found_tries_done, best_tries_done);
+ }
+
+ /* Last, just compare the filenames as strings */
+ if (d == 0)
+ d = strcmp((*entry)->d_name, best_filename);
+
+ if (d < 0) {
+ log_debug("Found entry '%s' but previously found entry '%s' matches better, hence skipping entry.", (*entry)->d_name, best_filename);
+ continue;
+ }
+ }
+
+ r = free_and_strdup_warn(&best_version, e);
+ if (r < 0)
+ return r;
+
+ r = free_and_strdup_warn(&best_filename, (*entry)->d_name);
+ if (r < 0)
+ return r;
+
+ best_architecture_index = found_architecture_index;
+ best_tries_left = found_tries_left;
+ best_tries_done = found_tries_done;
+ }
+
+ if (!best_filename) { /* Everything was good, but we didn't find any suitable entry */
+ *ret = PICK_RESULT_NULL;
+ return 0;
+ }
+
+ p = path_join(inode_path, best_filename);
+ if (!p)
+ return log_oom_debug();
+
+ object_fd = openat(dir_fd, best_filename, O_CLOEXEC|O_PATH);
+ if (object_fd < 0)
+ return log_debug_errno(errno, "Failed to open '%s/%s': %m", toplevel_path, p);
+
+ return pin_choice(
+ toplevel_path,
+ toplevel_fd,
+ p,
+ TAKE_FD(object_fd),
+ best_tries_left,
+ best_tries_done,
+ &(const PickFilter) {
+ .type_mask = filter->type_mask,
+ .basename = filter->basename,
+ .version = empty_to_null(best_version),
+ .architecture = best_architecture_index != SIZE_MAX ? architectures[best_architecture_index] : _ARCHITECTURE_INVALID,
+ .suffix = filter->suffix,
+ },
+ flags,
+ ret);
+}
+
+int path_pick(const char *toplevel_path,
+ int toplevel_fd,
+ const char *path,
+ const PickFilter *filter,
+ PickFlags flags,
+ PickResult *ret) {
+
+ _cleanup_free_ char *filter_bname = NULL, *dir = NULL, *parent = NULL, *fname = NULL;
+ const char *filter_suffix, *enumeration_path;
+ uint32_t filter_type_mask;
+ int r;
+
+ assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
+ assert(path);
+
+ toplevel_path = strempty(toplevel_path);
+
+ /* Given a path, resolve .v/ subdir logic (if used!), and returns the choice made. This supports
+ * three ways to be called:
+ *
+ * • with a path referring a directory of any name, and filter→basename *explicitly* specified, in
+ * which case we'll use a pattern "<filter→basename>_*<filter→suffix>" on the directory's files.
+ *
+ * • with no filter→basename explicitly specified and a path referring to a directory named in format
+ * "<somestring><filter→suffix>.v" . In this case the filter basename to search for inside the dir
+ * is derived from the directory name. Example: "/foo/bar/baz.suffix.v" → we'll search for
+ * "/foo/bar/baz.suffix.v/baz_*.suffix".
+ *
+ * • with a path whose penultimate component ends in ".v/". In this case the final component of the
+ * path refers to the pattern. Example: "/foo/bar/baz.v/waldo__.suffix" → we'll search for
+ * "/foo/bar/baz.v/waldo_*.suffix".
+ */
+
+ /* Explicit basename specified, then shortcut things and do .v mode regardless of the path name. */
+ if (filter->basename)
+ return make_choice(
+ toplevel_path,
+ toplevel_fd,
+ path,
+ /* inode_fd= */ -EBADF,
+ filter,
+ flags,
+ ret);
+
+ r = path_extract_filename(path, &fname);
+ if (r < 0) {
+ if (r != -EADDRNOTAVAIL) /* root dir or "." */
+ return r;
+
+ /* If there's no path element we can derive a pattern off, the don't */
+ goto bypass;
+ }
+
+ /* Remember if the path ends in a dash suffix */
+ bool slash_suffix = r == O_DIRECTORY;
+
+ const char *e = endswith(fname, ".v");
+ if (e) {
+ /* So a path in the form /foo/bar/baz.v is specified. In this case our search basename is
+ * "baz", possibly with a suffix chopped off if there's one specified. */
+ filter_bname = strndup(fname, e - fname);
+ if (!filter_bname)
+ return -ENOMEM;
+
+ if (filter->suffix) {
+ /* Chop off suffix, if specified */
+ char *f = endswith(filter_bname, filter->suffix);
+ if (f)
+ *f = 0;
+ }
+
+ filter_suffix = filter->suffix;
+ filter_type_mask = filter->type_mask;
+
+ enumeration_path = path;
+ } else {
+ /* The path does not end in '.v', hence see if the last element is a pattern. */
+
+ char *wildcard = strrstr(fname, "___");
+ if (!wildcard)
+ goto bypass; /* Not a pattern, then bypass */
+
+ /* We found the '___' wildcard, hence evertyhing after it is our filter suffix, and
+ * evertyhing before is our filter basename */
+ *wildcard = 0;
+ filter_suffix = empty_to_null(wildcard + 3);
+
+ filter_bname = TAKE_PTR(fname);
+
+ r = path_extract_directory(path, &dir);
+ if (r < 0) {
+ if (!IN_SET(r, -EDESTADDRREQ, -EADDRNOTAVAIL)) /* only filename specified (no dir), or root or "." */
+ return r;
+
+ goto bypass; /* No dir extractable, can't check if parent ends in ".v" */
+ }
+
+ r = path_extract_filename(dir, &parent);
+ if (r < 0) {
+ if (r != -EADDRNOTAVAIL) /* root dir or "." */
+ return r;
+
+ goto bypass; /* Cannot extract fname from parent dir, can't check if it ends in ".v" */
+ }
+
+ e = endswith(parent, ".v");
+ if (!e)
+ goto bypass; /* Doesn't end in ".v", shortcut */
+
+ filter_type_mask = filter->type_mask;
+ if (slash_suffix) {
+ /* If the pattern is suffixed by a / then we are looking for directories apparently. */
+ if (filter_type_mask != 0 && !FLAGS_SET(filter_type_mask, UINT32_C(1) << DT_DIR))
+ return log_debug_errno(SYNTHETIC_ERRNO(errno_from_mode(filter_type_mask, S_IFDIR)),
+ "Specified pattern ends in '/', but not looking for directories, refusing.");
+ filter_type_mask = UINT32_C(1) << DT_DIR;
+ }
+
+ enumeration_path = dir;
+ }
+
+ return make_choice(
+ toplevel_path,
+ toplevel_fd,
+ enumeration_path,
+ /* inode_fd= */ -EBADF,
+ &(const PickFilter) {
+ .type_mask = filter_type_mask,
+ .basename = filter_bname,
+ .version = filter->version,
+ .architecture = filter->architecture,
+ .suffix = filter_suffix,
+ },
+ flags,
+ ret);
+
+bypass:
+ /* Don't make any choice, but just use the passed path literally */
+ return pin_choice(
+ toplevel_path,
+ toplevel_fd,
+ path,
+ /* inode_fd= */ -EBADF,
+ /* tries_left= */ UINT_MAX,
+ /* tries_done= */ UINT_MAX,
+ filter,
+ flags,
+ ret);
+}
+
+int path_pick_update_warn(
+ char **path,
+ const PickFilter *filter,
+ PickFlags flags,
+ PickResult *ret_result) {
+
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+ int r;
+
+ assert(path);
+ assert(*path);
+
+ /* This updates the first argument if needed! */
+
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ *path,
+ filter,
+ flags,
+ &result);
+ if (r == -ENOENT) {
+ log_debug("Path '%s' doesn't exist, leaving as is.", *path);
+ *ret_result = PICK_RESULT_NULL;
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to pick version on path '%s': %m", *path);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No matching entries in versioned directory '%s' found.", *path);
+
+ log_debug("Resolved versioned directory pattern '%s' to file '%s' as version '%s'.", result.path, *path, strna(result.version));
+
+ if (ret_result) {
+ r = free_and_strdup_warn(path, result.path);
+ if (r < 0)
+ return r;
+
+ *ret_result = TAKE_PICK_RESULT(result);
+ } else
+ free_and_replace(*path, result.path);
+
+ return 1;
+}
+
+const PickFilter pick_filter_image_raw = {
+ .type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK),
+ .architecture = _ARCHITECTURE_INVALID,
+ .suffix = ".raw",
+};
+
+const PickFilter pick_filter_image_dir = {
+ .type_mask = UINT32_C(1) << DT_DIR,
+ .architecture = _ARCHITECTURE_INVALID,
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <sys/types.h>
+
+#include "architecture.h"
+
+typedef enum PickFlags {
+ PICK_ARCHITECTURE = 1 << 0, /* Look for an architecture suffix */
+ PICK_TRIES = 1 << 1, /* Look for tries left/tries done counters */
+ PICK_RESOLVE = 1 << 2, /* Return the fully resolved (chased) path, rather than the path to the entry */
+} PickFlags;
+
+typedef struct PickFilter {
+ uint32_t type_mask; /* A mask of 1U << DT_REG, 1U << DT_DIR, … */
+ const char *basename; /* Can be overriden by search pattern */
+ const char *version;
+ Architecture architecture;
+ const char *suffix; /* Can be overriden by search pattern */
+} PickFilter;
+
+typedef struct PickResult {
+ char *path;
+ int fd; /* O_PATH */
+ struct stat st;
+ char *version;
+ Architecture architecture;
+ unsigned tries_left;
+ unsigned tries_done;
+} PickResult;
+
+#define PICK_RESULT_NULL \
+ (const PickResult) { \
+ .fd = -EBADF, \
+ .st.st_mode = MODE_INVALID, \
+ .architecture = _ARCHITECTURE_INVALID, \
+ .tries_left = UINT_MAX, \
+ .tries_done = UINT_MAX, \
+ }
+
+#define TAKE_PICK_RESULT(ptr) TAKE_GENERIC(ptr, PickResult, PICK_RESULT_NULL)
+
+void pick_result_done(PickResult *p);
+
+int path_pick(const char *toplevel_path,
+ int toplevel_fd,
+ const char *path,
+ const PickFilter *filter,
+ PickFlags flags,
+ PickResult *ret);
+
+int path_pick_update_warn(
+ char **path,
+ const PickFilter *filter,
+ PickFlags flags,
+ PickResult *ret);
+
+extern const PickFilter pick_filter_image_raw;
+extern const PickFilter pick_filter_image_dir;
return log_error_errno(errno, "Failed to fstat '%s': %m", node);
if (S_ISBLK(st.st_mode)) {
if (!device) {
- r = sd_device_new_from_devnum(&allocated_device, 'b', st.st_dev);
+ r = sd_device_new_from_devnum(&allocated_device, 'b', st.st_rdev);
if (r < 0)
return log_error_errno(r, "Failed to get device information for device '%s': %m", node);
static void device_hash_func(const struct stat *q, struct siphash *state) {
assert(q);
+ mode_t m = q->st_mode & S_IFMT;
+ siphash24_compress_typesafe(m, state);
+
if (S_ISBLK(q->st_mode) || S_ISCHR(q->st_mode)) {
- mode_t m = q->st_mode & S_IFMT;
- siphash24_compress_typesafe(m, state);
siphash24_compress_typesafe(q->st_rdev, state);
return;
}
.st_mode = S_IFBLK,
};
- r = sd_device_get_devnum(device, &lookup_key.st_rdev);
+ /* MIPS OABI declares st_rdev as unsigned long instead of dev_t.
+ * Use a temp var to avoid passing an incompatible pointer.
+ * https://sourceware.org/bugzilla/show_bug.cgi?id=21278 */
+ dev_t devnum;
+ r = sd_device_get_devnum(device, &devnum);
if (r < 0)
return log_device_error_errno(device, r, "Failed to get major/minor from device: %m");
+ lookup_key.st_rdev = devnum;
if (hashmap_contains(c->subsystems, &lookup_key)) {
log_debug("Device '%s' already seen.", devname);
.st_mode = S_IFBLK,
};
- r = sd_device_get_devnum(device, &lookup_key.st_rdev);
+ /* MIPS OABI declares st_rdev as unsigned long instead of dev_t.
+ * Use a temp var to avoid passing an incompatible pointer.
+ * https://sourceware.org/bugzilla/show_bug.cgi?id=21278 */
+ dev_t devnum;
+ r = sd_device_get_devnum(device, &devnum);
if (r < 0)
return log_device_error_errno(device, r, "Failed to get major/minor from device: %m");
+ lookup_key.st_rdev = devnum;
NvmeSubsystem *s = hashmap_remove(c->subsystems, &lookup_key);
if (!s)
* picked up from an untrusted ESP. Thus, require a stricter policy by default for them. (For the
* other directories we assume the appropriate level of trust was already established already. */
- if (in_initrd() && path_startswith(img->path, "/.extra/sysext/"))
- return &image_policy_sysext_strict;
+ if (in_initrd()) {
+ if (path_startswith(img->path, "/.extra/sysext/"))
+ return &image_policy_sysext_strict;
+ if (path_startswith(img->path, "/.extra/confext/"))
+ return &image_policy_confext_strict;
+
+ /* Better safe than sorry, refuse everything else passed in via the untrusted /.extra/ dir */
+ if (path_startswith(img->path, "/.extra/"))
+ return &image_policy_deny;
+ }
return image_class_info[img->class].default_image_policy;
}
systemd_headers = files(_systemd_headers)
_not_installed_headers = [
+ 'sd-dhcp-client-id.h',
'sd-dhcp-client.h',
+ 'sd-dhcp-duid.h',
'sd-dhcp-lease.h',
'sd-dhcp-option.h',
'sd-dhcp-protocol.h',
+ 'sd-dhcp-server-lease.h',
'sd-dhcp-server.h',
'sd-dhcp6-client.h',
'sd-dhcp6-lease.h',
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcpclientidhfoo
+#define foosddhcpclientidhfoo
+
+/***
+ Copyright © 2013 Intel Corporation. All rights reserved.
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "sd-dhcp-duid.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_dhcp_client_id sd_dhcp_client_id;
+
+int sd_dhcp_client_id_new(sd_dhcp_client_id **ret);
+sd_dhcp_client_id* sd_dhcp_client_id_free(sd_dhcp_client_id *client_id);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_client_id, sd_dhcp_client_id_free);
+
+int sd_dhcp_client_id_clear(sd_dhcp_client_id *client_id);
+
+int sd_dhcp_client_id_is_set(const sd_dhcp_client_id *client_id);
+
+int sd_dhcp_client_id_get(const sd_dhcp_client_id *client_id, uint8_t *ret_type, const void **ret_data, size_t *ret_size);
+int sd_dhcp_client_id_get_raw(const sd_dhcp_client_id *client_id, const void **ret_data, size_t *ret_size);
+
+int sd_dhcp_client_id_set(
+ sd_dhcp_client_id *client_id,
+ uint8_t type,
+ const void *data,
+ size_t data_size);
+int sd_dhcp_client_id_set_raw(
+ sd_dhcp_client_id *client_id,
+ const void *data,
+ size_t data_size);
+int sd_dhcp_client_id_set_iaid_duid(
+ sd_dhcp_client_id *client_id,
+ uint32_t iaid,
+ sd_dhcp_duid *duid);
+
+int sd_dhcp_client_id_to_string(const sd_dhcp_client_id *client_id, char **ret);
+int sd_dhcp_client_id_to_string_from_raw(const void *data, size_t data_size, char **ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
#include <stdbool.h>
#include "sd-device.h"
+#include "sd-dhcp-client-id.h"
#include "sd-dhcp-lease.h"
#include "sd-dhcp-option.h"
#include "sd-event.h"
const uint8_t *bcast_addr,
size_t addr_len,
uint16_t arp_type);
+int sd_dhcp_client_get_client_id(
+ sd_dhcp_client *client,
+ const sd_dhcp_client_id **ret);
int sd_dhcp_client_set_client_id(
sd_dhcp_client *client,
uint8_t type,
__extension__ int sd_dhcp_client_set_rapid_commit(
sd_dhcp_client *client,
bool rapid_commit);
-int sd_dhcp_client_get_client_id(
- sd_dhcp_client *client,
- uint8_t *ret_type,
- const uint8_t **ret_data,
- size_t *ret_data_len);
int sd_dhcp_client_set_mtu(
sd_dhcp_client *client,
uint32_t mtu);
* options when using RFC7844 Anonymity Profiles */
int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize);
-int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret);
-
int sd_dhcp_client_attach_event(
sd_dhcp_client *client,
sd_event *event,
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcpduidhfoo
+#define foosddhcpduidhfoo
+
+/***
+ Copyright © 2013 Intel Corporation. All rights reserved.
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+enum {
+ SD_DUID_TYPE_LLT = 1,
+ SD_DUID_TYPE_EN = 2,
+ SD_DUID_TYPE_LL = 3,
+ SD_DUID_TYPE_UUID = 4
+};
+
+typedef struct sd_dhcp_duid sd_dhcp_duid;
+
+int sd_dhcp_duid_clear(sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size);
+int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size);
+
+int sd_dhcp_duid_set(
+ sd_dhcp_duid *duid,
+ uint16_t duid_type,
+ const void *data,
+ size_t data_size);
+int sd_dhcp_duid_set_raw(
+ sd_dhcp_duid *duid,
+ const void *data,
+ size_t data_size);
+int sd_dhcp_duid_set_llt(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type,
+ uint64_t usec);
+int sd_dhcp_duid_set_ll(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type);
+int sd_dhcp_duid_set_en(sd_dhcp_duid *duid);
+int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid);
+
+int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret);
+
+_SD_END_DECLARATIONS;
+
+#endif
#include <netinet/in.h>
#include <sys/types.h>
+#include "sd-dhcp-client-id.h"
+
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id **ret);
int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
int sd_dhcp_lease_get_6rd(
sd_dhcp_lease *lease,
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#ifndef foosddhcpserverleasehfoo
+#define foosddhcpserverleasehfoo
+
+/***
+ Copyright © 2013 Intel Corporation. All rights reserved.
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <https://www.gnu.org/licenses/>.
+***/
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_dhcp_server_lease sd_dhcp_server_lease;
+
+sd_dhcp_server_lease *sd_dhcp_server_lease_ref(sd_dhcp_server_lease *lease);
+sd_dhcp_server_lease *sd_dhcp_server_lease_unref(sd_dhcp_server_lease *lease);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server_lease, sd_dhcp_server_lease_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
#include <sys/types.h>
#include "sd-device.h"
+#include "sd-dhcp-duid.h"
#include "sd-dhcp6-lease.h"
#include "sd-dhcp6-option.h"
#include "sd-event.h"
int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client);
int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client);
int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len);
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid);
+int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret);
+int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret);
int sd_dhcp6_client_set_iaid(
sd_dhcp6_client *client,
uint32_t iaid);
int sd_dhcp6_client_get_iaid(
sd_dhcp6_client *client,
uint32_t *iaid);
-int sd_dhcp6_client_duid_as_string(
- sd_dhcp6_client *client,
- char **duid);
int sd_dhcp6_client_set_fqdn(
sd_dhcp6_client *client,
const char *fqdn);
int sd_rtnl_message_new_route(sd_netlink *nl, sd_netlink_message **ret, uint16_t nlmsg_type, int rtm_family, unsigned char rtm_protocol);
int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
+int sd_rtnl_message_route_set_tos(sd_netlink_message *m, unsigned char tos);
int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope);
int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags);
int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table);
'test-user-util.c',
'test-utf8.c',
'test-verbs.c',
+ 'test-vpick.c',
'test-web-util.c',
'test-xattr-util.c',
'test-xml.c',
libudev_basic,
],
},
+ test_template + {
+ 'sources' : files('test-aux-scope.c'),
+ 'type' : 'manual',
+ },
]
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "sd-event.h"
+
+#include "bus-error.h"
+#include "bus-message.h"
+#include "bus-wait-for-jobs.h"
+#include "fd-util.h"
+#include "log.h"
+#include "missing_syscall.h"
+#include "process-util.h"
+#include "tests.h"
+
+static int on_sigusr1(sd_event_source *s, const struct signalfd_siginfo *ssi, void *userdata) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL, *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ PidRef *pids = (PidRef *) userdata;
+ const char *job;
+ int r;
+
+ assert(pids);
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire bus: %m");
+
+ r = sd_bus_message_new_method_call(bus, &message,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "StartAuxiliaryScope");
+ if (r < 0)
+ return log_error_errno(r, "Failed to create bus message: %m");
+
+ r = sd_bus_message_append_basic(message, 's', "test-aux-scope.scope");
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach scope name: %m");
+
+ r = sd_bus_message_open_container(message, 'a', "h");
+ if (r < 0)
+ return log_error_errno(r, "Failed to create array of FDs: %m");
+
+ for (size_t i = 0; i < 10; i++) {
+ r = sd_bus_message_append_basic(message, 'h', &pids[i].fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to append PIDFD to message: %m");
+ }
+
+ r = sd_bus_message_close_container(message);
+ if (r < 0)
+ return log_error_errno(r, "Failed to close container: %m");
+
+ r = sd_bus_message_append(message, "ta(sv)", UINT64_C(0), 1, "Description", "s", "Test auxiliary scope");
+ if (r < 0)
+ return log_error_errno(r, "Failed to append unit properties: %m");
+
+ r = sd_bus_call(bus, message, 0, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start auxiliary scope: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "o", &job);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read reply: %m");
+
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+
+ r = bus_wait_for_jobs_one(w, job, false, NULL);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static void destroy_pidrefs(PidRef *pids, size_t npids) {
+ assert(pids || npids == 0);
+
+ for (size_t i = 0; i < npids; i++)
+ pidref_done(&pids[i]);
+
+ free(pids);
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(sd_event_unrefp) sd_event *event = NULL;
+ PidRef *pids = NULL;
+ size_t npids = 0;
+ int r, fd;
+
+ CLEANUP_ARRAY(pids, npids, destroy_pidrefs);
+
+ test_setup_logging(LOG_INFO);
+
+ fd = pidfd_open(getpid_cached(), 0);
+ if (fd < 0 && (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)))
+ return log_tests_skipped("pidfds are not available");
+ else if (fd < 0) {
+ log_error_errno(errno, "pidfd_open() failed: %m");
+ return EXIT_FAILURE;
+ }
+ safe_close(fd);
+
+ r = sd_event_new(&event);
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate event loop: %m");
+ return EXIT_FAILURE;
+ }
+
+ npids = 10;
+ pids = new0(PidRef, npids);
+ assert(pids);
+
+ r = sd_event_add_signal(event, NULL, SIGUSR1|SD_EVENT_SIGNAL_PROCMASK, on_sigusr1, pids);
+ if (r < 0) {
+ log_error_errno(r, "Failed to setup SIGUSR1 signal handling: %m");
+ return EXIT_FAILURE;
+ }
+
+ for (size_t i = 0; i < npids; i++) {
+ PidRef pidref = PIDREF_NULL;
+ pid_t pid;
+
+ r = safe_fork("(worker)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_CLOSE_ALL_FDS, &pid);
+ if (r < 0) {
+ log_error_errno(r, "Failed to fork(): %m");
+ return EXIT_FAILURE;
+ }
+
+ if (r == 0) {
+ /* Worker */
+ sleep(3600);
+ _exit(EXIT_SUCCESS);
+ }
+
+ r = pidref_set_pid(&pidref, pid);
+ if (r < 0) {
+ log_error_errno(r, "Failed to initialize PID ref: %m");
+ return EXIT_FAILURE;
+ }
+
+ assert_se(pidref.pid == pid);
+ assert_se(pidref.fd != -EBADF);
+
+ pids[i] = TAKE_PIDREF(pidref);
+ }
+
+ r = sd_event_loop(event);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run event loop: %m");
+
+ return 0;
+}
test_policy(&image_policy_sysext, "sysext");
test_policy(&image_policy_sysext_strict, "sysext-strict");
test_policy(&image_policy_confext, "confext");
+ test_policy(&image_policy_confext_strict, "confext-strict");
test_policy(&image_policy_container, "container");
test_policy(&image_policy_host, "host");
test_policy(&image_policy_service, "service");
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ false) > 0);
}
+TEST(inode_type_from_string) {
+ static const mode_t types[] = {
+ S_IFREG,
+ S_IFDIR,
+ S_IFLNK,
+ S_IFCHR,
+ S_IFBLK,
+ S_IFIFO,
+ S_IFSOCK,
+ };
+
+ FOREACH_ARRAY(m, types, ELEMENTSOF(types))
+ assert_se(inode_type_from_string(inode_type_to_string(*m)) == *m);
+}
+
static int intro(void) {
log_show_color(true);
return EXIT_SUCCESS;
assert_se(strlevenshtein("sunday", "saturday") == 3);
}
+TEST(strrstr) {
+ assert_se(!strrstr(NULL, NULL));
+ assert_se(!strrstr("foo", NULL));
+ assert_se(!strrstr(NULL, "foo"));
+
+ const char *p = "foo";
+ assert_se(strrstr(p, "foo") == p);
+ assert_se(strrstr(p, "fo") == p);
+ assert_se(strrstr(p, "f") == p);
+ assert_se(strrstr(p, "oo") == p + 1);
+ assert_se(strrstr(p, "o") == p + 2);
+ assert_se(strrstr(p, "") == p + strlen(p));
+ assert_se(!strrstr(p, "bar"));
+
+ p = "xoxoxox";
+ assert_se(strrstr(p, "") == p + strlen(p));
+ assert_se(strrstr(p, "x") == p + 6);
+ assert_se(strrstr(p, "ox") == p + 5);
+ assert_se(strrstr(p, "xo") == p + 4);
+ assert_se(strrstr(p, "xox") == p + 4);
+ assert_se(!strrstr(p, "xx"));
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
assert_se(streq_ptr(strv_find_first_field(STRV_MAKE("i", "k", "l", "m", "d", "c", "a", "b"), haystack), "j"));
}
+TEST(strv_endswith) {
+ assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("xxx", "yyy", "ldo", "zzz")), "ldo"));
+ assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("xxx", "yyy", "zzz")), NULL));
+ assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("waldo")), "waldo"));
+ assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("w", "o", "ldo")), "o"));
+ assert_se(streq_ptr(strv_endswith("waldo", STRV_MAKE("knurz", "", "waldo")), ""));
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+#include "vpick.h"
+
+TEST(path_pick) {
+ _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
+ _cleanup_close_ int dfd = -EBADF, sub_dfd = -EBADF;
+
+ dfd = mkdtemp_open(NULL, O_DIRECTORY|O_CLOEXEC, &p);
+ assert(dfd >= 0);
+
+ sub_dfd = open_mkdir_at(dfd, "foo.v", O_CLOEXEC, 0777);
+ assert(sub_dfd >= 0);
+
+ assert_se(write_string_file_at(sub_dfd, "foo_5.5.raw", "5.5", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "foo_55.raw", "55", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "foo_5.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "foo_5_ia64.raw", "5", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "foo_7.raw", "7", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "foo_7_x86-64.raw", "7 64bit", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "foo_55_x86-64.raw", "55 64bit", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "foo_55_x86.raw", "55 32bit", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "foo_99_x86.raw", "99 32bit", WRITE_STRING_FILE_CREATE) >= 0);
+
+ /* Let's add an entry for sparc (which is a valid arch, but almost certainly not what we test
+ * on). This entry should hence always be ignored */
+ if (native_architecture() != ARCHITECTURE_SPARC)
+ assert_se(write_string_file_at(sub_dfd, "foo_100_sparc.raw", "100 sparc", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(write_string_file_at(sub_dfd, "quux_1_s390.raw", "waldo1", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "quux_2_s390+4-6.raw", "waldo2", WRITE_STRING_FILE_CREATE) >= 0);
+ assert_se(write_string_file_at(sub_dfd, "quux_3_s390+0-10.raw", "waldo3", WRITE_STRING_FILE_CREATE) >= 0);
+
+ _cleanup_free_ char *pp = NULL;
+ pp = path_join(p, "foo.v");
+ assert_se(pp);
+
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+
+ PickFilter filter = {
+ .architecture = _ARCHITECTURE_INVALID,
+ .suffix = ".raw",
+ };
+
+ if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+ assert_se(S_ISREG(result.st.st_mode));
+ assert_se(streq_ptr(result.version, "99"));
+ assert_se(result.architecture == ARCHITECTURE_X86);
+ assert_se(endswith(result.path, "/foo_99_x86.raw"));
+
+ pick_result_done(&result);
+ }
+
+ filter.architecture = ARCHITECTURE_X86_64;
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+ assert_se(S_ISREG(result.st.st_mode));
+ assert_se(streq_ptr(result.version, "55"));
+ assert_se(result.architecture == ARCHITECTURE_X86_64);
+ assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
+ pick_result_done(&result);
+
+ filter.architecture = ARCHITECTURE_IA64;
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+ assert_se(S_ISREG(result.st.st_mode));
+ assert_se(streq_ptr(result.version, "5"));
+ assert_se(result.architecture == ARCHITECTURE_IA64);
+ assert_se(endswith(result.path, "/foo_5_ia64.raw"));
+ pick_result_done(&result);
+
+ filter.architecture = _ARCHITECTURE_INVALID;
+ filter.version = "5";
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+ assert_se(S_ISREG(result.st.st_mode));
+ assert_se(streq_ptr(result.version, "5"));
+ if (native_architecture() != ARCHITECTURE_IA64) {
+ assert_se(result.architecture == _ARCHITECTURE_INVALID);
+ assert_se(endswith(result.path, "/foo_5.raw"));
+ }
+ pick_result_done(&result);
+
+ filter.architecture = ARCHITECTURE_IA64;
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+ assert_se(S_ISREG(result.st.st_mode));
+ assert_se(streq_ptr(result.version, "5"));
+ assert_se(result.architecture == ARCHITECTURE_IA64);
+ assert_se(endswith(result.path, "/foo_5_ia64.raw"));
+ pick_result_done(&result);
+
+ filter.architecture = ARCHITECTURE_CRIS;
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) == 0);
+ assert_se(result.st.st_mode == MODE_INVALID);
+ assert_se(!result.version);
+ assert_se(result.architecture < 0);
+ assert_se(!result.path);
+
+ assert_se(unlinkat(sub_dfd, "foo_99_x86.raw", 0) >= 0);
+
+ filter.architecture = _ARCHITECTURE_INVALID;
+ filter.version = NULL;
+ if (IN_SET(native_architecture(), ARCHITECTURE_X86_64, ARCHITECTURE_X86)) {
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+ assert_se(S_ISREG(result.st.st_mode));
+ assert_se(streq_ptr(result.version, "55"));
+
+ if (native_architecture() == ARCHITECTURE_X86_64) {
+ assert_se(result.architecture == ARCHITECTURE_X86_64);
+ assert_se(endswith(result.path, "/foo_55_x86-64.raw"));
+ } else {
+ assert_se(result.architecture == ARCHITECTURE_X86);
+ assert_se(endswith(result.path, "/foo_55_x86.raw"));
+ }
+ pick_result_done(&result);
+ }
+
+ /* Test explicit patterns in last component of path not being .v */
+ free(pp);
+ pp = path_join(p, "foo.v/foo___.raw");
+ assert_se(pp);
+
+ if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+ assert_se(S_ISREG(result.st.st_mode));
+ assert_se(streq_ptr(result.version, "55"));
+ assert_se(result.architecture == native_architecture());
+ assert_se(endswith(result.path, ".raw"));
+ assert_se(strrstr(result.path, "/foo_55_x86"));
+ pick_result_done(&result);
+ }
+
+ /* Specify an explicit path */
+ free(pp);
+ pp = path_join(p, "foo.v/foo_5.raw");
+ assert_se(pp);
+
+ filter.type_mask = UINT32_C(1) << DT_DIR;
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) == -ENOTDIR);
+
+ filter.type_mask = UINT32_C(1) << DT_REG;
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+ assert_se(S_ISREG(result.st.st_mode));
+ assert_se(!result.version);
+ assert_se(result.architecture == _ARCHITECTURE_INVALID);
+ assert_se(path_equal(result.path, pp));
+ pick_result_done(&result);
+
+ free(pp);
+ pp = path_join(p, "foo.v");
+ assert_se(pp);
+
+ filter.architecture = ARCHITECTURE_S390;
+ filter.basename = "quux";
+
+ assert_se(path_pick(NULL, AT_FDCWD, pp, &filter, PICK_ARCHITECTURE|PICK_TRIES, &result) > 0);
+ assert_se(S_ISREG(result.st.st_mode));
+ assert_se(streq_ptr(result.version, "2"));
+ assert_se(result.tries_left == 4);
+ assert_se(result.tries_done == 6);
+ assert_se(endswith(result.path, "quux_2_s390+4-6.raw"));
+ assert_se(result.architecture == ARCHITECTURE_S390);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
libopenssl,
],
},
+
+ generator_template + {
+ 'name' : 'systemd-tpm2-generator',
+ 'sources' : files('tpm2-generator.c'),
+ },
+
]
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "generator.h"
+#include "proc-cmdline.h"
+#include "special.h"
+#include "tpm2-util.h"
+#include "parse-util.h"
+
+/* A small generator that enqueues tpm2.target as synchronization point if the TPM2 device hasn't shown up
+ * yet, but the firmware reports it to exist. This is supposed to deal with systems where the TPM2 driver
+ * support is built as kmod and must be loaded before it's ready to be used. The tpm2.target is only enqueued
+ * if firmware says there is a TPM2 device, our userspace support for TPM2 is fully available but the TPM2
+ * device hasn't shown up in /dev/ yet. */
+
+static const char *arg_dest = NULL;
+static int arg_tpm2_wait = -1; /* tri-state: negative → don't know */
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+ int r;
+
+ assert(key);
+
+ if (proc_cmdline_key_streq(key, "systemd.tpm2-wait")) {
+ r = value ? parse_boolean(value) : 1;
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse 'systemd.tpm2-wait' kernel command line argument, ignoring: %s", value);
+ else
+ arg_tpm2_wait = r;
+ }
+
+ return 0;
+}
+
+static int generate_tpm_target_symlink(void) {
+ int r;
+
+ if (arg_tpm2_wait == 0) {
+ log_debug("Not generating tpm2.target synchronization point, as this was explicitly turned off via kernel command line.");
+ return 0;
+ }
+
+ if (arg_tpm2_wait < 0) {
+ Tpm2Support support = tpm2_support();
+
+ if (FLAGS_SET(support, TPM2_SUPPORT_DRIVER)) {
+ log_debug("Not generating tpm2.target synchronization point, as TPM2 device is already present.");
+ return 0;
+ }
+
+ if (!FLAGS_SET(support, TPM2_SUPPORT_FIRMWARE)) {
+ log_debug("Not generating tpm2.target synchronization point, as firmware reports no TPM2 present.");
+ return 0;
+ }
+
+ if (!FLAGS_SET(support, TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_LIBRARIES)) {
+ log_debug("Not generating tpm2.target synchronization point, as userspace support for TPM2 is not complete.");
+ return 0;
+ }
+ }
+
+ r = generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_TPM2_TARGET);
+ if (r < 0)
+ return log_error_errno(r, "Failed to hook in tpm2.target: %m");
+
+ return 0;
+}
+
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ int r;
+
+ assert_se(arg_dest = dest);
+
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+
+ return generate_tpm_target_symlink();
+}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
* Copyright (C) 2020 Bastien Nocera <hadess@hadess.net>
*
* Unless specified otherwise, all references are aimed at the "System
- * Management BIOS Reference Specification, Version 3.2.0" document,
+ * Management BIOS Reference Specification, Version 3.7.0" document,
* available from http://www.dmtf.org/standards/smbios.
*
* Note to contributors:
[0x01] = "PC-98/C24 Add-on Card", /* 0xA1 */
[0x02] = "PC-98/E Add-on Card", /* 0xA2 */
[0x03] = "PC-98/Local Bus Add-on Card", /* 0xA3 */
- [0x04] = "CXL Flexbus 1.0", /* 0xA4 */
+ [0x04] = "CXL Add-on Card", /* 0xA4 */
};
const char *str = OUT_OF_SPEC_STR;
[0x1F] = "Logical non-volatile device",
[0x20] = "HBM",
[0x21] = "HBM2",
+ [0x22] = "DDR5",
+ [0x23] = "LPDDR5",
+ [0x24] = "HBM3",
};
printf("MEMORY_DEVICE_%u_TYPE=%s\n", slot_num,
[3] = "Fast-paged",
[4] = "Static Column",
[5] = "Pseudo-static",
- [6] = "RAMBus",
+ [6] = "RAMBUS",
[7] = "Synchronous",
[8] = "CMOS",
[9] = "EDO",
[0x04] = "NVDIMM-N",
[0x05] = "NVDIMM-F",
[0x06] = "NVDIMM-P",
- [0x07] = "Intel Optane DC persistent memory",
+ [0x07] = "Intel Optane persistent memory",
};
printf("MEMORY_DEVICE_%u_MEMORY_TECHNOLOGY=%s\n", slot_num,
#include <unistd.h>
#include "device-private.h"
+#include "device-util.h"
#include "fs-util.h"
#include "log.h"
#include "main-func.h"
if (r < 0)
return log_debug_errno(r, "Failed to open device '%s'", devpath);
- assert_se(event = udev_event_new(dev, 0, NULL, log_get_max_level()));
+ assert_se(event = udev_event_new(dev, NULL));
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD, -1) >= 0);
/* do what devtmpfs usually provides us */
if (sd_device_get_devname(dev, &devname) >= 0) {
- const char *subsystem;
mode_t mode = 0600;
- if (sd_device_get_subsystem(dev, &subsystem) >= 0 && streq(subsystem, "block"))
+ if (device_in_subsystem(dev, "block"))
mode |= S_IFBLK;
else
mode |= S_IFCHR;
}
}
- udev_event_execute_rules(event, -1, 3 * USEC_PER_SEC, SIGKILL, NULL, rules);
- udev_event_execute_run(event, 3 * USEC_PER_SEC, SIGKILL);
+ udev_event_execute_rules(event, rules);
+ udev_event_execute_run(event);
return 0;
}
assert_se(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1) >= 0);
assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net/lo") >= 0);
- assert_se(event = udev_event_new(dev, 0, NULL, LOG_DEBUG));
- assert_se(udev_event_spawn(event, 5 * USEC_PER_SEC, SIGKILL, false, cmd, result_buf, buf_size, NULL) == 0);
+ assert_se(event = udev_event_new(dev, NULL));
+ assert_se(udev_event_spawn(event, false, cmd, result_buf, buf_size, NULL) == 0);
assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
}
srcdev = dev;
for (sd_device *d = srcdev; d; ) {
- const char *dsubsys, *devtype, *modalias = NULL;
-
- if (sd_device_get_subsystem(d, &dsubsys) < 0)
- goto next;
+ const char *modalias = NULL;
/* look only at devices of a specific subsystem */
- if (subsystem && !streq(dsubsys, subsystem))
+ if (subsystem && !device_in_subsystem(d, subsystem))
goto next;
(void) sd_device_get_property_value(d, "MODALIAS", &modalias);
- if (streq(dsubsys, "usb") &&
- sd_device_get_devtype(d, &devtype) >= 0 &&
- streq(devtype, "usb_device")) {
+ if (device_in_subsystem(d, "usb") && device_is_devtype(d, "usb_device")) {
/* if the usb_device does not have a modalias, compose one */
if (!modalias)
modalias = modalias_usb(d, s, sizeof(s));
* safely ignore any virtio buses. see
* http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html */
while (dev) {
- const char *subsystem;
-
- if (sd_device_get_subsystem(dev, &subsystem) < 0)
- break;
-
- if (!streq(subsystem, "virtio"))
+ if (!device_in_subsystem(dev, "virtio"))
break;
if (sd_device_get_parent(dev, &dev) < 0)
return -ENODEV;
}
- if (!strv_isempty(parent_subsystems)) {
- const char *subsystem;
-
- /* check if our direct parent is in an expected subsystem. */
- r = sd_device_get_subsystem(parent, &subsystem);
- if (r < 0)
- return r;
-
- if (!strv_contains(parent_subsystems, subsystem))
- return -ENODEV;
- }
-
- if (ret)
- *ret = parent;
+ /* check if our direct parent is in an expected subsystem. */
+ STRV_FOREACH(s, parent_subsystems)
+ if (device_in_subsystem(parent, *s)) {
+ if (ret)
+ *ret = parent;
+ return 0;
+ }
- return 0;
+ return -ENODEV;
}
static int get_first_syspath_component(sd_device *dev, const char *prefix, char **ret) {
/* handle only ARPHRD_ETHER, ARPHRD_SLIP and ARPHRD_INFINIBAND devices */
switch (iftype) {
case ARPHRD_ETHER: {
- const char *s = NULL;
-
- r = sd_device_get_devtype(dev, &s);
- if (r < 0 && r != -ENOENT)
- return r;
-
- if (streq_ptr(s, "wlan"))
+ if (device_is_devtype(dev, "wlan"))
*ret = "wl";
- else if (streq_ptr(s, "wwan"))
+ else if (device_is_devtype(dev, "wwan"))
*ret = "ww";
else
*ret = "en";
*/
for (parent = dev; ; ) {
- const char *subsystem;
-
- if (sd_device_get_subsystem(parent, &subsystem) < 0)
- break;
-
- if (!streq(subsystem, subsys))
+ if (!device_in_subsystem(parent, subsys))
break;
dev = parent;
}
static sd_device *handle_scsi(sd_device *parent, char **path, char **compat_path, bool *supported_parent) {
- const char *devtype, *id, *name;
+ const char *id, *name;
- if (sd_device_get_devtype(parent, &devtype) < 0 ||
- !streq(devtype, "scsi_device"))
+ if (!device_is_devtype(parent, "scsi_device"))
return parent;
/* firewire */
}
static sd_device *handle_usb(sd_device *parent, char **path) {
- const char *devtype, *str, *port;
+ const char *str, *port;
int r;
- if (sd_device_get_devtype(parent, &devtype) < 0)
- return parent;
- if (!STR_IN_SET(devtype, "usb_interface", "usb_device"))
+ if (!device_is_devtype(parent, "usb_interface") && !device_is_devtype(parent, "usb_device"))
return parent;
if (sd_device_get_sysname(parent, &str) < 0)
_cleanup_(sd_device_unrefp) sd_device *dev_other_branch = NULL;
_cleanup_free_ char *path = NULL, *compat_path = NULL;
bool supported_transport = false, supported_parent = false;
- const char *subsystem;
int r;
/* walk up the chain of devices and compose path */
for (sd_device *parent = dev; parent; ) {
- const char *subsys, *sysname;
+ const char *sysname;
- if (sd_device_get_subsystem(parent, &subsys) < 0 ||
- sd_device_get_sysname(parent, &sysname) < 0) {
+ if (sd_device_get_sysname(parent, &sysname) < 0) {
;
- } else if (streq(subsys, "scsi_tape")) {
+ } else if (device_in_subsystem(parent, "scsi_tape")) {
handle_scsi_tape(parent, &path);
- } else if (streq(subsys, "scsi")) {
+ } else if (device_in_subsystem(parent, "scsi")) {
parent = handle_scsi(parent, &path, &compat_path, &supported_parent);
supported_transport = true;
- } else if (streq(subsys, "cciss")) {
+ } else if (device_in_subsystem(parent, "cciss")) {
parent = handle_cciss(parent, &path);
supported_transport = true;
- } else if (streq(subsys, "usb")) {
+ } else if (device_in_subsystem(parent, "usb")) {
parent = handle_usb(parent, &path);
supported_transport = true;
- } else if (streq(subsys, "bcma")) {
+ } else if (device_in_subsystem(parent, "bcma")) {
parent = handle_bcma(parent, &path);
supported_transport = true;
- } else if (streq(subsys, "serio")) {
+ } else if (device_in_subsystem(parent, "serio")) {
const char *sysnum;
if (sd_device_get_sysnum(parent, &sysnum) >= 0 && sysnum) {
path_prepend(&path, "serio-%s", sysnum);
parent = skip_subsystem(parent, "serio");
}
- } else if (streq(subsys, "pci")) {
+ } else if (device_in_subsystem(parent, "pci")) {
path_prepend(&path, "pci-%s", sysname);
if (compat_path)
path_prepend(&compat_path, "pci-%s", sysname);
parent = skip_subsystem(parent, "pci");
supported_parent = true;
- } else if (streq(subsys, "platform")) {
+ } else if (device_in_subsystem(parent, "platform")) {
path_prepend(&path, "platform-%s", sysname);
if (compat_path)
path_prepend(&compat_path, "platform-%s", sysname);
parent = skip_subsystem(parent, "platform");
supported_transport = true;
supported_parent = true;
- } else if (streq(subsys, "amba")) {
+ } else if (device_in_subsystem(parent, "amba")) {
path_prepend(&path, "amba-%s", sysname);
if (compat_path)
path_prepend(&compat_path, "amba-%s", sysname);
parent = skip_subsystem(parent, "amba");
supported_transport = true;
supported_parent = true;
- } else if (streq(subsys, "acpi")) {
+ } else if (device_in_subsystem(parent, "acpi")) {
path_prepend(&path, "acpi-%s", sysname);
if (compat_path)
path_prepend(&compat_path, "acpi-%s", sysname);
parent = skip_subsystem(parent, "acpi");
supported_parent = true;
- } else if (streq(subsys, "xen")) {
+ } else if (device_in_subsystem(parent, "xen")) {
path_prepend(&path, "xen-%s", sysname);
if (compat_path)
path_prepend(&compat_path, "xen-%s", sysname);
parent = skip_subsystem(parent, "xen");
supported_parent = true;
- } else if (streq(subsys, "virtio")) {
+ } else if (device_in_subsystem(parent, "virtio")) {
parent = skip_subsystem(parent, "virtio");
supported_transport = true;
- } else if (streq(subsys, "scm")) {
+ } else if (device_in_subsystem(parent, "scm")) {
path_prepend(&path, "scm-%s", sysname);
if (compat_path)
path_prepend(&compat_path, "scm-%s", sysname);
parent = skip_subsystem(parent, "scm");
supported_transport = true;
supported_parent = true;
- } else if (streq(subsys, "ccw")) {
+ } else if (device_in_subsystem(parent, "ccw")) {
path_prepend(&path, "ccw-%s", sysname);
if (compat_path)
path_prepend(&compat_path, "ccw-%s", sysname);
parent = skip_subsystem(parent, "ccw");
supported_transport = true;
supported_parent = true;
- } else if (streq(subsys, "ccwgroup")) {
+ } else if (device_in_subsystem(parent, "ccwgroup")) {
path_prepend(&path, "ccwgroup-%s", sysname);
if (compat_path)
path_prepend(&compat_path, "ccwgroup-%s", sysname);
parent = skip_subsystem(parent, "ccwgroup");
supported_transport = true;
supported_parent = true;
- } else if (streq(subsys, "ap")) {
+ } else if (device_in_subsystem(parent, "ap")) {
parent = handle_ap(parent, &path);
supported_transport = true;
supported_parent = true;
- } else if (streq(subsys, "iucv")) {
+ } else if (device_in_subsystem(parent, "iucv")) {
path_prepend(&path, "iucv-%s", sysname);
if (compat_path)
path_prepend(&compat_path, "iucv-%s", sysname);
parent = skip_subsystem(parent, "iucv");
supported_transport = true;
supported_parent = true;
- } else if (STR_IN_SET(subsys, "nvme", "nvme-subsystem")) {
+ } else if (device_in_subsystem(parent, "nvme") || device_in_subsystem(parent, "nvme-subsystem")) {
const char *nsid;
if (sd_device_get_sysattr_value(dev, "nsid", &nsid) >= 0) {
if (compat_path)
path_prepend(&compat_path, "nvme-%s", nsid);
- if (streq(subsys, "nvme-subsystem")) {
+ if (device_in_subsystem(parent, "nvme-subsystem")) {
r = find_real_nvme_parent(dev, &dev_other_branch);
if (r < 0)
return r;
supported_parent = true;
supported_transport = true;
}
- } else if (streq(subsys, "spi")) {
+ } else if (device_in_subsystem(parent, "spi")) {
const char *sysnum;
if (sd_device_get_sysnum(parent, &sysnum) >= 0 && sysnum) {
* devices do not expose their buses and do not provide a unique
* and predictable name that way.
*/
- if (sd_device_get_subsystem(dev, &subsystem) >= 0 &&
- streq(subsystem, "block") &&
- !supported_transport)
+ if (device_in_subsystem(dev, "block") && !supported_transport)
return -ENOENT;
add_id_with_usb_revision(dev, test, path);
size_t l;
char *s;
- const char *syspath, *sysname, *devtype, *interface_syspath;
+ const char *syspath, *sysname, *interface_syspath;
int r;
r = sd_device_get_syspath(dev, &syspath);
return r;
/* shortcut, if we are called directly for a "usb_device" type */
- if (sd_device_get_devtype(dev, &devtype) >= 0 && streq(devtype, "usb_device")) {
+ if (device_is_devtype(dev, "usb_device")) {
dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str));
dev_usb = dev;
goto fallback;
#include "udev-node.h"
#include "udev-trace.h"
#include "udev-util.h"
-#include "udev-watch.h"
#include "user-util.h"
-UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl, int log_level) {
+UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker) {
+ int log_level = worker ? worker->log_level : log_get_max_level();
UdevEvent *event;
assert(dev);
return NULL;
*event = (UdevEvent) {
+ .worker = worker,
+ .rtnl = worker ? sd_netlink_ref(worker->rtnl) : NULL,
.dev = sd_device_ref(dev),
.birth_usec = now(CLOCK_MONOTONIC),
- .exec_delay_usec = exec_delay_usec,
- .rtnl = sd_netlink_ref(rtnl),
.uid = UID_INVALID,
.gid = GID_INVALID,
.mode = MODE_INVALID,
return udev_node_update(dev, event->dev_db_clone);
}
-static int event_execute_rules_on_remove(
- UdevEvent *event,
- int inotify_fd,
- usec_t timeout_usec,
- int timeout_signal,
- Hashmap *properties_list,
- UdevRules *rules) {
-
+static int event_execute_rules_on_remove(UdevEvent *event, UdevRules *rules) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
int r;
if (r < 0)
log_device_debug_errno(dev, r, "Failed to delete database under /run/udev/data/, ignoring: %m");
- r = udev_watch_end(inotify_fd, dev);
- if (r < 0)
- log_device_warning_errno(dev, r, "Failed to remove inotify watch, ignoring: %m");
-
- r = udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list);
+ r = udev_rules_apply_to_event(rules, event);
if (sd_device_get_devnum(dev, NULL) >= 0)
(void) udev_node_remove(dev);
return 0;
}
-int udev_event_execute_rules(
- UdevEvent *event,
- int inotify_fd, /* This may be negative */
- usec_t timeout_usec,
- int timeout_signal,
- Hashmap *properties_list,
- UdevRules *rules) {
-
+int udev_event_execute_rules(UdevEvent *event, UdevRules *rules) {
sd_device_action_t action;
sd_device *dev;
int r;
return log_device_error_errno(dev, r, "Failed to get ACTION: %m");
if (action == SD_DEVICE_REMOVE)
- return event_execute_rules_on_remove(event, inotify_fd, timeout_usec, timeout_signal, properties_list, rules);
-
- /* Disable watch during event processing. */
- r = udev_watch_end(inotify_fd, dev);
- if (r < 0)
- log_device_warning_errno(dev, r, "Failed to remove inotify watch, ignoring: %m");
+ return event_execute_rules_on_remove(event, rules);
r = device_clone_with_db(dev, &event->dev_db_clone);
if (r < 0)
DEVICE_TRACE_POINT(rules_start, dev);
- r = udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list);
+ r = udev_rules_apply_to_event(rules, event);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to apply udev rules: %m");
#include "macro.h"
#include "time-util.h"
#include "udev-rules.h"
+#include "udev-worker.h"
#include "user-util.h"
typedef struct UdevEvent {
+ UdevWorker *worker;
+ sd_netlink *rtnl;
+
sd_device *dev;
sd_device *dev_parent;
sd_device *dev_db_clone;
gid_t gid;
OrderedHashmap *seclabel_list;
OrderedHashmap *run_list;
- usec_t exec_delay_usec;
usec_t birth_usec;
- sd_netlink *rtnl;
unsigned builtin_run;
unsigned builtin_ret;
UdevRuleEscapeType esc:8;
int default_log_level;
} UdevEvent;
-UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl, int log_level);
+UdevEvent *udev_event_new(sd_device *dev, UdevWorker *worker);
UdevEvent *udev_event_free(UdevEvent *event);
DEFINE_TRIVIAL_CLEANUP_FUNC(UdevEvent*, udev_event_free);
-int udev_event_execute_rules(
- UdevEvent *event,
- int inotify_fd,
- usec_t timeout_usec,
- int timeout_signal,
- Hashmap *properties_list,
- UdevRules *rules);
+int udev_event_execute_rules(UdevEvent *event, UdevRules *rules);
const char *attr,
char *dest,
size_t l,
- Hashmap *global_props,
bool *ret_truncated) {
sd_device *parent, *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
case FORMAT_SUBST_ENV:
if (isempty(attr))
return -EINVAL;
- r = device_get_property_value_with_fallback(dev, attr, global_props, &val);
+ r = device_get_property_value_with_fallback(dev, attr, event->worker ? event->worker->properties : NULL, &val);
if (r == -ENOENT)
goto null_terminate;
if (r < 0)
char *dest,
size_t size,
bool replace_whitespace,
- Hashmap *global_props,
bool *ret_truncated) {
bool truncated = false;
continue;
}
- subst_len = udev_event_subst_format(event, type, attr, dest, size, global_props, &t);
+ subst_len = udev_event_subst_format(event, type, attr, dest, size, &t);
if (subst_len < 0) {
log_device_warning_errno(event->dev, subst_len,
"Failed to substitute variable '$%s' or apply format '%%%c', ignoring: %m",
char *dest,
size_t size,
bool replace_whitespace,
- Hashmap *global_props,
bool *ret_truncated);
int udev_check_format(const char *value, size_t *offset, const char **hint);
return 1;
}
+static usec_t extra_timeout_usec(void) {
+ static usec_t saved = 10 * USEC_PER_SEC;
+ static bool parsed = false;
+ const char *e;
+ int r;
+
+ if (parsed)
+ return saved;
+
+ parsed = true;
+
+ e = getenv("SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC");
+ if (!e)
+ return saved;
+
+ r = parse_sec(e, &saved);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_UDEV_EXTRA_TIMEOUT_SEC=%s, ignoring: %m", e);
+
+ return saved;
+}
+
static void worker_attach_event(Worker *worker, Event *event) {
Manager *manager = ASSERT_PTR(ASSERT_PTR(worker)->manager);
sd_event *e = ASSERT_PTR(manager->event);
udev_warn_timeout(manager->timeout_usec), USEC_PER_SEC,
on_event_timeout_warning, event);
+ /* Manager.timeout_usec is also used as the timeout for running programs specified in
+ * IMPORT{program}=, PROGRAM=, or RUN=. Here, let's add an extra time before the manager
+ * kills a worker, to make it possible that the worker detects timed out of spawned programs,
+ * kills them, and finalizes the event. */
(void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
- manager->timeout_usec, USEC_PER_SEC,
+ usec_add(manager->timeout_usec, extra_timeout_usec()), USEC_PER_SEC,
on_event_timeout, event);
}
}
static int device_get_devpath_by_devnum(sd_device *dev, char **ret) {
- const char *subsystem;
dev_t devnum;
int r;
assert(dev);
assert(ret);
- r = sd_device_get_subsystem(dev, &subsystem);
- if (r < 0)
- return r;
-
r = sd_device_get_devnum(dev, &devnum);
if (r < 0)
return r;
- return device_path_make_major_minor(streq(subsystem, "block") ? S_IFBLK : S_IFCHR, devnum, ret);
+ return device_path_make_major_minor(device_in_subsystem(dev, "block") ? S_IFBLK : S_IFCHR, devnum, ret);
}
int udev_node_update(sd_device *dev, sd_device *dev_old) {
switch (token->attr_subst_type) {
case SUBST_TYPE_FORMAT:
- (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, NULL, &truncated);
+ (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "sysfs attribute name", name,
token->type == TK_M_ATTR ? "ATTR" : "ATTRS", /* is_match = */ true);
static int udev_rule_apply_token_to_event(
UdevRuleToken *token,
sd_device *dev,
- UdevEvent *event,
- usec_t timeout_usec,
- int timeout_signal,
- Hashmap *properties_list) {
+ UdevEvent *event) {
int r;
case TK_M_ENV: {
const char *val = NULL;
- (void) device_get_property_value_with_fallback(dev, token->data, properties_list, &val);
+ (void) device_get_property_value_with_fallback(dev, token->data, event->worker ? event->worker->properties : NULL, &val);
return token_match_string(token, val);
}
char buf[UDEV_PATH_SIZE];
bool truncated;
- (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ true);
return false;
struct stat statbuf;
bool match, truncated;
- (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "file name", token->value, "TEST", /* is_match = */ true);
return false;
size_t count;
event->program_result = mfree(event->program_result);
- (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "command", token->value, "PROGRAM", /* is_match = */ true);
return false;
log_event_debug(dev, token, "Running PROGRAM '%s'", buf);
- r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result), NULL);
+ r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof(result), NULL);
if (r != 0) {
if (r < 0)
log_event_warning_errno(dev, token, r, "Failed to execute \"%s\": %m", buf);
char buf[UDEV_PATH_SIZE];
bool truncated;
- (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "file name to be imported", token->value, "IMPORT", /* is_match = */ true);
return false;
char buf[UDEV_LINE_SIZE], result[UDEV_LINE_SIZE];
bool truncated;
- (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "command", token->value, "IMPORT", /* is_match = */ true);
return false;
log_event_debug(dev, token, "Importing properties from results of '%s'", buf);
- r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result, &truncated);
+ r = udev_event_spawn(event, /* accept_failure = */ true, buf, result, sizeof result, &truncated);
if (r != 0) {
if (r < 0)
log_event_warning_errno(dev, token, r, "Failed to execute '%s', ignoring: %m", buf);
event->builtin_run |= mask;
}
- (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "builtin command", token->value, "IMPORT", /* is_match = */ true);
return false;
char buf[UDEV_PATH_SIZE];
bool truncated;
- (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "property name", token->value, "IMPORT", /* is_match = */ true);
return false;
if (token->op == OP_ASSIGN_FINAL)
event->owner_final = true;
- (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "user name", token->value, "OWNER", /* is_match = */ false);
break;
if (token->op == OP_ASSIGN_FINAL)
event->group_final = true;
- (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "group name", token->value, "GROUP", /* is_match = */ false);
break;
if (token->op == OP_ASSIGN_FINAL)
event->mode_final = true;
- (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "mode", token->value, "MODE", /* is_match = */ false);
break;
if (!name)
return log_oom();
- (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "security label", token->value, "SECLABEL", /* is_match = */ false);
break;
}
if (token->op == OP_ADD &&
- device_get_property_value_with_fallback(dev, name, properties_list, &val) >= 0) {
+ device_get_property_value_with_fallback(dev, name, event->worker ? event->worker->properties : NULL, &val) >= 0) {
l = strpcpyl_full(&p, l, &truncated, val, " ", NULL);
if (truncated) {
log_event_warning(dev, token,
}
}
- (void) udev_event_apply_format(event, token->value, p, l, false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, p, l, false, &truncated);
if (truncated) {
_cleanup_free_ char *key_with_name = strjoin("ENV{", name, "}");
log_event_truncated(dev, token, "property value", token->value,
char buf[UDEV_PATH_SIZE];
bool truncated;
- (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "tag name", token->value, "TAG", /* is_match = */ false);
break;
break;
}
- (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "network interface name", token->value, "NAME", /* is_match = */ false);
break;
device_cleanup_devlinks(dev);
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf),
- /* replace_whitespace = */ event->esc != ESCAPE_NONE, properties_list, &truncated);
+ /* replace_whitespace = */ event->esc != ESCAPE_NONE, &truncated);
if (truncated) {
log_event_truncated(dev, token, "symbolic link path", token->value, "SYMLINK", /* is_match = */ false);
break;
log_event_error_errno(dev, token, r, "Could not find file matches '%s', ignoring: %m", buf);
break;
}
- (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "attribute value", token->value, "ATTR", /* is_match = */ false);
break;
char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
bool truncated;
- (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "sysctl entry name", token->data, "SYSCTL", /* is_match = */ false);
break;
}
- (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
if (truncated) {
_cleanup_free_ char *key_with_name = strjoin("SYSCTL{", buf, "}");
log_event_truncated(dev, token, "sysctl value", token->value,
if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
ordered_hashmap_clear_free_key(event->run_list);
- (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, properties_list, &truncated);
+ (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_event_truncated(dev, token, "command", token->value,
token->type == TK_A_RUN_BUILTIN ? "RUN{builtin}" : "RUN{program}",
return token->type >= TK_M_PARENTS_KERNEL && token->type <= TK_M_PARENTS_TAG;
}
-static int udev_rule_apply_parent_token_to_event(
- UdevRuleToken *head_token,
- UdevEvent *event,
- int timeout_signal) {
-
+static int udev_rule_apply_parent_token_to_event(UdevRuleToken *head_token, UdevEvent *event) {
int r;
assert(head_token);
if (!token_is_for_parents(token))
return true; /* All parent tokens match. */
- r = udev_rule_apply_token_to_event(token, event->dev_parent, event, 0, timeout_signal, NULL);
+ r = udev_rule_apply_token_to_event(token, event->dev_parent, event);
if (r < 0)
return r;
if (r == 0)
static int udev_rule_apply_line_to_event(
UdevRuleLine *line,
UdevEvent *event,
- usec_t timeout_usec,
- int timeout_signal,
- Hashmap *properties_list,
UdevRuleLine **next_line) {
UdevRuleLineType mask = LINE_HAS_GOTO | LINE_UPDATE_SOMETHING;
if (parents_done)
continue;
- r = udev_rule_apply_parent_token_to_event(token, event, timeout_signal);
+ r = udev_rule_apply_parent_token_to_event(token, event);
if (r <= 0)
return r;
continue;
}
- r = udev_rule_apply_token_to_event(token, event->dev, event, timeout_usec, timeout_signal, properties_list);
+ r = udev_rule_apply_token_to_event(token, event->dev, event);
if (r <= 0)
return r;
}
return 0;
}
-int udev_rules_apply_to_event(
- UdevRules *rules,
- UdevEvent *event,
- usec_t timeout_usec,
- int timeout_signal,
- Hashmap *properties_list) {
-
+int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event) {
int r;
assert(rules);
LIST_FOREACH(rule_files, file, rules->rule_files)
LIST_FOREACH_WITH_NEXT(rule_lines, line, next_line, file->rule_lines) {
- r = udev_rule_apply_line_to_event(line, event, timeout_usec, timeout_signal, properties_list, &next_line);
+ r = udev_rule_apply_line_to_event(line, event, &next_line);
if (r < 0)
return r;
}
#define udev_rules_free_and_replace(a, b) free_and_replace_full(a, b, udev_rules_free)
bool udev_rules_should_reload(UdevRules *rules);
-int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event,
- usec_t timeout_usec,
- int timeout_signal,
- Hashmap *properties_list);
+int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event);
int udev_rules_apply_static_dev_perms(UdevRules *rules);
ResolveNameTiming resolve_name_timing_from_string(const char *s) _pure_;
usec_t timeout_usec;
int timeout_signal;
usec_t event_birth_usec;
+ usec_t cmd_birth_usec;
bool accept_failure;
int fd_stdout;
int fd_stderr;
if (r < 0)
return log_device_debug_errno(spawn->device, r, "Failed to allocate sd-event object: %m");
- if (spawn->timeout_usec > 0) {
- usec_t usec, age_usec;
-
- usec = now(CLOCK_MONOTONIC);
- age_usec = usec - spawn->event_birth_usec;
- if (age_usec < spawn->timeout_usec) {
- if (spawn->timeout_warn_usec > 0 &&
- spawn->timeout_warn_usec < spawn->timeout_usec &&
- spawn->timeout_warn_usec > age_usec) {
- spawn->timeout_warn_usec -= age_usec;
-
- r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
- usec + spawn->timeout_warn_usec, USEC_PER_SEC,
- on_spawn_timeout_warning, spawn);
- if (r < 0)
- return log_device_debug_errno(spawn->device, r, "Failed to create timeout warning event source: %m");
- }
-
- spawn->timeout_usec -= age_usec;
-
+ if (spawn->timeout_usec != USEC_INFINITY) {
+ if (spawn->timeout_warn_usec < spawn->timeout_usec) {
r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
- usec + spawn->timeout_usec, USEC_PER_SEC, on_spawn_timeout, spawn);
+ usec_add(spawn->cmd_birth_usec, spawn->timeout_warn_usec), USEC_PER_SEC,
+ on_spawn_timeout_warning, spawn);
if (r < 0)
- return log_device_debug_errno(spawn->device, r, "Failed to create timeout event source: %m");
+ return log_device_debug_errno(spawn->device, r, "Failed to create timeout warning event source: %m");
}
+
+ r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
+ usec_add(spawn->cmd_birth_usec, spawn->timeout_usec), USEC_PER_SEC,
+ on_spawn_timeout, spawn);
+ if (r < 0)
+ return log_device_debug_errno(spawn->device, r, "Failed to create timeout event source: %m");
}
if (spawn->fd_stdout >= 0) {
int udev_event_spawn(
UdevEvent *event,
- usec_t timeout_usec,
- int timeout_signal,
bool accept_failure,
const char *cmd,
char *result,
assert(event->dev);
assert(result || result_size == 0);
+ int timeout_signal = event->worker ? event->worker->timeout_signal : SIGKILL;
+ usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
+ usec_t now_usec = now(CLOCK_MONOTONIC);
+ usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
+ usec_t cmd_timeout_usec = usec_sub_unsigned(timeout_usec, age_usec);
+ if (cmd_timeout_usec <= 0)
+ return log_device_warning_errno(event->dev, SYNTHETIC_ERRNO(ETIME),
+ "The event already takes longer (%s) than the timeout (%s), skipping execution of '%s'.",
+ FORMAT_TIMESPAN(age_usec, 1), FORMAT_TIMESPAN(timeout_usec, 1), cmd);
+
/* pipes from child to parent */
if (result || log_get_max_level() >= LOG_INFO)
if (pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) != 0)
.cmd = cmd,
.pid = pid,
.accept_failure = accept_failure,
- .timeout_warn_usec = udev_warn_timeout(timeout_usec),
- .timeout_usec = timeout_usec,
+ .timeout_warn_usec = udev_warn_timeout(cmd_timeout_usec),
+ .timeout_usec = cmd_timeout_usec,
.timeout_signal = timeout_signal,
.event_birth_usec = event->birth_usec,
+ .cmd_birth_usec = now_usec,
.fd_stdout = outpipe[READ_END],
.fd_stderr = errpipe[READ_END],
.result = result,
return r; /* 0 for success, and positive if the program failed */
}
-void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal) {
+void udev_event_execute_run(UdevEvent *event) {
const char *command;
void *val;
int r;
+ assert(event);
+
ORDERED_HASHMAP_FOREACH_KEY(val, command, event->run_list) {
UdevBuiltinCommand builtin_cmd = PTR_TO_UDEV_BUILTIN_CMD(val);
if (r < 0)
log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command);
} else {
- if (event->exec_delay_usec > 0) {
+ if (event->worker && event->worker->exec_delay_usec > 0) {
+ usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
+ usec_t now_usec = now(CLOCK_MONOTONIC);
+ usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
+
+ if (event->worker->exec_delay_usec >= usec_sub_unsigned(timeout_usec, age_usec)) {
+ log_device_warning(event->dev,
+ "Cannot delaying execution of \"%s\" for %s, skipping.",
+ command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC));
+ continue;
+ }
+
log_device_debug(event->dev, "Delaying execution of \"%s\" for %s.",
- command, FORMAT_TIMESPAN(event->exec_delay_usec, USEC_PER_SEC));
- (void) usleep_safe(event->exec_delay_usec);
+ command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC));
+ (void) usleep_safe(event->worker->exec_delay_usec);
}
log_device_debug(event->dev, "Running command \"%s\"", command);
- r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0, NULL);
+ r = udev_event_spawn(event, /* accept_failure = */ false, command, NULL, 0, NULL);
if (r < 0)
log_device_warning_errno(event->dev, r, "Failed to execute '%s', ignoring: %m", command);
else if (r > 0) /* returned value is positive when program fails */
int udev_event_spawn(
UdevEvent *event,
- usec_t timeout_usec,
- int timeout_signal,
bool accept_failure,
const char *cmd,
char *result,
size_t ressize,
bool *ret_truncated);
-void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal);
+void udev_event_execute_run(UdevEvent *event);
static inline usec_t udev_warn_timeout(usec_t timeout_usec) {
if (timeout_usec == USEC_INFINITY)
_cleanup_close_ int dirfd = -EBADF;
int wd, r;
+ assert(inotify_fd >= 0);
assert(dev);
- /* This may be called by 'udevadm test'. In that case, inotify_fd is not initialized. */
- if (inotify_fd < 0)
- return 0;
-
if (sd_device_get_devname(dev, NULL) < 0)
return 0;
if (!device_for_action(dev, SD_DEVICE_ADD))
return 0;
- r = sd_device_get_subsystem(dev, &val);
- if (r < 0)
- return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
-
- if (!streq(val, "block"))
+ if (!device_in_subsystem(dev, "block"))
return 0;
r = sd_device_get_sysname(dev, &val);
log_device_uevent(dev, "Processing device");
- udev_event = udev_event_new(dev, worker->exec_delay_usec, worker->rtnl, worker->log_level);
+ udev_event = udev_event_new(dev, worker);
if (!udev_event)
return -ENOMEM;
if (worker->blockdev_read_only)
(void) worker_mark_block_device_read_only(dev);
+ /* Disable watch during event processing. */
+ r = udev_watch_end(worker->inotify_fd, dev);
+ if (r < 0)
+ log_device_warning_errno(dev, r, "Failed to remove inotify watch, ignoring: %m");
+
/* apply rules, create node, symlinks */
- r = udev_event_execute_rules(
- udev_event,
- worker->inotify_fd,
- worker->timeout_usec,
- worker->timeout_signal,
- worker->properties,
- worker->rules);
+ r = udev_event_execute_rules(udev_event, worker->rules);
if (r < 0)
return r;
- udev_event_execute_run(udev_event, worker->timeout_usec, worker->timeout_signal);
+ udev_event_execute_run(udev_event);
if (!worker->rtnl)
/* in case rtnl was initialized */
goto finish;
}
- event = udev_event_new(dev, 0, NULL, LOG_DEBUG);
+ event = udev_event_new(dev, NULL);
if (!event) {
r = log_oom();
goto finish;
}
r = udev_builtin_run(event, cmd, arg_command, true);
- if (r < 0)
+ if (r < 0) {
log_debug_errno(r, "Builtin command '%s' fails: %m", arg_command);
+ goto finish;
+ }
+ r = 0;
finish:
udev_builtin_exit();
return r;
/* don't read info from the db */
device_seal(dev);
- event = udev_event_new(dev, 0, NULL, LOG_DEBUG);
+ event = udev_event_new(dev, NULL);
assert_se(sigfillset(&mask) >= 0);
assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
- udev_event_execute_rules(event, -1, 60 * USEC_PER_SEC, SIGKILL, NULL, rules);
+ udev_event_execute_rules(event, rules);
FOREACH_DEVICE_PROPERTY(dev, key, value)
printf("%s=%s\n", key, value);
char program[UDEV_PATH_SIZE];
bool truncated;
- (void) udev_event_apply_format(event, cmd, program, sizeof(program), false, NULL, &truncated);
+ (void) udev_event_apply_format(event, cmd, program, sizeof(program), false, &truncated);
if (truncated)
log_warning("The command '%s' is truncated while substituting into '%s'.", program, cmd);
printf("run: '%s'\n", program);
return udevadm_main(argc, argv);
}
-DEFINE_MAIN_FUNCTION(run);
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+executables += [
+ executable_template + {
+ 'name' : 'systemd-vpick',
+ 'public' : true,
+ 'sources' : files('vpick-tool.c'),
+ },
+]
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+
+#include "architecture.h"
+#include "build.h"
+#include "format-table.h"
+#include "fs-util.h"
+#include "main-func.h"
+#include "path-util.h"
+#include "parse-util.h"
+#include "pretty-print.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "vpick.h"
+
+static char *arg_filter_basename = NULL;
+static char *arg_filter_version = NULL;
+static Architecture arg_filter_architecture = _ARCHITECTURE_INVALID;
+static char *arg_filter_suffix = NULL;
+static uint32_t arg_filter_type_mask = 0;
+static enum {
+ PRINT_PATH,
+ PRINT_FILENAME,
+ PRINT_VERSION,
+ PRINT_TYPE,
+ PRINT_ARCHITECTURE,
+ PRINT_TRIES,
+ PRINT_ALL,
+ _PRINT_INVALID = -EINVAL,
+} arg_print = _PRINT_INVALID;
+static PickFlags arg_flags = PICK_ARCHITECTURE|PICK_TRIES;
+
+STATIC_DESTRUCTOR_REGISTER(arg_filter_basename, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_filter_version, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_filter_suffix, freep);
+
+static int help(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-vpick", "1", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%1$s [OPTIONS...] PATH...\n"
+ "\n%5$sPick entry from versioned directory.%6$s\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ "\n%3$sLookup Keys:%4$s\n"
+ " -B --basename=BASENAME\n"
+ " Look for specified basename\n"
+ " -V VERSION Look for specified version\n"
+ " -A ARCH Look for specified architecture\n"
+ " -S --suffix=SUFFIX Look for specified suffix\n"
+ " -t --type=TYPE Look for specified inode type\n"
+ "\n%3$sOutput:%4$s\n"
+ " -p --print=filename Print selected filename rather than path\n"
+ " -p --print=version Print selected version rather than path\n"
+ " -p --print=type Print selected inode type rather than path\n"
+ " -p --print=arch Print selected architecture rather than path\n"
+ " -p --print=tries Print selected tries left/tries done rather than path\n"
+ " -p --print=all Print all of the above\n"
+ " --resolve=yes Canonicalize the result path\n"
+ "\nSee the %2$s for details.\n",
+ program_invocation_short_name,
+ link,
+ ansi_underline(), ansi_normal(),
+ ansi_highlight(), ansi_normal());
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_RESOLVE,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "basename", required_argument, NULL, 'B' },
+ { "suffix", required_argument, NULL, 'S' },
+ { "type", required_argument, NULL, 't' },
+ { "print", required_argument, NULL, 'p' },
+ { "resolve", required_argument, NULL, ARG_RESOLVE },
+ {}
+ };
+
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "hB:V:A:S:t:p:", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case 'h':
+ return help();
+
+ case ARG_VERSION:
+ return version();
+
+ case 'B':
+ if (!filename_part_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid basename string: %s", optarg);
+
+ r = free_and_strdup_warn(&arg_filter_basename, optarg);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case 'V':
+ if (!version_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version string: %s", optarg);
+
+ r = free_and_strdup_warn(&arg_filter_version, optarg);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case 'A':
+ if (streq(optarg, "native"))
+ arg_filter_architecture = native_architecture();
+ else if (streq(optarg, "secondary")) {
+#ifdef ARCHITECTURE_SECONDARY
+ arg_filter_architecture = ARCHITECTURE_SECONDARY;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Local architecture has no secondary architecture.");
+#endif
+ } else if (streq(optarg, "uname"))
+ arg_filter_architecture = uname_architecture();
+ else if (streq(optarg, "auto"))
+ arg_filter_architecture = _ARCHITECTURE_INVALID;
+ else {
+ arg_filter_architecture = architecture_from_string(optarg);
+ if (arg_filter_architecture < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown architecture: %s", optarg);
+ }
+ break;
+
+ case 'S':
+ if (!filename_part_is_valid(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid suffix string: %s", optarg);
+
+ r = free_and_strdup_warn(&arg_filter_suffix, optarg);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case 't':
+ if (isempty(optarg))
+ arg_filter_type_mask = 0;
+ else {
+ mode_t m;
+
+ m = inode_type_from_string(optarg);
+ if (m == MODE_INVALID)
+ return log_error_errno(m, "Unknown inode type: %s", optarg);
+
+ arg_filter_type_mask |= UINT32_C(1) << IFTODT(m);
+ }
+
+ break;
+
+ case 'p':
+ if (streq(optarg, "path"))
+ arg_print = PRINT_PATH;
+ else if (streq(optarg, "filename"))
+ arg_print = PRINT_FILENAME;
+ else if (streq(optarg, "version"))
+ arg_print = PRINT_VERSION;
+ else if (streq(optarg, "type"))
+ arg_print = PRINT_TYPE;
+ else if (STR_IN_SET(optarg, "arch", "architecture"))
+ arg_print = PRINT_ARCHITECTURE;
+ else if (streq(optarg, "tries"))
+ arg_print = PRINT_TRIES;
+ else if (streq(optarg, "all"))
+ arg_print = PRINT_ALL;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown --print= argument: %s", optarg);
+
+ break;
+
+ case ARG_RESOLVE:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --resolve= value: %m");
+
+ SET_FLAG(arg_flags, PICK_RESOLVE, r);
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+ }
+
+ if (arg_print < 0)
+ arg_print = PRINT_PATH;
+
+ return 1;
+}
+
+static int run(int argc, char *argv[]) {
+ int r;
+
+ log_show_color(true);
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ if (optind >= argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path to resolve must be specified.");
+
+ for (int i = optind; i < argc; i++) {
+ _cleanup_free_ char *p = NULL;
+ r = path_make_absolute_cwd(argv[i], &p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make path '%s' absolute: %m", argv[i]);
+
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ p,
+ &(PickFilter) {
+ .basename = arg_filter_basename,
+ .version = arg_filter_version,
+ .architecture = arg_filter_architecture,
+ .suffix = arg_filter_suffix,
+ .type_mask = arg_filter_type_mask,
+ },
+ arg_flags,
+ &result);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pick version for '%s': %m", p);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "No matching version for '%s' found.", p);
+
+ switch (arg_print) {
+
+ case PRINT_PATH:
+ fputs(result.path, stdout);
+ if (result.st.st_mode != MODE_INVALID && S_ISDIR(result.st.st_mode) && !endswith(result.path, "/"))
+ fputc('/', stdout);
+ fputc('\n', stdout);
+ break;
+
+ case PRINT_FILENAME: {
+ _cleanup_free_ char *fname = NULL;
+
+ r = path_extract_filename(result.path, &fname);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from path '%s': %m", result.path);
+
+ puts(fname);
+ break;
+ }
+
+ case PRINT_VERSION:
+ if (!result.version)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No version information discovered.");
+
+ puts(result.version);
+ break;
+
+ case PRINT_TYPE:
+ if (result.st.st_mode == MODE_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No inode type information discovered.");
+
+ puts(inode_type_to_string(result.st.st_mode));
+ break;
+
+ case PRINT_ARCHITECTURE:
+ if (result.architecture < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No architecture information discovered.");
+
+ puts(architecture_to_string(result.architecture));
+ break;
+
+ case PRINT_TRIES:
+ if (result.tries_left == UINT_MAX)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No tries left/tries done information discovered.");
+
+ printf("+%u-%u", result.tries_left, result.tries_done);
+ break;
+
+ case PRINT_ALL: {
+ _cleanup_(table_unrefp) Table *t = NULL;
+
+ t = table_new_vertical();
+ if (!t)
+ return log_oom();
+
+ table_set_ersatz_string(t, TABLE_ERSATZ_NA);
+
+ r = table_add_many(
+ t,
+ TABLE_FIELD, "Path",
+ TABLE_PATH, result.path,
+ TABLE_FIELD, "Version",
+ TABLE_STRING, result.version,
+ TABLE_FIELD, "Type",
+ TABLE_STRING, result.st.st_mode == MODE_INVALID ? NULL : inode_type_to_string(result.st.st_mode),
+ TABLE_FIELD, "Architecture",
+ TABLE_STRING, result.architecture < 0 ? NULL : architecture_to_string(result.architecture));
+ if (r < 0)
+ return table_log_add_error(r);
+
+ if (result.tries_left != UINT_MAX) {
+ r = table_add_many(
+ t,
+ TABLE_FIELD, "Tries left",
+ TABLE_UINT, result.tries_left,
+ TABLE_FIELD, "Tries done",
+ TABLE_UINT, result.tries_done);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ r = table_print(t, stdout);
+ if (r < 0)
+ return table_log_print_error(r);
+
+ break;
+ }
+
+ default:
+ assert_not_reached();
+ }
+ }
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
+# (Hopefully) a temporary workaround for https://github.com/systemd/systemd/issues/30573
+KERNEL_APPEND="${KERNEL_APPEND:-} SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST=100"
+
test_append_files() {
local workspace="${1:?}"
+++ /dev/null
-#!/usr/bin/env python3
-# SPDX-License-Identifier: LGPL-2.1-or-later
-#
-# Copyright © 2017 Michal Sekletar <msekleta@redhat.com>
-
-# ATTENTION: This uses the *installed* systemd, not the one from the built
-# source tree.
-
-import os
-import subprocess
-import sys
-import time
-import unittest
-import uuid
-from enum import Enum
-
-class InstallChange(Enum):
- NO_CHANGE = 0
- LINES_SWAPPED = 1
- COMMAND_ADDED_BEFORE = 2
- COMMAND_ADDED_AFTER = 3
- COMMAND_INTERLEAVED = 4
- REMOVAL = 5
-
-class ExecutionResumeTest(unittest.TestCase):
- def setUp(self):
- self.unit = 'test-issue-518.service'
- self.unitfile_path = f'/run/systemd/system/{self.unit}'
- self.output_file = f"/tmp/test-issue-518-{uuid.uuid4()}"
- self.unit_files = {}
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/sleep 3
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- '''
- self.unit_files[InstallChange.NO_CHANGE] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- ExecStart=/bin/sleep 3
- '''
- self.unit_files[InstallChange.LINES_SWAPPED] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
- ExecStart=/bin/sleep 3
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- '''
- self.unit_files[InstallChange.COMMAND_ADDED_BEFORE] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/sleep 3
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
- '''
- self.unit_files[InstallChange.COMMAND_ADDED_AFTER] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/bash -c "echo baz >>{self.output_file}"
- ExecStart=/bin/sleep 3
- ExecStart=/bin/bash -c "echo foo >>{self.output_file}"
- ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
- '''
- self.unit_files[InstallChange.COMMAND_INTERLEAVED] = unit_file_content
-
- unit_file_content = f'''
- [Service]
- Type=oneshot
- ExecStart=/bin/bash -c "echo bar >>{self.output_file}"
- ExecStart=/bin/bash -c "echo baz >>{self.output_file}"
- '''
- self.unit_files[InstallChange.REMOVAL] = unit_file_content
-
- def reload(self):
- subprocess.check_call(['systemctl', 'daemon-reload'])
-
- def write_unit_file(self, unit_file_change):
- if not isinstance(unit_file_change, InstallChange):
- raise ValueError('Unknown unit file change')
-
- content = self.unit_files[unit_file_change]
-
- with open(self.unitfile_path, 'w', encoding='utf-8') as f:
- f.write(content)
-
- self.reload()
-
- def check_output(self, expected_output):
- for _ in range(15):
- # Wait until the unit finishes so we don't check an incomplete log
- if subprocess.call(['systemctl', '-q', 'is-active', self.unit]) == 0:
- continue
-
- os.sync()
-
- try:
- with open(self.output_file, 'r', encoding='utf-8') as log:
- output = log.read()
- self.assertEqual(output, expected_output)
- return
- except IOError:
- pass
-
- time.sleep(1)
-
- self.fail(f'Timed out while waiting for the output file {self.output_file} to appear')
-
- def setup_unit(self):
- self.write_unit_file(InstallChange.NO_CHANGE)
- subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', self.unit])
- time.sleep(1)
-
- def test_no_change(self):
- expected_output = 'foo\n'
-
- self.setup_unit()
- self.reload()
-
- self.check_output(expected_output)
-
- def test_swapped(self):
- self.setup_unit()
- self.write_unit_file(InstallChange.LINES_SWAPPED)
- self.reload()
-
- self.assertTrue(not os.path.exists(self.output_file))
-
- def test_added_before(self):
- expected_output = 'foo\n'
-
- self.setup_unit()
- self.write_unit_file(InstallChange.COMMAND_ADDED_BEFORE)
- self.reload()
-
- self.check_output(expected_output)
-
- def test_added_after(self):
- expected_output = 'foo\nbar\n'
-
- self.setup_unit()
- self.write_unit_file(InstallChange.COMMAND_ADDED_AFTER)
- self.reload()
-
- self.check_output(expected_output)
-
- def test_interleaved(self):
- expected_output = 'foo\nbar\n'
-
- self.setup_unit()
- self.write_unit_file(InstallChange.COMMAND_INTERLEAVED)
- self.reload()
-
- self.check_output(expected_output)
-
- def test_removal(self):
- self.setup_unit()
- self.write_unit_file(InstallChange.REMOVAL)
- self.reload()
-
- self.assertTrue(not os.path.exists(self.output_file))
-
- def test_issue_6533(self):
- unit = "test-issue-6533.service"
- unitfile_path = f"/run/systemd/system/{unit}"
-
- content = '''
- [Service]
- ExecStart=/bin/sleep 5
- '''
-
- with open(unitfile_path, 'w', encoding='utf-8') as f:
- f.write(content)
-
- self.reload()
-
- subprocess.check_call(['systemctl', '--job-mode=replace', '--no-block', 'start', unit])
- time.sleep(2)
-
- content = '''
- [Service]
- ExecStart=/bin/sleep 5
- ExecStart=/bin/true
- '''
-
- with open(unitfile_path, 'w', encoding='utf-8') as f:
- f.write(content)
-
- self.reload()
- time.sleep(5)
-
- self.assertNotEqual(subprocess.call("journalctl -b _PID=1 | grep -q 'Freezing execution'", shell=True), 0)
-
- def tearDown(self):
- for f in [self.output_file, self.unitfile_path]:
- try:
- os.remove(f)
- except OSError:
- # ignore error if log file doesn't exist
- pass
-
- self.reload()
-
-if __name__ == '__main__':
- unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=3))
image_install kpartx /lib/udev/kpartx_id lsmod mpathpersist multipath multipathd partx
image_install "${ROOTLIBDIR:?}"/system/multipathd.{service,socket}
if get_bool "$LOOKS_LIKE_DEBIAN"; then
- inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-multipath.rules 68-del-part-nodes.rules 95-kpartx.rules
+ # Note: try both 60-kpartx.rules (as seen on Debian Sid with 0.9.4-7) and 90-kpartx.rules (as seen on
+ # Ubuntu Jammy with 0.8.8-1ubuntu1.22.04.4)
+ inst_rules 56-dm-parts.rules 56-dm-mpath.rules 60-kpartx.rules 60-multipath.rules 68-del-part-nodes.rules 90-kpartx.rules
else
inst_rules 11-dm-mpath.rules 11-dm-parts.rules 62-multipath.rules 66-kpartx.rules 68-del-part-nodes.rules
fi
# Show messages from the testsuite-XX.service or messages with priority "warning" and higher
echo " --- $source_dir ---"
"$JOURNALCTL" --no-pager --no-hostname -o short-monotonic -D "$source_dir" \
- _SYSTEMD_UNIT="testsuite-${TESTID:?}.service" + PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0
+ _SYSTEMD_UNIT="testsuite-${TESTID:?}.service" + SYSLOG_IDENTIFIER="testsuite-$TESTID.sh" + \
+ PRIORITY=4 + PRIORITY=3 + PRIORITY=2 + PRIORITY=1 + PRIORITY=0
if get_bool "$save"; then
# If we don't have systemd-journal-remote copy all journals from /var/log/journal/
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+pre=test05
+cat >/run/systemd/system/"$pre"alpha.slice <<EOF
+[Slice]
+MemoryMax=40M
+MemoryHigh=40M
+TasksMax=400
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta.slice <<EOF
+[Slice]
+MemoryMax=10M
+MemoryHigh=10M
+TasksMax=100
+EOF
+
+cat >/run/systemd/system/"$pre"alpha-beta-gamma.slice <<EOF
+[Slice]
+MemoryMax=20M
+MemoryHigh=20M
+TasksMax=200
+EOF
+
+systemctl daemon-reload
+
+srv=probe.service
+slc0="$pre"alpha.slice
+slc="$pre"alpha-beta-gamma.slice
+
+systemd-run --unit "$srv" --slice "$slc" \
+ -p MemoryMax=5M \
+ -p MemoryHigh=5M \
+ -p TasksMax=50 \
+ sleep inf
+
+# Compare with inequality because test can run in a constrained container
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+systemd-run --unit "$srv" --slice "$slc" \
+ sleep inf
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "10485760"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "100"
+
+systemctl set-property "$slc0" \
+ MemoryMax=5M \
+ MemoryHigh=5M \
+ TasksMax=50
+
+assert_le "$(systemctl show -P EffectiveMemoryMax "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveMemoryHigh "$srv")" "5242880"
+assert_le "$(systemctl show -P EffectiveTasksMax "$srv")" "50"
+
+systemctl stop "$srv"
+
+rm -f /run/systemd/system/"$pre"* || :
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+P=/run/systemd/system.conf.d
+mkdir $P
+
+cat >$P/rlimits.conf <<EOF
+[Manager]
+DefaultLimitNOFILE=10000:16384
+EOF
+
+systemctl daemon-reload
+
+[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
+[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
+
+[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
+[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
+
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
+# shellcheck disable=SC2016
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
-Description=TEST-05-RLIMITS
+Description=TEST-05-LIMITS
[Service]
ExecStartPre=rm -f /failed /testok
set -eux
set -o pipefail
-P=/run/systemd/system.conf.d
-mkdir $P
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
-cat >$P/rlimits.conf <<EOF
-[Manager]
-DefaultLimitNOFILE=10000:16384
-EOF
-
-systemctl daemon-reload
-
-[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
-[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
-
-[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
-[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
-
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
-# shellcheck disable=SC2016
-systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
+run_subtests
touch /testok
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+export SYSTEMD_PAGER=cat
+
+if ! grep -q pidfd_open /proc/kallsyms; then
+ echo "pidfds not available, skipping the test..."
+ exit 0
+fi
+
+systemd-run --unit test-aux-scope.service \
+ -p Slice=aux.slice -p Type=exec -p TasksMax=99 -p CPUWeight=199 -p IPAccounting=yes \
+ /usr/lib/systemd/tests/unit-tests/manual/test-aux-scope
+kill -s USR1 "$(systemctl show --value --property MainPID test-aux-scope.service)"
+
+sleep 1
+
+systemctl status test-aux-scope.service
+# shellcheck disable=SC2009
+test "$(ps -eo pid,unit | grep -c test-aux-scope.service)" = 1
+
+systemctl status test-aux-scope.scope
+# shellcheck disable=SC2009
+test "$(ps -eo pid,unit | grep -c test-aux-scope.scope)" = 10
+
+test "$(systemctl show -p Slice --value test-aux-scope.scope)" = aux.slice
+test "$(systemctl show -p TasksMax --value test-aux-scope.scope)" = 99
+test "$(systemctl show -p CPUWeight --value test-aux-scope.scope)" = 199
+test "$(systemctl show -p IPAccounting --value test-aux-scope.scope)" = yes
+
+systemctl stop test-aux-scope.scope
+systemctl stop test-aux-scope.service
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# shellcheck disable=SC2016
+set -eux
+set -o pipefail
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+setup_base_unit() {
+ local unit_path="${1:?}"
+ local log_file="${2:?}"
+ local unit_name="${unit_path##*/}"
+
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ systemctl --job-mode=replace --no-block start "$unit_name"
+ # Wait until the unit leaves the "inactive" state
+ timeout 5s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" == inactive ]]; do sleep .1; done"
+ # Sleep for 1 second from the unit start to get well "into" the first (or second) ExecStart= directive
+ sleep 1
+}
+
+check_output() {
+ local unit_name="${1:?}"
+ local log_file="${2:?}"
+ local expected="${3?}"
+ local unit_name="${unit_path##*/}"
+
+ # Wait until the unit becomes inactive before checking the log
+ timeout 10s bash -xec "while [[ \"\$(systemctl show -P ActiveState $unit_name)\" != inactive ]]; do sleep .5; done"
+
+ diff "$log_file" <(echo -ne "$expected")
+}
+
+testcase_no_change() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-no-change-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Simple sanity test without any reordering shenanignans, to check if the base unit works as expected.
+ check_output "$unit_path" "$log_file" "foo\n"
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_swapped() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-swapped-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Swap the two ExecStart= lines.
+ #
+ # Since we should be in the first "sleep" of the base unit, after replacing the unit with the following
+ # one we should continue running from the respective "ExecStart=sleep 3" line, which is now the last
+ # one, resulting no output in the final log file.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=sleep 3
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" ""
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_added_before() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-added-before-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Add one new ExecStart= before the existing ones.
+ #
+ # Since, after reload, we should continue running from the "sleep 3" statement, the newly added "echo
+ # bar" one will have no efect and we should end up with the same output as in the previous case.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo bar >>$log_file"
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" "foo\n"
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_added_after() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-added-after-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Add an ExecStart= line after the existing ones.
+ #
+ # Same case as above, except the newly added ExecStart= should get executed, as it was added after the
+ # "sleep 3" statement.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=bash -c "echo bar >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" "foo\nbar\n"
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_interleaved() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-interleaved-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Combination of the two previous cases.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo baz >>$log_file"
+ExecStart=sleep 3
+ExecStart=bash -c "echo foo >>$log_file"
+ExecStart=bash -c "echo bar >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" "foo\nbar\n"
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_removal() {
+ local unit_path log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-removal-XXX.service)"
+ log_file="$(mktemp)"
+
+ setup_base_unit "$unit_path" "$log_file"
+
+ # Remove the currently executed ExecStart= line.
+ #
+ # In this case we completely drop the currently excuted "sleep 3" statement, so after reload systemd
+ # should complain that the currently executed command vanished and simply finish executing the unit,
+ # resulting in an empty log.
+ cat >"$unit_path" <<EOF
+[Service]
+Type=oneshot
+ExecStart=bash -c "echo bar >>$log_file"
+ExecStart=bash -c "echo baz >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" ""
+
+ rm -f "$unit_path" "$log_file"
+}
+
+testcase_issue_6533() {
+ local unit_path unit_name log_file
+
+ unit_path="$(mktemp /run/systemd/system/test-deserialization-issue-6533-XXX.service)"
+ unit_name="${unit_path##*/}"
+ log_file="$(mktemp)"
+
+ cat >"$unit_path" <<EOF
+[Service]
+Type=simple
+ExecStart=/bin/sleep 5
+EOF
+ systemctl daemon-reload
+
+ systemctl --job-mode=replace --no-block start "$unit_name"
+ sleep 2
+
+ # Make sure we try to execute the next command only for oneshot services, as for other types we allow
+ # only one ExecStart= directive.
+ #
+ # See: https://github.com/systemd/systemd/issues/6533
+ cat >"$unit_path" <<EOF
+[Service]
+Type=simple
+ExecStart=/bin/sleep 5
+ExecStart=bash -c "echo foo >>$log_file"
+EOF
+ systemctl daemon-reload
+
+ check_output "$unit_path" "$log_file" ""
+ (! journalctl -b --grep "Freezing execution" _PID=1)
+}
+
+mkdir -p /run/systemd/system/
+run_testcases
+systemctl daemon-reload
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
-set -ex
+set -eux
+TMPDIR=
TEST_RULE="/run/udev/rules.d/49-test.rules"
KILL_PID=
[[ -e /etc/udev/udev.conf ]] && cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bak
cat >"${TEST_RULE}" <<EOF
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
-ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", PROGRAM=="/bin/sleep 60"
+ACTION!="add", GOTO="test_end"
+SUBSYSTEM!="mem", GOTO="test_end"
+KERNEL!="null", GOTO="test_end"
+
+OPTIONS="log_level=debug"
+PROGRAM=="/bin/touch /tmp/test-udev-marker"
+PROGRAM!="/bin/sleep 60", ENV{PROGRAM_RESULT}="KILLED"
+
+LABEL="test_end"
EOF
cat >/etc/udev/udev.conf <<EOF
event_timeout=10
systemctl restart systemd-udevd.service
}
-run_test() {
- local since
+run_test_timeout() {
+ TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
+ udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
+ KILL_PID="$!"
+
+ SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
+
+ for _ in {1..40}; do
+ if grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt; then
+ sleep .5
+ kill "$KILL_PID"
+ KILL_PID=
+
+ cat "$TMPDIR"/monitor.txt
+ (! grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt)
+ (! grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt)
+ (! grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt)
+ grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt
+ rm -rf "$TMPDIR"
+ return 0
+ fi
+ sleep .5
+ done
+
+ return 1
+}
- since="$(date '+%F %T')"
+run_test_killed() {
+ local killed=
TMPDIR=$(mktemp -d -p /tmp udev-tests.XXXXXX)
udevadm monitor --udev --property --subsystem-match=mem >"$TMPDIR"/monitor.txt &
KILL_PID="$!"
+ rm -f /tmp/test-udev-marker
SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null
for _ in {1..40}; do
- if coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then
+ if [[ -z "$killed" ]]; then
+ if [[ -e /tmp/test-udev-marker ]]; then
+ killall --signal ABRT --regexp udev-worker
+ killed=1
+ fi
+ elif grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt; then
+ sleep .5
kill "$KILL_PID"
KILL_PID=
grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt
grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt
grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt
+ (! grep -q 'PROGRAM_RESULT=KILLED' "$TMPDIR"/monitor.txt)
+ rm -rf "$TMPDIR"
return 0
fi
sleep .5
trap teardown EXIT
setup
-run_test
+run_test_timeout
+run_test_killed
exit 0
udevadm wait --settle /sys/class/net/$netdev
udevadm wait -h
+udevadm lock --help
+udevadm lock --version
+for i in /dev/block/*; do
+ udevadm lock --device "$i" --print
+ udevadm lock --device "$i" true
+ (! udevadm lock --device "$i" false)
+done
+for i in / /usr; do
+ udevadm lock --backing "$i" --print
+ udevadm lock --backing "$i" true
+ (! udevadm lock --backing "$i" false)
+done
+
exit 0
fi
}
+add_suppression() {
+ local interface="${1:?}"
+ local suppression="${2:?}"
+
+ sed -i "\%\[$interface\]%a$suppression" /etc/dfuzzer.conf
+}
+
trap at_exit EXIT
systemctl log-level info
# FIXME: systemd-run doesn't play well with daemon-reexec
# See: https://github.com/systemd/systemd/issues/27204
-sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:Reexecute FIXME' /etc/dfuzzer.conf
+add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:Reexecute FIXME"
+
+add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Manager:SoftReboot destructive"
+add_suppression "org.freedesktop.login1" "Sleep destructive"
+
+# Skip calling start and stop methods on unit objects, as doing that is not only time consuming, but it also
+# starts/stops units that interfere with the machine state. The actual code paths should be covered (to some
+# degree) by the respective method counterparts on the manager object.
+for method in Start Stop Restart ReloadOrRestart ReloadOrTryRestart Kill; do
+ add_suppression "org.freedesktop.systemd1" "org.freedesktop.systemd1.Unit:$method"
+done
-sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:SoftReboot destructive' /etc/dfuzzer.conf
-sed -i '/\[org.freedesktop.login1\]/aSleep destructive' /etc/dfuzzer.conf
+cat /etc/dfuzzer.conf
# TODO
# * check for possibly newly introduced buses?
rm -rf /run/extensions/app-reject
rm /var/lib/extensions/app-nodistro.raw
+# Some super basic test that RootImage= works with .v/ dirs
+VBASE="vtest$RANDOM"
+VDIR="/tmp/${VBASE}.v"
+mkdir "$VDIR"
+
+ln -s "${image}.raw" "$VDIR/${VBASE}_33.raw"
+ln -s "${image}.raw" "$VDIR/${VBASE}_34.raw"
+ln -s "${image}.raw" "$VDIR/${VBASE}_35.raw"
+
+systemd-run -P -p RootImage="$VDIR" cat /usr/lib/os-release | grep -q -F "MARKER=1"
+
+rm "$VDIR/${VBASE}_33.raw" "$VDIR/${VBASE}_34.raw" "$VDIR/${VBASE}_35.raw"
+rmdir "$VDIR"
+
mkdir -p /run/machines /run/portables /run/extensions
touch /run/machines/a.raw /run/portables/b.raw /run/extensions/c.raw
if ! check_device_unit "$log_level" "$path"; then
return 1
fi
- done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ { print $1 }' | sed -e 's/\.device$//')
+ done < <(systemctl list-units --all --type=device --no-legend dev-* | awk '$1 !~ /dev-tty.+/ && $4 == "plugged" { print $1 }' | sed -e 's/\.device$//')
return 0
)}
udevadm control --reload
+ # initialize partition table
+ for disk in {0..9}; do
+ echo 'label: gpt' | udevadm lock --device="${devices[$disk]}" sfdisk -q "${devices[$disk]}"
+ done
+
# Delete the partitions, immediately recreate them, wait for udev to settle
# down, and then check if we have any dangling symlinks in /dev/disk/. Rinse
# and repeat.
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+at_exit() {
+ set +e
+ rm -rf /var/lib/machines/mymachine.raw.v
+ rm -rf /var/lib/machines/mytree.v
+ rm -rf /var/lib/machines/testroot.v
+ umount -l /tmp/dotvroot
+ rmdir /tmp/dotvroot
+}
+
+trap at_exit EXIT
+
+mkdir -p /var/lib/machines/mymachine.raw.v
+
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw
+touch /var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw
+
+mkdir -p /var/lib/machines/mytree.v
+
+mkdir /var/lib/machines/mytree.v/mytree_33.4
+mkdir /var/lib/machines/mytree.v/mytree_33.5
+mkdir /var/lib/machines/mytree.v/mytree_36.0+0-5
+mkdir /var/lib/machines/mytree.v/mytree_37.0_arm64+2-3
+mkdir /var/lib/machines/mytree.v/mytree_38.0_arm64+0-5
+
+ARCH="$(busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager Architecture | cut -d\" -f 2)"
+
+export SYSTEMD_LOG_LEVEL=debug
+
+if [ "$ARCH" = "x86-64" ] ; then
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.7.0_x86-64+0-5.raw"
+
+ systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+elif [ "$ARCH" = "arm64" ] ; then
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+ systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+else
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+
+ test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.13)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.5.14)
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.6.0)
+ (! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -V 7.7.0)
+
+ systemd-dissect --discover | grep "/var/lib/machines/mymachine.raw.v/mymachine_7.5.13.raw"
+fi
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A x86-64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.5.14_x86-64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A ia64)
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p version)" = "7.6.0"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p type)" = "reg"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p filename)" = "mymachine_7.6.0_arm64.raw"
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -p arch)" = "arm64"
+
+test "$(systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t reg)" = "/var/lib/machines/mymachine.raw.v/mymachine_7.6.0_arm64.raw"
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t dir)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t fifo)
+(! systemd-vpick /var/lib/machines/mymachine.raw.v --suffix=.raw -A arm64 -t sock)
+
+if [ "$ARCH" != "arm64" ] ; then
+ test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+ test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_33.5/"
+else
+ test "$(systemd-vpick /var/lib/machines/mytree.v)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+ test "$(systemd-vpick /var/lib/machines/mytree.v --type=dir)" = "/var/lib/machines/mytree.v/mytree_37.0_arm64+2-3/"
+fi
+
+(! systemd-vpick /var/lib/machines/mytree.v --type=reg)
+
+mkdir /tmp/dotvroot
+mount --bind / /tmp/dotvroot
+
+mkdir /var/lib/machines/testroot.v
+mkdir /var/lib/machines/testroot.v/testroot_32
+ln -s /tmp/dotvroot /var/lib/machines/testroot.v/testroot_33
+mkdir /var/lib/machines/testroot.v/testroot_34
+
+ls -l /var/lib/machines/testroot.v
+
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_34/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+find /var/lib/machines/testroot.v/testroot_34
+rm -rf /var/lib/machines/testroot.v/testroot_34
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_33/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /tmp/dotvroot/
+systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true
+
+rm /var/lib/machines/testroot.v/testroot_33
+test "$(systemd-vpick /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+test "$(systemd-vpick --resolve=yes /var/lib/machines/testroot.v)" = /var/lib/machines/testroot.v/testroot_32/
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
+
+rm -rf /var/lib/machines/testroot.v/testroot_32
+(! systemd-vpick /var/lib/machines/testroot.v)
+(! systemd-run --wait -p RootDirectory=/var/lib/machines/testroot.v /bin/true)
fi
)}
+assert_le() {(
+ set +ex
+
+ if [[ "${1:?}" -gt "${2:?}" ]]; then
+ echo "FAIL: '$1' > '$2'" >&2
+ exit 1
+ fi
+)}
+
assert_in() {(
set +ex
'file' : 'tmp.mount',
'symlinks' : ['local-fs.target.wants/'],
},
+ { 'file' : 'tpm2.target' },
{ 'file' : 'umount.target' },
{ 'file' : 'usb-gadget.target' },
{ 'file' : 'user-runtime-dir@.service.in' },
Description=TPM2 PCR Extension (Varlink)
Documentation=man:systemd-pcrextend(8)
DefaultDependencies=no
+After=tpm2.target
Before=sockets.target
ConditionSecurity=measured-uki
Description=TPM2 PCR Extension (Varlink)
Documentation=man:systemd-pcrphase.service(8)
DefaultDependencies=no
+After=tpm2.target
Conflicts=shutdown.target initrd-switch-root.target
Before=shutdown.target initrd-switch-root.target
Documentation=man:systemd-pcrfs-root.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
-After=systemd-pcrmachine.service
+After=tpm2.target systemd-pcrmachine.service
Before=shutdown.target
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
DefaultDependencies=no
BindsTo=%i.mount
Conflicts=shutdown.target
-After=%i.mount systemd-pcrfs-root.service
+After=%i.mount tpm2.target systemd-pcrfs-root.service
Before=shutdown.target
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
Documentation=man:systemd-pcrmachine.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
+After=tpm2.target
Before=sysinit.target shutdown.target
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
Documentation=man:systemd-pcrphase-initrd.service(8)
DefaultDependencies=no
Conflicts=shutdown.target initrd-switch-root.target
+After=tpm2.target
Before=sysinit.target cryptsetup-pre.target cryptsetup.target shutdown.target initrd-switch-root.target systemd-sysext.service
ConditionPathExists=/etc/initrd-release
ConditionSecurity=measured-uki
Documentation=man:systemd-pcrphase-sysinit.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
-After=sysinit.target
+After=sysinit.target tpm2.target
Before=basic.target shutdown.target
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
[Unit]
Description=TPM2 PCR Barrier (User)
Documentation=man:systemd-pcrphase.service(8)
-After=remote-fs.target remote-cryptsetup.target
+After=remote-fs.target remote-cryptsetup.target tpm2.target
Before=systemd-user-sessions.service
ConditionPathExists=!/etc/initrd-release
ConditionSecurity=measured-uki
Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients
DefaultDependencies=no
-After=systemd-sysusers.service
+After=systemd-sysctl.service systemd-sysusers.service
Before=sysinit.target network.target nss-lookup.target shutdown.target initrd-switch-root.target
Conflicts=shutdown.target initrd-switch-root.target
Wants=nss-lookup.target
Before=sysinit.target shutdown.target
ConditionSecurity=measured-uki
ConditionPathExists=!/run/systemd/tpm2-srk-public-key.pem
+After=tpm2.target
[Service]
Type=oneshot
RequiresMountsFor=/var/lib/systemd/tpm2-srk-public-key.pem
ConditionSecurity=measured-uki
ConditionPathExists=!/etc/initrd-release
+After=tpm2.target
[Service]
Type=oneshot
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Trusted Platform Module
+Documentation=man:systemd.special(7)
+
+# Make this a synchronization point on the first TPM device found
+After=dev-tpmrm0.device
+Wants=dev-tpmrm0.device