summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS90
-rw-r--r--README8
-rw-r--r--TODO7
-rw-r--r--docs/CODING_STYLE.md8
-rw-r--r--docs/DESKTOP_ENVIRONMENTS.md15
-rw-r--r--docs/ENVIRONMENT.md9
-rw-r--r--docs/THE_CASE_FOR_THE_USR_MERGE.md2
-rw-r--r--docs/TRANSIENT-SETTINGS.md1
-rw-r--r--man/nss-myhostname.xml11
-rw-r--r--man/oomd.conf.xml3
-rw-r--r--man/org.freedesktop.machine1.xml2
-rw-r--r--man/org.freedesktop.systemd1.xml42
-rw-r--r--man/org.freedesktop.sysupdate1.xml4
-rw-r--r--man/systemd-nspawn.xml69
-rw-r--r--man/systemd-resolved.service.xml11
-rw-r--r--man/systemd.generator.xml9
-rw-r--r--man/systemd.network.xml32
-rw-r--r--man/systemd.resource-control.xml23
-rw-r--r--man/systemd.unit.xml2
-rw-r--r--mkosi.conf.d/10-debian-ubuntu/mkosi.conf4
-rw-r--r--mkosi.conf.d/10-opensuse/mkosi.conf4
-rw-r--r--mkosi.conf.d/10-ubuntu/mkosi.conf.d/non-x86.conf4
-rw-r--r--mkosi.conf.d/10-ubuntu/mkosi.conf.d/x86.conf4
-rw-r--r--mkosi.images/build/mkosi.conf.d/opensuse/mkosi.conf4
-rw-r--r--rules.d/60-persistent-storage.rules.in4
-rw-r--r--rules.d/99-systemd.rules.in2
-rw-r--r--shell-completion/bash/busctl13
-rw-r--r--src/analyze/analyze-dump.c56
-rw-r--r--src/analyze/analyze-malloc.c3
-rw-r--r--src/analyze/analyze.c17
-rw-r--r--src/analyze/analyze.h2
-rw-r--r--src/basic/alloc-util.h5
-rw-r--r--src/basic/constants.h2
-rw-r--r--src/basic/fileio.c8
-rw-r--r--src/basic/fileio.h18
-rw-r--r--src/basic/fs-util.c4
-rw-r--r--src/basic/fs-util.h6
-rw-r--r--src/basic/namespace-util.h6
-rw-r--r--src/basic/pidref.c57
-rw-r--r--src/basic/pidref.h53
-rw-r--r--src/basic/process-util.h8
-rw-r--r--src/basic/socket-util.h4
-rw-r--r--src/basic/stat-util.c22
-rw-r--r--src/basic/sysctl-util.c27
-rw-r--r--src/basic/sysctl-util.h2
-rw-r--r--src/boot/efi/log.c9
-rw-r--r--src/boot/efi/log.h22
-rw-r--r--src/boot/efi/smbios.c43
-rw-r--r--src/boot/efi/util.h2
-rw-r--r--src/busctl/busctl.c87
-rw-r--r--src/core/bpf-restrict-ifaces.c2
-rw-r--r--src/core/bpf-socket-bind.c2
-rw-r--r--src/core/cgroup.c7
-rw-r--r--src/core/cgroup.h1
-rw-r--r--src/core/core-varlink.c8
-rw-r--r--src/core/dbus-cgroup.c31
-rw-r--r--src/core/dbus-service.c2
-rw-r--r--src/core/execute-serialize.c8
-rw-r--r--src/core/load-fragment-gperf.gperf.in1053
-rw-r--r--src/core/load-fragment.c38
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/manager.c66
-rw-r--r--src/core/mount.c14
-rw-r--r--src/core/namespace.c4
-rw-r--r--src/core/service.c12
-rw-r--r--src/core/unit.c16
-rw-r--r--src/debug-generator/debug-generator.c2
-rw-r--r--src/fundamental/sha256-fundamental.c8
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c30
-rw-r--r--src/hibernate-resume/hibernate-resume-generator.c5
-rw-r--r--src/journal/journalctl.c80
-rw-r--r--src/journal/journalctl.h16
-rw-r--r--src/libsystemd-network/radv-internal.h112
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c8
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c46
-rw-r--r--src/libsystemd-network/sd-radv.c691
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c5
-rw-r--r--src/libsystemd-network/test-ndisc-ra.c210
-rw-r--r--src/libsystemd/libsystemd.sym2
-rw-r--r--src/libsystemd/sd-json/json-util.c209
-rw-r--r--src/libsystemd/sd-json/json-util.h7
-rw-r--r--src/libsystemd/sd-json/sd-json.c66
-rw-r--r--src/libsystemd/sd-network/network-util.h12
-rw-r--r--src/libsystemd/sd-varlink/sd-varlink.c20
-rw-r--r--src/machine/image-dbus.c68
-rw-r--r--src/machine/image-dbus.h1
-rw-r--r--src/machine/image-varlink.c88
-rw-r--r--src/machine/image-varlink.h6
-rw-r--r--src/machine/machine-varlink.c58
-rw-r--r--src/machine/machine-varlink.h24
-rw-r--r--src/machine/machined-core.c80
-rw-r--r--src/machine/machined-varlink.c167
-rw-r--r--src/machine/machined.h2
-rw-r--r--src/machine/meson.build1
-rw-r--r--src/network/networkd-address.c36
-rw-r--r--src/network/networkd-dhcp-common.c2
-rw-r--r--src/network/networkd-dhcp6.c75
-rw-r--r--src/network/networkd-link.c14
-rw-r--r--src/network/networkd-link.h3
-rw-r--r--src/network/networkd-ndisc.c4
-rw-r--r--src/network/networkd-radv.c169
-rw-r--r--src/network/networkd-route-util.c17
-rw-r--r--src/network/networkd-routing-policy-rule.c1
-rw-r--r--src/network/networkd-sysctl.c103
-rw-r--r--src/network/networkd-sysctl.h1
-rw-r--r--src/nspawn/nspawn.c4
-rw-r--r--src/oom/oomctl.c10
-rw-r--r--src/oom/oomd-manager.c44
-rw-r--r--src/oom/oomd-util.c79
-rw-r--r--src/oom/oomd-util.h9
-rw-r--r--src/oom/oomd.c44
-rw-r--r--src/oom/test-oomd-util.c16
-rw-r--r--src/resolve/resolved-bus.c4
-rw-r--r--src/resolve/resolved-manager.c3
-rw-r--r--src/run/run.c7
-rw-r--r--src/shared/bus-message-util.c36
-rw-r--r--src/shared/bus-message-util.h3
-rw-r--r--src/shared/bus-print-properties.c6
-rw-r--r--src/shared/bus-unit-util.c5
-rw-r--r--src/shared/discover-image.h4
-rw-r--r--src/shared/dissect-image.c13
-rw-r--r--src/shared/dropin.c40
-rw-r--r--src/shared/exec-util.c100
-rw-r--r--src/shared/exec-util.h33
-rw-r--r--src/shared/fdset.c16
-rw-r--r--src/shared/fdset.h5
-rw-r--r--src/shared/fileio-label.c6
-rw-r--r--src/shared/fileio-label.h9
-rw-r--r--src/shared/generator.c23
-rw-r--r--src/shared/generator.h2
-rw-r--r--src/shared/local-addresses.c134
-rw-r--r--src/shared/local-addresses.h1
-rw-r--r--src/shared/meson.build1
-rw-r--r--src/shared/mount-util.c12
-rw-r--r--src/shared/mount-util.h9
-rw-r--r--src/shared/userdb.c8
-rw-r--r--src/shared/varlink-io.systemd.Machine.c2
-rw-r--r--src/shared/varlink-io.systemd.MachineImage.c69
-rw-r--r--src/shared/varlink-io.systemd.MachineImage.h6
-rw-r--r--src/shared/varlink-io.systemd.Resolve.c24
-rw-r--r--src/shared/varlink-io.systemd.oom.c3
-rw-r--r--src/sysext/sysext.c174
-rw-r--r--src/systemd/sd-json.h1
-rw-r--r--src/systemd/sd-radv.h108
-rw-r--r--src/systemd/sd-varlink.h2
-rw-r--r--src/sysupdate/sysupdate.c4
-rw-r--r--src/sysupdate/sysupdated.c6
-rw-r--r--src/sysupdate/updatectl.c20
-rw-r--r--src/test/test-exec-util.c4
-rw-r--r--src/test/test-json.c120
-rw-r--r--src/test/test-local-addresses.c93
-rw-r--r--src/test/test-pidref.c38
-rw-r--r--src/test/test-varlink-idl.c3
-rw-r--r--src/test/test-varlink.c57
-rw-r--r--src/tmpfiles/tmpfiles.c2
-rw-r--r--src/udev/udev-watch.c3
-rw-r--r--src/udev/udev-worker.c25
-rw-r--r--src/ukify/mypy.ini3
-rwxr-xr-xsrc/ukify/test/test_ukify.py2
-rwxr-xr-xsrc/ukify/ukify.py312
-rw-r--r--src/update-done/update-done.c2
-rw-r--r--test/README.testsuite2
-rw-r--r--test/fuzz/fuzz-network-parser/oss-fuzz-372994449bin0 -> 4740 bytes
-rw-r--r--test/fuzz/fuzz-unit-file/directives-all.service1
-rw-r--r--test/test-functions1
-rwxr-xr-xtest/units/TEST-13-NSPAWN.machined.sh58
-rwxr-xr-xtest/units/TEST-17-UDEV.database.sh22
-rwxr-xr-xtest/units/TEST-50-DISSECT.dissect.sh6
-rwxr-xr-xtest/units/TEST-50-DISSECT.sysext.sh21
-rw-r--r--test/units/TEST-55-OOMD-workload.slice2
-rw-r--r--test/units/TEST-55-OOMD-workload.slice.d/99-oom.conf7
-rwxr-xr-xtest/units/TEST-55-OOMD.sh221
-rwxr-xr-xtest/units/TEST-58-REPART.sh3
-rwxr-xr-xtest/units/TEST-60-MOUNT-RATELIMIT.sh137
-rwxr-xr-xtest/units/TEST-64-UDEV-STORAGE.sh10
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.busctl.sh4
-rwxr-xr-xtest/units/TEST-74-AUX-UTILS.run.sh12
-rwxr-xr-xtest/units/TEST-82-SOFTREBOOT.sh6
178 files changed, 4078 insertions, 2768 deletions
diff --git a/NEWS b/NEWS
index 51b067b239..d4e74d8551 100644
--- a/NEWS
+++ b/NEWS
@@ -16,47 +16,9 @@ CHANGES WITH 257 in spe:
LoadCredentialEncrypted= service setting. Previously it could only
read raw binary data.
- Announcements of Future Feature Removals and Incompatible Changes:
-
- * To work around limitations of X11's keyboard handling systemd's
- keyboard mapping hardware database (hwdb.d/60-keyboard.hwdb) so far
- mapped the microphone mute and touchpad on/off/toggle keys to the
- function keys F20, F21, F22, F23 instead of their correct key
- codes. This key code mangling will be removed in the next systemd
- release v258. To maintain compatibility with X11 applications that
- rely on the old function key code mappings, this mangling has now
- been moved to the relevant X11 keyboard driver modules instead. Thus,
- in order to ensure these keys continue to work as before make sure to
- update the xf86-input-evdev and xf86-input-libinput packages to the
- newest version before updating systemd to v258.
-
* Support for automatic flushing of the nscd user/group database caches
has been dropped.
- * Support for cgroup v1 ('legacy' and 'hybrid' hierarchies) is now
- considered obsolete and systemd by default will refuse to boot under
- it. To forcibly reenable cgroup v1 support,
- SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 must be set on kernel command
- line. The meson option 'default-hierarchy=' is also deprecated, i.e.
- only cgroup v2 ('unified' hierarchy) can be selected as build-time
- default.
-
- * Support for System V service scripts is deprecated and will be
- removed in a future release. Please make sure to update your software
- *now* to include a native systemd unit file instead of a legacy
- System V script to retain compatibility with future systemd releases.
-
- * Support for the SystemdOptions EFI variable is deprecated.
- 'bootctl systemd-efi-options' will emit a warning when used. It seems
- that this feature is little-used and it is better to use alternative
- approaches like credentials and confexts. The plan is to drop support
- altogether at a later point, but this might be revisited based on
- user feedback.
-
- * systemd-run's switch --expand-environment= which currently is disabled
- by default when combined with --scope, will be changed in a future
- release to be enabled by default.
-
* The FileDescriptorName= setting for socket units is now honored by
Accept=yes sockets too, where it was previously silently ignored and
"connection" was used unconditionally.
@@ -78,8 +40,52 @@ CHANGES WITH 257 in spe:
filesystem. $SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=0 can be used to override
this behavior.
+ Announcements of Future Feature Removals:
+
* D-Bus method org.freedesktop.systemd1.StartAuxiliaryScope() becomes
- deprecated (reach out if you have use cases).
+ deprecated because accounting data and such cannot be reasonably
+ migrated between cgroups. It might be fully removed in a future release
+ (reach out if you have use cases).
+
+ * The recommended kernel baseline version has been bumped to v5.4
+ (released in 2019). Expect limited testing on older kernel versions,
+ where "old-kernel" taint flag would also be set. Support for them
+ will be phased out in a future release in 2025, i.e. we expect to bump
+ the minimum baseline to v5.4 then too.
+
+ * Support for cgroup v1 ('legacy' and 'hybrid' hierarchies) is now
+ considered obsolete and systemd by default will refuse to boot under
+ it. To forcibly reenable cgroup v1 support,
+ SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 must be set on kernel command
+ line. The complete removal of cgroup v1 is scheduled for v258.
+
+ * Support for System V service scripts is deprecated and will be
+ removed in v258. Please make sure to update your software
+ *now* to include a native systemd unit file instead of a legacy
+ System V script to retain compatibility with future systemd releases.
+
+ * To work around limitations of X11's keyboard handling systemd's
+ keyboard mapping hardware database (hwdb.d/60-keyboard.hwdb) so far
+ mapped the microphone mute and touchpad on/off/toggle keys to the
+ function keys F20, F21, F22, F23 instead of their correct key
+ codes. This key code mangling will be removed in the next systemd
+ release v258. To maintain compatibility with X11 applications that
+ rely on the old function key code mappings, this mangling has now
+ been moved to the relevant X11 keyboard driver modules instead. Thus,
+ in order to ensure these keys continue to work as before make sure to
+ update the xf86-input-evdev and xf86-input-libinput packages to the
+ newest version before updating systemd to v258.
+
+ * Support for the SystemdOptions EFI variable is deprecated.
+ 'bootctl systemd-efi-options' will emit a warning when used. It seems
+ that this feature is little-used and it is better to use alternative
+ approaches like credentials and confexts. The plan is to drop support
+ altogether at a later point, but this might be revisited based on
+ user feedback.
+
+ * systemd-run's switch --expand-environment= which currently is disabled
+ by default when combined with --scope, will be changed in a future
+ release to be enabled by default.
libsystemd:
@@ -14075,7 +14081,7 @@ CHANGES WITH 218:
or are not older than the specified time.
* A new, native PPPoE library has been added to sd-network,
- systemd's library of light-weight networking protocols. This
+ systemd's library of lightweight networking protocols. This
library will be used in a future version of networkd to
enable PPPoE communication without an external pppd daemon.
@@ -14922,7 +14928,7 @@ CHANGES WITH 214:
have been added. When enabled, they will make the user data
(such as /home) inaccessible or read-only and the system
(such as /usr) read-only, for specific services. This allows
- very light-weight per-service sandboxing to avoid
+ very lightweight per-service sandboxing to avoid
modifications of user data or system files from
services. These two new switches have been enabled for all
of systemd's long-running services, where appropriate.
@@ -15631,7 +15637,7 @@ CHANGES WITH 209:
activation files automatically into native systemd .busname
and .service units.
- * sd-bus: add a light-weight vtable implementation that allows
+ * sd-bus: add a lightweight vtable implementation that allows
defining objects on the bus with a simple static const
vtable array of its methods, signals and properties.
diff --git a/README b/README
index 7c7bbaf070..9b84bf7e7a 100644
--- a/README
+++ b/README
@@ -39,16 +39,16 @@ REQUIREMENTS:
≥ 4.17 for cgroup-bpf socket address hooks
≥ 4.20 for PSI (used by systemd-oomd)
≥ 5.3 for bounded loops in BPF program
- ≥ 5.4 for signed Verity images
- ≥ 5.7 for BPF links and the BPF LSM hook
+ ≥ 5.4 for pidfd and signed Verity images
+ ≥ 5.7 for CLONE_INTO_CGROUP, BPF links and the BPF LSM hook
⛔ Kernel versions below 3.15 ("minimum baseline") are not supported at
all, and are missing required functionality (e.g. CLOCK_BOOTTIME
support for timerfd_create()).
- ⚠️ Kernel versions below 4.15 ("recommended baseline") have significant
+ ⚠️ Kernel versions below 5.4 ("recommended baseline") have significant
gaps in functionality and are not recommended for use with this version
- of systemd (e.g. lack sufficiently comprehensive and working cgroupv2
+ of systemd (e.g. lack race-free process tracking by pidfd and new mount API
support). Taint flag 'old-kernel' will be set. systemd will most likely
still function, but upstream support and testing are limited.
diff --git a/TODO b/TODO
index 6c3f029245..1f8f33c46f 100644
--- a/TODO
+++ b/TODO
@@ -129,8 +129,11 @@ Deprecations and removals:
Features:
-* add a generic varlink dispatcher for pidfd/pidinode/pid to PidRef handling
- (and formatter?)
+* port remaining getmntent() users over to libmount. There are subtle
+ differences in the parsers (see #25371 for example), and it hence makes sense
+ if we stick to one set of parsers on this, not mix both.
+
+* run0 and run0 --user=root have different effect on tty ownership?
* get rid of compat with libidn.so.11 (retain only for libidn.so.12)
diff --git a/docs/CODING_STYLE.md b/docs/CODING_STYLE.md
index 82ed0a553c..48fa4b093d 100644
--- a/docs/CODING_STYLE.md
+++ b/docs/CODING_STYLE.md
@@ -591,6 +591,14 @@ SPDX-License-Identifier: LGPL-2.1-or-later
important for objects that unprivileged users may allocate, but also matters
for everything else any user may allocate.
+- Please use `secure_getenv()` for all environment variable accesses, unless
+ it's clear that `getenv()` would be the better choice. This matters in
+ particular in `src/basic/` and `src/shared/` (i.e. library code that might
+ end up in unexpected processes), but should be followed everywhere else too
+ (in order to make it unproblematic to move code around). To say this clearly:
+ the default should be `secure_getenv()`, the exception should be regular
+ `getenv()`.
+
## Types
- Think about the types you use. If a value cannot sensibly be negative, do not
diff --git a/docs/DESKTOP_ENVIRONMENTS.md b/docs/DESKTOP_ENVIRONMENTS.md
index ebf4694934..8a62eb0054 100644
--- a/docs/DESKTOP_ENVIRONMENTS.md
+++ b/docs/DESKTOP_ENVIRONMENTS.md
@@ -65,11 +65,11 @@ desktop environments should adhere to the following conventions:
instead of the caller starting the process and letting systemd know about it,
is encouraged.
- * The RANDOM should be a string of random characters to ensure that multiple instances
- of the application can be launched.
-
- It can be omitted in the case of a non-transient application services which can ensure
- multiple instances are not spawned, such as a DBus activated application.
+ * `<RANDOM>` should be a string of random characters to ensure that multiple instances
+ of the application can be launched. This can be omitted for service files of
+ non-transient applications, which ensure multiple instances cannot be
+ spawned. For scope files `<RANDOM>` is mandatory, as the format would be
+ ambiguous otherwise.
* If no application ID is available, the launcher should generate a reasonable
name when possible (e.g. using `basename(argv[0])`). This name must not
@@ -81,7 +81,10 @@ This has the following advantages:
adjusted using desktop environment specific drop-in files.
* The application ID can be retrieved by stripping the prefix and postfix.
- This in turn should map to the corresponding `.desktop` file when available
+ This in turn should map to the corresponding `.desktop` file when available.
+
+ Note that this naming scheme might be a unit alias, so runtime detection
+ must check the entire name-array of a unit, rather than just its unit ID.
TODO: Define the name of slices that should be used.
This could be `app-<launcher>-<ApplicationID>-<RANDOM>.slice`.
diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md
index c416370440..cf5fb91eb9 100644
--- a/docs/ENVIRONMENT.md
+++ b/docs/ENVIRONMENT.md
@@ -351,6 +351,13 @@ All tools:
default is not appropriate for a given system. Defaults to `5`, accepts
positive integers.
+* `$SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_INTERVAL_SEC` — can be set to override the mount
+ units interval rate limit for parsing `/proc/self/mountinfo`. Similar to
+ `$SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST`, the interval limit maybe adjusted when
+ the default is not appropriate for a given system. The default value is 1 and the
+ default application time unit is second, and the time unit can beoverriden as usual
+ by specifying it explicitly, see the systemd.time(7) man page.
+
`systemd-remount-fs`:
* `$SYSTEMD_REMOUNT_ROOT_RW=1` — if set and no entry for the root directory
@@ -404,7 +411,7 @@ All tools:
subvolumes if the backing filesystem supports them. If set to `0`, these
lines will always create directories.
-`systemd-sysusers`
+`systemd-sysusers`:
* `$SOURCE_DATE_EPOCH` — if unset, the field of the date of last password change
in `/etc/shadow` will be the number of days from Jan 1, 1970 00:00 UTC until
diff --git a/docs/THE_CASE_FOR_THE_USR_MERGE.md b/docs/THE_CASE_FOR_THE_USR_MERGE.md
index c603e143e4..30901eb7ac 100644
--- a/docs/THE_CASE_FOR_THE_USR_MERGE.md
+++ b/docs/THE_CASE_FOR_THE_USR_MERGE.md
@@ -80,7 +80,7 @@ _With all vendor-supplied OS resources in a single directory /usr they may be sh
**Myth #4**: The /usr merge’s only purpose is to look pretty, and has no other benefits
-**Fact**: The /usr merge makes sharing the vendor-supplied OS resources between a host and networked clients as well as a host and local light-weight containers easier and atomic. Snapshotting the OS becomes a viable option. The /usr merge also allows making the entire vendor-supplied OS resources read-only for increased security and robustness.
+**Fact**: The /usr merge makes sharing the vendor-supplied OS resources between a host and networked clients as well as a host and local lightweight containers easier and atomic. Snapshotting the OS becomes a viable option. The /usr merge also allows making the entire vendor-supplied OS resources read-only for increased security and robustness.
**Myth #5**: Adopting the /usr merge in your distribution means additional work for your distribution's package maintainers
diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md
index e219131ce6..ebb8ba536a 100644
--- a/docs/TRANSIENT-SETTINGS.md
+++ b/docs/TRANSIENT-SETTINGS.md
@@ -281,6 +281,7 @@ All cgroup/resource control settings are available for transient units
✓ ManagedOOMSwap=
✓ ManagedOOMMemoryPressure=
✓ ManagedOOMMemoryPressureLimit=
+✓ ManagedOOMMemoryPressureDurationSec=
✓ ManagedOOMPreference=
✓ CoredumpReceive=
```
diff --git a/man/nss-myhostname.xml b/man/nss-myhostname.xml
index e0cfffe023..200c9220b9 100644
--- a/man/nss-myhostname.xml
+++ b/man/nss-myhostname.xml
@@ -53,11 +53,12 @@
current network configuration state.</para></listitem>
<listitem><para>The hostname <literal>_outbound</literal> is resolved to the local IPv4 and IPv6
- addresses that are most likely used for communication with other hosts. This is determined by
- requesting a routing decision to the configured default gateways from the kernel and then using the
- local IP addresses selected by this decision. This hostname is only available if there is at least one
- local default gateway configured. This assigns a stable hostname to the local outbound IP addresses,
- useful for referencing them independently of the current network configuration state.</para></listitem>
+ addresses that are most likely used for communication with other hosts. This is the preferred source
+ addresses of default gateways if specified, or determined by requesting a routing decision to the
+ configured default gateways from the kernel and then using the local IP addresses selected by this
+ decision. This hostname is only available if there is at least one local default gateway configured.
+ This assigns a stable hostname to the local outbound IP addresses, useful for referencing them
+ independently of the current network configuration state.</para></listitem>
</itemizedlist>
<para>Various software relies on an always-resolvable local
diff --git a/man/oomd.conf.xml b/man/oomd.conf.xml
index 582fb27de1..13f1f22e53 100644
--- a/man/oomd.conf.xml
+++ b/man/oomd.conf.xml
@@ -90,7 +90,8 @@
<term><varname>DefaultMemoryPressureDurationSec=</varname></term>
<listitem><para>Sets the amount of time a unit's control group needs to have exceeded memory pressure
- limits before <command>systemd-oomd</command> will take action. Memory pressure limits are defined by
+ limits before <command>systemd-oomd</command> will take action. A unit can override this value with
+ <varname>ManagedOOMMemoryPressureDurationSec=</varname>. Memory pressure limits are defined by
<varname>DefaultMemoryPressureLimit=</varname> and <varname>ManagedOOMMemoryPressureLimit=</varname>.
Must be set to 0, or at least 1 second. Defaults to 30 seconds when unset or 0.</para>
diff --git a/man/org.freedesktop.machine1.xml b/man/org.freedesktop.machine1.xml
index aac30d25d7..602c04bf4f 100644
--- a/man/org.freedesktop.machine1.xml
+++ b/man/org.freedesktop.machine1.xml
@@ -651,7 +651,7 @@ node /org/freedesktop/machine1/machine/rawhide {
<para><varname>Leader</varname> is the PID of the leader process of the machine.</para>
<para><varname>Class</varname> is the class of the machine and is either the string "vm" (for real VMs
- based on virtualized hardware) or "container" (for light-weight userspace virtualization sharing the
+ based on virtualized hardware) or "container" (for lightweight userspace virtualization sharing the
same kernel as the host).</para>
<para><varname>RootDirectory</varname> is the root directory of the container if it is known and
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 1e34ddbc85..25905de8c8 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -2993,6 +2993,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t ManagedOOMMemoryPressureDurationUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(ss) BPFProgram = [...];
@@ -4312,6 +4314,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureDurationUSec"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
@@ -4849,6 +4853,11 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
method. See <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
for more details on how to retrieve these file descriptors. Unlike the <varname>ExtraFileDescriptors</varname>
input property, <varname>ExtraFileDescriptorNames</varname> only contains names and not the file descriptors.</para>
+
+ <para><varname>ManagedOOMMemoryPressureDurationUSec</varname> implement the destination parameter of the
+ unit file setting <varname>ManagedOOMMemoryPressureDurationSec=</varname> listed in
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Note the time unit is expressed in <literal>μs</literal>.</para>
</refsect2>
</refsect1>
@@ -5148,6 +5157,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t ManagedOOMMemoryPressureDurationUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(ss) BPFProgram = [...];
@@ -6451,6 +6462,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureDurationUSec"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
@@ -7145,6 +7158,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t ManagedOOMMemoryPressureDurationUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(ss) BPFProgram = [...];
@@ -8286,6 +8301,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureDurationUSec"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
@@ -9109,6 +9126,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t ManagedOOMMemoryPressureDurationUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(ss) BPFProgram = [...];
@@ -10222,6 +10241,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureDurationUSec"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
@@ -10898,6 +10919,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t ManagedOOMMemoryPressureDurationUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(ss) BPFProgram = [...];
@@ -11285,6 +11308,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureDurationUSec"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
@@ -11309,6 +11334,11 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<title>Properties</title>
<para>Most properties correspond directly with the matching settings in slice unit files.</para>
+
+ <para><varname>ManagedOOMMemoryPressureDurationUSec</varname> implement the destination parameter of the
+ unit file setting <varname>ManagedOOMMemoryPressureDurationSec=</varname> listed in
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Note the time unit is expressed in <literal>μs</literal>.</para>
</refsect2>
</refsect1>
@@ -11507,6 +11537,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly t ManagedOOMMemoryPressureDurationUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(ss) BPFProgram = [...];
@@ -11944,6 +11976,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureDurationUSec"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
@@ -12004,6 +12038,11 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
the scope unit is to be shut down via a <function>RequestStop()</function> signal (see below). This is
set when the scope is created. If not set, the scope's processes will terminated with
<constant>SIGTERM</constant> directly.</para>
+
+ <para><varname>ManagedOOMMemoryPressureDurationUSec</varname> implement the destination parameter of the
+ unit file setting <varname>ManagedOOMMemoryPressureDurationSec=</varname> listed in
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Note the time unit is expressed in <literal>μs</literal>.</para>
</refsect2>
</refsect1>
@@ -12222,6 +12261,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>PrivateTmpEx</varname>,
<varname>ImportCredentialEx</varname>,
<varname>ExtraFileDescriptorNames</varname>,
+ <varname>ManagedOOMMemoryPressureDurationUSec</varname>,
<varname>BindLogSockets</varname>, and
<varname>PrivateUsersEx</varname> were added in version 257.</para>
</refsect2>
@@ -12362,6 +12402,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>EffectiveMemoryMax</varname>,
<varname>EffectiveTasksMax</varname>, and
<varname>MemoryZSwapWriteback</varname> were added in version 256.</para>
+ <para><varname>ManagedOOMMemoryPressureDurationUSec</varname> was added in version 257.</para>
</refsect2>
<refsect2>
<title>Scope Unit Objects</title>
@@ -12387,6 +12428,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>EffectiveMemoryMax</varname>,
<varname>EffectiveTasksMax</varname>, and
<varname>MemoryZSwapWriteback</varname> were added in version 256.</para>
+ <para><varname>ManagedOOMMemoryPressureDurationUSec</varname> was added in version 257.</para>
</refsect2>
<refsect2>
<title>Job Objects</title>
diff --git a/man/org.freedesktop.sysupdate1.xml b/man/org.freedesktop.sysupdate1.xml
index 79f718f93c..407057b2d8 100644
--- a/man/org.freedesktop.sysupdate1.xml
+++ b/man/org.freedesktop.sysupdate1.xml
@@ -231,8 +231,8 @@ node /org/freedesktop/sysupdate1/target/host {
</varlistentry>
<varlistentry>
- <term><literal>changelog_urls</literal></term>
- <listitem><para>A list of strings that contain user-presentable URLs to ChangeLogs associated with
+ <term><literal>changelogUrls</literal></term>
+ <listitem><para>A list of strings that contain user-presentable URLs to change logs associated with
this version.</para></listitem>
</varlistentry>
</variablelist>
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index cd7d349b95..c780abf96a 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -21,7 +21,7 @@
<refnamediv>
<refname>systemd-nspawn</refname>
- <refpurpose>Spawn a command or OS in a light-weight container</refpurpose>
+ <refpurpose>Spawn a command or OS in a lightweight container</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -43,11 +43,11 @@
<refsect1>
<title>Description</title>
- <para><command>systemd-nspawn</command> may be used to run a command or OS in a light-weight namespace
+ <para><command>systemd-nspawn</command> may be used to run a command or OS in a lightweight namespace
container. In many ways it is similar to <citerefentry
project='man-pages'><refentrytitle>chroot</refentrytitle><manvolnum>1</manvolnum></citerefentry>, but more powerful
- since it fully virtualizes the file system hierarchy, as well as the process tree, the various IPC subsystems and
- the host and domain name.</para>
+ since it virtualizes the file system hierarchy, as well as the process tree, the various IPC subsystems, and
+ the host and domain names.</para>
<para><command>systemd-nspawn</command> may be invoked on any directory tree containing an operating system tree,
using the <option>--directory=</option> command line option. By using the <option>--machine=</option> option an OS
@@ -59,11 +59,14 @@
project='man-pages'><refentrytitle>chroot</refentrytitle><manvolnum>1</manvolnum></citerefentry> <command>systemd-nspawn</command>
may be used to boot full Linux-based operating systems in a container.</para>
- <para><command>systemd-nspawn</command> limits access to various kernel interfaces in the container to read-only,
- such as <filename>/sys/</filename>, <filename>/proc/sys/</filename> or <filename>/sys/fs/selinux/</filename>. The
- host's network interfaces and the system clock may not be changed from within the container. Device nodes may not
- be created. The host system cannot be rebooted and kernel modules may not be loaded from within the
- container.</para>
+ <para><command>systemd-nspawn</command> limits access to various kernel interfaces in the container to
+ read-only, such as <filename>/sys/</filename>, <filename>/proc/sys/</filename>, or
+ <filename>/sys/fs/selinux/</filename>. The host's network interfaces and the system clock may not be
+ changed from within the container. Device nodes may not be created. The host system cannot be rebooted
+ and kernel modules may not be loaded from within the container. <emphasis>This sandbox can easily be
+ circumvented from within the container if user namespaces are not used</emphasis>. This means that
+ untrusted code must always be run in a user namespace, see the discussion of the
+ <option>--private-users=</option> option below.</para>
<para>Use a tool like <citerefentry
project='mankier'><refentrytitle>dnf</refentrytitle><manvolnum>8</manvolnum></citerefentry>, <citerefentry
@@ -100,8 +103,8 @@
template unit file, making it usually unnecessary to alter this template file directly.</para>
<para>Note that <command>systemd-nspawn</command> will mount file systems private to the container to
- <filename>/dev/</filename>, <filename>/run/</filename> and similar. These will not be visible outside of the
- container, and their contents will be lost when the container exits.</para>
+ <filename>/dev/</filename>, <filename>/run/</filename>, and similar. These will not be visible outside of
+ the container, and their contents will be lost when the container exits.</para>
<para>Note that running two <command>systemd-nspawn</command> containers from the same directory tree will not make
processes in them see each other. The PID namespace separation of the two containers is complete and the containers
@@ -810,17 +813,6 @@
range. In this mode, the number of UIDs/GIDs assigned to the container is 65536, and the owner
UID/GID of the root directory must be a multiple of 65536.</para></listitem>
- <listitem><para>If the parameter is <literal>no</literal>, user namespacing is turned off. This is
- the default.</para>
- </listitem>
-
- <listitem><para>If the parameter is <literal>identity</literal>, user namespacing is employed with
- an identity mapping for the first 65536 UIDs/GIDs. This is mostly equivalent to
- <option>--private-users=0:65536</option>. While it does not provide UID/GID isolation, since all
- host and container UIDs/GIDs are chosen identically it does provide process capability isolation,
- and hence is often a good choice if proper user namespacing with distinct UID maps is not
- appropriate.</para></listitem>
-
<listitem><para>The special value <literal>pick</literal> turns on user namespacing. In this case
the UID/GID range is automatically chosen. As first step, the file owner UID/GID of the root
directory of the container's directory tree is read, and it is checked that no other container is
@@ -837,22 +829,35 @@
for it, and thus in the (possibly expensive) file ownership adjustment operation. However,
subsequent invocations of the container will be cheap (unless of course the picked UID/GID range is
assigned to a different use by then).</para></listitem>
+
+ <listitem><para>If the parameter is <literal>no</literal>, user namespacing is turned off. This is
+ the default when <command>systemd-nspawn</command> is invoked directly. (Note that the
+ <filename>systemd-nspawn@.service</filename> unit enables private users.) This option is not
+ secure and must not be used to run untrusted code.</para></listitem>
+
+ <listitem><para>If the parameter is <literal>identity</literal>, user namespacing is employed with
+ an identity mapping for the first 65536 UIDs/GIDs. This is mostly equivalent to
+ <option>--private-users=0:65536</option>. While it does not provide UID/GID isolation, since all
+ host and container UIDs/GIDs are chosen identically it does provide process capability isolation,
+ but may be useful if proper user namespacing with distinct UID maps is not possible. This option is
+ not secure and must not be used to run untrusted code.</para></listitem>
</orderedlist>
- <para>It is recommended to assign at least 65536 UIDs/GIDs to each container, so that the usable UID/GID range in the
- container covers 16 bit. For best security, do not assign overlapping UID/GID ranges to multiple containers. It is
- hence a good idea to use the upper 16 bit of the host 32-bit UIDs/GIDs as container identifier, while the lower 16
- bit encode the container UID/GID used. This is in fact the behavior enforced by the
- <option>--private-users=pick</option> option.</para>
+ <para>It is recommended to assign at least 65536 UIDs/GIDs to each container, so that the usable
+ UID/GID range in the container covers 16 bits. For best security, do not assign overlapping UID/GID
+ ranges to multiple containers. It is hence a good idea to use the upper 16 bit of the host 32-bit
+ UIDs/GIDs as container identifier, while the lower 16 bits encode the container UID/GID used. This is
+ in fact the behavior enforced by the <option>--private-users=pick</option> option.</para>
- <para>When user namespaces are used, the GID range assigned to each container is always chosen identical to the
- UID range.</para>
+ <para>When user namespaces are used, the GID range assigned to each container is always chosen
+ identical to the UID range.</para>
- <para>In most cases, using <option>--private-users=pick</option> is the recommended option as it enhances
- container security massively and operates fully automatically in most cases.</para>
+ <para>In most cases, using <option>--private-users=pick</option> is the recommended option as user
+ namespacing is required for security, and this option massively enhances container security while
+ operating fully automatically in most cases.</para>
<para>Note that the picked UID/GID range is not written to <filename>/etc/passwd</filename> or
- <filename>/etc/group</filename>. In fact, the allocation of the range is not stored persistently anywhere,
+ <filename>/etc/group</filename>. In fact, the allocation of the range is not stored persistently,
except in the file ownership of the files and directories of the container.</para>
<para>Note that when user namespacing is used file ownership on disk reflects this, and all of the container's
diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml
index 8d22b22e85..210a843233 100644
--- a/man/systemd-resolved.service.xml
+++ b/man/systemd-resolved.service.xml
@@ -118,11 +118,12 @@
useful for referencing it independently of the current network configuration state.</para></listitem>
<listitem><para>The hostname <literal>_outbound</literal> is resolved to the local IPv4 and IPv6
- addresses that are most likely used for communication with other hosts. This is determined by
- requesting a routing decision to the configured default gateways from the kernel and then using the
- local IP addresses selected by this decision. This hostname is only available if there is at least one
- local default gateway configured. This assigns a stable hostname to the local outbound IP addresses,
- useful for referencing them independently of the current network configuration state.</para></listitem>
+ addresses that are most likely used for communication with other hosts. This is the preferred source
+ addresses of default gateways if specified, or determined by requesting a routing decision to the
+ configured default gateways from the kernel and then using the local IP addresses selected by this
+ decision. This hostname is only available if there is at least one local default gateway configured.
+ This assigns a stable hostname to the local outbound IP addresses, useful for referencing them
+ independently of the current network configuration state.</para></listitem>
<listitem><para>The hostname <literal>_localdnsstub</literal> is resolved to the IP address 127.0.0.53,
i.e. the address the local DNS stub (see above) is listening on.</para></listitem>
diff --git a/man/systemd.generator.xml b/man/systemd.generator.xml
index ae4c2c5c0b..1a9b5d1653 100644
--- a/man/systemd.generator.xml
+++ b/man/systemd.generator.xml
@@ -164,6 +164,15 @@
</varlistentry>
<varlistentry>
+ <term><varname>$SYSTEMD_SOFT_REBOOTS_COUNT</varname></term>
+
+ <listitem><para>If the system has soft-rebooted, this variable is set to the count of soft-reboots.
+ This environment variable is only set for system generators.</para>
+
+ <xi:include href="version-info.xml" xpointer="v257"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>$SYSTEMD_FIRST_BOOT</varname></term>
<listitem><para>If this boot-up cycle is considered a "first boot", this is set to
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index e743e6d2a0..d2a6773883 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -6243,22 +6243,22 @@ Name=enp1s0
[Network]
DHCP=ipv6
-# The below setting is optional, to also assign an address in the delegated prefix
-# to the upstream interface. If not necessary, then comment out the line below and
-# the [DHCPPrefixDelegation] section.
-DHCPPrefixDelegation=yes
-
-# If the upstream network provides Router Advertisement with Managed bit set,
-# then comment out the line below and WithoutRA= setting in the [DHCPv6] section.
-IPv6AcceptRA=no
-
-[DHCPv6]
-WithoutRA=solicit
-
-[DHCPPrefixDelegation]
-UplinkInterface=:self
-SubnetId=0
-Announce=no</programlisting>
+# The lines below are optional, to also assign an address in the delegated prefix
+# to the upstream interface. Uncomment the lines below if necessary.
+#[Network]
+#DHCPPrefixDelegation=yes
+#[DHCPPrefixDelegation]
+#UplinkInterface=:self
+#SubnetId=0
+#Announce=no
+
+# If the upstream network does not provides any Router Advertisement (RA) messages
+# or provides an RA with both Managed and Other-information bits unset, then
+# uncomment the lines below.
+#[Network]
+#IPv6AcceptRA=no
+#[DHCPv6]
+#WithoutRA=solicit</programlisting>
<programlisting># /etc/systemd/network/55-dhcpv6-pd-downstream.network
[Match]
diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml
index 2ffc279a35..1f16052a33 100644
--- a/man/systemd.resource-control.xml
+++ b/man/systemd.resource-control.xml
@@ -1535,10 +1535,10 @@ DeviceAllow=/dev/loop-control
<listitem>
<para>Overrides the default memory pressure limit set by
<citerefentry><refentrytitle>oomd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
- this unit (cgroup). Takes a percentage value between 0% and 100%, inclusive. This property is
- ignored unless <varname>ManagedOOMMemoryPressure=</varname><option>kill</option>. Defaults to 0%,
+ the cgroup of this unit. Takes a percentage value between 0% and 100%, inclusive. Defaults to 0%,
which means to use the default set by
<citerefentry><refentrytitle>oomd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ This property is ignored unless <varname>ManagedOOMMemoryPressure=</varname><option>kill</option>.
</para>
<xi:include href="version-info.xml" xpointer="v247"/>
@@ -1546,6 +1546,25 @@ DeviceAllow=/dev/loop-control
</varlistentry>
<varlistentry>
+ <term><varname>ManagedOOMMemoryPressureDurationSec=</varname></term>
+
+ <listitem>
+ <para>Overrides the default memory pressure duration set by
+ <citerefentry><refentrytitle>oomd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ the cgroup of this unit. The specified value supports a time unit such as <literal>ms</literal> or
+ <literal>μs</literal>, see
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for details on the permitted syntax. Must be set to either empty or a value of at least 1s. Defaults
+ to empty, which means to use the default set by
+ <citerefentry><refentrytitle>oomd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ This property is ignored unless <varname>ManagedOOMMemoryPressure=</varname><option>kill</option>.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v257"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>ManagedOOMPreference=none|avoid|omit</varname></term>
<listitem>
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index dfc9f6f994..73b28a47d8 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -601,7 +601,7 @@
<command>systemd</command> (and other UIs) as a user-visible label for the unit, so this string
should identify the unit rather than describe it, despite the name. This string also shouldn't just
repeat the unit name. <literal>Apache2 Web Server</literal> is a good example. Bad examples are
- <literal>high-performance light-weight HTTP server</literal> (too generic) or
+ <literal>high-performance lightweight HTTP server</literal> (too generic) or
<literal>Apache2</literal> (meaningless for people who do not know Apache, duplicates the unit
name). <command>systemd</command> may use this string as a noun in status messages (<literal>Starting
<replaceable>description</replaceable>...</literal>, <literal>Started
diff --git a/mkosi.conf.d/10-debian-ubuntu/mkosi.conf b/mkosi.conf.d/10-debian-ubuntu/mkosi.conf
index 472064e45d..46dd98828f 100644
--- a/mkosi.conf.d/10-debian-ubuntu/mkosi.conf
+++ b/mkosi.conf.d/10-debian-ubuntu/mkosi.conf
@@ -4,8 +4,8 @@
Distribution=|debian
Distribution=|ubuntu
-[Distribution]
-PackageManagerTrees=mkosi-pinning.pref:/etc/apt/preferences.d/mkosi-pinning.pref
+[Build]
+SandboxTrees=mkosi-pinning.pref:/etc/apt/preferences.d/mkosi-pinning.pref
[Content]
VolatilePackages=
diff --git a/mkosi.conf.d/10-opensuse/mkosi.conf b/mkosi.conf.d/10-opensuse/mkosi.conf
index dc9bf30429..8a08b1f8c7 100644
--- a/mkosi.conf.d/10-opensuse/mkosi.conf
+++ b/mkosi.conf.d/10-opensuse/mkosi.conf
@@ -6,7 +6,9 @@ Distribution=opensuse
[Distribution]
Release=tumbleweed
Repositories=non-oss
-PackageManagerTrees=macros.db_backend:/etc/rpm/macros.db_backend
+
+[Build]
+SandboxTrees=macros.db_backend:/etc/rpm/macros.db_backend
[Content]
VolatilePackages=
diff --git a/mkosi.conf.d/10-ubuntu/mkosi.conf.d/non-x86.conf b/mkosi.conf.d/10-ubuntu/mkosi.conf.d/non-x86.conf
index 582f038b5f..8e0e3c0d78 100644
--- a/mkosi.conf.d/10-ubuntu/mkosi.conf.d/non-x86.conf
+++ b/mkosi.conf.d/10-ubuntu/mkosi.conf.d/non-x86.conf
@@ -6,5 +6,5 @@ Architecture=!x86-64
Architecture=!x86
Release=noble
-[Distribution]
-PackageManagerTrees=noble-backports-ports.sources:/etc/apt/sources.list.d/noble-backports-ports.sources
+[Build]
+SandboxTrees=noble-backports-ports.sources:/etc/apt/sources.list.d/noble-backports-ports.sources
diff --git a/mkosi.conf.d/10-ubuntu/mkosi.conf.d/x86.conf b/mkosi.conf.d/10-ubuntu/mkosi.conf.d/x86.conf
index 7347be9069..e0e96b2c18 100644
--- a/mkosi.conf.d/10-ubuntu/mkosi.conf.d/x86.conf
+++ b/mkosi.conf.d/10-ubuntu/mkosi.conf.d/x86.conf
@@ -6,5 +6,5 @@ Architecture=|x86-64
Architecture=|x86
Release=noble
-[Distribution]
-PackageManagerTrees=noble-backports.sources:/etc/apt/sources.list.d/noble-backports.sources
+[Build]
+SandboxTrees=noble-backports.sources:/etc/apt/sources.list.d/noble-backports.sources
diff --git a/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.conf b/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.conf
index a4882df8b1..a94a27cd26 100644
--- a/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.conf
+++ b/mkosi.images/build/mkosi.conf.d/opensuse/mkosi.conf
@@ -6,8 +6,8 @@ Distribution=opensuse
[Build]
Environment=
GIT_URL=https://src.opensuse.org/pool/systemd
- GIT_BRANCH=factory
- GIT_COMMIT=612bc16021b28ab99002fa1069f1ec97124397a25c7a207d013213b5cfb86055
+ GIT_BRANCH=devel
+ GIT_COMMIT=22b8df87fc05953b48dd294d696ff617fb0ca44270b42feac63956f068fe8c57
PKG_SUBDIR=opensuse
[Content]
diff --git a/rules.d/60-persistent-storage.rules.in b/rules.d/60-persistent-storage.rules.in
index 1e5e2b3352..e07f7b5d7a 100644
--- a/rules.d/60-persistent-storage.rules.in
+++ b/rules.d/60-persistent-storage.rules.in
@@ -45,9 +45,9 @@ ENV{ID_WWN}=="?*", SYMLINK+="disk/by-id/nvme-$env{ID_WWN}$env{.PART_SUFFIX}"
# obsolete symlink with non-escaped characters, kept for backward compatibility
ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", ENV{ID_MODEL}!="*/*", ENV{ID_SERIAL_SHORT}!="*/*", \
- ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}$env{.PART_SUFFIX}"
+ ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", ENV{ID_NSID}=="1", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}$env{.PART_SUFFIX}"
# obsolete symlink that might get overridden on adding a new nvme controller, kept for backward compatibility
-ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", OPTIONS="string_escape=replace", \
+ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", ENV{ID_NSID}=="1", OPTIONS="string_escape=replace", \
ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}$env{.PART_SUFFIX}"
ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", ENV{ID_NSID}=="?*", OPTIONS="string_escape=replace", \
ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}_$env{ID_NSID}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}$env{.PART_SUFFIX}"
diff --git a/rules.d/99-systemd.rules.in b/rules.d/99-systemd.rules.in
index a57de9983c..882cda0dcd 100644
--- a/rules.d/99-systemd.rules.in
+++ b/rules.d/99-systemd.rules.in
@@ -11,7 +11,7 @@ ACTION=="remove", GOTO="systemd_end"
SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*|ttysclp*|sclp_line*|3270/tty[0-9]*", TAG+="systemd"
# Exclude 8250 serial ports with a zero IO port, as they are not usable until "setserial /dev/ttySxxx port …" is invoked.
-SUBSYSTEM=="tty", KERNEL=="ttyS*", DRIVERS=="serial8250", ATTR{port}=="0x0", ENV{SYSTEMD_READY}="0"
+SUBSYSTEM=="tty", KERNEL=="ttyS*", DRIVERS=="serial8250", ATTR{port}=="0x0", ATTR{iomem_base}=="0x0", ENV{SYSTEMD_READY}="0"
KERNEL=="vport*", TAG+="systemd"
SUBSYSTEM=="ptp", TAG+="systemd"
diff --git a/shell-completion/bash/busctl b/shell-completion/bash/busctl
index ea88cca8c6..441b2c7d43 100644
--- a/shell-completion/bash/busctl
+++ b/shell-completion/bash/busctl
@@ -128,6 +128,7 @@ _busctl() {
[OBJECT]='introspect'
[METHOD]='call'
[EMIT]='emit'
+ [WAIT]='wait'
[PROPERTY_GET]='get-property'
[PROPERTY_SET]='set-property'
)
@@ -174,6 +175,18 @@ _busctl() {
fi
elif __contains_word "$verb" ${VERBS[EMIT]}; then
comps=''
+ elif __contains_word "$verb" ${VERBS[WAIT]}; then
+ if [[ $n -eq 1 ]] ; then
+ comps=$( __get_busnames $mode)
+ elif [[ $n -eq 2 ]] ; then
+ comps=$( __get_objects $mode ${COMP_WORDS[COMP_CWORD-1]})
+ elif [[ $n -eq 3 ]] ; then
+ comps=$( __get_interfaces $mode ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]})
+ elif [[ $n -eq 4 ]] ; then
+ comps=$( __get_members $mode ${COMP_WORDS[COMP_CWORD-3]} ${COMP_WORDS[COMP_CWORD-2]} ${COMP_WORDS[COMP_CWORD-1]} signal)
+ else
+ comps=''
+ fi
elif __contains_word "$verb" ${VERBS[PROPERTY_GET]}; then
if [[ $n -eq 1 ]] ; then
comps=$( __get_busnames $mode)
diff --git a/src/analyze/analyze-dump.c b/src/analyze/analyze-dump.c
index 4ee547ed4c..4ca6278d6a 100644
--- a/src/analyze/analyze-dump.c
+++ b/src/analyze/analyze-dump.c
@@ -6,13 +6,12 @@
#include "analyze.h"
#include "bus-error.h"
#include "bus-locator.h"
+#include "bus-message-util.h"
#include "bus-util.h"
-#include "copy.h"
-static int dump_fallback(sd_bus *bus) {
+static int dump_string(sd_bus *bus) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- const char *text;
int r;
assert(bus);
@@ -21,36 +20,34 @@ static int dump_fallback(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to call Dump: %s", bus_error_message(&error, r));
- r = sd_bus_message_read(reply, "s", &text);
- if (r < 0)
- return bus_log_parse_error(r);
-
- fputs(text, stdout);
- return 0;
+ return bus_message_dump_string(reply);
}
-static int dump(sd_bus *bus) {
+static int dump_fd(sd_bus *bus) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int r;
r = bus_call_method(bus, bus_systemd_mgr, "DumpByFileDescriptor", &error, &reply, NULL);
if (IN_SET(r, -EACCES, -EBADR))
- return 0; /* Fall back to non-fd method. We need to do this even if the bus supports sending
- * fds to cater to very old managers which didn't have the fd-based method. */
+ /* Fall back to non-fd method. We need to do this even if the bus supports sending
+ * fds to cater to very old managers which didn't have the fd-based method. */
+ return dump_string(bus);
if (r < 0)
return log_error_errno(r, "Failed to call DumpByFileDescriptor: %s",
bus_error_message(&error, r));
- return dump_fd_reply(reply);
+ return bus_message_dump_fd(reply);
}
-static int dump_patterns_fallback(sd_bus *bus, char **patterns) {
+static int dump_patterns_string(sd_bus *bus, char **patterns) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
- const char *text;
int r;
+ if (strv_isempty(patterns))
+ return dump_string(bus);
+
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "DumpUnitsMatchingPatterns");
if (r < 0)
return bus_log_create_error(r);
@@ -64,19 +61,17 @@ static int dump_patterns_fallback(sd_bus *bus, char **patterns) {
return log_error_errno(r, "Failed to call DumpUnitsMatchingPatterns: %s",
bus_error_message(&error, r));
- r = sd_bus_message_read(reply, "s", &text);
- if (r < 0)
- return bus_log_parse_error(r);
-
- fputs(text, stdout);
- return 0;
+ return bus_message_dump_string(reply);
}
-static int dump_patterns(sd_bus *bus, char **patterns) {
+static int dump_patterns_fd(sd_bus *bus, char **patterns) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
int r;
+ if (strv_isempty(patterns))
+ return dump_fd(bus);
+
r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "DumpUnitsMatchingPatternsByFileDescriptor");
if (r < 0)
return bus_log_create_error(r);
@@ -90,7 +85,7 @@ static int dump_patterns(sd_bus *bus, char **patterns) {
return log_error_errno(r, "Failed to call DumpUnitsMatchingPatternsByFileDescriptor: %s",
bus_error_message(&error, r));
- return dump_fd_reply(reply);
+ return bus_message_dump_fd(reply);
}
static int mangle_patterns(char **args, char ***ret) {
@@ -109,9 +104,6 @@ static int mangle_patterns(char **args, char ***ret) {
return log_oom();
}
- if (strv_isempty(mangled))
- mangled = strv_free(mangled);
-
*ret = TAKE_PTR(mangled);
return 0;
}
@@ -125,8 +117,6 @@ int verb_dump(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_connect_error(r, arg_transport, arg_runtime_scope);
- pager_open(arg_pager_flags);
-
r = mangle_patterns(strv_skip(argv, 1), &patterns);
if (r < 0)
return r;
@@ -134,12 +124,8 @@ int verb_dump(int argc, char *argv[], void *userdata) {
r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD);
if (r < 0)
return log_error_errno(r, "Unable to determine if bus connection supports fd passing: %m");
- if (r > 0)
- r = patterns ? dump_patterns(bus, patterns) : dump(bus);
- if (r == 0) /* wasn't supported */
- r = patterns ? dump_patterns_fallback(bus, patterns) : dump_fallback(bus);
- if (r < 0)
- return r;
- return EXIT_SUCCESS;
+ pager_open(arg_pager_flags);
+
+ return r > 0 ? dump_patterns_fd(bus, patterns) : dump_patterns_string(bus, patterns);
}
diff --git a/src/analyze/analyze-malloc.c b/src/analyze/analyze-malloc.c
index 514526d142..9eb234e669 100644
--- a/src/analyze/analyze-malloc.c
+++ b/src/analyze/analyze-malloc.c
@@ -6,6 +6,7 @@
#include "analyze.h"
#include "bus-error.h"
#include "bus-internal.h"
+#include "bus-message-util.h"
static int dump_malloc_info(sd_bus *bus, char *service) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -26,7 +27,7 @@ static int dump_malloc_info(sd_bus *bus, char *service) {
if (r < 0)
return log_error_errno(r, "Failed to call GetMallocInfo on '%s': %s", service, bus_error_message(&error, r));
- return dump_fd_reply(reply);
+ return bus_message_dump_fd(reply);
}
int verb_malloc(int argc, char *argv[], void *userdata) {
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 24188311ff..5aba273ca6 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -179,23 +179,6 @@ void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timesp
"Use 'systemd-analyze timespan \"%s\"' instead?", p);
}
-int dump_fd_reply(sd_bus_message *message) {
- int fd, r;
-
- assert(message);
-
- r = sd_bus_message_read(message, "h", &fd);
- if (r < 0)
- return bus_log_parse_error(r);
-
- fflush(stdout);
- r = copy_bytes(fd, STDOUT_FILENO, UINT64_MAX, 0);
- if (r < 0)
- return r;
-
- return 1; /* Success */
-}
-
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL, *dot_link = NULL;
int r;
diff --git a/src/analyze/analyze.h b/src/analyze/analyze.h
index 48d9fabbd0..959b8a1b85 100644
--- a/src/analyze/analyze.h
+++ b/src/analyze/analyze.h
@@ -57,5 +57,3 @@ int acquire_bus(sd_bus **bus, bool *use_full_bus);
int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv);
void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan);
-
-int dump_fd_reply(sd_bus_message *message);
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h
index 462092703a..ba71298287 100644
--- a/src/basic/alloc-util.h
+++ b/src/basic/alloc-util.h
@@ -155,7 +155,10 @@ void* greedy_realloc_append(void **p, size_t *n_p, const void *from, size_t n_fr
greedy_realloc0((void**) &(array), (need), sizeof((array)[0]))
#define GREEDY_REALLOC_APPEND(array, n_array, from, n_from) \
- greedy_realloc_append((void**) &(array), (size_t*) &(n_array), (from), (n_from), sizeof((array)[0]))
+ ({ \
+ const typeof(*(array)) *_from_ = (from); \
+ greedy_realloc_append((void**) &(array), &(n_array), _from_, (n_from), sizeof((array)[0])); \
+ })
#define alloca0(n) \
({ \
diff --git a/src/basic/constants.h b/src/basic/constants.h
index e70817c51f..5aaf8f535c 100644
--- a/src/basic/constants.h
+++ b/src/basic/constants.h
@@ -85,4 +85,4 @@
/* Path where systemd-oomd listens for varlink connections from user managers to report changes in ManagedOOM settings. */
#define VARLINK_ADDR_PATH_MANAGED_OOM_USER "/run/systemd/oom/io.systemd.ManagedOOM"
-#define KERNEL_BASELINE_VERSION "4.15"
+#define KERNEL_BASELINE_VERSION "5.4"
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 24357c1ef8..bf1603de0e 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -118,7 +118,7 @@ FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode) {
return f;
}
-int write_string_stream_ts(
+int write_string_stream_full(
FILE *f,
const char *line,
WriteStringFileFlags flags,
@@ -234,7 +234,7 @@ static int write_string_file_atomic_at(
if (r < 0)
return r;
- r = write_string_stream_ts(f, line, flags, ts);
+ r = write_string_stream_full(f, line, flags, ts);
if (r < 0)
goto fail;
@@ -261,7 +261,7 @@ fail:
return r;
}
-int write_string_file_ts_at(
+int write_string_file_full(
int dir_fd,
const char *fn,
const char *line,
@@ -314,7 +314,7 @@ int write_string_file_ts_at(
if (flags & WRITE_STRING_FILE_DISABLE_BUFFER)
setvbuf(f, NULL, _IONBF, 0);
- r = write_string_stream_ts(f, line, flags, ts);
+ r = write_string_stream_full(f, line, flags, ts);
if (r < 0)
goto fail;
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 6986ed3276..dc514e3ccc 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -51,25 +51,21 @@ DIR* take_fdopendir(int *dfd);
FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc);
FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode);
-int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, const struct timespec *ts);
+int write_string_stream_full(FILE *f, const char *line, WriteStringFileFlags flags, const struct timespec *ts);
static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) {
- return write_string_stream_ts(f, line, flags, NULL);
+ return write_string_stream_full(f, line, flags, /* ts= */ NULL);
}
-int write_string_file_ts_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts);
-static inline int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts) {
- return write_string_file_ts_at(AT_FDCWD, fn, line, flags, ts);
+int write_string_file_full(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags, const struct timespec *ts);
+static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
+ return write_string_file_full(AT_FDCWD, fn, line, flags, /* ts= */ NULL);
}
static inline int write_string_file_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags) {
- return write_string_file_ts_at(dir_fd, fn, line, flags, NULL);
-}
-static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
- return write_string_file_ts(fn, line, flags, NULL);
+ return write_string_file_full(dir_fd, fn, line, flags, /* ts= */ NULL);
}
+int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4);
int write_base64_file_at(int dir_fd, const char *fn, const struct iovec *data, WriteStringFileFlags flags);
-int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4);
-
int read_one_line_file_at(int dir_fd, const char *filename, char **ret);
static inline int read_one_line_file(const char *filename, char **ret) {
return read_one_line_file_at(AT_FDCWD, filename, ret);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index a6b53e7067..9292e567c8 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -153,10 +153,6 @@ int readlinkat_malloc(int fd, const char *p, char **ret) {
}
}
-int readlink_malloc(const char *p, char **ret) {
- return readlinkat_malloc(AT_FDCWD, p, ret);
-}
-
int readlink_value(const char *p, char **ret) {
_cleanup_free_ char *link = NULL, *name = NULL;
int r;
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 82e865180b..702b6010e2 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -28,9 +28,11 @@ int rmdir_parents(const char *path, const char *stop);
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
int readlinkat_malloc(int fd, const char *p, char **ret);
-int readlink_malloc(const char *p, char **r);
+static inline int readlink_malloc(const char *p, char **ret) {
+ return readlinkat_malloc(AT_FDCWD, p, ret);
+}
int readlink_value(const char *p, char **ret);
-int readlink_and_make_absolute(const char *p, char **r);
+int readlink_and_make_absolute(const char *p, char **ret);
int chmod_and_chown_at(int dir_fd, const char *path, mode_t mode, uid_t uid, gid_t gid);
static inline int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h
index 0a7616443d..c9e1da39ab 100644
--- a/src/basic/namespace-util.h
+++ b/src/basic/namespace-util.h
@@ -3,9 +3,11 @@
#include <sys/types.h>
+typedef enum NamespaceType NamespaceType;
+
#include "pidref.h"
-typedef enum NamespaceType {
+enum NamespaceType {
NAMESPACE_CGROUP,
NAMESPACE_IPC,
NAMESPACE_NET,
@@ -16,7 +18,7 @@ typedef enum NamespaceType {
NAMESPACE_TIME,
_NAMESPACE_TYPE_MAX,
_NAMESPACE_TYPE_INVALID = -EINVAL,
-} NamespaceType;
+};
extern const struct namespace_info {
const char *proc_name;
diff --git a/src/basic/pidref.c b/src/basic/pidref.c
index 8529236c5e..b13cc96d6a 100644
--- a/src/basic/pidref.c
+++ b/src/basic/pidref.c
@@ -40,6 +40,9 @@ int pidref_acquire_pidfd_id(PidRef *pidref) {
if (!pidref_is_set(pidref))
return -ESRCH;
+ if (pidref_is_remote(pidref))
+ return -EREMOTE;
+
if (pidref->fd < 0)
return -ENOMEDIUM;
@@ -64,23 +67,36 @@ int pidref_acquire_pidfd_id(PidRef *pidref) {
bool pidref_equal(PidRef *a, PidRef *b) {
- if (pidref_is_set(a)) {
- if (!pidref_is_set(b))
+ if (!pidref_is_set(a))
+ return !pidref_is_set(b);
+
+ if (!pidref_is_set(b))
+ return false;
+
+ if (a->pid != b->pid)
+ return false;
+
+ if (pidref_is_remote(a)) {
+ /* If one is remote and the other isn't, they are not the same */
+ if (!pidref_is_remote(b))
return false;
- if (a->pid != b->pid)
+ /* If both are remote, compare fd IDs if we have both, otherwise don't bother, and cut things short */
+ if (a->fd_id == 0 || b->fd_id == 0)
+ return true;
+ } else {
+ if (pidref_is_remote(b))
return false;
- /* Try to compare pidfds using their inode numbers. This way we can ensure that we don't
- * spuriously consider two PidRefs equal if the pid has been reused once. Note that we
- * ignore all errors here, not only EOPNOTSUPP, as fstat() might fail due to many reasons. */
+ /* Try to compare pidfds using their inode numbers. This way we can ensure that we
+ * don't spuriously consider two PidRefs equal if the pid has been reused once. Note
+ * that we ignore all errors here, not only EOPNOTSUPP, as fstat() might fail due to
+ * many reasons. */
if (pidref_acquire_pidfd_id(a) < 0 || pidref_acquire_pidfd_id(b) < 0)
return true;
-
- return a->fd_id == b->fd_id;
}
- return !pidref_is_set(b);
+ return a->fd_id == b->fd_id;
}
int pidref_set_pid(PidRef *pidref, pid_t pid) {
@@ -240,7 +256,9 @@ int pidref_copy(const PidRef *pidref, PidRef *dest) {
assert(dest);
if (pidref) {
- if (pidref->fd >= 0) {
+ if (pidref_is_remote(pidref)) /* Propagate remote flag */
+ dup_fd = -EREMOTE;
+ else if (pidref->fd >= 0) {
dup_fd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
if (dup_fd < 0) {
if (!ERRNO_IS_RESOURCE(errno))
@@ -311,6 +329,9 @@ int pidref_kill(const PidRef *pidref, int sig) {
if (!pidref)
return -ESRCH;
+ if (pidref_is_remote(pidref))
+ return -EREMOTE;
+
if (pidref->fd >= 0)
return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
@@ -338,6 +359,9 @@ int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
if (!pidref)
return -ESRCH;
+ if (pidref_is_remote(pidref))
+ return -EREMOTE;
+
if (pidref->fd >= 0) {
siginfo_t si;
@@ -370,6 +394,9 @@ int pidref_verify(const PidRef *pidref) {
if (!pidref_is_set(pidref))
return -ESRCH;
+ if (pidref_is_remote(pidref))
+ return -EREMOTE;
+
if (pidref->pid == 1)
return 1; /* PID 1 can never go away, hence never be recycled to a different process → return 1 */
@@ -387,6 +414,9 @@ bool pidref_is_self(const PidRef *pidref) {
if (!pidref)
return false;
+ if (pidref_is_remote(pidref))
+ return false;
+
return pidref->pid == getpid_cached();
}
@@ -396,6 +426,9 @@ int pidref_wait(const PidRef *pidref, siginfo_t *ret, int options) {
if (!pidref_is_set(pidref))
return -ESRCH;
+ if (pidref_is_remote(pidref))
+ return -EREMOTE;
+
if (pidref->pid == 1 || pidref->pid == getpid_cached())
return -ECHILD;
@@ -428,6 +461,10 @@ int pidref_wait_for_terminate(const PidRef *pidref, siginfo_t *ret) {
}
}
+bool pidref_is_automatic(const PidRef *pidref) {
+ return pidref && pid_is_automatic(pidref->pid);
+}
+
static void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
siphash24_compress_typesafe(pidref->pid, state);
}
diff --git a/src/basic/pidref.h b/src/basic/pidref.h
index 4738c6eb5d..42ddf4e50b 100644
--- a/src/basic/pidref.h
+++ b/src/basic/pidref.h
@@ -1,18 +1,51 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include "macro.h"
+typedef struct PidRef PidRef;
-/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes continuously. */
-typedef struct PidRef {
- pid_t pid; /* always valid */
- int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd */
+#include "macro.h"
+#include "process-util.h"
+
+/* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes
+ * continuously. This combines a PID, a modern Linux pidfd and the 64bit inode number of the pidfd into one
+ * structure. Note that depending on kernel support the pidfd might not be initialized, and if it is
+ * initialized then fd_id might still not be initialized (because the concept was added to the kernel much
+ * later than pidfds themselves).
+ *
+ * There are three special states a PidRef can be in:
+ *
+ * 1. It can be *unset*. Use pidref_is_set() to detect this case. Most operations attempted on such a PidRef
+ * will fail with -ESRCH. Use PIDREF_NULL for initializing a PidRef in this state.
+ *
+ * 2. It can be marked as *automatic*. This is a special state indicating that a process reference is
+ * supposed to be derived automatically from the current context. This is used by the Varlink/JSON
+ * dispatcher as indication that a PidRef shall be derived from the connection peer, but might be
+ * otherwise used too. When marked *automatic* the PidRef will also be considered *unset*, hence most
+ * operations will fail with -ESRCH, as above.
+ *
+ * 3. It can be marked as *remote*. This is useful when deserializing a PidRef structure from an IPC message
+ * or similar, and it has been determined that the given PID definitely doesn't refer to a local
+ * process. In this case the PidRef logic will refrain from trying to acquire a pidfd for the
+ * process. Moreover, most operations will fail with -EREMOTE. Only PidRef structures that are not marked
+ * *unset* can be marked *remote*.
+ */
+struct PidRef {
+ pid_t pid; /* > 0 if the PidRef is set, otherwise set to PID_AUTOMATIC if automatic mode is
+ * desired, or 0 otherwise. */
+ int fd; /* only valid if pidfd are available in the kernel, and we manage to get an fd. If we
+ * know that the PID is not from the local machine we set this to -EREMOTE, otherwise
+ * we use -EBADF as indicator the fd is invalid. */
uint64_t fd_id; /* the inode number of pidfd. only useful in kernel 6.9+ where pidfds live in
their own pidfs and each process comes with a unique inode number */
-} PidRef;
+};
#define PIDREF_NULL (const PidRef) { .fd = -EBADF }
+/* A special pidref value that we are using when a PID shall be automatically acquired from some surrounding
+ * context, for example connection peer. Much like PIDREF_NULL it will be considered unset by
+ * pidref_is_set().*/
+#define PIDREF_AUTOMATIC (const PidRef) { .pid = PID_AUTOMATIC, .fd = -EBADF }
+
/* Turns a pid_t into a PidRef structure on-the-fly *without* acquiring a pidfd for it. (As opposed to
* pidref_set_pid() which does so *with* acquiring one, see below) */
#define PIDREF_MAKE_FROM_PID(x) (PidRef) { .pid = (x), .fd = -EBADF }
@@ -21,6 +54,14 @@ static inline bool pidref_is_set(const PidRef *pidref) {
return pidref && pidref->pid > 0;
}
+bool pidref_is_automatic(const PidRef *pidref);
+
+static inline bool pidref_is_remote(const PidRef *pidref) {
+ /* If the fd is set to -EREMOTE we assume PidRef does not refer to a local PID, but on another
+ * machine (and we just got the PidRef initialized due to deserialization of some RPC message) */
+ return pidref_is_set(pidref) && pidref->fd == -EREMOTE;
+}
+
int pidref_acquire_pidfd_id(PidRef *pidref);
bool pidref_equal(PidRef *a, PidRef *b);
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 2efe89e135..05b7a69fc6 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -15,6 +15,7 @@
#include "format-util.h"
#include "macro.h"
#include "namespace-util.h"
+#include "pidref.h"
#include "time-util.h"
#define procfs_file_alloca(pid, field) \
@@ -148,8 +149,7 @@ static inline bool sched_priority_is_valid(int i) {
return i >= 0 && i <= sched_get_priority_max(SCHED_RR);
}
-#define PID_AUTOMATIC ((pid_t) INT_MIN) /* special value indicating "acquire pid from connection peer */
-#define PID_INVALID ((pid_t) 0) /* default value for "invalid pid" */
+#define PID_AUTOMATIC ((pid_t) INT_MIN) /* special value indicating "acquire pid from connection peer" */
static inline bool pid_is_valid(pid_t p) {
return p > 0;
@@ -159,10 +159,6 @@ static inline bool pid_is_automatic(pid_t p) {
return p == PID_AUTOMATIC;
}
-static inline bool pid_is_valid_or_automatic(pid_t p) {
- return pid_is_valid(p) || pid_is_automatic(p);
-}
-
pid_t getpid_cached(void);
void reset_cached_pid(void);
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index ba86b907a7..874e559a4b 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -29,7 +29,7 @@ union sockaddr_union {
/* The libc provided version that allocates "enough room" for every protocol */
struct sockaddr_storage storage;
- /* Protoctol-specific implementations */
+ /* Protocol-specific implementations */
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
@@ -390,7 +390,7 @@ int socket_address_parse_unix(SocketAddress *ret_address, const char *s);
int socket_address_parse_vsock(SocketAddress *ret_address, const char *s);
/* libc's SOMAXCONN is defined to 128 or 4096 (at least on glibc). But actually, the value can be much
- * larger. In our codebase we want to set it to the max usually, since noawadays socket memory is properly
+ * larger. In our codebase we want to set it to the max usually, since nowadays socket memory is properly
* tracked by memcg, and hence we don't need to enforce extra limits here. Moreover, the kernel caps it to
* /proc/sys/net/core/somaxconn anyway, thus by setting this to unbounded we just make that sysctl file
* authoritative. */
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index f05e66d80d..2181ee2df5 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -157,25 +157,9 @@ int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup)
struct dirent *buf;
size_t m;
- if (path) {
- assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
-
- fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
- } else if (dir_fd == AT_FDCWD) {
- fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
- } else {
- /* Note that DUPing is not enough, as the internal pointer would still be shared and moved
- * getedents64(). */
- assert(dir_fd >= 0);
-
- fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
- if (fd < 0)
- return fd;
- }
+ fd = xopenat(dir_fd, path, O_DIRECTORY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
/* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." +
* ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If
diff --git a/src/basic/sysctl-util.c b/src/basic/sysctl-util.c
index 8a73dc6b3e..2feb4917d7 100644
--- a/src/basic/sysctl-util.c
+++ b/src/basic/sysctl-util.c
@@ -10,6 +10,7 @@
#include "fileio.h"
#include "log.h"
#include "macro.h"
+#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
#include "string-util.h"
@@ -193,3 +194,29 @@ int sysctl_read_ip_property(int af, const char *ifname, const char *property, ch
return sysctl_read(p, ret);
}
+
+int sysctl_read_ip_property_int(int af, const char *ifname, const char *property, int *ret) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ assert(ret);
+
+ r = sysctl_read_ip_property(af, ifname, property, &s);
+ if (r < 0)
+ return r;
+
+ return safe_atoi(s, ret);
+}
+
+int sysctl_read_ip_property_uint32(int af, const char *ifname, const char *property, uint32_t *ret) {
+ _cleanup_free_ char *s = NULL;
+ int r;
+
+ assert(ret);
+
+ r = sysctl_read_ip_property(af, ifname, property, &s);
+ if (r < 0)
+ return r;
+
+ return safe_atou32(s, ret);
+}
diff --git a/src/basic/sysctl-util.h b/src/basic/sysctl-util.h
index 041292f693..bbfc4ce1ea 100644
--- a/src/basic/sysctl-util.h
+++ b/src/basic/sysctl-util.h
@@ -17,6 +17,8 @@ static inline int sysctl_write(const char *property, const char *value) {
}
int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret);
+int sysctl_read_ip_property_int(int af, const char *ifname, const char *property, int *ret);
+int sysctl_read_ip_property_uint32(int af, const char *ifname, const char *property, uint32_t *ret);
int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value, Hashmap **shadow);
static inline int sysctl_write_ip_property_boolean(int af, const char *ifname, const char *property, bool value, Hashmap **shadow) {
return sysctl_write_ip_property(af, ifname, property, one_zero(value), shadow);
diff --git a/src/boot/efi/log.c b/src/boot/efi/log.c
index 8ada8e9d8c..edad4125c6 100644
--- a/src/boot/efi/log.c
+++ b/src/boot/efi/log.c
@@ -52,15 +52,6 @@ EFI_STATUS log_internal(EFI_STATUS status, uint8_t text_color, const char *forma
return status;
}
-#ifdef EFI_DEBUG
-void log_hexdump(const char16_t *prefix, const void *data, size_t size) {
- /* Debugging helper — please keep this around, even if not used */
-
- _cleanup_free_ char16_t *hex = hexdump(data, size);
- log_internal(EFI_SUCCESS, EFI_LIGHTRED, "%ls[%zu]: %ls", prefix, size, hex);
-}
-#endif
-
void log_wait(void) {
if (log_count == 0)
return;
diff --git a/src/boot/efi/log.h b/src/boot/efi/log.h
index 9ea8e1891f..0699631b89 100644
--- a/src/boot/efi/log.h
+++ b/src/boot/efi/log.h
@@ -23,13 +23,17 @@ __attribute__((no_stack_protector, noinline)) void __stack_chk_guard_init(void);
_noreturn_ void freeze(void);
void log_wait(void);
_gnu_printf_(3, 4) EFI_STATUS log_internal(EFI_STATUS status, uint8_t text_color, const char *format, ...);
-#define log_debug(...) log_internal(EFI_SUCCESS, EFI_LIGHTGRAY, __VA_ARGS__)
-#define log_info(...) log_internal(EFI_SUCCESS, EFI_WHITE, __VA_ARGS__)
-#define log_error_status(status, ...) log_internal(status, EFI_LIGHTRED, __VA_ARGS__)
-#define log_error(...) log_internal(EFI_INVALID_PARAMETER, EFI_LIGHTRED, __VA_ARGS__)
-#define log_oom() log_internal(EFI_OUT_OF_RESOURCES, EFI_LIGHTRED, "Out of memory.")
-#define log_trace() log_internal(EFI_SUCCESS, EFI_LIGHTRED, "%s:%i@%s", __FILE__, __LINE__, __func__)
+#define log_full(status, text_color, format, ...) \
+ log_internal(status, text_color, "%s:%i@%s: " format, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
+#define log_debug(...) log_full(EFI_SUCCESS, EFI_LIGHTGRAY, __VA_ARGS__)
+#define log_info(...) log_full(EFI_SUCCESS, EFI_WHITE, __VA_ARGS__)
+#define log_error_status(status, ...) log_full(status, EFI_LIGHTRED, __VA_ARGS__)
+#define log_error(...) log_full(EFI_INVALID_PARAMETER, EFI_LIGHTRED, __VA_ARGS__)
+#define log_oom() log_full(EFI_OUT_OF_RESOURCES, EFI_LIGHTRED, "Out of memory.")
-#ifdef EFI_DEBUG
-void log_hexdump(const char16_t *prefix, const void *data, size_t size);
-#endif
+/* Debugging helper — please keep this around, even if not used */
+#define log_hexdump(prefix, data, size) \
+ ({ \
+ _cleanup_free_ char16_t *hex = hexdump(data, size); \
+ log_debug("%ls[%zu]: %ls", prefix, size, hex); \
+ })
diff --git a/src/boot/efi/smbios.c b/src/boot/efi/smbios.c
index 997428a9c4..329619f85b 100644
--- a/src/boot/efi/smbios.c
+++ b/src/boot/efi/smbios.c
@@ -85,7 +85,7 @@ typedef struct {
char contents[];
} _packed_ SmbiosTableType11;
-static const void *find_smbios_configuration_table(uint64_t *ret_size) {
+static const void* find_smbios_configuration_table(uint64_t *ret_size) {
assert(ret_size);
const Smbios3EntryPoint *entry3 = find_configuration_table(MAKE_GUID_PTR(SMBIOS3_TABLE));
@@ -102,32 +102,33 @@ static const void *find_smbios_configuration_table(uint64_t *ret_size) {
return PHYSICAL_ADDRESS_TO_POINTER(entry->table_address);
}
+ *ret_size = 0;
return NULL;
}
-static const SmbiosHeader *get_smbios_table(uint8_t type, size_t min_size, uint64_t *ret_size_left) {
- uint64_t size = 0;
+static const SmbiosHeader* get_smbios_table(uint8_t type, size_t min_size, uint64_t *ret_size_left) {
+ uint64_t size;
const uint8_t *p = find_smbios_configuration_table(&size);
if (!p)
- return NULL;
+ goto not_found;
for (;;) {
if (size < sizeof(SmbiosHeader))
- return NULL;
+ goto not_found;
const SmbiosHeader *header = (const SmbiosHeader *) p;
/* End of table. */
if (header->type == 127)
- return NULL;
+ goto not_found;
if (size < header->length)
- return NULL;
+ goto not_found;
if (header->type == type) {
/* Table is smaller than the minimum expected size? Refuse */
if (header->length < min_size)
- return NULL;
+ goto not_found;
if (ret_size_left)
*ret_size_left = size;
@@ -150,7 +151,7 @@ static const SmbiosHeader *get_smbios_table(uint8_t type, size_t min_size, uint6
for (;;) {
const uint8_t *e = memchr(p, 0, size);
if (!e)
- return NULL;
+ goto not_found;
if (!first && e == p) {/* Double NUL byte means we've reached the end of the string table. */
p++;
@@ -164,6 +165,10 @@ static const SmbiosHeader *get_smbios_table(uint8_t type, size_t min_size, uint6
}
}
+not_found:
+ if (ret_size_left)
+ *ret_size_left = 0;
+
return NULL;
}
@@ -186,13 +191,10 @@ const char* smbios_find_oem_string(const char *name) {
if (!type11)
return NULL;
- const char *s = type11->contents;
-
assert(left >= type11->header.length); /* get_smbios_table() already validated this */
left -= type11->header.length;
- const char *limit = s + left;
- for (const char *p = s; p < limit; ) {
+ for (const char *p = type11->contents, *limit = type11->contents + left; p < limit; ) {
const char *e = memchr(p, 0, limit - p);
if (!e || e == p) /* Double NUL byte means we've reached the end of the OEM strings. */
break;
@@ -208,8 +210,7 @@ const char* smbios_find_oem_string(const char *name) {
}
static const char* smbios_get_string(const SmbiosHeader *header, size_t nr, uint64_t left) {
- assert(header);
- const char *s = (const char *) header;
+ const char *s = (const char *) ASSERT_PTR(header);
/* We assume that get_smbios_table() already validated the header size making some superficial sense */
assert(left >= header->length);
@@ -227,24 +228,34 @@ static const char* smbios_get_string(const SmbiosHeader *header, size_t nr, uint
p = e + 1;
}
+
return NULL;
}
void smbios_raw_info_populate(RawSmbiosInfo *ret_info) {
- assert(ret_info);
uint64_t left;
+ assert(ret_info);
+
const SmbiosTableType1 *type1 = (const SmbiosTableType1 *) get_smbios_table(1, sizeof(SmbiosTableType1), &left);
if (type1) {
ret_info->manufacturer = smbios_get_string(&type1->header, type1->manufacturer, left);
ret_info->product_name = smbios_get_string(&type1->header, type1->product_name, left);
ret_info->product_sku = smbios_get_string(&type1->header, type1->sku_number, left);
ret_info->family = smbios_get_string(&type1->header, type1->family, left);
+ } else {
+ ret_info->manufacturer = NULL;
+ ret_info->product_name = NULL;
+ ret_info->product_sku = NULL;
+ ret_info->family = NULL;
}
const SmbiosTableType2 *type2 = (const SmbiosTableType2 *) get_smbios_table(2, sizeof(SmbiosTableType2), &left);
if (type2) {
ret_info->baseboard_manufacturer = smbios_get_string(&type2->header, type2->manufacturer, left);
ret_info->baseboard_product = smbios_get_string(&type2->header, type2->product_name, left);
+ } else {
+ ret_info->baseboard_manufacturer = NULL;
+ ret_info->baseboard_product = NULL;
}
}
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index 35f6c80a02..054d49ef02 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -4,8 +4,8 @@
#include "efi.h"
#include "efi-string.h"
#include "log.h"
-#include "proto/file-io.h"
#include "memory-util-fundamental.h"
+#include "proto/file-io.h"
#include "string-util-fundamental.h"
/* This is provided by the linker. */
diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c
index c3e0a3a633..1e352a3729 100644
--- a/src/busctl/busctl.c
+++ b/src/busctl/busctl.c
@@ -21,6 +21,7 @@
#include "fileio.h"
#include "format-table.h"
#include "glyph-util.h"
+#include "json-util.h"
#include "log.h"
#include "main-func.h"
#include "memstream-util.h"
@@ -1728,18 +1729,22 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, FDSe
static int json_transform_one(sd_bus_message *m, sd_json_variant **ret);
-static int json_transform_and_append(sd_bus_message *m, sd_json_variant **ret) {
+static int json_transform_and_append(sd_bus_message *m, sd_json_variant **array) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *element = NULL;
int r;
assert(m);
- assert(ret);
+ assert(array);
r = json_transform_one(m, &element);
if (r < 0)
return r;
- return sd_json_variant_append_array(ret, element);
+ r = sd_json_variant_append_array(array, element);
+ if (r < 0)
+ return log_error_errno(r, "Failed to append json element to array: %m");
+
+ return 0;
}
static int json_transform_array_or_struct(sd_bus_message *m, sd_json_variant **ret) {
@@ -1749,6 +1754,10 @@ static int json_transform_array_or_struct(sd_bus_message *m, sd_json_variant **r
assert(m);
assert(ret);
+ r = sd_json_variant_new_array(&array, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate json empty array: %m");
+
for (;;) {
r = sd_bus_message_at_end(m, false);
if (r < 0)
@@ -1761,9 +1770,6 @@ static int json_transform_array_or_struct(sd_bus_message *m, sd_json_variant **r
return r;
}
- if (!array)
- return sd_json_variant_new_array(ret, NULL, 0);
-
*ret = TAKE_PTR(array);
return 0;
}
@@ -1784,7 +1790,7 @@ static int json_transform_variant(sd_bus_message *m, const char *contents, sd_js
SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(contents)),
SD_JSON_BUILD_PAIR("data", SD_JSON_BUILD_VARIANT(value)));
if (r < 0)
- return log_oom();
+ return log_error_errno(r, "Failed to build json object: %m");
return r;
}
@@ -1811,7 +1817,7 @@ static int json_transform_dict_array(sd_bus_message *m, sd_json_variant **ret) {
r = sd_bus_message_peek_type(m, &type, &contents);
if (r < 0)
- return r;
+ return bus_log_parse_error(r);
assert(type == 'e');
@@ -1839,7 +1845,11 @@ static int json_transform_dict_array(sd_bus_message *m, sd_json_variant **ret) {
return bus_log_parse_error(r);
}
- return sd_json_variant_new_object(ret, elements, n_elements);
+ r = sd_json_variant_new_object(ret, elements, n_elements);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create new json object: %m");
+
+ return 0;
}
static int json_transform_one(sd_bus_message *m, sd_json_variant **ret) {
@@ -1999,16 +2009,19 @@ static int json_transform_one(sd_bus_message *m, sd_json_variant **ret) {
break;
}
- case SD_BUS_TYPE_UNIX_FD:
- r = sd_bus_message_read_basic(m, type, NULL);
+ case SD_BUS_TYPE_UNIX_FD: {
+ int fd;
+
+ r = sd_bus_message_read_basic(m, type, &fd);
if (r < 0)
return bus_log_parse_error(r);
- r = sd_json_variant_new_null(&v);
+ r = json_variant_new_fd_info(&v, fd);
if (r < 0)
return log_error_errno(r, "Failed to transform fd: %m");
break;
+ }
case SD_BUS_TYPE_ARRAY:
case SD_BUS_TYPE_VARIANT:
@@ -2055,10 +2068,10 @@ static int json_transform_message(sd_bus_message *m, sd_json_variant **ret) {
return r;
r = sd_json_buildo(ret,
- SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(type)),
+ SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(type)),
SD_JSON_BUILD_PAIR("data", SD_JSON_BUILD_VARIANT(v)));
if (r < 0)
- return log_oom();
+ return log_error_errno(r, "Failed to build json object: %m");
return 0;
}
@@ -2130,38 +2143,37 @@ static int call(int argc, char **argv, void *userdata) {
r = sd_bus_message_is_empty(reply);
if (r < 0)
return bus_log_parse_error(r);
+ if (r > 0 || arg_quiet)
+ return 0;
- if (r == 0 && !arg_quiet) {
-
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+ if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- if (arg_json_format_flags & (SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
- pager_open(arg_pager_flags);
+ if (arg_json_format_flags & (SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
+ pager_open(arg_pager_flags);
- r = json_transform_message(reply, &v);
- if (r < 0)
- return r;
+ r = json_transform_message(reply, &v);
+ if (r < 0)
+ return r;
- sd_json_variant_dump(v, arg_json_format_flags, NULL, NULL);
+ sd_json_variant_dump(v, arg_json_format_flags, NULL, NULL);
- } else if (arg_verbose) {
- pager_open(arg_pager_flags);
+ } else if (arg_verbose) {
+ pager_open(arg_pager_flags);
- r = sd_bus_message_dump(reply, stdout, 0);
- if (r < 0)
- return r;
- } else {
+ r = sd_bus_message_dump(reply, stdout, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to dump dbus message: %m");
+ } else {
- fputs(sd_bus_message_get_signature(reply, true), stdout);
- fputc(' ', stdout);
+ fputs(sd_bus_message_get_signature(reply, true), stdout);
+ fputc(' ', stdout);
- r = format_cmdline(reply, stdout, false);
- if (r < 0)
- return bus_log_parse_error(r);
+ r = format_cmdline(reply, stdout, false);
+ if (r < 0)
+ return bus_log_parse_error(r);
- fputc('\n', stdout);
- }
+ fputc('\n', stdout);
}
return 0;
@@ -2289,7 +2301,6 @@ static int on_bus_signal_impl(sd_bus_message *msg) {
r = sd_bus_message_is_empty(msg);
if (r < 0)
return bus_log_parse_error(r);
-
if (r > 0 || arg_quiet)
return 0;
diff --git a/src/core/bpf-restrict-ifaces.c b/src/core/bpf-restrict-ifaces.c
index a39f4895f2..af49abd677 100644
--- a/src/core/bpf-restrict-ifaces.c
+++ b/src/core/bpf-restrict-ifaces.c
@@ -159,7 +159,7 @@ int bpf_restrict_ifaces_install(Unit *u) {
return 0;
r = restrict_ifaces_install_impl(u);
- fdset_close(crt->initial_restrict_ifaces_link_fds);
+ fdset_close(crt->initial_restrict_ifaces_link_fds, /* async= */ false);
return r;
}
diff --git a/src/core/bpf-socket-bind.c b/src/core/bpf-socket-bind.c
index 2a1a0278d5..8853f3eecc 100644
--- a/src/core/bpf-socket-bind.c
+++ b/src/core/bpf-socket-bind.c
@@ -229,7 +229,7 @@ int bpf_socket_bind_install(Unit *u) {
return 0;
r = socket_bind_install_impl(u);
- fdset_close(crt->initial_socket_bind_link_fds);
+ fdset_close(crt->initial_socket_bind_link_fds, /* async= */ false);
return r;
}
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index fb89a22d2e..47a771d51e 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -194,6 +194,9 @@ void cgroup_context_init(CGroupContext *c) {
.moom_swap = MANAGED_OOM_AUTO,
.moom_mem_pressure = MANAGED_OOM_AUTO,
.moom_preference = MANAGED_OOM_PREFERENCE_NONE,
+ /* The default duration value in oomd.conf will be used when
+ * moom_mem_pressure_duration_usec is set to infinity. */
+ .moom_mem_pressure_duration_usec = USEC_INFINITY,
.memory_pressure_watch = _CGROUP_PRESSURE_WATCH_INVALID,
.memory_pressure_threshold_usec = USEC_INFINITY,
@@ -947,6 +950,10 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
fprintf(f, "%sMemoryPressureThresholdSec: %s\n",
prefix, FORMAT_TIMESPAN(c->memory_pressure_threshold_usec, 1));
+ if (c->moom_mem_pressure_duration_usec != USEC_INFINITY)
+ fprintf(f, "%sManagedOOMMemoryPressureDurationSec: %s\n",
+ prefix, FORMAT_TIMESPAN(c->moom_mem_pressure_duration_usec, 1));
+
LIST_FOREACH(device_allow, a, c->device_allow)
/* strna() below should be redundant, for avoiding -Werror=format-overflow= error. See #30223. */
fprintf(f,
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 7525da728e..550c1ea88f 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -236,6 +236,7 @@ struct CGroupContext {
ManagedOOMMode moom_swap;
ManagedOOMMode moom_mem_pressure;
uint32_t moom_mem_pressure_limit; /* Normalized to 2^32-1 == 100% */
+ usec_t moom_mem_pressure_duration_usec;
ManagedOOMPreference moom_preference;
/* Memory pressure logic */
diff --git a/src/core/core-varlink.c b/src/core/core-varlink.c
index 0ecc8e23f1..352fd28b0d 100644
--- a/src/core/core-varlink.c
+++ b/src/core/core-varlink.c
@@ -57,7 +57,7 @@ static bool user_match_lookup_parameters(LookupParameters *p, const char *name,
}
static int build_managed_oom_json_array_element(Unit *u, const char *property, sd_json_variant **ret_v) {
- bool use_limit = false;
+ bool use_limit = false, use_duration = false;
CGroupContext *c;
const char *mode;
@@ -84,7 +84,8 @@ static int build_managed_oom_json_array_element(Unit *u, const char *property, s
mode = managed_oom_mode_to_string(c->moom_swap);
else if (streq(property, "ManagedOOMMemoryPressure")) {
mode = managed_oom_mode_to_string(c->moom_mem_pressure);
- use_limit = true;
+ use_limit = c->moom_mem_pressure_limit > 0;
+ use_duration = c->moom_mem_pressure_duration_usec != USEC_INFINITY;
} else
return -EINVAL;
@@ -92,7 +93,8 @@ static int build_managed_oom_json_array_element(Unit *u, const char *property, s
SD_JSON_BUILD_PAIR("mode", SD_JSON_BUILD_STRING(mode)),
SD_JSON_BUILD_PAIR("path", SD_JSON_BUILD_STRING(crt->cgroup_path)),
SD_JSON_BUILD_PAIR("property", SD_JSON_BUILD_STRING(property)),
- SD_JSON_BUILD_PAIR_CONDITION(use_limit, "limit", SD_JSON_BUILD_UNSIGNED(c->moom_mem_pressure_limit)));
+ SD_JSON_BUILD_PAIR_CONDITION(use_limit, "limit", SD_JSON_BUILD_UNSIGNED(c->moom_mem_pressure_limit)),
+ SD_JSON_BUILD_PAIR_CONDITION(use_duration, "duration", SD_JSON_BUILD_UNSIGNED(c->moom_mem_pressure_duration_usec)));
}
static int build_managed_oom_cgroups_json(Manager *m, sd_json_variant **ret) {
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 459fa6f774..445132a659 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -502,6 +502,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("ManagedOOMSwap", "s", property_get_managed_oom_mode, offsetof(CGroupContext, moom_swap), 0),
SD_BUS_PROPERTY("ManagedOOMMemoryPressure", "s", property_get_managed_oom_mode, offsetof(CGroupContext, moom_mem_pressure), 0),
SD_BUS_PROPERTY("ManagedOOMMemoryPressureLimit", "u", NULL, offsetof(CGroupContext, moom_mem_pressure_limit), 0),
+ SD_BUS_PROPERTY("ManagedOOMMemoryPressureDurationUSec", "t", bus_property_get_usec, offsetof(CGroupContext, moom_mem_pressure_duration_usec), 0),
SD_BUS_PROPERTY("ManagedOOMPreference", "s", property_get_managed_oom_preference, offsetof(CGroupContext, moom_preference), 0),
SD_BUS_PROPERTY("BPFProgram", "a(ss)", property_get_bpf_foreign_program, 0, 0),
SD_BUS_PROPERTY("SocketBindAllow", "a(iiqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_allow), 0),
@@ -2053,6 +2054,36 @@ int bus_cgroup_set_property(
return 1;
}
+ if (streq(name, "ManagedOOMMemoryPressureDurationUSec")) {
+ uint64_t t;
+
+ if (!UNIT_VTABLE(u)->can_set_managed_oom)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set %s for this unit type", name);
+
+ r = sd_bus_message_read(message, "t", &t);
+ if (r < 0)
+ return r;
+
+ if (t < 1 * USEC_PER_SEC)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= must be at least 1s, got %s", name,
+ FORMAT_TIMESPAN(t, USEC_PER_SEC));
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ c->memory_pressure_threshold_usec = t;
+ if (c->memory_pressure_threshold_usec == USEC_INFINITY)
+ unit_write_setting(u, flags, name, "ManagedOOMMemoryPressureDurationSec=");
+ else
+ unit_write_settingf(u, flags, name,
+ "ManagedOOMMemoryPressureDurationSec=%s",
+ FORMAT_TIMESPAN(c->memory_pressure_threshold_usec, 1));
+ }
+
+ if (c->moom_mem_pressure == MANAGED_OOM_KILL)
+ (void) manager_varlink_send_managed_oom_update(u);
+
+ return 1;
+ }
+
if (streq(name, "ManagedOOMPreference")) {
ManagedOOMPreference p;
const char *pref;
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 77e9a08bdd..85eb99f218 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -168,7 +168,7 @@ static int bus_service_method_mount(sd_bus_message *message, void *userdata, sd_
r = unit_can_live_mount(u, error);
if (r < 0)
- return r;
+ return log_unit_debug_errno(u, r, "Cannot schedule live mount operation: %s", bus_error_message(error, r));
r = mac_selinux_unit_access_check(u, message, "start", error);
if (r < 0)
diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c
index 13e7078b1a..1b44c49238 100644
--- a/src/core/execute-serialize.c
+++ b/src/core/execute-serialize.c
@@ -328,6 +328,10 @@ static int exec_cgroup_context_serialize(const CGroupContext *c, FILE *f) {
if (r < 0)
return r;
+ r = serialize_usec(f, "exec-cgroup-context-managed-oom-memory-pressure-duration-usec", c->moom_mem_pressure_duration_usec);
+ if (r < 0)
+ return r;
+
r = serialize_item(f, "exec-cgroup-context-managed-oom-preference", managed_oom_preference_to_string(c->moom_preference));
if (r < 0)
return r;
@@ -781,6 +785,10 @@ static int exec_cgroup_context_deserialize(CGroupContext *c, FILE *f) {
c->moom_preference = managed_oom_preference_from_string(val);
if (c->moom_preference < 0)
return -EINVAL;
+ } else if ((val = startswith(l, "exec-cgroup-context-managed-oom-memory-pressure-duration-usec="))) {
+ r = deserialize_usec(val, &c->moom_mem_pressure_duration_usec);
+ if (r < 0)
+ return r;
} else if ((val = startswith(l, "exec-cgroup-context-memory-pressure-watch="))) {
c->memory_pressure_watch = cgroup_pressure_watch_from_string(val);
if (c->memory_pressure_watch < 0)
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index e94b518a9d..ea82a578ba 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -2,267 +2,268 @@
{%- macro EXEC_CONTEXT_CONFIG_ITEMS(type) -%}
{# Define the context options only once #}
-{{type}}.WorkingDirectory, config_parse_working_directory, 0, offsetof({{type}}, exec_context)
-{{type}}.RootDirectory, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_directory)
-{{type}}.RootImage, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_image)
-{{type}}.RootImageOptions, config_parse_root_image_options, 0, offsetof({{type}}, exec_context)
-{{type}}.RootImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.root_image_policy)
-{{type}}.RootHash, config_parse_exec_root_hash, 0, offsetof({{type}}, exec_context)
-{{type}}.RootHashSignature, config_parse_exec_root_hash_sig, 0, offsetof({{type}}, exec_context)
-{{type}}.RootVerity, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_verity)
-{{type}}.RootEphemeral, config_parse_bool, 0, offsetof({{type}}, exec_context.root_ephemeral)
-{{type}}.ExtensionDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.extension_directories)
-{{type}}.ExtensionImages, config_parse_extension_images, 0, offsetof({{type}}, exec_context)
-{{type}}.ExtensionImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.extension_image_policy)
-{{type}}.MountImages, config_parse_mount_images, 0, offsetof({{type}}, exec_context)
-{{type}}.MountImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.mount_image_policy)
-{{type}}.User, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.user)
-{{type}}.Group, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.group)
-{{type}}.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof({{type}}, exec_context.supplementary_groups)
-{{type}}.SetLoginEnvironment, config_parse_tristate, 0, offsetof({{type}}, exec_context.set_login_environment)
-{{type}}.Nice, config_parse_exec_nice, 0, offsetof({{type}}, exec_context)
-{{type}}.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof({{type}}, exec_context)
-{{type}}.CoredumpFilter, config_parse_exec_coredump_filter, 0, offsetof({{type}}, exec_context)
-{{type}}.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof({{type}}, exec_context)
-{{type}}.IOSchedulingPriority, config_parse_exec_io_priority, 0, offsetof({{type}}, exec_context)
-{{type}}.CPUSchedulingPolicy, config_parse_exec_cpu_sched_policy, 0, offsetof({{type}}, exec_context)
-{{type}}.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0, offsetof({{type}}, exec_context)
-{{type}}.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof({{type}}, exec_context.cpu_sched_reset_on_fork)
-{{type}}.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof({{type}}, exec_context)
-{{type}}.NUMAPolicy, config_parse_numa_policy, 0, offsetof({{type}}, exec_context.numa_policy.type)
-{{type}}.NUMAMask, config_parse_numa_mask, 0, offsetof({{type}}, exec_context.numa_policy)
-{{type}}.UMask, config_parse_mode, 0, offsetof({{type}}, exec_context.umask)
-{{type}}.Environment, config_parse_environ, 0, offsetof({{type}}, exec_context.environment)
-{{type}}.EnvironmentFile, config_parse_unit_env_file, 0, offsetof({{type}}, exec_context.environment_files)
-{{type}}.PassEnvironment, config_parse_pass_environ, 0, offsetof({{type}}, exec_context.pass_environment)
-{{type}}.UnsetEnvironment, config_parse_unset_environ, 0, offsetof({{type}}, exec_context.unset_environment)
-{{type}}.DynamicUser, config_parse_bool, true, offsetof({{type}}, exec_context.dynamic_user)
-{{type}}.RemoveIPC, config_parse_bool, 0, offsetof({{type}}, exec_context.remove_ipc)
-{{type}}.StandardInput, config_parse_exec_input, 0, offsetof({{type}}, exec_context)
-{{type}}.StandardOutput, config_parse_exec_output, 0, offsetof({{type}}, exec_context)
-{{type}}.StandardError, config_parse_exec_output, 0, offsetof({{type}}, exec_context)
-{{type}}.StandardInputText, config_parse_exec_input_text, 0, offsetof({{type}}, exec_context)
-{{type}}.StandardInputData, config_parse_exec_input_data, 0, offsetof({{type}}, exec_context)
-{{type}}.TTYPath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.tty_path)
-{{type}}.TTYReset, config_parse_bool, 0, offsetof({{type}}, exec_context.tty_reset)
-{{type}}.TTYVHangup, config_parse_bool, 0, offsetof({{type}}, exec_context.tty_vhangup)
-{{type}}.TTYVTDisallocate, config_parse_bool, 0, offsetof({{type}}, exec_context.tty_vt_disallocate)
-{{type}}.TTYRows, config_parse_tty_size, 0, offsetof({{type}}, exec_context.tty_rows)
-{{type}}.TTYColumns, config_parse_tty_size, 0, offsetof({{type}}, exec_context.tty_cols)
-{{type}}.SyslogIdentifier, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.syslog_identifier)
-{{type}}.SyslogFacility, config_parse_log_facility, 0, offsetof({{type}}, exec_context.syslog_priority)
-{{type}}.SyslogLevel, config_parse_log_level, 0, offsetof({{type}}, exec_context.syslog_priority)
-{{type}}.SyslogLevelPrefix, config_parse_bool, 0, offsetof({{type}}, exec_context.syslog_level_prefix)
-{{type}}.LogLevelMax, config_parse_log_level, 0, offsetof({{type}}, exec_context.log_level_max)
-{{type}}.LogRateLimitIntervalSec, config_parse_sec, 0, offsetof({{type}}, exec_context.log_ratelimit.interval)
-{{type}}.LogRateLimitBurst, config_parse_unsigned, 0, offsetof({{type}}, exec_context.log_ratelimit.burst)
-{{type}}.LogExtraFields, config_parse_log_extra_fields, 0, offsetof({{type}}, exec_context)
-{{type}}.LogFilterPatterns, config_parse_log_filter_patterns, 0, offsetof({{type}}, exec_context)
-{{type}}.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof({{type}}, exec_context)
-{{type}}.SecureBits, config_parse_exec_secure_bits, 0, offsetof({{type}}, exec_context.secure_bits)
-{{type}}.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof({{type}}, exec_context.capability_bounding_set)
-{{type}}.AmbientCapabilities, config_parse_capability_set, 0, offsetof({{type}}, exec_context.capability_ambient_set)
-{{type}}.TimerSlackNSec, config_parse_nsec, 0, offsetof({{type}}, exec_context.timer_slack_nsec)
-{{type}}.NoNewPrivileges, config_parse_bool, 0, offsetof({{type}}, exec_context.no_new_privileges)
-{{type}}.KeyringMode, config_parse_exec_keyring_mode, 0, offsetof({{type}}, exec_context.keyring_mode)
-{{type}}.ProtectProc, config_parse_protect_proc, 0, offsetof({{type}}, exec_context.protect_proc)
-{{type}}.ProcSubset, config_parse_proc_subset, 0, offsetof({{type}}, exec_context.proc_subset)
+{{type}}.WorkingDirectory, config_parse_working_directory, 0, offsetof({{type}}, exec_context)
+{{type}}.RootDirectory, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_directory)
+{{type}}.RootImage, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_image)
+{{type}}.RootImageOptions, config_parse_root_image_options, 0, offsetof({{type}}, exec_context)
+{{type}}.RootImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.root_image_policy)
+{{type}}.RootHash, config_parse_exec_root_hash, 0, offsetof({{type}}, exec_context)
+{{type}}.RootHashSignature, config_parse_exec_root_hash_sig, 0, offsetof({{type}}, exec_context)
+{{type}}.RootVerity, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_verity)
+{{type}}.RootEphemeral, config_parse_bool, 0, offsetof({{type}}, exec_context.root_ephemeral)
+{{type}}.ExtensionDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.extension_directories)
+{{type}}.ExtensionImages, config_parse_extension_images, 0, offsetof({{type}}, exec_context)
+{{type}}.ExtensionImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.extension_image_policy)
+{{type}}.MountImages, config_parse_mount_images, 0, offsetof({{type}}, exec_context)
+{{type}}.MountImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.mount_image_policy)
+{{type}}.User, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.user)
+{{type}}.Group, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.group)
+{{type}}.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof({{type}}, exec_context.supplementary_groups)
+{{type}}.SetLoginEnvironment, config_parse_tristate, 0, offsetof({{type}}, exec_context.set_login_environment)
+{{type}}.Nice, config_parse_exec_nice, 0, offsetof({{type}}, exec_context)
+{{type}}.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof({{type}}, exec_context)
+{{type}}.CoredumpFilter, config_parse_exec_coredump_filter, 0, offsetof({{type}}, exec_context)
+{{type}}.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof({{type}}, exec_context)
+{{type}}.IOSchedulingPriority, config_parse_exec_io_priority, 0, offsetof({{type}}, exec_context)
+{{type}}.CPUSchedulingPolicy, config_parse_exec_cpu_sched_policy, 0, offsetof({{type}}, exec_context)
+{{type}}.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0, offsetof({{type}}, exec_context)
+{{type}}.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof({{type}}, exec_context.cpu_sched_reset_on_fork)
+{{type}}.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof({{type}}, exec_context)
+{{type}}.NUMAPolicy, config_parse_numa_policy, 0, offsetof({{type}}, exec_context.numa_policy.type)
+{{type}}.NUMAMask, config_parse_numa_mask, 0, offsetof({{type}}, exec_context.numa_policy)
+{{type}}.UMask, config_parse_mode, 0, offsetof({{type}}, exec_context.umask)
+{{type}}.Environment, config_parse_environ, 0, offsetof({{type}}, exec_context.environment)
+{{type}}.EnvironmentFile, config_parse_unit_env_file, 0, offsetof({{type}}, exec_context.environment_files)
+{{type}}.PassEnvironment, config_parse_pass_environ, 0, offsetof({{type}}, exec_context.pass_environment)
+{{type}}.UnsetEnvironment, config_parse_unset_environ, 0, offsetof({{type}}, exec_context.unset_environment)
+{{type}}.DynamicUser, config_parse_bool, true, offsetof({{type}}, exec_context.dynamic_user)
+{{type}}.RemoveIPC, config_parse_bool, 0, offsetof({{type}}, exec_context.remove_ipc)
+{{type}}.StandardInput, config_parse_exec_input, 0, offsetof({{type}}, exec_context)
+{{type}}.StandardOutput, config_parse_exec_output, 0, offsetof({{type}}, exec_context)
+{{type}}.StandardError, config_parse_exec_output, 0, offsetof({{type}}, exec_context)
+{{type}}.StandardInputText, config_parse_exec_input_text, 0, offsetof({{type}}, exec_context)
+{{type}}.StandardInputData, config_parse_exec_input_data, 0, offsetof({{type}}, exec_context)
+{{type}}.TTYPath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.tty_path)
+{{type}}.TTYReset, config_parse_bool, 0, offsetof({{type}}, exec_context.tty_reset)
+{{type}}.TTYVHangup, config_parse_bool, 0, offsetof({{type}}, exec_context.tty_vhangup)
+{{type}}.TTYVTDisallocate, config_parse_bool, 0, offsetof({{type}}, exec_context.tty_vt_disallocate)
+{{type}}.TTYRows, config_parse_tty_size, 0, offsetof({{type}}, exec_context.tty_rows)
+{{type}}.TTYColumns, config_parse_tty_size, 0, offsetof({{type}}, exec_context.tty_cols)
+{{type}}.SyslogIdentifier, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.syslog_identifier)
+{{type}}.SyslogFacility, config_parse_log_facility, 0, offsetof({{type}}, exec_context.syslog_priority)
+{{type}}.SyslogLevel, config_parse_log_level, 0, offsetof({{type}}, exec_context.syslog_priority)
+{{type}}.SyslogLevelPrefix, config_parse_bool, 0, offsetof({{type}}, exec_context.syslog_level_prefix)
+{{type}}.LogLevelMax, config_parse_log_level, 0, offsetof({{type}}, exec_context.log_level_max)
+{{type}}.LogRateLimitIntervalSec, config_parse_sec, 0, offsetof({{type}}, exec_context.log_ratelimit.interval)
+{{type}}.LogRateLimitBurst, config_parse_unsigned, 0, offsetof({{type}}, exec_context.log_ratelimit.burst)
+{{type}}.LogExtraFields, config_parse_log_extra_fields, 0, offsetof({{type}}, exec_context)
+{{type}}.LogFilterPatterns, config_parse_log_filter_patterns, 0, offsetof({{type}}, exec_context)
+{{type}}.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof({{type}}, exec_context)
+{{type}}.SecureBits, config_parse_exec_secure_bits, 0, offsetof({{type}}, exec_context.secure_bits)
+{{type}}.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof({{type}}, exec_context.capability_bounding_set)
+{{type}}.AmbientCapabilities, config_parse_capability_set, 0, offsetof({{type}}, exec_context.capability_ambient_set)
+{{type}}.TimerSlackNSec, config_parse_nsec, 0, offsetof({{type}}, exec_context.timer_slack_nsec)
+{{type}}.NoNewPrivileges, config_parse_bool, 0, offsetof({{type}}, exec_context.no_new_privileges)
+{{type}}.KeyringMode, config_parse_exec_keyring_mode, 0, offsetof({{type}}, exec_context.keyring_mode)
+{{type}}.ProtectProc, config_parse_protect_proc, 0, offsetof({{type}}, exec_context.protect_proc)
+{{type}}.ProcSubset, config_parse_proc_subset, 0, offsetof({{type}}, exec_context.proc_subset)
{% if HAVE_SECCOMP %}
-{{type}}.SystemCallFilter, config_parse_syscall_filter, 0, offsetof({{type}}, exec_context)
-{{type}}.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof({{type}}, exec_context.syscall_archs)
-{{type}}.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof({{type}}, exec_context)
-{{type}}.SystemCallLog, config_parse_syscall_log, 0, offsetof({{type}}, exec_context)
-{{type}}.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof({{type}}, exec_context.memory_deny_write_execute)
-{{type}}.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof({{type}}, exec_context)
-{{type}}.RestrictRealtime, config_parse_bool, 0, offsetof({{type}}, exec_context.restrict_realtime)
-{{type}}.RestrictSUIDSGID, config_parse_bool, 0, offsetof({{type}}, exec_context.restrict_suid_sgid)
-{{type}}.RestrictAddressFamilies, config_parse_address_families, 0, offsetof({{type}}, exec_context)
-{{type}}.LockPersonality, config_parse_bool, 0, offsetof({{type}}, exec_context.lock_personality)
+{{type}}.SystemCallFilter, config_parse_syscall_filter, 0, offsetof({{type}}, exec_context)
+{{type}}.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof({{type}}, exec_context.syscall_archs)
+{{type}}.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof({{type}}, exec_context)
+{{type}}.SystemCallLog, config_parse_syscall_log, 0, offsetof({{type}}, exec_context)
+{{type}}.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof({{type}}, exec_context.memory_deny_write_execute)
+{{type}}.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof({{type}}, exec_context)
+{{type}}.RestrictRealtime, config_parse_bool, 0, offsetof({{type}}, exec_context.restrict_realtime)
+{{type}}.RestrictSUIDSGID, config_parse_bool, 0, offsetof({{type}}, exec_context.restrict_suid_sgid)
+{{type}}.RestrictAddressFamilies, config_parse_address_families, 0, offsetof({{type}}, exec_context)
+{{type}}.LockPersonality, config_parse_bool, 0, offsetof({{type}}, exec_context.lock_personality)
{% else %}
-{{type}}.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-{{type}}.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-{{type}}.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-{{type}}.SystemCallLog, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-{{type}}.MemoryDenyWriteExecute, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-{{type}}.RestrictNamespaces, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-{{type}}.RestrictRealtime, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-{{type}}.RestrictSUIDSGID, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-{{type}}.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-{{type}}.LockPersonality, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.SystemCallLog, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.MemoryDenyWriteExecute, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.RestrictNamespaces, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.RestrictRealtime, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.RestrictSUIDSGID, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.LockPersonality, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
{% endif %}
-{{type}}.RestrictFileSystems, config_parse_restrict_filesystems, 0, offsetof({{type}}, exec_context)
-{{type}}.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitFSIZE, config_parse_rlimit, RLIMIT_FSIZE, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitDATA, config_parse_rlimit, RLIMIT_DATA, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitSTACK, config_parse_rlimit, RLIMIT_STACK, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitCORE, config_parse_rlimit, RLIMIT_CORE, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitRSS, config_parse_rlimit, RLIMIT_RSS, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitNOFILE, config_parse_rlimit, RLIMIT_NOFILE, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitAS, config_parse_rlimit, RLIMIT_AS, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitNPROC, config_parse_rlimit, RLIMIT_NPROC, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitMEMLOCK, config_parse_rlimit, RLIMIT_MEMLOCK, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitLOCKS, config_parse_rlimit, RLIMIT_LOCKS, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitSIGPENDING, config_parse_rlimit, RLIMIT_SIGPENDING, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitMSGQUEUE, config_parse_rlimit, RLIMIT_MSGQUEUE, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitNICE, config_parse_rlimit, RLIMIT_NICE, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitRTPRIO, config_parse_rlimit, RLIMIT_RTPRIO, offsetof({{type}}, exec_context.rlimit)
-{{type}}.LimitRTTIME, config_parse_rlimit, RLIMIT_RTTIME, offsetof({{type}}, exec_context.rlimit)
-{{type}}.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.read_write_paths)
-{{type}}.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.read_only_paths)
-{{type}}.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.inaccessible_paths)
-{{type}}.ReadWritePaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.read_write_paths)
-{{type}}.ReadOnlyPaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.read_only_paths)
-{{type}}.InaccessiblePaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.inaccessible_paths)
-{{type}}.ExecPaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.exec_paths)
-{{type}}.NoExecPaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.no_exec_paths)
-{{type}}.ExecSearchPath, config_parse_colon_separated_paths, 0, offsetof({{type}}, exec_context.exec_search_path)
-{{type}}.BindPaths, config_parse_bind_paths, 0, offsetof({{type}}, exec_context)
-{{type}}.BindReadOnlyPaths, config_parse_bind_paths, 0, offsetof({{type}}, exec_context)
-{{type}}.TemporaryFileSystem, config_parse_temporary_filesystems, 0, offsetof({{type}}, exec_context)
-{{type}}.PrivateTmp, config_parse_private_tmp, 0, offsetof({{type}}, exec_context.private_tmp)
-{{type}}.PrivateDevices, config_parse_bool, 0, offsetof({{type}}, exec_context.private_devices)
-{{type}}.ProtectKernelTunables, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_kernel_tunables)
-{{type}}.ProtectKernelModules, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_kernel_modules)
-{{type}}.ProtectKernelLogs, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_kernel_logs)
-{{type}}.ProtectClock, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_clock)
-{{type}}.ProtectControlGroups, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_control_groups)
-{{type}}.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.network_namespace_path)
-{{type}}.IPCNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.ipc_namespace_path)
-{{type}}.LogNamespace, config_parse_log_namespace, 0, offsetof({{type}}, exec_context)
-{{type}}.PrivateNetwork, config_parse_bool, 0, offsetof({{type}}, exec_context.private_network)
-{{type}}.PrivateUsers, config_parse_private_users, 0, offsetof({{type}}, exec_context.private_users)
-{{type}}.PrivateMounts, config_parse_tristate, 0, offsetof({{type}}, exec_context.private_mounts)
-{{type}}.PrivateIPC, config_parse_bool, 0, offsetof({{type}}, exec_context.private_ipc)
-{{type}}.ProtectSystem, config_parse_protect_system, 0, offsetof({{type}}, exec_context.protect_system)
-{{type}}.ProtectHome, config_parse_protect_home, 0, offsetof({{type}}, exec_context.protect_home)
-{{type}}.MountFlags, config_parse_exec_mount_propagation_flag, 0, offsetof({{type}}, exec_context.mount_propagation_flag)
-{{type}}.MountAPIVFS, config_parse_tristate, 0, offsetof({{type}}, exec_context.mount_apivfs)
-{{type}}.BindLogSockets, config_parse_tristate, 0, offsetof({{type}}, exec_context.bind_log_sockets)
-{{type}}.Personality, config_parse_personality, 0, offsetof({{type}}, exec_context.personality)
-{{type}}.RuntimeDirectoryPreserve, config_parse_exec_preserve_mode, 0, offsetof({{type}}, exec_context.runtime_directory_preserve_mode)
-{{type}}.RuntimeDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME].mode)
-{{type}}.RuntimeDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME])
-{{type}}.StateDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].mode)
-{{type}}.StateDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE])
-{{type}}.CacheDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE].mode)
-{{type}}.CacheDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE])
-{{type}}.LogsDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS].mode)
-{{type}}.LogsDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS])
-{{type}}.ConfigurationDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].mode)
-{{type}}.ConfigurationDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION])
-{{type}}.SetCredential, config_parse_set_credential, 0, offsetof({{type}}, exec_context)
-{{type}}.SetCredentialEncrypted, config_parse_set_credential, 1, offsetof({{type}}, exec_context)
-{{type}}.LoadCredential, config_parse_load_credential, 0, offsetof({{type}}, exec_context)
-{{type}}.LoadCredentialEncrypted, config_parse_load_credential, 1, offsetof({{type}}, exec_context)
-{{type}}.ImportCredential, config_parse_import_credential, 0, offsetof({{type}}, exec_context)
-{{type}}.TimeoutCleanSec, config_parse_sec, 0, offsetof({{type}}, exec_context.timeout_clean_usec)
+{{type}}.RestrictFileSystems, config_parse_restrict_filesystems, 0, offsetof({{type}}, exec_context)
+{{type}}.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitFSIZE, config_parse_rlimit, RLIMIT_FSIZE, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitDATA, config_parse_rlimit, RLIMIT_DATA, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitSTACK, config_parse_rlimit, RLIMIT_STACK, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitCORE, config_parse_rlimit, RLIMIT_CORE, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitRSS, config_parse_rlimit, RLIMIT_RSS, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitNOFILE, config_parse_rlimit, RLIMIT_NOFILE, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitAS, config_parse_rlimit, RLIMIT_AS, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitNPROC, config_parse_rlimit, RLIMIT_NPROC, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitMEMLOCK, config_parse_rlimit, RLIMIT_MEMLOCK, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitLOCKS, config_parse_rlimit, RLIMIT_LOCKS, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitSIGPENDING, config_parse_rlimit, RLIMIT_SIGPENDING, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitMSGQUEUE, config_parse_rlimit, RLIMIT_MSGQUEUE, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitNICE, config_parse_rlimit, RLIMIT_NICE, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitRTPRIO, config_parse_rlimit, RLIMIT_RTPRIO, offsetof({{type}}, exec_context.rlimit)
+{{type}}.LimitRTTIME, config_parse_rlimit, RLIMIT_RTTIME, offsetof({{type}}, exec_context.rlimit)
+{{type}}.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.read_write_paths)
+{{type}}.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.read_only_paths)
+{{type}}.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.inaccessible_paths)
+{{type}}.ReadWritePaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.read_write_paths)
+{{type}}.ReadOnlyPaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.read_only_paths)
+{{type}}.InaccessiblePaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.inaccessible_paths)
+{{type}}.ExecPaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.exec_paths)
+{{type}}.NoExecPaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.no_exec_paths)
+{{type}}.ExecSearchPath, config_parse_colon_separated_paths, 0, offsetof({{type}}, exec_context.exec_search_path)
+{{type}}.BindPaths, config_parse_bind_paths, 0, offsetof({{type}}, exec_context)
+{{type}}.BindReadOnlyPaths, config_parse_bind_paths, 0, offsetof({{type}}, exec_context)
+{{type}}.TemporaryFileSystem, config_parse_temporary_filesystems, 0, offsetof({{type}}, exec_context)
+{{type}}.PrivateTmp, config_parse_private_tmp, 0, offsetof({{type}}, exec_context.private_tmp)
+{{type}}.PrivateDevices, config_parse_bool, 0, offsetof({{type}}, exec_context.private_devices)
+{{type}}.ProtectKernelTunables, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_kernel_tunables)
+{{type}}.ProtectKernelModules, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_kernel_modules)
+{{type}}.ProtectKernelLogs, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_kernel_logs)
+{{type}}.ProtectClock, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_clock)
+{{type}}.ProtectControlGroups, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_control_groups)
+{{type}}.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.network_namespace_path)
+{{type}}.IPCNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.ipc_namespace_path)
+{{type}}.LogNamespace, config_parse_log_namespace, 0, offsetof({{type}}, exec_context)
+{{type}}.PrivateNetwork, config_parse_bool, 0, offsetof({{type}}, exec_context.private_network)
+{{type}}.PrivateUsers, config_parse_private_users, 0, offsetof({{type}}, exec_context.private_users)
+{{type}}.PrivateMounts, config_parse_tristate, 0, offsetof({{type}}, exec_context.private_mounts)
+{{type}}.PrivateIPC, config_parse_bool, 0, offsetof({{type}}, exec_context.private_ipc)
+{{type}}.ProtectSystem, config_parse_protect_system, 0, offsetof({{type}}, exec_context.protect_system)
+{{type}}.ProtectHome, config_parse_protect_home, 0, offsetof({{type}}, exec_context.protect_home)
+{{type}}.MountFlags, config_parse_exec_mount_propagation_flag, 0, offsetof({{type}}, exec_context.mount_propagation_flag)
+{{type}}.MountAPIVFS, config_parse_tristate, 0, offsetof({{type}}, exec_context.mount_apivfs)
+{{type}}.BindLogSockets, config_parse_tristate, 0, offsetof({{type}}, exec_context.bind_log_sockets)
+{{type}}.Personality, config_parse_personality, 0, offsetof({{type}}, exec_context.personality)
+{{type}}.RuntimeDirectoryPreserve, config_parse_exec_preserve_mode, 0, offsetof({{type}}, exec_context.runtime_directory_preserve_mode)
+{{type}}.RuntimeDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME].mode)
+{{type}}.RuntimeDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME])
+{{type}}.StateDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].mode)
+{{type}}.StateDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE])
+{{type}}.CacheDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE].mode)
+{{type}}.CacheDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CACHE])
+{{type}}.LogsDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS].mode)
+{{type}}.LogsDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_LOGS])
+{{type}}.ConfigurationDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].mode)
+{{type}}.ConfigurationDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION])
+{{type}}.SetCredential, config_parse_set_credential, 0, offsetof({{type}}, exec_context)
+{{type}}.SetCredentialEncrypted, config_parse_set_credential, 1, offsetof({{type}}, exec_context)
+{{type}}.LoadCredential, config_parse_load_credential, 0, offsetof({{type}}, exec_context)
+{{type}}.LoadCredentialEncrypted, config_parse_load_credential, 1, offsetof({{type}}, exec_context)
+{{type}}.ImportCredential, config_parse_import_credential, 0, offsetof({{type}}, exec_context)
+{{type}}.TimeoutCleanSec, config_parse_sec, 0, offsetof({{type}}, exec_context.timeout_clean_usec)
{% if HAVE_PAM %}
-{{type}}.PAMName, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.pam_name)
+{{type}}.PAMName, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.pam_name)
{% else %}
-{{type}}.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
{% endif %}
-{{type}}.IgnoreSIGPIPE, config_parse_bool, 0, offsetof({{type}}, exec_context.ignore_sigpipe)
-{{type}}.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.utmp_id)
-{{type}}.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof({{type}}, exec_context.utmp_mode)
+{{type}}.IgnoreSIGPIPE, config_parse_bool, 0, offsetof({{type}}, exec_context.ignore_sigpipe)
+{{type}}.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.utmp_id)
+{{type}}.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof({{type}}, exec_context.utmp_mode)
{% if HAVE_SELINUX %}
-{{type}}.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof({{type}}, exec_context)
+{{type}}.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof({{type}}, exec_context)
{% else %}
-{{type}}.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
{% endif %}
{% if HAVE_APPARMOR %}
-{{type}}.AppArmorProfile, config_parse_exec_apparmor_profile, 0, offsetof({{type}}, exec_context)
+{{type}}.AppArmorProfile, config_parse_exec_apparmor_profile, 0, offsetof({{type}}, exec_context)
{% else %}
-{{type}}.AppArmorProfile, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.AppArmorProfile, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
{% endif %}
{% if ENABLE_SMACK %}
-{{type}}.SmackProcessLabel, config_parse_exec_smack_process_label, 0, offsetof({{type}}, exec_context)
+{{type}}.SmackProcessLabel, config_parse_exec_smack_process_label, 0, offsetof({{type}}, exec_context)
{% else %}
-{{type}}.SmackProcessLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+{{type}}.SmackProcessLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
{% endif %}
-{{type}}.ProtectHostname, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_hostname)
-{{type}}.MemoryKSM, config_parse_tristate, 0, offsetof({{type}}, exec_context.memory_ksm)
+{{type}}.ProtectHostname, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_hostname)
+{{type}}.MemoryKSM, config_parse_tristate, 0, offsetof({{type}}, exec_context.memory_ksm)
{%- endmacro -%}
{%- macro KILL_CONTEXT_CONFIG_ITEMS(type) -%}
-{{type}}.SendSIGKILL, config_parse_bool, 0, offsetof({{type}}, kill_context.send_sigkill)
-{{type}}.SendSIGHUP, config_parse_bool, 0, offsetof({{type}}, kill_context.send_sighup)
-{{type}}.KillMode, config_parse_kill_mode, 0, offsetof({{type}}, kill_context.kill_mode)
-{{type}}.KillSignal, config_parse_signal, 0, offsetof({{type}}, kill_context.kill_signal)
-{{type}}.RestartKillSignal, config_parse_signal, 0, offsetof({{type}}, kill_context.restart_kill_signal)
-{{type}}.FinalKillSignal, config_parse_signal, 0, offsetof({{type}}, kill_context.final_kill_signal)
-{{type}}.WatchdogSignal, config_parse_signal, 0, offsetof({{type}}, kill_context.watchdog_signal)
+{{type}}.SendSIGKILL, config_parse_bool, 0, offsetof({{type}}, kill_context.send_sigkill)
+{{type}}.SendSIGHUP, config_parse_bool, 0, offsetof({{type}}, kill_context.send_sighup)
+{{type}}.KillMode, config_parse_kill_mode, 0, offsetof({{type}}, kill_context.kill_mode)
+{{type}}.KillSignal, config_parse_signal, 0, offsetof({{type}}, kill_context.kill_signal)
+{{type}}.RestartKillSignal, config_parse_signal, 0, offsetof({{type}}, kill_context.restart_kill_signal)
+{{type}}.FinalKillSignal, config_parse_signal, 0, offsetof({{type}}, kill_context.final_kill_signal)
+{{type}}.WatchdogSignal, config_parse_signal, 0, offsetof({{type}}, kill_context.watchdog_signal)
{%- endmacro -%}
{%- macro CGROUP_CONTEXT_CONFIG_ITEMS(type) -%}
-{{type}}.Slice, config_parse_unit_slice, 0, 0
-{{type}}.AllowedCPUs, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.cpuset_cpus)
-{{type}}.StartupAllowedCPUs, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.startup_cpuset_cpus)
-{{type}}.AllowedMemoryNodes, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.cpuset_mems)
-{{type}}.StartupAllowedMemoryNodes, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.startup_cpuset_mems)
-{{type}}.CPUAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.cpu_accounting)
-{{type}}.CPUWeight, config_parse_cg_cpu_weight, 0, offsetof({{type}}, cgroup_context.cpu_weight)
-{{type}}.StartupCPUWeight, config_parse_cg_cpu_weight, 0, offsetof({{type}}, cgroup_context.startup_cpu_weight)
-{{type}}.CPUShares, config_parse_cpu_shares, 0, offsetof({{type}}, cgroup_context.cpu_shares)
-{{type}}.StartupCPUShares, config_parse_cpu_shares, 0, offsetof({{type}}, cgroup_context.startup_cpu_shares)
-{{type}}.CPUQuota, config_parse_cpu_quota, 0, offsetof({{type}}, cgroup_context)
-{{type}}.CPUQuotaPeriodSec, config_parse_sec_def_infinity, 0, offsetof({{type}}, cgroup_context.cpu_quota_period_usec)
-{{type}}.MemoryAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.memory_accounting)
-{{type}}.MemoryMin, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.DefaultMemoryMin, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.DefaultMemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.DefaultStartupMemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.MemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.StartupMemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.MemoryHigh, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.StartupMemoryHigh, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.MemoryMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.StartupMemoryMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.MemorySwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.StartupMemorySwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.MemoryZSwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.StartupMemoryZSwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.MemoryZSwapWriteback, config_parse_bool, 0, offsetof({{type}}, cgroup_context.memory_zswap_writeback)
-{{type}}.MemoryLimit, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.DeviceAllow, config_parse_device_allow, 0, offsetof({{type}}, cgroup_context)
-{{type}}.DevicePolicy, config_parse_device_policy, 0, offsetof({{type}}, cgroup_context.device_policy)
-{{type}}.IOAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.io_accounting)
-{{type}}.IOWeight, config_parse_cg_weight, 0, offsetof({{type}}, cgroup_context.io_weight)
-{{type}}.StartupIOWeight, config_parse_cg_weight, 0, offsetof({{type}}, cgroup_context.startup_io_weight)
-{{type}}.IODeviceWeight, config_parse_io_device_weight, 0, offsetof({{type}}, cgroup_context)
-{{type}}.IOReadBandwidthMax, config_parse_io_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.IOWriteBandwidthMax, config_parse_io_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.IOReadIOPSMax, config_parse_io_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.IOWriteIOPSMax, config_parse_io_limit, 0, offsetof({{type}}, cgroup_context)
-{{type}}.IODeviceLatencyTargetSec, config_parse_io_device_latency, 0, offsetof({{type}}, cgroup_context)
-{{type}}.BlockIOAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.blockio_accounting)
-{{type}}.BlockIOWeight, config_parse_blockio_weight, 0, offsetof({{type}}, cgroup_context.blockio_weight)
-{{type}}.StartupBlockIOWeight, config_parse_blockio_weight, 0, offsetof({{type}}, cgroup_context.startup_blockio_weight)
-{{type}}.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof({{type}}, cgroup_context)
-{{type}}.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof({{type}}, cgroup_context)
-{{type}}.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof({{type}}, cgroup_context)
-{{type}}.TasksAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.tasks_accounting)
-{{type}}.TasksMax, config_parse_tasks_max, 0, offsetof({{type}}, cgroup_context.tasks_max)
-{{type}}.Delegate, config_parse_delegate, 0, offsetof({{type}}, cgroup_context)
-{{type}}.DelegateSubgroup, config_parse_delegate_subgroup , 0, offsetof({{type}}, cgroup_context)
-{{type}}.DisableControllers, config_parse_disable_controllers, 0, offsetof({{type}}, cgroup_context)
-{{type}}.IPAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.ip_accounting)
-{{type}}.IPAddressAllow, config_parse_in_addr_prefixes, AF_UNSPEC, offsetof({{type}}, cgroup_context.ip_address_allow)
-{{type}}.IPAddressDeny, config_parse_in_addr_prefixes, AF_UNSPEC, offsetof({{type}}, cgroup_context.ip_address_deny)
-{{type}}.IPIngressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof({{type}}, cgroup_context.ip_filters_ingress)
-{{type}}.IPEgressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof({{type}}, cgroup_context.ip_filters_egress)
-{{type}}.ManagedOOMSwap, config_parse_managed_oom_mode, 0, offsetof({{type}}, cgroup_context.moom_swap)
-{{type}}.ManagedOOMMemoryPressure, config_parse_managed_oom_mode, 0, offsetof({{type}}, cgroup_context.moom_mem_pressure)
-{{type}}.ManagedOOMMemoryPressureLimit, config_parse_managed_oom_mem_pressure_limit, 0, offsetof({{type}}, cgroup_context.moom_mem_pressure_limit)
-{{type}}.ManagedOOMPreference, config_parse_managed_oom_preference, 0, offsetof({{type}}, cgroup_context.moom_preference)
-{{type}}.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0
-{{type}}.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof({{type}}, cgroup_context)
-{{type}}.SocketBindAllow, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_allow)
-{{type}}.SocketBindDeny, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_deny)
-{{type}}.RestrictNetworkInterfaces, config_parse_restrict_network_interfaces, 0, offsetof({{type}}, cgroup_context)
-{{type}}.MemoryPressureThresholdSec, config_parse_sec, 0, offsetof({{type}}, cgroup_context.memory_pressure_threshold_usec)
-{{type}}.MemoryPressureWatch, config_parse_memory_pressure_watch, 0, offsetof({{type}}, cgroup_context.memory_pressure_watch)
-{{type}}.NFTSet, config_parse_cgroup_nft_set, NFT_SET_PARSE_CGROUP, offsetof({{type}}, cgroup_context)
-{{type}}.CoredumpReceive, config_parse_bool, 0, offsetof({{type}}, cgroup_context.coredump_receive)
+{{type}}.Slice, config_parse_unit_slice, 0, 0
+{{type}}.AllowedCPUs, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.cpuset_cpus)
+{{type}}.StartupAllowedCPUs, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.startup_cpuset_cpus)
+{{type}}.AllowedMemoryNodes, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.cpuset_mems)
+{{type}}.StartupAllowedMemoryNodes, config_parse_allowed_cpuset, 0, offsetof({{type}}, cgroup_context.startup_cpuset_mems)
+{{type}}.CPUAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.cpu_accounting)
+{{type}}.CPUWeight, config_parse_cg_cpu_weight, 0, offsetof({{type}}, cgroup_context.cpu_weight)
+{{type}}.StartupCPUWeight, config_parse_cg_cpu_weight, 0, offsetof({{type}}, cgroup_context.startup_cpu_weight)
+{{type}}.CPUShares, config_parse_cpu_shares, 0, offsetof({{type}}, cgroup_context.cpu_shares)
+{{type}}.StartupCPUShares, config_parse_cpu_shares, 0, offsetof({{type}}, cgroup_context.startup_cpu_shares)
+{{type}}.CPUQuota, config_parse_cpu_quota, 0, offsetof({{type}}, cgroup_context)
+{{type}}.CPUQuotaPeriodSec, config_parse_sec_def_infinity, 0, offsetof({{type}}, cgroup_context.cpu_quota_period_usec)
+{{type}}.MemoryAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.memory_accounting)
+{{type}}.MemoryMin, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.DefaultMemoryMin, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.DefaultMemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.DefaultStartupMemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.MemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.StartupMemoryLow, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.MemoryHigh, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.StartupMemoryHigh, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.MemoryMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.StartupMemoryMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.MemorySwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.StartupMemorySwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.MemoryZSwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.StartupMemoryZSwapMax, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.MemoryZSwapWriteback, config_parse_bool, 0, offsetof({{type}}, cgroup_context.memory_zswap_writeback)
+{{type}}.MemoryLimit, config_parse_memory_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.DeviceAllow, config_parse_device_allow, 0, offsetof({{type}}, cgroup_context)
+{{type}}.DevicePolicy, config_parse_device_policy, 0, offsetof({{type}}, cgroup_context.device_policy)
+{{type}}.IOAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.io_accounting)
+{{type}}.IOWeight, config_parse_cg_weight, 0, offsetof({{type}}, cgroup_context.io_weight)
+{{type}}.StartupIOWeight, config_parse_cg_weight, 0, offsetof({{type}}, cgroup_context.startup_io_weight)
+{{type}}.IODeviceWeight, config_parse_io_device_weight, 0, offsetof({{type}}, cgroup_context)
+{{type}}.IOReadBandwidthMax, config_parse_io_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.IOWriteBandwidthMax, config_parse_io_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.IOReadIOPSMax, config_parse_io_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.IOWriteIOPSMax, config_parse_io_limit, 0, offsetof({{type}}, cgroup_context)
+{{type}}.IODeviceLatencyTargetSec, config_parse_io_device_latency, 0, offsetof({{type}}, cgroup_context)
+{{type}}.BlockIOAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.blockio_accounting)
+{{type}}.BlockIOWeight, config_parse_blockio_weight, 0, offsetof({{type}}, cgroup_context.blockio_weight)
+{{type}}.StartupBlockIOWeight, config_parse_blockio_weight, 0, offsetof({{type}}, cgroup_context.startup_blockio_weight)
+{{type}}.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof({{type}}, cgroup_context)
+{{type}}.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof({{type}}, cgroup_context)
+{{type}}.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof({{type}}, cgroup_context)
+{{type}}.TasksAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.tasks_accounting)
+{{type}}.TasksMax, config_parse_tasks_max, 0, offsetof({{type}}, cgroup_context.tasks_max)
+{{type}}.Delegate, config_parse_delegate, 0, offsetof({{type}}, cgroup_context)
+{{type}}.DelegateSubgroup, config_parse_delegate_subgroup, 0, offsetof({{type}}, cgroup_context)
+{{type}}.DisableControllers, config_parse_disable_controllers, 0, offsetof({{type}}, cgroup_context)
+{{type}}.IPAccounting, config_parse_bool, 0, offsetof({{type}}, cgroup_context.ip_accounting)
+{{type}}.IPAddressAllow, config_parse_in_addr_prefixes, AF_UNSPEC, offsetof({{type}}, cgroup_context.ip_address_allow)
+{{type}}.IPAddressDeny, config_parse_in_addr_prefixes, AF_UNSPEC, offsetof({{type}}, cgroup_context.ip_address_deny)
+{{type}}.IPIngressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof({{type}}, cgroup_context.ip_filters_ingress)
+{{type}}.IPEgressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof({{type}}, cgroup_context.ip_filters_egress)
+{{type}}.ManagedOOMSwap, config_parse_managed_oom_mode, 0, offsetof({{type}}, cgroup_context.moom_swap)
+{{type}}.ManagedOOMMemoryPressure, config_parse_managed_oom_mode, 0, offsetof({{type}}, cgroup_context.moom_mem_pressure)
+{{type}}.ManagedOOMMemoryPressureLimit, config_parse_managed_oom_mem_pressure_limit, 0, offsetof({{type}}, cgroup_context.moom_mem_pressure_limit)
+{{type}}.ManagedOOMMemoryPressureDurationSec, config_parse_managed_oom_mem_pressure_duration_sec, 0, offsetof({{type}}, cgroup_context.moom_mem_pressure_duration_usec)
+{{type}}.ManagedOOMPreference, config_parse_managed_oom_preference, 0, offsetof({{type}}, cgroup_context.moom_preference)
+{{type}}.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0
+{{type}}.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof({{type}}, cgroup_context)
+{{type}}.SocketBindAllow, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_allow)
+{{type}}.SocketBindDeny, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_deny)
+{{type}}.RestrictNetworkInterfaces, config_parse_restrict_network_interfaces, 0, offsetof({{type}}, cgroup_context)
+{{type}}.MemoryPressureThresholdSec, config_parse_sec, 0, offsetof({{type}}, cgroup_context.memory_pressure_threshold_usec)
+{{type}}.MemoryPressureWatch, config_parse_memory_pressure_watch, 0, offsetof({{type}}, cgroup_context.memory_pressure_watch)
+{{type}}.NFTSet, config_parse_cgroup_nft_set, NFT_SET_PARSE_CGROUP, offsetof({{type}}, cgroup_context)
+{{type}}.CoredumpReceive, config_parse_bool, 0, offsetof({{type}}, cgroup_context.coredump_receive)
{%- endmacro -%}
%{
@@ -287,314 +288,314 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
-Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
-Unit.SourcePath, config_parse_unit_path_printf, 0, offsetof(Unit, source_path)
-Unit.Requires, config_parse_unit_deps, UNIT_REQUIRES, 0
-Unit.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 0
-Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0
-Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
-Unit.BindTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
-Unit.Upholds, config_parse_unit_deps, UNIT_UPHOLDS, 0
-Unit.Conflicts, config_parse_unit_deps, UNIT_CONFLICTS, 0
-Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0
-Unit.After, config_parse_unit_deps, UNIT_AFTER, 0
-Unit.OnSuccess, config_parse_unit_deps, UNIT_ON_SUCCESS, 0
-Unit.OnFailure, config_parse_unit_deps, UNIT_ON_FAILURE, 0
-Unit.PropagatesReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0
-Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0
-Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
-Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
-Unit.PropagatesStopTo, config_parse_unit_deps, UNIT_PROPAGATES_STOP_TO, 0
-Unit.StopPropagatedFrom, config_parse_unit_deps, UNIT_STOP_PROPAGATED_FROM, 0
-Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0
-Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0
-Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0
-Unit.RequisiteOverridable, config_parse_obsolete_unit_deps, UNIT_REQUISITE, 0
-Unit.RequiresMountsFor, config_parse_unit_mounts_for, 0, 0
-Unit.WantsMountsFor, config_parse_unit_mounts_for, 0, 0
-Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded)
-Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start)
-Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop)
-Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate)
-Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies)
-Unit.SurviveFinalKillSignal, config_parse_bool, 0, offsetof(Unit, survive_final_kill_signal)
-Unit.OnSuccessJobMode, config_parse_job_mode, 0, offsetof(Unit, on_success_job_mode)
-Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode)
+Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
+Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
+Unit.SourcePath, config_parse_unit_path_printf, 0, offsetof(Unit, source_path)
+Unit.Requires, config_parse_unit_deps, UNIT_REQUIRES, 0
+Unit.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 0
+Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0
+Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
+Unit.BindTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
+Unit.Upholds, config_parse_unit_deps, UNIT_UPHOLDS, 0
+Unit.Conflicts, config_parse_unit_deps, UNIT_CONFLICTS, 0
+Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0
+Unit.After, config_parse_unit_deps, UNIT_AFTER, 0
+Unit.OnSuccess, config_parse_unit_deps, UNIT_ON_SUCCESS, 0
+Unit.OnFailure, config_parse_unit_deps, UNIT_ON_FAILURE, 0
+Unit.PropagatesReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0
+Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0
+Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
+Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
+Unit.PropagatesStopTo, config_parse_unit_deps, UNIT_PROPAGATES_STOP_TO, 0
+Unit.StopPropagatedFrom, config_parse_unit_deps, UNIT_STOP_PROPAGATED_FROM, 0
+Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0
+Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0
+Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0
+Unit.RequisiteOverridable, config_parse_obsolete_unit_deps, UNIT_REQUISITE, 0
+Unit.RequiresMountsFor, config_parse_unit_mounts_for, 0, 0
+Unit.WantsMountsFor, config_parse_unit_mounts_for, 0, 0
+Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded)
+Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start)
+Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop)
+Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate)
+Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies)
+Unit.SurviveFinalKillSignal, config_parse_bool, 0, offsetof(Unit, survive_final_kill_signal)
+Unit.OnSuccessJobMode, config_parse_job_mode, 0, offsetof(Unit, on_success_job_mode)
+Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode)
{# The following is a legacy alias name for compatibility #}
-Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode)
-Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate)
-Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0
-Unit.JobTimeoutSec, config_parse_job_timeout_sec, 0, 0
-Unit.JobRunningTimeoutSec, config_parse_job_running_timeout_sec, 0, 0
-Unit.JobTimeoutAction, config_parse_emergency_action, 0, offsetof(Unit, job_timeout_action)
-Unit.JobTimeoutRebootArgument, config_parse_reboot_parameter, 0, offsetof(Unit, job_timeout_reboot_arg)
-Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_ratelimit.interval)
+Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode)
+Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate)
+Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0
+Unit.JobTimeoutSec, config_parse_job_timeout_sec, 0, 0
+Unit.JobRunningTimeoutSec, config_parse_job_running_timeout_sec, 0, 0
+Unit.JobTimeoutAction, config_parse_emergency_action, 0, offsetof(Unit, job_timeout_action)
+Unit.JobTimeoutRebootArgument, config_parse_reboot_parameter, 0, offsetof(Unit, job_timeout_reboot_arg)
+Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_ratelimit.interval)
{# The following is a legacy alias name for compatibility #}
-Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_ratelimit.interval)
-Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_ratelimit.burst)
-Unit.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action)
-Unit.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action)
-Unit.SuccessAction, config_parse_emergency_action, 0, offsetof(Unit, success_action)
-Unit.FailureActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, failure_action_exit_status)
-Unit.SuccessActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, success_action_exit_status)
-Unit.RebootArgument, config_parse_reboot_parameter, 0, offsetof(Unit, reboot_arg)
-Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions)
-Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions)
-Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions)
-Unit.ConditionPathIsSymbolicLink, config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK, offsetof(Unit, conditions)
-Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, conditions)
-Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, conditions)
-Unit.ConditionPathIsEncrypted, config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED, offsetof(Unit, conditions)
-Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, conditions)
-Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, conditions)
-Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, conditions)
-Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions)
-Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions)
-Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions)
-Unit.ConditionFirmware, config_parse_unit_condition_string, CONDITION_FIRMWARE, offsetof(Unit, conditions)
-Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions)
-Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions)
-Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions)
-Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions)
-Unit.ConditionCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, offsetof(Unit, conditions)
-Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions)
-Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions)
-Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions)
-Unit.ConditionMemory, config_parse_unit_condition_string, CONDITION_MEMORY, offsetof(Unit, conditions)
-Unit.ConditionCPUFeature, config_parse_unit_condition_string, CONDITION_CPU_FEATURE, offsetof(Unit, conditions)
-Unit.ConditionCPUs, config_parse_unit_condition_string, CONDITION_CPUS, offsetof(Unit, conditions)
-Unit.ConditionEnvironment, config_parse_unit_condition_string, CONDITION_ENVIRONMENT, offsetof(Unit, conditions)
-Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions)
-Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions)
-Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions)
-Unit.ConditionOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, conditions)
-Unit.ConditionMemoryPressure, config_parse_unit_condition_string, CONDITION_MEMORY_PRESSURE, offsetof(Unit, conditions)
-Unit.ConditionCPUPressure, config_parse_unit_condition_string, CONDITION_CPU_PRESSURE, offsetof(Unit, conditions)
-Unit.ConditionIOPressure, config_parse_unit_condition_string, CONDITION_IO_PRESSURE, offsetof(Unit, conditions)
-Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts)
-Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts)
-Unit.AssertPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, asserts)
-Unit.AssertPathIsSymbolicLink, config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK, offsetof(Unit, asserts)
-Unit.AssertPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, asserts)
-Unit.AssertPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, asserts)
-Unit.AssertPathIsEncrypted, config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED, offsetof(Unit, asserts)
-Unit.AssertDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, asserts)
-Unit.AssertFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, asserts)
-Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, asserts)
-Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts)
-Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts)
-Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts)
-Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts)
-Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts)
-Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts)
-Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts)
-Unit.AssertCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, offsetof(Unit, asserts)
-Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts)
-Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts)
-Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts)
-Unit.AssertMemory, config_parse_unit_condition_string, CONDITION_MEMORY, offsetof(Unit, asserts)
-Unit.AssertCPUFeature, config_parse_unit_condition_string, CONDITION_CPU_FEATURE, offsetof(Unit, asserts)
-Unit.AssertCPUs, config_parse_unit_condition_string, CONDITION_CPUS, offsetof(Unit, asserts)
-Unit.AssertEnvironment, config_parse_unit_condition_string, CONDITION_ENVIRONMENT, offsetof(Unit, asserts)
-Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts)
-Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts)
-Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts)
-Unit.AssertOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, asserts)
-Unit.AssertMemoryPressure, config_parse_unit_condition_string, CONDITION_MEMORY_PRESSURE, offsetof(Unit, asserts)
-Unit.AssertCPUPressure, config_parse_unit_condition_string, CONDITION_CPU_PRESSURE, offsetof(Unit, asserts)
-Unit.AssertIOPressure, config_parse_unit_condition_string, CONDITION_IO_PRESSURE, offsetof(Unit, asserts)
-Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode)
-Service.PIDFile, config_parse_pid_file, 0, offsetof(Service, pid_file)
-Service.ExecCondition, config_parse_exec, SERVICE_EXEC_CONDITION, offsetof(Service, exec_command)
-Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command)
-Service.ExecStart, config_parse_exec, SERVICE_EXEC_START, offsetof(Service, exec_command)
-Service.ExecStartPost, config_parse_exec, SERVICE_EXEC_START_POST, offsetof(Service, exec_command)
-Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command)
-Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command)
-Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command)
-Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec)
-Service.RestartSteps, config_parse_unsigned, 0, offsetof(Service, restart_steps)
-Service.RestartMaxDelaySec, config_parse_sec, 0, offsetof(Service, restart_max_delay_usec)
-Service.TimeoutSec, config_parse_service_timeout, 0, 0
-Service.TimeoutStartSec, config_parse_service_timeout, 0, 0
-Service.TimeoutStopSec, config_parse_sec_fix_0, 0, offsetof(Service, timeout_stop_usec)
-Service.TimeoutAbortSec, config_parse_service_timeout_abort, 0, 0
-Service.TimeoutStartFailureMode, config_parse_service_timeout_failure_mode, 0, offsetof(Service, timeout_start_failure_mode)
-Service.TimeoutStopFailureMode, config_parse_service_timeout_failure_mode, 0, offsetof(Service, timeout_stop_failure_mode)
-Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
-Service.RuntimeRandomizedExtraSec, config_parse_sec, 0, offsetof(Service, runtime_rand_extra_usec)
-Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
+Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_ratelimit.interval)
+Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_ratelimit.burst)
+Unit.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action)
+Unit.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action)
+Unit.SuccessAction, config_parse_emergency_action, 0, offsetof(Unit, success_action)
+Unit.FailureActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, failure_action_exit_status)
+Unit.SuccessActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, success_action_exit_status)
+Unit.RebootArgument, config_parse_reboot_parameter, 0, offsetof(Unit, reboot_arg)
+Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions)
+Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions)
+Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions)
+Unit.ConditionPathIsSymbolicLink, config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK, offsetof(Unit, conditions)
+Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, conditions)
+Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, conditions)
+Unit.ConditionPathIsEncrypted, config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED, offsetof(Unit, conditions)
+Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, conditions)
+Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, conditions)
+Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, conditions)
+Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions)
+Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions)
+Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions)
+Unit.ConditionFirmware, config_parse_unit_condition_string, CONDITION_FIRMWARE, offsetof(Unit, conditions)
+Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions)
+Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions)
+Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions)
+Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions)
+Unit.ConditionCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, offsetof(Unit, conditions)
+Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions)
+Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions)
+Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions)
+Unit.ConditionMemory, config_parse_unit_condition_string, CONDITION_MEMORY, offsetof(Unit, conditions)
+Unit.ConditionCPUFeature, config_parse_unit_condition_string, CONDITION_CPU_FEATURE, offsetof(Unit, conditions)
+Unit.ConditionCPUs, config_parse_unit_condition_string, CONDITION_CPUS, offsetof(Unit, conditions)
+Unit.ConditionEnvironment, config_parse_unit_condition_string, CONDITION_ENVIRONMENT, offsetof(Unit, conditions)
+Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions)
+Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions)
+Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions)
+Unit.ConditionOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, conditions)
+Unit.ConditionMemoryPressure, config_parse_unit_condition_string, CONDITION_MEMORY_PRESSURE, offsetof(Unit, conditions)
+Unit.ConditionCPUPressure, config_parse_unit_condition_string, CONDITION_CPU_PRESSURE, offsetof(Unit, conditions)
+Unit.ConditionIOPressure, config_parse_unit_condition_string, CONDITION_IO_PRESSURE, offsetof(Unit, conditions)
+Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts)
+Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts)
+Unit.AssertPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, asserts)
+Unit.AssertPathIsSymbolicLink, config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK, offsetof(Unit, asserts)
+Unit.AssertPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, asserts)
+Unit.AssertPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, asserts)
+Unit.AssertPathIsEncrypted, config_parse_unit_condition_path, CONDITION_PATH_IS_ENCRYPTED, offsetof(Unit, asserts)
+Unit.AssertDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, asserts)
+Unit.AssertFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, asserts)
+Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, asserts)
+Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts)
+Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts)
+Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts)
+Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts)
+Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts)
+Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts)
+Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts)
+Unit.AssertCredential, config_parse_unit_condition_string, CONDITION_CREDENTIAL, offsetof(Unit, asserts)
+Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts)
+Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts)
+Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts)
+Unit.AssertMemory, config_parse_unit_condition_string, CONDITION_MEMORY, offsetof(Unit, asserts)
+Unit.AssertCPUFeature, config_parse_unit_condition_string, CONDITION_CPU_FEATURE, offsetof(Unit, asserts)
+Unit.AssertCPUs, config_parse_unit_condition_string, CONDITION_CPUS, offsetof(Unit, asserts)
+Unit.AssertEnvironment, config_parse_unit_condition_string, CONDITION_ENVIRONMENT, offsetof(Unit, asserts)
+Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts)
+Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts)
+Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts)
+Unit.AssertOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, asserts)
+Unit.AssertMemoryPressure, config_parse_unit_condition_string, CONDITION_MEMORY_PRESSURE, offsetof(Unit, asserts)
+Unit.AssertCPUPressure, config_parse_unit_condition_string, CONDITION_CPU_PRESSURE, offsetof(Unit, asserts)
+Unit.AssertIOPressure, config_parse_unit_condition_string, CONDITION_IO_PRESSURE, offsetof(Unit, asserts)
+Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode)
+Service.PIDFile, config_parse_pid_file, 0, offsetof(Service, pid_file)
+Service.ExecCondition, config_parse_exec, SERVICE_EXEC_CONDITION, offsetof(Service, exec_command)
+Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command)
+Service.ExecStart, config_parse_exec, SERVICE_EXEC_START, offsetof(Service, exec_command)
+Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command)
+Service.ExecStartPost, config_parse_exec, SERVICE_EXEC_START_POST, offsetof(Service, exec_command)
+Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command)
+Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command)
+Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec)
+Service.RestartSteps, config_parse_unsigned, 0, offsetof(Service, restart_steps)
+Service.RestartMaxDelaySec, config_parse_sec, 0, offsetof(Service, restart_max_delay_usec)
+Service.TimeoutSec, config_parse_service_timeout, 0, 0
+Service.TimeoutStartSec, config_parse_service_timeout, 0, 0
+Service.TimeoutStopSec, config_parse_sec_fix_0, 0, offsetof(Service, timeout_stop_usec)
+Service.TimeoutAbortSec, config_parse_service_timeout_abort, 0, 0
+Service.TimeoutStartFailureMode, config_parse_service_timeout_failure_mode, 0, offsetof(Service, timeout_start_failure_mode)
+Service.TimeoutStopFailureMode, config_parse_service_timeout_failure_mode, 0, offsetof(Service, timeout_stop_failure_mode)
+Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
+Service.RuntimeRandomizedExtraSec, config_parse_sec, 0, offsetof(Service, runtime_rand_extra_usec)
+Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
{# The following five only exist for compatibility, they moved into Unit, see above #}
-Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_ratelimit.interval)
-Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_ratelimit.burst)
-Service.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action)
-Service.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action)
-Service.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
-Service.Type, config_parse_service_type, 0, offsetof(Service, type)
-Service.ExitType, config_parse_service_exit_type, 0, offsetof(Service, exit_type)
-Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
-Service.RestartMode, config_parse_service_restart_mode, 0, offsetof(Service, restart_mode)
-Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only)
-Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)
-Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit)
-Service.GuessMainPID, config_parse_bool, 0, offsetof(Service, guess_main_pid)
-Service.RestartPreventExitStatus, config_parse_set_status, 0, offsetof(Service, restart_prevent_status)
-Service.RestartForceExitStatus, config_parse_set_status, 0, offsetof(Service, restart_force_status)
-Service.SuccessExitStatus, config_parse_set_status, 0, offsetof(Service, success_status)
-Service.SysVStartPriority, config_parse_warn_compat, DISABLED_LEGACY, 0
-Service.NonBlocking, config_parse_bool, 0, offsetof(Service, exec_context.non_blocking)
-Service.BusName, config_parse_bus_name, 0, offsetof(Service, bus_name)
-Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max)
-Service.FileDescriptorStorePreserve, config_parse_exec_preserve_mode, 0, offsetof(Service, fd_store_preserve_mode)
-Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access)
-Service.Sockets, config_parse_service_sockets, 0, 0
-Service.BusPolicy, config_parse_warn_compat, DISABLED_LEGACY, 0
-Service.USBFunctionDescriptors, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_descriptors)
-Service.USBFunctionStrings, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_strings)
-Service.OOMPolicy, config_parse_oom_policy, 0, offsetof(Service, oom_policy)
-Service.OpenFile, config_parse_open_file, 0, offsetof(Service, open_files)
-Service.ReloadSignal, config_parse_signal, 0, offsetof(Service, reload_signal)
+Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_ratelimit.interval)
+Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_ratelimit.burst)
+Service.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action)
+Service.FailureAction, config_parse_emergency_action, 0, offsetof(Unit, failure_action)
+Service.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
+Service.Type, config_parse_service_type, 0, offsetof(Service, type)
+Service.ExitType, config_parse_service_exit_type, 0, offsetof(Service, exit_type)
+Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
+Service.RestartMode, config_parse_service_restart_mode, 0, offsetof(Service, restart_mode)
+Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only)
+Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)
+Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit)
+Service.GuessMainPID, config_parse_bool, 0, offsetof(Service, guess_main_pid)
+Service.RestartPreventExitStatus, config_parse_set_status, 0, offsetof(Service, restart_prevent_status)
+Service.RestartForceExitStatus, config_parse_set_status, 0, offsetof(Service, restart_force_status)
+Service.SuccessExitStatus, config_parse_set_status, 0, offsetof(Service, success_status)
+Service.SysVStartPriority, config_parse_warn_compat, DISABLED_LEGACY, 0
+Service.NonBlocking, config_parse_bool, 0, offsetof(Service, exec_context.non_blocking)
+Service.BusName, config_parse_bus_name, 0, offsetof(Service, bus_name)
+Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max)
+Service.FileDescriptorStorePreserve, config_parse_exec_preserve_mode, 0, offsetof(Service, fd_store_preserve_mode)
+Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access)
+Service.Sockets, config_parse_service_sockets, 0, 0
+Service.BusPolicy, config_parse_warn_compat, DISABLED_LEGACY, 0
+Service.USBFunctionDescriptors, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_descriptors)
+Service.USBFunctionStrings, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_strings)
+Service.OOMPolicy, config_parse_oom_policy, 0, offsetof(Service, oom_policy)
+Service.OpenFile, config_parse_open_file, 0, offsetof(Service, open_files)
+Service.ReloadSignal, config_parse_signal, 0, offsetof(Service, reload_signal)
{{ EXEC_CONTEXT_CONFIG_ITEMS('Service') }}
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Service') }}
{{ KILL_CONTEXT_CONFIG_ITEMS('Service') }}
-Socket.ListenStream, config_parse_socket_listen, SOCKET_SOCKET, 0
-Socket.ListenDatagram, config_parse_socket_listen, SOCKET_SOCKET, 0
-Socket.ListenSequentialPacket, config_parse_socket_listen, SOCKET_SOCKET, 0
-Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO, 0
-Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0
-Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0
-Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0
-Socket.ListenUSBFunction, config_parse_socket_listen, SOCKET_USB_FUNCTION, 0
-Socket.SocketProtocol, config_parse_socket_protocol, 0, offsetof(Socket, socket_protocol)
-Socket.BindIPv6Only, config_parse_socket_bind, 0, offsetof(Socket, bind_ipv6_only)
-Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog)
-Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0
-Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC_START_PRE, offsetof(Socket, exec_command)
-Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command)
-Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command)
-Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command)
-Socket.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Socket, timeout_usec)
-Socket.SocketUser, config_parse_user_group_compat, 0, offsetof(Socket, user)
-Socket.SocketGroup, config_parse_user_group_compat, 0, offsetof(Socket, group)
-Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
-Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
-Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)
-Socket.FlushPending, config_parse_bool, 0, offsetof(Socket, flush_pending)
-Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable)
-Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections)
-Socket.MaxConnectionsPerSource, config_parse_unsigned, 0, offsetof(Socket, max_connections_per_source)
-Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive)
-Socket.KeepAliveTimeSec, config_parse_sec, 0, offsetof(Socket, keep_alive_time)
-Socket.KeepAliveIntervalSec, config_parse_sec, 0, offsetof(Socket, keep_alive_interval)
-Socket.KeepAliveProbes, config_parse_unsigned, 0, offsetof(Socket, keep_alive_cnt)
-Socket.DeferAcceptSec, config_parse_sec, 0, offsetof(Socket, defer_accept)
-Socket.NoDelay, config_parse_bool, 0, offsetof(Socket, no_delay)
-Socket.Priority, config_parse_int, 0, offsetof(Socket, priority)
-Socket.ReceiveBuffer, config_parse_iec_size, 0, offsetof(Socket, receive_buffer)
-Socket.SendBuffer, config_parse_iec_size, 0, offsetof(Socket, send_buffer)
-Socket.IPTOS, config_parse_ip_tos, 0, offsetof(Socket, ip_tos)
-Socket.IPTTL, config_parse_int, 0, offsetof(Socket, ip_ttl)
-Socket.Mark, config_parse_int, 0, offsetof(Socket, mark)
-Socket.PipeSize, config_parse_iec_size, 0, offsetof(Socket, pipe_size)
-Socket.FreeBind, config_parse_bool, 0, offsetof(Socket, free_bind)
-Socket.Transparent, config_parse_bool, 0, offsetof(Socket, transparent)
-Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast)
-Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred)
-Socket.PassFileDescriptorsToExec, config_parse_bool, 0, offsetof(Socket, pass_fds_to_exec)
-Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec)
-Socket.PassPacketInfo, config_parse_bool, 0, offsetof(Socket, pass_pktinfo)
-Socket.Timestamping, config_parse_socket_timestamping, 0, offsetof(Socket, timestamping)
-Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion)
-Socket.ReusePort, config_parse_bool, 0, offsetof(Socket, reuse_port)
-Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg)
-Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize)
-Socket.RemoveOnStop, config_parse_bool, 0, offsetof(Socket, remove_on_stop)
-Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks)
-Socket.FileDescriptorName, config_parse_fdname, 0, 0
-Socket.Service, config_parse_socket_service, 0, 0
-Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval)
-Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst)
-Socket.PollLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, poll_limit.interval)
-Socket.PollLimitBurst, config_parse_unsigned, 0, offsetof(Socket, poll_limit.burst)
+Socket.ListenStream, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenDatagram, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenSequentialPacket, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO, 0
+Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0
+Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0
+Socket.ListenUSBFunction, config_parse_socket_listen, SOCKET_USB_FUNCTION, 0
+Socket.SocketProtocol, config_parse_socket_protocol, 0, offsetof(Socket, socket_protocol)
+Socket.BindIPv6Only, config_parse_socket_bind, 0, offsetof(Socket, bind_ipv6_only)
+Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog)
+Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0
+Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC_START_PRE, offsetof(Socket, exec_command)
+Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command)
+Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command)
+Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command)
+Socket.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Socket, timeout_usec)
+Socket.SocketUser, config_parse_user_group_compat, 0, offsetof(Socket, user)
+Socket.SocketGroup, config_parse_user_group_compat, 0, offsetof(Socket, group)
+Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
+Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
+Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)
+Socket.FlushPending, config_parse_bool, 0, offsetof(Socket, flush_pending)
+Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable)
+Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections)
+Socket.MaxConnectionsPerSource, config_parse_unsigned, 0, offsetof(Socket, max_connections_per_source)
+Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive)
+Socket.KeepAliveTimeSec, config_parse_sec, 0, offsetof(Socket, keep_alive_time)
+Socket.KeepAliveIntervalSec, config_parse_sec, 0, offsetof(Socket, keep_alive_interval)
+Socket.KeepAliveProbes, config_parse_unsigned, 0, offsetof(Socket, keep_alive_cnt)
+Socket.DeferAcceptSec, config_parse_sec, 0, offsetof(Socket, defer_accept)
+Socket.NoDelay, config_parse_bool, 0, offsetof(Socket, no_delay)
+Socket.Priority, config_parse_int, 0, offsetof(Socket, priority)
+Socket.ReceiveBuffer, config_parse_iec_size, 0, offsetof(Socket, receive_buffer)
+Socket.SendBuffer, config_parse_iec_size, 0, offsetof(Socket, send_buffer)
+Socket.IPTOS, config_parse_ip_tos, 0, offsetof(Socket, ip_tos)
+Socket.IPTTL, config_parse_int, 0, offsetof(Socket, ip_ttl)
+Socket.Mark, config_parse_int, 0, offsetof(Socket, mark)
+Socket.PipeSize, config_parse_iec_size, 0, offsetof(Socket, pipe_size)
+Socket.FreeBind, config_parse_bool, 0, offsetof(Socket, free_bind)
+Socket.Transparent, config_parse_bool, 0, offsetof(Socket, transparent)
+Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast)
+Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred)
+Socket.PassFileDescriptorsToExec, config_parse_bool, 0, offsetof(Socket, pass_fds_to_exec)
+Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec)
+Socket.PassPacketInfo, config_parse_bool, 0, offsetof(Socket, pass_pktinfo)
+Socket.Timestamping, config_parse_socket_timestamping, 0, offsetof(Socket, timestamping)
+Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion)
+Socket.ReusePort, config_parse_bool, 0, offsetof(Socket, reuse_port)
+Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg)
+Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize)
+Socket.RemoveOnStop, config_parse_bool, 0, offsetof(Socket, remove_on_stop)
+Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks)
+Socket.FileDescriptorName, config_parse_fdname, 0, 0
+Socket.Service, config_parse_socket_service, 0, 0
+Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval)
+Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst)
+Socket.PollLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, poll_limit.interval)
+Socket.PollLimitBurst, config_parse_unsigned, 0, offsetof(Socket, poll_limit.burst)
{% if ENABLE_SMACK %}
-Socket.SmackLabel, config_parse_unit_string_printf, 0, offsetof(Socket, smack)
-Socket.SmackLabelIPIn, config_parse_unit_string_printf, 0, offsetof(Socket, smack_ip_in)
-Socket.SmackLabelIPOut, config_parse_unit_string_printf, 0, offsetof(Socket, smack_ip_out)
+Socket.SmackLabel, config_parse_unit_string_printf, 0, offsetof(Socket, smack)
+Socket.SmackLabelIPIn, config_parse_unit_string_printf, 0, offsetof(Socket, smack_ip_in)
+Socket.SmackLabelIPOut, config_parse_unit_string_printf, 0, offsetof(Socket, smack_ip_out)
{% else %}
-Socket.SmackLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-Socket.SmackLabelIPIn, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
-Socket.SmackLabelIPOut, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+Socket.SmackLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+Socket.SmackLabelIPIn, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+Socket.SmackLabelIPOut, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
{% endif %}
{% if HAVE_SELINUX %}
-Socket.SELinuxContextFromNet, config_parse_bool, 0, offsetof(Socket, selinux_context_from_net)
+Socket.SELinuxContextFromNet, config_parse_bool, 0, offsetof(Socket, selinux_context_from_net)
{% else %}
-Socket.SELinuxContextFromNet, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+Socket.SELinuxContextFromNet, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
{% endif %}
{{ EXEC_CONTEXT_CONFIG_ITEMS('Socket') }}
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Socket') }}
{{ KILL_CONTEXT_CONFIG_ITEMS('Socket') }}
-Mount.What, config_parse_mount_node, 0, offsetof(Mount, parameters_fragment.what)
-Mount.Where, config_parse_unit_path_printf, 0, offsetof(Mount, where)
-Mount.Options, config_parse_unit_string_printf, 0, offsetof(Mount, parameters_fragment.options)
-Mount.Type, config_parse_unit_string_printf, 0, offsetof(Mount, parameters_fragment.fstype)
-Mount.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Mount, timeout_usec)
-Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode)
-Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options)
-Mount.LazyUnmount, config_parse_bool, 0, offsetof(Mount, lazy_unmount)
-Mount.ForceUnmount, config_parse_bool, 0, offsetof(Mount, force_unmount)
-Mount.ReadWriteOnly, config_parse_bool, 0, offsetof(Mount, read_write_only)
+Mount.What, config_parse_mount_node, 0, offsetof(Mount, parameters_fragment.what)
+Mount.Where, config_parse_unit_path_printf, 0, offsetof(Mount, where)
+Mount.Options, config_parse_unit_string_printf, 0, offsetof(Mount, parameters_fragment.options)
+Mount.Type, config_parse_unit_string_printf, 0, offsetof(Mount, parameters_fragment.fstype)
+Mount.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Mount, timeout_usec)
+Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode)
+Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options)
+Mount.LazyUnmount, config_parse_bool, 0, offsetof(Mount, lazy_unmount)
+Mount.ForceUnmount, config_parse_bool, 0, offsetof(Mount, force_unmount)
+Mount.ReadWriteOnly, config_parse_bool, 0, offsetof(Mount, read_write_only)
{{ EXEC_CONTEXT_CONFIG_ITEMS('Mount') }}
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Mount') }}
{{ KILL_CONTEXT_CONFIG_ITEMS('Mount') }}
-Automount.Where, config_parse_unit_path_printf, 0, offsetof(Automount, where)
-Automount.ExtraOptions, config_parse_unit_string_printf, 0, offsetof(Automount, extra_options)
-Automount.DirectoryMode, config_parse_mode, 0, offsetof(Automount, directory_mode)
-Automount.TimeoutIdleSec, config_parse_sec_fix_0, 0, offsetof(Automount, timeout_idle_usec)
-Swap.What, config_parse_mount_node, 0, offsetof(Swap, parameters_fragment.what)
-Swap.Priority, config_parse_swap_priority, 0, 0
-Swap.Options, config_parse_unit_string_printf, 0, offsetof(Swap, parameters_fragment.options)
-Swap.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Swap, timeout_usec)
+Automount.Where, config_parse_unit_path_printf, 0, offsetof(Automount, where)
+Automount.ExtraOptions, config_parse_unit_string_printf, 0, offsetof(Automount, extra_options)
+Automount.DirectoryMode, config_parse_mode, 0, offsetof(Automount, directory_mode)
+Automount.TimeoutIdleSec, config_parse_sec_fix_0, 0, offsetof(Automount, timeout_idle_usec)
+Swap.What, config_parse_mount_node, 0, offsetof(Swap, parameters_fragment.what)
+Swap.Priority, config_parse_swap_priority, 0, 0
+Swap.Options, config_parse_unit_string_printf, 0, offsetof(Swap, parameters_fragment.options)
+Swap.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Swap, timeout_usec)
{{ EXEC_CONTEXT_CONFIG_ITEMS('Swap') }}
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Swap') }}
{{ KILL_CONTEXT_CONFIG_ITEMS('Swap') }}
-Timer.OnCalendar, config_parse_timer, TIMER_CALENDAR, 0
-Timer.OnActiveSec, config_parse_timer, TIMER_ACTIVE, 0
-Timer.OnBootSec, config_parse_timer, TIMER_BOOT, 0
-Timer.OnStartupSec, config_parse_timer, TIMER_STARTUP, 0
-Timer.OnUnitActiveSec, config_parse_timer, TIMER_UNIT_ACTIVE, 0
-Timer.OnUnitInactiveSec, config_parse_timer, TIMER_UNIT_INACTIVE, 0
-Timer.OnClockChange, config_parse_bool, 0, offsetof(Timer, on_clock_change)
-Timer.OnTimezoneChange, config_parse_bool, 0, offsetof(Timer, on_timezone_change)
-Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent)
-Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
-Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse)
-Timer.FixedRandomDelay, config_parse_bool, 0, offsetof(Timer, fixed_random_delay)
-Timer.DeferReactivation, config_parse_bool, 0, offsetof(Timer, defer_reactivation)
-Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
-Timer.RandomizedDelaySec, config_parse_sec, 0, offsetof(Timer, random_usec)
-Timer.Unit, config_parse_trigger_unit, 0, 0
-Path.PathExists, config_parse_path_spec, 0, 0
-Path.PathExistsGlob, config_parse_path_spec, 0, 0
-Path.PathChanged, config_parse_path_spec, 0, 0
-Path.PathModified, config_parse_path_spec, 0, 0
-Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0
-Path.Unit, config_parse_trigger_unit, 0, 0
-Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory)
-Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode)
-Path.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Path, trigger_limit.interval)
-Path.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Path, trigger_limit.burst)
+Timer.OnCalendar, config_parse_timer, TIMER_CALENDAR, 0
+Timer.OnActiveSec, config_parse_timer, TIMER_ACTIVE, 0
+Timer.OnBootSec, config_parse_timer, TIMER_BOOT, 0
+Timer.OnStartupSec, config_parse_timer, TIMER_STARTUP, 0
+Timer.OnUnitActiveSec, config_parse_timer, TIMER_UNIT_ACTIVE, 0
+Timer.OnUnitInactiveSec, config_parse_timer, TIMER_UNIT_INACTIVE, 0
+Timer.OnClockChange, config_parse_bool, 0, offsetof(Timer, on_clock_change)
+Timer.OnTimezoneChange, config_parse_bool, 0, offsetof(Timer, on_timezone_change)
+Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent)
+Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
+Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse)
+Timer.FixedRandomDelay, config_parse_bool, 0, offsetof(Timer, fixed_random_delay)
+Timer.DeferReactivation, config_parse_bool, 0, offsetof(Timer, defer_reactivation)
+Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
+Timer.RandomizedDelaySec, config_parse_sec, 0, offsetof(Timer, random_usec)
+Timer.Unit, config_parse_trigger_unit, 0, 0
+Path.PathExists, config_parse_path_spec, 0, 0
+Path.PathExistsGlob, config_parse_path_spec, 0, 0
+Path.PathChanged, config_parse_path_spec, 0, 0
+Path.PathModified, config_parse_path_spec, 0, 0
+Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0
+Path.Unit, config_parse_trigger_unit, 0, 0
+Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory)
+Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode)
+Path.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Path, trigger_limit.interval)
+Path.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Path, trigger_limit.burst)
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Slice') }}
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Scope') }}
{{ KILL_CONTEXT_CONFIG_ITEMS('Scope') }}
-Scope.RuntimeMaxSec, config_parse_sec, 0, offsetof(Scope, runtime_max_usec)
-Scope.RuntimeRandomizedExtraSec, config_parse_sec, 0, offsetof(Scope, runtime_rand_extra_usec)
-Scope.TimeoutStopSec, config_parse_sec, 0, offsetof(Scope, timeout_stop_usec)
-Scope.OOMPolicy, config_parse_oom_policy, 0, offsetof(Scope, oom_policy)
+Scope.RuntimeMaxSec, config_parse_sec, 0, offsetof(Scope, runtime_max_usec)
+Scope.RuntimeRandomizedExtraSec, config_parse_sec, 0, offsetof(Scope, runtime_rand_extra_usec)
+Scope.TimeoutStopSec, config_parse_sec, 0, offsetof(Scope, timeout_stop_usec)
+Scope.OOMPolicy, config_parse_oom_policy, 0, offsetof(Scope, oom_policy)
{# The [Install] section is ignored here #}
-Install.Alias, NULL, 0, 0
-Install.WantedBy, NULL, 0, 0
-Install.RequiredBy, NULL, 0, 0
-Install.UpheldBy, NULL, 0, 0
-Install.Also, NULL, 0, 0
-Install.DefaultInstance, NULL, 0, 0
+Install.Alias, NULL, 0, 0
+Install.WantedBy, NULL, 0, 0
+Install.RequiredBy, NULL, 0, 0
+Install.UpheldBy, NULL, 0, 0
+Install.Also, NULL, 0, 0
+Install.DefaultInstance, NULL, 0, 0
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index ba6aad2f2b..4b702038e6 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -4121,6 +4121,44 @@ int config_parse_managed_oom_mem_pressure_limit(
return 0;
}
+int config_parse_managed_oom_mem_pressure_duration_sec(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t usec, *duration = ASSERT_PTR(data);
+ UnitType t;
+ int r;
+
+ t = unit_name_to_type(unit);
+ assert(t != _UNIT_TYPE_INVALID);
+
+ if (!unit_vtable[t]->can_set_managed_oom)
+ return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is not supported for this unit type, ignoring.", lvalue);
+
+ if (isempty(rvalue)) {
+ *duration = USEC_INFINITY;
+ return 0;
+ }
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0)
+ return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
+
+ if (usec < 1 * USEC_PER_SEC || usec == USEC_INFINITY)
+ return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= must be at least 1s and less than infinity, ignoring: %s", lvalue, rvalue);
+
+ *duration = usec;
+ return 0;
+}
+
int config_parse_device_allow(
const char *unit,
const char *filename,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index c7301cec52..e8b2eaee52 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -88,6 +88,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_delegate);
CONFIG_PARSER_PROTOTYPE(config_parse_delegate_subgroup);
CONFIG_PARSER_PROTOTYPE(config_parse_managed_oom_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_managed_oom_mem_pressure_limit);
+CONFIG_PARSER_PROTOTYPE(config_parse_managed_oom_mem_pressure_duration_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_managed_oom_preference);
CONFIG_PARSER_PROTOTYPE(config_parse_device_policy);
CONFIG_PARSER_PROTOTYPE(config_parse_device_allow);
diff --git a/src/core/manager.c b/src/core/manager.c
index 456ad46135..8e033c69c4 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -2793,7 +2793,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
}
}
- _cleanup_fdset_free_ FDSet *fds = NULL;
+ _cleanup_(fdset_free_asyncp) FDSet *fds = NULL;
if (n_fds > 0) {
assert(fd_array);
@@ -4007,30 +4007,26 @@ void manager_send_reloading(Manager *m) {
m->ready_sent = false;
}
-static bool generator_path_any(const char* const* paths) {
- bool found = false;
+static bool generator_path_any(char * const *paths) {
- /* Optimize by skipping the whole process by not creating output directories
- * if no generators are found. */
- STRV_FOREACH(path, paths)
- if (access(*path, F_OK) == 0)
- found = true;
- else if (errno != ENOENT)
- log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
+ /* Optimize by skipping the whole process by not creating output directories if no generators are found. */
- return found;
+ STRV_FOREACH(i, paths) {
+ if (access(*i, F_OK) >= 0)
+ return true;
+ if (errno != ENOENT)
+ log_warning_errno(errno, "Failed to check if generator dir '%s' exists, assuming not: %m", *i);
+ }
+
+ return false;
}
static int manager_run_environment_generators(Manager *m) {
- char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
_cleanup_strv_free_ char **paths = NULL;
- void* args[] = {
- [STDOUT_GENERATE] = &tmp,
- [STDOUT_COLLECT] = &tmp,
- [STDOUT_CONSUME] = &m->transient_environment,
- };
int r;
+ assert(m);
+
if (MANAGER_IS_TEST_RUN(m) && !(m->test_run_flags & MANAGER_TEST_RUN_ENV_GENERATORS))
return 0;
@@ -4038,9 +4034,16 @@ static int manager_run_environment_generators(Manager *m) {
if (!paths)
return log_oom();
- if (!generator_path_any((const char* const*) paths))
+ if (!generator_path_any(paths))
return 0;
+ char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
+ void *args[_STDOUT_CONSUME_MAX] = {
+ [STDOUT_GENERATE] = &tmp,
+ [STDOUT_COLLECT] = &tmp,
+ [STDOUT_CONSUME] = &m->transient_environment,
+ };
+
WITH_UMASK(0022)
r = execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, gather_environment,
args, NULL, m->transient_environment,
@@ -4078,6 +4081,12 @@ static int build_generator_environment(Manager *m, char ***ret) {
if (r < 0)
return r;
+ if (m->soft_reboots_count > 0) {
+ r = strv_env_assignf(&nl, "SYSTEMD_SOFT_REBOOTS_COUNT", "%u", m->soft_reboots_count);
+ if (r < 0)
+ return r;
+ }
+
if (m->first_boot >= 0) {
r = strv_env_assign(&nl, "SYSTEMD_FIRST_BOOT", one_zero(m->first_boot));
if (r < 0)
@@ -4117,17 +4126,12 @@ static int build_generator_environment(Manager *m, char ***ret) {
return 0;
}
-static int manager_execute_generators(Manager *m, char **paths, bool remount_ro) {
+static int manager_execute_generators(Manager *m, char * const *paths, bool remount_ro) {
_cleanup_strv_free_ char **ge = NULL;
- const char *argv[] = {
- NULL, /* Leave this empty, execute_directory() will fill something in */
- m->lookup_paths.generator,
- m->lookup_paths.generator_early,
- m->lookup_paths.generator_late,
- NULL,
- };
int r;
+ assert(m);
+
r = build_generator_environment(m, &ge);
if (r < 0)
return log_error_errno(r, "Failed to build generator environment: %m");
@@ -4144,6 +4148,14 @@ static int manager_execute_generators(Manager *m, char **paths, bool remount_ro)
log_warning_errno(r, "Read-only bind remount failed, ignoring: %m");
}
+ const char *argv[] = {
+ NULL, /* Leave this empty, execute_directory() will fill something in */
+ m->lookup_paths.generator,
+ m->lookup_paths.generator_early,
+ m->lookup_paths.generator_late,
+ NULL,
+ };
+
BLOCK_WITH_UMASK(0022);
return execute_directories(
(const char* const*) paths,
@@ -4168,7 +4180,7 @@ static int manager_run_generators(Manager *m) {
if (!paths)
return log_oom();
- if (!generator_path_any((const char* const*) paths))
+ if (!generator_path_any(paths))
return 0;
r = lookup_paths_mkdir_generator(&m->lookup_paths);
diff --git a/src/core/mount.c b/src/core/mount.c
index 0a403b6759..19dd09c58f 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1997,6 +1997,7 @@ static void mount_enumerate(Manager *m) {
mnt_init_debug(0);
if (!m->mount_monitor) {
+ usec_t mount_rate_limit_interval = 1 * USEC_PER_SEC;
unsigned mount_rate_limit_burst = 5;
int fd;
@@ -2038,14 +2039,21 @@ static void mount_enumerate(Manager *m) {
}
/* Let users override the default (5 in 1s), as it stalls the boot sequence on busy systems. */
- const char *e = secure_getenv("SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST");
+ const char *e = secure_getenv("SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_INTERVAL_SEC");
+ if (e) {
+ r = parse_sec(e, &mount_rate_limit_interval);
+ if (r < 0)
+ log_debug_errno(r, "Invalid value in $SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_INTERVAL_SEC, ignoring: %s", e);
+ }
+
+ e = secure_getenv("SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST");
if (e) {
r = safe_atou(e, &mount_rate_limit_burst);
if (r < 0)
- log_debug("Invalid value in $SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST, ignoring: %s", e);
+ log_debug_errno(r, "Invalid value in $SYSTEMD_DEFAULT_MOUNT_RATE_LIMIT_BURST, ignoring: %s", e);
}
- r = sd_event_source_set_ratelimit(m->mount_event_source, 1 * USEC_PER_SEC, mount_rate_limit_burst);
+ r = sd_event_source_set_ratelimit(m->mount_event_source, mount_rate_limit_interval, mount_rate_limit_burst);
if (r < 0) {
log_error_errno(r, "Failed to enable rate limit for mount events: %m");
goto fail;
diff --git a/src/core/namespace.c b/src/core/namespace.c
index d13d57f11a..e1a88f2455 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -526,6 +526,8 @@ static int append_extensions(
&pick_filter_image_raw,
PICK_ARCHITECTURE|PICK_TRIES,
&result);
+ if (r == -ENOENT && m->ignore_enoent)
+ continue;
if (r < 0)
return r;
if (!result.path) {
@@ -594,6 +596,8 @@ static int append_extensions(
&pick_filter_image_dir,
PICK_ARCHITECTURE|PICK_TRIES,
&result);
+ if (r == -ENOENT && ignore_enoent)
+ continue;
if (r < 0)
return r;
if (!result.path) {
diff --git a/src/core/service.c b/src/core/service.c
index 8f4cd63670..dfad574e65 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -968,7 +968,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
"%sResult: %s\n"
"%sReload Result: %s\n"
"%sClean Result: %s\n"
- "%sMount Result: %s\n"
+ "%sLiveMount Result: %s\n"
"%sPermissionsStartOnly: %s\n"
"%sRootDirectoryStartOnly: %s\n"
"%sRemainAfterExit: %s\n"
@@ -5309,18 +5309,12 @@ static int service_can_live_mount(const Unit *u, sd_bus_error *error) {
assert(u);
/* Ensure that the unit runs in a private mount namespace */
- if (!exec_needs_mount_namespace(unit_get_exec_context(u), /* params= */ NULL, unit_get_exec_runtime(u))) {
-
- /* This is also called in property_get_can_live_mount(). Suppress the debugging log when called in it. */
- if (error)
- log_unit_debug(u, "Unit not running in private mount namespace, cannot live mount");
-
+ if (!exec_needs_mount_namespace(unit_get_exec_context(u), /* params= */ NULL, unit_get_exec_runtime(u)))
return sd_bus_error_setf(
error,
SD_BUS_ERROR_INVALID_ARGS,
- "Live mounting for unit '%s' cannot be scheduled: unit not running in private mount namespace",
+ "Unit '%s' not running in private mount namespace, cannot live mount.",
u->id);
- }
return 0;
}
diff --git a/src/core/unit.c b/src/core/unit.c
index 684fe698ee..4384e3bfcb 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -6396,24 +6396,20 @@ Condition *unit_find_failed_condition(Unit *u) {
int unit_can_live_mount(const Unit *u, sd_bus_error *error) {
assert(u);
- if (!UNIT_VTABLE(u)->live_mount) {
- log_unit_debug(u, "Live mounting not supported for unit type '%s'", unit_type_to_string(u->type));
+ if (!UNIT_VTABLE(u)->live_mount)
return sd_bus_error_setf(
error,
SD_BUS_ERROR_INVALID_ARGS,
- "Live mounting for unit '%s' cannot be scheduled: live mounting not supported for unit type '%s'",
- u->id,
- unit_type_to_string(u->type));
- }
+ "Live mounting not supported for unit type '%s' of unit '%s'.",
+ unit_type_to_string(u->type),
+ u->id);
- if (u->load_state != UNIT_LOADED) {
- log_unit_debug(u, "Unit not loaded");
+ if (u->load_state != UNIT_LOADED)
return sd_bus_error_setf(
error,
BUS_ERROR_NO_SUCH_UNIT,
- "Live mounting for unit '%s' cannot be scheduled: unit not loaded",
+ "Unit '%s' not loaded, cannot live mount.",
u->id);
- }
if (!UNIT_VTABLE(u)->can_live_mount)
return 0;
diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c
index 7637980689..98670a6c90 100644
--- a/src/debug-generator/debug-generator.c
+++ b/src/debug-generator/debug-generator.c
@@ -206,7 +206,7 @@ static int process_unit_credentials(const char *credentials_dir) {
if (!p)
return log_oom();
- r = write_string_file_atomic_label(p, d);
+ r = write_string_file_at_label(AT_FDCWD, p, d, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
if (r < 0) {
log_warning_errno(r, "Failed to write unit file '%s' from credential '%s', ignoring: %m",
unit, de->d_name);
diff --git a/src/fundamental/sha256-fundamental.c b/src/fundamental/sha256-fundamental.c
index f8524bae69..03381835d6 100644
--- a/src/fundamental/sha256-fundamental.c
+++ b/src/fundamental/sha256-fundamental.c
@@ -21,14 +21,8 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <stdbool.h>
-#if SD_BOOT
-# include "efi-string.h"
-#else
-# include <string.h>
-#endif
-
#include "macro-fundamental.h"
+#include "memory-util-fundamental.h"
#include "sha256-fundamental.h"
#include "unaligned-fundamental.h"
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index d69a16af81..8bb32afb27 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -503,21 +503,6 @@ static int add_partition_xbootldr(DissectedPartition *p) {
}
#if ENABLE_EFI
-static bool slash_boot_exists(void) {
- static int cache = -1;
-
- if (cache >= 0)
- return cache;
-
- if (access("/boot", F_OK) >= 0)
- return (cache = true);
- if (errno != ENOENT)
- log_error_errno(errno, "Failed to determine whether /boot/ exists, assuming no: %m");
- else
- log_debug_errno(errno, "/boot/: %m");
- return (cache = false);
-}
-
static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) {
const char *esp_path = NULL, *id = NULL;
_cleanup_free_ char *options = NULL;
@@ -538,10 +523,10 @@ static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) {
if (r > 0)
return 0;
- /* If /boot/ is present, unused, and empty, we'll take that.
+ /* If XBOOTLDR partition is not present and /boot/ is unused and empty, we'll take that.
* Otherwise, if /efi/ is unused and empty (or missing), we'll take that.
* Otherwise, we do nothing. */
- if (!has_xbootldr && slash_boot_exists()) {
+ if (!has_xbootldr) {
r = path_is_busy("/boot");
if (r < 0)
return r;
@@ -955,7 +940,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
}
static int run(const char *dest, const char *dest_early, const char *dest_late) {
- int r, k;
+ int r;
assert_se(arg_dest = dest_late);
@@ -975,12 +960,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
if (arg_root_enabled)
r = add_root_mount();
+ else
+ r = 0;
- if (!in_initrd()) {
- k = add_mounts();
- if (r >= 0)
- r = k;
- }
+ if (!in_initrd())
+ RET_GATHER(r, add_mounts());
return r;
}
diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c
index 0168428394..2b175342c1 100644
--- a/src/hibernate-resume/hibernate-resume-generator.c
+++ b/src/hibernate-resume/hibernate-resume-generator.c
@@ -107,6 +107,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
return 0;
}
+ if (generator_soft_rebooted()) {
+ log_debug("Running in an initrd entered through soft-reboot, not initiating resume.");
+ return 0;
+ }
+
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 8ed5d98675..084e1b492d 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -50,11 +50,11 @@ sd_id128_t arg_boot_id = {};
int arg_boot_offset = 0;
bool arg_dmesg = false;
bool arg_no_hostname = false;
-const char *arg_cursor = NULL;
-const char *arg_cursor_file = NULL;
-const char *arg_after_cursor = NULL;
+char *arg_cursor = NULL;
+char *arg_cursor_file = NULL;
+char *arg_after_cursor = NULL;
bool arg_show_cursor = false;
-const char *arg_directory = NULL;
+char *arg_directory = NULL;
char **arg_file = NULL;
bool arg_file_stdin = false;
int arg_priorities = 0;
@@ -75,7 +75,7 @@ char **arg_user_units = NULL;
bool arg_invocation = false;
sd_id128_t arg_invocation_id = SD_ID128_NULL;
int arg_invocation_offset = 0;
-const char *arg_field = NULL;
+char *arg_field = NULL;
bool arg_catalog = false;
bool arg_reverse = false;
int arg_journal_type = 0;
@@ -83,27 +83,35 @@ int arg_journal_additional_open_flags = 0;
int arg_namespace_flags = 0;
char *arg_root = NULL;
char *arg_image = NULL;
-const char *arg_machine = NULL;
-const char *arg_namespace = NULL;
+char *arg_machine = NULL;
+char *arg_namespace = NULL;
uint64_t arg_vacuum_size = 0;
uint64_t arg_vacuum_n_files = 0;
usec_t arg_vacuum_time = 0;
Set *arg_output_fields = NULL;
-const char *arg_pattern = NULL;
+char *arg_pattern = NULL;
pcre2_code *arg_compiled_pattern = NULL;
PatternCompileCase arg_case = PATTERN_COMPILE_CASE_AUTO;
static ImagePolicy *arg_image_policy = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_cursor, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_cursor_file, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_after_cursor, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_facilities, set_freep);
-STATIC_DESTRUCTOR_REGISTER(arg_verify_key, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_verify_key, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_syslog_identifier, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_exclude_identifier, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_system_units, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_user_units, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_field, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_machine, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_namespace, freep);
STATIC_DESTRUCTOR_REGISTER(arg_output_fields, set_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_pattern, freep);
STATIC_DESTRUCTOR_REGISTER(arg_compiled_pattern, pattern_freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
@@ -567,24 +575,29 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'M':
- arg_machine = optarg;
+ r = free_and_strdup_warn(&arg_machine, optarg);
+ if (r < 0)
+ return r;
break;
case ARG_NAMESPACE:
if (streq(optarg, "*")) {
arg_namespace_flags = SD_JOURNAL_ALL_NAMESPACES;
- arg_namespace = NULL;
+ arg_namespace = mfree(arg_namespace);
} else if (startswith(optarg, "+")) {
arg_namespace_flags = SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE;
- arg_namespace = optarg + 1;
+ r = free_and_strdup_warn(&arg_namespace, optarg + 1);
+ if (r < 0)
+ return r;
} else if (isempty(optarg)) {
arg_namespace_flags = 0;
- arg_namespace = NULL;
+ arg_namespace = mfree(arg_namespace);
} else {
arg_namespace_flags = 0;
- arg_namespace = optarg;
+ r = free_and_strdup_warn(&arg_namespace, optarg);
+ if (r < 0)
+ return r;
}
-
break;
case ARG_LIST_NAMESPACES:
@@ -592,7 +605,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'D':
- arg_directory = optarg;
+ r = free_and_strdup_warn(&arg_directory, optarg);
+ if (r < 0)
+ return r;
break;
case 'i':
@@ -628,15 +643,21 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'c':
- arg_cursor = optarg;
+ r = free_and_strdup_warn(&arg_cursor, optarg);
+ if (r < 0)
+ return r;
break;
case ARG_CURSOR_FILE:
- arg_cursor_file = optarg;
+ r = free_and_strdup_warn(&arg_cursor_file, optarg);
+ if (r < 0)
+ return r;
break;
case ARG_AFTER_CURSOR:
- arg_after_cursor = optarg;
+ r = free_and_strdup_warn(&arg_after_cursor, optarg);
+ if (r < 0)
+ return r;
break;
case ARG_SHOW_CURSOR:
@@ -689,9 +710,11 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_VERIFY_KEY:
- r = free_and_strdup(&arg_verify_key, optarg);
- if (r < 0)
- return r;
+ erase_and_free(arg_verify_key);
+ arg_verify_key = strdup(optarg);
+ if (!arg_verify_key)
+ return log_oom();
+
/* Use memset not explicit_bzero() or similar so this doesn't look confusing
* in ps or htop output. */
memset(optarg, 'x', strlen(optarg));
@@ -791,7 +814,9 @@ static int parse_argv(int argc, char *argv[]) {
}
case 'g':
- arg_pattern = optarg;
+ r = free_and_strdup_warn(&arg_pattern, optarg);
+ if (r < 0)
+ return r;
break;
case ARG_CASE_SENSITIVE:
@@ -861,7 +886,9 @@ static int parse_argv(int argc, char *argv[]) {
case 'F':
arg_action = ACTION_LIST_FIELDS;
- arg_field = optarg;
+ r = free_and_strdup_warn(&arg_field, optarg);
+ if (r < 0)
+ return r;
break;
case 'N':
@@ -1032,6 +1059,7 @@ static int parse_argv(int argc, char *argv[]) {
static int run(int argc, char *argv[]) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_freep) char *mounted_dir = NULL;
+ _cleanup_strv_free_ char **args = NULL;
int r;
setlocale(LC_ALL, "");
@@ -1041,7 +1069,9 @@ static int run(int argc, char *argv[]) {
if (r <= 0)
return r;
- char **args = strv_skip(argv, optind);
+ r = strv_copy_unless_empty(strv_skip(argv, optind), &args);
+ if (r < 0)
+ return log_oom();
if (arg_image) {
assert(!arg_root);
diff --git a/src/journal/journalctl.h b/src/journal/journalctl.h
index 409cfe2bca..5e3e73544f 100644
--- a/src/journal/journalctl.h
+++ b/src/journal/journalctl.h
@@ -55,11 +55,11 @@ extern sd_id128_t arg_boot_id;
extern int arg_boot_offset;
extern bool arg_dmesg;
extern bool arg_no_hostname;
-extern const char *arg_cursor;
-extern const char *arg_cursor_file;
-extern const char *arg_after_cursor;
+extern char *arg_cursor;
+extern char *arg_cursor_file;
+extern char *arg_after_cursor;
extern bool arg_show_cursor;
-extern const char *arg_directory;
+extern char *arg_directory;
extern char **arg_file;
extern bool arg_file_stdin;
extern int arg_priorities;
@@ -80,7 +80,7 @@ extern char **arg_user_units;
extern bool arg_invocation;
extern sd_id128_t arg_invocation_id;
extern int arg_invocation_offset;
-extern const char *arg_field;
+extern char *arg_field;
extern bool arg_catalog;
extern bool arg_reverse;
extern int arg_journal_type;
@@ -88,13 +88,13 @@ extern int arg_journal_additional_open_flags;
extern int arg_namespace_flags;
extern char *arg_root;
extern char *arg_image;
-extern const char *arg_machine;
-extern const char *arg_namespace;
+extern char *arg_machine;
+extern char *arg_namespace;
extern uint64_t arg_vacuum_size;
extern uint64_t arg_vacuum_n_files;
extern usec_t arg_vacuum_time;
extern Set *arg_output_fields;
-extern const char *arg_pattern;
+extern char *arg_pattern;
extern pcre2_code *arg_compiled_pattern;
extern PatternCompileCase arg_case;
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index 0a535ddf4b..5ed7784a85 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -75,24 +75,11 @@
* The maximum value corresponds to 18.2 hours. 0 MUST NOT be used. */
#define RADV_HOME_AGENT_MAX_LIFETIME_USEC (UINT16_MAX * USEC_PER_SEC)
-#define RADV_OPT_ROUTE_INFORMATION 24
-#define RADV_OPT_RDNSS 25
-#define RADV_OPT_DNSSL 31
-/* Pref64 option type (RFC8781, section 4) */
-#define RADV_OPT_PREF64 38
-
typedef enum RAdvState {
RADV_STATE_IDLE = 0,
RADV_STATE_ADVERTISING = 1,
} RAdvState;
-struct sd_radv_opt_dns {
- uint8_t type;
- uint8_t length;
- uint16_t reserved;
- be32_t lifetime;
-} _packed_;
-
struct sd_radv {
unsigned n_ref;
RAdvState state;
@@ -104,114 +91,19 @@ struct sd_radv {
sd_event *event;
int event_priority;
- struct ether_addr mac_addr;
uint8_t hop_limit;
uint8_t flags;
uint8_t preference;
- uint32_t mtu;
usec_t reachable_usec;
usec_t retransmit_usec;
usec_t lifetime_usec; /* timespan */
+ Set *options;
+
int fd;
unsigned ra_sent;
sd_event_source *recv_event_source;
sd_event_source *timeout_event_source;
-
- unsigned n_prefixes;
- LIST_HEAD(sd_radv_prefix, prefixes);
-
- unsigned n_route_prefixes;
- LIST_HEAD(sd_radv_route_prefix, route_prefixes);
-
- unsigned n_pref64_prefixes;
- LIST_HEAD(sd_radv_pref64_prefix, pref64_prefixes);
-
- size_t n_rdnss;
- struct sd_radv_opt_dns *rdnss;
- struct sd_radv_opt_dns *dnssl;
-
- /* Mobile IPv6 extension: Home Agent Info. */
- struct nd_opt_home_agent_info home_agent;
-};
-
-#define radv_prefix_opt__contents { \
- uint8_t type; \
- uint8_t length; \
- uint8_t prefixlen; \
- uint8_t flags; \
- be32_t lifetime_valid; \
- be32_t lifetime_preferred; \
- uint32_t reserved; \
- struct in6_addr in6_addr; \
-}
-
-struct radv_prefix_opt radv_prefix_opt__contents;
-
-/* We need the opt substructure to be packed, because we use it in send(). But
- * if we use _packed_, this means that the structure cannot be used directly in
- * normal code in general, because the fields might not be properly aligned.
- * But in this particular case, the structure is defined in a way that gives
- * proper alignment, even without the explicit _packed_ attribute. To appease
- * the compiler we use the "unpacked" structure, but we also verify that
- * structure contains no holes, so offsets are the same when _packed_ is used.
- */
-struct radv_prefix_opt__packed radv_prefix_opt__contents _packed_;
-assert_cc(sizeof(struct radv_prefix_opt) == sizeof(struct radv_prefix_opt__packed));
-
-struct sd_radv_prefix {
- unsigned n_ref;
-
- struct radv_prefix_opt opt;
-
- LIST_FIELDS(struct sd_radv_prefix, prefix);
-
- /* These are timespans, NOT points in time. */
- usec_t lifetime_valid_usec;
- usec_t lifetime_preferred_usec;
- /* These are points in time specified with clock_boottime_or_monotonic(), NOT timespans. */
- usec_t valid_until;
- usec_t preferred_until;
-};
-
-#define radv_route_prefix_opt__contents { \
- uint8_t type; \
- uint8_t length; \
- uint8_t prefixlen; \
- uint8_t flags_reserved; \
- be32_t lifetime; \
- struct in6_addr in6_addr; \
-}
-
-struct radv_route_prefix_opt radv_route_prefix_opt__contents;
-
-struct radv_route_prefix_opt__packed radv_route_prefix_opt__contents _packed_;
-assert_cc(sizeof(struct radv_route_prefix_opt) == sizeof(struct radv_route_prefix_opt__packed));
-
-struct sd_radv_route_prefix {
- unsigned n_ref;
-
- struct radv_route_prefix_opt opt;
-
- LIST_FIELDS(struct sd_radv_route_prefix, prefix);
-
- /* This is a timespan, NOT a point in time. */
- usec_t lifetime_usec;
- /* This is a point in time specified with clock_boottime_or_monotonic(), NOT a timespan. */
- usec_t valid_until;
-};
-
-struct sd_radv_pref64_prefix {
- unsigned n_ref;
-
- struct nd_opt_prefix64_info opt;
-
- struct in6_addr in6_addr;
- uint8_t prefixlen;
-
- usec_t lifetime_usec;
-
- LIST_FIELDS(struct sd_radv_pref64_prefix, prefix);
};
#define log_radv_errno(radv, error, fmt, ...) \
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 3e992d7cad..5661beeb65 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -767,6 +767,14 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
switch (client->state) {
case DHCP6_STATE_INFORMATION_REQUEST:
+ /* RFC 7084 section 4.2 (https://datatracker.ietf.org/doc/html/rfc7084#section-4.2)
+ * WPD-4: By default, the IPv6 CE router MUST initiate DHCPv6 prefix delegation when either
+ * the M or O flags are set to 1 in a received Router Advertisement (RA) message. */
+ if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) {
+ r = dhcp6_option_append_ia(&buf, &offset, (client->lease ? client->lease->ia_pd : NULL) ?: &client->ia_pd);
+ if (r < 0)
+ return r;
+ }
break;
case DHCP6_STATE_SOLICITATION:
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index c2eb0db87d..261ab83c64 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -71,6 +71,32 @@ static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
lease->lifetime_t2 = t2;
}
+static void dhcp6_client_set_information_refresh_time(sd_dhcp6_client *client, sd_dhcp6_lease *lease, usec_t irt) {
+ usec_t t1 = USEC_INFINITY, t2 = USEC_INFINITY, min_valid_lt = USEC_INFINITY;
+
+ if (lease->ia_pd) {
+ t1 = be32_sec_to_usec(lease->ia_pd->header.lifetime_t1, /* max_as_infinity = */ true);
+ t2 = be32_sec_to_usec(lease->ia_pd->header.lifetime_t2, /* max_as_infinity = */ true);
+
+ LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
+ min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true));
+
+ if (t2 == 0 || t2 > min_valid_lt) {
+ /* If T2 is zero or longer than the minimum valid lifetime of the prefixes,
+ * then adjust lifetime with it. */
+ t1 = min_valid_lt / 2;
+ t2 = min_valid_lt / 10 * 8;
+ }
+
+ /* Adjust the received information refresh time with T1. */
+ irt = MIN(irt, t1);
+ }
+
+ client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
+ log_dhcp6_client(client, "New information request will be refused in %s.",
+ FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
+}
+
#define DEFINE_GET_TIME_FUNCTIONS(name, val) \
int sd_dhcp6_lease_get_##name( \
sd_dhcp6_lease *lease, \
@@ -763,18 +789,12 @@ static int dhcp6_lease_parse_message(
continue;
}
- dhcp6_ia_free(lease->ia_na);
- lease->ia_na = TAKE_PTR(ia);
+ free_and_replace_full(lease->ia_na, ia, dhcp6_ia_free);
break;
}
case SD_DHCP6_OPTION_IA_PD: {
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
- if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
- log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
- break;
- }
-
r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
if (r == -ENOMEM)
return log_oom_debug();
@@ -788,8 +808,7 @@ static int dhcp6_lease_parse_message(
continue;
}
- dhcp6_ia_free(lease->ia_pd);
- lease->ia_pd = TAKE_PTR(ia);
+ free_and_replace_full(lease->ia_pd, ia, dhcp6_ia_free);
break;
}
case SD_DHCP6_OPTION_RAPID_COMMIT:
@@ -872,12 +891,9 @@ static int dhcp6_lease_parse_message(
"The client ID in %s message does not match. Ignoring.",
dhcp6_message_type_to_string(message->type));
- if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
- client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
- log_dhcp6_client(client, "New information request will be refused in %s.",
- FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
-
- } else {
+ if (client->state == DHCP6_STATE_INFORMATION_REQUEST)
+ dhcp6_client_set_information_refresh_time(client, lease, irt);
+ else {
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
if (r < 0)
return log_dhcp6_client_errno(client, r, "%s has no server id",
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index c384d4e627..f241929ad5 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -3,6 +3,7 @@
Copyright © 2017 Intel Corporation. All rights reserved.
***/
+#include <linux/ipv6.h>
#include <netinet/icmp6.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -68,7 +69,6 @@ int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) {
}
int sd_radv_detach_event(sd_radv *ra) {
-
assert_return(ra, -EINVAL);
ra->event = sd_event_unref(ra->event);
@@ -102,13 +102,6 @@ static sd_radv *radv_free(sd_radv *ra) {
if (!ra)
return NULL;
- LIST_CLEAR(prefix, ra->prefixes, sd_radv_prefix_unref);
- LIST_CLEAR(prefix, ra->route_prefixes, sd_radv_route_prefix_unref);
- LIST_CLEAR(prefix, ra->pref64_prefixes, sd_radv_pref64_prefix_unref);
-
- free(ra->rdnss);
- free(ra->dnssl);
-
radv_reset(ra);
sd_event_source_unref(ra->timeout_event_source);
@@ -117,6 +110,8 @@ static sd_radv *radv_free(sd_radv *ra) {
ra->fd = safe_close(ra->fd);
free(ra->ifname);
+ set_free(ra->options);
+
return mfree(ra);
}
@@ -135,6 +130,7 @@ static int radv_send_router_on_stop(sd_radv *ra) {
};
_cleanup_set_free_ Set *options = NULL;
+ struct ether_addr mac_addr;
usec_t time_now;
int r;
@@ -144,8 +140,9 @@ static int radv_send_router_on_stop(sd_radv *ra) {
if (r < 0)
return r;
- if (!ether_addr_is_null(&ra->mac_addr)) {
- r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &ra->mac_addr);
+ /* On stop, we only send source link-layer address option. */
+ if (ndisc_option_get_mac(ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &mac_addr) >= 0) {
+ r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &mac_addr);
if (r < 0)
return r;
}
@@ -156,39 +153,12 @@ static int radv_send_router_on_stop(sd_radv *ra) {
static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) {
assert(ra);
- struct sockaddr_in6 dst_addr = {
- .sin6_family = AF_INET6,
- .sin6_addr = IN6_ADDR_ALL_NODES_MULTICAST,
- };
struct nd_router_advert adv = {
.nd_ra_type = ND_ROUTER_ADVERT,
.nd_ra_router_lifetime = usec_to_be16_sec(ra->lifetime_usec),
.nd_ra_reachable = usec_to_be32_msec(ra->reachable_usec),
.nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec),
};
- struct {
- struct nd_opt_hdr opthdr;
- struct ether_addr slladdr;
- } _packed_ opt_mac = {
- .opthdr = {
- .nd_opt_type = ND_OPT_SOURCE_LINKADDR,
- .nd_opt_len = DIV_ROUND_UP(sizeof(struct nd_opt_hdr) + sizeof(struct ether_addr), 8),
- },
- .slladdr = ra->mac_addr,
- };
- struct nd_opt_mtu opt_mtu = {
- .nd_opt_mtu_type = ND_OPT_MTU,
- .nd_opt_mtu_len = 1,
- .nd_opt_mtu_mtu = htobe32(ra->mtu),
- };
- /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, N routes, N pref64 prefixes, RDNSS,
- * DNSSL, and home agent. */
- struct iovec iov[6 + ra->n_prefixes + ra->n_route_prefixes + ra->n_pref64_prefixes];
- struct msghdr msg = {
- .msg_name = &dst_addr,
- .msg_namelen = sizeof(dst_addr),
- .msg_iov = iov,
- };
usec_t time_now;
int r;
@@ -196,72 +166,16 @@ static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) {
if (r < 0)
return r;
- if (dst && in6_addr_is_set(dst))
- dst_addr.sin6_addr = *dst;
-
/* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
* simultaneously in the structured initializer in the above. */
adv.nd_ra_curhoplimit = ra->hop_limit;
/* RFC 4191, Section 2.2,
* "...If the Router Lifetime is zero, the preference value MUST be set to (00) by the sender..." */
adv.nd_ra_flags_reserved = ra->flags | (ra->lifetime_usec > 0 ? (ra->preference << 3) : 0);
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&adv, sizeof(adv));
-
- /* MAC address is optional, either because the link does not use L2 addresses or load sharing is
- * desired. See RFC 4861, Section 4.2. */
- if (!ether_addr_is_null(&ra->mac_addr))
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mac, sizeof(opt_mac));
-
- if (ra->mtu > 0)
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mtu, sizeof(opt_mtu));
-
- LIST_FOREACH(prefix, p, ra->prefixes) {
- usec_t lifetime_valid_usec, lifetime_preferred_usec;
-
- lifetime_valid_usec = MIN(usec_sub_unsigned(p->valid_until, time_now),
- p->lifetime_valid_usec);
-
- lifetime_preferred_usec = MIN3(usec_sub_unsigned(p->preferred_until, time_now),
- p->lifetime_preferred_usec,
- lifetime_valid_usec);
-
- p->opt.lifetime_valid = usec_to_be32_sec(lifetime_valid_usec);
- p->opt.lifetime_preferred = usec_to_be32_sec(lifetime_preferred_usec);
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
- }
-
- LIST_FOREACH(prefix, rt, ra->route_prefixes) {
- rt->opt.lifetime = usec_to_be32_sec(MIN(usec_sub_unsigned(rt->valid_until, time_now),
- rt->lifetime_usec));
-
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&rt->opt, sizeof(rt->opt));
- }
-
- LIST_FOREACH(prefix, p, ra->pref64_prefixes)
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt));
-
- if (ra->rdnss)
- iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->rdnss, ra->rdnss->length * 8);
-
- if (ra->dnssl)
- iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->dnssl, ra->dnssl->length * 8);
-
- if (FLAGS_SET(ra->flags, ND_RA_FLAG_HOME_AGENT)) {
- ra->home_agent.nd_opt_home_agent_info_type = ND_OPT_HOME_AGENT_INFO;
- ra->home_agent.nd_opt_home_agent_info_len = 1;
-
- /* 0 means to place the current Router Lifetime value */
- if (ra->home_agent.nd_opt_home_agent_info_lifetime == 0)
- ra->home_agent.nd_opt_home_agent_info_lifetime = adv.nd_ra_router_lifetime;
-
- iov[msg.msg_iovlen++] = IOVEC_MAKE(&ra->home_agent, sizeof(ra->home_agent));
- }
-
- if (sendmsg(ra->fd, &msg, 0) < 0)
- return -errno;
-
- return 0;
+ return ndisc_send(ra->fd,
+ (dst && in6_addr_is_set(dst)) ? dst : &IN6_ADDR_ALL_NODES_MULTICAST,
+ &adv.nd_ra_hdr, ra->options, time_now);
}
static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) {
@@ -512,25 +426,7 @@ int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr) {
return 0;
}
-int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
- assert_return(ra, -EINVAL);
-
- if (mac_addr)
- ra->mac_addr = *mac_addr;
- else
- zero(ra->mac_addr);
-
- return 0;
-}
-
-int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
- assert_return(ra, -EINVAL);
- assert_return(mtu >= 1280, -EINVAL);
-
- ra->mtu = mtu;
-
- return 0;
-}
+/* Managing RA header. */
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
assert_return(ra, -EINVAL);
@@ -588,497 +484,240 @@ int sd_radv_set_preference(sd_radv *ra, uint8_t preference) {
return 0;
}
-int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent) {
- assert_return(ra, -EINVAL);
+/* Managing options. */
- SET_FLAG(ra->flags, ND_RA_FLAG_HOME_AGENT, home_agent);
- return 0;
-}
-
-int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference) {
+int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
assert_return(ra, -EINVAL);
- ra->home_agent.nd_opt_home_agent_info_preference = htobe16(preference);
- return 0;
+ return ndisc_option_set_link_layer_address(&ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, mac_addr);
}
-int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
- assert_return(ra, -EINVAL);
-
- if (lifetime_usec > RADV_HOME_AGENT_MAX_LIFETIME_USEC)
- return -EINVAL;
+void sd_radv_unset_mac(sd_radv *ra) {
+ if (!ra)
+ return;
- ra->home_agent.nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime_usec);
- return 0;
+ ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS);
}
-int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
- sd_radv_prefix *found = NULL;
+int sd_radv_add_prefix(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint8_t flags,
+ uint64_t valid_lifetime_usec,
+ uint64_t preferred_lifetime_usec,
+ uint64_t valid_until,
+ uint64_t preferred_until) {
assert_return(ra, -EINVAL);
- assert_return(p, -EINVAL);
-
- /* Refuse prefixes that don't have a prefix set */
- if (in6_addr_is_null(&p->opt.in6_addr))
- return -ENOEXEC;
+ assert_return(prefix, -EINVAL);
- const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
+ sd_ndisc_option *opt;
+ SET_FOREACH(opt, ra->options) {
+ if (opt->type != SD_NDISC_OPTION_PREFIX_INFORMATION)
+ continue;
- LIST_FOREACH(prefix, cur, ra->prefixes) {
- if (!in6_addr_prefix_intersect(&cur->opt.in6_addr, cur->opt.prefixlen,
- &p->opt.in6_addr, p->opt.prefixlen))
+ if (!in6_addr_prefix_intersect(&opt->prefix.address, opt->prefix.prefixlen, prefix, prefixlen))
continue; /* no intersection */
- if (cur->opt.prefixlen == p->opt.prefixlen) {
- found = cur;
+ if (opt->prefix.prefixlen == prefixlen)
break; /* same prefix */
- }
return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
"IPv6 prefix %s conflicts with %s, ignoring.",
- addr_p,
- IN6_ADDR_PREFIX_TO_STRING(&cur->opt.in6_addr, cur->opt.prefixlen));
+ IN6_ADDR_PREFIX_TO_STRING(prefix, prefixlen),
+ IN6_ADDR_PREFIX_TO_STRING(&opt->prefix.address, opt->prefix.prefixlen));
}
- if (found) {
- /* p and cur may be equivalent. First increment the reference counter. */
- sd_radv_prefix_ref(p);
-
- /* Then, remove the old entry. */
- LIST_REMOVE(prefix, ra->prefixes, found);
- sd_radv_prefix_unref(found);
-
- /* Finally, add the new entry. */
- LIST_APPEND(prefix, ra->prefixes, p);
-
- log_radv(ra, "Updated/replaced IPv6 prefix %s (preferred: %s, valid: %s)",
- addr_p,
- FORMAT_TIMESPAN(p->lifetime_preferred_usec, USEC_PER_SEC),
- FORMAT_TIMESPAN(p->lifetime_valid_usec, USEC_PER_SEC));
- } else {
- /* The prefix is new. Let's simply add it. */
-
- sd_radv_prefix_ref(p);
- LIST_APPEND(prefix, ra->prefixes, p);
- ra->n_prefixes++;
-
- log_radv(ra, "Added prefix %s", addr_p);
- }
-
- return 0;
+ return ndisc_option_set_prefix(
+ &ra->options,
+ flags,
+ prefixlen,
+ prefix,
+ valid_lifetime_usec,
+ preferred_lifetime_usec,
+ valid_until,
+ preferred_until);
}
void sd_radv_remove_prefix(
sd_radv *ra,
const struct in6_addr *prefix,
- unsigned char prefixlen) {
+ uint8_t prefixlen) {
- if (!ra)
+ if (!ra || !prefix)
return;
- if (!prefix)
- return;
-
- LIST_FOREACH(prefix, cur, ra->prefixes) {
- if (prefixlen != cur->opt.prefixlen)
- continue;
-
- if (!in6_addr_equal(prefix, &cur->opt.in6_addr))
- continue;
-
- LIST_REMOVE(prefix, ra->prefixes, cur);
- ra->n_prefixes--;
- sd_radv_prefix_unref(cur);
- return;
- }
+ ndisc_option_remove(ra->options,
+ &(sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_PREFIX_INFORMATION,
+ .prefix.prefixlen = prefixlen,
+ .prefix.address = *prefix,
+ });
}
-int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
- sd_radv_route_prefix *found = NULL;
-
+int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
assert_return(ra, -EINVAL);
- assert_return(p, -EINVAL);
-
- LIST_FOREACH(prefix, cur, ra->route_prefixes)
- if (cur->opt.prefixlen == p->opt.prefixlen &&
- in6_addr_equal(&cur->opt.in6_addr, &p->opt.in6_addr)) {
- found = cur;
- break;
- }
-
- if (found) {
- /* p and cur may be equivalent. First increment the reference counter. */
- sd_radv_route_prefix_ref(p);
-
- /* Then, remove the old entry. */
- LIST_REMOVE(prefix, ra->route_prefixes, found);
- sd_radv_route_prefix_unref(found);
-
- /* Finally, add the new entry. */
- LIST_APPEND(prefix, ra->route_prefixes, p);
-
- log_radv(ra, "Updated/replaced IPv6 route prefix %s (lifetime: %s)",
- IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen),
- FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
- } else {
- /* The route prefix is new. Let's simply add it. */
-
- sd_radv_route_prefix_ref(p);
- LIST_APPEND(prefix, ra->route_prefixes, p);
- ra->n_route_prefixes++;
-
- log_radv(ra, "Added route prefix %s",
- IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen));
- }
+ assert_return(mtu >= IPV6_MIN_MTU, -EINVAL);
- return 0;
+ return ndisc_option_set_mtu(&ra->options, mtu);
}
-int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
- sd_radv_pref64_prefix *found = NULL;
-
- assert_return(ra, -EINVAL);
- assert_return(p, -EINVAL);
-
- LIST_FOREACH(prefix, cur, ra->pref64_prefixes)
- if (cur->prefixlen == p->prefixlen &&
- in6_addr_equal(&cur->in6_addr, &p->in6_addr)) {
- found = cur;
- break;
- }
-
- if (found) {
- /* p and cur may be equivalent. First increment the reference counter. */
- sd_radv_pref64_prefix_ref(p);
-
- /* Then, remove the old entry. */
- LIST_REMOVE(prefix, ra->pref64_prefixes, found);
- sd_radv_pref64_prefix_unref(found);
-
- /* Finally, add the new entry. */
- LIST_APPEND(prefix, ra->pref64_prefixes, p);
-
- log_radv(ra, "Updated/replaced IPv6 PREF64 prefix %s (lifetime: %s)",
- IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen),
- FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
- } else {
- /* The route prefix is new. Let's simply add it. */
-
- sd_radv_pref64_prefix_ref(p);
- LIST_APPEND(prefix, ra->pref64_prefixes, p);
- ra->n_pref64_prefixes++;
-
- log_radv(ra, "Added PREF64 prefix %s",
- IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen));
- }
+void sd_radv_unset_mtu(sd_radv *ra) {
+ if (!ra)
+ return;
- return 0;
+ ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_MTU);
}
-int sd_radv_set_rdnss(
- sd_radv *ra,
- uint64_t lifetime_usec,
- const struct in6_addr *dns,
- size_t n_dns) {
-
- _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL;
- size_t len;
-
+int sd_radv_set_home_agent(sd_radv *ra, uint16_t preference, uint64_t lifetime_usec, uint64_t valid_until) {
assert_return(ra, -EINVAL);
- assert_return(n_dns < 128, -EINVAL);
-
- if (lifetime_usec > RADV_RDNSS_MAX_LIFETIME_USEC)
- return -EINVAL;
-
- if (!dns || n_dns == 0) {
- ra->rdnss = mfree(ra->rdnss);
- ra->n_rdnss = 0;
-
- return 0;
- }
-
- len = sizeof(struct sd_radv_opt_dns) + sizeof(struct in6_addr) * n_dns;
-
- opt_rdnss = malloc0(len);
- if (!opt_rdnss)
- return -ENOMEM;
-
- opt_rdnss->type = RADV_OPT_RDNSS;
- opt_rdnss->length = len / 8;
- opt_rdnss->lifetime = usec_to_be32_sec(lifetime_usec);
- memcpy(opt_rdnss + 1, dns, n_dns * sizeof(struct in6_addr));
-
- free_and_replace(ra->rdnss, opt_rdnss);
+ ra->flags |= ND_RA_FLAG_HOME_AGENT;
+ return ndisc_option_set_home_agent(&ra->options, preference, lifetime_usec, valid_until);
+}
- ra->n_rdnss = n_dns;
+void sd_radv_unset_home_agent(sd_radv *ra) {
+ if (!ra)
+ return;
- return 0;
+ ra->flags &= ~ND_RA_FLAG_HOME_AGENT;
+ ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_HOME_AGENT);
}
-int sd_radv_set_dnssl(
+int sd_radv_add_route(
sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint8_t preference,
uint64_t lifetime_usec,
- char **search_list) {
-
- _cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL;
- size_t len = 0;
- uint8_t *p;
+ uint64_t valid_until) {
assert_return(ra, -EINVAL);
+ assert_return(prefix, -EINVAL);
- if (lifetime_usec > RADV_DNSSL_MAX_LIFETIME_USEC)
- return -EINVAL;
-
- if (strv_isempty(search_list)) {
- ra->dnssl = mfree(ra->dnssl);
- return 0;
- }
-
- STRV_FOREACH(s, search_list)
- len += strlen(*s) + 2;
-
- len = (sizeof(struct sd_radv_opt_dns) + len + 7) & ~0x7;
-
- opt_dnssl = malloc0(len);
- if (!opt_dnssl)
- return -ENOMEM;
-
- opt_dnssl->type = RADV_OPT_DNSSL;
- opt_dnssl->length = len / 8;
- opt_dnssl->lifetime = usec_to_be32_sec(lifetime_usec);
-
- p = (uint8_t *)(opt_dnssl + 1);
- len -= sizeof(struct sd_radv_opt_dns);
-
- STRV_FOREACH(s, search_list) {
- int r;
-
- r = dns_name_to_wire_format(*s, p, len, false);
- if (r < 0)
- return r;
-
- if (len < (size_t)r)
- return -ENOBUFS;
-
- p += r;
- len -= r;
- }
-
- free_and_replace(ra->dnssl, opt_dnssl);
-
- return 0;
-}
-
-int sd_radv_prefix_new(sd_radv_prefix **ret) {
- sd_radv_prefix *p;
-
- assert_return(ret, -EINVAL);
-
- p = new(sd_radv_prefix, 1);
- if (!p)
- return -ENOMEM;
-
- *p = (sd_radv_prefix) {
- .n_ref = 1,
-
- .opt.type = ND_OPT_PREFIX_INFORMATION,
- .opt.length = (sizeof(p->opt) - 1)/8 + 1,
- .opt.prefixlen = 64,
-
- /* RFC 4861, Section 6.2.1 */
- .opt.flags = ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO,
-
- .lifetime_valid_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
- .lifetime_preferred_usec = RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
- .valid_until = USEC_INFINITY,
- .preferred_until = USEC_INFINITY,
- };
-
- *ret = p;
- return 0;
-}
-
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_prefix, sd_radv_prefix, mfree);
-
-int sd_radv_prefix_set_prefix(
- sd_radv_prefix *p,
- const struct in6_addr *in6_addr,
- unsigned char prefixlen) {
-
- assert_return(p, -EINVAL);
- assert_return(in6_addr, -EINVAL);
-
- if (prefixlen < 3 || prefixlen > 128)
- return -EINVAL;
-
- if (prefixlen > 64)
- /* unusual but allowed, log it */
- log_radv(NULL, "Unusual prefix length %d greater than 64", prefixlen);
-
- p->opt.in6_addr = *in6_addr;
- p->opt.prefixlen = prefixlen;
-
- return 0;
-}
-
-int sd_radv_prefix_get_prefix(
- sd_radv_prefix *p,
- struct in6_addr *ret_in6_addr,
- unsigned char *ret_prefixlen) {
-
- assert_return(p, -EINVAL);
- assert_return(ret_in6_addr, -EINVAL);
- assert_return(ret_prefixlen, -EINVAL);
-
- *ret_in6_addr = p->opt.in6_addr;
- *ret_prefixlen = p->opt.prefixlen;
-
- return 0;
-}
-
-int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
- assert_return(p, -EINVAL);
-
- SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, onlink);
-
- return 0;
+ return ndisc_option_set_route(
+ &ra->options,
+ preference,
+ prefixlen,
+ prefix,
+ lifetime_usec,
+ valid_until);
}
-int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, int address_autoconfiguration) {
- assert_return(p, -EINVAL);
+void sd_radv_remove_route(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen) {
- SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration);
+ if (!ra || !prefix)
+ return;
- return 0;
+ ndisc_option_remove(ra->options,
+ &(sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_ROUTE_INFORMATION,
+ .route.prefixlen = prefixlen,
+ .route.address = *prefix,
+ });
}
-int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
- assert_return(p, -EINVAL);
+int sd_radv_add_rdnss(
+ sd_radv *ra,
+ size_t n_dns,
+ const struct in6_addr *dns,
+ uint64_t lifetime_usec,
+ uint64_t valid_until) {
- p->lifetime_valid_usec = lifetime_usec;
- p->valid_until = valid_until;
+ assert_return(ra, -EINVAL);
+ assert_return(dns, -EINVAL);
- return 0;
+ return ndisc_option_set_rdnss(
+ &ra->options,
+ n_dns,
+ dns,
+ lifetime_usec,
+ valid_until);
}
-int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
- assert_return(p, -EINVAL);
-
- p->lifetime_preferred_usec = lifetime_usec;
- p->preferred_until = valid_until;
+void sd_radv_clear_rdnss(sd_radv *ra) {
+ if (!ra)
+ return;
- return 0;
+ sd_ndisc_option *opt;
+ SET_FOREACH(opt, ra->options)
+ if (opt->type == SD_NDISC_OPTION_RDNSS)
+ ndisc_option_remove(ra->options, opt);
}
-int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) {
- sd_radv_route_prefix *p;
-
- assert_return(ret, -EINVAL);
-
- p = new(sd_radv_route_prefix, 1);
- if (!p)
- return -ENOMEM;
-
- *p = (sd_radv_route_prefix) {
- .n_ref = 1,
-
- .opt.type = RADV_OPT_ROUTE_INFORMATION,
- .opt.length = DIV_ROUND_UP(sizeof(p->opt), 8),
- .opt.prefixlen = 64,
+int sd_radv_add_dnssl(
+ sd_radv *ra,
+ char * const *domains,
+ uint64_t lifetime_usec,
+ uint64_t valid_until) {
- .lifetime_usec = RADV_DEFAULT_VALID_LIFETIME_USEC,
- .valid_until = USEC_INFINITY,
- };
+ assert_return(ra, -EINVAL);
- *ret = p;
- return 0;
+ return ndisc_option_set_dnssl(
+ &ra->options,
+ domains,
+ lifetime_usec,
+ valid_until);
}
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix, sd_radv_route_prefix, mfree);
-
-int sd_radv_route_prefix_set_prefix(
- sd_radv_route_prefix *p,
- const struct in6_addr *in6_addr,
- unsigned char prefixlen) {
-
- assert_return(p, -EINVAL);
- assert_return(in6_addr, -EINVAL);
-
- if (prefixlen > 128)
- return -EINVAL;
-
- if (prefixlen > 64)
- /* unusual but allowed, log it */
- log_radv(NULL, "Unusual prefix length %u greater than 64", prefixlen);
-
- p->opt.in6_addr = *in6_addr;
- p->opt.prefixlen = prefixlen;
+void sd_radv_clear_dnssl(sd_radv *ra) {
+ if (!ra)
+ return;
- return 0;
+ sd_ndisc_option *opt;
+ SET_FOREACH(opt, ra->options)
+ if (opt->type == SD_NDISC_OPTION_DNSSL)
+ ndisc_option_remove(ra->options, opt);
}
-int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint64_t lifetime_usec, uint64_t valid_until) {
- assert_return(p, -EINVAL);
-
- p->lifetime_usec = lifetime_usec;
- p->valid_until = valid_until;
+int sd_radv_set_captive_portal(sd_radv *ra, const char *portal) {
+ assert_return(ra, -EINVAL);
+ assert_return(portal, -EINVAL);
- return 0;
+ return ndisc_option_set_captive_portal(&ra->options, portal);
}
-int sd_radv_pref64_prefix_new(sd_radv_pref64_prefix **ret) {
- sd_radv_pref64_prefix *p;
-
- assert_return(ret, -EINVAL);
-
- p = new(sd_radv_pref64_prefix, 1);
- if (!p)
- return -ENOMEM;
-
- *p = (sd_radv_pref64_prefix) {
- .n_ref = 1,
-
- .opt.type = RADV_OPT_PREF64,
- .opt.length = 2,
- };
+void sd_radv_unset_captive_portal(sd_radv *ra) {
+ if (!ra)
+ return;
- *ret = p;
- return 0;
+ ndisc_option_remove_by_type(ra->options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
}
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_pref64_prefix, sd_radv_pref64_prefix, mfree);
-
-int sd_radv_pref64_prefix_set_prefix(
- sd_radv_pref64_prefix *p,
+int sd_radv_add_prefix64(
+ sd_radv *ra,
const struct in6_addr *prefix,
uint8_t prefixlen,
- uint64_t lifetime_usec) {
-
- uint16_t pref64_lifetime;
- uint8_t prefixlen_code;
- int r;
+ uint64_t lifetime_usec,
+ uint64_t valid_until) {
- assert_return(p, -EINVAL);
+ assert_return(ra, -EINVAL);
assert_return(prefix, -EINVAL);
- r = pref64_prefix_length_to_plc(prefixlen, &prefixlen_code);
- if (r < 0)
- return log_radv_errno(NULL, r,
- "Unsupported PREF64 prefix length %u. Valid lengths are 32, 40, 48, 56, 64 and 96", prefixlen);
-
- if (lifetime_usec > PREF64_MAX_LIFETIME_USEC)
- return -EINVAL;
-
- /* RFC 8781 - 4.1 rounding up lifetime to multiply of 8 */
- pref64_lifetime = DIV_ROUND_UP(lifetime_usec, 8 * USEC_PER_SEC) << 3;
- pref64_lifetime |= prefixlen_code;
+ return ndisc_option_set_prefix64(
+ &ra->options,
+ prefixlen,
+ prefix,
+ lifetime_usec,
+ valid_until);
+}
- unaligned_write_be16(&p->opt.lifetime_and_plc, pref64_lifetime);
- memcpy(&p->opt.prefix, prefix, sizeof(p->opt.prefix));
+void sd_radv_remove_prefix64(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen) {
- p->in6_addr = *prefix;
- p->prefixlen = prefixlen;
+ if (!ra || !prefix)
+ return;
- return 0;
+ ndisc_option_remove(ra->options,
+ &(sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_PREF64,
+ .prefix64.prefixlen = prefixlen,
+ .prefix64.prefix = *prefix,
+ });
}
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 7afd464dc0..29cbdc95b4 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -493,6 +493,11 @@ static const uint8_t msg_information_request[] = {
DHCP6_MESSAGE_INFORMATION_REQUEST,
/* Transaction ID */
0x0f, 0xb4, 0xe5,
+ /* IA_PD */
+ 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x0c,
+ IA_ID_BYTES,
+ 0x00, 0x00, 0x00, 0x00, /* lifetime T1 */
+ 0x00, 0x00, 0x00, 0x00, /* lifetime T2 */
/* MUD URL */
/* ORO */
0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x0c,
diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c
index b2ea8f57b3..2f736aa4ea 100644
--- a/src/libsystemd-network/test-ndisc-ra.c
+++ b/src/libsystemd-network/test-ndisc-ra.c
@@ -12,6 +12,7 @@
#include "alloc-util.h"
#include "hexdecoct.h"
#include "icmp6-test-util.h"
+#include "radv-internal.h"
#include "socket-util.h"
#include "strv.h"
#include "tests.h"
@@ -67,92 +68,6 @@ static const struct in6_addr test_rdnss = { { { 0x20, 0x01, 0x0d, 0xb8,
static const char *test_dnssl[] = { "lab.intra",
NULL };
-TEST(radv_prefix) {
- sd_radv_prefix *p;
-
- assert_se(sd_radv_prefix_new(&p) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_onlink(NULL, true) < 0);
- assert_se(sd_radv_prefix_set_onlink(p, true) >= 0);
- assert_se(sd_radv_prefix_set_onlink(p, false) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_address_autoconfiguration(NULL, true) < 0);
- assert_se(sd_radv_prefix_set_address_autoconfiguration(p, true) >= 0);
- assert_se(sd_radv_prefix_set_address_autoconfiguration(p, false) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_valid_lifetime(NULL, 1, 1) < 0);
- assert_se(sd_radv_prefix_set_valid_lifetime(p, 0, 0) >= 0);
- assert_se(sd_radv_prefix_set_valid_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
- assert_se(sd_radv_prefix_set_valid_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_preferred_lifetime(NULL, 1, 1) < 0);
- assert_se(sd_radv_prefix_set_preferred_lifetime(p, 0, 0) >= 0);
- assert_se(sd_radv_prefix_set_preferred_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
- assert_se(sd_radv_prefix_set_preferred_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(NULL, NULL, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(p, NULL, 0) < 0);
-
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 64) >= 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 0) < 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 1) < 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 2) < 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 3) >= 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 125) >= 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 128) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(p, &prefix[0].address, 129) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(p, &prefix[0].address, 255) < 0);
-
- assert_se(!sd_radv_prefix_unref(p));
-}
-
-TEST(radv_route_prefix) {
- sd_radv_route_prefix *p;
-
- assert_se(sd_radv_route_prefix_new(&p) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_lifetime(NULL, 1, 1) < 0);
- assert_se(sd_radv_route_prefix_set_lifetime(p, 0, 0) >= 0);
- assert_se(sd_radv_route_prefix_set_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
- assert_se(sd_radv_route_prefix_set_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(NULL, NULL, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(p, NULL, 0) < 0);
-
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 64) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 0) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 1) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 2) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 3) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 125) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 128) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 129) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 255) < 0);
-
- assert_se(!sd_radv_route_prefix_unref(p));
-}
-
-TEST(radv_pref64_prefix) {
- sd_radv_pref64_prefix *p;
-
- assert_se(sd_radv_pref64_prefix_new(&p) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_pref64_prefix_set_prefix(NULL, NULL, 0, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_pref64_prefix_set_prefix(p, NULL, 0, 0) < 0);
-
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 32, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 40, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 48, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 56, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 64, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 96, 300 * USEC_PER_SEC) >= 0);
-
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 80, 300 * USEC_PER_SEC) < 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 80, USEC_PER_DAY) < 0);
-
- assert_se(!sd_radv_pref64_prefix_unref(p));
-}
-
TEST(radv) {
sd_radv *ra;
@@ -165,16 +80,7 @@ TEST(radv) {
ASSERT_RETURN_EXPECTED_SE(sd_radv_set_ifindex(ra, -2) < 0);
assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(NULL, NULL) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(ra, NULL) >= 0);
- assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(NULL, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 0) < 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 1279) < 0);
- assert_se(sd_radv_set_mtu(ra, 1280) >= 0);
- assert_se(sd_radv_set_mtu(ra, ~0) >= 0);
-
+ /* header */
ASSERT_RETURN_EXPECTED_SE(sd_radv_set_hop_limit(NULL, 0) < 0);
assert_se(sd_radv_set_hop_limit(ra, 0) >= 0);
assert_se(sd_radv_set_hop_limit(ra, ~0) >= 0);
@@ -209,31 +115,37 @@ TEST(radv) {
assert_se(sd_radv_set_retransmit(ra, 0) >= 0);
assert_se(sd_radv_set_retransmit(ra, USEC_INFINITY) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_rdnss(NULL, 0, NULL, 0) < 0);
- assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_rdnss(ra, 0, NULL, 128) < 0);
- assert_se(sd_radv_set_rdnss(ra, 600 * USEC_PER_SEC, &test_rdnss, 0) >= 0);
- assert_se(sd_radv_set_rdnss(ra, 600 * USEC_PER_SEC, &test_rdnss, 1) >= 0);
- assert_se(sd_radv_set_rdnss(ra, 0, &test_rdnss, 1) >= 0);
- assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0);
-
- assert_se(sd_radv_set_dnssl(ra, 0, NULL) >= 0);
- assert_se(sd_radv_set_dnssl(ra, 600 * USEC_PER_SEC, NULL) >= 0);
- assert_se(sd_radv_set_dnssl(ra, 0, (char **)test_dnssl) >= 0);
- assert_se(sd_radv_set_dnssl(ra, 600 * USEC_PER_SEC, (char **)test_dnssl) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent_information(NULL, true) < 0);
- assert_se(sd_radv_set_home_agent_information(ra, true) >= 0);
- assert_se(sd_radv_set_home_agent_information(ra, false) >= 0);
-
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent_preference(NULL, 10) < 0);
- assert_se(sd_radv_set_home_agent_preference(ra, 10) >= 0);
- assert_se(sd_radv_set_home_agent_preference(ra, 0) >= 0);
+ /* options */
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(NULL, NULL) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(ra, NULL) >= 0);
+ assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
+ sd_radv_unset_mac(ra);
- ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent_lifetime(NULL, 300 * USEC_PER_SEC) < 0);
- assert_se(sd_radv_set_home_agent_lifetime(ra, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_set_home_agent_lifetime(ra, 0) >= 0);
- assert_se(sd_radv_set_home_agent_lifetime(ra, USEC_PER_DAY) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 1279) < 0);
+ assert_se(sd_radv_set_mtu(ra, 1280) >= 0);
+ assert_se(sd_radv_set_mtu(ra, 9999) >= 0);
+ sd_radv_unset_mtu(ra);
+
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_add_rdnss(NULL, 0, NULL, 0, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_add_rdnss(ra, 0, NULL, 0, 0) < 0);
+ assert_se(sd_radv_add_rdnss(ra, 0, &test_rdnss, 600 * USEC_PER_SEC, USEC_INFINITY) < 0);
+ assert_se(sd_radv_add_rdnss(ra, 1, &test_rdnss, 600 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+ assert_se(sd_radv_add_rdnss(ra, 1, &test_rdnss, 0, 0) >= 0);
+ sd_radv_clear_rdnss(ra);
+
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_add_dnssl(NULL, NULL, 0, 0) < 0);
+ assert_se(sd_radv_add_dnssl(ra, NULL, 0, 0) < 0);
+ assert_se(sd_radv_add_dnssl(ra, NULL, 600 * USEC_PER_SEC, USEC_INFINITY) < 0);
+ assert_se(sd_radv_add_dnssl(ra, (char**) test_dnssl, 600 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+ assert_se(sd_radv_add_dnssl(ra, (char**) test_dnssl, 0, 0) >= 0);
+ sd_radv_clear_dnssl(ra);
+
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent(NULL, 0, 0, 0) < 0);
+ assert_se(sd_radv_set_home_agent(ra, 0, 0, 0) >= 0);
+ assert_se(sd_radv_set_home_agent(ra, 10, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+ sd_radv_unset_home_agent(ra);
ra = sd_radv_unref(ra);
assert_se(!ra);
@@ -266,9 +178,9 @@ static void verify_message(const uint8_t *buf, size_t len) {
/* Source Link Layer Address Option */
0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
/* Prefix Information Option */
- 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
- 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x03, 0x04, 0x30, 0xc0, 0x00, 0x00, 0x0e, 0x10,
+ 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Prefix Information Option */
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x0e, 0x10,
@@ -276,9 +188,9 @@ static void verify_message(const uint8_t *buf, size_t len) {
0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Prefix Information Option */
- 0x03, 0x04, 0x30, 0xc0, 0x00, 0x00, 0x0e, 0x10,
- 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad,
+ 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
+ 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Recursive DNS Server Option */
0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
@@ -347,33 +259,35 @@ TEST(ra) {
assert_se(sd_radv_attach_event(ra, e, 0) >= 0);
assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
- assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
assert_se(sd_radv_set_router_lifetime(ra, 180 * USEC_PER_SEC) >= 0);
assert_se(sd_radv_set_hop_limit(ra, 64) >= 0);
assert_se(sd_radv_set_managed_information(ra, true) >= 0);
assert_se(sd_radv_set_other_information(ra, true) >= 0);
- assert_se(sd_radv_set_rdnss(ra, 60 * USEC_PER_SEC, &test_rdnss, 1) >= 0);
- assert_se(sd_radv_set_dnssl(ra, 60 * USEC_PER_SEC, (char **)test_dnssl) >= 0);
-
- for (unsigned i = 0; i < ELEMENTSOF(prefix); i++) {
- sd_radv_prefix *p;
-
- printf("Test prefix %u\n", i);
- assert_se(sd_radv_prefix_new(&p) >= 0);
-
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[i].address,
- prefix[i].prefixlen) >= 0);
- if (prefix[i].valid > 0)
- assert_se(sd_radv_prefix_set_valid_lifetime(p, prefix[i].valid * USEC_PER_SEC, USEC_INFINITY) >= 0);
- if (prefix[i].preferred > 0)
- assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred * USEC_PER_SEC, USEC_INFINITY) >= 0);
-
- assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].successful);
+ assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
+ assert_se(sd_radv_add_rdnss(ra, 1, &test_rdnss, 60 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+ assert_se(sd_radv_add_dnssl(ra, (char**) test_dnssl, 60 * USEC_PER_SEC, USEC_INFINITY) >= 0);
+
+ FOREACH_ARRAY(p, prefix, ELEMENTSOF(prefix)) {
+ printf("Test prefix %s\n", IN6_ADDR_PREFIX_TO_STRING(&p->address, p->prefixlen));
+ assert_se((sd_radv_add_prefix(
+ ra,
+ &p->address,
+ p->prefixlen,
+ ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO,
+ p->valid > 0 ? p->valid * USEC_PER_SEC : RADV_DEFAULT_VALID_LIFETIME_USEC,
+ p->preferred > 0 ? p->preferred * USEC_PER_SEC : RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
+ USEC_INFINITY,
+ USEC_INFINITY) >= 0) == p->successful);
/* If the previous sd_radv_add_prefix() succeeds, then also the second call should also succeed. */
- assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].successful);
-
- p = sd_radv_prefix_unref(p);
- assert_se(!p);
+ assert_se((sd_radv_add_prefix(
+ ra,
+ &p->address,
+ p->prefixlen,
+ ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO,
+ p->valid > 0 ? p->valid * USEC_PER_SEC : RADV_DEFAULT_VALID_LIFETIME_USEC,
+ p->preferred > 0 ? p->preferred * USEC_PER_SEC : RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
+ USEC_INFINITY,
+ USEC_INFINITY) >= 0) == p->successful);
}
assert_se(sd_event_add_io(e, &recv_router_advertisement, test_fd[0], EPOLLIN, radv_recv, ra) >= 0);
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index d642ef2581..e1836da08f 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -864,7 +864,6 @@ global:
sd_json_dispatch_strv;
sd_json_dispatch_tristate;
sd_json_dispatch_uid_gid;
- sd_json_dispatch_pid;
sd_json_dispatch_uint16;
sd_json_dispatch_uint32;
sd_json_dispatch_uint64;
@@ -976,6 +975,7 @@ global:
sd_varlink_error_errno;
sd_varlink_error_invalid_parameter;
sd_varlink_error_invalid_parameter_name;
+ sd_varlink_error_is_invalid_parameter;
sd_varlink_error_to_errno;
sd_varlink_errorb;
sd_varlink_flush;
diff --git a/src/libsystemd/sd-json/json-util.c b/src/libsystemd/sd-json/json-util.c
index 2ff4587565..a1b2ebc0fe 100644
--- a/src/libsystemd/sd-json/json-util.c
+++ b/src/libsystemd/sd-json/json-util.c
@@ -1,10 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
+#include "devnum-util.h"
+#include "fd-util.h"
#include "glyph-util.h"
#include "in-addr-util.h"
#include "iovec-util.h"
#include "json-util.h"
+#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
@@ -148,7 +151,7 @@ int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispa
}
int json_variant_new_pidref(sd_json_variant **ret, PidRef *pidref) {
- sd_id128_t boot_id;
+ sd_id128_t boot_id = SD_ID128_NULL;
int r;
/* Turns a PidRef into a triplet of PID, pidfd inode nr, and the boot ID. The triplet should uniquely
@@ -157,22 +160,24 @@ int json_variant_new_pidref(sd_json_variant **ret, PidRef *pidref) {
if (!pidref_is_set(pidref))
return sd_json_variant_new_null(ret);
- r = pidref_acquire_pidfd_id(pidref);
- if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r) && r != -ENOMEDIUM)
- return r;
+ if (!pidref_is_remote(pidref)) {
+ r = pidref_acquire_pidfd_id(pidref);
+ if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r) && r != -ENOMEDIUM)
+ return r;
- if (pidref->fd_id > 0) {
/* If we have the pidfd inode number, also acquire the boot ID, to make things universally unique */
- r = sd_id128_get_boot(&boot_id);
- if (r < 0)
- return r;
+ if (pidref->fd_id > 0) {
+ r = sd_id128_get_boot(&boot_id);
+ if (r < 0)
+ return r;
+ }
}
return sd_json_buildo(
ret,
SD_JSON_BUILD_PAIR_INTEGER("pid", pidref->pid),
SD_JSON_BUILD_PAIR_CONDITION(pidref->fd_id > 0, "pidfdId", SD_JSON_BUILD_INTEGER(pidref->fd_id)),
- SD_JSON_BUILD_PAIR_CONDITION(pidref->fd_id > 0, "bootId", SD_JSON_BUILD_ID128(boot_id)));
+ SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(boot_id), "bootId", SD_JSON_BUILD_ID128(boot_id)));
}
int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
@@ -185,7 +190,13 @@ int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dis
* above. If SD_JSON_STRICT is set this will acquire a pidfd for the process, and validate that the
* auxiliary fields match it. Otherwise, this will just store the pid and the pidfd inode number (the
* latter not if the provided boot id differs from the local one), and not attempt to get a pidfd for
- * it, or authenticate it. */
+ * it, or authenticate it.
+ *
+ * If SD_JSON_RELAX is specified, a specified but zero/empty PID will be mapped to PIDREF_AUTOMATIC,
+ * which is supposed to indicate that the PID shall be automatically derived, typically from the
+ * connection peer.
+ *
+ * Note that SD_JSON_RELAX and SD_JSON_STRICT can be combined. */
if (sd_json_variant_is_null(variant)) {
pidref_done(p);
@@ -220,6 +231,13 @@ int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dis
} else
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is neither a numeric PID nor a PID object.", strna(name));
+ /* If SD_JSON_RELAX is set then we'll take a specified but zero field as request for "automatic" PID derivation */
+ if ((flags & SD_JSON_RELAX) && data.pid == 0 && data.fd_id == 0 && sd_id128_is_null(data.boot_id)) {
+ pidref_done(p);
+ *p = PIDREF_AUTOMATIC;
+ return 0;
+ }
+
/* Before casting the 64bit data.pid field to pid_t, let's ensure it fits the pid_t range. */
if (data.pid > PID_T_MAX || !pid_is_valid(data.pid))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' does not contain a valid PID.", strna(name));
@@ -236,7 +254,7 @@ int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dis
} else {
local_boot_id = sd_id128_equal(data.boot_id, my_boot_id);
if (!local_boot_id) {
- json_log(variant, flags | (FLAGS_SET(flags, SD_JSON_STRICT) ? 0 : SD_JSON_DEBUG), 0, "JSON field '%s' refers to non-local PID.", strna(name));
+ json_log(variant, flags | (FLAGS_SET(flags, SD_JSON_STRICT) ? 0 : SD_JSON_DEBUG), 0, "JSON field '%s' refers to non-local PID%s.", strna(name), FLAGS_SET(flags, SD_JSON_STRICT) ? "" : ", proceeding");
if (FLAGS_SET(flags, SD_JSON_STRICT))
return -ESRCH;
}
@@ -244,35 +262,41 @@ int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dis
}
_cleanup_(pidref_done) PidRef np = PIDREF_NULL;
- if (local_boot_id != 0) {
- /* Try to acquire a pidfd – unless this is definitely not a local PID */
+ if (local_boot_id == 0)
+ /* If this is definitely not the local boot ID, then mark the PidRef as remote in the sense of pidref_is_remote() */
+ np = (PidRef) {
+ .pid = data.pid,
+ .fd = -EREMOTE,
+ .fd_id = data.fd_id,
+ };
+ else {
+ /* Try to acquire a pidfd if this is or might be a local PID */
r = pidref_set_pid(&np, data.pid);
if (r < 0) {
json_log(variant, flags | (FLAGS_SET(flags, SD_JSON_STRICT) ? 0 : SD_JSON_DEBUG), r, "Unable to get fd for PID in JSON field '%s': %m", strna(name));
if (FLAGS_SET(flags, SD_JSON_STRICT))
return r;
- }
- }
- /* If the the PID is dead or we otherwise can't get a pidfd of it, then store at least the PID number */
- if (!pidref_is_set(&np))
- np = PIDREF_MAKE_FROM_PID(data.pid);
-
- /* If the pidfd inode nr is specified, validate it or at least state */
- if (data.fd_id > 0) {
- if (np.fd >= 0) {
- r = pidref_acquire_pidfd_id(&np);
- if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
- return json_log(variant, flags, r, "Unable to get pidfd ID to validate JSON field '%s': %m", strna(name));
+ /* If the PID is dead or we otherwise can't get a pidfd of it, then store at least the PID number */
+ np = PIDREF_MAKE_FROM_PID(data.pid);
+ }
- if (data.fd_id != np.fd_id) {
- json_log(variant, flags | (FLAGS_SET(flags, SD_JSON_STRICT) ? 0 : SD_JSON_DEBUG), 0, "JSON field '%s' references PID with non-matching inode number.", strna(name));
- if (FLAGS_SET(flags, SD_JSON_STRICT))
- return -ESRCH;
+ /* If the pidfd inode nr is specified, validate it or at least state */
+ if (data.fd_id > 0) {
+ if (np.fd >= 0) {
+ r = pidref_acquire_pidfd_id(&np);
+ if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
+ return json_log(variant, flags, r, "Unable to get pidfd ID to validate JSON field '%s': %m", strna(name));
+
+ if (data.fd_id != np.fd_id) {
+ json_log(variant, flags | (FLAGS_SET(flags, SD_JSON_STRICT) ? 0 : SD_JSON_DEBUG), 0, "JSON field '%s' references PID with non-matching inode number.", strna(name));
+ if (FLAGS_SET(flags, SD_JSON_STRICT))
+ return -ESRCH;
+ }
+ } else {
+ json_log(variant, flags|SD_JSON_DEBUG, 0, "Not validating PID inode number on JSON field '%s', because operating without pidfd.", strna(name));
+ np.fd_id = data.fd_id;
}
- } else if (local_boot_id != 0) {
- json_log(variant, flags|SD_JSON_DEBUG, 0, "Not validating PID inode number on JSON field '%s', because operating without pidfd.", strna(name));
- np.fd_id = data.fd_id;
}
}
@@ -281,3 +305,124 @@ int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dis
return 0;
}
+
+int json_variant_new_devnum(sd_json_variant **ret, dev_t devnum) {
+ if (devnum == 0)
+ return sd_json_variant_new_null(ret);
+
+ return sd_json_buildo(
+ ret,
+ SD_JSON_BUILD_PAIR_UNSIGNED("major", major(devnum)),
+ SD_JSON_BUILD_PAIR_UNSIGNED("minor", minor(devnum)));
+}
+
+int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
+ dev_t *ret = ASSERT_PTR(userdata);
+ int r;
+
+ assert(variant);
+
+ if (sd_json_variant_is_null(variant)) {
+ *ret = 0;
+ return 0;
+ }
+
+ struct {
+ uint32_t major;
+ uint32_t minor;
+ } data;
+
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "major", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, voffsetof(data, major), SD_JSON_MANDATORY },
+ { "minor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, voffsetof(data, minor), SD_JSON_MANDATORY },
+ {}
+ };
+
+ r = sd_json_dispatch(variant, dispatch_table, flags, &data);
+ if (r < 0)
+ return r;
+
+ if (!DEVICE_MAJOR_VALID(data.major) || !DEVICE_MINOR_VALID(data.minor))
+ return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid device number.", strna(name));
+
+ *ret = makedev(data.major, data.minor);
+ return 0;
+}
+
+static int json_variant_new_stat(sd_json_variant **ret, const struct stat *st) {
+ char mode[STRLEN("0755")+1];
+
+ assert(st);
+
+ if (!stat_is_set(st))
+ return sd_json_variant_new_null(ret);
+
+ xsprintf(mode, "%04o", st->st_mode & ~S_IFMT);
+
+ return sd_json_buildo(
+ ret,
+ JSON_BUILD_PAIR_DEVNUM("dev", st->st_dev),
+ SD_JSON_BUILD_PAIR_UNSIGNED("inode", st->st_ino),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("type", inode_type_to_string(st->st_mode)),
+ SD_JSON_BUILD_PAIR_STRING("mode", mode),
+ SD_JSON_BUILD_PAIR_UNSIGNED("linkCount", st->st_nlink),
+ SD_JSON_BUILD_PAIR_UNSIGNED("uid", st->st_uid),
+ SD_JSON_BUILD_PAIR_UNSIGNED("gid", st->st_gid),
+ SD_JSON_BUILD_PAIR_CONDITION(
+ S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode),
+ "rdev",
+ JSON_BUILD_DEVNUM(st->st_rdev)),
+ SD_JSON_BUILD_PAIR_UNSIGNED("size", st->st_size),
+ SD_JSON_BUILD_PAIR_UNSIGNED("blockSize", st->st_blksize),
+ SD_JSON_BUILD_PAIR_UNSIGNED("blocks", st->st_blocks));
+}
+
+static int json_variant_new_file_handle(sd_json_variant **ret, const struct file_handle *fid) {
+ assert(ret);
+
+ if (!fid)
+ return sd_json_variant_new_null(ret);
+
+ return sd_json_buildo(
+ ret,
+ SD_JSON_BUILD_PAIR_INTEGER("type", fid->handle_type),
+ SD_JSON_BUILD_PAIR_BASE64("handle", fid->f_handle, fid->handle_bytes));
+}
+
+int json_variant_new_fd_info(sd_json_variant **ret, int fd) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL;
+ _cleanup_free_ char *path = NULL;
+ _cleanup_free_ struct file_handle *fid = NULL;
+ struct stat st;
+ int mntid = -1, r;
+
+ assert(fd >= 0 || fd == AT_FDCWD);
+
+ r = fd_get_path(fd, &path);
+ if (r < 0)
+ return r;
+
+ /* If AT_FDCWD is specified, show information about the current working directory. */
+ if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
+ return -errno;
+
+ r = json_variant_new_stat(&v, &st);
+ if (r < 0)
+ return r;
+
+ r = name_to_handle_at_try_fid(fd, "", &fid, &mntid, AT_EMPTY_PATH);
+ if (r < 0 && is_name_to_handle_at_fatal_error(r))
+ return r;
+
+ r = json_variant_new_file_handle(&w, fid);
+ if (r < 0)
+ return r;
+
+ return sd_json_buildo(
+ ret,
+ JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE("fd", fd),
+ SD_JSON_BUILD_PAIR_STRING("path", path),
+ SD_JSON_BUILD_PAIR_VARIANT("stat", v),
+ JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE("mountId", mntid),
+ SD_JSON_BUILD_PAIR_VARIANT("fileHandle", w));
+}
diff --git a/src/libsystemd/sd-json/json-util.h b/src/libsystemd/sd-json/json-util.h
index 6b72d0d93d..2aeb076823 100644
--- a/src/libsystemd/sd-json/json-util.h
+++ b/src/libsystemd/sd-json/json-util.h
@@ -114,6 +114,7 @@ int json_dispatch_const_user_group_name(const char *name, sd_json_variant *varia
int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
+int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
static inline int json_variant_unbase64_iovec(sd_json_variant *v, struct iovec *ret) {
return sd_json_variant_unbase64(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL);
@@ -147,6 +148,7 @@ enum {
_JSON_BUILD_RATELIMIT,
_JSON_BUILD_TRISTATE,
_JSON_BUILD_PIDREF,
+ _JSON_BUILD_DEVNUM,
_JSON_BUILD_PAIR_INTEGER_NON_ZERO,
_JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE,
@@ -172,6 +174,7 @@ enum {
_JSON_BUILD_PAIR_OCTESCAPE_NON_EMPTY,
_JSON_BUILD_PAIR_TRISTATE_NON_NULL,
_JSON_BUILD_PAIR_PIDREF_NON_NULL,
+ _JSON_BUILD_PAIR_DEVNUM,
_SD_JSON_BUILD_REALLYMAX,
};
@@ -192,6 +195,7 @@ enum {
#define JSON_BUILD_RATELIMIT(rl) _JSON_BUILD_RATELIMIT, (const RateLimit*) { rl }
#define JSON_BUILD_TRISTATE(i) _JSON_BUILD_TRISTATE, (int) { i }
#define JSON_BUILD_PIDREF(p) _JSON_BUILD_PIDREF, (const PidRef*) { p }
+#define JSON_BUILD_DEVNUM(d) _JSON_BUILD_DEVNUM, (dev_t) { d }
#define JSON_BUILD_PAIR_INTEGER_NON_ZERO(name, i) _JSON_BUILD_PAIR_INTEGER_NON_ZERO, (const char*) { name }, (int64_t) { i }
#define JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE(name, i) _JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE, (const char*) { name }, (int64_t) { i }
@@ -230,5 +234,8 @@ enum {
#define JSON_BUILD_PAIR_RATELIMIT(name, rl) SD_JSON_BUILD_PAIR(name, JSON_BUILD_RATELIMIT(rl))
#define JSON_BUILD_PAIR_TRISTATE(name, i) SD_JSON_BUILD_PAIR(name, JSON_BUILD_TRISTATE(i))
#define JSON_BUILD_PAIR_PIDREF(name, p) SD_JSON_BUILD_PAIR(name, JSON_BUILD_PIDREF(p))
+#define JSON_BUILD_PAIR_DEVNUM(name, d) SD_JSON_BUILD_PAIR(name, JSON_BUILD_DEVNUM(d))
int json_variant_new_pidref(sd_json_variant **ret, PidRef *pidref);
+int json_variant_new_devnum(sd_json_variant **ret, dev_t devnum);
+int json_variant_new_fd_info(sd_json_variant **ret, int fd);
diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c
index 5ff1b584a6..297052bdb4 100644
--- a/src/libsystemd/sd-json/sd-json.c
+++ b/src/libsystemd/sd-json/sd-json.c
@@ -4224,6 +4224,34 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
break;
}
+ case _JSON_BUILD_DEVNUM: {
+ dev_t devnum;
+
+ if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ devnum = va_arg(ap, dev_t);
+
+ if (current->n_suppress == 0) {
+ r = json_variant_new_devnum(&add, devnum);
+ if (r < 0)
+ goto finish;
+ }
+
+ n_subtract = 1;
+
+ if (current->expect == EXPECT_TOPLEVEL)
+ current->expect = EXPECT_END;
+ else if (current->expect == EXPECT_OBJECT_VALUE)
+ current->expect = EXPECT_OBJECT_KEY;
+ else
+ assert(current->expect == EXPECT_ARRAY_ELEMENT);
+
+ break;
+ }
+
case _JSON_BUILD_TRISTATE: {
int tristate;
@@ -5564,44 +5592,6 @@ _public_ int sd_json_dispatch_uid_gid(const char *name, sd_json_variant *variant
return 0;
}
-_public_ int sd_json_dispatch_pid(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
- /* pid_t is a signed type, but we don't consider negative values as valid.
- * There is a special treatment for 0 if SD_JSON_RELAX flag present. */
-
- pid_t *pid = userdata;
- uint32_t k;
- int r;
-
- assert_return(variant, -EINVAL);
- assert_cc(sizeof(pid_t) == sizeof(uint32_t));
-
- if (sd_json_variant_is_null(variant)) {
- *pid = PID_INVALID;
- return 0;
- }
-
- r = sd_json_dispatch_uint32(name, variant, flags, &k);
- if (r < 0)
- return r;
-
- if (k == 0) {
- if (!FLAGS_SET(flags, SD_JSON_RELAX))
- goto invalid_value;
-
- *pid = PID_AUTOMATIC;
- return 0;
- }
-
- if (!pid_is_valid(k))
- goto invalid_value;
-
- *pid = k;
- return 0;
-
-invalid_value:
- return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid PID.", strna(name));
-}
-
_public_ int sd_json_dispatch_id128(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
sd_id128_t *uuid = userdata;
int r;
diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h
index 14c6f6819d..80b4cc3cd6 100644
--- a/src/libsystemd/sd-network/network-util.h
+++ b/src/libsystemd/sd-network/network-util.h
@@ -2,6 +2,7 @@
#pragma once
#include <errno.h>
+#include <netinet/in.h>
#include <stdbool.h>
#include "constants.h"
@@ -22,6 +23,17 @@ typedef enum AddressFamily {
_ADDRESS_FAMILY_INVALID = -EINVAL,
} AddressFamily;
+static inline AddressFamily AF_TO_ADDRESS_FAMILY(int af) {
+ switch (af) {
+ case AF_INET:
+ return ADDRESS_FAMILY_IPV4;
+ case AF_INET6:
+ return ADDRESS_FAMILY_IPV6;
+ default:
+ return ADDRESS_FAMILY_NO;
+ }
+}
+
typedef enum LinkOperationalState {
LINK_OPERSTATE_MISSING,
LINK_OPERSTATE_OFF,
diff --git a/src/libsystemd/sd-varlink/sd-varlink.c b/src/libsystemd/sd-varlink/sd-varlink.c
index 70615bb3e3..fb42e3205d 100644
--- a/src/libsystemd/sd-varlink/sd-varlink.c
+++ b/src/libsystemd/sd-varlink/sd-varlink.c
@@ -4112,3 +4112,23 @@ _public_ int sd_varlink_error_to_errno(const char *error, sd_json_variant *param
return -EBADR; /* Catch-all */
}
+
+_public_ int sd_varlink_error_is_invalid_parameter(const char *error, sd_json_variant *parameter, const char *name) {
+
+ /* Returns true if the specified error result is an invalid parameter error for the parameter 'name' */
+
+ if (!streq_ptr(error, SD_VARLINK_ERROR_INVALID_PARAMETER))
+ return false;
+
+ if (!name)
+ return true;
+
+ if (!sd_json_variant_is_object(parameter))
+ return false;
+
+ sd_json_variant *e = sd_json_variant_by_key(parameter, "parameter");
+ if (!e || !sd_json_variant_is_string(e))
+ return false;
+
+ return streq(sd_json_variant_string(e), name);
+}
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 78c05ff502..da71b9115f 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -127,17 +127,9 @@ int bus_image_method_rename(
if (r == 0)
return 1; /* Will call us back */
- /* The image is cached with its name, hence it is necessary to remove from the cache before renaming. */
- assert_se(hashmap_remove_value(m->image_cache, image->name, image));
-
- r = image_rename(image, new_name);
- if (r < 0) {
- image_unref(image);
- return r;
- }
-
- /* Then save the object again in the cache. */
- assert_se(hashmap_put(m->image_cache, image->name, image) > 0);
+ r = rename_image_and_update_cache(m, image, new_name);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to rename image: %m");
return sd_bus_reply_method_return(message, NULL);
}
@@ -377,60 +369,6 @@ int bus_image_method_get_os_release(
return bus_reply_pair_array(message, image->os_release);
}
-static int image_flush_cache(sd_event_source *s, void *userdata) {
- Manager *m = ASSERT_PTR(userdata);
-
- assert(s);
-
- hashmap_clear(m->image_cache);
- return 0;
-}
-
-int manager_acquire_image(Manager *m, const char *name, Image **ret) {
- int r;
-
- assert(m);
- assert(name);
-
- Image *existing = hashmap_get(m->image_cache, name);
- if (existing) {
- if (ret)
- *ret = existing;
- return 0;
- }
-
- if (!m->image_cache_defer_event) {
- r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
- if (r < 0)
- return r;
- }
-
- r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
- if (r < 0)
- return r;
-
- _cleanup_(image_unrefp) Image *image = NULL;
- r = image_find(IMAGE_MACHINE, name, NULL, &image);
- if (r < 0)
- return r;
-
- image->userdata = m;
-
- r = hashmap_ensure_put(&m->image_cache, &image_hash_ops, image->name, image);
- if (r < 0)
- return r;
-
- if (ret)
- *ret = image;
-
- TAKE_PTR(image);
- return 0;
-}
-
static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
_cleanup_free_ char *e = NULL;
Manager *m = userdata;
diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h
index c0d56b2eb4..0a7975bc52 100644
--- a/src/machine/image-dbus.h
+++ b/src/machine/image-dbus.h
@@ -7,7 +7,6 @@
extern const BusObjectImplementation image_object;
-int manager_acquire_image(Manager *m, const char *name, Image **ret);
char* image_bus_path(const char *name);
int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/machine/image-varlink.c b/src/machine/image-varlink.c
new file mode 100644
index 0000000000..3177a93847
--- /dev/null
+++ b/src/machine/image-varlink.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-json.h"
+#include "sd-varlink.h"
+
+#include "bus-polkit.h"
+#include "image-varlink.h"
+#include "machine.h"
+#include "string-util.h"
+
+int vl_method_update_image(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ struct params {
+ const char *image_name;
+ const char *new_name;
+ int read_only;
+ uint64_t limit;
+ };
+
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct params, image_name), SD_JSON_MANDATORY },
+ { "newName", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct params, new_name), 0 },
+ { "readOnly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(struct params, read_only), 0 },
+ { "limit", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(struct params, limit), 0 },
+ VARLINK_DISPATCH_POLKIT_FIELD,
+ {}
+ };
+
+ Manager *manager = ASSERT_PTR(userdata);
+ struct params p = {
+ .read_only = -1,
+ .limit = UINT64_MAX,
+ };
+ Image *image;
+ int r, ret = 0;
+
+ assert(link);
+ assert(parameters);
+
+ r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (!image_name_is_valid(p.image_name))
+ return sd_varlink_error_invalid_parameter_name(link, "name");
+
+ if (p.new_name && !image_name_is_valid(p.new_name))
+ return sd_varlink_error_invalid_parameter_name(link, "newName");
+
+ r = manager_acquire_image(manager, p.image_name, &image);
+ if (r == -ENOENT)
+ return sd_varlink_error(link, "io.systemd.MachineImage.NoSuchImage", NULL);
+ if (r < 0)
+ return r;
+
+ r = varlink_verify_polkit_async(
+ link,
+ manager->bus,
+ "org.freedesktop.machine1.manage-images",
+ (const char**) STRV_MAKE("image", image->name,
+ "verb", "update_image"),
+ &manager->polkit_registry);
+ if (r <= 0)
+ return r;
+
+ if (p.new_name) {
+ r = rename_image_and_update_cache(manager, image, p.new_name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to rename image: %m");
+ }
+
+ if (p.read_only >= 0) {
+ r = image_read_only(image, p.read_only);
+ if (r < 0)
+ RET_GATHER(ret, log_debug_errno(r, "Failed to toggle image read only, ignoring: %m"));
+ }
+
+ if (p.limit != UINT64_MAX) {
+ r = image_set_limit(image, p.limit);
+ if (r < 0)
+ RET_GATHER(ret, log_debug_errno(r, "Failed to set image limit, ignoring: %m"));
+ }
+
+ /* We intentionally swallowed errors from image_read_only() and image_set_limit(). Here we return first one to the user if any */
+ if (ret < 0)
+ return ret;
+
+ return sd_varlink_reply(link, NULL);
+}
diff --git a/src/machine/image-varlink.h b/src/machine/image-varlink.h
new file mode 100644
index 0000000000..72028239de
--- /dev/null
+++ b/src/machine/image-varlink.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-varlink.h"
+
+int vl_method_update_image(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c
index 8f0e4f9363..c9f44f20ab 100644
--- a/src/machine/machine-varlink.c
+++ b/src/machine/machine-varlink.c
@@ -200,8 +200,8 @@ static int lookup_machine_by_name(sd_varlink *link, Manager *manager, const char
return 0;
}
-static int lookup_machine_by_pid(sd_varlink *link, Manager *manager, pid_t pid, Machine **ret_machine) {
- _cleanup_(pidref_done) PidRef pidref = PIDREF_MAKE_FROM_PID(pid);
+static int lookup_machine_by_pidref(sd_varlink *link, Manager *manager, const PidRef *pidref, Machine **ret_machine) {
+ _cleanup_(pidref_done) PidRef peer = PIDREF_NULL;
Machine *machine;
int r;
@@ -209,16 +209,16 @@ static int lookup_machine_by_pid(sd_varlink *link, Manager *manager, pid_t pid,
assert(manager);
assert(ret_machine);
- if (pid == PID_AUTOMATIC) {
- r = varlink_get_peer_pidref(link, &pidref);
+ if (pidref_is_automatic(pidref)) {
+ r = varlink_get_peer_pidref(link, &peer);
if (r < 0)
return log_debug_errno(r, "Failed to get peer pidref: %m");
- }
- if (!pidref_is_set(&pidref))
+ pidref = &peer;
+ } else if (!pidref_is_set(pidref))
return -EINVAL;
- r = manager_get_machine_by_pidref(manager, &pidref, &machine);
+ r = manager_get_machine_by_pidref(manager, pidref, &machine);
if (r < 0)
return r;
if (!machine)
@@ -228,7 +228,7 @@ static int lookup_machine_by_pid(sd_varlink *link, Manager *manager, pid_t pid,
return 0;
}
-int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char *machine_name, pid_t pid, Machine **ret_machine) {
+int lookup_machine_by_name_or_pidref(sd_varlink *link, Manager *manager, const char *machine_name, const PidRef *pidref, Machine **ret_machine) {
Machine *machine = NULL, *pid_machine = NULL;
int r;
@@ -236,6 +236,8 @@ int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char
assert(manager);
assert(ret_machine);
+ /* This returns 0 on success, 1 on error and it is replied, and a negative errno otherwise. */
+
if (machine_name) {
r = lookup_machine_by_name(link, manager, machine_name, &machine);
if (r == -EINVAL)
@@ -244,8 +246,8 @@ int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char
return r;
}
- if (pid_is_valid_or_automatic(pid)) {
- r = lookup_machine_by_pid(link, manager, pid, &pid_machine);
+ if (pidref_is_set(pidref) || pidref_is_automatic(pidref)) {
+ r = lookup_machine_by_pidref(link, manager, pidref, &pid_machine);
if (r == -EINVAL)
return sd_varlink_error_invalid_parameter_name(link, "pid");
if (r < 0)
@@ -253,7 +255,7 @@ int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char
}
if (machine && pid_machine && machine != pid_machine)
- return log_debug_errno(SYNTHETIC_ERRNO(ESRCH), "Search by machine name '%s' and pid %d resulted in two different machines", machine_name, pid);
+ return log_debug_errno(SYNTHETIC_ERRNO(ESRCH), "Search by machine name '%s' and pid " PID_FMT " resulted in two different machines", machine_name, pidref->pid);
if (machine)
*ret_machine = machine;
else if (pid_machine)
@@ -308,24 +310,32 @@ int vl_method_terminate_internal(sd_varlink *link, sd_json_variant *parameters,
return sd_varlink_reply(link, NULL);
}
-int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
- struct params {
- const char *machine_name;
- pid_t pid;
- const char *swhom;
- int32_t signo;
- };
+typedef struct MachineKillParameters {
+ const char *name;
+ PidRef pidref;
+ const char *swhom;
+ int32_t signo;
+} MachineKillParameters;
+
+static void machine_kill_paramaters_done(MachineKillParameters *p) {
+ assert(p);
+ pidref_done(&p->pidref);
+}
+
+int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
static const sd_json_dispatch_field dispatch_table[] = {
- VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(struct params),
- { "whom", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct params, swhom), 0 },
- { "signal", _SD_JSON_VARIANT_TYPE_INVALID , sd_json_dispatch_signal, offsetof(struct params, signo), SD_JSON_MANDATORY },
+ VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineKillParameters),
+ { "whom", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MachineKillParameters, swhom), 0 },
+ { "signal", _SD_JSON_VARIANT_TYPE_INVALID , sd_json_dispatch_signal, offsetof(MachineKillParameters, signo), SD_JSON_MANDATORY },
VARLINK_DISPATCH_POLKIT_FIELD,
{}
};
Manager *manager = ASSERT_PTR(userdata);
- struct params p = {};
+ _cleanup_(machine_kill_paramaters_done) MachineKillParameters p = {
+ .pidref = PIDREF_NULL,
+ };
KillWhom whom;
int r;
@@ -337,10 +347,10 @@ int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met
return r;
Machine *machine;
- r = lookup_machine_by_name_or_pid(link, manager, p.machine_name, p.pid, &machine);
+ r = lookup_machine_by_name_or_pidref(link, manager, p.name, &p.pidref, &machine);
if (r == -ESRCH)
return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL);
- if (r < 0)
+ if (r != 0)
return r;
if (isempty(p.swhom))
diff --git a/src/machine/machine-varlink.h b/src/machine/machine-varlink.h
index 77db7b14ed..5b58b35d80 100644
--- a/src/machine/machine-varlink.h
+++ b/src/machine/machine-varlink.h
@@ -5,20 +5,20 @@
#include "machine.h"
-#define VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(t) { \
- .name = "name", \
- .type = SD_JSON_VARIANT_STRING, \
- .callback = sd_json_dispatch_const_string, \
- .offset = offsetof(t, machine_name) \
- }, { \
- .name = "pid", \
- .type = _SD_JSON_VARIANT_TYPE_INVALID, \
- .callback = sd_json_dispatch_pid, \
- .offset = offsetof(t, pid), \
- .flags = SD_JSON_RELAX /* allows pid=0 */ \
+#define VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(t) { \
+ .name = "name", \
+ .type = SD_JSON_VARIANT_STRING, \
+ .callback = sd_json_dispatch_const_string, \
+ .offset = offsetof(t, name) \
+ }, { \
+ .name = "pid", \
+ .type = _SD_JSON_VARIANT_TYPE_INVALID, \
+ .callback = json_dispatch_pidref, \
+ .offset = offsetof(t, pidref), \
+ .flags = SD_JSON_RELAX /* allows PID_AUTOMATIC */ \
}
-int lookup_machine_by_name_or_pid(sd_varlink *link, Manager *manager, const char *machine_name, pid_t pid, Machine **ret_machine);
+int lookup_machine_by_name_or_pidref(sd_varlink *link, Manager *manager, const char *machine_name, const PidRef *pidref, Machine **ret_machine);
int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
diff --git a/src/machine/machined-core.c b/src/machine/machined-core.c
index baa9a605ba..b1468b62c6 100644
--- a/src/machine/machined-core.c
+++ b/src/machine/machined-core.c
@@ -402,3 +402,83 @@ int machine_get_os_release(Machine *machine, char ***ret_os_release) {
*ret_os_release = TAKE_PTR(l);
return 0;
}
+
+static int image_flush_cache(sd_event_source *s, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+
+ assert(s);
+
+ hashmap_clear(m->image_cache);
+ return 0;
+}
+
+int manager_acquire_image(Manager *m, const char *name, Image **ret) {
+ int r;
+
+ assert(m);
+ assert(name);
+
+ Image *existing = hashmap_get(m->image_cache, name);
+ if (existing) {
+ if (ret)
+ *ret = existing;
+ return 0;
+ }
+
+ if (!m->image_cache_defer_event) {
+ r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add deferred event: %m");
+
+ r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to set source priority for event: %m");
+ }
+
+ r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to enable source: %m") ;
+
+ _cleanup_(image_unrefp) Image *image = NULL;
+ r = image_find(IMAGE_MACHINE, name, NULL, &image);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to find image: %m");
+
+ image->userdata = m;
+
+ r = hashmap_ensure_put(&m->image_cache, &image_hash_ops, image->name, image);
+ if (r < 0)
+ return r;
+
+ if (ret)
+ *ret = image;
+
+ TAKE_PTR(image);
+ return 0;
+}
+
+int rename_image_and_update_cache(Manager *m, Image *image, const char* new_name) {
+ int r;
+
+ assert(m);
+ assert(image);
+ assert(new_name);
+
+ /* The image is cached with its name, hence it is necessary to remove from the cache before renaming. */
+ assert_se(hashmap_remove_value(m->image_cache, image->name, image));
+
+ r = image_rename(image, new_name);
+ if (r < 0) {
+ image = image_unref(image);
+ return r;
+ }
+
+ /* Then save the object again in the cache. */
+ r = hashmap_put(m->image_cache, image->name, image);
+ if (r < 0) {
+ image = image_unref(image);
+ log_debug_errno(r, "Failed to put renamed image into cache, ignoring: %m");
+ }
+
+ return 0;
+}
diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c
index 45e71266ed..47dbbe226e 100644
--- a/src/machine/machined-varlink.c
+++ b/src/machine/machined-varlink.c
@@ -3,8 +3,10 @@
#include "sd-varlink.h"
#include "bus-polkit.h"
+#include "discover-image.h"
#include "format-util.h"
#include "hostname-util.h"
+#include "image-varlink.h"
#include "json-util.h"
#include "machine-varlink.h"
#include "machined-varlink.h"
@@ -13,6 +15,7 @@
#include "socket-util.h"
#include "user-util.h"
#include "varlink-io.systemd.Machine.h"
+#include "varlink-io.systemd.MachineImage.h"
#include "varlink-io.systemd.UserDatabase.h"
typedef struct LookupParameters {
@@ -419,10 +422,16 @@ static int list_machine_one(sd_varlink *link, Machine *m, bool more) {
}
typedef struct MachineLookupParameters {
- const char *machine_name;
- pid_t pid;
+ const char *name;
+ PidRef pidref;
} MachineLookupParameters;
+static void machine_lookup_parameters_done(MachineLookupParameters *p) {
+ assert(p);
+
+ pidref_done(&p->pidref);
+}
+
static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
static const sd_json_dispatch_field dispatch_table[] = {
VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineLookupParameters),
@@ -431,7 +440,9 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl
};
Manager *m = ASSERT_PTR(userdata);
- MachineLookupParameters p = {};
+ _cleanup_(machine_lookup_parameters_done) MachineLookupParameters p = {
+ .pidref = PIDREF_NULL,
+ };
Machine *machine;
int r;
@@ -442,11 +453,11 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl
if (r != 0)
return r;
- if (p.machine_name || pid_is_valid_or_automatic(p.pid)) {
- r = lookup_machine_by_name_or_pid(link, m, p.machine_name, p.pid, &machine);
+ if (p.name || pidref_is_set(&p.pidref) || pidref_is_automatic(&p.pidref)) {
+ r = lookup_machine_by_name_or_pidref(link, m, p.name, &p.pidref, &machine);
if (r == -ESRCH)
return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL);
- if (r < 0)
+ if (r != 0)
return r;
return list_machine_one(link, machine, /* more= */ false);
@@ -480,7 +491,9 @@ static int lookup_machine_and_call_method(sd_varlink *link, sd_json_variant *par
};
Manager *manager = ASSERT_PTR(userdata);
- MachineLookupParameters p = {};
+ _cleanup_(machine_lookup_parameters_done) MachineLookupParameters p = {
+ .pidref = PIDREF_NULL,
+ };
Machine *machine;
int r;
@@ -491,10 +504,10 @@ static int lookup_machine_and_call_method(sd_varlink *link, sd_json_variant *par
if (r != 0)
return r;
- r = lookup_machine_by_name_or_pid(link, manager, p.machine_name, p.pid, &machine);
+ r = lookup_machine_by_name_or_pidref(link, manager, p.name, &p.pidref, &machine);
if (r == -ESRCH)
return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL);
- if (r < 0)
+ if (r != 0)
return r;
return method(link, parameters, flags, machine);
@@ -508,6 +521,117 @@ static int vl_method_terminate(sd_varlink *link, sd_json_variant *parameters, sd
return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_terminate_internal);
}
+static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image, bool more, bool read_metadata) {
+ int r;
+
+ assert(link);
+ assert(image);
+
+ if (read_metadata && !image->metadata_valid) {
+ r = image_read_metadata(image, &image_policy_container);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read image metadata: %m");
+ }
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+
+ r = sd_json_buildo(
+ &v,
+ SD_JSON_BUILD_PAIR_STRING("name", image->name),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("path", image->path),
+ SD_JSON_BUILD_PAIR_STRING("type", image_type_to_string(image->type)),
+ SD_JSON_BUILD_PAIR_STRING("class", image_class_to_string(image->class)),
+ SD_JSON_BUILD_PAIR_BOOLEAN("readOnly", image->read_only),
+ JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("creationTimestamp", image->crtime),
+ JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("modificationTimestamp", image->mtime),
+ JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("usage", image->usage, UINT64_MAX),
+ JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("usageExclusive", image->usage_exclusive, UINT64_MAX),
+ JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("limit", image->limit, UINT64_MAX),
+ JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("limitExclusive", image->limit_exclusive, UINT64_MAX));
+ if (r < 0)
+ return r;
+
+ if (image->metadata_valid) {
+ r = sd_json_variant_merge_objectbo(
+ &v,
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("hostname", image->hostname),
+ SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(image->machine_id), "machineId", SD_JSON_BUILD_ID128(image->machine_id)),
+ SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(image->machine_info), "machineInfo", JSON_BUILD_STRV_ENV_PAIR(image->machine_info)),
+ SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(image->os_release), "OSRelease", JSON_BUILD_STRV_ENV_PAIR(image->os_release)));
+ if (r < 0)
+ return r;
+ }
+
+ if (more)
+ return sd_varlink_notify(link, v);
+
+ return sd_varlink_reply(link, v);
+}
+
+static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ struct params {
+ const char *image_name;
+ bool acquire_metadata;
+ } p = {};
+ int r;
+
+ static const sd_json_dispatch_field dispatch_table[] = {
+ { "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct params, image_name), 0 },
+ { "acquireMetadata", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct params, acquire_metadata), 0 },
+ VARLINK_DISPATCH_POLKIT_FIELD,
+ {}
+ };
+
+ assert(link);
+ assert(parameters);
+
+ r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (p.image_name) {
+ _cleanup_(image_unrefp) Image *found = NULL;
+
+ if (!image_name_is_valid(p.image_name))
+ return sd_varlink_error_invalid_parameter_name(link, "name");
+
+ r = image_find(IMAGE_MACHINE, p.image_name, /* root = */ NULL, &found);
+ if (r == -ENOENT)
+ return sd_varlink_error(link, "io.systemd.MachineImage.NoSuchImage", NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to find image: %m");
+
+ return list_image_one_and_maybe_read_metadata(link, found, /* more = */ false, p.acquire_metadata);
+ }
+
+ if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
+ return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
+
+ _cleanup_hashmap_free_ Hashmap *images = hashmap_new(&image_hash_ops);
+ if (!images)
+ return -ENOMEM;
+
+ r = image_discover(IMAGE_MACHINE, /* root = */ NULL, images);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to discover images: %m");
+
+ Image *image, *previous = NULL;
+ HASHMAP_FOREACH(image, images) {
+ if (previous) {
+ r = list_image_one_and_maybe_read_metadata(link, previous, /* more = */ true, p.acquire_metadata);
+ if (r < 0)
+ return r;
+ }
+
+ previous = image;
+ }
+
+ if (previous)
+ return list_image_one_and_maybe_read_metadata(link, previous, /* more = */ false, p.acquire_metadata);
+
+ return sd_varlink_error(link, "io.systemd.MachineImage.NoSuchImage", NULL);
+}
+
static int manager_varlink_init_userdb(Manager *m) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
int r;
@@ -564,17 +688,22 @@ static int manager_varlink_init_machine(Manager *m) {
sd_varlink_server_set_userdata(s, m);
- r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_Machine);
+ r = sd_varlink_server_add_interface_many(
+ s,
+ &vl_interface_io_systemd_Machine,
+ &vl_interface_io_systemd_MachineImage);
if (r < 0)
- return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
+ return log_error_errno(r, "Failed to add Machine and MachineImage interfaces to varlink server: %m");
r = sd_varlink_server_bind_method_many(
s,
- "io.systemd.Machine.Register", vl_method_register,
- "io.systemd.Machine.List", vl_method_list,
- "io.systemd.Machine.Unregister", vl_method_unregister,
- "io.systemd.Machine.Terminate", vl_method_terminate,
- "io.systemd.Machine.Kill", vl_method_kill);
+ "io.systemd.Machine.Register", vl_method_register,
+ "io.systemd.Machine.List", vl_method_list,
+ "io.systemd.Machine.Unregister", vl_method_unregister,
+ "io.systemd.Machine.Terminate", vl_method_terminate,
+ "io.systemd.Machine.Kill", vl_method_kill,
+ "io.systemd.MachineImage.List", vl_method_list_images,
+ "io.systemd.MachineImage.Update", vl_method_update_image);
if (r < 0)
return log_error_errno(r, "Failed to register varlink methods: %m");
@@ -582,7 +711,11 @@ static int manager_varlink_init_machine(Manager *m) {
r = sd_varlink_server_listen_address(s, "/run/systemd/machine/io.systemd.Machine", 0666);
if (r < 0)
- return log_error_errno(r, "Failed to bind to varlink socket: %m");
+ return log_error_errno(r, "Failed to bind to io.systemd.Machine varlink socket: %m");
+
+ r = sd_varlink_server_listen_address(s, "/run/systemd/machine/io.systemd.MachineImage", 0666);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind to io.systemd.MachineImage varlink socket: %m");
r = sd_varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
diff --git a/src/machine/machined.h b/src/machine/machined.h
index 721bf68012..3d1f502699 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -68,3 +68,5 @@ void manager_enqueue_gc(Manager *m);
int machine_get_addresses(Machine* machine, struct local_address **ret_addresses);
int machine_get_os_release(Machine *machine, char ***ret_os_release);
+int manager_acquire_image(Manager *m, const char *name, Image **ret);
+int rename_image_and_update_cache(Manager *m, Image *image, const char* new_name);
diff --git a/src/machine/meson.build b/src/machine/meson.build
index 3150b33de5..f3358d4b98 100644
--- a/src/machine/meson.build
+++ b/src/machine/meson.build
@@ -2,6 +2,7 @@
libmachine_core_sources = files(
'image-dbus.c',
+ 'image-varlink.c',
'machine-dbus.c',
'machine-varlink.c',
'machine.c',
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index deffa4dead..d580840a26 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -27,8 +27,8 @@
#include "strv.h"
#include "strxcpyx.h"
-#define ADDRESSES_PER_LINK_MAX 2048U
-#define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U
+#define ADDRESSES_PER_LINK_MAX 16384U
+#define STATIC_ADDRESSES_PER_NETWORK_MAX 8192U
#define KNOWN_FLAGS \
(IFA_F_SECONDARY | \
@@ -644,12 +644,7 @@ static int address_set_masquerade(Address *address, bool add) {
if (!address->link->network)
return 0;
- if (address->family == AF_INET &&
- !FLAGS_SET(address->link->network->ip_masquerade, ADDRESS_FAMILY_IPV4))
- return 0;
-
- if (address->family == AF_INET6 &&
- !FLAGS_SET(address->link->network->ip_masquerade, ADDRESS_FAMILY_IPV6))
+ if (!FLAGS_SET(address->link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(address->family)))
return 0;
if (address->scope >= RT_SCOPE_LINK)
@@ -2030,12 +2025,14 @@ static int config_parse_broadcast(
void *userdata) {
Address *address = ASSERT_PTR(userdata);
- union in_addr_union u;
int r;
+ /* Do not check or set address->family here. It will be checked later in
+ * address_section_verify() -> address_section_adjust_broadcast() . */
+
if (isempty(rvalue)) {
/* The broadcast address will be calculated based on Address=, and set if the link is
- * not a wireguard interface. Here, we do not check or set address->family. */
+ * not a wireguard interface. */
address->broadcast = (struct in_addr) {};
address->set_broadcast = -1;
return 1;
@@ -2043,25 +2040,20 @@ static int config_parse_broadcast(
r = parse_boolean(rvalue);
if (r >= 0) {
- /* The broadcast address will be calculated based on Address=. Here, we do not check or
- * set address->family. */
+ /* The broadcast address will be calculated based on Address=. */
address->broadcast = (struct in_addr) {};
address->set_broadcast = r;
return 1;
}
- r = in_addr_from_string(AF_INET, rvalue, &u);
- if (r < 0)
- return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
- if (in4_addr_is_null(&u.in)) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Broadcast cannot be ANY address, ignoring assignment: %s", rvalue);
- return 0;
- }
+ r = config_parse_in_addr_non_null(
+ unit, filename, line, section, section_line,
+ lvalue, /* ltype = */ AF_INET, rvalue,
+ &address->broadcast, /* userdata = */ NULL);
+ if (r <= 0)
+ return r;
- address->broadcast = u.in;
address->set_broadcast = true;
- address->family = AF_INET;
return 1;
}
diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c
index 1799d04212..8b64dfe8f0 100644
--- a/src/network/networkd-dhcp-common.c
+++ b/src/network/networkd-dhcp-common.c
@@ -71,7 +71,7 @@ bool link_dhcp_enabled(Link *link, int family) {
if (!link->network)
return false;
- return link->network->dhcp & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
+ return link->network->dhcp & AF_TO_ADDRESS_FAMILY(family);
}
void network_adjust_dhcp(Network *network) {
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 3f773cbb99..f1ecf64205 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -264,30 +264,40 @@ static int dhcp6_address_acquired(Link *link) {
return r;
}
- if (link->network->dhcp6_use_hostname) {
- const char *dhcpname = NULL;
- _cleanup_free_ char *hostname = NULL;
-
- (void) sd_dhcp6_lease_get_fqdn(link->dhcp6_lease, &dhcpname);
-
- if (dhcpname) {
- r = shorten_overlong(dhcpname, &hostname);
- if (r < 0)
- log_link_warning_errno(link, r, "Unable to shorten overlong DHCP hostname '%s', ignoring: %m", dhcpname);
- if (r == 1)
- log_link_notice(link, "Overlong DHCP hostname received, shortened from '%s' to '%s'", dhcpname, hostname);
- }
- if (hostname) {
- r = manager_set_hostname(link->manager, hostname);
- if (r < 0)
- log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname);
- }
- }
+ return 0;
+}
+
+static int dhcp6_request_hostname(Link *link) {
+ _cleanup_free_ char *hostname = NULL;
+ const char *dhcpname = NULL;
+ int r;
+
+ assert(link);
+ assert(link->network);
+
+ if (!link->network->dhcp6_use_hostname)
+ return 0;
+
+ r = sd_dhcp6_lease_get_fqdn(link->dhcp6_lease, &dhcpname);
+ if (r == -ENODATA)
+ return 0;
+ if (r < 0)
+ return r;
+
+ r = shorten_overlong(dhcpname, &hostname);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Unable to shorten overlong DHCP hostname '%s': %m", dhcpname);
+ if (r == 1)
+ log_link_notice(link, "Overlong DHCP hostname received, shortened from '%s' to '%s'", dhcpname, hostname);
+
+ r = manager_set_hostname(link->manager, hostname);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to set transient hostname to '%s', ignoring: %m", hostname);
return 0;
}
-static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
+static int dhcp6_lease_acquired(sd_dhcp6_client *client, Link *link) {
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease_old = NULL;
sd_dhcp6_lease *lease;
int r;
@@ -302,6 +312,10 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
lease_old = TAKE_PTR(link->dhcp6_lease);
link->dhcp6_lease = sd_dhcp6_lease_ref(lease);
+ r = dhcp6_request_hostname(link);
+ if (r < 0)
+ return r;
+
r = dhcp6_address_acquired(link);
if (r < 0)
return r;
@@ -327,22 +341,6 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
link_set_state(link, LINK_STATE_CONFIGURING);
link_check_ready(link);
- return 0;
-}
-
-static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) {
- sd_dhcp6_lease *lease;
- int r;
-
- assert(client);
- assert(link);
-
- r = sd_dhcp6_client_get_lease(client, &lease);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get DHCPv6 lease: %m");
-
- unref_and_replace_full(link->dhcp6_lease, lease, sd_dhcp6_lease_ref, sd_dhcp6_lease_unref);
-
link_dirty(link);
return 0;
}
@@ -387,11 +385,8 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
break;
case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
- r = dhcp6_lease_ip_acquired(client, link);
- break;
-
case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
- r = dhcp6_lease_information_acquired(client, link);
+ r = dhcp6_lease_acquired(client, link);
break;
default:
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 234e08680b..59240bbc36 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -288,6 +288,7 @@ static Link *link_free(Link *link) {
network_unref(link->network);
sd_event_source_disable_unref(link->carrier_lost_timer);
+ sd_event_source_disable_unref(link->ipv6_mtu_wait_synced_event_source);
return mfree(link);
}
@@ -1869,8 +1870,6 @@ static int link_carrier_lost(Link *link) {
}
static int link_admin_state_up(Link *link) {
- int r;
-
assert(link);
/* This is called every time an interface admin state changes to up;
@@ -1886,9 +1885,7 @@ static int link_admin_state_up(Link *link) {
/* We set the ipv6 mtu after the device mtu, but the kernel resets
* ipv6 mtu on NETDEV_UP, so we need to reset it. */
- r = link_set_ipv6_mtu(link, LOG_INFO);
- if (r < 0)
- log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
+ (void) link_set_ipv6_mtu(link, LOG_INFO);
return 0;
}
@@ -2439,12 +2436,9 @@ static int link_update_mtu(Link *link, sd_netlink_message *message) {
link->mtu = mtu;
- if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
+ if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
/* The kernel resets IPv6 MTU after changing device MTU. So, we need to re-set IPv6 MTU again. */
- r = link_set_ipv6_mtu(link, LOG_INFO);
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to set IPv6 MTU, ignoring: %m");
- }
+ (void) link_set_ipv6_mtu_async(link);
if (link->dhcp_client) {
r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 27bc299bc5..e86839af8e 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -74,6 +74,9 @@ typedef struct Link {
sd_device *dev;
char *driver;
+ sd_event_source *ipv6_mtu_wait_synced_event_source;
+ unsigned ipv6_mtu_wait_trial_count;
+
/* bridge vlan */
uint16_t bridge_vlan_pvid;
bool bridge_vlan_pvid_is_untagged;
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 529311f4a4..fc06e5c38b 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -1083,9 +1083,7 @@ static int ndisc_router_process_mtu(Link *link, sd_ndisc_router *rt) {
link->ndisc_mtu = mtu;
- r = link_set_ipv6_mtu(link, LOG_DEBUG);
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to apply IPv6 MTU (%"PRIu32"), ignoring: %m", mtu);
+ (void) link_set_ipv6_mtu(link, LOG_DEBUG);
return 0;
}
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index 5115646e7d..8cff0237a9 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -277,83 +277,6 @@ int link_reconfigure_radv_address(Address *address, Link *link) {
return 0;
}
-static int radv_set_prefix(Link *link, Prefix *prefix) {
- _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
- int r;
-
- assert(link);
- assert(link->radv);
- assert(prefix);
-
- r = sd_radv_prefix_new(&p);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_prefix(p, &prefix->prefix.address, prefix->prefix.prefixlen);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_preferred_lifetime(p, prefix->prefix.preferred_lifetime, prefix->prefix.preferred_until);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_valid_lifetime(p, prefix->prefix.valid_lifetime, prefix->prefix.valid_until);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_onlink(p, FLAGS_SET(prefix->prefix.flags, ND_OPT_PI_FLAG_ONLINK));
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_address_autoconfiguration(p, FLAGS_SET(prefix->prefix.flags, ND_OPT_PI_FLAG_AUTO));
- if (r < 0)
- return r;
-
- return sd_radv_add_prefix(link->radv, p);
-}
-
-static int radv_set_route_prefix(Link *link, RoutePrefix *prefix) {
- _cleanup_(sd_radv_route_prefix_unrefp) sd_radv_route_prefix *p = NULL;
- int r;
-
- assert(link);
- assert(link->radv);
- assert(prefix);
-
- r = sd_radv_route_prefix_new(&p);
- if (r < 0)
- return r;
-
- r = sd_radv_route_prefix_set_prefix(p, &prefix->route.address, prefix->route.prefixlen);
- if (r < 0)
- return r;
-
- r = sd_radv_route_prefix_set_lifetime(p, prefix->route.lifetime, prefix->route.valid_until);
- if (r < 0)
- return r;
-
- return sd_radv_add_route_prefix(link->radv, p);
-}
-
-static int radv_set_pref64_prefix(Link *link, Prefix64 *prefix) {
- _cleanup_(sd_radv_pref64_prefix_unrefp) sd_radv_pref64_prefix *p = NULL;
- int r;
-
- assert(link);
- assert(link->radv);
- assert(prefix);
-
- r = sd_radv_pref64_prefix_new(&p);
- if (r < 0)
- return r;
-
- r = sd_radv_pref64_prefix_set_prefix(p, &prefix->prefix64.prefix, prefix->prefix64.prefixlen, prefix->prefix64.lifetime);
- if (r < 0)
- return r;
-
- return sd_radv_add_pref64_prefix(link->radv, p);
-}
-
static int network_get_ipv6_dns(Network *network, struct in6_addr **ret_addresses, size_t *ret_size) {
_cleanup_free_ struct in6_addr *addresses = NULL;
size_t n_addresses = 0;
@@ -430,9 +353,12 @@ static int radv_set_dns(Link *link, Link *uplink) {
return 0;
set_dns:
- return sd_radv_set_rdnss(link->radv,
- link->network->router_dns_lifetime_usec,
- dns, n_dns);
+ return sd_radv_add_rdnss(
+ link->radv,
+ n_dns,
+ dns,
+ link->network->router_dns_lifetime_usec,
+ /* valid_until = */ USEC_INFINITY);
}
static int radv_set_domains(Link *link, Link *uplink) {
@@ -466,9 +392,11 @@ set_domains:
if (!s)
return log_oom();
- return sd_radv_set_dnssl(link->radv,
- link->network->router_dns_lifetime_usec,
- s);
+ return sd_radv_add_dnssl(
+ link->radv,
+ s,
+ link->network->router_dns_lifetime_usec,
+ /* valid_until = */ USEC_INFINITY);
}
@@ -556,21 +484,40 @@ static int radv_configure(Link *link) {
Prefix *p;
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
- r = radv_set_prefix(link, p);
+ r = sd_radv_add_prefix(
+ link->radv,
+ &p->prefix.address,
+ p->prefix.prefixlen,
+ p->prefix.flags,
+ p->prefix.valid_lifetime,
+ p->prefix.preferred_lifetime,
+ p->prefix.valid_until,
+ p->prefix.preferred_until);
if (r < 0 && r != -EEXIST)
return r;
}
RoutePrefix *q;
HASHMAP_FOREACH(q, link->network->route_prefixes_by_section) {
- r = radv_set_route_prefix(link, q);
+ r = sd_radv_add_route(
+ link->radv,
+ &q->route.address,
+ q->route.prefixlen,
+ q->route.preference,
+ q->route.lifetime,
+ q->route.valid_until);
if (r < 0 && r != -EEXIST)
return r;
}
Prefix64 *n;
HASHMAP_FOREACH(n, link->network->pref64_prefixes_by_section) {
- r = radv_set_pref64_prefix(link, n);
+ r = sd_radv_add_prefix64(
+ link->radv,
+ &n->prefix64.prefix,
+ n->prefix64.prefixlen,
+ n->prefix64.lifetime,
+ n->prefix64.valid_until);
if (r < 0 && r != -EEXIST)
return r;
}
@@ -585,17 +532,15 @@ static int radv_configure(Link *link) {
if (r < 0)
return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
- r = sd_radv_set_home_agent_information(link->radv, link->network->router_home_agent_information);
- if (r < 0)
- return r;
-
- r = sd_radv_set_home_agent_preference(link->radv, link->network->router_home_agent_preference);
- if (r < 0)
- return r;
-
- r = sd_radv_set_home_agent_lifetime(link->radv, link->network->home_agent_lifetime_usec);
- if (r < 0)
- return r;
+ if (link->network->router_home_agent_information) {
+ r = sd_radv_set_home_agent(
+ link->radv,
+ link->network->router_home_agent_preference,
+ link->network->home_agent_lifetime_usec,
+ /* valid_until = */ USEC_INFINITY);
+ if (r < 0)
+ return r;
+ }
return 0;
}
@@ -739,31 +684,23 @@ int radv_add_prefix(
usec_t lifetime_preferred_usec,
usec_t lifetime_valid_usec) {
- _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
int r;
assert(link);
+ assert(prefix);
if (!link->radv)
return 0;
- r = sd_radv_prefix_new(&p);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_preferred_lifetime(p, RADV_DEFAULT_PREFERRED_LIFETIME_USEC, lifetime_preferred_usec);
- if (r < 0)
- return r;
-
- r = sd_radv_prefix_set_valid_lifetime(p, RADV_DEFAULT_VALID_LIFETIME_USEC, lifetime_valid_usec);
- if (r < 0)
- return r;
-
- r = sd_radv_add_prefix(link->radv, p);
+ r = sd_radv_add_prefix(
+ link->radv,
+ prefix,
+ prefix_len,
+ ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO,
+ RADV_DEFAULT_VALID_LIFETIME_USEC,
+ RADV_DEFAULT_PREFERRED_LIFETIME_USEC,
+ lifetime_valid_usec,
+ lifetime_preferred_usec);
if (r == -EEXIST)
return 0;
if (r < 0)
@@ -1435,7 +1372,7 @@ int config_parse_router_prefix_delegation(
}
/* When IPv6SendRA= is enabled, only static prefixes are sent by default, and users
- * need to explicitly enable DHCPv6PrefixDelegation=. */
+ * need to explicitly enable DHCPPrefixDelegation=. */
*ra = r ? RADV_PREFIX_DELEGATION_STATIC : RADV_PREFIX_DELEGATION_NONE;
return 0;
}
diff --git a/src/network/networkd-route-util.c b/src/network/networkd-route-util.c
index 9bb1e0f707..dc9663e24b 100644
--- a/src/network/networkd-route-util.c
+++ b/src/network/networkd-route-util.c
@@ -16,23 +16,26 @@
#include "strv.h"
#include "sysctl-util.h"
-#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
+#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096
unsigned routes_max(void) {
static thread_local unsigned cached = 0;
- _cleanup_free_ char *s4 = NULL, *s6 = NULL;
- unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
+ int val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
if (cached > 0)
return cached;
- if (sysctl_read_ip_property(AF_INET, NULL, "route/max_size", &s4) >= 0)
- if (safe_atou(s4, &val4) >= 0 && val4 == 2147483647U)
+ /* The kernel internally stores these maximum size in int. */
+
+ if (sysctl_read_ip_property_int(AF_INET, /* ifname = */ NULL, "route/max_size", &val4) >= 0)
+ if (val4 == INT_MAX)
/* This is the default "no limit" value in the kernel */
val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
- if (sysctl_read_ip_property(AF_INET6, NULL, "route/max_size", &s6) >= 0)
- (void) safe_atou(s6, &val6);
+ if (sysctl_read_ip_property_int(AF_INET6, /* ifname = */ NULL, "route/max_size", &val6) >= 0)
+ if (val6 == INT_MAX)
+ /* This is the default "no limit" value in the kernel */
+ val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c
index ec050e3a2d..e2c76616dd 100644
--- a/src/network/networkd-routing-policy-rule.c
+++ b/src/network/networkd-routing-policy-rule.c
@@ -12,6 +12,7 @@
#include "hashmap.h"
#include "ip-protocol-list.h"
#include "netlink-util.h"
+#include "network-util.h"
#include "networkd-manager.h"
#include "networkd-queue.h"
#include "networkd-route-util.h"
diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c
index 7330c2cc76..17a2c6da38 100644
--- a/src/network/networkd-sysctl.c
+++ b/src/network/networkd-sysctl.c
@@ -8,6 +8,7 @@
#include "af-list.h"
#include "cgroup-util.h"
+#include "event-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "missing_network.h"
@@ -302,9 +303,7 @@ int link_get_ip_forwarding(Link *link, int family) {
return t;
/* If IPMasquerade= is enabled, also enable IP forwarding. */
- if (family == AF_INET && FLAGS_SET(link->network->ip_masquerade, ADDRESS_FAMILY_IPV4))
- return true;
- if (family == AF_INET6 && FLAGS_SET(link->network->ip_masquerade, ADDRESS_FAMILY_IPV6))
+ if (FLAGS_SET(link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(family)))
return true;
/* If IPv6SendRA= is enabled, also enable IPv6 forwarding. */
@@ -376,7 +375,7 @@ static int link_set_ip_forwarding(Link *link, int family) {
/* When IPMasquerade= is enabled and the global setting is unset, enable _global_ IP forwarding, and
* re-apply per-link setting for all links. */
- if (FLAGS_SET(link->network->ip_masquerade, family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6) &&
+ if (FLAGS_SET(link->network->ip_masquerade, AF_TO_ADDRESS_FAMILY(family)) &&
link->manager->ip_forwarding[family == AF_INET6] < 0) {
link->manager->ip_forwarding[family == AF_INET6] = true;
@@ -503,6 +502,7 @@ static int link_set_ipv6_proxy_ndp(Link *link) {
int link_set_ipv6_mtu(Link *link, int log_level) {
uint32_t mtu = 0;
+ int r;
assert(link);
assert(link->manager);
@@ -510,6 +510,14 @@ int link_set_ipv6_mtu(Link *link, int log_level) {
if (!link_is_configured_for_family(link, AF_INET6))
return 0;
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ if (sd_event_source_get_enabled(link->ipv6_mtu_wait_synced_event_source, /* ret = */ NULL) > 0) {
+ log_link_debug(link, "Waiting for IPv6 MTU is synced to link MTU, delaying to set IPv6 MTU.");
+ return 0;
+ }
+
assert(link->network);
if (link->network->ndisc_use_mtu)
@@ -526,7 +534,88 @@ int link_set_ipv6_mtu(Link *link, int log_level) {
mtu = link->mtu;
}
- return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, manager_get_sysctl_shadow(link->manager));
+ r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, manager_get_sysctl_shadow(link->manager));
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to set IPv6 MTU to %"PRIu32": %m", mtu);
+
+ return 0;
+}
+
+static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata);
+
+static int link_set_ipv6_mtu_async_impl(Link *link) {
+ uint32_t current_mtu;
+ int r;
+
+ assert(link);
+
+ /* When the link MTU is updated, it seems that the kernel IPv6 MTU of the interface is asynchronously
+ * reset to the link MTU. Hence, we need to check if it is already reset, and wait for a while if not. */
+
+ if (++link->ipv6_mtu_wait_trial_count >= 10) {
+ log_link_debug(link, "Timed out waiting for IPv6 MTU being synced to link MTU, proceeding anyway.");
+ r = link_set_ipv6_mtu(link, LOG_INFO);
+ if (r < 0)
+ return r;
+
+ return 1; /* done */
+ }
+
+ /* Check if IPv6 MTU is synced. */
+ r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", &current_mtu);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m");
+
+ if (current_mtu == link->mtu) {
+ /* Already synced. Update IPv6 MTU now. */
+ r = link_set_ipv6_mtu(link, LOG_INFO);
+ if (r < 0)
+ return r;
+
+ return 1; /* done */
+ }
+
+ /* If not, set up a timer event source. */
+ r = event_reset_time_relative(
+ link->manager->event, &link->ipv6_mtu_wait_synced_event_source,
+ CLOCK_BOOTTIME, 100 * USEC_PER_MSEC, 0,
+ ipv6_mtu_wait_synced_handler, link,
+ /* priority = */ 0, "ipv6-mtu-wait-synced", /* force = */ true);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure timer event source for waiting for IPv6 MTU being synced: %m");
+
+ /* Check again. */
+ r = sysctl_read_ip_property_uint32(AF_INET6, link->ifname, "mtu", &current_mtu);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to read IPv6 MTU: %m");
+
+ if (current_mtu == link->mtu) {
+ /* Synced while setting up the timer event source. Disable it and update IPv6 MTU now. */
+ r = sd_event_source_set_enabled(link->ipv6_mtu_wait_synced_event_source, SD_EVENT_OFF);
+ if (r < 0)
+ log_link_debug_errno(link, r, "Failed to disable timer event source for IPv6 MTU, ignoring: %m");
+
+ r = link_set_ipv6_mtu(link, LOG_INFO);
+ if (r < 0)
+ return r;
+
+ return 1; /* done */
+ }
+
+ log_link_debug(link, "IPv6 MTU is not synced to the link MTU after it is changed. Waiting for a while.");
+ return 0; /* waiting */
+}
+
+static int ipv6_mtu_wait_synced_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+ (void) link_set_ipv6_mtu_async_impl(ASSERT_PTR(userdata));
+ return 0;
+}
+
+int link_set_ipv6_mtu_async(Link *link) {
+ assert(link);
+
+ link->ipv6_mtu_wait_trial_count = 0;
+ return link_set_ipv6_mtu_async_impl(link);
}
static int link_set_ipv4_accept_local(Link *link) {
@@ -616,9 +705,7 @@ int link_set_sysctl(Link *link) {
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv6 proxy NDP, ignoring: %m");
- r = link_set_ipv6_mtu(link, LOG_INFO);
- if (r < 0)
- log_link_warning_errno(link, r, "Cannot set IPv6 MTU, ignoring: %m");
+ (void) link_set_ipv6_mtu(link, LOG_INFO);
r = link_set_ipv6ll_stable_secret(link);
if (r < 0)
diff --git a/src/network/networkd-sysctl.h b/src/network/networkd-sysctl.h
index 446b835555..1c19fbd2d6 100644
--- a/src/network/networkd-sysctl.h
+++ b/src/network/networkd-sysctl.h
@@ -42,6 +42,7 @@ void manager_set_sysctl(Manager *manager);
int link_get_ip_forwarding(Link *link, int family);
int link_set_sysctl(Link *link);
int link_set_ipv6_mtu(Link *link, int log_level);
+int link_set_ipv6_mtu_async(Link *link);
const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 70ce4e6d79..d659937f3b 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -320,7 +320,7 @@ static int help(void) {
return log_oom();
printf("%1$s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
- "%5$sSpawn a command or OS in a light-weight container.%6$s\n\n"
+ "%5$sSpawn a command or OS in a lightweight container.%6$s\n\n"
" -h --help Show this help\n"
" --version Print version string\n"
" -q --quiet Do not show status information\n"
@@ -5267,7 +5267,7 @@ static int run_container(
barrier_set_role(&barrier, BARRIER_PARENT);
- fdset_close(fds);
+ fdset_close(fds, /* async= */ false);
fd_inner_socket_pair[1] = safe_close(fd_inner_socket_pair[1]);
fd_outer_socket_pair[1] = safe_close(fd_outer_socket_pair[1]);
diff --git a/src/oom/oomctl.c b/src/oom/oomctl.c
index 00914f35df..b206a78c3d 100644
--- a/src/oom/oomctl.c
+++ b/src/oom/oomctl.c
@@ -6,7 +6,7 @@
#include "build.h"
#include "bus-error.h"
#include "bus-locator.h"
-#include "copy.h"
+#include "bus-message-util.h"
#include "main-func.h"
#include "pretty-print.h"
#include "terminal-util.h"
@@ -47,7 +47,6 @@ static int dump_state(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- int fd = -EBADF;
int r;
r = sd_bus_open_system(&bus);
@@ -60,12 +59,7 @@ static int dump_state(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to dump context: %s", bus_error_message(&error, r));
- r = sd_bus_message_read(reply, "h", &fd);
- if (r < 0)
- return bus_log_parse_error(r);
-
- fflush(stdout);
- return copy_bytes(fd, STDOUT_FILENO, UINT64_MAX, 0);
+ return bus_message_dump_fd(reply);
}
static int parse_argv(int argc, char *argv[]) {
diff --git a/src/oom/oomd-manager.c b/src/oom/oomd-manager.c
index 6ff4d98c32..7437a6e889 100644
--- a/src/oom/oomd-manager.c
+++ b/src/oom/oomd-manager.c
@@ -24,6 +24,7 @@ typedef struct ManagedOOMMessage {
char *path;
char *property;
uint32_t limit;
+ usec_t duration;
} ManagedOOMMessage;
static void managed_oom_message_destroy(ManagedOOMMessage *message) {
@@ -43,6 +44,7 @@ static int process_managed_oom_message(Manager *m, uid_t uid, sd_json_variant *p
{ "path", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ManagedOOMMessage, path), SD_JSON_MANDATORY },
{ "property", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(ManagedOOMMessage, property), SD_JSON_MANDATORY },
{ "limit", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(ManagedOOMMessage, limit), 0 },
+ { "duration", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(ManagedOOMMessage, duration), 0 },
{},
};
@@ -55,10 +57,13 @@ static int process_managed_oom_message(Manager *m, uid_t uid, sd_json_variant *p
/* Skip malformed elements and keep processing in case the others are good */
JSON_VARIANT_ARRAY_FOREACH(c, cgroups) {
- _cleanup_(managed_oom_message_destroy) ManagedOOMMessage message = {};
+ _cleanup_(managed_oom_message_destroy) ManagedOOMMessage message = {
+ .duration = USEC_INFINITY,
+ };
OomdCGroupContext *ctx;
Hashmap *monitor_hm;
loadavg_t limit;
+ usec_t duration;
if (!sd_json_variant_is_object(c))
continue;
@@ -104,6 +109,11 @@ static int process_managed_oom_message(Manager *m, uid_t uid, sd_json_variant *p
continue;
}
+ if (streq(message.property, "ManagedOOMMemoryPressure") && message.duration != USEC_INFINITY)
+ duration = message.duration;
+ else
+ duration = m->default_mem_pressure_duration_usec;
+
r = oomd_insert_cgroup_context(NULL, monitor_hm, message.path);
if (r == -ENOMEM)
return r;
@@ -113,8 +123,10 @@ static int process_managed_oom_message(Manager *m, uid_t uid, sd_json_variant *p
/* Always update the limit in case it was changed. For non-memory pressure detection the value is
* ignored so always updating it here is not a problem. */
ctx = hashmap_get(monitor_hm, empty_to_root(message.path));
- if (ctx)
+ if (ctx) {
ctx->mem_pressure_limit = limit;
+ ctx->mem_pressure_duration_usec = duration;
+ }
}
/* Toggle wake-ups for "ManagedOOMSwap" if entries are present. */
@@ -472,7 +484,7 @@ static int monitor_memory_pressure_contexts_handler(sd_event_source *s, uint64_t
m->mem_pressure_post_action_delay_start = 0;
}
- r = oomd_pressure_above(m->monitored_mem_pressure_cgroup_contexts, m->default_mem_pressure_duration_usec, &targets);
+ r = oomd_pressure_above(m->monitored_mem_pressure_cgroup_contexts, &targets);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
@@ -494,7 +506,7 @@ static int monitor_memory_pressure_contexts_handler(sd_event_source *s, uint64_t
t->path,
LOADAVG_INT_SIDE(t->memory_pressure.avg10), LOADAVG_DECIMAL_SIDE(t->memory_pressure.avg10),
LOADAVG_INT_SIDE(t->mem_pressure_limit), LOADAVG_DECIMAL_SIDE(t->mem_pressure_limit),
- FORMAT_TIMESPAN(m->default_mem_pressure_duration_usec, USEC_PER_SEC));
+ FORMAT_TIMESPAN(t->mem_pressure_duration_usec, USEC_PER_SEC));
r = update_monitored_cgroup_contexts_candidates(
m->monitored_mem_pressure_cgroup_contexts, &m->monitored_mem_pressure_cgroup_contexts_candidates);
@@ -526,7 +538,7 @@ static int monitor_memory_pressure_contexts_handler(sd_event_source *s, uint64_t
selected, t->path,
LOADAVG_INT_SIDE(t->memory_pressure.avg10), LOADAVG_DECIMAL_SIDE(t->memory_pressure.avg10),
LOADAVG_INT_SIDE(t->mem_pressure_limit), LOADAVG_DECIMAL_SIDE(t->mem_pressure_limit),
- FORMAT_TIMESPAN(m->default_mem_pressure_duration_usec, USEC_PER_SEC));
+ FORMAT_TIMESPAN(t->mem_pressure_duration_usec, USEC_PER_SEC));
/* send dbus signal */
(void) sd_bus_emit_signal(m->bus,
@@ -770,7 +782,7 @@ int manager_start(
if (r < 0)
return r;
- m->default_mem_pressure_duration_usec = mem_pressure_usec ?: DEFAULT_MEM_PRESSURE_DURATION_USEC;
+ m->default_mem_pressure_duration_usec = mem_pressure_usec;
r = manager_connect_bus(m);
if (r < 0)
@@ -797,7 +809,8 @@ int manager_start(
int manager_get_dump_string(Manager *m, char **ret) {
_cleanup_(memstream_done) MemStream ms = {};
- OomdCGroupContext *c;
+ _cleanup_free_ OomdCGroupContext **sorted = NULL;
+ size_t n;
FILE *f;
int r;
@@ -826,13 +839,22 @@ int manager_get_dump_string(Manager *m, char **ret) {
FORMAT_TIMESPAN(m->default_mem_pressure_duration_usec, USEC_PER_SEC));
oomd_dump_system_context(&m->system_context, f, "\t");
+ r = hashmap_dump_sorted(m->monitored_swap_cgroup_contexts, (void***) &sorted, &n);
+ if (r < 0)
+ return r;
+
fprintf(f, "Swap Monitored CGroups:\n");
- HASHMAP_FOREACH(c, m->monitored_swap_cgroup_contexts)
- oomd_dump_swap_cgroup_context(c, f, "\t");
+ FOREACH_ARRAY(c, sorted, n)
+ oomd_dump_swap_cgroup_context(*c, f, "\t");
+
+ sorted = mfree(sorted);
+ r = hashmap_dump_sorted(m->monitored_mem_pressure_cgroup_contexts, (void***) &sorted, &n);
+ if (r < 0)
+ return r;
fprintf(f, "Memory Pressure Monitored CGroups:\n");
- HASHMAP_FOREACH(c, m->monitored_mem_pressure_cgroup_contexts)
- oomd_dump_memory_pressure_cgroup_context(c, f, "\t");
+ FOREACH_ARRAY(c, sorted, n)
+ oomd_dump_memory_pressure_cgroup_context(*c, f, "\t");
return memstream_finalize(&ms, ret, NULL);
}
diff --git a/src/oom/oomd-util.c b/src/oom/oomd-util.c
index 7c08d6714f..b996787039 100644
--- a/src/oom/oomd-util.c
+++ b/src/oom/oomd-util.c
@@ -21,8 +21,8 @@
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
oomd_cgroup_ctx_hash_ops,
char,
- string_hash_func,
- string_compare_func,
+ path_hash_func,
+ path_compare,
OomdCGroupContext,
oomd_cgroup_context_free);
@@ -69,7 +69,7 @@ OomdCGroupContext *oomd_cgroup_context_free(OomdCGroupContext *ctx) {
return mfree(ctx);
}
-int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret) {
+int oomd_pressure_above(Hashmap *h, Set **ret) {
_cleanup_set_free_ Set *targets = NULL;
OomdCGroupContext *ctx;
char *key;
@@ -90,7 +90,7 @@ int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret) {
ctx->mem_pressure_limit_hit_start = now(CLOCK_MONOTONIC);
diff = now(CLOCK_MONOTONIC) - ctx->mem_pressure_limit_hit_start;
- if (diff >= duration) {
+ if (diff >= ctx->mem_pressure_duration_usec) {
r = set_put(targets, ctx);
if (r < 0)
return -ENOMEM;
@@ -281,27 +281,45 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) {
typedef void (*dump_candidate_func)(const OomdCGroupContext *ctx, FILE *f, const char *prefix);
-static int dump_kill_candidates(OomdCGroupContext **sorted, int n, int dump_until, dump_candidate_func dump_func) {
+static int dump_kill_candidates(
+ OomdCGroupContext *sorted[],
+ size_t n,
+ const OomdCGroupContext *killed,
+ dump_candidate_func dump_func) {
+
_cleanup_(memstream_done) MemStream m = {};
FILE *f;
/* Try dumping top offendors, ignoring any errors that might happen. */
+ assert(sorted || n == 0);
+ assert(dump_func);
+
+ /* If nothing killed, then limit the number of contexts to be dumped, for safety. */
+ if (!killed)
+ n = MIN(n, DUMP_ON_KILL_COUNT);
+
f = memstream_init(&m);
if (!f)
return -ENOMEM;
- fprintf(f, "Considered %d cgroups for killing, top candidates were:\n", n);
- for (int i = 0; i < dump_until; i++)
- dump_func(sorted[i], f, "\t");
+ fprintf(f, "Considered %zu cgroups for killing, top candidates were:\n", n);
+ FOREACH_ARRAY(i, sorted, n) {
+ const OomdCGroupContext *c = *i;
+
+ dump_func(c, f, "\t");
+
+ if (c == killed)
+ break;
+ }
return memstream_dump(LOG_INFO, &m);
}
int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char **ret_selected) {
_cleanup_free_ OomdCGroupContext **sorted = NULL;
+ const OomdCGroupContext *killed = NULL;
int n, r, ret = 0;
- int dump_until;
assert(h);
assert(ret_selected);
@@ -310,14 +328,15 @@ int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char
if (n < 0)
return n;
- dump_until = MIN(n, DUMP_ON_KILL_COUNT);
- for (int i = 0; i < n; i++) {
+ FOREACH_ARRAY(i, sorted, n) {
+ const OomdCGroupContext *c = *i;
+
/* Skip cgroups with no reclaim and memory usage; it won't alleviate pressure.
* Continue since there might be "avoid" cgroups at the end. */
- if (sorted[i]->pgscan == 0 && sorted[i]->current_memory_usage == 0)
+ if (c->pgscan == 0 && c->current_memory_usage == 0)
continue;
- r = oomd_cgroup_kill(sorted[i]->path, /* recurse= */ true, /* dry_run= */ dry_run);
+ r = oomd_cgroup_kill(c->path, /* recurse= */ true, /* dry_run= */ dry_run);
if (r == -ENOMEM)
return r; /* Treat oom as a hard error */
if (r < 0) {
@@ -325,24 +344,23 @@ int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char
continue; /* Try to find something else to kill */
}
- dump_until = MAX(dump_until, i + 1);
-
ret = r;
- r = strdup_to(ret_selected, sorted[i]->path);
+ r = strdup_to(ret_selected, c->path);
if (r < 0)
return r;
+
+ killed = c;
break;
}
- dump_kill_candidates(sorted, n, dump_until, oomd_dump_memory_pressure_cgroup_context);
-
+ (void) dump_kill_candidates(sorted, n, killed, oomd_dump_memory_pressure_cgroup_context);
return ret;
}
int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, char **ret_selected) {
_cleanup_free_ OomdCGroupContext **sorted = NULL;
+ const OomdCGroupContext *killed = NULL;
int n, r, ret = 0;
- int dump_until;
assert(h);
assert(ret_selected);
@@ -351,16 +369,18 @@ int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run,
if (n < 0)
return n;
- dump_until = MIN(n, DUMP_ON_KILL_COUNT);
/* Try to kill cgroups with non-zero swap usage until we either succeed in killing or we get to a cgroup with
* no swap usage. Threshold killing only cgroups with more than threshold swap usage. */
- for (int i = 0; i < n; i++) {
+
+ FOREACH_ARRAY(i, sorted, n) {
+ const OomdCGroupContext *c = *i;
+
/* Skip over cgroups with not enough swap usage. Don't break since there might be "avoid"
* cgroups at the end. */
- if (sorted[i]->swap_usage <= threshold_usage)
+ if (c->swap_usage <= threshold_usage)
continue;
- r = oomd_cgroup_kill(sorted[i]->path, /* recurse= */ true, /* dry_run= */ dry_run);
+ r = oomd_cgroup_kill(c->path, /* recurse= */ true, /* dry_run= */ dry_run);
if (r == -ENOMEM)
return r; /* Treat oom as a hard error */
if (r < 0) {
@@ -368,17 +388,16 @@ int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run,
continue; /* Try to find something else to kill */
}
- dump_until = MAX(dump_until, i + 1);
-
ret = r;
- r = strdup_to(ret_selected, sorted[i]->path);
+ r = strdup_to(ret_selected, c->path);
if (r < 0)
return r;
+
+ killed = c;
break;
}
- dump_kill_candidates(sorted, n, dump_until, oomd_dump_swap_cgroup_context);
-
+ (void) dump_kill_candidates(sorted, n, killed, oomd_dump_swap_cgroup_context);
return ret;
}
@@ -545,6 +564,7 @@ int oomd_insert_cgroup_context(Hashmap *old_h, Hashmap *new_h, const char *path)
curr_ctx->last_pgscan = old_ctx->pgscan;
curr_ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
curr_ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
+ curr_ctx->mem_pressure_duration_usec = old_ctx->mem_pressure_duration_usec;
curr_ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
}
@@ -575,6 +595,7 @@ void oomd_update_cgroup_contexts_between_hashmaps(Hashmap *old_h, Hashmap *curr_
ctx->last_pgscan = old_ctx->pgscan;
ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
+ ctx->mem_pressure_duration_usec = old_ctx->mem_pressure_duration_usec;
ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
if (oomd_pgscan_rate(ctx) > 0)
@@ -607,10 +628,12 @@ void oomd_dump_memory_pressure_cgroup_context(const OomdCGroupContext *ctx, FILE
fprintf(f,
"%sPath: %s\n"
"%s\tMemory Pressure Limit: %lu.%02lu%%\n"
+ "%s\tMemory Pressure Duration: %s\n"
"%s\tPressure: Avg10: %lu.%02lu, Avg60: %lu.%02lu, Avg300: %lu.%02lu, Total: %s\n"
"%s\tCurrent Memory Usage: %s\n",
strempty(prefix), ctx->path,
strempty(prefix), LOADAVG_INT_SIDE(ctx->mem_pressure_limit), LOADAVG_DECIMAL_SIDE(ctx->mem_pressure_limit),
+ strempty(prefix), FORMAT_TIMESPAN(ctx->mem_pressure_duration_usec, USEC_PER_SEC),
strempty(prefix),
LOADAVG_INT_SIDE(ctx->memory_pressure.avg10), LOADAVG_DECIMAL_SIDE(ctx->memory_pressure.avg10),
LOADAVG_INT_SIDE(ctx->memory_pressure.avg60), LOADAVG_DECIMAL_SIDE(ctx->memory_pressure.avg60),
diff --git a/src/oom/oomd-util.h b/src/oom/oomd-util.h
index f53e4c47e8..14fe5c5eba 100644
--- a/src/oom/oomd-util.h
+++ b/src/oom/oomd-util.h
@@ -7,7 +7,7 @@
#include "hashmap.h"
#include "psi-util.h"
-#define DUMP_ON_KILL_COUNT 10
+#define DUMP_ON_KILL_COUNT 10u
#define GROWING_SIZE_PERCENTILE 80
extern const struct hash_ops oomd_cgroup_ctx_hash_ops;
@@ -37,6 +37,7 @@ struct OomdCGroupContext {
loadavg_t mem_pressure_limit;
usec_t mem_pressure_limit_hit_start;
usec_t last_had_mem_reclaim;
+ usec_t mem_pressure_duration_usec;
};
struct OomdSystemContext {
@@ -53,12 +54,12 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(OomdCGroupContext*, oomd_cgroup_context_free);
* key: cgroup paths -> value: OomdCGroupContext. */
/* Scans all the OomdCGroupContexts in `h` and returns 1 and a set of pointers to those OomdCGroupContexts in `ret`
- * if any of them have exceeded their supplied memory pressure limits for the `duration` length of time.
+ * if any of them have exceeded their supplied memory pressure limits for the `ctx->mem_pressure_duration_usec` length of time.
* `mem_pressure_limit_hit_start` is updated accordingly for the first time the limit is exceeded, and when it returns
* below the limit.
- * Returns 0 and sets `ret` to an empty set if no entries exceeded limits for `duration`.
+ * Returns 0 and sets `ret` to an empty set if no entries exceeded limits for `ctx->mem_pressure_duration_usec`.
* Returns -ENOMEM for allocation errors. */
-int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret);
+int oomd_pressure_above(Hashmap *h, Set **ret);
/* Returns true if the amount of memory available (see proc(5)) is below the permyriad of memory specified by `threshold_permyriad`. */
bool oomd_mem_available_below(const OomdSystemContext *ctx, int threshold_permyriad);
diff --git a/src/oom/oomd.c b/src/oom/oomd.c
index 336549aff7..dd34251cd2 100644
--- a/src/oom/oomd.c
+++ b/src/oom/oomd.c
@@ -21,13 +21,50 @@
static bool arg_dry_run = false;
static int arg_swap_used_limit_permyriad = -1;
static int arg_mem_pressure_limit_permyriad = -1;
-static usec_t arg_mem_pressure_usec = 0;
+static usec_t arg_mem_pressure_usec = DEFAULT_MEM_PRESSURE_DURATION_USEC;
+
+static int config_parse_duration(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t usec, *duration = ASSERT_PTR(data);
+ int r;
+
+ if (isempty(rvalue)) {
+ *duration = DEFAULT_MEM_PRESSURE_DURATION_USEC;
+ return 0;
+ }
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0)
+ return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
+
+ if (usec == 0) {
+ /* Map zero -> default for backwards compatibility. */
+ *duration = DEFAULT_MEM_PRESSURE_DURATION_USEC;
+ return 0;
+ }
+
+ if (usec < 1 * USEC_PER_SEC || usec == USEC_INFINITY)
+ return log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= must be at least 1s and less than infinity, ignoring: %s", lvalue, rvalue);
+
+ *duration = usec;
+ return 0;
+}
static int parse_config(void) {
static const ConfigTableItem items[] = {
{ "OOM", "SwapUsedLimit", config_parse_permyriad, 0, &arg_swap_used_limit_permyriad },
{ "OOM", "DefaultMemoryPressureLimit", config_parse_permyriad, 0, &arg_mem_pressure_limit_permyriad },
- { "OOM", "DefaultMemoryPressureDurationSec", config_parse_sec, 0, &arg_mem_pressure_usec },
+ { "OOM", "DefaultMemoryPressureDurationSec", config_parse_duration, 0, &arg_mem_pressure_usec },
{}
};
@@ -167,9 +204,6 @@ static int run(int argc, char *argv[]) {
if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Requires the cgroup memory controller.");
- if (arg_mem_pressure_usec > 0 && arg_mem_pressure_usec < 1 * USEC_PER_SEC)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "DefaultMemoryPressureDurationSec= must be 0 or at least 1s");
-
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Failed to create manager: %m");
diff --git a/src/oom/test-oomd-util.c b/src/oom/test-oomd-util.c
index 1aef6039e1..53109c160d 100644
--- a/src/oom/test-oomd-util.c
+++ b/src/oom/test-oomd-util.c
@@ -138,6 +138,7 @@ static void test_oomd_cgroup_context_acquire_and_insert(void) {
c1->pgscan = UINT64_MAX;
c1->mem_pressure_limit = 6789;
c1->mem_pressure_limit_hit_start = 42;
+ c1->mem_pressure_duration_usec = 1234;
c1->last_had_mem_reclaim = 888;
assert_se(h2 = hashmap_new(&oomd_cgroup_ctx_hash_ops));
assert_se(oomd_insert_cgroup_context(h1, h2, cgroup) == 0);
@@ -149,6 +150,7 @@ static void test_oomd_cgroup_context_acquire_and_insert(void) {
assert_se(c2->last_pgscan == UINT64_MAX);
assert_se(c2->mem_pressure_limit == 6789);
assert_se(c2->mem_pressure_limit_hit_start == 42);
+ assert_se(c2->mem_pressure_duration_usec == 1234);
assert_se(c2->last_had_mem_reclaim == 888); /* assumes the live pgscan is less than UINT64_MAX */
}
@@ -162,11 +164,13 @@ static void test_oomd_update_cgroup_contexts_between_hashmaps(void) {
{ .path = paths[0],
.mem_pressure_limit = 5,
.mem_pressure_limit_hit_start = 777,
+ .mem_pressure_duration_usec = 111,
.last_had_mem_reclaim = 888,
.pgscan = 57 },
{ .path = paths[1],
.mem_pressure_limit = 6,
.mem_pressure_limit_hit_start = 888,
+ .mem_pressure_duration_usec = 222,
.last_had_mem_reclaim = 888,
.pgscan = 42 },
};
@@ -193,6 +197,7 @@ static void test_oomd_update_cgroup_contexts_between_hashmaps(void) {
assert_se(c_old->pgscan == c_new->last_pgscan);
assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit);
assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
+ assert_se(c_old->mem_pressure_duration_usec == c_new->mem_pressure_duration_usec);
assert_se(c_old->last_had_mem_reclaim == c_new->last_had_mem_reclaim);
assert_se(c_old = hashmap_get(h_old, "/1.slice"));
@@ -200,6 +205,7 @@ static void test_oomd_update_cgroup_contexts_between_hashmaps(void) {
assert_se(c_old->pgscan == c_new->last_pgscan);
assert_se(c_old->mem_pressure_limit == c_new->mem_pressure_limit);
assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
+ assert_se(c_old->mem_pressure_duration_usec == c_new->mem_pressure_duration_usec);
assert_se(c_new->last_had_mem_reclaim > c_old->last_had_mem_reclaim);
}
@@ -255,17 +261,21 @@ static void test_oomd_pressure_above(void) {
assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg60)) == 0);
assert_se(store_loadavg_fixed_point(99, 99, &(ctx[0].memory_pressure.avg300)) == 0);
ctx[0].mem_pressure_limit = threshold;
+ /* Set memory pressure duration to 0 since we use the real system monotonic clock
+ * in oomd_pressure_above() and we want to avoid this test depending on timing. */
+ ctx[0].mem_pressure_duration_usec = 0;
/* /derp.slice */
assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg10)) == 0);
assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg60)) == 0);
assert_se(store_loadavg_fixed_point(1, 11, &(ctx[1].memory_pressure.avg300)) == 0);
ctx[1].mem_pressure_limit = threshold;
+ ctx[1].mem_pressure_duration_usec = 0;
/* High memory pressure */
assert_se(h1 = hashmap_new(&string_hash_ops));
assert_se(hashmap_put(h1, "/herp.slice", &ctx[0]) >= 0);
- assert_se(oomd_pressure_above(h1, 0 /* duration */, &t1) == 1);
+ assert_se(oomd_pressure_above(h1, &t1) == 1);
assert_se(set_contains(t1, &ctx[0]));
assert_se(c = hashmap_get(h1, "/herp.slice"));
assert_se(c->mem_pressure_limit_hit_start > 0);
@@ -273,14 +283,14 @@ static void test_oomd_pressure_above(void) {
/* Low memory pressure */
assert_se(h2 = hashmap_new(&string_hash_ops));
assert_se(hashmap_put(h2, "/derp.slice", &ctx[1]) >= 0);
- assert_se(oomd_pressure_above(h2, 0 /* duration */, &t2) == 0);
+ assert_se(oomd_pressure_above(h2, &t2) == 0);
assert_se(!t2);
assert_se(c = hashmap_get(h2, "/derp.slice"));
assert_se(c->mem_pressure_limit_hit_start == 0);
/* High memory pressure w/ multiple cgroups */
assert_se(hashmap_put(h1, "/derp.slice", &ctx[1]) >= 0);
- assert_se(oomd_pressure_above(h1, 0 /* duration */, &t3) == 1);
+ assert_se(oomd_pressure_above(h1, &t3) == 1);
assert_se(set_contains(t3, &ctx[0]));
assert_se(set_size(t3) == 1);
assert_se(c = hashmap_get(h1, "/herp.slice"));
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 58701bc656..2fc702200c 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -1752,7 +1752,7 @@ static int get_any_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error
static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_handler_t handler, sd_bus_error *error) {
int ifindex, r;
- Link *l;
+ Link *l = NULL; /* avoid false maybe-uninitialized warning */
assert(m);
assert(message);
@@ -1813,7 +1813,7 @@ static int bus_method_get_link(sd_bus_message *message, void *userdata, sd_bus_e
_cleanup_free_ char *p = NULL;
Manager *m = ASSERT_PTR(userdata);
int r, ifindex;
- Link *l;
+ Link *l = NULL; /* avoid false maybe-uninitialized warning */
assert(message);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index e6d112964a..dbaad81734 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -97,6 +97,9 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *
log_debug("Removing link %i/%s", l->ifindex, l->ifname);
link_remove_user(l);
link_free(l);
+
+ /* Make sure DNS servers are dropped from written resolv.conf if their link goes away */
+ manager_write_resolv_conf(m);
}
break;
diff --git a/src/run/run.c b/src/run/run.c
index 1f2938f9ea..5d2298a5f6 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -862,7 +862,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
break;
case 'D':
- r = parse_path_argument(optarg, true, &arg_working_directory);
+ /* Root will be manually suppressed later. */
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_working_directory);
if (r < 0)
return r;
@@ -901,6 +902,10 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to get current working directory: %m");
}
+ } else {
+ /* Root was not suppressed earlier, to allow the above check to work properly. */
+ if (empty_or_root(arg_working_directory))
+ arg_working_directory = mfree(arg_working_directory);
}
arg_service_type = "exec";
diff --git a/src/shared/bus-message-util.c b/src/shared/bus-message-util.c
index 53f63508eb..d8c483ef62 100644
--- a/src/shared/bus-message-util.c
+++ b/src/shared/bus-message-util.c
@@ -1,7 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include "bus-message-util.h"
+#include <unistd.h>
+#include "bus-message-util.h"
+#include "bus-util.h"
+#include "copy.h"
#include "resolve-util.h"
int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret) {
@@ -183,3 +186,34 @@ clear:
return r;
}
+
+int bus_message_dump_string(sd_bus_message *message) {
+ const char *s;
+ int r;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ fputs(s, stdout);
+ return 0;
+}
+
+int bus_message_dump_fd(sd_bus_message *message) {
+ int fd, r;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "h", &fd);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ fflush(stdout);
+ r = copy_bytes(fd, STDOUT_FILENO, UINT64_MAX, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to dump contents in received file descriptor: %m");
+
+ return 0;
+}
diff --git a/src/shared/bus-message-util.h b/src/shared/bus-message-util.h
index b82c0830a0..50025766c2 100644
--- a/src/shared/bus-message-util.h
+++ b/src/shared/bus-message-util.h
@@ -16,3 +16,6 @@ int bus_message_read_dns_servers(
bool extended,
struct in_addr_full ***ret_dns,
size_t *ret_n_dns);
+
+int bus_message_dump_string(sd_bus_message *message);
+int bus_message_dump_fd(sd_bus_message *message);
diff --git a/src/shared/bus-print-properties.c b/src/shared/bus-print-properties.c
index 7da8cb1b12..5857fde5ad 100644
--- a/src/shared/bus-print-properties.c
+++ b/src/shared/bus-print-properties.c
@@ -109,6 +109,12 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
bus_print_property_value(name, expected_value, flags, FORMAT_TIMESTAMP(u));
+ /* Managed OOM pressure default implies "unset" and use the default set in oomd.conf. Without
+ * this condition, we will print "infinity" which implies there is no limit on memory
+ * pressure duration and is incorrect. */
+ else if (streq(name, "ManagedOOMMemoryPressureDurationUSec") && u == USEC_INFINITY)
+ bus_print_property_value(name, expected_value, flags, "[not set]");
+
else if (strstr(name, "USec"))
bus_print_property_value(name, expected_value, flags, FORMAT_TIMESPAN(u, 0));
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index b151826920..59e4901878 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -1008,6 +1008,11 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
if (streq(field, "NFTSet"))
return bus_append_nft_set(m, field, eq);
+ if (streq(field, "ManagedOOMMemoryPressureDurationSec"))
+ /* While infinity is disallowed in unit file, infinity is allowed in D-Bus API which
+ * means use the default memory pressure duration from oomd.conf. */
+ return bus_append_parse_sec_rename(m, field, isempty(eq) ? "infinity" : eq);
+
return 0;
}
diff --git a/src/shared/discover-image.h b/src/shared/discover-image.h
index e14ef9dcb5..b20b586ae0 100644
--- a/src/shared/discover-image.h
+++ b/src/shared/discover-image.h
@@ -60,9 +60,9 @@ Image *image_ref(Image *i);
DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref);
-int image_find(ImageClass class, const char *root, const char *name, Image **ret);
+int image_find(ImageClass class, const char *name, const char *root, Image **ret);
int image_from_path(const char *path, Image **ret);
-int image_find_harder(ImageClass class, const char *root, const char *name_or_path, Image **ret);
+int image_find_harder(ImageClass class, const char *name_or_path, const char *root, Image **ret);
int image_discover(ImageClass class, const char *root, Hashmap *map);
int image_remove(Image *i);
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index 4585582523..86fd1ef865 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -708,7 +708,9 @@ static int dissect_image(
* Returns -ERFKILL if image doesn't match image policy
* Returns -EBADR if verity data was provided externally for an image that has a GPT partition table (i.e. is not just a naked fs)
* Returns -EPROTONOSUPPORT if DISSECT_IMAGE_ADD_PARTITION_DEVICES is set but the block device does not have partition logic enabled
- * Returns -ENOMSG if we didn't find a single usable partition (and DISSECT_IMAGE_REFUSE_EMPTY is set) */
+ * Returns -ENOMSG if we didn't find a single usable partition (and DISSECT_IMAGE_REFUSE_EMPTY is set)
+ * Returns -EUCLEAN if some file system had an ambiguous file system superblock signature
+ */
uint64_t diskseq = m->loop ? m->loop->diskseq : 0;
@@ -1659,13 +1661,16 @@ int dissect_log_error(int log_level, int r, const char *name, const VeritySettin
name, strna(verity ? verity->data_path : NULL));
case -ERFKILL:
- return log_full_errno(log_level, r, "%s: image does not match image policy.", name);
+ return log_full_errno(log_level, r, "%s: Image does not match image policy.", name);
case -ENOMSG:
- return log_full_errno(log_level, r, "%s: no suitable partitions found.", name);
+ return log_full_errno(log_level, r, "%s: No suitable partitions found.", name);
+
+ case -EUCLEAN:
+ return log_full_errno(log_level, r, "%s: Partition with ambiguous file system superblock signature found.", name);
default:
- return log_full_errno(log_level, r, "%s: cannot dissect image: %m", name);
+ return log_full_errno(log_level, r, "%s: Cannot dissect image: %m", name);
}
}
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index c3050634bd..3e1eaa6623 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -31,14 +31,11 @@ int drop_in_file(
char **ret_unit_dir,
char **ret_path) {
- char prefix[DECIMAL_STR_MAX(unsigned) + 1] = {};
- _cleanup_free_ char *n = NULL, *unit_dir = NULL, *path = NULL;
+ _cleanup_free_ char *n = NULL, *unit_dir = NULL;
assert(dir);
assert(unit);
assert(name);
- assert(ret_unit_dir);
- assert(ret_path);
n = xescape(name, "/.");
if (!n)
@@ -46,16 +43,28 @@ int drop_in_file(
if (!filename_is_valid(n))
return -EINVAL;
- if (level != UINT_MAX)
- xsprintf(prefix, "%u-", level);
+ if (ret_unit_dir || ret_path) {
+ unit_dir = path_join(dir, strjoina(unit, ".d"));
+ if (!unit_dir)
+ return -ENOMEM;
+ }
- unit_dir = path_join(dir, strjoina(unit, ".d"));
- path = strjoin(unit_dir, "/", prefix, n, ".conf");
- if (!unit_dir || !path)
- return -ENOMEM;
+ if (ret_path) {
+ char prefix[DECIMAL_STR_MAX(unsigned) + 1] = {};
+
+ if (level != UINT_MAX)
+ xsprintf(prefix, "%u-", level);
+
+ _cleanup_free_ char *path = strjoin(unit_dir, "/", prefix, n, ".conf");
+ if (!path)
+ return -ENOMEM;
+
+ *ret_path = TAKE_PTR(path);
+ }
+
+ if (ret_unit_dir)
+ *ret_unit_dir = TAKE_PTR(unit_dir);
- *ret_unit_dir = TAKE_PTR(unit_dir);
- *ret_path = TAKE_PTR(path);
return 0;
}
@@ -66,7 +75,7 @@ int write_drop_in(
const char *name,
const char *data) {
- _cleanup_free_ char *p = NULL, *q = NULL;
+ _cleanup_free_ char *p = NULL;
int r;
assert(dir);
@@ -74,12 +83,11 @@ int write_drop_in(
assert(name);
assert(data);
- r = drop_in_file(dir, unit, level, name, &p, &q);
+ r = drop_in_file(dir, unit, level, name, /* ret_unit_dir= */ NULL, &p);
if (r < 0)
return r;
- (void) mkdir_p(p, 0755);
- return write_string_file_atomic_label(q, data);
+ return write_string_file_at_label(AT_FDCWD, p, data, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
}
int write_drop_in_format(
diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c
index 628e777da1..8435c4f118 100644
--- a/src/shared/exec-util.c
+++ b/src/shared/exec-util.c
@@ -50,13 +50,13 @@ static int do_spawn(
assert(ret_pid);
if (null_or_empty_path(path) > 0) {
- log_debug("%s is empty (a mask).", path);
+ log_debug("%s is masked, skipping.", path);
return 0;
}
pid_t pid;
r = safe_fork_full(
- "(direxec)",
+ "(exec-inner)",
(const int[]) { STDIN_FILENO, stdout_fd < 0 ? STDOUT_FILENO : stdout_fd, STDERR_FILENO },
/* except_fds= */ NULL, /* n_except_fds= */ 0,
FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REARRANGE_STDIO|FORK_CLOSE_ALL_FDS,
@@ -89,11 +89,11 @@ static int do_spawn(
}
static int do_execute(
- char* const* paths,
+ char * const *paths,
const char *root,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
- void* const callback_args[_STDOUT_CONSUME_MAX],
+ void * const callback_args[_STDOUT_CONSUME_MAX],
int output_fd,
char *argv[],
char *envp[],
@@ -103,12 +103,15 @@ static int do_execute(
bool parallel_execution;
int r;
- /* We fork this all off from a child process so that we can somewhat cleanly make
- * use of SIGALRM to set a time limit.
+ /* We fork this all off from a child process so that we can somewhat cleanly make use of SIGALRM
+ * to set a time limit.
*
- * We attempt to perform parallel execution if configured by the user, however
- * if `callbacks` is nonnull, execution must be serial.
+ * We attempt to perform parallel execution if configured by the user, however if `callbacks` is nonnull,
+ * execution must be serial.
*/
+
+ assert(!strv_isempty(paths));
+
parallel_execution = FLAGS_SET(flags, EXEC_DIR_PARALLEL) && !callbacks;
if (parallel_execution) {
@@ -218,13 +221,12 @@ static int do_execute(
while (!hashmap_isempty(pids)) {
_cleanup_free_ char *t = NULL;
pid_t pid;
+ void *p;
- pid = PTR_TO_PID(hashmap_first_key(pids));
+ t = ASSERT_PTR(hashmap_steal_first_key_and_value(pids, &p));
+ pid = PTR_TO_PID(p);
assert(pid > 0);
- t = hashmap_remove(pids, PID_TO_PTR(pid));
- assert(t);
-
r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
if (r < 0)
return r;
@@ -237,11 +239,11 @@ static int do_execute(
int execute_strv(
const char *name,
- char* const* paths,
+ char * const *paths,
const char *root,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
- void* const callback_args[_STDOUT_CONSUME_MAX],
+ void * const callback_args[_STDOUT_CONSUME_MAX],
char *argv[],
char *envp[],
ExecDirFlags flags) {
@@ -257,10 +259,10 @@ int execute_strv(
if (callbacks) {
assert(name);
- assert(callback_args);
assert(callbacks[STDOUT_GENERATE]);
assert(callbacks[STDOUT_COLLECT]);
assert(callbacks[STDOUT_CONSUME]);
+ assert(callback_args);
fd = open_serialization_fd(name);
if (fd < 0)
@@ -298,10 +300,10 @@ int execute_strv(
}
int execute_directories(
- const char* const* directories,
+ const char * const *directories,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
- void* const callback_args[_STDOUT_CONSUME_MAX],
+ void * const callback_args[_STDOUT_CONSUME_MAX],
char *argv[],
char *envp[],
ExecDirFlags flags) {
@@ -310,7 +312,7 @@ int execute_directories(
_cleanup_free_ char *name = NULL;
int r;
- assert(!strv_isempty((char**) directories));
+ assert(!strv_isempty((char* const*) directories));
r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, directories);
if (r < 0)
@@ -327,7 +329,7 @@ int execute_directories(
return log_error_errno(r, "Failed to extract file name from '%s': %m", directories[0]);
}
- return execute_strv(name, paths, NULL, timeout, callbacks, callback_args, argv, envp, flags);
+ return execute_strv(name, paths, /* root = */ NULL, timeout, callbacks, callback_args, argv, envp, flags);
}
static int gather_environment_generate(int fd, void *arg) {
@@ -336,12 +338,14 @@ static int gather_environment_generate(int fd, void *arg) {
_cleanup_strv_free_ char **new = NULL;
int r;
- /* Read a series of VAR=value assignments from fd, use them to update the list of
- * variables in env. Also update the exported environment.
+ /* Read a series of VAR=value assignments from fd, use them to update the list of variables in env.
+ * Also update the exported environment.
*
* fd is always consumed, even on error.
*/
+ assert(fd >= 0);
+
f = fdopen(fd, "r");
if (!f) {
safe_close(fd);
@@ -362,7 +366,7 @@ static int gather_environment_generate(int fd, void *arg) {
if (r < 0)
return r;
- if (setenv(*x, *y, true) < 0)
+ if (setenv(*x, *y, /* overwrite = */ true) < 0)
return -errno;
}
@@ -370,12 +374,14 @@ static int gather_environment_generate(int fd, void *arg) {
}
static int gather_environment_collect(int fd, void *arg) {
- _cleanup_fclose_ FILE *f = NULL;
char ***env = ASSERT_PTR(arg);
+ _cleanup_fclose_ FILE *f = NULL;
int r;
/* Write out a series of env=cescape(VAR=value) assignments to fd. */
+ assert(fd >= 0);
+
f = fdopen(fd, "w");
if (!f) {
safe_close(fd);
@@ -394,12 +400,14 @@ static int gather_environment_collect(int fd, void *arg) {
}
static int gather_environment_consume(int fd, void *arg) {
- _cleanup_fclose_ FILE *f = NULL;
char ***env = ASSERT_PTR(arg);
- int r = 0;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r, ret = 0;
/* Read a series of env=cescape(VAR=value) assignments from fd into env. */
+ assert(fd >= 0);
+
f = fdopen(fd, "r");
if (!f) {
safe_close(fd);
@@ -409,35 +417,33 @@ static int gather_environment_consume(int fd, void *arg) {
for (;;) {
_cleanup_free_ char *line = NULL;
const char *v;
- int k;
- k = read_line(f, LONG_LINE_MAX, &line);
- if (k < 0)
- return k;
- if (k == 0)
- break;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return ret;
v = startswith(line, "env=");
if (!v) {
- log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
- if (r == 0)
- r = -EINVAL;
-
+ RET_GATHER(ret, log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Serialization line unexpectedly didn't start with \"env=\", ignoring: %s",
+ line));
continue;
}
- k = deserialize_environment(v, env);
- if (k < 0) {
- log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
-
- if (r == 0)
- r = k;
- }
+ r = deserialize_environment(v, env);
+ if (r < 0)
+ RET_GATHER(ret, log_debug_errno(r, "Failed to deserialize line \"%s\": %m", line));
}
-
- return r;
}
+const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX] = {
+ gather_environment_generate,
+ gather_environment_collect,
+ gather_environment_consume,
+};
+
int exec_command_flags_from_strv(char * const *ex_opts, ExecCommandFlags *ret) {
ExecCommandFlags flags = 0;
@@ -478,12 +484,6 @@ int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ret) {
return 0;
}
-const gather_stdout_callback_t gather_environment[] = {
- gather_environment_generate,
- gather_environment_collect,
- gather_environment_consume,
-};
-
static const char* const exec_command_strings[] = {
"ignore-failure", /* EXEC_COMMAND_IGNORE_FAILURE */
"privileged", /* EXEC_COMMAND_FULLY_PRIVILEGED */
diff --git a/src/shared/exec-util.h b/src/shared/exec-util.h
index 4565ddbee0..ca7d30d45e 100644
--- a/src/shared/exec-util.h
+++ b/src/shared/exec-util.h
@@ -14,8 +14,7 @@ enum {
_STDOUT_CONSUME_MAX,
};
-typedef enum {
- EXEC_DIR_NONE = 0, /* No execdir flags */
+typedef enum ExecDirFlags {
EXEC_DIR_PARALLEL = 1 << 0, /* Execute scripts in parallel, if possible */
EXEC_DIR_IGNORE_ERRORS = 1 << 1, /* Ignore non-zero exit status of scripts */
EXEC_DIR_SET_SYSTEMD_EXEC_PID = 1 << 2, /* Set $SYSTEMD_EXEC_PID environment variable */
@@ -23,40 +22,40 @@ typedef enum {
EXEC_DIR_WARN_WORLD_WRITABLE = 1 << 4, /* Warn if world writable files are found */
} ExecDirFlags;
-typedef enum ExecCommandFlags {
- EXEC_COMMAND_IGNORE_FAILURE = 1 << 0,
- EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
- EXEC_COMMAND_NO_SETUID = 1 << 2,
- EXEC_COMMAND_AMBIENT_MAGIC = 1 << 3,
- EXEC_COMMAND_NO_ENV_EXPAND = 1 << 4,
- _EXEC_COMMAND_FLAGS_INVALID = -EINVAL,
-} ExecCommandFlags;
-
int execute_strv(
const char *name,
- char* const* paths,
+ char * const *paths,
const char *root,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
- void* const callback_args[_STDOUT_CONSUME_MAX],
+ void * const callback_args[_STDOUT_CONSUME_MAX],
char *argv[],
char *envp[],
ExecDirFlags flags);
int execute_directories(
- const char* const* directories,
+ const char * const *directories,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
- void* const callback_args[_STDOUT_CONSUME_MAX],
+ void * const callback_args[_STDOUT_CONSUME_MAX],
char *argv[],
char *envp[],
ExecDirFlags flags);
+extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
+
+typedef enum ExecCommandFlags {
+ EXEC_COMMAND_IGNORE_FAILURE = 1 << 0,
+ EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
+ EXEC_COMMAND_NO_SETUID = 1 << 2,
+ EXEC_COMMAND_AMBIENT_MAGIC = 1 << 3,
+ EXEC_COMMAND_NO_ENV_EXPAND = 1 << 4,
+ _EXEC_COMMAND_FLAGS_INVALID = -EINVAL,
+} ExecCommandFlags;
+
int exec_command_flags_from_strv(char * const *ex_opts, ExecCommandFlags *ret);
int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ret);
-extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
-
const char* exec_command_flags_to_string(ExecCommandFlags i);
ExecCommandFlags exec_command_flags_from_string(const char *s);
diff --git a/src/shared/fdset.c b/src/shared/fdset.c
index cb5a69ef22..b3d3cfa784 100644
--- a/src/shared/fdset.c
+++ b/src/shared/fdset.c
@@ -8,6 +8,7 @@
#include "sd-daemon.h"
#include "alloc-util.h"
+#include "async.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fdset.h"
@@ -51,7 +52,7 @@ int fdset_new_array(FDSet **ret, const int fds[], size_t n_fds) {
return 0;
}
-void fdset_close(FDSet *s) {
+void fdset_close(FDSet *s, bool async) {
void *p;
while ((p = set_steal_first(MAKE_SET(s)))) {
@@ -72,12 +73,21 @@ void fdset_close(FDSet *s) {
log_debug("Closing set fd %i (%s)", fd, strna(path));
}
- (void) close(fd);
+ if (async)
+ (void) asynchronous_close(fd);
+ else
+ (void) close(fd);
}
}
FDSet* fdset_free(FDSet *s) {
- fdset_close(s);
+ fdset_close(s, /* async= */ false);
+ set_free(MAKE_SET(s));
+ return NULL;
+}
+
+FDSet* fdset_free_async(FDSet *s) {
+ fdset_close(s, /* async= */ true);
set_free(MAKE_SET(s));
return NULL;
}
diff --git a/src/shared/fdset.h b/src/shared/fdset.h
index 70a764fb4d..3e69d32146 100644
--- a/src/shared/fdset.h
+++ b/src/shared/fdset.h
@@ -11,6 +11,7 @@ typedef struct FDSet FDSet;
FDSet* fdset_new(void);
FDSet* fdset_free(FDSet *s);
+FDSet* fdset_free_async(FDSet *s);
int fdset_put(FDSet *s, int fd);
int fdset_consume(FDSet *s, int fd);
@@ -36,7 +37,7 @@ int fdset_iterate(FDSet *s, Iterator *i);
int fdset_steal_first(FDSet *fds);
-void fdset_close(FDSet *fds);
+void fdset_close(FDSet *fds, bool async);
#define _FDSET_FOREACH(fd, fds, i) \
for (Iterator i = ITERATOR_FIRST; ((fd) = fdset_iterate((fds), &i)) >= 0; )
@@ -45,3 +46,5 @@ void fdset_close(FDSet *fds);
DEFINE_TRIVIAL_CLEANUP_FUNC(FDSet*, fdset_free);
#define _cleanup_fdset_free_ _cleanup_(fdset_freep)
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(FDSet*, fdset_free_async);
diff --git a/src/shared/fileio-label.c b/src/shared/fileio-label.c
index 572b8f62ad..39de54fa25 100644
--- a/src/shared/fileio-label.c
+++ b/src/shared/fileio-label.c
@@ -6,14 +6,14 @@
#include "fileio.h"
#include "selinux-util.h"
-int write_string_file_atomic_label_ts(const char *fn, const char *line, struct timespec *ts) {
+int write_string_file_full_label(int atfd, const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts) {
int r;
- r = mac_selinux_create_file_prepare(fn, S_IFREG);
+ r = mac_selinux_create_file_prepare_at(atfd, fn, S_IFREG);
if (r < 0)
return r;
- r = write_string_file_ts(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC, ts);
+ r = write_string_file_full(atfd, fn, line, flags, ts);
mac_selinux_create_file_clear();
diff --git a/src/shared/fileio-label.h b/src/shared/fileio-label.h
index 03b4a16417..5aa5c40224 100644
--- a/src/shared/fileio-label.h
+++ b/src/shared/fileio-label.h
@@ -7,9 +7,14 @@
* optimize linking: This way, -lselinux is needed only for the callers of these functions that need selinux, but not
* for all */
-int write_string_file_atomic_label_ts(const char *fn, const char *line, struct timespec *ts);
+#include "fileio.h"
+
+int write_string_file_full_label(int atfd, const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts);
+static inline int write_string_file_at_label(int atfd, const char *fn, const char *line, WriteStringFileFlags flags) {
+ return write_string_file_full_label(atfd, fn, line, flags, /* ts= */ NULL);
+}
static inline int write_string_file_atomic_label(const char *fn, const char *line) {
- return write_string_file_atomic_label_ts(fn, line, NULL);
+ return write_string_file_at_label(AT_FDCWD, fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
}
int create_shutdown_run_nologin_or_warn(void);
diff --git a/src/shared/generator.c b/src/shared/generator.c
index bff44cfc15..b3e57770aa 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -16,6 +16,7 @@
#include "macro.h"
#include "mkdir-label.h"
#include "mountpoint-util.h"
+#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "special.h"
@@ -974,3 +975,25 @@ void log_setup_generator(void) {
log_parse_environment();
log_open();
}
+
+bool generator_soft_rebooted(void) {
+ static int cached = -1;
+ int r;
+
+ if (cached >= 0)
+ return cached;
+
+ const char *e = secure_getenv("SYSTEMD_SOFT_REBOOTS_COUNT");
+ if (!e)
+ return (cached = false);
+
+ unsigned u;
+
+ r = safe_atou(e, &u);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse $SYSTEMD_SOFT_REBOOTS_COUNT, assuming the system hasn't soft-rebooted: %m");
+ return (cached = false);
+ }
+
+ return (cached = u > 0);
+}
diff --git a/src/shared/generator.h b/src/shared/generator.h
index baf1dafca3..f07d7d6b3a 100644
--- a/src/shared/generator.h
+++ b/src/shared/generator.h
@@ -100,6 +100,8 @@ int generator_enable_remount_fs_service(const char *dir);
void log_setup_generator(void);
+bool generator_soft_rebooted(void);
+
/* Similar to DEFINE_MAIN_FUNCTION, but initializes logging and assigns positional arguments. */
#define DEFINE_MAIN_GENERATOR_FUNCTION(impl) \
_DEFINE_MAIN_FUNCTION( \
diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c
index 10550fca20..4e811ba2bf 100644
--- a/src/shared/local-addresses.c
+++ b/src/shared/local-addresses.c
@@ -44,8 +44,8 @@ bool has_local_address(const struct local_address *addresses, size_t n_addresses
assert(addresses || n_addresses == 0);
assert(needle);
- for (size_t i = 0; i < n_addresses; i++)
- if (address_compare(addresses + i, needle) == 0)
+ FOREACH_ARRAY(i, addresses, n_addresses)
+ if (address_compare(i, needle) == 0)
return true;
return false;
@@ -81,7 +81,8 @@ static int add_local_address_full(
uint32_t priority,
uint32_t weight,
int family,
- const union in_addr_union *address) {
+ const union in_addr_union *address,
+ const union in_addr_union *prefsrc) {
assert(list);
assert(n_list);
@@ -99,6 +100,7 @@ static int add_local_address_full(
.weight = weight,
.family = family,
.address = *address,
+ .prefsrc = prefsrc ? *prefsrc : IN_ADDR_NULL,
};
return 1;
@@ -112,7 +114,10 @@ int add_local_address(
int family,
const union in_addr_union *address) {
- return add_local_address_full(list, n_list, ifindex, scope, 0, 0, family, address);
+ return add_local_address_full(
+ list, n_list, ifindex,
+ scope, /* priority = */ 0, /* weight = */ 0,
+ family, address, /* prefsrc = */ NULL);
}
int local_addresses(
@@ -235,9 +240,14 @@ static int add_local_gateway(
uint32_t priority,
uint32_t weight,
int family,
- const union in_addr_union *address) {
-
- return add_local_address_full(list, n_list, ifindex, 0, priority, weight, family, address);
+ const union in_addr_union *address,
+ const union in_addr_union *prefsrc) {
+
+ return add_local_address_full(
+ list, n_list,
+ ifindex,
+ /* scope = */ 0, priority, weight,
+ family, address, prefsrc);
}
static int parse_nexthop_one(
@@ -246,6 +256,7 @@ static int parse_nexthop_one(
bool allow_via,
int family,
uint32_t priority,
+ const union in_addr_union *prefsrc,
const struct rtnexthop *rtnh) {
bool has_gw = false;
@@ -268,7 +279,7 @@ static int parse_nexthop_one(
union in_addr_union a;
memcpy(&a, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family));
- r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, family, &a);
+ r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, family, &a, prefsrc);
if (r < 0)
return r;
@@ -294,7 +305,8 @@ static int parse_nexthop_one(
return -EBADMSG; /* gateway address should be always IPv6. */
r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, via->family,
- &(union in_addr_union) { .in6 = via->address.in6 });
+ &(union in_addr_union) { .in6 = via->address.in6 },
+ /* prefsrc = */ NULL);
if (r < 0)
return r;
@@ -311,6 +323,7 @@ static int parse_nexthops(
bool allow_via,
int family,
uint32_t priority,
+ const union in_addr_union *prefsrc,
const struct rtnexthop *rtnh,
size_t size) {
@@ -334,7 +347,7 @@ static int parse_nexthops(
if (ifindex > 0 && rtnh->rtnh_ifindex != ifindex)
goto next_nexthop;
- r = parse_nexthop_one(list, n_list, allow_via, family, priority, rtnh);
+ r = parse_nexthop_one(list, n_list, allow_via, family, priority, prefsrc, rtnh);
if (r < 0)
return r;
@@ -393,6 +406,7 @@ int local_gateways(
return r;
for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
+ union in_addr_union prefsrc = IN_ADDR_NULL;
uint16_t type;
unsigned char dst_len, src_len, table;
uint32_t ifi = 0, priority = 0;
@@ -439,6 +453,10 @@ int local_gateways(
if (af != AF_UNSPEC && af != family)
continue;
+ r = netlink_message_read_in_addr_union(m, RTA_PREFSRC, family, &prefsrc);
+ if (r < 0 && r != -ENODATA)
+ return r;
+
r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi);
if (r < 0 && r != -ENODATA)
return r;
@@ -453,7 +471,7 @@ int local_gateways(
if (r < 0 && r != -ENODATA)
return r;
if (r >= 0) {
- r = add_local_gateway(&list, &n_list, ifi, priority, 0, family, &gateway);
+ r = add_local_gateway(&list, &n_list, ifi, priority, 0, family, &gateway, &prefsrc);
if (r < 0)
return r;
@@ -474,8 +492,10 @@ int local_gateways(
if (via.family != AF_INET6)
return -EBADMSG;
+ /* Ignore prefsrc, and let's take the source address by socket command, if necessary. */
r = add_local_gateway(&list, &n_list, ifi, priority, 0, via.family,
- &(union in_addr_union) { .in6 = via.address.in6 });
+ &(union in_addr_union) { .in6 = via.address.in6 },
+ /* prefsrc = */ NULL);
if (r < 0)
return r;
}
@@ -490,7 +510,7 @@ int local_gateways(
if (r < 0 && r != -ENODATA)
return r;
if (r >= 0) {
- r = parse_nexthops(&list, &n_list, ifindex, allow_via, family, priority, rta_multipath, rta_len);
+ r = parse_nexthops(&list, &n_list, ifindex, allow_via, family, priority, &prefsrc, rta_multipath, rta_len);
if (r < 0)
return r;
}
@@ -512,7 +532,51 @@ static int add_local_outbound(
int family,
const union in_addr_union *address) {
- return add_local_address_full(list, n_list, ifindex, 0, 0, 0, family, address);
+ return add_local_address_full(
+ list, n_list, ifindex,
+ /* scope = */ 0, /* priority = */ 0, /* weight = */ 0,
+ family, address, /* prefsrc = */ NULL);
+}
+
+static int add_local_outbound_by_prefsrc(
+ struct local_address **list,
+ size_t *n_list,
+ const struct local_address *gateway,
+ const struct local_address *addresses,
+ size_t n_addresses) {
+
+ int r;
+
+ assert(list);
+ assert(n_list);
+ assert(gateway);
+
+ if (!in_addr_is_set(gateway->family, &gateway->prefsrc))
+ return 0;
+
+ /* If the gateway has prefsrc, then let's honor the field. But, check if the address is assigned to
+ * the same interface, like we do with SO_BINDTOINDEX. */
+
+ bool found = false;
+ FOREACH_ARRAY(a, addresses, n_addresses) {
+ if (a->ifindex != gateway->ifindex)
+ continue;
+ if (a->family != gateway->family)
+ continue;
+ if (in_addr_equal(a->family, &a->address, &gateway->prefsrc) <= 0)
+ continue;
+
+ found = true;
+ break;
+ }
+ if (!found)
+ return -EHOSTUNREACH;
+
+ r = add_local_outbound(list, n_list, gateway->ifindex, gateway->family, &gateway->prefsrc);
+ if (r < 0)
+ return r;
+
+ return 1;
}
int local_outbounds(
@@ -521,9 +585,9 @@ int local_outbounds(
int af,
struct local_address **ret) {
- _cleanup_free_ struct local_address *list = NULL, *gateways = NULL;
+ _cleanup_free_ struct local_address *list = NULL, *gateways = NULL, *addresses = NULL;
size_t n_list = 0;
- int r, n_gateways;
+ int r, n_gateways, n_addresses;
/* Determines our default outbound addresses, i.e. the "primary" local addresses we use to talk to IP
* addresses behind the default routes. This is still an address of the local host (i.e. this doesn't
@@ -544,21 +608,31 @@ int local_outbounds(
return 0;
}
- for (int i = 0; i < n_gateways; i++) {
+ n_addresses = local_addresses(context, ifindex, af, &addresses);
+ if (n_addresses < 0)
+ return n_addresses;
+
+ FOREACH_ARRAY(i, gateways, n_gateways) {
_cleanup_close_ int fd = -EBADF;
union sockaddr_union sa;
socklen_t salen;
- fd = socket(gateways[i].family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ r = add_local_outbound_by_prefsrc(&list, &n_list, i, addresses, n_addresses);
+ if (r > 0 || r == -EHOSTUNREACH)
+ continue;
+ if (r < 0)
+ return r;
+
+ fd = socket(i->family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return -errno;
- switch (gateways[i].family) {
+ switch (i->family) {
case AF_INET:
sa.in = (struct sockaddr_in) {
.sin_family = AF_INET,
- .sin_addr = gateways[i].address.in,
+ .sin_addr = i->address.in,
.sin_port = htobe16(53), /* doesn't really matter which port we pick —
* we just care about the routing decision */
};
@@ -568,9 +642,9 @@ int local_outbounds(
case AF_INET6:
sa.in6 = (struct sockaddr_in6) {
.sin6_family = AF_INET6,
- .sin6_addr = gateways[i].address.in6,
+ .sin6_addr = i->address.in6,
.sin6_port = htobe16(53),
- .sin6_scope_id = gateways[i].ifindex,
+ .sin6_scope_id = i->ifindex,
};
break;
@@ -584,18 +658,18 @@ int local_outbounds(
* IP_UNICAST_IF doesn't actually influence the routing decision for UDP — which I think
* should probably just be considered a bug. Once that bug is fixed this is the best API to
* use, since it is the most lightweight. */
- r = socket_set_unicast_if(fd, gateways[i].family, gateways[i].ifindex);
+ r = socket_set_unicast_if(fd, i->family, i->ifindex);
if (r < 0)
- log_debug_errno(r, "Failed to set unicast interface index %i, ignoring: %m", gateways[i].ifindex);
+ log_debug_errno(r, "Failed to set unicast interface index %i, ignoring: %m", i->ifindex);
/* We'll also use SO_BINDTOINDEX. This requires CAP_NET_RAW on old kernels, hence there's a
* good chance this fails. Since 5.7 this restriction was dropped and the first
* SO_BINDTOINDEX on a socket may be done without privileges. This one has the benefit of
* really influencing the routing decision, i.e. this one definitely works for us — as long
* as we have the privileges for it. */
- r = socket_bind_to_ifindex(fd, gateways[i].ifindex);
+ r = socket_bind_to_ifindex(fd, i->ifindex);
if (r < 0)
- log_debug_errno(r, "Failed to bind socket to interface %i, ignoring: %m", gateways[i].ifindex);
+ log_debug_errno(r, "Failed to bind socket to interface %i, ignoring: %m", i->ifindex);
/* Let's now connect() to the UDP socket, forcing the kernel to make a routing decision and
* auto-bind the socket. We ignore failures on this, since that failure might happen for a
@@ -612,16 +686,16 @@ int local_outbounds(
salen = SOCKADDR_LEN(sa);
if (getsockname(fd, &sa.sa, &salen) < 0)
return -errno;
- assert(sa.sa.sa_family == gateways[i].family);
+ assert(sa.sa.sa_family == i->family);
assert(salen == SOCKADDR_LEN(sa));
- switch (gateways[i].family) {
+ switch (i->family) {
case AF_INET:
if (in4_addr_is_null(&sa.in.sin_addr)) /* Auto-binding didn't work. :-( */
continue;
- r = add_local_outbound(&list, &n_list, gateways[i].ifindex, gateways[i].family,
+ r = add_local_outbound(&list, &n_list, i->ifindex, i->family,
&(union in_addr_union) { .in = sa.in.sin_addr });
if (r < 0)
return r;
@@ -631,7 +705,7 @@ int local_outbounds(
if (in6_addr_is_null(&sa.in6.sin6_addr))
continue;
- r = add_local_outbound(&list, &n_list, gateways[i].ifindex, gateways[i].family,
+ r = add_local_outbound(&list, &n_list, i->ifindex, i->family,
&(union in_addr_union) { .in6 = sa.in6.sin6_addr });
if (r < 0)
return r;
diff --git a/src/shared/local-addresses.h b/src/shared/local-addresses.h
index 42abca7838..9e8374fa6f 100644
--- a/src/shared/local-addresses.h
+++ b/src/shared/local-addresses.h
@@ -12,6 +12,7 @@ struct local_address {
uint32_t weight;
int family;
union in_addr_union address;
+ union in_addr_union prefsrc;
};
bool has_local_address(const struct local_address *addresses, size_t n_addresses, const struct local_address *needle);
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 42dd32024a..1141efa453 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -184,6 +184,7 @@ shared_sources = files(
'varlink-io.systemd.Import.c',
'varlink-io.systemd.Journal.c',
'varlink-io.systemd.Machine.c',
+ 'varlink-io.systemd.MachineImage.c',
'varlink-io.systemd.ManagedOOM.c',
'varlink-io.systemd.MountFileSystem.c',
'varlink-io.systemd.NamespaceResource.c',
diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c
index 1353395c89..93f8cf379e 100644
--- a/src/shared/mount-util.c
+++ b/src/shared/mount-util.c
@@ -1463,11 +1463,6 @@ int remount_idmap(
return remount_idmap_fd(p, userns_fd);
}
-typedef struct SubMount {
- char *path;
- int mount_fd;
-} SubMount;
-
static void sub_mount_clear(SubMount *s) {
assert(s);
@@ -1475,7 +1470,7 @@ static void sub_mount_clear(SubMount *s) {
s->mount_fd = safe_close(s->mount_fd);
}
-static void sub_mount_array_free(SubMount *s, size_t n) {
+void sub_mount_array_free(SubMount *s, size_t n) {
assert(s || n == 0);
for (size_t i = 0; i < n; i++)
@@ -1504,10 +1499,7 @@ static void sub_mount_drop(SubMount *s, size_t n) {
}
}
-static int get_sub_mounts(
- const char *prefix,
- SubMount **ret_mounts,
- size_t *ret_n_mounts) {
+int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_mounts) {
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h
index 069378cf4d..765dbf72f8 100644
--- a/src/shared/mount-util.h
+++ b/src/shared/mount-util.h
@@ -12,6 +12,15 @@
#include "macro.h"
#include "pidref.h"
+typedef struct SubMount {
+ char *path;
+ int mount_fd;
+} SubMount;
+
+void sub_mount_array_free(SubMount *s, size_t n);
+
+int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_mounts);
+
int repeat_unmount(const char *path, int flags);
int umount_recursive_full(const char *target, int flags, char **keep);
diff --git a/src/shared/userdb.c b/src/shared/userdb.c
index 557de70ea8..ff83d4bf90 100644
--- a/src/shared/userdb.c
+++ b/src/shared/userdb.c
@@ -171,9 +171,15 @@ static int userdb_on_query_reply(
if (error_id) {
log_debug("Got lookup error: %s", error_id);
+ /* Convert various forms of record not found into -ESRCH, since NSS typically doesn't care,
+ * about the details. Note that if a userName specification is refused as invalid parameter,
+ * we also turn this into -ESRCH following the logic that there cannot be a user record for a
+ * completely invalid user name. */
if (STR_IN_SET(error_id,
"io.systemd.UserDatabase.NoRecordFound",
- "io.systemd.UserDatabase.ConflictingRecordFound"))
+ "io.systemd.UserDatabase.ConflictingRecordFound") ||
+ sd_varlink_error_is_invalid_parameter(error_id, parameters, "userName") ||
+ sd_varlink_error_is_invalid_parameter(error_id, parameters, "groupName"))
r = -ESRCH;
else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable"))
r = -EHOSTDOWN;
diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c
index 7b2bb4969f..6d90f1e6ce 100644
--- a/src/shared/varlink-io.systemd.Machine.c
+++ b/src/shared/varlink-io.systemd.Machine.c
@@ -10,7 +10,7 @@
SD_VARLINK_FIELD_COMMENT("If non-null the name of a machine."), \
SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), \
SD_VARLINK_FIELD_COMMENT("If non-null the PID of a machine. Special value 0 means to take pid of the machine the caller is part of."), \
- SD_VARLINK_DEFINE_INPUT(pid, SD_VARLINK_INT, SD_VARLINK_NULLABLE), \
+ SD_VARLINK_DEFINE_INPUT_BY_TYPE(pid, ProcessId, SD_VARLINK_NULLABLE), \
VARLINK_DEFINE_POLKIT_INPUT
static SD_VARLINK_DEFINE_METHOD(
diff --git a/src/shared/varlink-io.systemd.MachineImage.c b/src/shared/varlink-io.systemd.MachineImage.c
new file mode 100644
index 0000000000..d417af8027
--- /dev/null
+++ b/src/shared/varlink-io.systemd.MachineImage.c
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-varlink-idl.h"
+
+#include "bus-polkit.h"
+#include "varlink-io.systemd.MachineImage.h"
+
+static SD_VARLINK_DEFINE_METHOD_FULL(
+ List,
+ SD_VARLINK_SUPPORTS_MORE,
+ SD_VARLINK_FIELD_COMMENT("If non-null the name of a image to report details on."),
+ SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("If true the output will include image metadata fields such as 'machineInfo' and 'OSRelease'."),
+ SD_VARLINK_DEFINE_INPUT(acquireMetadata, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+ VARLINK_DEFINE_POLKIT_INPUT,
+ SD_VARLINK_FIELD_COMMENT("Name of the image"),
+ SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, 0),
+ SD_VARLINK_FIELD_COMMENT("The file system path where image is stored"),
+ SD_VARLINK_DEFINE_OUTPUT(path, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The type of this image"),
+ SD_VARLINK_DEFINE_OUTPUT(type, SD_VARLINK_STRING, 0),
+ SD_VARLINK_FIELD_COMMENT("The class of this image"),
+ SD_VARLINK_DEFINE_OUTPUT(class, SD_VARLINK_STRING, 0),
+ SD_VARLINK_FIELD_COMMENT("Whether the image is read-only"),
+ SD_VARLINK_DEFINE_OUTPUT(readOnly, SD_VARLINK_BOOL, 0),
+ SD_VARLINK_FIELD_COMMENT("The image creation timestamp"),
+ SD_VARLINK_DEFINE_OUTPUT(creationTimestamp, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The image creation timestamp"),
+ SD_VARLINK_DEFINE_OUTPUT(modificationTimestamp, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The image creation timestamp"),
+ SD_VARLINK_DEFINE_OUTPUT(usage, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The image disk usage (exclusive)"),
+ SD_VARLINK_DEFINE_OUTPUT(usageExclusive, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The image disk usage (exclusive)"),
+ SD_VARLINK_DEFINE_OUTPUT(limit, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The image disk usage limit (exclusive)"),
+ SD_VARLINK_DEFINE_OUTPUT(limitExclusive, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The hostname of the image"),
+ SD_VARLINK_DEFINE_OUTPUT(hostname, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The machine ID of the image"),
+ SD_VARLINK_DEFINE_OUTPUT(machineId, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("Machine info information of an image. It contains an array of key value pairs read from the machine-info(5) file in the image."),
+ SD_VARLINK_DEFINE_OUTPUT(machineInfo, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
+ SD_VARLINK_FIELD_COMMENT("OS release information of an image. It contains an array of key value pairs read from the os-release(5) file in the image."),
+ SD_VARLINK_DEFINE_OUTPUT(OSRelease, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY));
+
+static SD_VARLINK_DEFINE_METHOD(
+ Update,
+ SD_VARLINK_FIELD_COMMENT("The name of a image to update."),
+ SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0),
+ SD_VARLINK_FIELD_COMMENT("If non-null the new name of the image"),
+ SD_VARLINK_DEFINE_INPUT(newName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("If non-null value of the read-only flag of the image"),
+ SD_VARLINK_DEFINE_INPUT(readOnly, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("If non-null value of image quota limit"),
+ SD_VARLINK_DEFINE_INPUT(limit, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ VARLINK_DEFINE_POLKIT_INPUT);
+
+static SD_VARLINK_DEFINE_ERROR(NoSuchImage);
+
+SD_VARLINK_DEFINE_INTERFACE(
+ io_systemd_MachineImage,
+ "io.systemd.MachineImage",
+ SD_VARLINK_SYMBOL_COMMENT("List images"),
+ &vl_method_List,
+ SD_VARLINK_SYMBOL_COMMENT("Update image allowing to rename or toggle read-only flag"),
+ &vl_method_Update,
+ SD_VARLINK_SYMBOL_COMMENT("No matching image exists"),
+ &vl_error_NoSuchImage);
diff --git a/src/shared/varlink-io.systemd.MachineImage.h b/src/shared/varlink-io.systemd.MachineImage.h
new file mode 100644
index 0000000000..91e21f6f99
--- /dev/null
+++ b/src/shared/varlink-io.systemd.MachineImage.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-varlink-idl.h"
+
+extern const sd_varlink_interface vl_interface_io_systemd_MachineImage;
diff --git a/src/shared/varlink-io.systemd.Resolve.c b/src/shared/varlink-io.systemd.Resolve.c
index 484a0ccdc7..48e5e12e87 100644
--- a/src/shared/varlink-io.systemd.Resolve.c
+++ b/src/shared/varlink-io.systemd.Resolve.c
@@ -4,8 +4,11 @@
SD_VARLINK_DEFINE_STRUCT_TYPE(
ResourceKey,
+ SD_VARLINK_FIELD_COMMENT("The RR class, almost always IN, i.e 0x01."),
SD_VARLINK_DEFINE_FIELD(class, SD_VARLINK_INT, 0),
+ SD_VARLINK_FIELD_COMMENT("The RR types, one of A, AAAA, PTR, …"),
SD_VARLINK_DEFINE_FIELD(type, SD_VARLINK_INT, 0),
+ SD_VARLINK_FIELD_COMMENT("The domain name."),
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, 0));
SD_VARLINK_DEFINE_STRUCT_TYPE(
@@ -79,12 +82,19 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
static SD_VARLINK_DEFINE_METHOD(
ResolveHostname,
+ SD_VARLINK_FIELD_COMMENT("The Linux interface index for the network interface to search on. Typically left unspecified, in order to search on all interfaces."),
SD_VARLINK_DEFINE_INPUT(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The host name to resolve."),
SD_VARLINK_DEFINE_INPUT(name, SD_VARLINK_STRING, 0),
+ SD_VARLINK_FIELD_COMMENT("The address family to search for, one of AF_INET or AF_INET6."),
SD_VARLINK_DEFINE_INPUT(family, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("Various search flags."),
SD_VARLINK_DEFINE_INPUT(flags, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("Array of resolved IP addresses"),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(addresses, ResolvedAddress, SD_VARLINK_ARRAY),
+ SD_VARLINK_FIELD_COMMENT("Canonical name of the host."),
SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, 0),
+ SD_VARLINK_FIELD_COMMENT("Various flags indicating details on discovered data."),
SD_VARLINK_DEFINE_OUTPUT(flags, SD_VARLINK_INT, 0));
static SD_VARLINK_DEFINE_STRUCT_TYPE(
@@ -94,8 +104,11 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
static SD_VARLINK_DEFINE_METHOD(
ResolveAddress,
+ SD_VARLINK_FIELD_COMMENT("The Linux interface index for the network interface to search on. Typically left unspecified, in order to search on all interfaces."),
SD_VARLINK_DEFINE_INPUT(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("The address family of the specified address, one of AF_INET or AF_INET6."),
SD_VARLINK_DEFINE_INPUT(family, SD_VARLINK_INT, 0),
+ SD_VARLINK_FIELD_COMMENT("The IP address to look up, either 4 or 16 integers (depending if an AF_INET or AF_INET6 address shall be resolved)."),
SD_VARLINK_DEFINE_INPUT(address, SD_VARLINK_INT, SD_VARLINK_ARRAY),
SD_VARLINK_DEFINE_INPUT(flags, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(names, ResolvedName, SD_VARLINK_ARRAY),
@@ -177,18 +190,27 @@ static SD_VARLINK_DEFINE_ERROR(InconsistentServiceRecords);
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_Resolve,
"io.systemd.Resolve",
+ SD_VARLINK_SYMBOL_COMMENT("Resolves a hostname to one or more IP addresses."),
&vl_method_ResolveHostname,
+ SD_VARLINK_SYMBOL_COMMENT("Resolves an IP address to a hostname."),
&vl_method_ResolveAddress,
SD_VARLINK_SYMBOL_COMMENT("Resolves a named DNS-SD or unnamed simple SRV service."),
&vl_method_ResolveService,
+ SD_VARLINK_SYMBOL_COMMENT("Resolves a domain name to one or more DNS resource records."),
&vl_method_ResolveRecord,
+ SD_VARLINK_SYMBOL_COMMENT("Encapsulates a resolved address."),
&vl_type_ResolvedAddress,
+ SD_VARLINK_SYMBOL_COMMENT("Encapsulates a resolved host name."),
&vl_type_ResolvedName,
+ SD_VARLINK_SYMBOL_COMMENT("Encapsulates resolved service information."),
&vl_type_ResolvedService,
- SD_VARLINK_SYMBOL_COMMENT("Encodes the canonical name, type and domain of a DNS-SD or simple SRV service. Note that due to CNAME redirections and similar, a named DNS-SD service might resolve to a canonical service that is an unnamed simple SRV service. Or in other words: resolving a named service might return an unnamed canonical service."),
+ SD_VARLINK_SYMBOL_COMMENT("Encapsulates the canonical name, type and domain of a DNS-SD or simple SRV service. Note that due to CNAME redirections and similar, a named DNS-SD service might resolve to a canonical service that is an unnamed simple SRV service. Or in other words: resolving a named service might return an unnamed canonical service."),
&vl_type_ResolvedCanonical,
+ SD_VARLINK_SYMBOL_COMMENT("The 'key' part of a DNS resource record."),
&vl_type_ResourceKey,
+ SD_VARLINK_SYMBOL_COMMENT("A full DNS resource record.."),
&vl_type_ResourceRecord,
+ SD_VARLINK_SYMBOL_COMMENT("Encapsulates information about a resolved DNS resource record "),
&vl_type_ResolvedRecord,
&vl_error_NoNameServers,
&vl_error_NoSuchResourceRecord,
diff --git a/src/shared/varlink-io.systemd.oom.c b/src/shared/varlink-io.systemd.oom.c
index 67beb6b780..350b933d03 100644
--- a/src/shared/varlink-io.systemd.oom.c
+++ b/src/shared/varlink-io.systemd.oom.c
@@ -12,7 +12,8 @@ SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD(mode, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_FIELD(path, SD_VARLINK_STRING, 0),
SD_VARLINK_DEFINE_FIELD(property, SD_VARLINK_STRING, 0),
- SD_VARLINK_DEFINE_FIELD(limit, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
+ SD_VARLINK_DEFINE_FIELD(limit, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
+ SD_VARLINK_DEFINE_FIELD(duration, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
ReportManagedOOMCGroups,
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
index f6b38d6615..fcf29a99d3 100644
--- a/src/sysext/sysext.c
+++ b/src/sysext/sysext.c
@@ -273,6 +273,52 @@ static int need_reload(
return false;
}
+static int move_submounts(const char *src, const char *dst) {
+ SubMount *submounts = NULL;
+ size_t n_submounts = 0;
+ int r;
+
+ assert(src);
+ assert(dst);
+
+ CLEANUP_ARRAY(submounts, n_submounts, sub_mount_array_free);
+
+ r = get_sub_mounts(src, &submounts, &n_submounts);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get submounts for %s: %m", src);
+
+ FOREACH_ARRAY(m, submounts, n_submounts) {
+ _cleanup_free_ char *t = NULL;
+ const char *suffix;
+ struct stat st;
+
+ assert_se(suffix = path_startswith(m->path, src));
+
+ t = path_join(dst, suffix);
+ if (!t)
+ return log_oom();
+
+ if (fstat(m->mount_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat %s: %m", m->path);
+
+ r = mkdir_parents(t, 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create parent directories of %s: %m", t);
+
+ r = make_mount_point_inode_from_stat(&st, t, 0755);
+ if (r < 0 && r != -EEXIST)
+ return log_error_errno(r, "Failed to create mountpoint %s: %m", t);
+
+ r = mount_follow_verbose(LOG_ERR, m->path, t, NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0)
+ return r;
+
+ (void) umount_verbose(LOG_WARNING, m->path, MNT_DETACH);
+ }
+
+ return 0;
+}
+
static int daemon_reload(void) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
@@ -284,11 +330,10 @@ static int daemon_reload(void) {
return bus_service_manager_reload(bus);
}
-static int unmerge_hierarchy(
- ImageClass image_class,
- const char *p) {
+static int unmerge_hierarchy(ImageClass image_class, const char *p, const char *submounts_path) {
_cleanup_free_ char *dot_dir = NULL, *work_dir_info_file = NULL;
+ int n_unmerged = 0;
int r;
assert(p);
@@ -338,6 +383,12 @@ static int unmerge_hierarchy(
return log_error_errno(r, "Failed to unmount '%s': %m", dot_dir);
}
+ /* After we've unmounted the metadata directory, save all other submounts so we can restore
+ * them after unmerging the hierarchy. */
+ r = move_submounts(p, submounts_path);
+ if (r < 0)
+ return r;
+
r = umount_verbose(LOG_ERR, p, MNT_DETACH|UMOUNT_NOFOLLOW);
if (r < 0)
return r;
@@ -349,43 +400,94 @@ static int unmerge_hierarchy(
}
log_info("Unmerged '%s'.", p);
+ n_unmerged++;
}
- return 0;
+ return n_unmerged;
}
-static int unmerge(
+static int unmerge_subprocess(
ImageClass image_class,
char **hierarchies,
- bool no_reload) {
+ const char *workspace) {
int r, ret = 0;
- bool need_to_reload;
- r = need_reload(image_class, hierarchies, no_reload);
+ assert(workspace);
+ assert(path_startswith(workspace, "/run/"));
+
+ /* Mark the whole of /run as MS_SLAVE, so that we can mount stuff below it that doesn't show up on
+ * the host otherwise. */
+ r = mount_nofollow_verbose(LOG_ERR, NULL, "/run", NULL, MS_SLAVE|MS_REC, NULL);
if (r < 0)
return r;
- need_to_reload = r > 0;
- STRV_FOREACH(p, hierarchies) {
- _cleanup_free_ char *resolved = NULL;
+ /* Let's create the workspace if it's missing */
+ r = mkdir_p(workspace, 0700);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create '%s': %m", workspace);
- r = chase(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
+ STRV_FOREACH(h, hierarchies) {
+ _cleanup_free_ char *submounts_path = NULL, *resolved = NULL;
+
+ submounts_path = path_join(workspace, "submounts", *h);
+ if (!submounts_path)
+ return log_oom();
+
+ r = chase(*h, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
if (r == -ENOENT) {
- log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *p);
+ log_debug_errno(r, "Hierarchy '%s%s' does not exist, ignoring.", strempty(arg_root), *h);
continue;
}
if (r < 0) {
- log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *p);
- if (ret == 0)
- ret = r;
+ RET_GATHER(ret, log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *h));
+ continue;
+ }
+ r = unmerge_hierarchy(image_class, resolved, submounts_path);
+ if (r < 0) {
+ RET_GATHER(ret, r);
continue;
}
+ if (r == 0)
+ continue;
- r = unmerge_hierarchy(image_class, resolved);
- if (r < 0 && ret == 0)
- ret = r;
+ /* If we unmerged something, then we have to move the submounts from the hierarchy back into
+ * place in the host's original hierarchy. */
+
+ r = move_submounts(submounts_path, resolved);
+ if (r < 0)
+ return r;
+ }
+
+ return ret;
+}
+
+static int unmerge(
+ ImageClass image_class,
+ char **hierarchies,
+ bool no_reload) {
+
+ bool need_to_reload;
+ int r;
+
+ r = need_reload(image_class, hierarchies, no_reload);
+ if (r < 0)
+ return r;
+ need_to_reload = r > 0;
+
+ r = safe_fork("(sd-unmerge)", FORK_WAIT|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_NEW_MOUNTNS, /* ret_pid= */ NULL);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child with its own mount namespace */
+
+ r = unmerge_subprocess(image_class, hierarchies, "/run/systemd/sysext");
+
+ /* Our namespace ceases to exist here, also implicitly detaching all temporary mounts we
+ * created below /run. Nice! */
+
+ _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
if (need_to_reload) {
@@ -394,7 +496,7 @@ static int unmerge(
return r;
}
- return ret;
+ return 0;
}
static int verb_unmerge(int argc, char **argv, void *userdata) {
@@ -1483,6 +1585,8 @@ static int merge_subprocess(
Image *img;
int r;
+ assert(path_startswith(workspace, "/run/"));
+
/* Mark the whole of /run as MS_SLAVE, so that we can mount stuff below it that doesn't show up on
* the host otherwise. */
r = mount_nofollow_verbose(LOG_ERR, NULL, "/run", NULL, MS_SLAVE|MS_REC, NULL);
@@ -1711,20 +1815,33 @@ static int merge_subprocess(
/* Let's now unmerge the status quo ante, since to build the new overlayfs we need a reference to the
* underlying fs. */
STRV_FOREACH(h, hierarchies) {
- _cleanup_free_ char *resolved = NULL;
+ _cleanup_free_ char *submounts_path = NULL, *resolved = NULL;
+
+ submounts_path = path_join(workspace, "submounts", *h);
+ if (!submounts_path)
+ return log_oom();
r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
if (r < 0)
return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(arg_root), *h);
- r = unmerge_hierarchy(image_class, resolved);
+ r = unmerge_hierarchy(image_class, resolved, submounts_path);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ /* If we didn't unmerge anything, then we have to move the submounts from the host's
+ * original hierarchy. */
+
+ r = move_submounts(resolved, submounts_path);
if (r < 0)
return r;
}
/* Create overlayfs mounts for all hierarchies */
STRV_FOREACH(h, hierarchies) {
- _cleanup_free_ char *meta_path = NULL, *overlay_path = NULL, *merge_hierarchy_workspace = NULL;
+ _cleanup_free_ char *meta_path = NULL, *overlay_path = NULL, *merge_hierarchy_workspace = NULL, *submounts_path = NULL;
meta_path = path_join(workspace, "meta", *h); /* The place where to store metadata about this instance */
if (!meta_path)
@@ -1739,6 +1856,10 @@ static int merge_subprocess(
if (!merge_hierarchy_workspace)
return log_oom();
+ submounts_path = path_join(workspace, "submounts", *h);
+ if (!submounts_path)
+ return log_oom();
+
r = merge_hierarchy(
image_class,
*h,
@@ -1750,6 +1871,13 @@ static int merge_subprocess(
merge_hierarchy_workspace);
if (r < 0)
return r;
+
+ /* After the new hierarchy is set up, move the submounts from the original hierarchy into
+ * place. */
+
+ r = move_submounts(submounts_path, overlay_path);
+ if (r < 0)
+ return r;
}
/* And move them all into place. This is where things appear in the host namespace */
diff --git a/src/systemd/sd-json.h b/src/systemd/sd-json.h
index a73eeb8dd6..f3c9a27257 100644
--- a/src/systemd/sd-json.h
+++ b/src/systemd/sd-json.h
@@ -328,7 +328,6 @@ int sd_json_dispatch_int8(const char *name, sd_json_variant *variant, sd_json_di
int sd_json_dispatch_uint8(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int sd_json_dispatch_double(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int sd_json_dispatch_uid_gid(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
-int sd_json_dispatch_pid(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int sd_json_dispatch_id128(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int sd_json_dispatch_signal(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int sd_json_dispatch_unsupported(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h
index 3120dd7f00..6c1c4154e2 100644
--- a/src/systemd/sd-radv.h
+++ b/src/systemd/sd-radv.h
@@ -32,11 +32,7 @@
_SD_BEGIN_DECLARATIONS;
typedef struct sd_radv sd_radv;
-typedef struct sd_radv_prefix sd_radv_prefix;
-typedef struct sd_radv_route_prefix sd_radv_route_prefix;
-typedef struct sd_radv_pref64_prefix sd_radv_pref64_prefix;
-/* Router Advertisement */
int sd_radv_new(sd_radv **ret);
sd_radv *sd_radv_ref(sd_radv *ra);
sd_radv *sd_radv_unref(sd_radv *ra);
@@ -54,8 +50,8 @@ int sd_radv_set_ifindex(sd_radv *ra, int interface_index);
int sd_radv_set_ifname(sd_radv *ra, const char *interface_name);
int sd_radv_get_ifname(sd_radv *ra, const char **ret);
int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr);
-int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
-int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu);
+
+/* RA header */
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit);
int sd_radv_set_reachable_time(sd_radv *ra, uint64_t usec);
int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec);
@@ -63,51 +59,65 @@ int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec);
int sd_radv_set_managed_information(sd_radv *ra, int b);
int sd_radv_set_other_information(sd_radv *ra, int b);
int sd_radv_set_preference(sd_radv *ra, uint8_t preference);
-int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
-int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p);
-int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p);
-void sd_radv_remove_prefix(sd_radv *ra, const struct in6_addr *prefix, unsigned char prefixlen);
-int sd_radv_set_rdnss(sd_radv *ra, uint64_t lifetime_usec,
- const struct in6_addr *dns, size_t n_dns);
-int sd_radv_set_dnssl(sd_radv *ra, uint64_t lifetime_usec, char **search_list);
-
-/* Advertised prefixes */
-int sd_radv_prefix_new(sd_radv_prefix **ret);
-sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *ra);
-sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *ra);
-
-int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr,
- unsigned char prefixlen);
-int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr,
- unsigned char *ret_prefixlen);
-int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink);
-int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
- int address_autoconfiguration);
-int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until);
-int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, uint64_t lifetime_usec, uint64_t valid_until);
-
-int sd_radv_route_prefix_new(sd_radv_route_prefix **ret);
-sd_radv_route_prefix *sd_radv_route_prefix_ref(sd_radv_route_prefix *ra);
-sd_radv_route_prefix *sd_radv_route_prefix_unref(sd_radv_route_prefix *ra);
-
-int sd_radv_route_prefix_set_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr, unsigned char prefixlen);
-int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint64_t lifetime_usec, uint64_t valid_until);
-
-int sd_radv_pref64_prefix_new(sd_radv_pref64_prefix **ret);
-int sd_radv_pref64_prefix_set_prefix(sd_radv_pref64_prefix *p, const struct in6_addr *prefix,
- uint8_t prefixlen, uint64_t lifetime_usec);
-sd_radv_pref64_prefix *sd_radv_pref64_prefix_ref(sd_radv_pref64_prefix *ra);
-sd_radv_pref64_prefix *sd_radv_pref64_prefix_unref(sd_radv_pref64_prefix *ra);
-
-/* Mobile IPv6 extension: Home Agent Info. */
-int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent);
-int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference);
-int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t usec);
+
+/* Options */
+int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
+void sd_radv_unset_mac(sd_radv *ra);
+int sd_radv_add_prefix(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint8_t flags,
+ uint64_t valid_lifetime_usec,
+ uint64_t preferred_lifetime_usec,
+ uint64_t valid_until,
+ uint64_t preferred_until);
+void sd_radv_remove_prefix(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen);
+int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu);
+void sd_radv_unset_mtu(sd_radv *ra);
+int sd_radv_set_home_agent(sd_radv *ra, uint16_t preference, uint64_t lifetime_usec, uint64_t valid_until);
+void sd_radv_unset_home_agent(sd_radv *ra);
+int sd_radv_add_route(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint8_t preference,
+ uint64_t lifetime_usec,
+ uint64_t valid_until);
+void sd_radv_remove_route(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen);
+int sd_radv_add_rdnss(
+ sd_radv *ra,
+ size_t n_dns,
+ const struct in6_addr *dns,
+ uint64_t lifetime_usec,
+ uint64_t valid_until);
+void sd_radv_clear_rdnss(sd_radv *ra);
+int sd_radv_add_dnssl(
+ sd_radv *ra,
+ char * const *domains,
+ uint64_t lifetime_usec,
+ uint64_t valid_until);
+void sd_radv_clear_dnssl(sd_radv *ra);
+int sd_radv_set_captive_portal(sd_radv *ra, const char *portal);
+void sd_radv_unset_captive_portal(sd_radv *ra);
+int sd_radv_add_prefix64(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen,
+ uint64_t lifetime_usec,
+ uint64_t valid_until);
+void sd_radv_remove_prefix64(
+ sd_radv *ra,
+ const struct in6_addr *prefix,
+ uint8_t prefixlen);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_prefix, sd_radv_prefix_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_route_prefix, sd_radv_route_prefix_unref);
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_pref64_prefix, sd_radv_pref64_prefix_unref);
_SD_END_DECLARATIONS;
diff --git a/src/systemd/sd-varlink.h b/src/systemd/sd-varlink.h
index 4c943d5389..4596561ed3 100644
--- a/src/systemd/sd-varlink.h
+++ b/src/systemd/sd-varlink.h
@@ -266,6 +266,8 @@ int sd_varlink_invocation(sd_varlink_invocation_flags_t flags);
int sd_varlink_error_to_errno(const char *error, sd_json_variant *parameters);
+int sd_varlink_error_is_invalid_parameter(const char *error, sd_json_variant *parameter, const char *name);
+
/* Define helpers so that __attribute__((cleanup(sd_varlink_unrefp))) and similar may be used. */
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_varlink, sd_varlink_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_varlink, sd_varlink_close_unref);
diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c
index 490466228d..2105f134eb 100644
--- a/src/sysupdate/sysupdate.c
+++ b/src/sysupdate/sysupdate.c
@@ -732,7 +732,7 @@ static int context_show_version(Context *c, const char *version) {
SD_JSON_BUILD_PAIR_BOOLEAN("obsolete", FLAGS_SET(us->flags, UPDATE_OBSOLETE)),
SD_JSON_BUILD_PAIR_BOOLEAN("protected", FLAGS_SET(us->flags, UPDATE_PROTECTED)),
SD_JSON_BUILD_PAIR_BOOLEAN("incomplete", FLAGS_SET(us->flags, UPDATE_INCOMPLETE)),
- SD_JSON_BUILD_PAIR_STRV("changelog_urls", changelog_urls),
+ SD_JSON_BUILD_PAIR_STRV("changelogUrls", changelog_urls),
SD_JSON_BUILD_PAIR_VARIANT("contents", t_json));
if (r < 0)
return log_error_errno(r, "Failed to create JSON: %m");
@@ -1083,7 +1083,7 @@ static int verb_list(int argc, char **argv, void *userdata) {
r = sd_json_buildo(&json, SD_JSON_BUILD_PAIR_STRING("current", current),
SD_JSON_BUILD_PAIR_STRV("all", versions),
- SD_JSON_BUILD_PAIR_STRV("appstream_urls", appstream_urls));
+ SD_JSON_BUILD_PAIR_STRV("appstreamUrls", appstream_urls));
if (r < 0)
return log_error_errno(r, "Failed to create JSON: %m");
diff --git a/src/sysupdate/sysupdated.c b/src/sysupdate/sysupdated.c
index 8709c60b78..dc166ec40e 100644
--- a/src/sysupdate/sysupdated.c
+++ b/src/sysupdate/sysupdated.c
@@ -1215,13 +1215,13 @@ static int target_get_appstream(Target *t, char ***ret) {
if (r < 0)
return log_error_errno(r, "Failed to run 'systemd-sysupdate list': %m");
- appstream_url_json = sd_json_variant_by_key(v, "appstream_urls");
+ appstream_url_json = sd_json_variant_by_key(v, "appstreamUrls");
if (!appstream_url_json)
- return log_sysupdate_bad_json(SYNTHETIC_ERRNO(EPROTO), "list", "Missing key 'appstream_urls'");
+ return log_sysupdate_bad_json(SYNTHETIC_ERRNO(EPROTO), "list", "Missing key 'appstreamUrls'");
r = sd_json_variant_strv(appstream_url_json, ret);
if (r < 0)
- return log_sysupdate_bad_json(SYNTHETIC_ERRNO(EPROTO), "list", "Key 'appstream_urls' should be strv");
+ return log_sysupdate_bad_json(SYNTHETIC_ERRNO(EPROTO), "list", "Key 'appstreamUrls' should be strv");
return 0;
}
diff --git a/src/sysupdate/updatectl.c b/src/sysupdate/updatectl.c
index cea1274623..9b65bbb42a 100644
--- a/src/sysupdate/updatectl.c
+++ b/src/sysupdate/updatectl.c
@@ -358,15 +358,15 @@ static int parse_describe(sd_bus_message *reply, Version *ret) {
assert(sd_json_variant_is_object(json));
static const sd_json_dispatch_field dispatch_table[] = {
- { "version", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DescribeParams, v.version), 0 },
- { "newest", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, newest), 0 },
- { "available", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, available), 0 },
- { "installed", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, installed), 0 },
- { "obsolete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, obsolete), 0 },
- { "protected", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, protected), 0 },
- { "incomplete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, incomplete), 0 },
- { "changelog_urls", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(DescribeParams, v.changelog), 0 },
- { "contents", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(DescribeParams, contents_json), 0 },
+ { "version", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(DescribeParams, v.version), 0 },
+ { "newest", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, newest), 0 },
+ { "available", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, available), 0 },
+ { "installed", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, installed), 0 },
+ { "obsolete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, obsolete), 0 },
+ { "protected", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, protected), 0 },
+ { "incomplete", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(DescribeParams, incomplete), 0 },
+ { "changelogUrls", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(DescribeParams, v.changelog), 0 },
+ { "contents", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant, offsetof(DescribeParams, contents_json), 0 },
{},
};
@@ -1240,7 +1240,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "hH:", options, NULL)) >= 0) {
switch (c) {
case 'h':
diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c
index 301e02db0b..8ccede252a 100644
--- a/src/test/test-exec-util.c
+++ b/src/test/test-exec-util.c
@@ -402,7 +402,9 @@ TEST(error_catching) {
if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
return;
- r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL, EXEC_DIR_NONE);
+ r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC,
+ /* callbacks = */ NULL, /* callback_args = */ NULL,
+ /* argv = */ NULL, /* envp = */ NULL, /* flags = */ 0);
/* we should exit with the error code of the first script that failed */
assert_se(r == 42);
diff --git a/src/test/test-json.c b/src/test/test-json.c
index 32cd285841..8dd5746495 100644
--- a/src/test/test-json.c
+++ b/src/test/test-json.c
@@ -17,6 +17,7 @@
#include "string-util.h"
#include "strv.h"
#include "tests.h"
+#include "tmpfile-util.h"
static void test_tokenizer_one(const char *data, ...) {
unsigned line = 0, column = 0;
@@ -1271,24 +1272,34 @@ TEST(pidref) {
assert_se(pidref_set_pid(&pid1, 1) >= 0);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+ sd_id128_t randomized_boot_id;
+ assert_se(sd_id128_randomize(&randomized_boot_id) >= 0);
assert_se(sd_json_buildo(&v,
JSON_BUILD_PAIR_PIDREF("myself", &myself),
- JSON_BUILD_PAIR_PIDREF("pid1", &pid1)) >= 0);
+ JSON_BUILD_PAIR_PIDREF("pid1", &pid1),
+ SD_JSON_BUILD_PAIR("remote", SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR_UNSIGNED("pid", 1),
+ SD_JSON_BUILD_PAIR_UNSIGNED("pidfdId", 4711),
+ SD_JSON_BUILD_PAIR_ID128("bootId", randomized_boot_id))),
+ SD_JSON_BUILD_PAIR("automatic", SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR_UNSIGNED("pid", 0)))) >= 0);
sd_json_variant_dump(v, SD_JSON_FORMAT_COLOR|SD_JSON_FORMAT_PRETTY, NULL, NULL);
struct {
- PidRef myself, pid1;
+ PidRef myself, pid1, remote, automatic;
} data = {
.myself = PIDREF_NULL,
.pid1 = PIDREF_NULL,
+ .remote = PIDREF_NULL,
+ .automatic = PIDREF_NULL,
};
assert_se(sd_json_dispatch(
v,
(const sd_json_dispatch_field[]) {
- { "myself", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, voffsetof(data, myself), 0 },
- { "pid1", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, voffsetof(data, pid1), 0 },
+ { "myself", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, voffsetof(data, myself), SD_JSON_STRICT },
+ { "pid1", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, voffsetof(data, pid1), SD_JSON_STRICT },
+ { "remote", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, voffsetof(data, remote), 0 },
+ { "automatic", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, voffsetof(data, automatic), SD_JSON_RELAX },
{},
},
/* flags= */ 0,
@@ -1299,12 +1310,113 @@ TEST(pidref) {
assert_se(!pidref_equal(&myself, &data.pid1));
assert_se(!pidref_equal(&pid1, &data.myself));
+ assert_se(!pidref_equal(&myself, &data.remote));
+ assert_se(!pidref_equal(&pid1, &data.remote));
assert_se((myself.fd_id > 0) == (data.myself.fd_id > 0));
assert_se((pid1.fd_id > 0) == (data.pid1.fd_id > 0));
+ assert_se(!pidref_is_set(&data.automatic));
+ assert_se(pidref_is_automatic(&data.automatic));
+ assert_se(pidref_is_set(&data.remote));
+ assert_se(pidref_is_remote(&data.remote));
+
pidref_done(&data.myself);
pidref_done(&data.pid1);
+ pidref_done(&data.remote);
+}
+
+TEST(devnum) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+ dev_t dev = makedev(123, 456), parsed;
+
+ ASSERT_OK(json_variant_new_devnum(&v, dev));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ ASSERT_OK(json_dispatch_devnum("devnum", v, /* flags= */ 0, &parsed));
+ ASSERT_EQ(major(parsed), major(dev));
+ ASSERT_EQ(minor(parsed), minor(dev));
+ v = sd_json_variant_unref(v);
+
+ dev = makedev(1 << 12, 456);
+ ASSERT_OK(json_variant_new_devnum(&v, dev));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ ASSERT_FAIL(json_dispatch_devnum("devnum", v, /* flags= */ 0, &parsed));
+ v = sd_json_variant_unref(v);
+
+ dev = makedev(123, 1 << 20);
+ ASSERT_OK(json_variant_new_devnum(&v, dev));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ ASSERT_FAIL(json_dispatch_devnum("devnum", v, /* flags= */ 0, &parsed));
+}
+
+TEST(fd_info) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ _cleanup_close_ int fd = -EBADF;
+
+ /* directories */
+ ASSERT_OK(json_variant_new_fd_info(&v, AT_FDCWD));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ v = sd_json_variant_unref(v);
+
+ ASSERT_OK_ERRNO(fd = openat(AT_FDCWD, ".", O_CLOEXEC | O_DIRECTORY | O_PATH));
+ ASSERT_OK(json_variant_new_fd_info(&v, fd));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ v = sd_json_variant_unref(v);
+ fd = safe_close(fd);
+
+ /* regular file */
+ ASSERT_OK(fd = open_tmpfile_unlinkable(NULL, O_RDWR));
+ ASSERT_OK(json_variant_new_fd_info(&v, fd));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ v = sd_json_variant_unref(v);
+ fd = safe_close(fd);
+
+ fd = open("/sys/class/net/lo/uevent", O_CLOEXEC | O_PATH);
+ if (fd >= 0) {
+ ASSERT_OK(json_variant_new_fd_info(&v, fd));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ v = sd_json_variant_unref(v);
+ fd = safe_close(fd);
+ }
+
+ /* block device */
+ fd = open("/dev/sda", O_CLOEXEC | O_PATH);
+ if (fd >= 0) {
+ ASSERT_OK(json_variant_new_fd_info(&v, fd));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ v = sd_json_variant_unref(v);
+ fd = safe_close(fd);
+ }
+
+ /* stream */
+ ASSERT_OK(json_variant_new_fd_info(&v, fileno(stdout)));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ v = sd_json_variant_unref(v);
+
+ /* socket */
+ ASSERT_OK_ERRNO(fd = socket(AF_INET, SOCK_DGRAM, 0));
+ ASSERT_OK(json_variant_new_fd_info(&v, fd));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ v = sd_json_variant_unref(v);
+ fd = safe_close(fd);
+
+ /* pidfd */
+ ASSERT_OK(pidref_set_pid(&pidref, 0));
+ if (pidref.fd >= 0) {
+ ASSERT_OK(json_variant_new_fd_info(&v, pidref.fd));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ v = sd_json_variant_unref(v);
+ }
+ pidref_done(&pidref);
+
+ ASSERT_OK(pidref_set_pid(&pidref, 1));
+ if (pidref.fd >= 0) {
+ ASSERT_OK(json_variant_new_fd_info(&v, pidref.fd));
+ ASSERT_OK(sd_json_variant_dump(v, SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO, NULL, NULL));
+ v = sd_json_variant_unref(v);
+ }
+ pidref_done(&pidref);
}
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/test/test-local-addresses.c b/src/test/test-local-addresses.c
index f418f3712d..d4a2a6a097 100644
--- a/src/test/test-local-addresses.c
+++ b/src/test/test-local-addresses.c
@@ -153,12 +153,13 @@ static void check_local_gateways(sd_netlink *rtnl, int ifindex, int request_ifin
}) == IN_SET(family, AF_UNSPEC, AF_INET6));
}
-static void check_local_outbounds(sd_netlink *rtnl, int ifindex, int request_ifindex, int family) {
+static void check_local_outbounds(sd_netlink *rtnl, int ifindex, int request_ifindex, int family, const char *ipv6_expected) {
_cleanup_free_ struct local_address *a = NULL;
union in_addr_union u;
int n;
- log_debug("/* Local Outbounds (ifindex:%i, %s) */", request_ifindex, family == AF_UNSPEC ? "AF_UNSPEC" : af_to_name(family));
+ log_debug("/* Local Outbounds (ifindex:%i, %s, expected_ipv6_address=%s) */",
+ request_ifindex, family == AF_UNSPEC ? "AF_UNSPEC" : af_to_name(family), ipv6_expected);
n = local_outbounds(rtnl, request_ifindex, family, &a);
assert_se(n >= 0);
@@ -180,7 +181,7 @@ static void check_local_outbounds(sd_netlink *rtnl, int ifindex, int request_ifi
.address = u,
}) == (family == AF_UNSPEC && support_rta_via));
- assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::123", &u) >= 0);
+ assert_se(in_addr_from_string(AF_INET6, ipv6_expected, &u) >= 0);
assert_se(has_local_address(a, n,
&(struct local_address) {
.ifindex = ifindex,
@@ -312,12 +313,86 @@ TEST(local_addresses_with_dummy) {
check_local_gateways(rtnl, ifindex, ifindex, AF_UNSPEC);
check_local_gateways(rtnl, ifindex, ifindex, AF_INET);
check_local_gateways(rtnl, ifindex, ifindex, AF_INET6);
- check_local_outbounds(rtnl, ifindex, 0, AF_UNSPEC);
- check_local_outbounds(rtnl, ifindex, 0, AF_INET);
- check_local_outbounds(rtnl, ifindex, 0, AF_INET6);
- check_local_outbounds(rtnl, ifindex, ifindex, AF_UNSPEC);
- check_local_outbounds(rtnl, ifindex, ifindex, AF_INET);
- check_local_outbounds(rtnl, ifindex, ifindex, AF_INET6);
+ check_local_outbounds(rtnl, ifindex, 0, AF_UNSPEC, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, 0, AF_INET, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, 0, AF_INET6, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, ifindex, AF_UNSPEC, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, ifindex, AF_INET, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, ifindex, AF_INET6, "2001:db8:1:123::123");
+
+ /* Add one more IPv6 address. */
+ assert_se(sd_rtnl_message_new_addr_update(rtnl, &message, ifindex, AF_INET6) >= 0);
+ assert_se(sd_rtnl_message_addr_set_scope(message, RT_SCOPE_UNIVERSE) >= 0);
+ assert_se(sd_rtnl_message_addr_set_prefixlen(message, 64) >= 0);
+ assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::124", &u) >= 0);
+ assert_se(sd_netlink_message_append_in6_addr(message, IFA_LOCAL, &u.in6) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, IFA_FLAGS, IFA_F_NODAD) >= 0);
+ assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0);
+ message = sd_netlink_message_unref(message);
+
+ /* Replace the previous IPv6 default gateway with one with preferred source address. */
+ assert_se(sd_rtnl_message_new_route(rtnl, &message, RTM_DELROUTE, AF_INET6, RTPROT_STATIC) >= 0);
+ assert_se(sd_rtnl_message_route_set_type(message, RTN_UNICAST) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_PRIORITY, 1234) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_TABLE, RT_TABLE_MAIN) >= 0);
+ assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::1", &u) >= 0);
+ assert_se(sd_netlink_message_append_in6_addr(message, RTA_GATEWAY, &u.in6) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_OIF, ifindex) >= 0);
+ assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0);
+ message = sd_netlink_message_unref(message);
+
+ assert_se(sd_rtnl_message_new_route(rtnl, &message, RTM_NEWROUTE, AF_INET6, RTPROT_STATIC) >= 0);
+ assert_se(sd_rtnl_message_route_set_type(message, RTN_UNICAST) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_PRIORITY, 1234) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_TABLE, RT_TABLE_MAIN) >= 0);
+ assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::1", &u) >= 0);
+ assert_se(sd_netlink_message_append_in6_addr(message, RTA_GATEWAY, &u.in6) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_OIF, ifindex) >= 0);
+ assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::123", &u) >= 0);
+ assert_se(sd_netlink_message_append_in6_addr(message, RTA_PREFSRC, &u.in6) >= 0);
+ assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0);
+ message = sd_netlink_message_unref(message);
+
+ /* Check again. */
+ check_local_outbounds(rtnl, ifindex, 0, AF_UNSPEC, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, 0, AF_INET, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, 0, AF_INET6, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, ifindex, AF_UNSPEC, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, ifindex, AF_INET, "2001:db8:1:123::123");
+ check_local_outbounds(rtnl, ifindex, ifindex, AF_INET6, "2001:db8:1:123::123");
+
+ /* Replace the preferred source address. */
+ assert_se(sd_rtnl_message_new_route(rtnl, &message, RTM_DELROUTE, AF_INET6, RTPROT_STATIC) >= 0);
+ assert_se(sd_rtnl_message_route_set_type(message, RTN_UNICAST) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_PRIORITY, 1234) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_TABLE, RT_TABLE_MAIN) >= 0);
+ assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::1", &u) >= 0);
+ assert_se(sd_netlink_message_append_in6_addr(message, RTA_GATEWAY, &u.in6) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_OIF, ifindex) >= 0);
+ assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::123", &u) >= 0);
+ assert_se(sd_netlink_message_append_in6_addr(message, RTA_PREFSRC, &u.in6) >= 0);
+ assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0);
+ message = sd_netlink_message_unref(message);
+
+ assert_se(sd_rtnl_message_new_route(rtnl, &message, RTM_NEWROUTE, AF_INET6, RTPROT_STATIC) >= 0);
+ assert_se(sd_rtnl_message_route_set_type(message, RTN_UNICAST) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_PRIORITY, 1234) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_TABLE, RT_TABLE_MAIN) >= 0);
+ assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::1", &u) >= 0);
+ assert_se(sd_netlink_message_append_in6_addr(message, RTA_GATEWAY, &u.in6) >= 0);
+ assert_se(sd_netlink_message_append_u32(message, RTA_OIF, ifindex) >= 0);
+ assert_se(in_addr_from_string(AF_INET6, "2001:db8:1:123::124", &u) >= 0);
+ assert_se(sd_netlink_message_append_in6_addr(message, RTA_PREFSRC, &u.in6) >= 0);
+ assert_se(sd_netlink_call(rtnl, message, 0, NULL) >= 0);
+ message = sd_netlink_message_unref(message);
+
+ /* Check again. */
+ check_local_outbounds(rtnl, ifindex, 0, AF_UNSPEC, "2001:db8:1:123::124");
+ check_local_outbounds(rtnl, ifindex, 0, AF_INET, "2001:db8:1:123::124");
+ check_local_outbounds(rtnl, ifindex, 0, AF_INET6, "2001:db8:1:123::124");
+ check_local_outbounds(rtnl, ifindex, ifindex, AF_UNSPEC, "2001:db8:1:123::124");
+ check_local_outbounds(rtnl, ifindex, ifindex, AF_INET, "2001:db8:1:123::124");
+ check_local_outbounds(rtnl, ifindex, ifindex, AF_INET6, "2001:db8:1:123::124");
/* Cleanup */
assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_DELLINK, ifindex) >= 0);
diff --git a/src/test/test-pidref.c b/src/test/test-pidref.c
index 53ed10d153..5535e98ab0 100644
--- a/src/test/test-pidref.c
+++ b/src/test/test-pidref.c
@@ -224,4 +224,42 @@ TEST(pidref_verify) {
assert_se(pidref_verify(&pidref) == (pidref.fd >= 0));
}
+TEST(pidref_is_automatic) {
+ assert_se(!pidref_is_automatic(NULL));
+ assert_se(!pidref_is_automatic(&PIDREF_NULL));
+ assert_se(!pidref_is_automatic(&PIDREF_MAKE_FROM_PID(1)));
+ assert_se(!pidref_is_automatic(&PIDREF_MAKE_FROM_PID(getpid_cached())));
+ assert_se(pidref_is_automatic(&PIDREF_AUTOMATIC));
+
+ assert_se(!pid_is_automatic(0));
+ assert_se(!pid_is_automatic(1));
+ assert_se(!pid_is_automatic(getpid_cached()));
+ assert_se(pid_is_automatic(PID_AUTOMATIC));
+
+ assert_se(!pidref_is_set(&PIDREF_AUTOMATIC));
+ assert_se(!pid_is_valid(PID_AUTOMATIC));
+}
+
+TEST(pidref_is_remote) {
+ assert_se(!pidref_is_remote(NULL));
+ assert_se(!pidref_is_remote(&PIDREF_NULL));
+ assert_se(!pidref_is_remote(&PIDREF_MAKE_FROM_PID(1)));
+ assert_se(!pidref_is_remote(&PIDREF_MAKE_FROM_PID(getpid_cached())));
+ assert_se(!pidref_is_remote(&PIDREF_AUTOMATIC));
+
+ static const PidRef p = {
+ .pid = 1,
+ .fd = -EREMOTE,
+ .fd_id = 4711,
+ };
+
+ assert_se(pidref_is_set(&p));
+ assert_se(pidref_is_remote(&p));
+ assert_se(!pidref_is_automatic(&p));
+ assert_se(pidref_kill(&p, SIGTERM) == -EREMOTE);
+ assert_se(pidref_kill_and_sigcont(&p, SIGTERM) == -EREMOTE);
+ assert_se(pidref_wait_for_terminate(&p, /* ret= */ NULL) == -EREMOTE);
+ assert_se(pidref_verify(&p) == -EREMOTE);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/test/test-varlink-idl.c b/src/test/test-varlink-idl.c
index 519056d044..c54b24aa8b 100644
--- a/src/test/test-varlink-idl.c
+++ b/src/test/test-varlink-idl.c
@@ -15,6 +15,7 @@
#include "varlink-io.systemd.Import.h"
#include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.Machine.h"
+#include "varlink-io.systemd.MachineImage.h"
#include "varlink-io.systemd.ManagedOOM.h"
#include "varlink-io.systemd.MountFileSystem.h"
#include "varlink-io.systemd.NamespaceResource.h"
@@ -190,6 +191,8 @@ TEST(parse_format) {
print_separator();
test_parse_format_one(&vl_interface_io_systemd_Machine);
print_separator();
+ test_parse_format_one(&vl_interface_io_systemd_MachineImage);
+ print_separator();
test_parse_format_one(&vl_interface_xyz_test);
}
diff --git a/src/test/test-varlink.c b/src/test/test-varlink.c
index 17a78633cc..cafe98871b 100644
--- a/src/test/test-varlink.c
+++ b/src/test/test-varlink.c
@@ -326,7 +326,7 @@ static int block_fd_handler(sd_event_source *s, int fd, uint32_t revents, void *
return 0;
}
-int main(int argc, char *argv[]) {
+TEST(chat) {
_cleanup_(sd_event_source_unrefp) sd_event_source *block_event = NULL;
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
_cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *c = NULL;
@@ -337,8 +337,6 @@ int main(int argc, char *argv[]) {
pthread_t t;
const char *sp;
- test_setup_logging(LOG_DEBUG);
-
assert_se(mkdtemp_malloc("/tmp/varlink-test-XXXXXX", &tmpdir) >= 0);
sp = strjoina(tmpdir, "/socket");
@@ -377,6 +375,59 @@ int main(int argc, char *argv[]) {
assert_se(sd_event_loop(e) >= 0);
assert_se(pthread_join(t, NULL) == 0);
+}
+
+static int method_invalid(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
+ int r;
+
+ sd_json_dispatch_field table[] = {
+ { "iexist", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
+ {}
+ };
+
+ const char *p = NULL;
+ r = sd_varlink_dispatch(link, parameters, table, &p);
+ if (r != 0)
+ return r;
+
+ assert_not_reached();
+}
+
+static int reply_invalid(sd_varlink *link, sd_json_variant *parameters, const char *error_id, sd_varlink_reply_flags_t flags, void *userdata) {
+ assert(sd_varlink_error_is_invalid_parameter(error_id, parameters, "idontexist"));
+ assert(sd_event_exit(sd_varlink_get_event(link), EXIT_SUCCESS) >= 0);
return 0;
}
+
+TEST(invalid_parameter) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ assert_se(sd_event_default(&e) >= 0);
+
+ _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
+ assert_se(sd_varlink_server_new(&s, 0) >= 0);
+
+ assert_se(sd_varlink_server_attach_event(s, e, 0) >= 0);
+
+ assert_se(sd_varlink_server_bind_method(s, "foo.mytest.Invalid", method_invalid) >= 0);
+
+ int connfd[2];
+ assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, connfd) >= 0);
+ assert_se(sd_varlink_server_add_connection(s, connfd[0], /* ret= */ NULL) >= 0);
+
+ _cleanup_(sd_varlink_unrefp) sd_varlink *c = NULL;
+ assert_se(sd_varlink_connect_fd(&c, connfd[1]) >= 0);
+
+ assert_se(sd_varlink_attach_event(c, e, 0) >= 0);
+
+ assert_se(sd_varlink_bind_reply(c, reply_invalid) >= 0);
+
+ assert_se(sd_varlink_invokebo(c, "foo.mytest.Invalid",
+ SD_JSON_BUILD_PAIR_STRING("iexist", "foo"),
+ SD_JSON_BUILD_PAIR_STRING("idontexist", "bar")) >= 0);
+
+
+ assert_se(sd_event_loop(e) >= 0);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 421e465d71..c4e032d8d7 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -2007,7 +2007,7 @@ static int create_directory_or_subvolume(
if (r == 0)
/* Don't create a subvolume unless the root directory is one, too. We do this under
* the assumption that if the root directory is just a plain directory (i.e. very
- * light-weight), we shouldn't try to split it up into subvolumes (i.e. more
+ * lightweight), we shouldn't try to split it up into subvolumes (i.e. more
* heavy-weight). Thus, chroot() environments and suchlike will get a full brtfs
* subvolume set up below their tree only if they specifically set up a btrfs
* subvolume for the root dir too. */
diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c
index c28c43b2af..1b8e2b8dbd 100644
--- a/src/udev/udev-watch.c
+++ b/src/udev/udev-watch.c
@@ -181,6 +181,9 @@ int udev_watch_begin(int inotify_fd, sd_device *dev) {
assert(inotify_fd >= 0);
assert(dev);
+ /* Ignore the request of watching the device node on remove event, as the device node specified by
+ * DEVNAME= has already been removed, and may already be assigned to another device. Consider the
+ * case e.g. a USB stick memory was unplugged and then another one is plugged. */
if (device_for_action(dev, SD_DEVICE_REMOVE))
return 0;
diff --git a/src/udev/udev-worker.c b/src/udev/udev-worker.c
index 5577f5fab8..59f56f653c 100644
--- a/src/udev/udev-worker.c
+++ b/src/udev/udev-worker.c
@@ -97,6 +97,12 @@ static int worker_lock_whole_disk(sd_device *dev, int *ret_fd) {
* event handling; in the case udev acquired the lock, the external process can block until udev has
* finished its event handling. */
+ /* Do not try to lock device on remove event, as the device node specified by DEVNAME= has already
+ * been removed, and may already be assigned to another device. Consider the case e.g. a USB stick
+ * memory was unplugged and then another one is plugged. */
+ if (device_for_action(dev, SD_DEVICE_REMOVE))
+ goto nolock;
+
r = udev_get_whole_disk(dev, &dev_whole_disk, &val);
if (r < 0)
return r;
@@ -200,26 +206,31 @@ static int worker_process_device(UdevWorker *worker, sd_device *dev) {
if (r < 0)
return r;
+ /* Process RUN=. */
udev_event_execute_run(udev_event);
if (!worker->rtnl)
/* in case rtnl was initialized */
worker->rtnl = sd_netlink_ref(udev_event->rtnl);
+ /* Enable watch if requested. */
if (udev_event->inotify_watch) {
r = udev_watch_begin(worker->inotify_fd, dev);
if (r < 0 && r != -ENOENT) /* The device may be already removed, ignore -ENOENT. */
log_device_warning_errno(dev, r, "Failed to add inotify watch, ignoring: %m");
}
- /* Finalize database. */
- r = device_add_property(dev, "ID_PROCESSING", NULL);
- if (r < 0)
- return log_device_warning_errno(dev, r, "Failed to remove 'ID_PROCESSING' property: %m");
+ /* Finalize database. But do not re-create database on remove, which has been already removed in
+ * event_execute_rules_on_remove(). */
+ if (!device_for_action(dev, SD_DEVICE_REMOVE)) {
+ r = device_add_property(dev, "ID_PROCESSING", NULL);
+ if (r < 0)
+ return log_device_warning_errno(dev, r, "Failed to remove 'ID_PROCESSING' property: %m");
- r = device_update_db(dev);
- if (r < 0)
- return log_device_warning_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+ r = device_update_db(dev);
+ if (r < 0)
+ return log_device_warning_errno(dev, r, "Failed to update database under /run/udev/data/: %m");
+ }
log_device_uevent(dev, "Device processed");
return 0;
diff --git a/src/ukify/mypy.ini b/src/ukify/mypy.ini
index bae4e70f44..c9836169c1 100644
--- a/src/ukify/mypy.ini
+++ b/src/ukify/mypy.ini
@@ -1,8 +1,9 @@
[mypy]
python_version = 3.9
-allow_redefinition = True
# belonging to --strict
warn_unused_configs = true
+disallow_any_generics = true
+disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_untyped_decorators = true
diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py
index 48182483c2..c547e7caee 100755
--- a/src/ukify/test/test_ukify.py
+++ b/src/ukify/test/test_ukify.py
@@ -364,7 +364,7 @@ def test_config_priority(tmp_path):
pathlib.Path('some/path8')]
assert opts.pcr_banks == ['SHA1', 'SHA256']
assert opts.signing_engine == 'ENGINE'
- assert opts.signtool == 'sbsign' # from args
+ assert opts.signtool == ukify.SbSign # from args
assert opts.sb_key == 'SBKEY' # from args
assert opts.sb_cert == 'SBCERT' # from args
assert opts.sb_certdir == 'some/path5' # from config
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py
index e78c6119cb..02220dd983 100755
--- a/src/ukify/ukify.py
+++ b/src/ukify/ukify.py
@@ -20,12 +20,14 @@
# pylint: disable=unnecessary-lambda-assignment
import argparse
+import builtins
import collections
import configparser
import contextlib
import dataclasses
import datetime
import fnmatch
+import inspect
import itertools
import json
import os
@@ -48,6 +50,7 @@ from typing import (
IO,
Any,
Callable,
+ Literal,
Optional,
TypeVar,
Union,
@@ -230,6 +233,50 @@ def maybe_decompress(filename: Union[str, Path]) -> bytes:
raise NotImplementedError(f'unknown file format (starts with {start!r})')
+@dataclasses.dataclass
+class UkifyConfig:
+ all: bool
+ cmdline: Union[str, Path, None]
+ devicetree: Path
+ efi_arch: str
+ initrd: list[Path]
+ join_profiles: list[Path]
+ json: Union[Literal['pretty'], Literal['short'], Literal['off']]
+ linux: Optional[Path]
+ measure: bool
+ microcode: Path
+ os_release: Union[str, Path, None]
+ output: Optional[str]
+ pcr_banks: list[str]
+ pcr_private_keys: list[str]
+ pcr_public_keys: list[Path]
+ pcrpkey: Optional[Path]
+ phase_path_groups: Optional[list[str]]
+ profile: Union[str, Path, None]
+ sb_cert: Path
+ sb_cert_name: Optional[str]
+ sb_cert_validity: int
+ sb_certdir: Path
+ sb_key: Optional[Path]
+ sbat: Optional[list[str]]
+ sections: list['Section']
+ sections_by_name: dict[str, 'Section']
+ sign_kernel: bool
+ signing_engine: Optional[str]
+ signtool: Optional[type['SignTool']]
+ splash: Optional[Path]
+ stub: Path
+ summary: bool
+ tools: list[Path]
+ uname: Optional[str]
+ verb: str
+ files: list[str] = dataclasses.field(default_factory=list)
+
+ @classmethod
+ def from_namespace(cls, ns: argparse.Namespace) -> 'UkifyConfig':
+ return cls(**{k: v for k, v in vars(ns).items() if k in inspect.signature(cls).parameters})
+
+
class Uname:
# This class is here purely as a namespace for the functions
@@ -243,7 +290,7 @@ class Uname:
TEXT_PATTERN = rb'Linux version (?P<version>\d\.\S+) \('
@classmethod
- def scrape_x86(cls, filename: Path, opts: Optional[argparse.Namespace] = None) -> str:
+ def scrape_x86(cls, filename: Path, opts: Optional[UkifyConfig] = None) -> str:
# Based on https://gitlab.archlinux.org/archlinux/mkinitcpio/mkinitcpio/-/blob/master/functions#L136
# and https://docs.kernel.org/arch/x86/boot.html#the-real-mode-kernel-header
with open(filename, 'rb') as f:
@@ -263,7 +310,7 @@ class Uname:
return m.group('version')
@classmethod
- def scrape_elf(cls, filename: Path, opts: Optional[argparse.Namespace] = None) -> str:
+ def scrape_elf(cls, filename: Path, opts: Optional[UkifyConfig] = None) -> str:
readelf = find_tool('readelf', opts=opts)
cmd = [
@@ -285,7 +332,7 @@ class Uname:
return text.rstrip('\0')
@classmethod
- def scrape_generic(cls, filename: Path, opts: Optional[argparse.Namespace] = None) -> str:
+ def scrape_generic(cls, filename: Path, opts: Optional[UkifyConfig] = None) -> str:
# import libarchive
# libarchive-c fails with
# ArchiveError: Unrecognized archive format (errno=84, retcode=-30, archive_p=94705420454656)
@@ -299,7 +346,7 @@ class Uname:
return m.group('version').decode()
@classmethod
- def scrape(cls, filename: Path, opts: Optional[argparse.Namespace] = None) -> Optional[str]:
+ def scrape(cls, filename: Path, opts: Optional[UkifyConfig] = None) -> Optional[str]:
for func in (cls.scrape_x86, cls.scrape_elf, cls.scrape_generic):
try:
version = func(filename, opts=opts)
@@ -387,7 +434,7 @@ class Section:
@dataclasses.dataclass
class UKI:
- executable: list[Union[Path, str]]
+ executable: Path
sections: list[Section] = dataclasses.field(default_factory=list, init=False)
def add_section(self, section: Section) -> None:
@@ -404,6 +451,81 @@ class UKI:
self.sections += [section]
+class SignTool:
+ @staticmethod
+ def sign(input_f: str, output_f: str, opts: UkifyConfig) -> None:
+ raise NotImplementedError()
+
+ @staticmethod
+ def verify(opts: UkifyConfig) -> bool:
+ raise NotImplementedError()
+
+
+class PeSign(SignTool):
+ @staticmethod
+ def sign(input_f: str, output_f: str, opts: UkifyConfig) -> None:
+ assert opts.sb_certdir is not None
+ assert opts.sb_cert_name is not None
+
+ tool = find_tool('pesign', opts=opts, msg='pesign, required for signing, is not installed')
+ cmd = [
+ tool,
+ '-s',
+ '--force',
+ '-n', opts.sb_certdir,
+ '-c', opts.sb_cert_name,
+ '-i', input_f,
+ '-o', output_f,
+ ] # fmt: skip
+
+ print('+', shell_join(cmd))
+ subprocess.check_call(cmd)
+
+ @staticmethod
+ def verify(opts: UkifyConfig) -> bool:
+ assert opts.linux is not None
+
+ tool = find_tool('pesign', opts=opts)
+ cmd = [tool, '-i', opts.linux, '-S']
+
+ print('+', shell_join(cmd))
+ info = subprocess.check_output(cmd, text=True)
+
+ return 'No signatures found.' in info
+
+
+class SbSign(SignTool):
+ @staticmethod
+ def sign(input_f: str, output_f: str, opts: UkifyConfig) -> None:
+ assert opts.sb_key is not None
+ assert opts.sb_cert is not None
+
+ tool = find_tool('sbsign', opts=opts, msg='sbsign, required for signing, is not installed')
+ cmd = [
+ tool,
+ '--key', opts.sb_key,
+ '--cert', opts.sb_cert,
+ *(['--engine', opts.signing_engine] if opts.signing_engine is not None else []),
+ input_f,
+ '--output', output_f,
+ ] # fmt: skip
+
+ print('+', shell_join(cmd))
+ subprocess.check_call(cmd)
+
+ @staticmethod
+ def verify(opts: UkifyConfig) -> bool:
+ assert opts.linux is not None
+
+ tool = find_tool('sbverify', opts=opts)
+ cmd = [tool, '--list', opts.linux]
+
+ print('+', shell_join(cmd))
+ info = subprocess.check_output(cmd, text=True)
+
+ return 'No signature table present' in info
+
+
def parse_banks(s: str) -> list[str]:
banks = re.split(r',|\s+', s)
# TODO: do some sanity checking here
@@ -432,7 +554,7 @@ def parse_phase_paths(s: str) -> list[str]:
return paths
-def check_splash(filename: Optional[str]) -> None:
+def check_splash(filename: Optional[Path]) -> None:
if filename is None:
return
@@ -446,7 +568,7 @@ def check_splash(filename: Optional[str]) -> None:
print(f'Splash image {filename} is {img.width}×{img.height} pixels')
-def check_inputs(opts: argparse.Namespace) -> None:
+def check_inputs(opts: UkifyConfig) -> None:
for name, value in vars(opts).items():
if name in {'output', 'tools'}:
continue
@@ -462,9 +584,9 @@ def check_inputs(opts: argparse.Namespace) -> None:
check_splash(opts.splash)
-def check_cert_and_keys_nonexistent(opts: argparse.Namespace) -> None:
+def check_cert_and_keys_nonexistent(opts: UkifyConfig) -> None:
# Raise if any of the keys and certs are found on disk
- paths = itertools.chain(
+ paths: Iterator[Union[str, Path, None]] = itertools.chain(
(opts.sb_key, opts.sb_cert),
*((priv_key, pub_key) for priv_key, pub_key, _ in key_path_groups(opts)),
)
@@ -476,14 +598,14 @@ def check_cert_and_keys_nonexistent(opts: argparse.Namespace) -> None:
def find_tool(
name: str,
fallback: Optional[str] = None,
- opts: Optional[argparse.Namespace] = None,
+ opts: Optional[UkifyConfig] = None,
msg: str = 'Tool {name} not installed!',
) -> Union[str, Path]:
if opts and opts.tools:
for d in opts.tools:
tool = d / name
if tool.exists():
- return cast(Path, tool)
+ return tool
if shutil.which(name) is not None:
return name
@@ -504,18 +626,19 @@ def combine_signatures(pcrsigs: list[dict[str, str]]) -> str:
return json.dumps(combined)
-def key_path_groups(opts: argparse.Namespace) -> Iterator:
+def key_path_groups(opts: UkifyConfig) -> Iterator[tuple[str, Optional[Path], Optional[str]]]:
if not opts.pcr_private_keys:
return
n_priv = len(opts.pcr_private_keys)
- pub_keys = opts.pcr_public_keys or [None] * n_priv
- pp_groups = opts.phase_path_groups or [None] * n_priv
+ pub_keys = opts.pcr_public_keys or []
+ pp_groups = opts.phase_path_groups or []
- yield from zip(
+ yield from itertools.zip_longest(
opts.pcr_private_keys,
- pub_keys,
- pp_groups,
+ pub_keys[:n_priv],
+ pp_groups[:n_priv],
+ fillvalue=None,
)
@@ -523,7 +646,7 @@ def pe_strip_section_name(name: bytes) -> str:
return name.rstrip(b'\x00').decode()
-def call_systemd_measure(uki: UKI, opts: argparse.Namespace, profile_start: int = 0) -> None:
+def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) -> None:
measure_tool = find_tool(
'systemd-measure',
'/usr/lib/systemd/systemd-measure',
@@ -798,79 +921,6 @@ def merge_sbat(input_pe: list[Path], input_text: list[str]) -> str:
)
-def signer_sign(cmd: list[Union[str, Path]]) -> None:
- print('+', shell_join(cmd))
- subprocess.check_call(cmd)
-
-
-def sbsign_sign(
- sbsign_tool: Union[str, Path],
- input_f: str,
- output_f: str,
- opts: argparse.Namespace,
-) -> None:
- sign_invocation = [
- sbsign_tool,
- '--key', opts.sb_key,
- '--cert', opts.sb_cert,
- ] # fmt: skip
- if opts.signing_engine is not None:
- sign_invocation += ['--engine', opts.signing_engine]
- sign_invocation += [
- input_f,
- '--output', output_f,
- ] # fmt: skip
- signer_sign(sign_invocation)
-
-
-def pesign_sign(
- pesign_tool: Union[str, Path],
- input_f: str,
- output_f: str,
- opts: argparse.Namespace,
-) -> None:
- sign_invocation = [
- pesign_tool,
- '-s',
- '--force',
- '-n', opts.sb_certdir,
- '-c', opts.sb_cert_name,
- '-i', input_f,
- '-o', output_f,
- ] # fmt: skip
- signer_sign(sign_invocation)
-
-
-SBVERIFY = {
- 'name': 'sbverify',
- 'option': '--list',
- 'output': 'No signature table present',
-}
-
-PESIGCHECK = {
- 'name': 'pesign',
- 'option': '-i',
- 'output': 'No signatures found.',
- 'flags': '-S',
-}
-
-
-def verify(tool: dict[str, str], opts: argparse.Namespace) -> bool:
- verify_tool = find_tool(tool['name'], opts=opts)
- cmd = [
- verify_tool,
- tool['option'],
- opts.linux,
- ]
- if 'flags' in tool:
- cmd.append(tool['flags'])
-
- print('+', shell_join(cmd))
- info = subprocess.check_output(cmd, text=True)
-
- return tool['output'] in info
-
-
STUB_SBAT = """\
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
uki,1,UKI,uki,1,https://uapi-group.org/specifications/specs/unified_kernel_image/
@@ -882,35 +932,27 @@ uki-addon,1,UKI Addon,addon,1,https://www.freedesktop.org/software/systemd/man/l
"""
-def make_uki(opts: argparse.Namespace) -> None:
+def make_uki(opts: UkifyConfig) -> None:
+ assert opts.output is not None
+
# kernel payload signing
- sign_tool = None
sign_args_present = opts.sb_key or opts.sb_cert_name
sign_kernel = opts.sign_kernel
- sign: Optional[Callable[[Union[str, Path], str, str, argparse.Namespace], None]] = None
linux = opts.linux
if sign_args_present:
- if opts.signtool == 'sbsign':
- sign_tool = find_tool('sbsign', opts=opts, msg='sbsign, required for signing, is not installed')
- sign = sbsign_sign
- verify_tool = SBVERIFY
- else:
- sign_tool = find_tool('pesign', opts=opts, msg='pesign, required for signing, is not installed')
- sign = pesign_sign
- verify_tool = PESIGCHECK
+ assert opts.linux is not None
+ assert opts.signtool is not None
- if sign_kernel is None and opts.linux is not None:
+ if not sign_kernel:
# figure out if we should sign the kernel
- sign_kernel = verify(verify_tool, opts)
+ sign_kernel = opts.signtool.verify(opts)
if sign_kernel:
- assert sign is not None
- assert sign_tool is not None
linux_signed = tempfile.NamedTemporaryFile(prefix='linux-signed')
linux = Path(linux_signed.name)
- sign(sign_tool, opts.linux, linux, opts=opts)
+ opts.signtool.sign(os.fspath(opts.linux), os.fspath(linux), opts=opts)
if opts.uname is None and opts.linux is not None:
print('Kernel version not specified, starting autodetection 😖.')
@@ -919,7 +961,7 @@ def make_uki(opts: argparse.Namespace) -> None:
uki = UKI(opts.stub)
initrd = join_initrds(opts.initrd)
- pcrpkey = opts.pcrpkey
+ pcrpkey: Union[bytes, Path, None] = opts.pcrpkey
if pcrpkey is None:
if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
pcrpkey = opts.pcr_public_keys[0]
@@ -1041,15 +1083,15 @@ def make_uki(opts: argparse.Namespace) -> None:
if names.count('.profile') > 1:
raise ValueError(f'Profile PE binary {profile} contains multiple .profile sections')
- for section in pe.sections:
- n = pe_strip_section_name(section.Name)
+ for pesection in pe.sections:
+ n = pe_strip_section_name(pesection.Name)
if n not in to_import:
continue
- print(f"Copying section '{n}' from '{profile}': {section.Misc_VirtualSize} bytes")
+ print(f"Copying section '{n}' from '{profile}': {pesection.Misc_VirtualSize} bytes")
uki.add_section(
- Section.create(n, section.get_data(length=section.Misc_VirtualSize), measure=True)
+ Section.create(n, pesection.get_data(length=pesection.Misc_VirtualSize), measure=True)
)
call_systemd_measure(uki, opts=opts, profile_start=prev_len)
@@ -1067,9 +1109,8 @@ def make_uki(opts: argparse.Namespace) -> None:
# UKI signing
if sign_args_present:
- assert sign is not None
- assert sign_tool is not None
- sign(sign_tool, unsigned_output, opts.output, opts)
+ assert opts.signtool is not None
+ opts.signtool.sign(os.fspath(unsigned_output), os.fspath(opts.output), opts)
# We end up with no executable bits, let's reapply them
os.umask(umask := os.umask(0))
@@ -1171,12 +1212,12 @@ def generate_priv_pub_key_pair(keylength: int = 2048) -> tuple[bytes, bytes]:
return priv_key_pem, pub_key_pem
-def generate_keys(opts: argparse.Namespace) -> None:
+def generate_keys(opts: UkifyConfig) -> None:
work = False
# This will generate keys and certificates and write them to the paths that
# are specified as input paths.
- if opts.sb_key or opts.sb_cert:
+ if opts.sb_key and opts.sb_cert:
fqdn = socket.getfqdn()
cn = f'SecureBoot signing key on host {fqdn}'
key_pem, cert_pem = generate_key_cert_pair(
@@ -1210,7 +1251,7 @@ def generate_keys(opts: argparse.Namespace) -> None:
def inspect_section(
- opts: argparse.Namespace,
+ opts: UkifyConfig,
section: pefile.SectionStructure,
) -> tuple[str, Optional[dict[str, Union[int, str]]]]:
name = pe_strip_section_name(section.Name)
@@ -1253,7 +1294,7 @@ def inspect_section(
return name, struct
-def inspect_sections(opts: argparse.Namespace) -> None:
+def inspect_sections(opts: UkifyConfig) -> None:
indent = 4 if opts.json == 'pretty' else None
for file in opts.files:
@@ -1346,9 +1387,9 @@ class ConfigItem:
name: Union[str, tuple[str, str]]
dest: Optional[str] = None
metavar: Optional[str] = None
- type: Optional[Callable] = None
+ type: Optional[Callable[[str], Any]] = None
nargs: Optional[str] = None
- action: Optional[Union[str, Callable]] = None
+ action: Optional[Union[str, Callable[[str], Any], builtins.type[argparse.Action]]] = None
default: Any = None
version: Optional[str] = None
choices: Optional[tuple[str, ...]] = None
@@ -1422,6 +1463,24 @@ class ConfigItem:
return (section_name, key, value)
+class SignToolAction(argparse.Action):
+ def __call__(
+ self,
+ parser: argparse.ArgumentParser,
+ namespace: argparse.Namespace,
+ values: Union[str, Sequence[Any], None] = None,
+ option_string: Optional[str] = None,
+ ) -> None:
+ if values is None:
+ setattr(namespace, 'signtool', None)
+ elif values == 'sbsign':
+ setattr(namespace, 'signtool', SbSign)
+ elif values == 'pesign':
+ setattr(namespace, 'signtool', PeSign)
+ else:
+ raise ValueError(f"Unknown signtool '{values}' (this is unreachable)")
+
+
VERBS = ('build', 'genkey', 'inspect')
CONFIG_ITEMS = [
@@ -1566,6 +1625,7 @@ CONFIG_ITEMS = [
ConfigItem(
'--signtool',
choices=('sbsign', 'pesign'),
+ action=SignToolAction,
dest='signtool',
help=(
'whether to use sbsign or pesign. It will also be inferred by the other '
@@ -1880,18 +1940,18 @@ def finalize_options(opts: argparse.Namespace) -> None:
)
elif bool(opts.sb_key) and bool(opts.sb_cert):
# both param given, infer sbsign and in case it was given, ensure signtool=sbsign
- if opts.signtool and opts.signtool != 'sbsign':
+ if opts.signtool and opts.signtool != SbSign:
raise ValueError(
f'Cannot provide --signtool={opts.signtool} with --secureboot-private-key= and --secureboot-certificate=' # noqa: E501
)
- opts.signtool = 'sbsign'
+ opts.signtool = SbSign
elif bool(opts.sb_cert_name):
# sb_cert_name given, infer pesign and in case it was given, ensure signtool=pesign
- if opts.signtool and opts.signtool != 'pesign':
+ if opts.signtool and opts.signtool != PeSign:
raise ValueError(
f'Cannot provide --signtool={opts.signtool} with --secureboot-certificate-name='
)
- opts.signtool = 'pesign'
+ opts.signtool = PeSign
if opts.sign_kernel and not opts.sb_key and not opts.sb_cert_name:
raise ValueError(
@@ -1926,7 +1986,7 @@ def parse_args(args: Optional[list[str]] = None) -> argparse.Namespace:
def main() -> None:
- opts = parse_args()
+ opts = UkifyConfig.from_namespace(parse_args())
if opts.summary:
# TODO: replace pprint() with some fancy formatting.
pprint.pprint(vars(opts))
diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c
index f448b3b1d1..436bae639b 100644
--- a/src/update-done/update-done.c
+++ b/src/update-done/update-done.c
@@ -29,7 +29,7 @@ static int apply_timestamp(const char *path, struct timespec *ts) {
timespec_load_nsec(ts)) < 0)
return log_oom();
- r = write_string_file_atomic_label_ts(path, message, ts);
+ r = write_string_file_full_label(AT_FDCWD, path, message, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC, ts);
if (r == -EROFS)
log_debug_errno(r, "Cannot create \"%s\", file system is read-only.", path);
else if (r < 0)
diff --git a/test/README.testsuite b/test/README.testsuite
index 8cacbb40af..a513d1c6b9 100644
--- a/test/README.testsuite
+++ b/test/README.testsuite
@@ -25,7 +25,7 @@ Environment=NO_BUILD=1
You might also want to use the `PackageDirectories=` or `Repositories=` option to provide
mkosi with a directory or repository containing the systemd packages that should be installed
instead. If the repository containing the systemd packages is not a builtin repository known
-by mkosi, you can use the `PackageManagerTrees=` option to write an extra repository definition
+by mkosi, you can use the `SandboxTrees=` option to write an extra repository definition
to /etc which is used when building the image instead.
Next, we can build the integration test image with meson:
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-372994449 b/test/fuzz/fuzz-network-parser/oss-fuzz-372994449
new file mode 100644
index 0000000000..76c3a4903c
--- /dev/null
+++ b/test/fuzz/fuzz-network-parser/oss-fuzz-372994449
Binary files differ
diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service
index 1cb212bcad..a0883d0ebe 100644
--- a/test/fuzz/fuzz-unit-file/directives-all.service
+++ b/test/fuzz/fuzz-unit-file/directives-all.service
@@ -154,6 +154,7 @@ MaxConnectionsPerSource=
ManagedOOMSwap=
ManagedOOMMemoryPressure=
ManagedOOMMemoryPressureLimitPercent=
+ManagedOOMMemoryPressureDurationSec=
ManagedOOMPreference=
MemoryAccounting=
MemoryHigh=
diff --git a/test/test-functions b/test/test-functions
index 269cd8812b..8a4cc55626 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -211,6 +211,7 @@ BASICTOOLS=(
ping
pkill
ps
+ pwd
readlink
realpath
rev
diff --git a/test/units/TEST-13-NSPAWN.machined.sh b/test/units/TEST-13-NSPAWN.machined.sh
index 1ae393dd1f..da62b465ea 100755
--- a/test/units/TEST-13-NSPAWN.machined.sh
+++ b/test/units/TEST-13-NSPAWN.machined.sh
@@ -12,7 +12,7 @@ export PAGER=
at_exit() {
set +e
- machinectl status long-running >/dev/null && machinectl kill --signal=KILL long-running
+ machinectl status long-running &>/dev/null && machinectl kill --signal=KILL long-running
mountpoint -q /var/lib/machines && timeout 10 sh -c "until umount /var/lib/machines; do sleep .5; done"
[[ -n "${NSPAWN_FRAGMENT:-}" ]] && rm -f "/etc/systemd/nspawn/$NSPAWN_FRAGMENT" "/var/lib/machines/$NSPAWN_FRAGMENT"
rm -f /run/systemd/nspawn/*.nspawn
@@ -39,6 +39,7 @@ cat >/var/lib/machines/long-running/sbin/init <<\EOF
PID=0
+trap "touch /terminate; kill $PID" RTMIN+3
trap "touch /poweroff" RTMIN+4
trap "touch /reboot" INT
trap "touch /trap" TRAP
@@ -59,11 +60,13 @@ EOF
long_running_machine_start() {
# shellcheck disable=SC2015
- machinectl status long-running >/dev/null && return 0 || true
+ machinectl status long-running &>/dev/null && return 0 || true
+
+ # Ensure the service stopped.
+ systemctl stop systemd-nspawn@long-running.service 2>/dev/null || :
rm -f /var/lib/machines/long-running/ready
- # sometime `machinectl start` returns 1 and then do a success
- machinectl start long-running || machinectl start long-running
+ machinectl start long-running
# !!!! DO NOT REMOVE THIS TEST
# The test makes sure that the long-running's init script has enough time to start and registered signal traps
timeout 30 bash -c "until test -e /var/lib/machines/long-running/ready; do sleep .5; done"
@@ -115,7 +118,14 @@ timeout 10 bash -c "until test -e /var/lib/machines/long-running/poweroff; do sl
rm -f /var/lib/machines/long-running/reboot
machinectl reboot long-running
timeout 10 bash -c "until test -e /var/lib/machines/long-running/reboot; do sleep .5; done"
-# Skip machinectl terminate for now, as it doesn't play well with our "init"
+# Test for 'machinectl terminate'
+rm -f /var/lib/machines/long-running/terminate
+machinectl terminate long-running
+timeout 10 bash -c "until test -e /var/lib/machines/long-running/terminate; do sleep .5; done"
+timeout 10 bash -c "while machinectl status long-running &>/dev/null; do sleep .5; done"
+# Restart container
+long_running_machine_start
+# Test for 'machinectl kill'
rm -f /var/lib/machines/long-running/trap
machinectl kill --signal=SIGTRAP --kill-whom=leader long-running
timeout 10 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done"
@@ -246,18 +256,23 @@ done
long_running_machine_start
+varlinkctl introspect /run/systemd/machine/io.systemd.Machine io.systemd.Machine
+varlinkctl introspect /run/systemd/machine/io.systemd.Machine io.systemd.MachineImage
+varlinkctl introspect /run/systemd/machine/io.systemd.MachineImage io.systemd.Machine
+varlinkctl introspect /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage
+
# test io.systemd.Machine.List
varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{}' | grep 'long-running'
varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{}' | grep '.host'
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}'
-pid=$(varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}' | jq '.leader.pid')
+pid=$(varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}' | jq '.leader')
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"long-running"}' >/tmp/expected
-varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"pid\":$pid}" >/tmp/got
-diff -u /tmp/expected /tmp/got
-
-varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"name\":\"long-running\", \"pid\":$pid}"
+varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"pid\":$pid}" | diff /tmp/expected -
+varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"name\":\"long-running\", \"pid\":$pid}" | diff /tmp/expected -
(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List "{\"name\":\"non-existent\", \"pid\":$pid}")
+(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":""}')
+(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"ah@??.hmm"}')
# test io.systemd.Machine.Kill
# sending TRAP signal
@@ -265,14 +280,12 @@ rm -f /var/lib/machines/long-running/trap
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Kill '{"name":"long-running", "whom": "leader", "signal": 5}'
timeout 30 bash -c "until test -e /var/lib/machines/long-running/trap; do sleep .5; done"
-# sending KILL signal
-varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Kill '{"name":"long-running", "signal": 9}'
-timeout 30 bash -c "while varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"long-running\"}'; do sleep 0.5; done"
-
# test io.systemd.Machine.Terminate
long_running_machine_start
+rm -f /var/lib/machines/long-running/terminate
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Terminate '{"name":"long-running"}'
-timeout 120 bash -c "while varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"long-running\"}'; do sleep 0.5; done"
+timeout 10 bash -c "until test -e /var/lib/machines/long-running/terminate; do sleep .5; done"
+timeout 30 bash -c "while varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{\"name\":\"long-running\"}'; do sleep 0.5; done"
# test io.systemd.Machine.Register
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Register '{"name": "registered-container", "class": "container"}'
@@ -288,3 +301,18 @@ timeout 30 bash -c "until varlinkctl call /run/systemd/machine/io.systemd.Machin
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"registered-container"}' | jq '.sshAddress' | grep -q 'localhost'
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name":"registered-container"}' | jq '.sshPrivateKeyPath' | grep -q 'non-existent'
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Unregister '{"name": "registered-container"}'
+
+# test io.systemd.MachineImage.List
+varlinkctl --more call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{}' | grep 'long-running'
+varlinkctl --more call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{}' | grep '.host'
+varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running"}'
+varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running", "acquireMetadata": true}' | grep 'OSRelease'
+
+# test io.systemd.MachineImage.Update
+varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.Update '{"name":"long-running", "newName": "long-running-renamed", "readOnly": true}'
+varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running-renamed"}'
+varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running-renamed"}' | jq '.readOnly' | grep 'true'
+
+varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.Update '{"name":"long-running-renamed", "newName": "long-running", "readOnly": false}'
+varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running"}'
+varlinkctl call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{"name":"long-running"}' | jq '.readOnly' | grep 'false'
diff --git a/test/units/TEST-17-UDEV.database.sh b/test/units/TEST-17-UDEV.database.sh
new file mode 100755
index 0000000000..2b66333cad
--- /dev/null
+++ b/test/units/TEST-17-UDEV.database.sh
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+udevadm control --log-level=debug
+
+IFNAME=test-udev-aaa
+ip link add "$IFNAME" type dummy
+IFINDEX=$(ip -json link show "$IFNAME" | jq '.[].ifindex')
+udevadm wait --timeout 10 "/sys/class/net/$IFNAME"
+# Check if the database file is created.
+[[ -e "/run/udev/data/n$IFINDEX" ]]
+
+ip link del "$IFNAME"
+udevadm wait --timeout 10 --removed --settle "/sys/class/net/$IFNAME"
+# CHeck if the database file is removed.
+[[ ! -e "/run/udev/data/n$IFINDEX" ]]
+
+udevadm control --log-level=info
+
+exit 0
diff --git a/test/units/TEST-50-DISSECT.dissect.sh b/test/units/TEST-50-DISSECT.dissect.sh
index 306ea1f504..6cf1213551 100755
--- a/test/units/TEST-50-DISSECT.dissect.sh
+++ b/test/units/TEST-50-DISSECT.dissect.sh
@@ -428,12 +428,13 @@ systemctl is-active testservice-50e.service
VBASE="vtest$RANDOM"
VDIR="/tmp/$VBASE.v"
EMPTY_VDIR="/tmp/$VBASE-empty.v"
+NONEXISTENT_VDIR="/tmp/$VBASE-nonexistent.v"
mkdir "$VDIR" "$EMPTY_VDIR"
ln -s /tmp/app0.raw "$VDIR/${VBASE}_0.raw"
ln -s /tmp/app1.raw "$VDIR/${VBASE}_1.raw"
-systemd-run -P -p ExtensionImages="$VDIR -$EMPTY_VDIR" bash -c '/opt/script1.sh | grep ID'
+systemd-run -P -p ExtensionImages="$VDIR -$EMPTY_VDIR -$NONEXISTENT_VDIR" bash -c '/opt/script1.sh | grep ID'
rm -rf "$VDIR" "$EMPTY_VDIR"
@@ -504,12 +505,13 @@ systemctl is-active testservice-50f.service
VBASE="vtest$RANDOM"
VDIR="/tmp/$VBASE.v"
EMPTY_VDIR="/tmp/$VBASE-empty.v"
+NONEXISTENT_VDIR="/tmp/$VBASE-nonexistent.v"
mkdir "$VDIR" "$EMPTY_VDIR"
ln -s "$IMAGE_DIR/app0" "$VDIR/${VBASE}_0"
ln -s "$IMAGE_DIR/app1" "$VDIR/${VBASE}_1"
-systemd-run -P --property ExtensionDirectories="$VDIR -$EMPTY_VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
+systemd-run -P --property ExtensionDirectories="$VDIR -$EMPTY_VDIR -$NONEXISTENT_VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
rm -rf "$VDIR" "$EMPTY_VDIR"
diff --git a/test/units/TEST-50-DISSECT.sysext.sh b/test/units/TEST-50-DISSECT.sysext.sh
index 3bc88999f8..b0f0792690 100755
--- a/test/units/TEST-50-DISSECT.sysext.sh
+++ b/test/units/TEST-50-DISSECT.sysext.sh
@@ -3,6 +3,9 @@
set -eux
set -o pipefail
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
FAKE_ROOTS_DIR="$(mktemp -d --tmpdir="" fake-roots-XXX)"
FSTYPE=$(stat --file-system --format "%T" /usr)
@@ -1008,5 +1011,23 @@ if [[ -w /usr ]]; then
fi
run_sysext_tests "$FAKE_ROOTS_DIR"
+install_extension_images
+
+# Test that mountpoints are carried over into and back from the sysext overlayfs.
+ln -s /tmp/app0.raw /var/lib/extensions/app0.raw
+mkdir /tmp/foo
+mount --bind /tmp/foo /usr/share
+systemd-sysext merge
+test -f /usr/lib/systemd/system/some_file
+mountpoint /usr/share
+touch /tmp/foo/abc
+test -f /usr/share/abc
+umount /usr/share
+test ! -f /usr/share/abc
+mount --bind /tmp/foo /usr/share
+systemd-sysext unmerge
+test ! -f /usr/lib/systemd/system/some_file
+mountpoint /usr/share
+umount /usr/share
exit 0
diff --git a/test/units/TEST-55-OOMD-workload.slice b/test/units/TEST-55-OOMD-workload.slice
index d117b754ba..1558073604 100644
--- a/test/units/TEST-55-OOMD-workload.slice
+++ b/test/units/TEST-55-OOMD-workload.slice
@@ -7,5 +7,3 @@ CPUAccounting=true
MemoryAccounting=true
IOAccounting=true
TasksAccounting=true
-ManagedOOMMemoryPressure=kill
-ManagedOOMMemoryPressureLimit=20%
diff --git a/test/units/TEST-55-OOMD-workload.slice.d/99-oom.conf b/test/units/TEST-55-OOMD-workload.slice.d/99-oom.conf
new file mode 100644
index 0000000000..1bed89c5ee
--- /dev/null
+++ b/test/units/TEST-55-OOMD-workload.slice.d/99-oom.conf
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Slice]
+# Fedora and friends has a drop-in config for the settings in
+# /usr/lib/systemd/user/slice.d/ . Hence, settings in the main .slice may be
+# overridden. Let's set below in a drop-in with higher decimal prefix.
+ManagedOOMMemoryPressure=kill
+ManagedOOMMemoryPressureLimit=20%
diff --git a/test/units/TEST-55-OOMD.sh b/test/units/TEST-55-OOMD.sh
index 944067c541..6eb066e57e 100755
--- a/test/units/TEST-55-OOMD.sh
+++ b/test/units/TEST-55-OOMD.sh
@@ -3,8 +3,10 @@
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
+. "$(dirname "$0")"/util.sh
systemd-analyze log-level debug
@@ -19,8 +21,6 @@ if [[ -s /skipped ]]; then
exit 77
fi
-rm -rf /run/systemd/system/TEST-55-OOMD-testbloat.service.d
-
# Activate swap file if we are in a VM
if systemd-detect-virt --vm --quiet; then
swapoff --all
@@ -73,6 +73,9 @@ if systemctl is-active systemd-oomd.service; then
systemctl restart systemd-oomd.service
fi
+# Check if the oomd.conf drop-in config is loaded.
+assert_in 'Default Memory Pressure Duration: 2s' "$(oomctl)"
+
if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
# If we're running with sanitizers, sd-executor might pull in quite a significant chunk of shared
# libraries, which in turn causes a lot of pressure that can put us in the front when sd-oomd decides to
@@ -92,68 +95,79 @@ else
systemd-run -t -p MemoryMax=10M -p MemorySwapMax=0 -p MemoryZSwapMax=0 /bin/true
fi
-systemctl start TEST-55-OOMD-testchill.service
-systemctl start TEST-55-OOMD-testbloat.service
+check_killed() {
+ local unit="${1:?}"
+ shift
-# Verify systemd-oomd is monitoring the expected units
-timeout 1m bash -xec 'until oomctl | grep "/TEST-55-OOMD-workload.slice"; do sleep 1; done'
-oomctl | grep "/TEST-55-OOMD-workload.slice"
-oomctl | grep "20.00%"
-oomctl | grep "Default Memory Pressure Duration: 2s"
+ systemctl "$@" status "$unit" || return 0 # Yay! The service has been expectedly killed.
-systemctl status TEST-55-OOMD-testchill.service
-
-# systemd-oomd watches for elevated pressure for 2 seconds before acting.
-# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
-for _ in {0..59}; do
- if ! systemctl status TEST-55-OOMD-testbloat.service; then
- break
- fi
- oomctl
- sleep 2
-done
-
-# testbloat should be killed and testchill should be fine
-if systemctl status TEST-55-OOMD-testbloat.service; then exit 42; fi
-if ! systemctl status TEST-55-OOMD-testchill.service; then exit 24; fi
-
-# Make sure we also work correctly on user units.
-loginctl enable-linger testuser
-
-systemctl start --machine "testuser@.host" --user TEST-55-OOMD-testchill.service
-systemctl start --machine "testuser@.host" --user TEST-55-OOMD-testbloat.service
-
-# Verify systemd-oomd is monitoring the expected units
-# Try to avoid racing the oomctl output check by checking in a loop with a timeout
-timeout 1m bash -xec 'until oomctl | grep "/TEST-55-OOMD-workload.slice"; do sleep 1; done'
-oomctl | grep -E "/user.slice.*/TEST-55-OOMD-workload.slice"
-oomctl | grep "20.00%"
-oomctl | grep "Default Memory Pressure Duration: 2s"
-
-systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testchill.service
-
-# systemd-oomd watches for elevated pressure for 2 seconds before acting.
-# It can take time to build up pressure so either wait 2 minutes or for the service to fail.
-for _ in {0..59}; do
- if ! systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testbloat.service; then
- break
+ # Workaround for the regression in kernel 6.12-rcX, explained in issue #32730.
+ if journalctl --no-hostname -k -t kernel --grep 'psi: inconsistent task state!'; then
+ echo "$unit is unexpectedly still alive, and inconsistency in PSI is reported by the kernel, skipping." >/skipped
+ exit 77
fi
- oomctl
- sleep 2
-done
-# testbloat should be killed and testchill should be fine
-if systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testbloat.service; then exit 42; fi
-if ! systemctl --machine "testuser@.host" --user status TEST-55-OOMD-testchill.service; then exit 24; fi
+ return 1 # Huh? Something borked.
+}
+
+test_basic() {
+ local cgroup_path="${1:?}"
+ shift
+
+ systemctl "$@" start TEST-55-OOMD-testchill.service
+ systemctl "$@" status TEST-55-OOMD-testchill.service
+ systemctl "$@" status TEST-55-OOMD-workload.slice
+
+ # Verify systemd-oomd is monitoring the expected units.
+ timeout 1m bash -xec "until oomctl | grep -q -F 'Path: $cgroup_path'; do sleep 1; done"
+ assert_in 'Memory Pressure Limit: 20.00%' \
+ "$(oomctl | tac | sed -e '/Memory Pressure Monitored CGroups:/q' | tac | grep -A8 "Path: $cgroup_path")"
+
+ systemctl "$@" start TEST-55-OOMD-testbloat.service
+
+ # systemd-oomd watches for elevated pressure for 2 seconds before acting.
+ # It can take time to build up pressure so either wait 2 minutes or for the service to fail.
+ for _ in {0..59}; do
+ if ! systemctl "$@" status TEST-55-OOMD-testbloat.service; then
+ break
+ fi
+ oomctl
+ sleep 2
+ done
+
+ # testbloat should be killed and testchill should be fine
+ if ! check_killed TEST-55-OOMD-testbloat.service "$@"; then exit 42; fi
+ if ! systemctl "$@" status TEST-55-OOMD-testchill.service; then exit 24; fi
+
+ systemctl "$@" kill --signal=KILL TEST-55-OOMD-testbloat.service || :
+ systemctl "$@" stop TEST-55-OOMD-testbloat.service
+ systemctl "$@" stop TEST-55-OOMD-testchill.service
+ systemctl "$@" stop TEST-55-OOMD-workload.slice
+}
+
+testcase_basic_system() {
+ test_basic /TEST.slice/TEST-55.slice/TEST-55-OOMD.slice/TEST-55-OOMD-workload.slice
+}
+
+testcase_basic_user() {
+ # Make sure we also work correctly on user units.
+ loginctl enable-linger testuser
-loginctl disable-linger testuser
+ test_basic "/user.slice/user-$(id -u testuser).slice/user@$(id -u testuser).service/TEST.slice/TEST-55.slice/TEST-55-OOMD.slice/TEST-55-OOMD-workload.slice" \
+ --machine "testuser@.host" --user
-# only run this portion of the test if we can set xattrs
-if cgroupfs_supports_user_xattrs; then
- sleep 120 # wait for systemd-oomd kill cool down and elevated memory pressure to come down
+ loginctl disable-linger testuser
+}
+
+testcase_preference_avoid() {
+ # only run this portion of the test if we can set xattrs
+ if ! cgroupfs_supports_user_xattrs; then
+ echo "cgroup does not support user xattrs, skipping test for ManagedOOMPreference=avoid"
+ return 0
+ fi
mkdir -p /run/systemd/system/TEST-55-OOMD-testbloat.service.d/
- cat >/run/systemd/system/TEST-55-OOMD-testbloat.service.d/override.conf <<EOF
+ cat >/run/systemd/system/TEST-55-OOMD-testbloat.service.d/99-managed-oom-preference.conf <<EOF
[Service]
ManagedOOMPreference=avoid
EOF
@@ -173,9 +187,98 @@ EOF
# testmunch should be killed since testbloat had the avoid xattr on it
if ! systemctl status TEST-55-OOMD-testbloat.service; then exit 25; fi
- if systemctl status TEST-55-OOMD-testmunch.service; then exit 43; fi
+ if ! check_killed TEST-55-OOMD-testmunch.service; then exit 43; fi
if ! systemctl status TEST-55-OOMD-testchill.service; then exit 24; fi
-fi
+
+ systemctl kill --signal=KILL TEST-55-OOMD-testbloat.service || :
+ systemctl kill --signal=KILL TEST-55-OOMD-testmunch.service || :
+ systemctl stop TEST-55-OOMD-testbloat.service
+ systemctl stop TEST-55-OOMD-testmunch.service
+ systemctl stop TEST-55-OOMD-testchill.service
+ systemctl stop TEST-55-OOMD-workload.slice
+
+ # clean up overrides since test cases can be run in any order
+ # and overrides shouldn't affect other tests
+ rm -rf /run/systemd/system/TEST-55-OOMD-testbloat.service.d
+ systemctl daemon-reload
+}
+
+testcase_duration_analyze() {
+ # Verify memory pressure duration is valid if >= 1 second
+ cat <<EOF >/tmp/TEST-55-OOMD-valid-duration.service
+[Service]
+ExecStart=echo hello
+ManagedOOMMemoryPressureDurationSec=1s
+EOF
+
+ # Verify memory pressure duration is invalid if < 1 second
+ cat <<EOF >/tmp/TEST-55-OOMD-invalid-duration.service
+[Service]
+ExecStart=echo hello
+ManagedOOMMemoryPressureDurationSec=0
+EOF
+
+ systemd-analyze --recursive-errors=no verify /tmp/TEST-55-OOMD-valid-duration.service
+ (! systemd-analyze --recursive-errors=no verify /tmp/TEST-55-OOMD-invalid-duration.service)
+
+ rm -f /tmp/TEST-55-OOMD-valid-duration.service
+ rm -f /tmp/TEST-55-OOMD-invalid-duration.service
+}
+
+testcase_duration_override() {
+ # Verify memory pressure duration can be overriden to non-zero values
+ mkdir -p /run/systemd/system/TEST-55-OOMD-testmunch.service.d/
+ cat >/run/systemd/system/TEST-55-OOMD-testmunch.service.d/99-duration-test.conf <<EOF
+[Service]
+ManagedOOMMemoryPressureDurationSec=3s
+ManagedOOMMemoryPressure=kill
+EOF
+
+ # Verify memory pressure duration will use default if set to empty
+ mkdir -p /run/systemd/system/TEST-55-OOMD-testchill.service.d/
+ cat >/run/systemd/system/TEST-55-OOMD-testchill.service.d/99-duration-test.conf <<EOF
+[Service]
+ManagedOOMMemoryPressureDurationSec=
+ManagedOOMMemoryPressure=kill
+EOF
+
+ systemctl daemon-reload
+ systemctl start TEST-55-OOMD-testmunch.service
+ systemctl start TEST-55-OOMD-testchill.service
+
+ timeout 1m bash -xec 'until oomctl | grep "/TEST-55-OOMD-testmunch.service"; do sleep 1; done'
+ oomctl | grep -A 2 "/TEST-55-OOMD-testmunch.service" | grep "Memory Pressure Duration: 3s"
+
+ timeout 1m bash -xec 'until oomctl | grep "/TEST-55-OOMD-testchill.service"; do sleep 1; done'
+ oomctl | grep -A 2 "/TEST-55-OOMD-testchill.service" | grep "Memory Pressure Duration: 2s"
+
+ [[ "$(systemctl show -P ManagedOOMMemoryPressureDurationUSec TEST-55-OOMD-testmunch.service)" == "3s" ]]
+ [[ "$(systemctl show -P ManagedOOMMemoryPressureDurationUSec TEST-55-OOMD-testchill.service)" == "[not set]" ]]
+
+ for _ in {0..59}; do
+ if ! systemctl status TEST-55-OOMD-testmunch.service; then
+ break
+ fi
+ oomctl
+ sleep 2
+ done
+
+ if ! check_killed TEST-55-OOMD-testmunch.service; then exit 44; fi
+ if ! systemctl status TEST-55-OOMD-testchill.service; then exit 23; fi
+
+ systemctl kill --signal=KILL TEST-55-OOMD-testmunch.service || :
+ systemctl stop TEST-55-OOMD-testmunch.service
+ systemctl stop TEST-55-OOMD-testchill.service
+ systemctl stop TEST-55-OOMD-workload.slice
+
+ # clean up overrides since test cases can be run in any order
+ # and overrides shouldn't affect other tests
+ rm -rf /run/systemd/system/TEST-55-OOMD-testmunch.service.d
+ rm -rf /run/systemd/system/TEST-55-OOMD-testchill.service.d
+ systemctl daemon-reload
+}
+
+run_testcases
systemd-analyze log-level info
diff --git a/test/units/TEST-58-REPART.sh b/test/units/TEST-58-REPART.sh
index 0af6f6ad28..b3181cea99 100755
--- a/test/units/TEST-58-REPART.sh
+++ b/test/units/TEST-58-REPART.sh
@@ -4,9 +4,6 @@
set -eux
set -o pipefail
-# shellcheck source=test/units/util.sh
-. "$(dirname "$0")"/util.sh
-
if ! command -v systemd-repart >/dev/null; then
echo "no systemd-repart" >/skipped
exit 77
diff --git a/test/units/TEST-60-MOUNT-RATELIMIT.sh b/test/units/TEST-60-MOUNT-RATELIMIT.sh
index a0e99dc40f..3f27a16beb 100755
--- a/test/units/TEST-60-MOUNT-RATELIMIT.sh
+++ b/test/units/TEST-60-MOUNT-RATELIMIT.sh
@@ -3,6 +3,8 @@
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
@@ -59,7 +61,7 @@ check_dependencies() {
# mount LOOP_0
mount -t ext4 "${LOOP_0}p1" /tmp/deptest
- sleep 1
+ timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_in "local-fs-pre.target" "$after"
assert_not_in "remote-fs-pre.target" "$after"
@@ -68,7 +70,7 @@ check_dependencies() {
assert_in "blockdev@${escaped_0}.target" "$after"
assert_not_in "${escaped_1}.device" "$after"
assert_not_in "blockdev@${escaped_1}.target" "$after"
- umount /tmp/deptest
+ systemctl stop tmp-deptest.mount
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
@@ -79,7 +81,7 @@ check_dependencies() {
# mount LOOP_1 (using fake _netdev option)
mount -t ext4 -o _netdev "${LOOP_1}p1" /tmp/deptest
- sleep 1
+ timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_not_in "local-fs-pre.target" "$after"
assert_in "remote-fs-pre.target" "$after"
@@ -88,7 +90,7 @@ check_dependencies() {
assert_not_in "blockdev@${escaped_0}.target" "$after"
assert_in "${escaped_1}.device" "$after"
assert_in "blockdev@${escaped_1}.target" "$after"
- umount /tmp/deptest
+ systemctl stop tmp-deptest.mount
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
@@ -99,7 +101,7 @@ check_dependencies() {
# mount tmpfs
mount -t tmpfs tmpfs /tmp/deptest
- sleep 1
+ timeout 10 bash -c 'until systemctl -q is-active tmp-deptest.mount; do sleep .1; done'
after=$(systemctl show --property=After --value tmp-deptest.mount)
assert_in "local-fs-pre.target" "$after"
assert_not_in "remote-fs-pre.target" "$after"
@@ -108,7 +110,7 @@ check_dependencies() {
assert_not_in "blockdev@${escaped_0}.target" "$after"
assert_not_in "${escaped_1}.device" "$after"
assert_not_in "blockdev@${escaped_1}.target" "$after"
- umount /tmp/deptest
+ systemctl stop tmp-deptest.mount
if [[ -f /run/systemd/system/tmp-deptest.mount ]]; then
after=$(systemctl show --property=After --value tmp-deptest.mount)
@@ -118,7 +120,9 @@ check_dependencies() {
fi
}
-test_dependencies() {
+testcase_dependencies() {
+ # test for issue #19983 and #23552.
+
if systemd-detect-virt --quiet --container; then
echo "Skipping test_dependencies in container"
return
@@ -152,7 +156,9 @@ EOF
check_dependencies
}
-test_issue_20329() {
+testcase_issue_20329() {
+ # test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
+
local tmpdir unit
tmpdir="$(mktemp -d)"
unit=$(systemd-escape --suffix mount --path "$tmpdir")
@@ -202,7 +208,9 @@ EOF
}
}
-test_issue_23796() {
+testcase_issue_23796() {
+ # test for reexecuting with background mount job
+
local mount_path mount_mytmpfs since
mount_path="$(command -v mount 2>/dev/null)"
@@ -244,77 +252,82 @@ EOF
done
}
-systemd-analyze log-level debug
-systemd-analyze log-target journal
+testcase_long_path() {
+ local long_path long_mnt ts
-NUM_DIRS=20
+ # make sure we can handle mounts at very long paths such that mount unit name must be hashed to fall within our unit name limit
+ long_path="$(printf "/$(printf "x%0.s" {1..255})%0.s" {1..7})"
+ long_mnt="$(systemd-escape --suffix=mount --path "$long_path")"
-# make sure we can handle mounts at very long paths such that mount unit name must be hashed to fall within our unit name limit
-LONGPATH="$(printf "/$(printf "x%0.s" {1..255})%0.s" {1..7})"
-LONGMNT="$(systemd-escape --suffix=mount --path "$LONGPATH")"
+ journalctl --sync
+ ts="$(date '+%H:%M:%S')"
-journalctl --sync
-TS="$(date '+%H:%M:%S')"
+ mkdir -p "$long_path"
+ mount -t tmpfs tmpfs "$long_path"
+ systemctl daemon-reload
-mkdir -p "$LONGPATH"
-mount -t tmpfs tmpfs "$LONGPATH"
-systemctl daemon-reload
+ # check that unit is active(mounted)
+ systemctl --no-pager show -p SubState --value "$long_path" | grep -q mounted
-# check that unit is active(mounted)
-systemctl --no-pager show -p SubState --value "$LONGPATH" | grep -q mounted
+ # check that relevant part of journal doesn't contain any errors related to unit
+ [ "$(journalctl -b --since="$ts" --priority=err | grep -c "$long_mnt")" = "0" ]
-# check that relevant part of journal doesn't contain any errors related to unit
-[ "$(journalctl -b --since="$TS" --priority=err | grep -c "$LONGMNT")" = "0" ]
+ # check that we can successfully stop the mount unit
+ systemctl stop "$long_path"
+ rm -rf "$long_path"
+}
-# check that we can successfully stop the mount unit
-systemctl stop "$LONGPATH"
-rm -rf "$LONGPATH"
+testcase_mount_ratelimit() {
+ local num_dirs=20
+ local ts i
-# mount/unmount enough times to trigger the /proc/self/mountinfo parsing rate limiting
+ # mount/unmount enough times to trigger the /proc/self/mountinfo parsing rate limiting
-for ((i = 0; i < NUM_DIRS; i++)); do
- mkdir "/tmp/meow${i}"
-done
+ for ((i = 0; i < num_dirs; i++)); do
+ mkdir "/tmp/meow${i}"
+ done
-# The following loop may produce many journal entries.
-# Let's process all pending entries before testing.
-journalctl --sync
-TS="$(date '+%H:%M:%S')"
+ # The following loop may produce many journal entries.
+ # Let's process all pending entries before testing.
+ journalctl --sync
+ ts="$(date '+%H:%M:%S')"
-for ((i = 0; i < NUM_DIRS; i++)); do
- mount -t tmpfs tmpfs "/tmp/meow${i}"
-done
+ for ((i = 0; i < num_dirs; i++)); do
+ mount -t tmpfs tmpfs "/tmp/meow${i}"
+ done
-systemctl daemon-reload
-systemctl list-units -t mount tmp-meow* | grep -q tmp-meow
+ systemctl daemon-reload
+ systemctl list-units -t mount tmp-meow* | grep -q tmp-meow
-for ((i = 0; i < NUM_DIRS; i++)); do
- umount "/tmp/meow${i}"
-done
+ for ((i = 0; i < num_dirs; i++)); do
+ umount "/tmp/meow${i}"
+ done
-# Figure out if we have entered the rate limit state.
-# If the infra is slow we might not enter the rate limit state; in that case skip the exit check.
-set +o pipefail
-journalctl --sync
-if timeout 2m journalctl -u init.scope --since="$TS" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) entered rate limit'; then
+ # Figure out if we have entered the rate limit state.
+ # If the infra is slow we might not enter the rate limit state; in that case skip the exit check.
+ set +o pipefail
journalctl --sync
- timeout 2m journalctl -u init.scope --since="$TS" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) left rate limit'
-fi
-set -o pipefail
-
-# Verify that the mount units are always cleaned up at the end.
-# Give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units.
-timeout 2m bash -c 'while systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; do systemctl daemon-reload; sleep 10; done'
+ if timeout 2m journalctl -u init.scope --since="$ts" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) entered rate limit'; then
+ journalctl --sync
+ timeout 2m journalctl -u init.scope --since="$ts" -n all --follow | grep -m 1 -q -F '(mount-monitor-dispatch) left rate limit'
+ fi
+ set -o pipefail
-# test for issue #19983 and #23552.
-test_dependencies
+ # Verify that the mount units are always cleaned up at the end.
+ # Give some time for units to settle so we don't race between exiting the rate limit state and cleaning up the units.
+ timeout 2m bash -c 'while systemctl list-units -t mount tmp-meow* | grep -q tmp-meow; do systemctl daemon-reload; sleep 10; done'
+}
-# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
-test_issue_20329
+systemd-analyze log-level debug
+systemd-analyze log-target journal
-# test for reexecuting with background mount job
-test_issue_23796
+mkdir -p /run/systemd/journald.conf.d
+cat >/run/systemd/journald.conf.d/99-ratelimit.conf <<EOF
+[Journal]
+RateLimitBurst=0
+EOF
+systemctl restart systemd-journald.service
-systemd-analyze log-level info
+run_testcases
touch /testok
diff --git a/test/units/TEST-64-UDEV-STORAGE.sh b/test/units/TEST-64-UDEV-STORAGE.sh
index 431b5305e4..6c8f6d63e6 100755
--- a/test/units/TEST-64-UDEV-STORAGE.sh
+++ b/test/units/TEST-64-UDEV-STORAGE.sh
@@ -219,6 +219,7 @@ testcase_nvme_basic() {
for i in "${expected_symlinks[@]}"; do
udevadm wait --settle --timeout=30 "$i"
done
+ test ! -e /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef
lsblk --noheadings | grep "^nvme"
[[ "$(lsblk --noheadings | grep -c "^nvme")" -ge 20 ]]
@@ -227,7 +228,6 @@ testcase_nvme_basic() {
testcase_nvme_subsystem() {
local expected_symlinks=(
# Controller(s)
- /dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef
/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_16
/dev/disk/by-id/nvme-QEMU_NVMe_Ctrl_deadbeef_17
# Shared namespaces
@@ -290,15 +290,21 @@ label: gpt
name="first_partition", size=5M
uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
EOF
+ # Partitioning triggers a synthesized event. Wait for the event being finished.
udevadm settle
+
udevadm lock --device /dev/disk/by-id/wwn-0xdeaddeadbeef0000-part2 \
mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" /dev/disk/by-id/wwn-0xdeaddeadbeef0000-part2
+ # Making filesystem triggers a synthesized event. Wait for the event being finished.
+ udevadm settle
modprobe -v dm_multipath
systemctl start multipathd.service
systemctl status multipathd.service
- multipath -ll
+ # multipathd touches many devices on start. multipath command may fail if it is invoked before the
+ # initial setup finished. Let's wait for a while.
udevadm settle
+ multipath -ll
ls -l /dev/disk/by-id/
for i in {0..15}; do
diff --git a/test/units/TEST-74-AUX-UTILS.busctl.sh b/test/units/TEST-74-AUX-UTILS.busctl.sh
index eb1f9e265d..4949f4bac7 100755
--- a/test/units/TEST-74-AUX-UTILS.busctl.sh
+++ b/test/units/TEST-74-AUX-UTILS.busctl.sh
@@ -46,6 +46,10 @@ busctl call -j \
busctl call --verbose --timeout=60 --expect-reply=yes \
org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
ListUnitsByPatterns asas 1 "active" 2 "systemd-*.socket" "*.mount"
+# show information passed fd
+busctl call --json=pretty \
+ org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
+ DumpByFileDescriptor | jq
busctl emit /org/freedesktop/login1 org.freedesktop.login1.Manager \
PrepareForSleep b false
diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh
index 9e0a429e41..5b46e11409 100755
--- a/test/units/TEST-74-AUX-UTILS.run.sh
+++ b/test/units/TEST-74-AUX-UTILS.run.sh
@@ -246,4 +246,16 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
# Let's chain a couple of run0 calls together, for fun
readarray -t cmdline < <(printf "%.0srun0\n" {0..31})
assert_eq "$("${cmdline[@]}" bash -c 'echo $SUDO_USER')" "$USER"
+
+ # Tests for working directory, especially for specifying "/" (see PR #34771).
+ cd /
+ assert_eq "$(run0 pwd)" "/"
+ assert_eq "$(run0 -D /tmp pwd)" "/tmp"
+ assert_eq "$(run0 --user=testuser pwd)" "/home/testuser"
+ assert_eq "$(run0 -D /tmp --user=testuser pwd)" "/tmp"
+ cd /tmp
+ assert_eq "$(run0 pwd)" "/tmp"
+ assert_eq "$(run0 -D / pwd)" "/"
+ assert_eq "$(run0 --user=testuser pwd)" "/home/testuser"
+ assert_eq "$(run0 -D / --user=testuser pwd)" "/"
fi
diff --git a/test/units/TEST-82-SOFTREBOOT.sh b/test/units/TEST-82-SOFTREBOOT.sh
index 57c6431ffc..9f76c86245 100755
--- a/test/units/TEST-82-SOFTREBOOT.sh
+++ b/test/units/TEST-82-SOFTREBOOT.sh
@@ -157,9 +157,9 @@ elif [ -f /run/TEST-82-SOFTREBOOT.touch ]; then
# Copy os-release away, so that we can manipulate it and check that it is updated in the propagate
# directory across soft reboots. Try to cover corner cases by truncating it.
- mkdir -p /tmp/nextroot-lower/usr/lib
- grep ID /etc/os-release >/tmp/nextroot-lower/usr/lib/os-release
- echo MARKER=1 >>/tmp/nextroot-lower/usr/lib/os-release
+ mkdir -p /tmp/nextroot-lower/etc
+ grep ID /etc/os-release >/tmp/nextroot-lower/etc/os-release
+ echo MARKER=1 >>/tmp/nextroot-lower/etc/os-release
cmp /etc/os-release /run/systemd/propagate/.os-release-stage/os-release
(! grep -q MARKER=1 /etc/os-release)