---
-title: The File Descriptor Store
+title: File Descriptor Store
category: Interfaces
layout: default
SPDX-License-Identifier: LGPL-2.1-or-later
# The File Descriptor Store
*TL;DR: The systemd service manager may optionally maintain a set of file
-descriptors for each service, that are under control of the service and that
-help making service restarts without losing connectivity or context easier to
-implement.*
+descriptors for each service. Those file descriptors are under control of the
+service. Storing file descriptors in the manager makes is easier to restart
+services without dropping connections or losing state.*
Since its inception `systemd` has supported the *socket* *activation*
mechanism: the service manager creates and listens on some sockets (and similar
also in possession of the service itself, and it may (and is expected to)
invoke any operations on it that it likes.
-The primary use case of this logic is to permit services to restart seamlessly
+The primary use-case of this logic is to permit services to restart seamlessly
(for example to update them to a newer version), without losing execution
context, dropping pinned resources, terminating established connections or even
just momentarily losing connectivity. In fact, as the file descriptors can be
-uploaded freely at any time during the service runtime, this can even be used to
-implement services that robustly handle abnormal termination and can recover
+uploaded freely at any time during the service runtime, this can even be used
+to implement services that robustly handle abnormal termination and can recover
from that without losing pinned resources.
Note that Linux supports the
If set to values > 0, the fdstore is enabled. When invoked the service may now
(asynchronously) upload file descriptors to the fdstore via the
[`sd_pid_notify_with_fds()`](https://www.freedesktop.org/software/systemd/man/sd_pid_notify_with_fds.html)
-API call (or an equivalent reimplementation). When uploading the fds it is
+API call (or an equivalent re-implementation). When uploading the fds it is
necessary to set the `FDSTORE=1` field in the message, to indicate what the fd
is intended for. It's recommended to also set the `FDNAME=…` field to any
string of choice, which may be used to identify the fd later.
generally problematic. It also collides with the systemd service manager's
general principle of guaranteeing a pristine execution environment, a pristine
security context, and a pristine resource management context for freshly
-started services, without uncontrolled "left-overs" from previous runs. For
+started services, without uncontrolled "leftovers" from previous runs. For
example: leaving processes from previous runs generally negatively affects
-lifecycle management (i.e. `KillMode=none` must be set), which disables large
+life-cycle management (i.e. `KillMode=none` must be set), which disables large
parts of the service managers state tracking, resource management (as resource
counters cannot start at zero during service activation anymore, since the old
processes remaining skew them), security policies (as processes with possibly
out-of-date security policies – SElinux, AppArmor, any LSM, seccomp, BPF — in
effect remain), and similar.
-# File Descriptor Store Lifecycle
+# File Descriptor Store Life-cycle
By default any file descriptor stored in the fdstore for which a `POLLHUP` or
`POLLERR` is seen is automatically closed and removed from the fdstore. This
-behaviour can be turned off, by setting the `FDPOLL=0` field when uploading the
+behavior can be turned off, by setting the `FDPOLL=0` field when uploading the
fd via `sd_notify_with_fds()`.
The fdstore is automatically closed whenever the service is fully deactivated
service will leave the fdstore intact, but a separate stop and start job for
it — executed synchronously one after the other — will likely not.
-This behaviour can be modified via the
+This behavior can be modified via the
[`FileDescriptorStorePreserve=`](https://www.freedesktop.org/software/systemd/man/systemd.service.html#FileDescriptorStorePreserve=)
setting in service unit files. If set to `yes` the fdstore will be kept as long
as the service definition is loaded into memory by the service manager, i.e. as
uploaded an fd that the new version doesn't recognize anymore it's good idea to
close it both in the service and in the fdstore.
-Note that storing a duplicate of an fd in the fdstore means the fd remains
-pinned even if the service closes it. This in particular means that peers on a
-connection socket uploaded this way will not receive an automatic `POLLHUP`
-event anymore if the service code issues `close()` on the socket. It must
-accompany it with an `FDSTOREREMOVE=1` notification to the service manager, so
-that the fd is comprehensively closed.
+Note that storing a duplicate of an fd in the fdstore means the resource pinned
+by the fd remains pinned even if the service closes its duplicate of the
+fd. This in particular means that peers on a connection socket uploaded this
+way will not receive an automatic `POLLHUP` event anymore if the service code
+issues `close()` on the socket. It must accompany it with an `FDSTOREREMOVE=1`
+notification to the service manager, so that the fd is comprehensively closed.
# Access Control
This mechanism can be enabled either by making sure the service survives until
the very end (i.e. by setting `DefaultDependencies=no` so that it keeps running
for the whole system lifetime without being regularly deactivated at shutdown)
-or by setting `FileDescriptorStorePresever=yes` (and referencing the unit
+or by setting `FileDescriptorStorePreserve=yes` (and referencing the unit
continuously).
+For further details see [Resource
+Pass-Through](https://www.freedesktop.org/software/systemd/man/systemd-soft-reboot.service.html#Resource%20Pass-Through).
+
+# initrd Transitions
+
+The fdstore may also be used to hand over file descriptor to resources from the
+initrd context to the main system. This is important as code running off the
+initrd should generally not continue to run after the initrd to host
+transition, since it pins the backing files from the initrd, and might run a
+slightly different version of things than the host.
+
+Any service that still runs during the initrd→host transition will have its
+fdstore passed over the transition, where it will be passed back to any queued
+services of the same name.
+
+The soft reboot cycle transition and the initrd→host transition are
+semantically very similar, hence similar rules apply, and in both cases it is
+recommended to use the fdstore if pinned resources shall be passed over.
+
# Debugging
The
KEYBOARD_KEY_ce=unknown # Brightness Up, also emitted by acpi-video, ignore
KEYBOARD_KEY_ef=unknown # Brightness Down, also emitted by acpi-video, ignore
+# Predator PT314-52s
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnPredator*PT314-52s:pvr*
+ KEYBOARD_KEY_66=micmute # Microphone mute button
+
# Predator PH 315-52
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnPredator*PH*315-52:*
KEYBOARD_KEY_ef=kbdillumup # Fn+F10
<para>David Zeuthen's original Fedora
<ulink url="https://fedoraproject.org/wiki/Features/BetterHostname">Feature page about xdg-hostname</ulink></para>
</refsect1>
+
+ <refsect1>
+ <title>History</title>
+ <refsect2>
+ <title>The D-Bus API</title>
+ <para><varname>FirmwareVersion</varname> and
+ <function>GetHardwareSerial()</function> were added in version 251.</para>
+ <para><varname>OperatingSystemSupportEnd</varname>,
+ <varname>FirmwareVendor</varname>, and
+ <varname>FirmwareDate</varname> were added in version 253.</para>
+ </refsect2>
+ </refsect1>
</refentry>
</refsect1>
<xi:include href="org.freedesktop.locale1.xml" xpointer="versioning"/>
+
+ <refsect1>
+ <title>History</title>
+ <refsect2>
+ <title>The Manager Object</title>
+ <para><varname>HandlePowerKeyLongPress</varname>,
+ <varname>HandleRebootKey</varname>,
+ <varname>HandleRebootKeyLongPress</varname>,
+ <varname>HandleSuspendKeyLongPress</varname>, and
+ <varname>HandleHibernateKeyLongPress</varname> were added in version 251.</para>
+ <para><varname>StopIdleSessionUSec</varname> was added in version 252.</para>
+ <para><function>PrepareForShutdownWithMetadata</function> was added in version 255.</para>
+ </refsect2>
+ <refsect2>
+ <title>Session Objects</title>
+ <para><function>SetDisplay()</function> was added in version 252.</para>
+ <para><function>SetTTY()</function> was added in version 254.</para>
+ </refsect2>
+ </refsect1>
</refentry>
</refsect1>
<xi:include href="org.freedesktop.locale1.xml" xpointer="versioning"/>
+
+ <refsect1>
+ <title>History</title>
+ <refsect2>
+ <title>The Manager Object</title>
+ <para><function>CopyFromMachineWithFlags()</function> and
+ <function>CopyToMachineWithFlags()</function> were added in version 252.</para>
+ </refsect2>
+ <refsect2>
+ <title>Machine Objects</title>
+ <para><function>CopyFromWithFlags()</function> and
+ <function>CopyToWithFlags()</function> were added in version 252.</para>
+ </refsect2>
+ </refsect1>
</refentry>
</refsect1>
<xi:include href="org.freedesktop.locale1.xml" xpointer="versioning"/>
+
+ <refsect1>
+ <title>History</title>
+ <refsect2>
+ <title>DHCPv4 Client Object</title>
+ <para><varname>State</varname> was added in version 255.</para>
+ </refsect2>
+ <refsect2>
+ <title>DHCPv6 Client Object</title>
+ <para><varname>State</varname> was added in version 255.</para>
+ </refsect2>
+ </refsect1>
</refentry>
</refsect1>
<xi:include href="org.freedesktop.locale1.xml" xpointer="versioning"/>
+
+ <refsect1>
+ <title>History</title>
+ <refsect2>
+ <title>The Manager Object</title>
+ <para><function>Killed</function> was added in version 252.</para>
+ </refsect2>
+ </refsect1>
</refentry>
</refsect1>
<xi:include href="org.freedesktop.locale1.xml" xpointer="versioning"/>
+
+ <refsect1>
+ <title>History</title>
+ <refsect2>
+ <title>The Manager Object</title>
+ <para><function>GetImageStateWithExtensions()</function> was added in version 251.</para>
+ </refsect2>
+ <refsect2>
+ <title>The Image Object</title>
+ <para><function>GetStateWithExtensions()</function> was added in version 251.</para>
+ <para><function>ReattachWithExtensions()</function> was added in version 254.</para>
+ </refsect2>
+ </refsect1>
</refentry>
</refsect1>
<xi:include href="org.freedesktop.locale1.xml" xpointer="versioning"/>
+
+ <refsect1>
+ <title>History</title>
+ <refsect2>
+ <title>The Manager Object</title>
+ <para><varname>RuntimeWatchdogPreUSec</varname> and
+ <varname>RuntimeWatchdogPreGovernor</varname> were added in version 251.</para>
+ <para><varname>WatchdogDevice</varname>,
+ <varname>WatchdogLastPingTimestamp</varname>,
+ <varname>WatchdogLastPingTimestampMonotonic</varname>,
+ <varname>DefaultDeviceTimeoutUSec</varname>,
+ <function>DumpUnitsMatchingPatterns()</function>, and
+ <function>DumpUnitsMatchingPatternsByFileDescriptor()</function> were added in version 252.</para>
+ <para><function>GetUnitByPIDFD()</function> and
+ <function>DisableUnitFilesWithFlagsAndInstallInfo()</function> were added in version 253.</para>
+ <para><varname>ConfidentialVirtualization</varname>,
+ <varname>DefaultIOAccounting</varname>,
+ <varname>DefaultIPAccounting</varname>,
+ <varname>DefaultMemoryPressureThresholdUSec</varname>,
+ <varname>DefaultMemoryPressureWatch</varname>,
+ <function>QueueSignalUnit()</function>,
+ <function>SoftReboot()</function>, and
+ <function>DumpUnitFileDescriptorStore()</function> were added in version 254.</para>
+ </refsect2>
+ <refsect2>
+ <title>Unit Objects</title>
+ <para><varname>Upholds</varname> and
+ <varname>UpheldBy</varname> were added in version 251.</para>
+ <para><varname>AccessSELinuxContext</varname> and
+ <varname>ActivationDetails</varname> were added in version 252.</para>
+ <para><function>QueueSignal()</function> was added in version 254.</para>
+ </refsect2>
+ <refsect2>
+ <title>Service Unit Objects</title>
+ <para><varname>ControlGroupId</varname> and
+ <varname>ExtensionDirectories</varname> were added in version 251.</para>
+ <para><varname>OpenFile</varname>,
+ <varname>ReloadSignal</varname>,
+ <varname>MemoryZSwapMax</varname>, and
+ <varname>LogFilterPatterns</varname> were added in version 253.</para>
+ <para><varname>RestartMode</varname>,
+ <varname>RestartSteps</varname>,
+ <varname>RestartMaxDelayUSec</varname>,
+ <varname>RestartUSecNext</varname>,
+ <varname>FileDescriptorStorePreserve</varname>,
+ <function>DumpFileDescriptorStore()</function>,
+ <varname>DelegateSubgroup</varname>,
+ <varname>DefaultStartupMemoryLow</varname>,
+ <varname>StartupMemoryLow</varname>,
+ <varname>StartupMemoryHigh</varname>,
+ <varname>StartupMemoryMax</varname>,
+ <varname>StartupMemorySwapMax</varname>,
+ <varname>StartupMemoryZSwapMax</varname>,
+ <varname>MemoryPressureWatch</varname>,
+ <varname>MemoryPressureThresholdUSec</varname>,
+ <varname>RootEphemeral</varname>,
+ <varname>ImportCredential</varname>,
+ <varname>MemoryKSM</varname>,
+ <varname>RootImagePolicy</varname>,
+ <varname>MountImagePolicy</varname>, and
+ <varname>ExtensionImagePolicy</varname> were added in version 254.</para>
+ </refsect2>
+ <refsect2>
+ <title>Socket Unit Objects</title>
+ <para><varname>ControlGroupId</varname> and
+ <varname>ExtensionDirectories</varname> were added in version 251.</para>
+ <para><varname>MemoryZSwapMax</varname> and
+ <varname>LogFilterPatterns</varname> were added in version 253.</para>
+ <para><varname>DelegateSubgroup</varname>,
+ <varname>DefaultStartupMemoryLow</varname>,
+ <varname>StartupMemoryLow</varname>,
+ <varname>StartupMemoryHigh</varname>,
+ <varname>StartupMemoryMax</varname>,
+ <varname>StartupMemorySwapMax</varname>,
+ <varname>StartupMemoryZSwapMax</varname>,
+ <varname>MemoryPressureWatch</varname>,
+ <varname>MemoryPressureThresholdUSec</varname>,
+ <varname>RootEphemeral</varname>,
+ <varname>ImportCredential</varname>,
+ <varname>MemoryKSM</varname>,
+ <varname>RootImagePolicy</varname>,
+ <varname>MountImagePolicy</varname>, and
+ <varname>ExtensionImagePolicy</varname> were added in version 254.</para>
+ <para><varname>PollLimitIntervalUSec</varname> and
+ <varname>PollLimitBurst</varname> were added in version 255.</para>
+ </refsect2>
+ <refsect2>
+ <title>Mount Unit Objects</title>
+ <para><varname>ControlGroupId</varname> and
+ <varname>ExtensionDirectories</varname> were added in version 251.</para>
+ <para><varname>MemoryZSwapMax</varname> and
+ <varname>LogFilterPatterns</varname> were added in version 253.</para>
+ <para><varname>DelegateSubgroup</varname>,
+ <varname>DefaultStartupMemoryLow</varname>,
+ <varname>StartupMemoryLow</varname>,
+ <varname>StartupMemoryHigh</varname>,
+ <varname>StartupMemoryMax</varname>,
+ <varname>StartupMemorySwapMax</varname>,
+ <varname>StartupMemoryZSwapMax</varname>,
+ <varname>MemoryPressureWatch</varname>,
+ <varname>MemoryPressureThresholdUSec</varname>,
+ <varname>RootEphemeral</varname>,
+ <varname>ImportCredential</varname>,
+ <varname>MemoryKSM</varname>,
+ <varname>RootImagePolicy</varname>,
+ <varname>MountImagePolicy</varname>, and
+ <varname>ExtensionImagePolicy</varname> were added in version 254.</para>
+ </refsect2>
+ <refsect2>
+ <title>Swap Unit Objects</title>
+ <para><varname>ControlGroupId</varname> and
+ <varname>ExtensionDirectories</varname> were added in version 251.</para>
+ <para><varname>MemoryZSwapMax</varname> and
+ <varname>LogFilterPatterns</varname> were added in version 253.</para>
+ <para><varname>DelegateSubgroup</varname>,
+ <varname>DefaultStartupMemoryLow</varname>,
+ <varname>StartupMemoryLow</varname>,
+ <varname>StartupMemoryHigh</varname>,
+ <varname>StartupMemoryMax</varname>,
+ <varname>StartupMemorySwapMax</varname>,
+ <varname>StartupMemoryZSwapMax</varname>,
+ <varname>MemoryPressureWatch</varname>,
+ <varname>MemoryPressureThresholdUSec</varname>,
+ <varname>RootEphemeral</varname>,
+ <varname>ImportCredential</varname>,
+ <varname>MemoryKSM</varname>,
+ <varname>RootImagePolicy</varname>,
+ <varname>MountImagePolicy</varname>, and
+ <varname>ExtensionImagePolicy</varname> were added in version 254.</para>
+ </refsect2>
+ <refsect2>
+ <title>Slice Unit Objects</title>
+ <para><varname>ControlGroupId</varname> was added in version 251.</para>
+ <para><varname>MemoryZSwapMax</varname> was added in version 253.</para>
+ <para><varname>DelegateSubgroup</varname>,
+ <varname>DefaultStartupMemoryLow</varname>,
+ <varname>StartupMemoryLow</varname>,
+ <varname>StartupMemoryHigh</varname>,
+ <varname>StartupMemoryMax</varname>,
+ <varname>StartupMemorySwapMax</varname>,
+ <varname>StartupMemoryZSwapMax</varname>,
+ <varname>MemoryPressureWatch</varname>, and
+ <varname>MemoryPressureThresholdUSec</varname> were added in version 254.</para>
+ </refsect2>
+ <refsect2>
+ <title>Scope Unit Objects</title>
+ <para><varname>ControlGroupId</varname> was added in version 251.</para>
+ <para><varname>OOMPolicy</varname> and
+ <varname>MemoryZSwapMax</varname> were added in version 253.</para>
+ <para><varname>DelegateSubgroup</varname>,
+ <varname>DefaultStartupMemoryLow</varname>,
+ <varname>StartupMemoryLow</varname>,
+ <varname>StartupMemoryHigh</varname>,
+ <varname>StartupMemoryMax</varname>,
+ <varname>StartupMemorySwapMax</varname>,
+ <varname>StartupMemoryZSwapMax</varname>,
+ <varname>MemoryPressureWatch</varname>, and
+ <varname>MemoryPressureThresholdUSec</varname> were added in version 254.</para>
+ </refsect2>
+ <refsect2>
+ <title>Job Objects</title>
+ <para><varname>ActivationDetails</varname> was added in version 252.</para>
+ </refsect2>
+ </refsect1>
</refentry>
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-14 10:32+0100\n"
-"PO-Revision-Date: 2023-07-22 16:21+0000\n"
+"PO-Revision-Date: 2023-09-19 14:35+0000\n"
"Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n"
"Language-Team: Czech <https://translate.fedoraproject.org/projects/systemd/"
"master/cs/>\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
-"X-Generator: Weblate 4.18.2\n"
+"X-Generator: Weblate 5.0.2\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#, c-format
msgid "Home of user %s is currently absent, please plug in the necessary storage device or backing file system."
msgstr ""
+"Domovská složka uživatele %s v tuto chvíli není dostupná, připojte prosím "
+"potřebné úložné zařízení nebo záložní souborový systém."
#: src/home/pam_systemd_home.c:292
#, c-format
msgid "Too frequent login attempts for user %s, try again later."
-msgstr ""
+msgstr "Příliš časté pokusy o přihlášení uživatele %s, zkuste to znovu později."
#: src/home/pam_systemd_home.c:304
msgid "Password: "
#, c-format
msgid "Password/recovery key incorrect or not sufficient for authentication of user %s."
msgstr ""
+"Heslo/obnovovací klíč jsou nesprávné nebo nedostatečné pro ověření uživatele "
+"%s."
#: src/home/pam_systemd_home.c:332
msgid "Sorry, reenter recovery key: "
#: src/home/pam_systemd_home.c:352
#, c-format
msgid "Security token of user %s not inserted."
-msgstr ""
+msgstr "Bezpečnostní token uživatele %s není vložen."
#: src/home/pam_systemd_home.c:353 src/home/pam_systemd_home.c:356
msgid "Try again with password: "
-msgstr ""
+msgstr "Zkuste znovu s heslem: "
#: src/home/pam_systemd_home.c:355
#, c-format
msgid "Password incorrect or not sufficient, and configured security token of user %s not inserted."
msgstr ""
+"Nesprávné nebo nedostatečné heslo a není vložen nakonfigurovaný bezpečnostní "
+"token uživatele %s."
#: src/home/pam_systemd_home.c:376
msgid "Security token PIN: "
-msgstr ""
+msgstr "PIN bezpečnostního tokenu: "
#: src/home/pam_systemd_home.c:393
#, c-format
msgid "Please authenticate physically on security token of user %s."
-msgstr ""
+msgstr "Ověřte se prosím fyzicky na bezpečnostním tokenu uživatele %s."
#: src/home/pam_systemd_home.c:404
#, c-format
msgid "Please confirm presence on security token of user %s."
-msgstr ""
+msgstr "Potvrďte prosím přítomnost na bezpečnostním tokenu uživatele %s."
#: src/home/pam_systemd_home.c:415
#, c-format
msgid "Please verify user on security token of user %s."
-msgstr ""
+msgstr "Ověřte prosím uživatele na bezpečnostním tokenu uživatele %s."
#: src/home/pam_systemd_home.c:424
msgid "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"
msgstr ""
+"PIN bezpečnostního tokenu je uzamčen, nejprve jej prosím odemkněte. (Tip: "
+"Vyjmutí a opětovné vložení může stačit.)"
#: src/home/pam_systemd_home.c:432
#, c-format
msgid "Security token PIN incorrect for user %s."
-msgstr ""
+msgstr "Nesprávný PIN bezpečnostního tokenu pro uživatele %s."
#: src/home/pam_systemd_home.c:433 src/home/pam_systemd_home.c:452
#: src/home/pam_systemd_home.c:471
msgid "Sorry, retry security token PIN: "
-msgstr ""
+msgstr "Je nám líto, zkuste PIN bezpečnostního tokenu znovu: "
#: src/home/pam_systemd_home.c:451
#, c-format
msgid "Security token PIN of user %s incorrect (only a few tries left!)"
msgstr ""
+"PIN bezpečnostního tokenu uživatele %s je nesprávný (zbývá jen několik "
+"pokusů!)"
#: src/home/pam_systemd_home.c:470
#, c-format
msgid "Security token PIN of user %s incorrect (only one try left!)"
msgstr ""
+"PIN bezpečnostního tokenu uživatele %s je nesprávný (zbývá pouze jeden "
+"pokus!)"
#: src/home/pam_systemd_home.c:616
#, c-format
#include "user-util.h"
#include "xattr-util.h"
-static int cg_enumerate_items(const char *controller, const char *path, FILE **_f, const char *item) {
+static int cg_enumerate_items(const char *controller, const char *path, FILE **ret, const char *item) {
_cleanup_free_ char *fs = NULL;
FILE *f;
int r;
- assert(_f);
+ assert(ret);
r = cg_get_path(controller, path, item, &fs);
if (r < 0)
if (!f)
return -errno;
- *_f = f;
+ *ret = f;
return 0;
}
-int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
- return cg_enumerate_items(controller, path, _f, "cgroup.procs");
+int cg_enumerate_processes(const char *controller, const char *path, FILE **ret) {
+ return cg_enumerate_items(controller, path, ret, "cgroup.procs");
}
-int cg_read_pid(FILE *f, pid_t *_pid) {
+int cg_read_pid(FILE *f, pid_t *ret) {
unsigned long ul;
- /* Note that the cgroup.procs might contain duplicates! See
- * cgroups.txt for details. */
+ /* Note that the cgroup.procs might contain duplicates! See cgroups.txt for details. */
assert(f);
- assert(_pid);
+ assert(ret);
errno = 0;
if (fscanf(f, "%lu", &ul) != 1) {
- if (feof(f))
+ if (feof(f)) {
+ *ret = 0;
return 0;
+ }
return errno_or_else(EIO);
}
if (ul <= 0)
return -EIO;
+ if (ul > PID_T_MAX)
+ return -EIO;
- *_pid = (pid_t) ul;
+ *ret = (pid_t) ul;
return 1;
}
return supported;
}
-int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
+int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret) {
_cleanup_free_ char *fs = NULL;
- int r;
DIR *d;
+ int r;
- assert(_d);
+ assert(ret);
/* This is not recursive! */
if (!d)
return -errno;
- *_d = d;
+ *ret = d;
return 0;
}
-int cg_read_subgroup(DIR *d, char **fn) {
+int cg_read_subgroup(DIR *d, char **ret) {
assert(d);
- assert(fn);
+ assert(ret);
FOREACH_DIRENT_ALL(de, d, return -errno) {
char *b;
if (!b)
return -ENOMEM;
- *fn = b;
+ *ret = b;
return 1;
}
+ *ret = NULL;
return 0;
}
* generate paths with multiple adjacent / removed.
*/
-int cg_enumerate_processes(const char *controller, const char *path, FILE **_f);
-int cg_read_pid(FILE *f, pid_t *_pid);
-int cg_read_event(const char *controller, const char *path, const char *event,
- char **val);
+int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
+int cg_read_pid(FILE *f, pid_t *ret);
+int cg_read_event(const char *controller, const char *path, const char *event, char **ret);
-int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d);
-int cg_read_subgroup(DIR *d, char **fn);
+int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret);
+int cg_read_subgroup(DIR *d, char **ret);
typedef enum CGroupFlags {
CGROUP_SIGCONT = 1 << 0,
return pidref && pidref->pid > 0;
}
+static inline bool pidref_equal(const PidRef *a, const PidRef *b) {
+
+ if (pidref_is_set(a)) {
+ if (!pidref_is_set(b))
+ return false;
+
+ return a->pid == b->pid;
+ }
+
+ return !pidref_is_set(b);
+}
+
int pidref_set_pid(PidRef *pidref, pid_t pid);
int pidref_set_pidstr(PidRef *pidref, const char *pid);
int pidref_set_pidfd(PidRef *pidref, int fd);
#include "proto/device-path.h"
#include "proto/simple-text-io.h"
#include "random-seed.h"
+#include "sbat.h"
#include "secure-boot.h"
#include "shim.h"
#include "ticks.h"
"VERSION=\"" GIT_VERSION "\"\n"
"NAME=\"systemd-boot " GIT_VERSION "\"\n";
+DECLARE_SBAT(SBAT_BOOT_SECTION_TEXT);
+
typedef enum LoaderType {
LOADER_UNDEFINED,
LOADER_AUTO,
if (err != EFI_SUCCESS)
return log_error_status(err, "Error loading %ls: %m", entry->loader);
- if (entry->devicetree) {
+ /* DTBs are loaded by the kernel before ExitBootServices, and they can be used to map and assign
+ * arbitrary memory ranges, so skip it when secure boot is enabled as the DTB here is unverified. */
+ if (entry->devicetree && !secure_boot_enabled()) {
err = devicetree_install(&dtstate, image_root, entry->devicetree);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error loading %ls: %m", entry->devicetree);
#include "console.h"
#include "proto/security-arch.h"
-#include "sbat.h"
#include "secure-boot.h"
#include "util.h"
#include "vmm.h"
return decode_secure_boot_mode(secure, audit, deployed, setup);
}
-#ifdef SBAT_DISTRO
-static const char sbat[] _used_ _section_(".sbat") = SBAT_SECTION_TEXT;
-#endif
-
EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool force) {
assert(root_dir);
assert(path);
#include "pe.h"
#include "proto/shell-parameters.h"
#include "random-seed.h"
+#include "sbat.h"
#include "secure-boot.h"
#include "shim.h"
#include "splash.h"
/* magic string to find in the binary image */
_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
+DECLARE_SBAT(SBAT_STUB_SECTION_TEXT);
+
static EFI_STATUS combine_initrd(
EFI_PHYSICAL_ADDRESS initrd_base, size_t initrd_size,
const void * const extra_initrds[], const size_t extra_initrd_sizes[], size_t n_extra_initrds,
[SCOPE_ABANDONED] = UNIT_ACTIVE,
[SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING,
[SCOPE_STOP_SIGKILL] = UNIT_DEACTIVATING,
- [SCOPE_FAILED] = UNIT_FAILED
+ [SCOPE_FAILED] = UNIT_FAILED,
};
static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
if (!IN_SET(state, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL, SCOPE_START_CHOWN, SCOPE_RUNNING))
s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source);
- if (IN_SET(state, SCOPE_DEAD, SCOPE_FAILED)) {
+ if (!IN_SET(old_state, SCOPE_DEAD, SCOPE_FAILED) && IN_SET(state, SCOPE_DEAD, SCOPE_FAILED)) {
unit_unwatch_all_pids(UNIT(s));
unit_dequeue_rewatch_pids(UNIT(s));
}
r = unit_attach_pids_to_cgroup(u, u->pids, NULL);
if (r < 0) {
log_unit_warning_errno(u, r, "Failed to add PIDs to scope's control group: %m");
- scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
- return r;
+ goto fail;
}
if (r == 0) {
- log_unit_warning(u, "No PIDs left to attach to the scope's control group, refusing.");
- scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
- return -ECHILD;
+ r = log_unit_warning_errno(u, SYNTHETIC_ERRNO(ECHILD), "No PIDs left to attach to the scope's control group, refusing.");
+ goto fail;
}
log_unit_debug(u, "%i %s added to scope's control group.", r, r == 1 ? "process" : "processes");
/* Start watching the PIDs currently in the scope (legacy hierarchy only) */
(void) unit_enqueue_rewatch_pids(u);
return 1;
+
+fail:
+ scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
+ return r;
}
static int scope_start(Unit *u) {
if (pidref->pid == getpid_cached())
return -EINVAL;
- if (s->main_pid.pid == pidref->pid && s->main_pid_known) {
+ if (pidref_equal(&s->main_pid, pidref) && s->main_pid_known) {
pidref_done(pidref);
return 0;
}
- if (s->main_pid.pid != pidref->pid) {
+ if (!pidref_equal(&s->main_pid, pidref)) {
service_unwatch_main_pid(s);
exec_status_start(&s->main_exec_status, pidref->pid);
}
dummy_t __empty__ ## name; \
type name[]; \
}
+
+#ifdef SBAT_DISTRO
+ #define DECLARE_SBAT(text) \
+ static const char sbat[] _used_ _section_(".sbat") = (text)
+#else
+ #define DECLARE_SBAT(text)
+#endif
#ifdef SBAT_DISTRO
# include "version.h"
-# define SBAT_SECTION_TEXT \
- "sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md\n" \
- SBAT_PROJECT ",1,The systemd Developers," SBAT_PROJECT "," PROJECT_VERSION "," PROJECT_URL "\n" \
- SBAT_PROJECT "." SBAT_DISTRO "," STRINGIFY(SBAT_DISTRO_GENERATION) "," SBAT_DISTRO_SUMMARY "," SBAT_DISTRO_PKGNAME "," SBAT_DISTRO_VERSION "," SBAT_DISTRO_URL "\n"
+# define SBAT_MAGIC "sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md\n"
+# define SBAT_BOOT_SECTION_TEXT \
+ SBAT_MAGIC \
+ SBAT_PROJECT "-boot" ",1,The systemd Developers," SBAT_PROJECT "," PROJECT_VERSION "," PROJECT_URL "\n" \
+ SBAT_PROJECT "-boot" "." SBAT_DISTRO "," STRINGIFY(SBAT_DISTRO_GENERATION) "," SBAT_DISTRO_SUMMARY "," SBAT_DISTRO_PKGNAME "," SBAT_DISTRO_VERSION "," SBAT_DISTRO_URL "\n"
+# define SBAT_STUB_SECTION_TEXT \
+ SBAT_MAGIC \
+ SBAT_PROJECT "-stub" ",1,The systemd Developers," SBAT_PROJECT "," PROJECT_VERSION "," PROJECT_URL "\n" \
+ SBAT_PROJECT "-stub" "." SBAT_DISTRO "," STRINGIFY(SBAT_DISTRO_GENERATION) "," SBAT_DISTRO_SUMMARY "," SBAT_DISTRO_PKGNAME "," SBAT_DISTRO_VERSION "," SBAT_DISTRO_URL "\n"
#endif
/* If we got an exact match, this is the best */
matching = 10;
else {
- /* We have multiple X layouts, look for an
- * entry that matches our key with everything
- * but the first layout stripped off. */
- if (startswith_comma(xc->layout, a[1]))
- matching = 5;
+ /* see if we get an exact match with the order reversed */
+ _cleanup_strv_free_ char **b = NULL;
+ _cleanup_free_ char *c = NULL;
+ r = strv_split_full(&b, a[1], ",", 0);
+ if (r < 0)
+ return r;
+ strv_reverse(b);
+ c = strv_join(b, ",");
+ if (!c)
+ return log_oom();
+ if (streq(xc->layout, c))
+ matching = 9;
else {
- _cleanup_free_ char *x = NULL;
-
- /* If that didn't work, strip off the
- * other layouts from the entry, too */
- x = strdupcspn(a[1], ",");
- if (!x)
- return -ENOMEM;
- if (startswith_comma(xc->layout, x))
- matching = 1;
+ /* We have multiple X layouts, look for an
+ * entry that matches our key with everything
+ * but the first layout stripped off. */
+ if (startswith_comma(xc->layout, a[1]))
+ matching = 5;
+ else {
+ _cleanup_free_ char *x = NULL;
+
+ /* If that didn't work, strip off the
+ * other layouts from the entry, too */
+ x = strdupcspn(a[1], ",");
+ if (!x)
+ return -ENOMEM;
+ if (startswith_comma(xc->layout, x))
+ matching = 1;
+ }
}
}
if (isempty(xc->model) || streq_ptr(xc->model, a[2])) {
matching++;
- if (streq_ptr(xc->variant, a[3])) {
+ if (streq_ptr(xc->variant, a[3]) || (isempty(xc->variant) && streq(a[3], "-"))) {
matching++;
if (streq_ptr(xc->options, a[4]))
}
}
- if (best_matching < 10 && !isempty(xc->layout)) {
+ if (best_matching < 9 && !isempty(xc->layout)) {
_cleanup_free_ char *l = NULL, *v = NULL, *converted = NULL;
/* The best match is only the first part of the X11
assert_se(streq(vc.keymap, "es-dvorak"));
vc_context_clear(&vc);
+ /* es no-variant test is not very good as the desired match
+ comes first in the list so will win if both candidates score
+ the same. in this case the desired match comes second so will
+ not win unless we correctly give the no-variant match a bonus
+ */
+ log_info("/* test without variant, desired match second (bg,us:) */");
+ assert_se(free_and_strdup(&xc.layout, "bg,us") >= 0);
+ assert_se(free_and_strdup(&xc.variant, NULL) >= 0);
+ assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0);
+ assert_se(streq(vc.keymap, "bg_bds-utf8"));
+ vc_context_clear(&vc);
+
log_info("/* test with old mapping (fr:latin9) */");
assert_se(free_and_strdup(&xc.layout, "fr") >= 0);
assert_se(free_and_strdup(&xc.variant, "latin9") >= 0);
assert_se(streq(vc.keymap, "fr-latin9"));
vc_context_clear(&vc);
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1039185 */
+ /* us,ru is the x config users want, but they still want ru
+ as the console layout in this case */
log_info("/* test with a compound mapping (us,ru:) */");
assert_se(free_and_strdup(&xc.layout, "us,ru") >= 0);
assert_se(free_and_strdup(&xc.variant, NULL) >= 0);
assert_se(x11_convert_to_vconsole(&xc, &vc) >= 0);
- assert_se(streq(vc.keymap, "us"));
+ assert_se(streq(vc.keymap, "ru"));
vc_context_clear(&vc);
log_info("/* test with a compound mapping (ru,us:) */");
return true;
}
-static uint32_t address_prefix(const Address *a) {
+static int address_ipv4_prefix(const Address *a, struct in_addr *ret) {
+ struct in_addr p;
+ int r;
+
assert(a);
+ assert(a->family == AF_INET);
+ assert(ret);
- /* make sure we don't try to shift by 32.
- * See ISO/IEC 9899:TC3 § 6.5.7.3. */
- if (a->prefixlen == 0)
- return 0;
+ p = in4_addr_is_set(&a->in_addr_peer.in) ? a->in_addr_peer.in : a->in_addr.in;
+ r = in4_addr_mask(&p, a->prefixlen);
+ if (r < 0)
+ return r;
- if (a->in_addr_peer.in.s_addr != 0)
- return be32toh(a->in_addr_peer.in.s_addr) >> (32 - a->prefixlen);
- else
- return be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen);
+ *ret = p;
+ return 0;
}
static void address_hash_func(const Address *a, struct siphash *state) {
siphash24_compress(&a->family, sizeof(a->family), state);
switch (a->family) {
- case AF_INET:
+ case AF_INET: {
+ struct in_addr prefix;
+
siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
- uint32_t prefix = address_prefix(a);
+ assert_se(address_ipv4_prefix(a, &prefix) >= 0);
siphash24_compress(&prefix, sizeof(prefix), state);
- _fallthrough_;
+ siphash24_compress(&a->in_addr.in, sizeof(a->in_addr.in), state);
+ break;
+ }
case AF_INET6:
- siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
+ siphash24_compress(&a->in_addr.in6, sizeof(a->in_addr.in6), state);
+
+ if (in6_addr_is_null(&a->in_addr.in6))
+ siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
break;
+
default:
/* treat any other address family as AF_UNSPEC */
break;
return r;
switch (a1->family) {
- case AF_INET:
+ case AF_INET: {
+ struct in_addr p1, p2;
+
/* See kernel's find_matching_ifa() in net/ipv4/devinet.c */
r = CMP(a1->prefixlen, a2->prefixlen);
if (r != 0)
return r;
- r = CMP(address_prefix(a1), address_prefix(a2));
+ assert_se(address_ipv4_prefix(a1, &p1) >= 0);
+ assert_se(address_ipv4_prefix(a2, &p2) >= 0);
+ r = memcmp(&p1, &p2, sizeof(p1));
if (r != 0)
return r;
- _fallthrough_;
+ return memcmp(&a1->in_addr.in, &a2->in_addr.in, sizeof(a1->in_addr.in));
+ }
case AF_INET6:
/* See kernel's ipv6_get_ifaddr() in net/ipv6/addrconf.c */
- return memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family));
+ r = memcmp(&a1->in_addr.in6, &a2->in_addr.in6, sizeof(a1->in_addr.in6));
+ if (r != 0)
+ return r;
+
+ /* To distinguish IPv6 null addresses with different prefixlen, e.g. ::48 vs ::64, let's
+ * compare the prefix length. */
+ if (in6_addr_is_null(&a1->in_addr.in6))
+ r = CMP(a1->prefixlen, a2->prefixlen);
+
+ return r;
+
default:
/* treat any other address family as AF_UNSPEC */
return 0;
_cleanup_(unlink_and_freep) char *protofile = NULL;
char vol_id[CONST_MAX(SD_ID128_UUID_STRING_MAX, 8U + 1U)] = {};
int stdio_fds[3] = { -EBADF, STDERR_FILENO, STDERR_FILENO};
+ ForkFlags flags = FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|
+ FORK_CLOSE_ALL_FDS|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG;
int r;
assert(node);
if (extra_mkfs_args && strv_extend_strv(&argv, extra_mkfs_args, false) < 0)
return log_oom();
+ if (streq(fstype, "btrfs")) {
+ struct stat st;
+
+ if (stat(node, &st) < 0)
+ return log_error_errno(r, "Failed to stat '%s': %m", node);
+
+ if (S_ISBLK(st.st_mode))
+ flags |= FORK_NEW_MOUNTNS;
+ }
+
if (DEBUG_LOGGING) {
_cleanup_free_ char *j = NULL;
stdio_fds,
/*except_fds=*/ NULL,
/*n_except_fds=*/ 0,
- FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|
- FORK_CLOSE_ALL_FDS|FORK_REARRANGE_STDIO|FORK_NEW_MOUNTNS|FORK_REOPEN_LOG,
+ flags,
/*ret_pid=*/ NULL);
if (r < 0)
return r;
* on unformatted free space, so let's trick it and other mkfs tools into thinking no
* partitions are mounted. See https://github.com/kdave/btrfs-progs/issues/640 for more
° information. */
- (void) mount_nofollow_verbose(LOG_DEBUG, "/dev/null", "/proc/self/mounts", NULL, MS_BIND, NULL);
+ if (flags & FORK_NEW_MOUNTNS)
+ (void) mount_nofollow_verbose(LOG_DEBUG, "/dev/null", "/proc/self/mounts", NULL, MS_BIND, NULL);
execvp(mkfs, argv);
TEST(sbat_section_text) {
log_info("---SBAT-----------&<----------------------------------------\n"
+ "%s"
"%s"
"------------------>&-----------------------------------------",
#ifdef SBAT_DISTRO
- SBAT_SECTION_TEXT
+ SBAT_BOOT_SECTION_TEXT,
+ SBAT_STUB_SECTION_TEXT
#else
"(not defined)"
#endif
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=0.0.0.0/16
-Address=0.0.0.0/30
-Address=::/64
-
-[Address]
-Address=0.0.0.0/24
-# The broadcast address will be ignored.
-Broadcast=192.168.0.255
[Address]
Address=::/73
+[Address]
+Address=::/74
+
[Address]
Address=10.10.1.1/24
# just a random label which should exist
flag2: str,
flag3: str,
flag4: str,
+ ip4_null_16: str,
+ ip4_null_24: str,
+ ip6_null_73: str,
+ ip6_null_74: str,
):
output = check_output('ip address show dev dummy98')
print(output)
self.assertIn(f'inet6 2001:db8:0:f106::2/64 scope global{flag4}', output)
# null address
- self.assertRegex(output, r'inet [0-9]*.[0-9]*.0.1/16 brd [0-9]*.[0-9]*.255.255 scope global subnet16')
- self.assertRegex(output, r'inet [0-9]*.[0-9]*.[0-9]*.1/24 brd [0-9]*.[0-9]*.[0-9]*.255 scope global subnet24')
- self.assertRegex(output, r'inet6 [0-9a-f:]*:1/73 scope global')
+ self.assertTrue(ip4_null_16.endswith('.0.1'))
+ prefix16 = ip4_null_16[:-len('.0.1')]
+ self.assertTrue(ip4_null_24.endswith('.1'))
+ prefix24 = ip4_null_24[:-len('.1')]
+ self.assertIn(f'inet {ip4_null_16}/16 brd {prefix16}.255.255 scope global subnet16', output)
+ self.assertIn(f'inet {ip4_null_24}/24 brd {prefix24}.255 scope global subnet24', output)
+ self.assertIn(f'inet6 {ip6_null_73}/73 scope global', output)
+ self.assertIn(f'inet6 {ip6_null_74}/74 scope global', output)
# invalid sections
self.assertNotIn('10.4.4.1', output)
self.setup_nftset('ifindex', 'iface_index')
self.wait_online(['dummy98:routable'])
+
+ ip4_null_16 = None
+ ip4_null_24 = None
+ output = check_output('ip -4 --json address show dev dummy98')
+ for i in json.loads(output)[0]['addr_info']:
+ if i['label'] == 'subnet16':
+ ip4_null_16 = i['local']
+ elif i['label'] == 'subnet24':
+ ip4_null_24 = i['local']
+ self.assertTrue(ip4_null_16.endswith('.0.1'))
+ self.assertTrue(ip4_null_24.endswith('.1'))
+
+ ip6_null_73 = None
+ ip6_null_74 = None
+ output = check_output('ip -6 --json address show dev dummy98')
+ for i in json.loads(output)[0]['addr_info']:
+ if i['prefixlen'] == 73:
+ ip6_null_73 = i['local']
+ elif i['prefixlen'] == 74:
+ ip6_null_74 = i['local']
+ self.assertTrue(ip6_null_73.endswith(':1'))
+ self.assertTrue(ip6_null_74.endswith(':1'))
+
self.verify_address_static(
label1='label1',
label2='label2',
flag2='',
flag3=' noprefixroute',
flag4=' home mngtmpaddr',
+ ip4_null_16=ip4_null_16,
+ ip4_null_24=ip4_null_24,
+ ip6_null_73=ip6_null_73,
+ ip6_null_74=ip6_null_74,
)
# nft set
self.check_nftset('addr4', r'10\.10\.1\.1')
flag2=' noprefixroute',
flag3=' home mngtmpaddr',
flag4=' noprefixroute',
+ ip4_null_16=ip4_null_16,
+ ip4_null_24=ip4_null_24,
+ ip6_null_73=ip6_null_73,
+ ip6_null_74=ip6_null_74,
)
networkctl_reconfigure('dummy98')
flag2=' noprefixroute',
flag3=' home mngtmpaddr',
flag4=' noprefixroute',
+ ip4_null_16=ip4_null_16,
+ ip4_null_24=ip4_null_24,
+ ip6_null_73=ip6_null_73,
+ ip6_null_74=ip6_null_74,
)
# Tests for #20891.
flag2=' noprefixroute',
flag3=' home mngtmpaddr',
flag4=' noprefixroute',
+ ip4_null_16=ip4_null_16,
+ ip4_null_24=ip4_null_24,
+ ip6_null_73=ip6_null_73,
+ ip6_null_74=ip6_null_74,
)
# test for ENOBUFS issue #17012 (with reload)
for i in range(1, 254):
self.assertIn(f'inet 10.3.3.{i}/16 brd 10.3.255.255', output)
- def test_address_null(self):
- copy_network_unit('25-address-null.network', '12-dummy.netdev')
- start_networkd()
-
- self.wait_online(['dummy98:routable'])
-
- output = check_output('ip address show dev dummy98 scope global')
- print(output)
-
- ipv4_address_16 = re.findall(r'inet 172.[0-9]*.0.1/16 brd 172.[0-9]*.255.255', output)
- self.assertEqual(len(ipv4_address_16), 1)
- ipv4_address_24 = re.findall(r'inet 192.168.[0-9]*.1/24 brd 192.168.[0-9]*.255', output)
- self.assertEqual(len(ipv4_address_24), 1)
- ipv4_address_30 = re.findall(r'inet 192.168.[0-9]*.[0-9]*/30 brd 192.168.[0-9]*.[0-9]*', output)
- self.assertEqual(len(ipv4_address_30), 1)
- ipv6_address = re.findall(r'inet6 fd[0-9a-f:]*/64', output)
- self.assertEqual(len(ipv6_address), 1)
-
- networkctl_reconfigure('dummy98')
- self.wait_online(['dummy98:routable'])
-
- output = check_output('ip address show dev dummy98 scope global')
- print(output)
- self.assertIn(ipv4_address_16[0], output)
- self.assertIn(ipv4_address_24[0], output)
- self.assertIn(ipv4_address_30[0], output)
- self.assertIn(ipv6_address[0], output)
-
def test_address_ipv4acd(self):
check_output('ip netns add ns99')
check_output('ip link add veth99 type veth peer veth-peer')
print(f'''{prefix}}};''', file=file)
def document_has_elem_with_text(document, elem, item_repr):
- predicate = f".//{elem}" # [text() = 'foo'] doesn't seem supported :(
+ predicate = f".//{elem}[. = '{item_repr}']"
+
+ # Ignore mentions in the History section
+ history = document.find(".//refsect1[title = 'History']")
+ history_mentions = history.findall(predicate) if history else []
+
for loc in document.findall(predicate):
- if loc.text == item_repr:
- return True
+ if loc in history_mentions:
+ continue
+ return True
return False
def check_documented(document, declarations, stats):