diff options
178 files changed, 4078 insertions, 2768 deletions
@@ -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. @@ -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. @@ -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", ¤t_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", ¤t_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 Binary files differnew file mode 100644 index 0000000000..76c3a4903c --- /dev/null +++ b/test/fuzz/fuzz-network-parser/oss-fuzz-372994449 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) |