python
python-lxml
qrencode
+ rsync
xz
zstd
Features:
+* nspawn: support uid mapping bind mounts, as defined available in kernel 5.12,
+ for all our disk image needs
+
+* homed: if kernel 5.12 uid mapping mounts exist, use that instead of recursive
+ chowns.
+
+* cryptsetup: tweak tpm2-device=auto logic, abort quickly if firmware tells us
+ there isn't any TPM2 device anyway. that way, we'll wait for the TPM2 device
+ to show up only if registered in LUKS header + the firmware suggests there is
+ a device worth waiting for.
+
+* systemd-sysext: optionally, run it in initrd already, before transitioning
+ into host, to open up possibility for services shipped like that.
+
+* add a flag to the GPT spec that says "grow my fs to partition size", and make
+ it settable via systemd-repart. Add in growfs jobs in
+ systemd-gpt-auto-generator when it is set, and issue the ioctls while
+ mounting in systemd-npsawn --image=. That way systemd-repart suffices to
+ enlarge an image.
+
+* add a new switch --auto-definitions=yes/no or so to systemd-repart. If
+ specified, synthesize a definition automatically if we can: enlarge last
+ partition on disk, but only if it is marked for growing and not read-only.
+
+* add a switch to homectl (maybe called --first-boot) where it will check if
+ any non-system users exist, and if not prompts interactively for basic user
+ info, mimicing systemd-firstboot. Then, place this in a service that runs
+ after systemd-homed, but before gdm and friends, as a simple, barebones
+ fallback logic to get a regular user created on uninitialized systems.
+
* maybe add a tool that displays most recent journal logs as QR code to scan
off screen and run it automatically on boot failures, emergency logs and
such. Use DRM APIs directly, see
* systemd-repart: read LUKS encryption key from $CREDENTIALS_PATH
* introduce /dev/disk/root/* symlinks that allow referencing partitions on the
- disk the rootfs is on in a reasonably secure way.
+ disk the rootfs is on in a reasonably secure way. (or maybe: add
+ /dev/gpt-auto-{home,srv,boot,…} similar in style to /dev/gpt-auto-root as we
+ already have it.
* systemd-repart: add a switch to factory reset the partition table without
immediately applying the new configuration again. i.e. --factory-reset=leave
* Add service setting to run a service within the specified VRF. i.e. do the
equivalent of "ip vrf exec".
-* export action of device object on sd-device, so that monitor becomes useful
-
-* add root=tmpfs that mounts a tmpfs to /sysroot (to be used in combination
- with usr=…, for a similar effect as systemd.volatile=yes but without the
- "hide-out" effect). Also, add root=gpt-auto-late support or so, that is like
- root=gpt-auto but initially mounts a tmpfs to /sysroot, and then revisits
- later after systemd-repart ran. Usecase: let's ship images with only /usr
- partition, then on first boot create the root partition. In this case we want
- to read the repart data from /usr before the root partition exists. Add
- usr=gpt-auto that automatically finds a /usr partition.
+* Add root=gpt-auto-late support or so, that is like root=gpt-auto but
+ initially mounts a tmpfs to /sysroot, and then revisits later after
+ systemd-repart ran. Usecase: let's ship images with only /usr partition, then
+ on first boot create the root partition. In this case we want to read the
+ repart data from /usr before the root partition exists. Add usr=gpt-auto that
+ automatically finds a /usr partition.
* change SwitchRoot() implementation in PID 1 to use pivot_root(".", "."), as
documented in the pivot_root(2) man page, so that we can drop the /oldroot
discovered Windows installation might have the identifier `auto-windows` or
`auto-windows-10` or so.).
-4. Similar, boot menu entries referring to Apple MacOS X installations should
+4. Similar, boot menu entries referring to Apple macOS installations should
use the identifier `osx` or one that is prefixed with `osx-`. If such an
entry is automatically discovered by the boot loader use `auto-osx` as
identifier, or `auto-osx-` as prefix for the identifier, see above.
Note that these configurations snippets do not need to be the only configuration source for a boot loader. It may extend this list of entries with additional items from other configuration files (for example its own native configuration files) or automatically detected other entries without explicit configuration.
-To make this explicitly clear: this specification is designed with "free" operating systems in mind, starting Windows or MacOS is out of focus with these configuration snippets, use boot-loader specific solutions for that. In the text above, if we say "OS" we hence imply "free", i.e. primarily Linux (though this could be easily be extended to the BSDs and whatnot).
+To make this explicitly clear: this specification is designed with "free" operating systems in mind, starting Windows or macOS is out of focus with these configuration snippets, use boot-loader specific solutions for that. In the text above, if we say "OS" we hence imply "free", i.e. primarily Linux (though this could be easily be extended to the BSDs and whatnot).
Note that all paths used in the configuration snippets use a Unix-style "/" as path separator. This needs to be converted to an EFI-style "\" separator in EFI boot loaders.
--- /dev/null
+---
+title: Package Metadata for Core Files
+category: Interfaces
+layout: default
+---
+
+# Package Metadata for Core Files
+
+*Intended audience: hackers working on userspace subsystems that create ELF binaries
+or parse ELF core files.*
+
+## Motivation
+
+ELF binaries get stamped with a unique, build-time generated hex string identifier called
+`build-id`, [which gets embedded as an ELF note called `.note.gnu.build-id`](https://fedoraproject.org/wiki/Releases/FeatureBuildId).
+In most cases, this allows to associate a stripped binary with its debugging information.
+It is used, for example, to dynamically fetch DWARF symbols from a debuginfo server, or
+to query the local package manager and find out the package metadata or, again, the DWARF
+symbols or program sources.
+
+However, this usage of the `build-id` requires either local metadata, usually set up by
+the package manager, or access to a remote server over the network. Both of those might
+be unavailable or forbidden.
+
+Thus it becomes desirable to add additional metadata to a binary at build time, so that
+`systemd-coredump` and other services analyzing core files are able to extract said
+metadata simply from the core file itself, without external dependencies.
+
+## Implementation
+
+This document will attempt to define a common metadata format specification, so that
+multiple implementers might use it when building packages, or core file analyzers, and
+so on.
+
+The metadata will be embedded in a single, new ELF header section, in a key-value JSON
+format. Implementers working on parsing core files should not assume a specific list of
+keys, but parse anything that is included in the section.
+Implementers working on build tools should strive to use the same key names, for
+consistency. The most common will be listed here. When corresponding to the content of
+os-release, the values should match, again for consistency.
+
+* Section header
+
+```
+SECTION: `.note.package`
+node-id: `0xcafe1a7e`
+Owner: `FDO` (FreeDesktop.org)
+Value: a JSON string with the structure described below
+```
+
+* JSON payload
+
+```json
+{
+ "type":"rpm", # this provides a namespace for the package+package-version fields
+ "os":"fedora",
+ "osVersion":"33",
+ "name":"coreutils",
+ "version": "4711.0815.fc13.arm32",
+ "osCpe": # A CPE name for the operating system, `CPE_NAME` from os-release is a good default
+}
+```
+
+A reference implementations of a [build-time tool is provided](https://github.com/systemd/package-notes)
+and can be used to generate a linker script, which can then be used at build time via
+```LDFLAGS="-Wl,-T,/path/to/generated/script"``` to include the note in the binary.
+
+Generator:
+```console
+$ ./generate-package-notes.py --rpm systemd-248~rc2-1.fc34
+SECTIONS
+{
+ .note.package : ALIGN(4) {
+ BYTE(0x04) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Owner including NUL */
+ BYTE(0x64) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Value including NUL */
+ BYTE(0x7e) BYTE(0x1a) BYTE(0xfe) BYTE(0xca) /* Note ID */
+ BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */
+ BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","name":"systemd","version":"248~rc2-1.fc34","osCpe":"cpe:/o:fedoraproject:fedora:33"}\x00' */
+ BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a)
+ BYTE(0x22) BYTE(0x72) BYTE(0x70) BYTE(0x6d)
+ BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6e)
+ BYTE(0x61) BYTE(0x6d) BYTE(0x65) BYTE(0x22)
+ BYTE(0x3a) BYTE(0x22) BYTE(0x73) BYTE(0x79)
+ BYTE(0x73) BYTE(0x74) BYTE(0x65) BYTE(0x6d)
+ BYTE(0x64) BYTE(0x22) BYTE(0x2c) BYTE(0x22)
+ BYTE(0x76) BYTE(0x65) BYTE(0x72) BYTE(0x73)
+ BYTE(0x69) BYTE(0x6f) BYTE(0x6e) BYTE(0x22)
+ BYTE(0x3a) BYTE(0x22) BYTE(0x32) BYTE(0x34)
+ BYTE(0x38) BYTE(0x7e) BYTE(0x72) BYTE(0x63)
+ BYTE(0x32) BYTE(0x2d) BYTE(0x31) BYTE(0x2e)
+ BYTE(0x66) BYTE(0x63) BYTE(0x33) BYTE(0x34)
+ BYTE(0x22) BYTE(0x2c) BYTE(0x22) BYTE(0x6f)
+ BYTE(0x73) BYTE(0x43) BYTE(0x70) BYTE(0x65)
+ BYTE(0x22) BYTE(0x3a) BYTE(0x22) BYTE(0x63)
+ BYTE(0x70) BYTE(0x65) BYTE(0x3a) BYTE(0x2f)
+ BYTE(0x6f) BYTE(0x3a) BYTE(0x66) BYTE(0x65)
+ BYTE(0x64) BYTE(0x6f) BYTE(0x72) BYTE(0x61)
+ BYTE(0x70) BYTE(0x72) BYTE(0x6f) BYTE(0x6a)
+ BYTE(0x65) BYTE(0x63) BYTE(0x74) BYTE(0x3a)
+ BYTE(0x66) BYTE(0x65) BYTE(0x64) BYTE(0x6f)
+ BYTE(0x72) BYTE(0x61) BYTE(0x3a) BYTE(0x33)
+ BYTE(0x33) BYTE(0x22) BYTE(0x7d) BYTE(0x00)
+ }
+}
+INSERT AFTER .note.gnu.build-id;
+```
--- /dev/null
+---
+title: Native Journal Protocol
+category: Interfaces
+layout: default
+---
+
+# Native Journal Protocol
+
+`systemd-journald.service` accepts log data via various protocols:
+
+* Classic RFC3164 BSD syslog via the `/dev/log` socket
+* STDOUT/STDERR of programs via `StandardOutput=journal` + `StandardError=journal` in service files (both of which are default settings)
+* Kernel log messages via the `/dev/kmsg` device node
+* Audit records via the kernel's audit subsystem
+* Structured log messages via `journald`'s native protocol
+
+The latter is what this document is about: if you are developing a program and
+want to pass structured log data to `journald`, it's the Journal's native
+protocol what you want to use. The systemd project provides the
+[`sd_journal_print(3)`](https://www.freedesktop.org/software/systemd/man/sd_journal_print.html)
+API that implements the client side of this protocol. This document explains
+what this interface does behind the scenes, in case you'd like to implement a
+client for it yourself, without linking to `libsystemd` — for example because
+you work in a programming language other than C or otherwise want to avoid the
+dependency.
+
+## Basics
+
+The native protocol of `journald` is spoken on the
+`/run/systemd/journal/socket` `AF_UNIX`/`SOCK_DGRAM` socket on which
+`systemd-journald.service` listens. Each datagram sent to this socket
+encapsulates one journal entry that shall be written. Since datagrams are
+subject to a size limit and we want to allow large journal entries, datagrams
+sent over this socket may come in one of two formats:
+
+* A datagram with the literal journal entry data as payload, without
+ any file descriptors attached.
+
+* A datagram with an empty payload, but with a single
+ [`memfd`](https://man7.org/linux/man-pages/man2/memfd_create.2.html)
+ file descriptor that contains the literal journal entry data.
+
+Other combinations are not permitted, i.e. datagrams with both payload and file
+descriptors, or datagrams with neither, or more than one file descriptor. Such
+datagrams are ignored. The `memfd` file descriptor should be fully sealed. The
+binary format in the datagram payload and in the `memfd` memory is
+identical. Typically a client would attempt to first send the data as datagram
+payload, but if this fails with an `EMSGSIZE` error it would immediately retry
+via the `memfd` logic.
+
+A client probably should bump up the `SO_SNDBUF` socket option of its `AF_UNIX`
+socket towards `journald` in order to delay blocking I/O as much as possible.
+
+## Data Format
+
+Each datagram should consist of a number of environment-like key/value
+assignments. Unlike environment variable assignments the value may contain NUL
+bytes however, as well as any other binary data. Keys may not include the `=`
+or newline characters (or any other control characters or non-ASCII characters)
+and may not be empty.
+
+Serialization into the datagram payload or `memfd` is straight-forward: each
+key/value pair is serialized via one of two methods:
+
+* The first method inserts a `=` character between key and value, and suffixes
+the result with `\n` (i.e. the newline character, ASCII code 10). Example: a
+key `FOO` with a value `BAR` is serialized `F`, `O`, `O`, `=`, `B`, `A`, `R`,
+`\n`.
+
+* The second method should be used if the value of a field contains a `\n`
+byte. In this case, the key name is serialized as is, followed by a `\n`
+character, followed by a (non-aligned) little-endian unsigned 64bit integer
+encoding the size of the value, followed by the literal value data, followed by
+`\n`. Example: a key `FOO` with a value `BAR` may be serialized using this
+second method as: `F`, `O`, `O`, `\n`, `\003`, `\000`, `\000`, `\000`, `\000`,
+`\000`, `\000`, `\000`, `B`, `A`, `R`, `\n`.
+
+If the value of a key/value pair contains a newline character (`\n`), it *must*
+be serialized using the second method. If it does not, either method is
+permitted. However, it is generally recommended to use the first method if
+possible for all key/value pairs where applicable since the generated datagrams
+are easily recognized and understood by the human eye this way, without any
+manual binary decoding — which improves the debugging experience a lot, in
+particular with tools such as `strace` that can show datagram content as text
+dump. After all, log messages are highly relevant for debugging programs, hence
+optimizing log traffic for readability without special tools is generally
+desirable.
+
+Note that keys that begin with `_` have special semantics in `journald`: they
+are *trusted* and implicitly appended by `journald` on the receiving
+side. Clients should not send them — if they do anyway, they will be ignored.
+
+The most important key/value pair to send is `MESSAGE=`, as that contains the
+actual log message text. Other relevant keys a client should send in most cases
+are `PRIORITY=`, `CODE_FILE=`, `CODE_LINE=`, `CODE_FUNC=`, `ERRNO=`. It's
+recommended to generate these fields implicitly on the client side. For further
+information see the [relevant documentation of these
+fields](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html).
+
+The order in which the fields are serialized within one datagram is undefined
+and may be freely chosen by the client. The server side might or might not
+retain or reorder it when writing it to the Journal.
+
+Some programs might generate multi-line log messages (e.g. a stack unwinder
+generating log output about a stack trace, with one line for each stack
+frame). It's highly recommended to send these as a single datagram, using a
+single `MESSAGE=` field with embedded newline characters between the lines (the
+second serialization method described above must hence be used for this
+field). If possible do not split up individual events into multiple Journal
+events that might then be processed and written into the Journal as separate
+entries. The Journal toolchain is capable of handling multi-line log entries
+just fine, and it's generally preferred to have a single set of metadata fields
+associated with each multi-line message.
+
+Note that the same keys may be used multiple times within the same datagram,
+with different values. The Journal supports this and will write such entries to
+disk without complaining. This is useful for associating a single log entry
+with multiple suitable objects of the same type at once. This should only be
+used for specific Journal fields however, where this is expected. Do not use
+this for Journal fields where this is not expected and where code reasonably
+assumes per-event uniqueness of the keys. In most cases code that consumes and
+displays log entries is likely to ignore such non-unique fields or only
+consider the first of the specified values. Specifically, if a Journal entry
+contains multiple `MESSAGE=` fields, likely only the first one is
+displayed. Note that a well-written logging client library thus will not use a
+plain dictionary for accepting structured log metadata, but rather a data
+structure that allows non-unique keys, for example an array, or a dictionary
+that optionally maps to a set of values instead of a single value.
+
+## Example Datagram
+
+Here's an encoded message, with various common fields, all encoded according to
+the first serialization method, with the exception of one, where the value
+contains a newline character, and thus the second method is needed to be used.
+
+```
+PRIORITY=3\n
+SYSLOG_FACILITY=3\n
+CODE_FILE=src/foobar.c\n
+CODE_LINE=77\n
+BINARY_BLOB\n
+\004\000\000\000\000\000\000\000xx\nx\n
+CODE_FUNC=some_func\n
+SYSLOG_IDENTIFIER=footool\n
+MESSAGE=Something happened.\n
+```
+
+(Lines are broken here after each `\n` to make things more readable. C-style
+backslash escaping is used.)
+
+## Automatic Protocol Upgrading
+
+It might be wise to automatically upgrade to logging via the Journal's native
+protocol in clients that previously used the BSD syslog protocol. Behaviour in
+this case should be pretty obvious: try connecting a socket to
+`/run/systemd/journal/socket` first (on success use the native Journal
+protocol), and if that fails fall back to `/dev/log` (and use the BSD syslog
+protocol).
+
+Programs normally logging to STDERR might also choose to upgrade to native
+Journal logging in case they are invoked via systemd's service logic, where
+STDOUT and STDERR are going to the Journal anyway. By preferring the native
+protocol over STDERR-based logging, structured metadata can be passed along,
+including priority information and more — which is not available on STDERR
+based logging. If a program wants to detect automatically whether its STDERR is
+connected to the Journal's stream transport, look for the `$JOURNAL_STREAM`
+environment variable. The systemd service logic sets this variable to a
+colon-separated pair of device and inode number (formatted in decimal ASCII) of
+the STDERR file descriptor. If the `.st_dev` and `.st_ino` fields of the
+`struct stat` data returned by `fstat(STDERR_FILENO, …)` match these values a
+program can be sure its STDERR is connected to the Journal, and may then opt to
+upgrade to the native Journal protocol via an `AF_UNIX` socket of its own, and
+cease to use STDERR.
+
+Why bother with this environment variable check? A service program invoked by
+systemd might employ shell-style I/O redirection on invoked subprograms, and
+those should likely not upgrade to the native Journal protocol, but instead
+continue to use the redirected file descriptors passed to them. Thus, by
+comparing the device and inode number of the actual STDERR file descriptor with
+the one the service manager passed, one can make sure that no I/O redirection
+took place for the current program.
+
+## Alternative Implementations
+
+If you are looking for alternative implementations of this protocol (besides
+systemd's own in `sd_journal_print()`), consider
+[GLib's](https://gitlab.gnome.org/GNOME/glib/-/blob/master/glib/gmessages.c) or
+[`dbus-broker`'s](https://github.com/bus1/dbus-broker/blob/main/src/util/log.c).
+
+And that's already all there is to it.
KEYBOARD_KEY_73=slash # Slash key
KEYBOARD_KEY_f8=wlan # Wireless HW switch button
+# HP ProBook 455 G5
+evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*ProBook*455*G5*:*
+ KEYBOARD_KEY_85=unknown # lid close; also reported via special evdev
+ KEYBOARD_KEY_f8=wlan # Wireless HW switch button
+
+# HP mt44 Mobile Thin Client
+evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHP*mt44*Mobile*Thin*Client*:*
+ KEYBOARD_KEY_64=calendar # Calendar icon (Fn + F12)
+ KEYBOARD_KEY_6d=displaytoggle # Display icon
+ KEYBOARD_KEY_66=connect # Pickup phone button → connect → XF86Go
+ KEYBOARD_KEY_65=cancel # Hangup phone button → cancel → Cancel
+ KEYBOARD_KEY_81=f20 # Fn+F8; Microphone mute button, should be micmute
+ KEYBOARD_KEY_85=unknown # lid close; also reported via special evdev
+ KEYBOARD_KEY_f8=wlan # Wireless HW switch button
+
# HP Stream 7
# The ACPI tables contains a gpio-keys entry for a non connected GPIO
# causing spurious events, map this to unknown to disable it
# MSI Modern series
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMicro-StarInternational*:pnModern*:*
+ KEYBOARD_KEY_56=backslash # secondary backslash key
KEYBOARD_KEY_f1=f20 # Fn+F5 micmute
KEYBOARD_KEY_76=f21 # Fn+F4 touchpad, becomes meta+ctrl+toggle
KEYBOARD_KEY_91=prog1 # Fn+F7 Creation Center, sometime F7
KEYBOARD_KEY_f2=prog2 # Fn+F12 screen rotation
+ KEYBOARD_KEY_8d=prog3 # Fn+a
+ KEYBOARD_KEY_8c=prog4 # Fn+z
+ KEYBOARD_KEY_f5=fn_esc # Fn+esc toggle the behaviour of Fn keys
KEYBOARD_KEY_97=unknown # lid close
KEYBOARD_KEY_98=unknown # lid open
- #Fn+PrntScr sends meta+shif+s
+ # Fn+PrntScr sends meta+shift+s
+
###########################################################
# MSI
<title>Exit status</title>
<para>On success, 0 is returned, a non-zero failure code otherwise.</para>
+
+ <para>When a command is invoked with <command>with</command>, the exit status of the child is
+ propagated. Effectively, <command>homectl</command> will exit without error if the command is
+ successfully invoked <emphasis>and</emphasis> finishes successfully.</para>
</refsect1>
<xi:include href="common-variables.xml" />
<listitem><para>Sets the limit for swap usage on the system before <command>systemd-oomd</command>
will take action. If the fraction of swap used on the system is more than what is defined here,
- <command>systemd-oomd</command> will act on eligible descendant control groups, starting from the
- ones with the highest swap usage to the lowest swap usage. Which control groups are monitored and
- what action gets taken depends on what the unit has configured for
+ <command>systemd-oomd</command> will act on eligible descendant control groups with swap usage greater
+ than 5% of total swap, starting from the ones with the highest swap usage. Which
+ control groups are monitored and what action gets taken depends on what the unit has configured for
<varname>ManagedOOMSwap=</varname>. Takes a value specified in percent (when suffixed with "%"),
permille ("‰") or permyriad ("‱"), between 0% and 100%, inclusive. Defaults to 90%.</para></listitem>
</varlistentry>
<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
<varname>DefaultMemoryPressureLimit=</varname> and <varname>ManagedOOMMemoryPressureLimit=</varname>.
- Defaults to 30 seconds when this property is unset or set to 0.</para></listitem>
+ Must be set to 0, or at least 1 second. Defaults to 30 seconds when unset or 0.</para></listitem>
</varlistentry>
</variablelist>
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<!--End of Autogenerated section-->
<refsect2>
readonly u ManagedOOMMemoryPressureLimit = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly s ManagedOOMPreference = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+ readonly a(ss) BPFProgram = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
<!--property ManagedOOMPreference is not documented!-->
+ <!--property BPFProgram is not documented!-->
+
<!--property KillMode is not documented!-->
<!--property KillSignal is not documented!-->
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
<refsect1>
<title>Description</title>
- <para>The <structname>sd_bus_error</structname> structure carries
- information about a D-Bus error condition. The functions described
- below may be used to set and query fields in this structure. The
- <structfield>name</structfield> field contains a short identifier
- of an error. It should follow the rules for error names described
- in the D-Bus specification, subsection <ulink
- url="http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names">Valid
- Names</ulink>. A number of common, standardized error names are
- described in
- <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- but additional domain-specific errors may be defined by
- applications. The <structfield>message</structfield> field usually
- contains a human-readable string describing the details, but might
- be <constant>NULL</constant>. An unset <structname>sd_bus_error</structname> structure
- should have both fields initialized to <constant>NULL</constant>. Set an error
- structure to <constant>SD_BUS_ERROR_NULL</constant> in order to
- reset both fields to <constant>NULL</constant>. When no longer necessary, resources
- held by the <structname>sd_bus_error</structname> structure should
- be destroyed with <function>sd_bus_error_free()</function>.</para>
-
- <para><function>sd_bus_error_set()</function> sets an error
- structure to the specified name and message strings. The strings
- will be copied into internal, newly allocated memory. It is
- essential to free the error structure again when it is not
- required anymore (see above). The function will return an
- <varname>errno</varname>-like negative value (see <citerefentry
+ <para>The <structname>sd_bus_error</structname> structure carries information about a D-Bus error
+ condition, or lack thereof. The functions described below may be used to set and query fields in this
+ structure.
+ <itemizedlist>
+ <listitem><para>The <structfield>name</structfield> field contains a short identifier of an error. It
+ should follow the rules for error names described in the D-Bus specification, subsection <ulink
+ url="http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names">Valid
+ Names</ulink>. A number of common, standardized error names are described in
+ <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>, but
+ additional domain-specific errors may be defined by applications.</para></listitem>
+
+ <listitem><para>The <structfield>message</structfield> field usually contains a human-readable string
+ describing the details, but might be <constant>NULL</constant>.</para></listitem>
+ </itemizedlist>
+ An unset <structname>sd_bus_error</structname> structure should have both fields initialized to
+ <constant>NULL</constant>, and signifies lack of an error, i.e. success. Assign
+ <constant>SD_BUS_ERROR_NULL</constant> to the structure in order to initialize both fields to
+ <constant>NULL</constant>. When no longer necessary, resources held by the
+ <structname>sd_bus_error</structname> structure should be destroyed with
+ <function>sd_bus_error_free()</function>.</para>
+
+ <para><function>sd_bus_error_set()</function> sets an error structure to the specified name and message
+ strings. The strings will be copied into internal, newly allocated memory. It is essential to free the
+ contents again when they are not required anymore (see above). Do not use this call on error structures
+ that have already been set. If you intend to reuse an error structure, free the old data stored in it
+ with <function>sd_bus_error_free()</function> first.</para>
+
+ <para><function>sd_bus_error_set()</function> will return an <varname>errno</varname>-like value (see
+ <citerefentry
project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>)
- determined from the specified error name. Various well-known
- D-Bus errors are converted to well-known <varname>errno</varname>
- counterparts, and the other ones to <constant>-EIO</constant>. See
- <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- for a list of well-known error names. Additional error mappings
- may be defined with
- <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>. If
- <parameter>e</parameter> is <constant>NULL</constant>, no error structure is initialized,
- but the error is still converted into an
- <varname>errno</varname>-style error. If
- <parameter>name</parameter> is <constant>NULL</constant>, it is
- assumed that no error occurred, and 0 is returned. This means that
- this function may be conveniently used in a
- <function>return</function> statement. If
- <parameter>message</parameter> is <constant>NULL</constant>, no message is set. This
- call can fail if no memory may be allocated for the name and
- message strings, in which case an
- <constant>SD_BUS_ERROR_NO_MEMORY</constant> error might be set
- instead and -ENOMEM be returned. Do not use this call on error
- structures that are already initialized. If you intend to reuse an
- error structure, free the old data stored in it with
- <function>sd_bus_error_free()</function> first.</para>
+ determined from the specified error name <parameter>name</parameter>. If <parameter>name</parameter> is
+ <constant>NULL</constant>, it is assumed that no error occurred, and <constant>0</constant> is returned.
+ If <parameter>name</parameter> is nonnull, a negative value is always returned. If
+ <parameter>e</parameter> is <constant>NULL</constant>, no error structure is initialized, but
+ <parameter>name</parameter> is still converted into an <varname>errno</varname>-style value.</para>
+
+ <para>Various well-known D-Bus errors are converted to well-known <varname>errno</varname> counterparts,
+ and the other ones to <constant>-EIO</constant>. See
+ <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry> for a
+ list of well-known error names. Additional error mappings may be defined with
+ <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+
+ <para><function>sd_bus_error_set()</function> is designed to be conveniently used in a
+ <function>return</function> statement. If <parameter>message</parameter> is <constant>NULL</constant>, no
+ message is set. This call can fail if no memory may be allocated for the name and message strings, in
+ which case an <constant>SD_BUS_ERROR_NO_MEMORY</constant> error will be set instead and
+ <constant>-ENOMEM</constant> returned. </para>
<para><function>sd_bus_error_setf()</function> is similar to
<function>sd_bus_error_set()</function>, but takes a <citerefentry
<structfield>message</structfield> field.</para>
<para><function>sd_bus_error_set_const()</function> is similar to
- <function>sd_bus_error_set()</function>, but the string parameters
- are not copied internally, and must hence remain constant and
- valid for the lifetime of <parameter>e</parameter>. Use this call
- to avoid memory allocations when setting error structures. Since
- this call does not allocate memory, it will not fail with an
- out-of-memory condition as
- <function>sd_bus_error_set()</function> can, as described
- above. Alternatively, the
- <constant>SD_BUS_ERROR_MAKE_CONST()</constant> macro may be used
- to generate a literal, constant bus error structure
- on-the-fly.</para>
-
- <para><function>sd_bus_error_set_errno()</function> will set
- <structfield>name</structfield> from an
- <varname>errno</varname>-like value that is converted to a D-Bus
+ <function>sd_bus_error_set()</function>, but the string parameters are not copied internally, and must
+ hence remain constant and valid for the lifetime of <parameter>e</parameter>. Use this call to avoid
+ memory allocations when setting error structures. Since this call does not allocate memory, it will not
+ fail with an out-of-memory condition as <function>sd_bus_error_set()</function> may, as described
+ above. Alternatively, the <constant>SD_BUS_ERROR_MAKE_CONST()</constant> macro may be used to generate a
+ literal, constant bus error structure on-the-fly.</para>
+
+ <para><function>sd_bus_error_set_errno()</function> will immediately return <constant>0</constant> if the
+ specified error parameter <parameter>error</parameter> is <constant>0</constant>. Otherwise, it will set
+ <structfield>name</structfield> from an <varname>errno</varname>-like value that is converted to a D-Bus
error. <citerefentry
- project='die-net'><refentrytitle>strerror_r</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- will be used to set <structfield>message</structfield>. Well-known
- D-Bus error names will be used for <structfield>name</structfield>
- if applicable, otherwise a name in the
- <literal>System.Error.</literal> namespace will be generated. The
- sign of the specified error number is ignored. The absolute value
- is used implicitly. The call always returns a negative value, for
- convenient usage in <function>return</function> statements. This
- call might fail due to lack of memory, in which case an
- <constant>SD_BUS_ERROR_NO_MEMORY</constant> error is set instead,
- and -ENOMEM is returned.</para>
+ project='die-net'><refentrytitle>strerror_r</refentrytitle><manvolnum>3</manvolnum></citerefentry> will
+ be used to set <structfield>message</structfield>. Well-known D-Bus error names will be used for
+ <structfield>name</structfield> if applicable, otherwise a name in the <literal>System.Error.</literal>
+ namespace will be generated. The sign of the specified error number is ignored and the absolute value is
+ used implicitly. If the specified error <parameter>error</parameter> is non-zero, the call always returns
+ a negative value, for convenient usage in <function>return</function> statements. This call might fail
+ due to lack of memory, in which case an <constant>SD_BUS_ERROR_NO_MEMORY</constant> error is set instead,
+ and <constant>-ENOMEM</constant> is returned.</para>
<para><function>sd_bus_error_set_errnof()</function> is similar to
<function>sd_bus_error_set_errno()</function>, but in addition to
project='man-pages'><refentrytitle>va_arg</refentrytitle><manvolnum>3</manvolnum></citerefentry>
parameter list.</para>
- <para><function>sd_bus_error_get_errno()</function> converts the
- <structfield>name</structfield> field of an error structure to an
- <varname>errno</varname>-like (positive) value using the same
- rules as <function>sd_bus_error_set()</function>. If
- <parameter>e</parameter> is <constant>NULL</constant>, 0 will be
- returned.</para>
-
- <para><function>sd_bus_error_copy()</function> will initialize
- <parameter>dst</parameter> using the values in
- <parameter>e</parameter>. If the strings in
- <parameter>e</parameter> were set using
- <function>sd_bus_error_set_const()</function>, they will be shared.
- Otherwise, they will be copied. Returns a converted
- <varname>errno</varname>-like, negative error code.</para>
-
- <para><function>sd_bus_error_move()</function> is similar to <function>sd_bus_error_copy()</function>, but will
- move any error information from <parameter>e</parameter> into <parameter>dst</parameter>, resetting the
- former. This function cannot fail, as no new memory is allocated. Note that if <parameter>e</parameter> is not set
- (or <constant>NULL</constant>) <parameter>dst</parameter> is initializated to
- <constant>SD_BUS_ERROR_NULL</constant>. Moreover, if <parameter>dst</parameter> is <constant>NULL</constant> no
- operation is executed on it and resources held by <parameter>e</parameter> are freed and reset. Returns a
- converted <varname>errno</varname>-like, negative error code.</para>
+ <para><function>sd_bus_error_get_errno()</function> converts the <structfield>name</structfield> field of
+ an error structure to an <varname>errno</varname>-like (positive) value using the same rules as
+ <function>sd_bus_error_set()</function>. If <parameter>e</parameter> is <constant>NULL</constant>,
+ <constant>0</constant> will be returned.</para>
+
+ <para><function>sd_bus_error_copy()</function> will initialize <parameter>dst</parameter> using the
+ values in <parameter>e</parameter>, if <parameter>e</parameter> has been set with an error value before.
+ Otherwise, it will return immediately. If the strings in <parameter>e</parameter> were set using
+ <function>sd_bus_error_set_const()</function>, they will be shared. Otherwise, they will be
+ copied. Returns a converted <varname>errno</varname>-like, negative error code or <constant>0</constant>.
+ Before this call, <parameter>dst</parameter> must be unset, i.e. either freshly initialized with
+ <constant>NULL</constant> or reset using <function>sd_bus_error_free()</function>.</para>
+
+ <para><function>sd_bus_error_move()</function> is similar to <function>sd_bus_error_copy()</function>,
+ but will move any error information from <parameter>e</parameter> into <parameter>dst</parameter>,
+ resetting the former. This function cannot fail, as no new memory is allocated. Note that if
+ <parameter>e</parameter> is not set, <parameter>dst</parameter> is initializated to
+ <constant>SD_BUS_ERROR_NULL</constant>. Moreover, if <parameter>dst</parameter> is
+ <constant>NULL</constant> no operation is executed on it and resources held by <parameter>e</parameter>
+ are freed and reset. Returns a converted <varname>errno</varname>-like, non-positive error value.</para>
<para><function>sd_bus_error_is_set()</function> will return a
non-zero value if <parameter>e</parameter> is
<refsect1>
<title>Return Value</title>
- <para>The functions <function>sd_bus_error_set()</function>,
- <function>sd_bus_error_setf()</function>, and
- <function>sd_bus_error_set_const()</function>, when successful,
- return the negative errno value corresponding to the
- <parameter>name</parameter> parameter. The functions
- <function>sd_bus_error_set_errno()</function>,
- <function>sd_bus_error_set_errnof()</function> and
- <function>sd_bus_error_set_errnofv()</function>, when successful,
- return the negative value of the <parameter>error</parameter>
- parameter. If an error occurs, one of the negative error values
- listed below will be returned.</para>
+ <para>The functions <function>sd_bus_error_set()</function>, <function>sd_bus_error_setf()</function>,
+ and <function>sd_bus_error_set_const()</function> always return <constant>0</constant> when the specified
+ error value is <constant>NULL</constant>, and a negative errno-like value corresponding to the
+ <parameter>name</parameter> parameter otherwise. The functions
+ <function>sd_bus_error_set_errno()</function>, <function>sd_bus_error_set_errnof()</function> and
+ <function>sd_bus_error_set_errnofv()</function>, return <constant>0</constant> when the specified error
+ value is <constant>0</constant>, and a a negative errno-like value corresponding to the
+ <parameter>error</parameter> parameter otherwise. If an error occurs internally, one of the negative
+ error values listed below will be returned.</para>
<para><function>sd_bus_error_get_errno()</function> returns
<constant>false</constant> when <parameter>e</parameter> is
<constant>NULL</constant>, and a positive errno value mapped from
<parameter>e->name</parameter> otherwise.</para>
- <para><function>sd_bus_error_copy()</function> and <function>sd_bus_error_move()</function> return 0 or a positive
- integer on success, and a negative error value converted from the error name otherwise.</para>
+ <para><function>sd_bus_error_copy()</function> and <function>sd_bus_error_move()</function> return a
+ negative error value converted from the source error, and zero if the error has not been set.</para>
<para><function>sd_bus_error_is_set()</function> returns a
non-zero value when <parameter>e</parameter> and the
zero outside of this state, and positive otherwise. Effectively, this function returns positive while regular
messages can be sent or received on the connection.</para>
+ <para>The <parameter>bus</parameter> argument may be <constant>NULL</constant>, zero is also returned in
+ that case.</para>
+
<para>To be notified when the connection is fully established, use
<citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
install a match for the <function>Connected()</function> signal on the
<refsect1>
<title>Return Value</title>
- <para>On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style
- error code.</para>
+ <para>Those functions return 0 if the bus is <emphasis>not</emphasis> in the given state, and a positive
+ integer when it is. On failure, a negative errno-style error code is returned.</para>
<refsect2>
<title>Errors</title>
<function>sd_journal_send()</function>. Using
<function>syslog()</function> has the benefit of being
more portable.</para>
+
+ <para>These functions implement a client to the <ulink
+ url="https://systemd.io/JOURNAL_NATIVE_PROTOCOL">Native Journal Protocol</ulink>.</para>
</refsect1>
<refsect1>
<listitem><para>The Microsoft Windows EFI boot manager, if installed</para></listitem>
- <listitem><para>The Apple MacOS X boot manager, if installed</para></listitem>
+ <listitem><para>The Apple macOS boot manager, if installed</para></listitem>
<listitem><para>The EFI Shell binary, if installed</para></listitem>
<varlistentry>
<term><keycap>a</keycap></term>
- <listitem><para>OS X</para></listitem>
+ <listitem><para>macOS</para></listitem>
</varlistentry>
<varlistentry>
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>COREDUMP_PACKAGE_NAME=</varname></term>
+ <term><varname>COREDUMP_PACKAGE_VERSION=</varname></term>
+ <term><varname>COREDUMP_PACKAGE_JSON=</varname></term>
+
+ <listitem><para>If the executable contained .package metadata ELF notes, they will be
+ parsed and attached. The <varname>package</varname> and <varname>packageVersion</varname>
+ of the 'main' ELF module (ie: the excutable) will be appended individually. The
+ JSON-formatted content of all modules will be appended as a single JSON object, each with
+ the module name as the key. For more information about this metadata format and content, see
+ <ulink url="https://systemd.io/COREDUMP_PACKAGE_METADATA/">the coredump metadata spec</ulink>.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>MESSAGE=</varname></term>
<row>
<entry>8</entry>
- <entry><citerefentry><refentrytitle>sd-boot</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures the kernel command line in this PCR.</entry>
+ <entry><citerefentry><refentrytitle>sd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the kernel command line in this PCR.</entry>
</row>
</tbody>
</tgroup>
<refnamediv>
<refname>systemd-cryptsetup@.service</refname>
+ <!-- <refname>system-systemd\x2dcryptsetup.slice</refname> — this causes meson to go haywire because it
+ thinks this is a (windows) path. Let's just not create the alias for this name, and only include it
+ in the synopsis. -->
<refname>systemd-cryptsetup</refname>
<refpurpose>Full disk decryption logic</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-cryptsetup@.service</filename></para>
+ <para><filename>system-systemd\x2dcryptsetup.slice</filename></para>
<para><filename>/usr/lib/systemd/systemd-cryptsetup</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
- <para><filename>systemd-cryptsetup@.service</filename> is a
- service responsible for setting up encrypted block devices. It is
- instantiated for each device that requires decryption for
- access.</para>
+ <para><filename>systemd-cryptsetup@.service</filename> is a service responsible for setting up encrypted
+ block devices. It is instantiated for each device that requires decryption for access.</para>
+
+ <para><filename>systemd-cryptsetup@.service</filename> instances are part of the
+ <filename>system-systemd\x2dcryptsetup.slice</filename> slice, which is destroyed only very late in the
+ shutdown procedure. This allows the encrypted devices to remain up until filesystems have been unmounted.
+ </para>
<para><filename>systemd-cryptsetup@.service</filename> will ask
for hard disk passwords via the <ulink
project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call</para></listitem>
- <listitem><para>Structured system log messages via the native
- Journal API, see
- <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry></para></listitem>
+ <listitem><para>Structured system log messages via the native Journal API, see
+ <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ and <ulink url="https://systemd.io/JOURNAL_NATIVE_PROTOCOL">Native Journal
+ Protocol</ulink></para></listitem>
<listitem><para>Standard output and standard error of service units. For further details see
below.</para></listitem>
<refsect1>
<title>Exit status</title>
- <para>On success, 0 is returned. If the configuration was syntactically invalid (syntax errors,
- missing arguments, …), so some lines had to be ignored, but no other errors occurred,
- <constant>65</constant> is returned (<constant>EX_DATAERR</constant> from
- <filename>/usr/include/sysexits.h</filename>). If the configuration was syntactically valid, but
- could not be executed (lack of permissions, creation of files in missing directories, invalid
- contents when writing to <filename>/sys/</filename> values, …), <constant>73</constant> is
- returned (<constant>EX_CANTCREAT</constant> from <filename>/usr/include/sysexits.h</filename>).
- Otherwise, <constant>1</constant> is returned (<constant>EXIT_FAILURE</constant> from
- <filename>/usr/include/stdlib.h</filename>).
- </para>
+ <para>On success, 0 is returned. If the configuration was syntactically invalid (syntax errors, missing
+ arguments, …), so some lines had to be ignored, but no other errors occurred, <constant>65</constant> is
+ returned (<constant>EX_DATAERR</constant> from <filename>/usr/include/sysexits.h</filename>). If the
+ configuration was syntactically valid, but could not be executed (lack of permissions, creation of files
+ in missing directories, invalid contents when writing to <filename>/sys/</filename> values, …),
+ <constant>73</constant> is returned (<constant>EX_CANTCREAT</constant> from
+ <filename>/usr/include/sysexits.h</filename>). Otherwise, <constant>1</constant> is returned
+ (<constant>EXIT_FAILURE</constant> from <filename>/usr/include/stdlib.h</filename>).</para>
+
+ <para>Note: when creating items, if the target already exists, but is of the wrong type or otherwise does
+ not match the requested state, and forced operation has not been requested with <literal>+</literal>,
+ a message is emitted, but the failure is otherwise ignored.</para>
</refsect1>
<refsect1>
<term><option>-e</option></term>
<term><option>--exec-delay=</option></term>
<listitem>
- <para>Delay the execution of <varname>RUN</varname>
- instructions by the given number of seconds. This option
+ <para>Delay the execution of each <varname>RUN{<replaceable>program</replaceable>}</varname>
+ parameter by the given number of seconds. This option
might be useful when debugging system crashes during
coldplug caused by loading non-working kernel
modules.</para>
<term><varname>udev.exec_delay=</varname></term>
<term><varname>rd.udev.exec_delay=</varname></term>
<listitem>
- <para>Delay the execution of <varname>RUN</varname> instructions by the given
+ <para>Delay the execution of each <varname>RUN{<replaceable>program</replaceable>}</varname> parameter by the given
number of seconds. This option might be useful when
debugging system crashes during coldplug caused by loading
non-working kernel modules.</para>
property.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><constant>v249</constant></term>
+
+ <listitem><para>PCI hotplug slot names for the s390 PCI driver are a hexadecimal representation
+ of the <filename>function_id</filename> device attribute. This attribute is now used to build the
+ <varname>ID_NET_NAME_SLOT</varname>. Before that, all slot names were parsed as decimal
+ numbers, which could either result in an incorrect value of the <varname>ID_NET_NAME_SLOT</varname>
+ property or none at all.</para></listitem>
+ </varlistentry>
+
</variablelist>
<para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
<para>If these settings are used multiple times in the same unit all the specified programs are attached. If an
empty string is assigned to these settings the program list is reset and all previous specified programs ignored.</para>
+ <para>If the path <replaceable>BPF_FS_PROGRAM_PATH</replaceable> in <varname>IPIngressFilterPath=</varname> assignment
+ is already being handled by <varname>BPFProgram=</varname> ingress hook, e.g.
+ <varname>BPFProgram=</varname><constant>ingress</constant>:<replaceable>BPF_FS_PROGRAM_PATH</replaceable>,
+ the assignment will be still considered valid and the program will be attached to a cgroup. Same for
+ <varname>IPEgressFilterPath=</varname> path and <constant>egress</constant> hook.</para>
+
<para>Note that for socket-activated services, the IP filter programs configured on the socket unit apply to
all sockets associated with it directly, but not to any sockets created by the ultimately activated services
for it. Conversely, the IP filter programs configured for the service are not applied to any sockets passed into
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>BPFProgram=<replaceable>type</replaceable><constant>:</constant><replaceable>program-path</replaceable></varname></term>
+ <listitem>
+ <para>Add a custom cgroup BPF program.</para>
+
+ <para><varname>BPFProgram=</varname> allows attaching BPF hooks to the cgroup of a systemd unit.
+ (This generalizes the functionality exposed via <varname>IPEgressFilterPath=</varname> for egress and
+ <varname>IPIngressFilterPath=</varname> for ingress.)
+ Cgroup-bpf hooks in the form of BPF programs loaded to the BPF filesystem are attached with cgroup-bpf attach
+ flags determined by the unit. For details about attachment types and flags see <ulink
+ url="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/bpf.h"/>.
+ For general BPF documentation please refer to <ulink url="https://www.kernel.org/doc/html/latest/bpf/index.html"/>.</para>
+
+ <para>The specification of BPF program consists of a <replaceable>type</replaceable> followed by a
+ <replaceable>program-path</replaceable> with <literal>:</literal> as the separator:
+ <replaceable>type</replaceable><constant>:</constant><replaceable>program-path</replaceable>.</para>
+
+ <para><replaceable>type</replaceable> is the string name of BPF attach type also used in
+ <command>bpftool</command>. <replaceable>type</replaceable> can be one of <constant>egress</constant>,
+ <constant>ingress</constant>, <constant>sock_create</constant>, <constant>sock_ops</constant>,
+ <constant>device</constant>, <constant>bind4</constant>, <constant>bind6</constant>,
+ <constant>connect4</constant>, <constant>connect6</constant>, <constant>post_bind4</constant>,
+ <constant>post_bind6</constant>, <constant>sendmsg4</constant>, <constant>sendmsg6</constant>,
+ <constant>sysctl</constant>, <constant>recvmsg4</constant>, <constant>recvmsg6</constant>,
+ <constant>getsockopt</constant>, <constant>setsockopt</constant>.</para>
+
+ <para>Setting <varname>BPFProgram=</varname> to an empty value makes previous assignments ineffective.</para>
+ <para>Multiple assignments of the same <replaceable>type</replaceable>:<replaceable>program-path</replaceable>
+ value have the same effect as a single assignment: the program with the path <replaceable>program-path</replaceable>
+ will be attached to cgroup hook <replaceable>type</replaceable> just once.</para>
+ <para>If BPF <constant>egress</constant> pinned to <replaceable>program-path</replaceable> path is already being
+ handled by <varname>IPEgressFilterPath=</varname>, <varname>BPFProgram=</varname>
+ assignment will be considered valid and <varname>BPFProgram=</varname> will be attached to a cgroup.
+ Similarly for <constant>ingress</constant> hook and <varname>IPIngressFilterPath=</varname> assignment.</para>
+
+ <para>BPF programs passed with <varname>BPFProgram=</varname> are attached to the cgroup of a unit with BPF
+ attach flag <constant>multi</constant>, that allows further attachments of the same
+ <replaceable>type</replaceable> within cgroup hierarchy topped by the unit cgroup.</para>
+
+ <para>Examples:<programlisting>
+BPFProgram=egress:/sys/fs/bpf/egress-hook
+BPFProgram=bind6:/sys/fs/bpf/sock-addr-hook
+</programlisting></para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>DeviceAllow=</varname></term>
b /dev/block-device-to-create mode user group - major:minor
b+ /dev/block-device-to-[re]create mode user group - major:minor
C /target/to/create - - - - /source/to/copy
-x /path-or-glob/to/ignore - - - - -
-X /path-or-glob/to/ignore/recursively - - - - -
+x /path-or-glob/to/ignore/recursively - - - - -
+X /path-or-glob/to/ignore - - - - -
r /empty/dir/to/remove - - - - -
R /dir/to/remove/recursively - - - - -
z /path-or-glob/to/adjust/mode mode user group - -
<term><varname>exec_delay=</varname></term>
<listitem>
- <para>An integer. Delay the execution of <varname>RUN</varname>
- instructions by the given number of seconds. This option
+ <para>An integer. Delay the execution of each <varname>RUN{<replaceable>program</replaceable>}</varname>
+ parameter by the given number of seconds. This option
might be useful when debugging system crashes during
coldplug caused by loading non-working kernel
modules.</para>
'-Wno-error=#warnings', # clang
'-Wno-string-plus-int', # clang
- # Disable -Wmaybe-uninitialized, since it's noisy on gcc 8 with
- # optimizations enabled, producing essentially false positives.
- '-Wno-maybe-uninitialized',
-
'-ffast-math',
'-fno-common',
'-fdiagnostics-show-option',
'--param=ssp-buffer-size=4',
]
+# Disable -Wmaybe-unitialized when compiling with -Os/-O1/-O3/etc. There are
+# too many false positives with gcc >= 8. Effectively, we only test with -O0
+# and -O2; this should be enough to catch most important cases without too much
+# busywork. See https://github.com/systemd/systemd/pull/19226.
+if cc.get_id() == 'gcc' and (not '02'.contains(get_option('optimization')) or
+ cc.version().version_compare('<10'))
+ possible_cc_flags += '-Wno-maybe-uninitialized'
+endif
+
# --as-needed and --no-undefined are provided by meson by default,
# run mesonconf to see what is enabled
possible_link_flags = [
if want_tests != 'false'
test('check-directives',
check_directives_sh,
- args : project_source_root)
+ args : [project_source_root, project_build_root])
endif
############################################################
umask 022
fi
+# On Fedora "ld" is (unfortunately — if you ask me) managed via
+# "alternatives". Since we'd like to support building images in environments
+# with only /usr/ around (e.g. mkosi's UsrOnly=1 option), we have the problem
+# that /usr/bin/ld is a symlink that points to a non-existing file in
+# /etc/alternative/ in this mode. Let's work around this for now by manually
+# redirect "ld" to "ld.bfd", i.e. circumventing the /usr/bin/ld symlink.
+if [ ! -x /usr/bin/ld -a -x /usr/bin/ld.bfd ] ; then
+ mkdir -p "$HOME"/bin
+ ln -s /usr/bin/ld.bfd "$HOME"/bin/ld
+ PATH="$HOME/bin:$PATH"
+fi
+
# If mkosi.builddir/ exists mkosi will set $BUILDDIR to it, let's then use it
# as out-of-tree build dir. Otherwise, let's make up our own builddir.
[ -z "$BUILDDIR" ] && BUILDDIR=build
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
-"PO-Revision-Date: 2021-03-25 03:01+0000\n"
+"PO-Revision-Date: 2021-04-09 07:01+0000\n"
"Last-Translator: simmon <simmon@nplob.com>\n"
"Language-Team: Korean <https://translate.fedoraproject.org/projects/systemd/"
"master/ko/>\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.5.1\n"
+"X-Generator: Weblate 4.5.3\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid ""
"Authentication is required to set the statically configured local hostname, "
"as well as the pretty hostname."
-msgstr "로컬호스트 이름을 모양새를 갖춘 호스트이름 처럼 정적으로 설정하려면 인증이 필요합니다."
+msgstr "로컬호스트 이름을 지정 호스트이름 처럼 정적으로 설정하려면 인증이 필요합니다."
#: src/hostname/org.freedesktop.hostname1.policy:41
msgid "Set machine information"
}
_machinectl() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
_networkctl() {
local i verb comps
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-a --all -h --help --version --no-pager --no-legend -s --stats -l --full'
[ARG]='-n --lines'
_systemd_analyze() {
local i verb comps mode
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version --system --user --global --order --require --no-pager
}
_systemd_cat() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
}
_systemd_cgls() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
}
_systemd_cgtop() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local comps
local -A OPTS=(
}
_systemd-delta() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local comps
local -A OPTS=(
}
_systemd_detect_virt() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
_systemd_id128() {
local i verb comps
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version -p --pretty'
[ARG]='-a --app-specific'
}
_systemd_nspawn() {
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local i verb comps
local -A OPTS=(
_systemd_path() {
local comps
- local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
+ local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version'
[ARG]='--suffix'
}
static int get_max_brightness(sd_device *device, unsigned *ret) {
- const char *max_brightness_str;
- unsigned max_brightness;
+ const char *s;
int r;
assert(device);
assert(ret);
- r = sd_device_get_sysattr_value(device, "max_brightness", &max_brightness_str);
+ r = sd_device_get_sysattr_value(device, "max_brightness", &s);
if (r < 0)
return log_device_warning_errno(device, r, "Failed to read 'max_brightness' attribute: %m");
- r = safe_atou(max_brightness_str, &max_brightness);
+ r = safe_atou(s, ret);
if (r < 0)
- return log_device_warning_errno(device, r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str);
-
- if (max_brightness <= 0)
- return log_device_warning_errno(device, SYNTHETIC_ERRNO(EINVAL), "Maximum brightness is 0, ignoring device.");
+ return log_device_warning_errno(device, r, "Failed to parse 'max_brightness' \"%s\": %m", s);
- log_device_debug(device, "Maximum brightness is %u", max_brightness);
- *ret = max_brightness;
return 0;
}
if (get_max_brightness(device, &max_brightness) < 0)
return 0;
+ if (max_brightness == 0) {
+ log_device_warning(device, "Maximum brightness is 0, ignoring device.");
+ return 0;
+ }
+
+ log_device_debug(device, "Maximum brightness is %u", max_brightness);
+
escaped_ss = cescape(ss);
if (!escaped_ss)
return log_oom();
int cg_pid_get_path(const char *controller, pid_t pid, char **ret_path) {
_cleanup_fclose_ FILE *f = NULL;
- const char *fs, *controller_str;
+ const char *fs, *controller_str = NULL; /* avoid false maybe-uninitialized warning */
int unified, r;
assert(pid >= 0);
continue;
*e = 0;
+ assert(controller_str);
r = string_contains_word(l, ",", controller_str);
if (r < 0)
return r;
[CGROUP_CONTROLLER_PIDS] = "pids",
[CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall",
[CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
+ [CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
/* BPF-based pseudo-controllers, v2 only */
CGROUP_CONTROLLER_BPF_FIREWALL,
CGROUP_CONTROLLER_BPF_DEVICES,
+ CGROUP_CONTROLLER_BPF_FOREIGN,
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -EINVAL,
CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS),
CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL),
CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
+ CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
/* All real cgroup v1 controllers */
CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
/* All cgroup v2 BPF pseudo-controllers */
- CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES,
+ CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN,
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
} CGroupMask;
_cleanup_close_ int fdf = -1;
struct stat st;
- int fdt = -1, r;
+ int r, fdt = -1; /* avoid false maybe-uninitialized warning */
assert(from);
assert(to);
_cleanup_free_ char *p = NULL;
_cleanup_free_ void *buf = NULL;
struct stat st;
- usec_t begin;
+ usec_t begin = 0; /* Unnecessary initialization to appease gcc */
uint32_t a;
ssize_t n;
ALTERNATE_VALUE,
} state = WORD;
- const char *e, *word = format, *test_value;
+ const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
char *k;
_cleanup_free_ char *r = NULL;
- size_t i, len;
+ size_t i, len = 0; /* len is initialized to appease gcc */
int nest = 0;
assert(format);
word = e+1;
state = WORD;
} else if (*e == ':') {
- if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
+ if (flags & REPLACE_ENV_ALLOW_EXTENDED) {
+ len = e - word - 2;
+ state = TEST;
+ } else
/* Treat this as unsupported syntax, i.e. do no replacement */
state = WORD;
- else {
- len = e-word-2;
- state = TEST;
- }
}
break;
static inline const char *strerror_safe(int error) {
/* 'safe' here does NOT mean thread safety. */
- return strerror(abs(error));
+ return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */
}
static inline int errno_or_else(int fallback) {
const struct timespec *ts) {
bool needs_nl;
- int r, fd;
+ int r, fd = -1;
assert(f);
assert(line);
needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
- /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so
- * that the write goes out in one go, instead of two */
+ /* If STDIO buffering was disabled, then let's append the newline character to the string
+ * itself, so that the write goes out in one go, instead of two */
line = strjoina(line, "\n");
needs_nl = false;
if (ts) {
const struct timespec twice[2] = {*ts, *ts};
+ assert(fd >= 0);
if (futimens(fd, twice) < 0)
return -errno;
}
bool in6_addr_is_link_local(const struct in6_addr *a) {
assert(a);
- return IN6_IS_ADDR_LINKLOCAL(a);
+ return IN6_IS_ADDR_LINKLOCAL(a); /* lgtm [cpp/potentially-dangerous-function] */
}
int in_addr_is_link_local(int family, const union in_addr_union *u) {
return in4_addr_is_localhost(&u->in);
if (family == AF_INET6)
- return IN6_IS_ADDR_LOOPBACK(&u->in6);
+ return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */
return -EAFNOSUPPORT;
}
},
};
- assert(code < _SPECIAL_GLYPH_MAX);
+ if (code < 0)
+ return NULL;
+ assert(code < _SPECIAL_GLYPH_MAX);
return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code];
}
bool is_locale_utf8(void);
-typedef enum {
+typedef enum SpecialGlyph {
SPECIAL_GLYPH_TREE_VERTICAL,
SPECIAL_GLYPH_TREE_BRANCH,
SPECIAL_GLYPH_TREE_RIGHT,
SPECIAL_GLYPH_LOCK_AND_KEY,
SPECIAL_GLYPH_TOUCH,
_SPECIAL_GLYPH_MAX,
+ _SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
const char *special_glyph(SpecialGlyph code) _const_;
#include <unistd.h>
#define SD_LOGIND_ROOT_CHECK_INHIBITORS (UINT64_C(1) << 0)
-#define SD_LOGIND_KEXEC_REBOOT (UINT64_C(1) << 1)
+#define SD_LOGIND_REBOOT_VIA_KEXEC (UINT64_C(1) << 1)
/* For internal use only */
#define SD_LOGIND_INTERACTIVE (UINT64_C(1) << 63)
-#define SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC (SD_LOGIND_ROOT_CHECK_INHIBITORS|SD_LOGIND_KEXEC_REBOOT)
+#define SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC (SD_LOGIND_ROOT_CHECK_INHIBITORS|SD_LOGIND_REBOOT_VIA_KEXEC)
#define SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_ALL (SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC|SD_LOGIND_INTERACTIVE)
bool session_id_valid(const char *id);
return path_equal(a, b) || files_same(a, b, flags) > 0;
}
+bool path_equal_filename(const char *a, const char *b) {
+ _cleanup_free_ char *a_basename = NULL, *b_basename = NULL;
+ int r;
+
+ assert(a);
+ assert(b);
+
+ r = path_extract_filename(a, &a_basename);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse basename of %s: %m", a);
+ return false;
+ }
+ r = path_extract_filename(b, &b_basename);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse basename of %s: %m", b);
+ return false;
+ }
+
+ return path_equal(a_basename, b_basename);
+}
+
char* path_join_internal(const char *first, ...) {
char *joined, *q;
const char *p;
int path_compare(const char *a, const char *b) _pure_;
bool path_equal(const char *a, const char *b) _pure_;
bool path_equal_or_files_same(const char *a, const char *b, int flags);
+/* Compares only the last portion of the input paths, ie: the filenames */
+bool path_equal_filename(const char *a, const char *b);
char* path_join_internal(const char *first, ...);
#define path_join(x, ...) path_join_internal(x, __VA_ARGS__, POINTER_MAX)
_cleanup_free_ char *word = NULL;
const char *c;
- r = extract_first_word(&q, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
+ r = extract_first_word(&q, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
if (r == 0)
int make_recovery_key(char **ret) {
_cleanup_(erase_and_freep) char *formatted = NULL;
_cleanup_(erase_and_freep) uint8_t *key = NULL;
+ size_t j = 0;
int r;
assert(ret);
if (!formatted)
return -ENOMEM;
- for (size_t i = 0, j = 0; i < RECOVERY_KEY_MODHEX_RAW_LENGTH; i++) {
+ for (size_t i = 0; i < RECOVERY_KEY_MODHEX_RAW_LENGTH; i++) {
formatted[j++] = modhex_alphabet[key[i] >> 4];
formatted[j++] = modhex_alphabet[key[i] & 0xF];
formatted[j++] = '-';
}
- formatted[RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1] = 0;
+ assert(j == RECOVERY_KEY_MODHEX_FORMATTED_LENGTH);
+ assert(formatted[RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1] == '-');
+ formatted[RECOVERY_KEY_MODHEX_FORMATTED_LENGTH-1] = 0; /* replace final dash with a NUL */
*ret = TAKE_PTR(formatted);
return 0;
#include <sys/types.h>
#include <sys/un.h>
+#include "errno-util.h"
#include "macro.h"
#include "missing_network.h"
#include "missing_socket.h"
socklen_t sl = sizeof(v);
if (getsockopt(fd, level, optname, &v, &sl) < 0)
- return -errno;
+ return negative_errno();
if (sl != sizeof(v))
return -EIO;
/* add string, return the index/offset into the buffer */
ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) {
uint8_t c;
- struct strbuf_node *node;
- size_t depth;
char *buf_new;
struct strbuf_child_entry *child;
- struct strbuf_node *node_child;
+ struct strbuf_node *node;
ssize_t off;
if (!str->root)
str->in_len += len;
node = str->root;
- for (depth = 0; depth <= len; depth++) {
+ for (size_t depth = 0; depth <= len; depth++) {
struct strbuf_child_entry search;
/* match against current node */
str->buf[str->len++] = '\0';
/* new node */
+ _cleanup_free_ struct strbuf_node *node_child = NULL;
+
node_child = new(struct strbuf_node, 1);
if (!node_child)
return -ENOMEM;
/* extend array, add new entry, sort for bisection */
child = reallocarray(node->children, node->children_count + 1, sizeof(struct strbuf_child_entry));
- if (!child) {
- free(node_child);
+ if (!child)
return -ENOMEM;
- }
str->nodes_count++;
node->children = child;
- bubbleinsert(node, c, node_child);
+ bubbleinsert(node, c, TAKE_PTR(node_child));
return off;
}
DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
SpecialGlyph unit_active_state_to_glyph(UnitActiveState state) {
- switch (state) {
- case UNIT_ACTIVE:
- return SPECIAL_GLYPH_BLACK_CIRCLE;
- case UNIT_RELOADING:
- return SPECIAL_GLYPH_CIRCLE_ARROW;
- case UNIT_INACTIVE:
- return SPECIAL_GLYPH_WHITE_CIRCLE;
- case UNIT_FAILED:
- return SPECIAL_GLYPH_MULTIPLICATION_SIGN;
- case UNIT_ACTIVATING:
- case UNIT_DEACTIVATING:
- return SPECIAL_GLYPH_BLACK_CIRCLE;
- case UNIT_MAINTENANCE:
- return SPECIAL_GLYPH_WHITE_CIRCLE;
-
- default:
- return SPECIAL_GLYPH_BLACK_CIRCLE;
- }
+ static const SpecialGlyph map[_UNIT_ACTIVE_STATE_MAX] = {
+ [UNIT_ACTIVE] = SPECIAL_GLYPH_BLACK_CIRCLE,
+ [UNIT_RELOADING] = SPECIAL_GLYPH_CIRCLE_ARROW,
+ [UNIT_INACTIVE] = SPECIAL_GLYPH_WHITE_CIRCLE,
+ [UNIT_FAILED] = SPECIAL_GLYPH_MULTIPLICATION_SIGN,
+ [UNIT_ACTIVATING] = SPECIAL_GLYPH_BLACK_CIRCLE,
+ [UNIT_DEACTIVATING] = SPECIAL_GLYPH_BLACK_CIRCLE,
+ [UNIT_MAINTENANCE] = SPECIAL_GLYPH_WHITE_CIRCLE,
+ };
+
+ if (state < 0)
+ return _SPECIAL_GLYPH_INVALID;
+
+ assert(state < _UNIT_ACTIVE_STATE_MAX);
+ return map[state];
}
if (r < 0)
return log_unit_error_errno(u, r, "Failed to determine cgroup path: %m");
- flags = (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI &&
- (u->type == UNIT_SLICE || unit_cgroup_delegate(u))) ? BPF_F_ALLOW_MULTI : 0;
+ flags = supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI ? BPF_F_ALLOW_MULTI : 0;
/* Unref the old BPF program (which will implicitly detach it) right before attaching the new program, to
* minimize the time window when we don't account for IP traffic. */
u->ip_bpf_ingress_installed = bpf_program_unref(u->ip_bpf_ingress_installed);
if (u->ip_bpf_egress) {
- r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path,
- flags | (set_isempty(u->ip_bpf_custom_egress) ? 0 : BPF_F_ALLOW_MULTI));
+ r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, flags);
if (r < 0)
return log_unit_error_errno(u, r, "Attaching egress BPF program to cgroup %s failed: %m", path);
}
if (u->ip_bpf_ingress) {
- r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path,
- flags | (set_isempty(u->ip_bpf_custom_ingress) ? 0 : BPF_F_ALLOW_MULTI));
+ r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, flags);
if (r < 0)
return log_unit_error_errno(u, r, "Attaching ingress BPF program to cgroup %s failed: %m", path);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bpf-foreign.h"
+#include "bpf-program.h"
+#include "cgroup.h"
+#include "memory-util.h"
+#include "mountpoint-util.h"
+#include "set.h"
+
+typedef struct BPFForeignKey BPFForeignKey;
+struct BPFForeignKey {
+ uint32_t prog_id;
+ uint32_t attach_type;
+};
+
+static int bpf_foreign_key_new(uint32_t prog_id,
+ enum bpf_attach_type attach_type,
+ BPFForeignKey **ret) {
+ _cleanup_free_ BPFForeignKey *p = NULL;
+
+ assert(ret);
+
+ p = new(BPFForeignKey, 1);
+ if (!p)
+ return log_oom();
+
+ *p = (BPFForeignKey) {
+ .prog_id = prog_id,
+ .attach_type = attach_type,
+ };
+
+ *ret = TAKE_PTR(p);
+
+ return 0;
+}
+
+static int bpf_foreign_key_compare_func(const BPFForeignKey *a, const BPFForeignKey *b) {
+ int r = CMP(a->prog_id, b->prog_id);
+ if (r != 0)
+ return r;
+
+ return CMP(a->attach_type, b->attach_type);
+}
+
+static void bpf_foreign_key_hash_func(const BPFForeignKey *p, struct siphash *h) {
+ siphash24_compress(&p->prog_id, sizeof(p->prog_id), h);
+ siphash24_compress(&p->attach_type, sizeof(p->attach_type), h);
+}
+
+DEFINE_PRIVATE_HASH_OPS_FULL(bpf_foreign_by_key_hash_ops,
+ BPFForeignKey, bpf_foreign_key_hash_func, bpf_foreign_key_compare_func, free,
+ BPFProgram, bpf_program_unref);
+
+static int attach_programs(Unit *u, const char *path, Hashmap* foreign_by_key, uint32_t attach_flags) {
+ const BPFForeignKey *key;
+ BPFProgram *prog;
+ int r;
+
+ assert(u);
+
+ HASHMAP_FOREACH_KEY(prog, key, foreign_by_key) {
+ r = bpf_program_cgroup_attach(prog, key->attach_type, path, attach_flags);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Attaching foreign BPF program to cgroup %s failed: %m", path);
+ }
+
+ return 0;
+}
+
+/*
+ * Prepare foreign BPF program for installation:
+ * - Load the program from BPF filesystem to the kernel;
+ * - Store program FD identified by program ID and attach type in the unit.
+ */
+static int bpf_foreign_prepare(
+ Unit *u,
+ enum bpf_attach_type attach_type,
+ const char *bpffs_path) {
+ _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_free_ BPFForeignKey *key = NULL;
+ uint32_t prog_id;
+ int r;
+
+ assert(u);
+ assert(bpffs_path);
+
+ r = bpf_program_new_from_bpffs_path(bpffs_path, &prog);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to create foreign BPFProgram: %m");
+
+ r = bpf_program_get_id_by_fd(prog->kernel_fd, &prog_id);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to get BPF program id by fd: %m");
+
+ r = bpf_foreign_key_new(prog_id, attach_type, &key);
+ if (r < 0)
+ return log_unit_error_errno(u, r,
+ "Failed to create foreign BPF program key from path '%s': %m", bpffs_path);
+
+ r = hashmap_ensure_put(&u->bpf_foreign_by_key, &bpf_foreign_by_key_hash_ops, key, prog);
+ if (r == -EEXIST) {
+ log_unit_warning_errno(u, r, "Foreign BPF program already exists, ignoring: %m");
+ return 0;
+ }
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to put foreign BPFProgram into map: %m");
+
+ TAKE_PTR(key);
+ TAKE_PTR(prog);
+
+ return 0;
+}
+
+int bpf_foreign_supported(void) {
+ int r;
+
+ r = cg_all_unified();
+ if (r <= 0)
+ return r;
+
+ return path_is_mount_point("/sys/fs/bpf", NULL, 0);
+}
+
+int bpf_foreign_install(Unit *u) {
+ _cleanup_free_ char *cgroup_path = NULL;
+ CGroupBPFForeignProgram *p;
+ CGroupContext *cc;
+ int r;
+
+ assert(u);
+
+ cc = unit_get_cgroup_context(u);
+ if (!cc)
+ return 0;
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to get cgroup path: %m");
+
+ LIST_FOREACH(programs, p, cc->bpf_foreign_programs) {
+ r = bpf_foreign_prepare(u, p->attach_type, p->bpffs_path);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to prepare foreign BPF hashmap: %m");
+ }
+
+ r = attach_programs(u, cgroup_path, u->bpf_foreign_by_key, BPF_F_ALLOW_MULTI);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to install foreign BPF programs: %m");
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#pragma once
+
+#include "unit.h"
+
+int bpf_foreign_supported(void);
+/*
+ * Attach cgroup-bpf programs foreign to systemd, i.e. loaded to the kernel by an entity
+ * external to systemd.
+ */
+int bpf_foreign_install(Unit *u);
#include "blockdev-util.h"
#include "bpf-devices.h"
#include "bpf-firewall.h"
+#include "bpf-foreign.h"
#include "btrfs-util.h"
#include "bus-error.h"
#include "cgroup-setup.h"
free(b);
}
+void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeignProgram *p) {
+ assert(c);
+ assert(p);
+
+ LIST_REMOVE(programs, c->bpf_foreign_programs, p);
+ free(p->bpffs_path);
+ free(p);
+}
+
void cgroup_context_done(CGroupContext *c) {
assert(c);
c->ip_filters_ingress = strv_free(c->ip_filters_ingress);
c->ip_filters_egress = strv_free(c->ip_filters_egress);
+ while (c->bpf_foreign_programs)
+ cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
+
cpu_set_reset(&c->cpuset_cpus);
cpu_set_reset(&c->cpuset_mems);
}
CGroupIODeviceLatency *l;
CGroupBlockIODeviceBandwidth *b;
CGroupBlockIODeviceWeight *w;
+ CGroupBPFForeignProgram *p;
CGroupDeviceAllow *a;
CGroupContext *c;
IPAddressAccessItem *iaai;
STRV_FOREACH(path, c->ip_filters_egress)
fprintf(f, "%sIPEgressFilterPath: %s\n", prefix, *path);
+
+ LIST_FOREACH(programs, p, c->bpf_foreign_programs)
+ fprintf(f, "%sBPFProgram: %s:%s",
+ prefix, bpf_cgroup_attach_type_to_string(p->attach_type), p->bpffs_path);
}
int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) {
return 0;
}
+int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *bpffs_path) {
+ CGroupBPFForeignProgram *p;
+ _cleanup_free_ char *d = NULL;
+
+ assert(c);
+ assert(bpffs_path);
+
+ if (!path_is_normalized(bpffs_path) || !path_is_absolute(bpffs_path))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not normalized: %m");
+
+ d = strdup(bpffs_path);
+ if (!d)
+ return log_oom();
+
+ p = new(CGroupBPFForeignProgram, 1);
+ if (!p)
+ return log_oom();
+
+ *p = (CGroupBPFForeignProgram) {
+ .attach_type = attach_type,
+ .bpffs_path = TAKE_PTR(d),
+ };
+
+ LIST_PREPEND(programs, c->bpf_foreign_programs, TAKE_PTR(p));
+
+ return 0;
+}
+
#define UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(entry) \
uint64_t unit_get_ancestor_##entry(Unit *u) { \
CGroupContext *c; \
(void) set_attribute_and_warn(u, controller, p, buf);
}
+static void cgroup_apply_bpf_foreign_program(Unit *u) {
+ assert(u);
+
+ (void) bpf_foreign_install(u);
+}
+
static void cgroup_context_apply(
Unit *u,
CGroupMask apply_mask,
if (apply_mask & CGROUP_MASK_BPF_FIREWALL)
cgroup_apply_firewall(u);
+
+ if (apply_mask & CGROUP_MASK_BPF_FOREIGN)
+ cgroup_apply_bpf_foreign_program(u);
}
static bool unit_get_needs_bpf_firewall(Unit *u) {
return false;
}
+static bool unit_get_needs_bpf_foreign_program(Unit *u) {
+ CGroupContext *c;
+ assert(u);
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return false;
+
+ return !LIST_IS_EMPTY(c->bpf_foreign_programs);
+}
+
static CGroupMask unit_get_cgroup_mask(Unit *u) {
CGroupMask mask = 0;
CGroupContext *c;
if (unit_get_needs_bpf_firewall(u))
mask |= CGROUP_MASK_BPF_FIREWALL;
+ if (unit_get_needs_bpf_foreign_program(u))
+ mask |= CGROUP_MASK_BPF_FOREIGN;
+
return mask;
}
if (r > 0)
mask |= CGROUP_MASK_BPF_DEVICES;
+ /* BPF pinned prog */
+ r = bpf_foreign_supported();
+ if (r > 0)
+ mask |= CGROUP_MASK_BPF_FOREIGN;
+
*ret = mask;
return 0;
}
typedef struct CGroupIODeviceLatency CGroupIODeviceLatency;
typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth;
+typedef struct CGroupBPFForeignProgram CGroupBPFForeignProgram;
typedef enum CGroupDevicePolicy {
/* When devices listed, will allow those, plus built-in ones, if none are listed will allow
uint64_t wbps;
};
+struct CGroupBPFForeignProgram {
+ LIST_FIELDS(CGroupBPFForeignProgram, programs);
+ uint32_t attach_type;
+ char *bpffs_path;
+};
+
struct CGroupContext {
bool cpu_accounting;
bool io_accounting;
char **ip_filters_ingress;
char **ip_filters_egress;
+ LIST_HEAD(CGroupBPFForeignProgram, bpf_foreign_programs);
/* For legacy hierarchies */
uint64_t cpu_shares;
void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLatency *l);
void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w);
void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
+void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeignProgram *p);
int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode);
+int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path);
#include "af-list.h"
#include "alloc-util.h"
#include "bpf-firewall.h"
+#include "bpf-foreign.h"
#include "bus-get-properties.h"
#include "cgroup-util.h"
#include "cgroup.h"
return sd_bus_message_close_container(reply);
}
+static int property_get_bpf_foreign_program(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ CGroupContext *c = userdata;
+ CGroupBPFForeignProgram *p;
+ int r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(programs, p, c->bpf_foreign_programs) {
+ const char *attach_type = bpf_cgroup_attach_type_to_string(p->attach_type);
+
+ r = sd_bus_message_append(reply, "(ss)", attach_type, p->bpffs_path);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 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("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_VTABLE_END
};
int b;
if (!UNIT_VTABLE(u)->can_delegate)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
r = sd_bus_message_read(message, "b", &b);
if (r < 0)
CGroupMask mask = 0;
if (streq(name, "DelegateControllers") && !UNIT_VTABLE(u)->can_delegate)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
r = sd_bus_message_enter_container(message, 'a', "s");
if (r < 0)
}
}
+ return 1;
+ } else if (streq(name, "BPFProgram")) {
+ const char *a, *p;
+ size_t n = 0;
+
+ r = sd_bus_message_enter_container(message, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(ss)", &a, &p)) > 0) {
+ int attach_type = bpf_cgroup_attach_type_from_string(a);
+ if (attach_type < 0)
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "%s expects a valid BPF attach type, got '%s'.",
+ name, a);
+
+ if (!path_is_normalized(p) || !path_is_absolute(p))
+ return sd_bus_error_setf(
+ error,
+ SD_BUS_ERROR_INVALID_ARGS,
+ "%s= expects a normalized absolute path.",
+ name);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ r = cgroup_add_bpf_foreign_program(c, attach_type, p);
+ if (r < 0)
+ return r;
+ }
+ n++;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ CGroupBPFForeignProgram *fp;
+ size_t size = 0;
+
+ if (n == 0)
+ while (c->bpf_foreign_programs)
+ cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
+
+ f = open_memstream_unlocked(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fputs(name, f);
+ fputs("=\n", f);
+
+ LIST_FOREACH(programs, fp, c->bpf_foreign_programs)
+ fprintf(f, "%s=%s:%s\n", name,
+ bpf_cgroup_attach_type_to_string(fp->attach_type),
+ fp->bpffs_path);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
+
+ unit_write_setting(u, flags, name, buf);
+
+ if (!LIST_IS_EMPTY(c->bpf_foreign_programs)) {
+ r = bpf_foreign_supported();
+ if (r < 0)
+ return r;
+ if (r == 0)
+ log_full(LOG_DEBUG,
+ "Transient unit %s configures a BPF program pinned to BPF "
+ "filesystem, but the local system does not support that.\n"
+ "Starting this unit will fail!", u->id);
+ }
+ }
+
return 1;
}
return r;
if (u64 <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "CPUQuotaPerSecUSec= value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "CPUQuotaPerSecUSec= value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->cpu_quota_per_sec_usec = u64;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IODeviceWeight= value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "IODeviceWeight= value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
CGroupIODeviceWeight *a = NULL, *b;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIODeviceWeight= out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIODeviceWeight= out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
CGroupBlockIODeviceWeight *a = NULL, *b;
while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
if (!valid_device_allow_pattern(path) || strpbrk(path, WHITESPACE))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node or pattern");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node or pattern");
if (isempty(rwm))
rwm = "rwm";
else if (!in_charset(rwm, "rwm"))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
CGroupDeviceAllow *a = NULL, *b;
return r;
if (!log_level_is_valid(level))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log level value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Log level value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->syslog_priority = (c->syslog_priority & LOG_FACMASK) | level;
return r;
if (!log_facility_unshifted_is_valid(facility))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log facility value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Log facility value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->syslog_priority = (facility << 3) | LOG_PRI(c->syslog_priority);
return r;
if (!isempty(n) && !log_namespace_name_valid(n))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log namespace name not valid");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Log namespace name not valid");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
break;
if (memchr(p, 0, sz))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains zero byte");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains zero byte");
eq = memchr(p, '=', sz);
if (!eq)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains no '=' character");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field contains no '=' character");
if (!journal_field_valid(p, eq - (const char*) p, false))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field invalid");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field invalid");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
t = reallocarray(c->log_extra_fields, c->n_log_extra_fields+1, sizeof(struct iovec));
((uint8_t*) copy)[sz] = 0;
if (!utf8_is_valid(copy))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field is not valid UTF-8");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Journal field is not valid UTF-8");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->log_extra_fields[c->n_log_extra_fields++] = IOVEC_MAKE(copy, sz);
missing_ok = false;
if (!isempty(s) && !streq(s, "~") && !path_is_absolute(s))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (streq(s, "~")) {
return r;
if (!isempty(s) && !fdname_is_valid(s))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
return r;
if (!strv_env_is_valid(l))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (strv_isempty(l)) {
return r;
if (!strv_env_name_or_assignment_is_valid(l))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UnsetEnvironment= list.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UnsetEnvironment= list.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (strv_isempty(l)) {
return r;
if (!oom_score_adjust_is_valid(oa))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "OOM score adjust value out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "OOM score adjust value out of range");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->oom_score_adjust = oa;
return r;
if (!strv_env_name_is_valid(l))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment= block.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment= block.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (strv_isempty(l)) {
if (!path_is_absolute(destination))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not absolute.", destination);
if (!IN_SET(mount_flags, 0, MS_REC))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mount flags.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mount flags.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
u = manager_get_unit_by_pid(m, pid);
if (!u)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
+ return sd_bus_error_set(error, BUS_ERROR_NO_SUCH_UNIT, "Client not member of any unit.");
} else {
u = manager_get_unit(m, name);
if (!u)
else if (sz == 16)
memcpy(&id, a, sz);
else
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid invocation ID");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid invocation ID");
if (sd_id128_is_null(id)) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_ALREADY_SUBSCRIBED, "Client is already subscribed.");
+ return sd_bus_error_set(error, BUS_ERROR_ALREADY_SUBSCRIBED, "Client is already subscribed.");
}
return sd_bus_reply_method_return(message, NULL);
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
}
return sd_bus_reply_method_return(message, NULL);
}
static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed.");
}
static int verify_run_space(const char *message, sd_bus_error *error) {
if (r < 0)
return r;
if (!strv_env_is_valid(plus))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
r = bus_verify_set_environment_async(m, message, error);
if (r < 0)
return r;
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers.");
m->return_value = code;
/* We can't support direct connections with this, as direct connections know no service or unique name
* concept, but the Controller field stores exactly that. */
if (sd_bus_message_get_bus(message) != u->manager->api_bus)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Sorry, Controller= logic only supported via the bus.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Sorry, Controller= logic only supported via the bus.");
r = sd_bus_message_read(message, "s", &controller);
if (r < 0)
assert(u);
if (!MANAGER_IS_SYSTEM(u->manager))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Adding bind mounts at runtime is only supported for system managers.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Adding bind mounts at runtime is only supported for system managers.");
r = mac_selinux_unit_access_check(u, message, "start", error);
if (r < 0)
return r;
if (!path_is_absolute(src) || !path_is_normalized(src))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
if (!is_image && isempty(dest))
dest = src;
else if (!path_is_absolute(dest) || !path_is_normalized(dest))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
if (is_image) {
r = bus_read_mount_options(message, error, &options, NULL, "");
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
if (u->type != UNIT_SERVICE)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not of type .service");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not of type .service");
/* If it would be dropped at startup time, return an error. The context should always be available, but
* there's an assert in exec_needs_mount_namespace, so double-check just in case. */
c = unit_get_exec_context(u);
if (!c)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot access unit execution context");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot access unit execution context");
if (path_startswith_strv(dest, c->inaccessible_paths))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s is not accessible to this unit", dest);
/* Ensure that the unit was started in a private mount namespace */
if (!exec_needs_mount_namespace(c, NULL, unit_get_exec_runtime(u)))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit not running in private mount namespace, cannot activate bind mount");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit not running in private mount namespace, cannot activate bind mount");
unit_pid = unit_main_pid(u);
if (unit_pid == 0 || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not running");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not running");
propagate_directory = strjoina("/run/systemd/propagate/", u->id);
if (is_image)
r = calendar_spec_from_string(str, &c);
if (r == -EINVAL)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec");
if (r < 0)
return r;
}
if (!SIGNAL_VALID(signo))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
r = bus_verify_manage_units_async_full(
u,
r = bus_unit_track_remove_sender(u, message);
if (r == -EUNATCH)
- return sd_bus_error_setf(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
if (r < 0)
return r;
if (r == -EOPNOTSUPP)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not supporting cleaning.", u->id);
if (r == -EUNATCH)
- return sd_bus_error_setf(error, BUS_ERROR_NOTHING_TO_CLEAN, "No matching resources found.");
+ return sd_bus_error_set(error, BUS_ERROR_NOTHING_TO_CLEAN, "No matching resources found.");
if (r == -EBUSY)
- return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit is not inactive or has pending job.");
+ return sd_bus_error_set(error, BUS_ERROR_UNIT_BUSY, "Unit is not inactive or has pending job.");
if (r < 0)
return r;
if (r == -EOPNOTSUPP)
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not support freezing.", u->id);
if (r == -EBUSY)
- return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job.");
+ return sd_bus_error_set(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job.");
if (r == -EHOSTDOWN)
- return sd_bus_error_setf(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive.");
+ return sd_bus_error_set(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive.");
if (r == -EALREADY)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Previously requested freezer operation for unit '%s' is still in progress.", u->id);
if (r < 0)
}
if (!unit_cgroup_delegate(u))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Process migration not available on non-delegated units.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Process migration not available on non-delegated units.");
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not active, refusing.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unit is not active, refusing.");
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID, &creds);
if (r < 0)
return r;
if (some_plus_minus && some_absolute)
- return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, "Bad marker syntax.");
+ return sd_bus_error_set(error, BUS_ERROR_BAD_UNIT_SETTING, "Bad marker syntax.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (some_absolute)
return r;
if (k > 255)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Exit status must be in range 0…255 or negative.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Exit status must be in range 0…255 or negative.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
*p = k < 0 ? -1 : k;
const char *s;
if (!UNIT_HAS_CGROUP_CONTEXT(u))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
if (u->type == UNIT_SLICE)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
if (unit_has_name(u, SPECIAL_INIT_SCOPE))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
r = sd_bus_message_read(message, "s", &s);
if (r < 0)
if (manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SERVICE) ||
manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SOCKET)) {
- r = sd_bus_error_setf(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down.");
+ r = sd_bus_error_set(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down.");
goto failed;
}
c->stdin_data_size = 0;
c->network_namespace_path = mfree(c->network_namespace_path);
+ c->ipc_namespace_path = mfree(c->ipc_namespace_path);
c->log_namespace = mfree(c->log_namespace);
$1.ManagedOOMMemoryPressure, config_parse_managed_oom_mode, 0, offsetof($1, cgroup_context.moom_mem_pressure)
$1.ManagedOOMMemoryPressureLimit, config_parse_managed_oom_mem_pressure_limit, 0, offsetof($1, cgroup_context.moom_mem_pressure_limit)
$1.ManagedOOMPreference, config_parse_managed_oom_preference, 0, offsetof($1, cgroup_context.moom_preference)
-$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0'
+$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0
+$1.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof($1, cgroup_context)'
)m4_dnl
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
#include "all-units.h"
#include "alloc-util.h"
#include "bpf-firewall.h"
+#include "bpf-program.h"
#include "bus-error.h"
#include "bus-internal.h"
#include "bus-util.h"
return 0;
}
+int config_parse_bpf_foreign_program(
+ 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) {
+ _cleanup_free_ char *resolved = NULL, *word = NULL;
+ CGroupContext *c = data;
+ Unit *u = userdata;
+ int attach_type, r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ while (c->bpf_foreign_programs)
+ cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
+
+ return 0;
+ }
+
+ r = extract_first_word(&rvalue, &word, ":", 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse foreign BPF program, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ attach_type = bpf_cgroup_attach_type_from_string(word);
+ if (attach_type < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown BPF attach type=%s, ignoring: %s", word, rvalue);
+ return 0;
+ }
+
+ r = unit_full_printf(u, rvalue, &resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ return 0;
+
+ r = cgroup_add_bpf_foreign_program(c, attach_type, resolved);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add foreign BPF program to cgroup context: %m");
+
+ return 0;
+}
+
static int merge_by_names(Unit **u, Set *names, const char *id) {
char *k;
int r;
CONFIG_PARSER_PROTOTYPE(config_parse_mount_images);
CONFIG_PARSER_PROTOTYPE(config_parse_socket_timestamping);
CONFIG_PARSER_PROTOTYPE(config_parse_extension_images);
+CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
static int write_container_id(void) {
const char *c;
- int r;
+ int r = 0; /* avoid false maybe-uninitialized warning */
c = getenv("container");
if (isempty(c))
assert(mode < _JOB_MODE_MAX);
if (mode == JOB_ISOLATE && type != JOB_START)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start.");
if (mode == JOB_ISOLATE && !unit->allow_isolate)
- return sd_bus_error_setf(error, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated.");
+ return sd_bus_error_set(error, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated.");
if (mode == JOB_TRIGGERING && type != JOB_STOP)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "--job-mode=triggering is only valid for stop.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "--job-mode=triggering is only valid for stop.");
log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
bpf-devices.h
bpf-firewall.c
bpf-firewall.h
+ bpf-foreign.c
+ bpf-foreign.h
cgroup.c
cgroup.h
core-varlink.c
if (!enforce)
return 0;
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
}
tclass = "system";
r = errno_or_else(EPERM);
if (enforce)
- sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
+ sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
}
log_debug_errno(r, "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s path=%s cmdline=%s: %m",
#include "all-units.h"
#include "alloc-util.h"
#include "bpf-firewall.h"
+#include "bpf-foreign.h"
#include "bus-common-errors.h"
#include "bus-util.h"
#include "cgroup-setup.h"
set_free(u->ip_bpf_custom_ingress_installed);
set_free(u->ip_bpf_custom_egress_installed);
+ hashmap_free(u->bpf_foreign_by_key);
+
bpf_program_unref(u->bpf_device_control_installed);
condition_free_list(u->conditions);
Set *ip_bpf_custom_egress;
Set *ip_bpf_custom_egress_installed;
+ /* BPF programs managed (e.g. loaded to kernel) by an entity external to systemd,
+ * attached to unit cgroup by provided program fd and attach type. */
+ Hashmap *bpf_foreign_by_key;
+
uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX];
/* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new
struct iovec_wrapper *iovw,
int input_fd) {
+ _cleanup_(json_variant_unrefp) JsonVariant *json_metadata = NULL;
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
_cleanup_free_ char *filename = NULL, *coredump_data = NULL;
_cleanup_free_ char *stacktrace = NULL;
char *core_message;
+ const char *module_name;
uint64_t coredump_size = UINT64_MAX;
bool truncated = false;
+ JsonVariant *module_json;
int r;
-
assert(context);
assert(iovw);
assert(input_fd >= 0);
"than %"PRIu64" (the configured maximum)",
coredump_size, arg_process_size_max);
} else
- coredump_make_stack_trace(coredump_fd, context->meta[META_EXE], &stacktrace);
+ coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata);
#endif
log:
if (truncated)
(void) iovw_put_string_field(iovw, "COREDUMP_TRUNCATED=", "1");
+ /* If we managed to parse any ELF metadata (build-id, ELF package meta),
+ * attach it as journal metadata. */
+ if (json_metadata) {
+ _cleanup_free_ char *formatted_json = NULL;
+
+ r = json_variant_format(json_metadata, 0, &formatted_json);
+ if (r < 0)
+ return log_error_errno(r, "Failed to format JSON package metadata: %m");
+
+ (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_JSON=", formatted_json);
+ }
+
+ JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, json_metadata) {
+ JsonVariant *package_name, *package_version;
+
+ /* We only add structured fields for the 'main' ELF module */
+ if (!path_equal_filename(module_name, context->meta[META_EXE]))
+ continue;
+
+ package_name = json_variant_by_key(module_json, "name");
+ if (package_name)
+ (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_NAME=", json_variant_string(package_name));
+
+ package_version = json_variant_by_key(module_json, "version");
+ if (package_version)
+ (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_VERSION=", json_variant_string(package_version));
+ }
+
/* Optionally store the entire coredump in the journal */
if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
if (coredump_size <= arg_journal_size_max) {
*boot_id = NULL, *machine_id = NULL, *hostname = NULL,
*slice = NULL, *cgroup = NULL, *owner_uid = NULL,
*message = NULL, *timestamp = NULL, *filename = NULL,
- *truncated = NULL, *coredump = NULL;
+ *truncated = NULL, *coredump = NULL,
+ *pkgmeta_name = NULL, *pkgmeta_version = NULL, *pkgmeta_json = NULL;
const void *d;
size_t l;
bool normal_coredump;
RETRIEVE(d, l, "COREDUMP_FILENAME", filename);
RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated);
RETRIEVE(d, l, "COREDUMP", coredump);
+ RETRIEVE(d, l, "COREDUMP_PACKAGE_NAME", pkgmeta_name);
+ RETRIEVE(d, l, "COREDUMP_PACKAGE_VERSION", pkgmeta_version);
+ RETRIEVE(d, l, "COREDUMP_PACKAGE_JSON", pkgmeta_json);
RETRIEVE(d, l, "_BOOT_ID", boot_id);
RETRIEVE(d, l, "_MACHINE_ID", machine_id);
RETRIEVE(d, l, "MESSAGE", message);
else
fprintf(file, " Storage: none\n");
+ if (pkgmeta_name && pkgmeta_version)
+ fprintf(file, " Package: %s/%s\n", pkgmeta_name, pkgmeta_version);
+
+ /* Print out the build-id of the 'main' ELF module, by matching the JSON key
+ * with the 'exe' field. */
+ if (exe && pkgmeta_json) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+ r = json_parse(pkgmeta_json, 0, &v, NULL, NULL);
+ if (r < 0)
+ log_warning_errno(r, "json_parse on %s failed, ignoring: %m", pkgmeta_json);
+ else {
+ const char *module_name;
+ JsonVariant *module_json;
+
+ JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, v) {
+ JsonVariant *build_id;
+
+ /* We only print the build-id for the 'main' ELF module */
+ if (!path_equal_filename(module_name, exe))
+ continue;
+
+ build_id = json_variant_by_key(module_json, "buildId");
+ if (build_id)
+ fprintf(file, " build-id: %s\n", json_variant_string(build_id));
+
+ break;
+ }
+ }
+ }
+
if (message) {
_cleanup_free_ char *m = NULL;
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <dwarf.h>
+#include <elfutils/libdwelf.h>
#include <elfutils/libdwfl.h>
+#include <libelf.h>
#include <sys/types.h>
#include <unistd.h>
#include "fileio.h"
#include "fd-util.h"
#include "format-util.h"
+#include "hexdecoct.h"
#include "macro.h"
#include "stacktrace.h"
#include "string-util.h"
#define FRAMES_MAX 64
#define THREADS_MAX 64
+#define ELF_PACKAGE_METADATA_ID 0xcafe1a7e
struct stack_context {
FILE *f;
Elf *elf;
unsigned n_thread;
unsigned n_frame;
+ JsonVariant **package_metadata;
+ Set **modules;
};
static int frame_callback(Dwfl_Frame *frame, void *userdata) {
return DWARF_CB_OK;
}
-static int make_stack_trace(int fd, const char *executable, char **ret) {
+static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *elf, struct stack_context *c) {
+ size_t n_program_headers;
+ int r;
+
+ assert(name);
+ assert(elf);
+ assert(c);
+
+ /* When iterating over PT_LOAD we will visit modules more than once */
+ if (set_contains(*c->modules, name))
+ return DWARF_CB_OK;
+
+ r = elf_getphdrnum(elf, &n_program_headers);
+ if (r < 0) /* Not the handle we are looking for - that's ok, skip it */
+ return DWARF_CB_OK;
+
+ /* Iterate over all program headers in that ELF object. These will have been copied by
+ * the kernel verbatim when the core file is generated. */
+ for (size_t i = 0; i < n_program_headers; ++i) {
+ size_t note_offset = 0, name_offset, desc_offset;
+ GElf_Phdr mem, *program_header;
+ GElf_Nhdr note_header;
+ Elf_Data *data;
+
+ /* Package metadata is in PT_NOTE headers. */
+ program_header = gelf_getphdr(elf, i, &mem);
+ if (!program_header || program_header->p_type != PT_NOTE)
+ continue;
+
+ /* Fortunately there is an iterator we can use to walk over the
+ * elements of a PT_NOTE program header. We are interested in the
+ * note with type. */
+ data = elf_getdata_rawchunk(elf,
+ program_header->p_offset,
+ program_header->p_filesz,
+ ELF_T_NHDR);
+
+ while (note_offset < data->d_size &&
+ (note_offset = gelf_getnote(data, note_offset, ¬e_header, &name_offset, &desc_offset)) > 0) {
+ const char *note_name = (const char *)data->d_buf + name_offset;
+ const char *payload = (const char *)data->d_buf + desc_offset;
+
+ if (note_header.n_namesz == 0 || note_header.n_descsz == 0)
+ continue;
+
+ /* Package metadata might have different owners, but the
+ * magic ID is always the same. */
+ if (note_header.n_type == ELF_PACKAGE_METADATA_ID) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
+
+ r = json_parse(payload, 0, &v, NULL, NULL);
+ if (r < 0) {
+ log_error_errno(r, "json_parse on %s failed: %m", payload);
+ return DWARF_CB_ABORT;
+ }
+
+ /* First pretty-print to the buffer, so that the metadata goes as
+ * plaintext in the journal. */
+ fprintf(c->f, "Metadata for module %s owned by %s found: ",
+ name, note_name);
+ json_variant_dump(v, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, c->f, NULL);
+ fputc('\n', c->f);
+
+ /* Secondly, if we have a build-id, merge it in the same JSON object
+ * so that it appears all nicely together in the logs/metadata. */
+ if (id_json) {
+ r = json_variant_merge(&v, id_json);
+ if (r < 0) {
+ log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
+ return DWARF_CB_ABORT;
+ }
+ }
+
+ /* Then we build a new object using the module name as the key, and merge it
+ * with the previous parses, so that in the end it all fits together in a single
+ * JSON blob. */
+ r = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(name, JSON_BUILD_VARIANT(v))));
+ if (r < 0) {
+ log_error_errno(r, "Failed to build JSON object: %m");
+ return DWARF_CB_ABORT;
+ }
+ r = json_variant_merge(c->package_metadata, w);
+ if (r < 0) {
+ log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
+ return DWARF_CB_ABORT;
+ }
+
+ /* Finally stash the name, so we avoid double visits. */
+ r = set_put_strdup(c->modules, name);
+ if (r < 0) {
+ log_error_errno(r, "set_put_strdup failed: %m");
+ return DWARF_CB_ABORT;
+ }
+
+ return DWARF_CB_OK;
+ }
+ }
+ }
+
+ /* Didn't find package metadata for this module - that's ok, just go to the next. */
+ return DWARF_CB_OK;
+}
+
+static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, Dwarf_Addr start, void *arg) {
+ _cleanup_(json_variant_unrefp) JsonVariant *id_json = NULL;
+ struct stack_context *c = arg;
+ size_t n_program_headers;
+ GElf_Addr id_vaddr, bias;
+ const unsigned char *id;
+ int id_len, r;
+ Elf *elf;
+
+ assert(mod);
+ assert(c);
+
+ if (!name)
+ name = "(unnamed)"; /* For logging purposes */
+
+ /* We are iterating on each "module", which is what dwfl calls ELF objects contained in the
+ * core file, and extracting the build-id first and then the package metadata.
+ * We proceed in a best-effort fashion - not all ELF objects might contain both or either.
+ * The build-id is easy, as libdwfl parses it during the dwfl_core_file_report() call and
+ * stores it separately in an internal library struct. */
+ id_len = dwfl_module_build_id(mod, &id, &id_vaddr);
+ if (id_len <= 0)
+ /* If we don't find a build-id, note it in the journal message, and try
+ * anyway to find the package metadata. It's unlikely to have the latter
+ * without the former, but there's no hard rule. */
+ fprintf(c->f, "Found module %s without build-id.\n", name);
+ else {
+ JsonVariant *build_id;
+
+ /* We will later parse package metadata json and pass it to our caller. Prepare the
+ * build-id in json format too, so that it can be appended and parsed cleanly. It
+ * will then be added as metadata to the journal message with the stack trace. */
+ r = json_build(&id_json, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("buildId", JSON_BUILD_HEX(id, id_len))));
+ if (r < 0) {
+ log_error_errno(r, "json_build on build-id failed: %m");
+ return DWARF_CB_ABORT;
+ }
+
+ build_id = json_variant_by_key(id_json, "buildId");
+ assert_se(build_id);
+ fprintf(c->f, "Found module %s with build-id: %s\n", name, json_variant_string(build_id));
+ }
+
+ /* The .note.package metadata is more difficult. From the module, we need to get a reference
+ * to the ELF object first. We might be lucky and just get it from elfutils. */
+ elf = dwfl_module_getelf(mod, &bias);
+ if (elf)
+ return parse_package_metadata(name, id_json, elf, c);
+
+ /* We did not get the ELF object. That is likely because we didn't get direct
+ * access to the executable, and the version of elfutils does not yet support
+ * parsing it out of the core file directly.
+ * So fallback to manual extraction - get the PT_LOAD section from the core,
+ * and if it's the right one we can interpret it as an Elf object, and parse
+ * its notes manually. */
+
+ r = elf_getphdrnum(c->elf, &n_program_headers);
+ if (r < 0) {
+ log_warning("Could not parse number of program headers from core file: %s",
+ elf_errmsg(-1)); /* -1 retrieves the most recent error */
+ return DWARF_CB_OK;
+ }
+
+ for (size_t i = 0; i < n_program_headers; ++i) {
+ GElf_Phdr mem, *program_header;
+ Elf_Data *data;
+
+ /* The core file stores the ELF files in the PT_LOAD segment .*/
+ program_header = gelf_getphdr(c->elf, i, &mem);
+ if (!program_header || program_header->p_type != PT_LOAD)
+ continue;
+
+ /* Now get a usable Elf reference, and parse the notes from it. */
+ data = elf_getdata_rawchunk(c->elf,
+ program_header->p_offset,
+ program_header->p_filesz,
+ ELF_T_NHDR);
+
+ Elf *memelf = elf_memory(data->d_buf, data->d_size);
+ if (!memelf)
+ continue;
+ r = parse_package_metadata(name, id_json, memelf, c);
+ if (r != DWARF_CB_OK)
+ return r;
+ }
+
+ return DWARF_CB_OK;
+}
+
+static int parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
static const Dwfl_Callbacks callbacks = {
.find_elf = dwfl_build_id_find_elf,
+ .section_address = dwfl_offline_section_address,
.find_debuginfo = dwfl_standard_find_debuginfo,
};
- struct stack_context c = {};
+ _cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
+ _cleanup_(set_freep) Set *modules = NULL;
+ struct stack_context c = {
+ .package_metadata = &package_metadata,
+ .modules = &modules,
+ };
char *buf = NULL;
size_t sz = 0;
int r;
goto finish;
}
+ if (dwfl_getmodules(c.dwfl, &module_callback, &c, 0) < 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
r = -EINVAL;
goto finish;
c.f = safe_fclose(c.f);
*ret = TAKE_PTR(buf);
+ if (ret_package_metadata)
+ *ret_package_metadata = TAKE_PTR(package_metadata);
r = 0;
return r;
}
-void coredump_make_stack_trace(int fd, const char *executable, char **ret) {
+void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
int r;
- r = make_stack_trace(fd, executable, ret);
+ r = parse_core(fd, executable, ret, ret_package_metadata);
if (r == -EINVAL)
log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
else if (r < 0)
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-void coredump_make_stack_trace(int fd, const char *executable, char **ret);
+#include "json.h"
+
+void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata);
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_free_ char *friendly = NULL;
int keyslot = arg_key_slot, r;
- size_t decrypted_key_size;
+ size_t decrypted_key_size = 0; /* Silence gcc warning about unitialized variable */
assert(cd);
assert(name);
&policy_hash, &policy_hash_size,
&keyslot,
&token);
- if (r == -ENXIO) {
+ if (r == -ENXIO)
/* No further TPM2 tokens found in the LUKS2 header.*/
- if (found_some)
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "No TPM2 metadata matching the current system state found in LUKS2 header, falling back to traditional unlocking.");
- else
- return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
- "No TPM2 metadata enrolled in LUKS2 header, falling back to traditional unlocking.");
- }
+ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN),
+ found_some
+ ? "No TPM2 metadata matching the current system state found in LUKS2 header, falling back to traditional unlocking."
+ : "No TPM2 metadata enrolled in LUKS2 header, falling back to traditional unlocking.");
if (r < 0)
return r;
if (r != -EAGAIN) /* EAGAIN means: no tpm2 chip found */
return r;
}
+ assert(decrypted_key);
if (!monitor) {
/* We didn't find the TPM2 device. In this case, watch for it via udev. Let's create
}
static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
+#if HAVE_LIBCRYPTSETUP
_cleanup_free_ char *e = NULL, *n = NULL, *d = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
}
return 0;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Partition is encrypted, but the project was compiled without libcryptsetup support");
+#endif
}
static int add_mount(
return dispatch_verb(argc, argv, verbs, NULL);
}
-DEFINE_MAIN_FUNCTION(run);
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
r = user_record_load(hr, v, flags);
if (r < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "JSON data is not a valid identity record");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "JSON data is not a valid identity record");
*ret = TAKE_PTR(hr);
return 0;
switch (e) {
case -EMSGSIZE:
- return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrunk");
+ return sd_bus_error_set(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrunk");
case -ETXTBSY:
- return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrunk offline");
+ return sd_bus_error_set(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrunk offline");
case -ERANGE:
- return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File system size too small");
+ return sd_bus_error_set(error, BUS_ERROR_BAD_HOME_SIZE, "File system size too small");
case -ENOLINK:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected storage backend");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected storage backend");
case -EPROTONOSUPPORT:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected file system");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "System does not support selected file system");
case -ENOTTY:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on storage backend");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on storage backend");
case -ESOCKTNOSUPPORT:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on file system");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Operation not supported on file system");
case -ENOKEY:
return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD, "Password for home %s is incorrect or not sufficient for authentication.", h->user_name);
case -EBADSLT:
case -EREMOTEIO:
return sd_bus_error_setf(error, BUS_ERROR_BAD_RECOVERY_KEY, "Recovery key for home %s is incorrect or not sufficient for authentication.", h->user_name);
case -ENOANO:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
case -ERFKILL:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
case -EMEDIUMTYPE:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence.");
case -ENOSTR:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)");
case -EOWNERDEAD:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
case -ENOLCK:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN, "Bad PIN of security token.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_BAD_PIN, "Bad PIN of security token.");
case -ETOOMANYREFS:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, "Bad PIN of security token, and only a few tries left.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, "Bad PIN of security token, and only a few tries left.");
case -EUCLEAN:
- return sd_bus_error_setf(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT, "Bad PIN of security token, and only one try left.");
+ return sd_bus_error_set(error, BUS_ERROR_TOKEN_BAD_PIN_ONE_TRY_LEFT, "Bad PIN of security token, and only one try left.");
case -EBUSY:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
case -ENOEXEC:
return sd_bus_error_setf(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again in %s!",
format_timespan(buf, sizeof(buf), t - n, USEC_PER_SEC));
- return sd_bus_error_setf(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again later.");
+ return sd_bus_error_set(error, BUS_ERROR_AUTHENTICATION_LIMIT_HIT, "Too many login attempts, please try again later.");
}
return 0;
assert(hr);
if (!user_record_compatible(hr, h->record))
- return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Updated user record is not compatible with existing one.");
+ return sd_bus_error_set(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Updated user record is not compatible with existing one.");
c = user_record_compare_last_change(hr, h->record); /* refuse downgrades */
if (c < 0)
- return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_DOWNGRADE, "Refusing to update to older home record.");
+ return sd_bus_error_set(error, BUS_ERROR_HOME_RECORD_DOWNGRADE, "Refusing to update to older home record.");
if (!secret && FLAGS_SET(hr->mask, USER_RECORD_SECRET)) {
r = user_record_clone(hr, USER_RECORD_EXTRACT_SECRET, &saved_secret);
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Home record different but timestamp remained the same, refusing.");
+ return sd_bus_error_set(error, BUS_ERROR_HOME_RECORD_MISMATCH, "Home record different but timestamp remained the same, refusing.");
}
r = home_start_work(h, verb, new_hr, secret);
if (disk_size == UINT64_MAX || disk_size == h->record->disk_size) {
if (h->record->disk_size == UINT64_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "No disk size to resize to specified.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "No disk size to resize to specified.");
c = user_record_ref(h->record); /* Shortcut if size is unspecified or matches the record */
} else {
assert(o);
assert(o->type == OPERATION_ACQUIRE);
+ assert(!h->current_operation);
+
switch (home_get_state(h)) {
case HOME_UNFIXATED:
break;
case HOME_ABSENT:
- r = sd_bus_error_setf(&error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
- break;
+ r = sd_bus_error_setf(&error, BUS_ERROR_HOME_ABSENT,
+ "Home %s is currently missing or not plugged in.", h->user_name);
+ goto check;
case HOME_INACTIVE:
case HOME_DIRTY:
return 0;
}
- assert(!h->current_operation);
-
- if (call) {
- r = home_ratelimit(h, &error);
- if (r >= 0)
- r = call(h, o->secret, for_state, &error);
- }
+ r = home_ratelimit(h, &error);
+ if (r >= 0)
+ r = call(h, o->secret, for_state, &error);
+ check:
if (r != 0) /* failure or completed */
operation_result(o, r, &error);
else /* ongoing */
if (o) {
if (ordered_set_size(h->pending_operations) >= PENDING_OPERATIONS_MAX)
- return sd_bus_error_setf(error, BUS_ERROR_TOO_MANY_OPERATIONS, "Too many client operations requested");
+ return sd_bus_error_set(error, BUS_ERROR_TOO_MANY_OPERATIONS, "Too many client operations requested");
r = ordered_set_ensure_put(&h->pending_operations, &operation_hash_ops, o);
if (r < 0)
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_KEY, "Can't sign without local key.");
+ return sd_bus_error_set(error, BUS_ERROR_NO_PRIVATE_KEY, "Can't sign without local key.");
return user_record_sign(u, m->private_key, ret);
}
UserRecord **ret_home) {
_cleanup_free_ char *dm_name = NULL, *dm_node = NULL, *subdir = NULL, *disk_uuid_path = NULL, *temporary_image_path = NULL;
- uint64_t host_size, encrypted_size, partition_offset, partition_size;
+ uint64_t encrypted_size,
+ host_size = 0, partition_offset = 0, partition_size = 0; /* Unnecessary initialization to appease gcc */
bool image_created = false, dm_activated = false, mounted = false;
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
sd_id128_t partition_uuid, fs_uuid, luks_uuid, disk_uuid;
assert(hr);
if (hr->disposition >= 0 && hr->disposition != USER_REGULAR)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
if (hr->storage >= 0 && !IN_SET(hr->storage, USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
if (gid_is_valid(hr->gid) && hr->uid != (uid_t) hr->gid)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
if (hr->service && !streq(hr->service, "io.systemd.Home"))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
return 0;
}
if (r < 0)
return r;
if (id <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
if (!t)
case SD_DHCP6_OPTION_IA_PD_PREFIX:
- if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD))
+ if (ia->type != SD_DHCP6_OPTION_IA_PD)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA PD Prefix option not in IA PD option");
/* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */
int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
-int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
+int dhcp_lease_save(const sd_dhcp_lease *lease, const char *lease_file);
int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
#include "tmpfile-util.h"
#include "unaligned.h"
-int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_address(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_broadcast(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
+int sd_dhcp_lease_get_lifetime(const sd_dhcp_lease *lease, uint32_t *lifetime) {
assert_return(lease, -EINVAL);
assert_return(lifetime, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) {
+int sd_dhcp_lease_get_t1(const sd_dhcp_lease *lease, uint32_t *t1) {
assert_return(lease, -EINVAL);
assert_return(t1, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) {
+int sd_dhcp_lease_get_t2(const sd_dhcp_lease *lease, uint32_t *t2) {
assert_return(lease, -EINVAL);
assert_return(t2, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
+int sd_dhcp_lease_get_mtu(const sd_dhcp_lease *lease, uint16_t *mtu) {
assert_return(lease, -EINVAL);
assert_return(mtu, -EINVAL);
}
int sd_dhcp_lease_get_servers(
- sd_dhcp_lease *lease,
+ const sd_dhcp_lease *lease,
sd_dhcp_lease_server_type_t what,
const struct in_addr **addr) {
return (int) lease->servers[what].size;
}
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_dns(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_DNS, addr);
}
-int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_ntp(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_NTP, addr);
}
-int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_sip(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr);
}
-int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_pop3(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr);
}
-int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_smtp(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr);
}
-int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_lpr(const sd_dhcp_lease *lease, const struct in_addr **addr) {
return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr);
}
-int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
+int sd_dhcp_lease_get_domainname(const sd_dhcp_lease *lease, const char **domainname) {
assert_return(lease, -EINVAL);
assert_return(domainname, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
+int sd_dhcp_lease_get_hostname(const sd_dhcp_lease *lease, const char **hostname) {
assert_return(lease, -EINVAL);
assert_return(hostname, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
+int sd_dhcp_lease_get_root_path(const sd_dhcp_lease *lease, const char **root_path) {
assert_return(lease, -EINVAL);
assert_return(root_path, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_router(const sd_dhcp_lease *lease, const struct in_addr **addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
return (int) lease->router_size;
}
-int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_netmask(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_server_identifier(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
+int sd_dhcp_lease_get_next_server(const sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);
* The returned routes array must be freed by the caller.
* Route objects have the same lifetime of the lease and must not be freed.
*/
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
+int sd_dhcp_lease_get_routes(const sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
sd_dhcp_route **ret;
unsigned i;
return (int) lease->static_route_size;
}
-int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
+int sd_dhcp_lease_get_search_domains(const sd_dhcp_lease *lease, char ***domains) {
size_t r;
assert_return(lease, -EINVAL);
return -ENODATA;
}
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
+int sd_dhcp_lease_get_vendor_specific(const sd_dhcp_lease *lease, const void **data, size_t *data_len) {
assert_return(lease, -EINVAL);
assert_return(data, -EINVAL);
assert_return(data_len, -EINVAL);
return 0;
}
-int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
+int dhcp_lease_save(const sd_dhcp_lease *lease, const char *lease_file) {
_cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
struct sd_dhcp_raw_option *option;
return 0;
}
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
+int sd_dhcp_lease_get_client_id(const sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
assert_return(lease, -EINVAL);
assert_return(client_id, -EINVAL);
assert_return(client_id_len, -EINVAL);
return 0;
}
-int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) {
+int sd_dhcp_lease_get_timezone(const sd_dhcp_lease *lease, const char **tz) {
assert_return(lease, -EINVAL);
assert_return(tz, -EINVAL);
return 0;
}
-int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination) {
+int sd_dhcp_route_get_destination(const sd_dhcp_route *route, struct in_addr *destination) {
assert_return(route, -EINVAL);
assert_return(destination, -EINVAL);
return 0;
}
-int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length) {
+int sd_dhcp_route_get_destination_prefix_length(const sd_dhcp_route *route, uint8_t *length) {
assert_return(route, -EINVAL);
assert_return(length, -EINVAL);
return 0;
}
-int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
+int sd_dhcp_route_get_gateway(const sd_dhcp_route *route, struct in_addr *gateway) {
assert_return(route, -EINVAL);
assert_return(gateway, -EINVAL);
return 0;
}
-int sd_dhcp_route_get_option(sd_dhcp_route *route) {
+int sd_dhcp_route_get_option(const sd_dhcp_route *route) {
assert_return(route, -EINVAL);
return route->option;
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
assert_return(client, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->ifindex = ifindex;
return 0;
assert_return(client, -EINVAL);
assert_return(local_address, -EINVAL);
assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL);
-
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->local_address = *local_address;
assert_return(client, -EINVAL);
assert_return(addr, -EINVAL);
assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
-
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
if (arp_type == ARPHRD_ETHER)
assert_return(addr_len == ETH_ALEN, -EINVAL);
assert_return(client, -EINVAL);
assert_return(pd_address, -EINVAL);
-
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->hint_pd_prefix.iapdprefix.address = *pd_address;
client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen;
assert_return(client, -EINVAL);
assert_return(duid_len == 0 || duid != NULL, -EINVAL);
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
if (duid) {
r = dhcp_validate_duid_len(duid_type, duid_len, true);
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->ia_na.ia_na.id = htobe32(iaid);
client->ia_pd.ia_pd.id = htobe32(iaid);
int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
client->information_request = enabled;
assert_return(client->ifindex > 0, -EINVAL);
assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL);
- if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
+ if (client->state != DHCP6_STATE_STOPPED)
return -EBUSY;
if (!client->information_request && !client->request)
sd_device_new_from_stat_rdev;
sd_device_trigger;
} LIBSYSTEMD_247;
+
+LIBSYSTEMD_249 {
+global:
+ sd_device_monitor_filter_add_match_sysattr;
+ sd_device_monitor_filter_add_match_parent;
+} LIBSYSTEMD_248;
sd-device/device-monitor.c
sd-device/device-private.c
sd-device/device-private.h
+ sd-device/device-util.c
sd-device/device-util.h
sd-device/sd-device.c
sd-hwdb/hwdb-internal.h
[['src/libsystemd/sd-device/test-sd-device.c']],
+ [['src/libsystemd/sd-device/test-device-util.c']],
+
[['src/libsystemd/sd-device/test-sd-device-monitor.c']],
]
if (m->code == BUS_ERROR_MAP_END_MARKER)
break;
- if (streq(m->name, name))
+ if (streq(m->name, name)) {
+ assert(m->code > 0);
return m->code;
+ }
}
m = ALIGN_TO_PTR(__start_SYSTEMD_BUS_ERROR_MAP, sizeof(void*));
continue;
}
- if (streq(m->name, name))
+ if (streq(m->name, name)) {
+ assert(m->code > 0);
return m->code;
+ }
m++;
}
}
_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
+ int r;
if (!name)
return 0;
- if (!e)
- goto finish;
- assert_return(!bus_error_is_dirty(e), -EINVAL);
+ if (e) {
+ assert_return(!bus_error_is_dirty(e), -EINVAL);
- e->name = strdup(name);
- if (!e->name) {
- *e = BUS_ERROR_OOM;
- return -ENOMEM;
- }
+ e->name = strdup(name);
+ if (!e->name) {
+ *e = BUS_ERROR_OOM;
+ return -ENOMEM;
+ }
- if (message)
- e->message = strdup(message);
+ if (message)
+ e->message = strdup(message);
- e->_need_free = 1;
+ e->_need_free = 1;
+ }
-finish:
- return -bus_error_name_to_errno(name);
+ r = bus_error_name_to_errno(name);
+ assert(r > 0);
+ return -r;
}
int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
+ int r;
if (!name)
return 0;
e->_need_free = 1;
}
- return -bus_error_name_to_errno(name);
+ r = bus_error_name_to_errno(name);
+ assert(r > 0);
+ return -r;
}
_public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
+ int r;
if (format) {
- int r;
va_list ap;
va_start(ap, format);
r = bus_error_setfv(e, name, format, ap);
+ assert(!name || r < 0);
va_end(ap);
return r;
}
- return sd_bus_error_set(e, name, NULL);
+ r = sd_bus_error_set(e, name, NULL);
+ assert(!name || r < 0);
+ return r;
}
_public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
if (!e)
return -error;
if (error == 0)
- return -error;
+ return 0;
assert_return(!bus_error_is_dirty(e), -EINVAL);
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus, sd_bus, bus_free);
_public_ int sd_bus_is_open(sd_bus *bus) {
- assert_return(bus, -EINVAL);
+ if (!bus)
+ return 0;
+
assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
}
_public_ int sd_bus_is_ready(sd_bus *bus) {
- assert_return(bus, -EINVAL);
+ if (!bus)
+ return 0;
+
assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_pid_changed(bus), -ECHILD);
return 1;
}
- return sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry.");
} else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR)
return sd_bus_error_copy(error, &incoming->error);
return 0;
}
-static bool match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) {
- const char *value;
-
- assert(device);
- assert(sysattr);
-
- if (sd_device_get_sysattr_value(device, sysattr, &value) < 0)
- return false;
-
- if (!match_value)
- return true;
-
- if (fnmatch(match_value, value, 0) == 0)
- return true;
-
- return false;
-}
-
-static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) {
- const char *sysattr;
- const char *value;
-
- assert(enumerator);
- assert(device);
-
- HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr)
- if (match_sysattr_value(device, sysattr, value))
- return false;
-
- HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr)
- if (!match_sysattr_value(device, sysattr, value))
- return false;
-
- return true;
-}
-
static bool match_property(sd_device_enumerator *enumerator, sd_device *device) {
const char *property;
const char *value;
return true;
}
-static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) {
- const char *syspath_parent, *syspath;
-
- assert(enumerator);
- assert(device);
-
- if (set_isempty(enumerator->match_parent))
- return true;
-
- assert_se(sd_device_get_syspath(device, &syspath) >= 0);
-
- SET_FOREACH(syspath_parent, enumerator->match_parent)
- if (path_startswith(syspath, syspath_parent))
- return true;
-
- return false;
-}
-
static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
const char *sysname_match;
sd_device_get_ifindex(device, NULL) >= 0))
continue;
- if (!match_parent(enumerator, device))
+ if (!device_match_parent(device, enumerator->match_parent, NULL))
continue;
if (!match_tag(enumerator, device))
if (!match_property(enumerator, device))
continue;
- if (!match_sysattr(enumerator, device))
+ if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
continue;
k = device_enumerator_add_device(enumerator, device);
if (!match_sysname(enumerator, sysname))
continue;
- if (!match_parent(enumerator, device))
+ if (!device_match_parent(device, enumerator->match_parent, NULL))
continue;
if (!match_property(enumerator, device))
continue;
- if (!match_sysattr(enumerator, device))
+ if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
continue;
k = device_enumerator_add_device(enumerator, device);
if (!match_property(enumerator, device))
return 0;
- if (!match_sysattr(enumerator, device))
+ if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
return 0;
r = device_enumerator_add_device(enumerator, device);
Hashmap *subsystem_filter;
Set *tag_filter;
+ Hashmap *match_sysattr_filter;
+ Hashmap *nomatch_sysattr_filter;
+ Set *match_parent_filter;
+ Set *nomatch_parent_filter;
bool filter_uptodate;
sd_event *event;
hashmap_free(m->subsystem_filter);
set_free(m->tag_filter);
+ hashmap_free(m->match_sysattr_filter);
+ hashmap_free(m->nomatch_sysattr_filter);
+ set_free(m->match_parent_filter);
+ set_free(m->nomatch_parent_filter);
return mfree(m);
}
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_monitor, sd_device_monitor, device_monitor_free);
-static int passes_filter(sd_device_monitor *m, sd_device *device) {
- const char *tag, *subsystem, *devtype, *s, *d = NULL;
+static int check_subsystem_filter(sd_device_monitor *m, sd_device *device) {
+ const char *s, *subsystem, *d, *devtype = NULL;
int r;
assert(m);
assert(device);
if (hashmap_isempty(m->subsystem_filter))
- goto tag;
+ return true;
- r = sd_device_get_subsystem(device, &s);
+ r = sd_device_get_subsystem(device, &subsystem);
if (r < 0)
return r;
- r = sd_device_get_devtype(device, &d);
+ r = sd_device_get_devtype(device, &devtype);
if (r < 0 && r != -ENOENT)
return r;
- HASHMAP_FOREACH_KEY(devtype, subsystem, m->subsystem_filter) {
+ HASHMAP_FOREACH_KEY(d, s, m->subsystem_filter) {
if (!streq(s, subsystem))
continue;
- if (!devtype)
- goto tag;
+ if (!d || streq_ptr(d, devtype))
+ return true;
+ }
- if (!d)
- continue;
+ return false;
+}
- if (streq(d, devtype))
- goto tag;
- }
+static bool check_tag_filter(sd_device_monitor *m, sd_device *device) {
+ const char *tag;
- return 0;
+ assert(m);
+ assert(device);
-tag:
if (set_isempty(m->tag_filter))
- return 1;
+ return true;
SET_FOREACH(tag, m->tag_filter)
if (sd_device_has_tag(device, tag) > 0)
- return 1;
+ return true;
- return 0;
+ return false;
+}
+
+static int passes_filter(sd_device_monitor *m, sd_device *device) {
+ int r;
+
+ assert(m);
+ assert(device);
+
+ r = check_subsystem_filter(m, device);
+ if (r <= 0)
+ return r;
+
+ if (!check_tag_filter(m, device))
+ return false;
+
+ if (!device_match_sysattr(device, m->match_sysattr_filter, m->nomatch_sysattr_filter))
+ return false;
+
+ return device_match_parent(device, m->match_parent_filter, m->nomatch_parent_filter);
}
int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) {
return r;
}
+_public_ int sd_device_monitor_filter_add_match_sysattr(sd_device_monitor *m, const char *sysattr, const char *value, int match) {
+ Hashmap **hashmap;
+
+ assert_return(m, -EINVAL);
+ assert_return(sysattr, -EINVAL);
+
+ if (match)
+ hashmap = &m->match_sysattr_filter;
+ else
+ hashmap = &m->nomatch_sysattr_filter;
+
+ /* TODO: unset m->filter_uptodate on success when we support this filter on BPF. */
+ return hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value);
+}
+
+_public_ int sd_device_monitor_filter_add_match_parent(sd_device_monitor *m, sd_device *device, int match) {
+ const char *syspath;
+ Set **set;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(device, -EINVAL);
+
+ r = sd_device_get_syspath(device, &syspath);
+ if (r < 0)
+ return r;
+
+ if (match)
+ set = &m->match_parent_filter;
+ else
+ set = &m->nomatch_parent_filter;
+
+ /* TODO: unset m->filter_uptodate on success when we support this filter on BPF. */
+ return set_put_strdup(set, syspath);
+}
+
_public_ int sd_device_monitor_filter_remove(sd_device_monitor *m) {
static const struct sock_fprog filter = { 0, NULL };
m->subsystem_filter = hashmap_free(m->subsystem_filter);
m->tag_filter = set_free(m->tag_filter);
+ m->match_sysattr_filter = hashmap_free(m->match_sysattr_filter);
+ m->nomatch_sysattr_filter = hashmap_free(m->nomatch_sysattr_filter);
+ m->match_parent_filter = set_free(m->match_parent_filter);
+ m->nomatch_parent_filter = set_free(m->nomatch_parent_filter);
if (setsockopt(m->sock, SOL_SOCKET, SO_DETACH_FILTER, &filter, sizeof(filter)) < 0)
return -errno;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <fnmatch.h>
+
+#include "device-util.h"
+#include "path-util.h"
+
+static bool device_match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) {
+ const char *value;
+
+ assert(device);
+ assert(sysattr);
+
+ if (sd_device_get_sysattr_value(device, sysattr, &value) < 0)
+ return false;
+
+ if (!match_value)
+ return true;
+
+ if (fnmatch(match_value, value, 0) == 0)
+ return true;
+
+ return false;
+}
+
+bool device_match_sysattr(sd_device *device, Hashmap *match_sysattr, Hashmap *nomatch_sysattr) {
+ const char *sysattr;
+ const char *value;
+
+ assert(device);
+
+ HASHMAP_FOREACH_KEY(value, sysattr, match_sysattr)
+ if (!device_match_sysattr_value(device, sysattr, value))
+ return false;
+
+ HASHMAP_FOREACH_KEY(value, sysattr, nomatch_sysattr)
+ if (device_match_sysattr_value(device, sysattr, value))
+ return false;
+
+ return true;
+}
+
+bool device_match_parent(sd_device *device, Set *match_parent, Set *nomatch_parent) {
+ const char *syspath_parent, *syspath;
+
+ assert(device);
+
+ if (sd_device_get_syspath(device, &syspath) < 0)
+ return false;
+
+ SET_FOREACH(syspath_parent, nomatch_parent)
+ if (path_startswith(syspath, syspath_parent))
+ return false;
+
+ if (set_isempty(match_parent))
+ return true;
+
+ SET_FOREACH(syspath_parent, match_parent)
+ if (path_startswith(syspath, syspath_parent))
+ return true;
+
+ return false;
+}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <stdbool.h>
+
+#include "sd-device.h"
+
+#include "hashmap.h"
+#include "log.h"
+#include "macro.h"
+#include "set.h"
+
#define FOREACH_DEVICE_PROPERTY(device, key, value) \
for (key = sd_device_get_property_first(device, &(value)); \
key; \
#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__)
#define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__)
#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__)
+
+bool device_match_sysattr(sd_device *device, Hashmap *match_sysattr, Hashmap *nomatch_sysattr);
+bool device_match_parent(sd_device *device, Set *match_parent, Set *nomatch_parent);
const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
char *path;
size_t uevent_len;
- unsigned i;
int r;
enum {
device->uevent_loaded = true;
- for (i = 0; i < uevent_len; i++)
+ for (size_t i = 0; i < uevent_len; i++)
switch (state) {
case PRE_KEY:
if (!strchr(NEWLINE, uevent[i])) {
pos = strrchr(subdir, '/');
if (!pos || pos < subdir + 2)
- break;
+ return -ENODEV;
*pos = '\0';
return 0;
}
-
- return -ENODEV;
}
_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
int device_read_db_internal_filename(sd_device *device, const char *filename) {
_cleanup_free_ char *db = NULL;
const char *value;
- size_t db_len, i;
+ size_t db_len;
char key;
int r;
device->db_loaded = true;
- for (i = 0; i < db_len; i++) {
+ for (size_t i = 0; i < db_len; i++) {
switch (state) {
case PRE_KEY:
if (!strchr(NEWLINE, db[i])) {
db[i] = '\0';
r = handle_db_line(device, key, value);
if (r < 0)
- log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m", key, value);
+ log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m",
+ key, value);
state = PRE_KEY;
}
return r;
path = prefix_roota(syspath, sysattr);
- r = lstat(path, &statbuf);
- if (r < 0) {
+ if (lstat(path, &statbuf) < 0) {
int k;
+ r = -errno;
+
/* remember that we could not access the sysattr */
k = device_cache_sysattr_value(device, sysattr, NULL);
if (k < 0)
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "device-util.h"
+#include "tests.h"
+
+static void test_log_device_full(void) {
+ int r;
+
+ log_info("/* %s */", __func__);
+
+ for (int level = LOG_ERR; level <= LOG_DEBUG; level++) {
+ log_device_full(NULL, level, "test level=%d: %m", level);
+
+ r = log_device_full_errno(NULL, level, EUCLEAN, "test level=%d errno=EUCLEAN: %m", level);
+ assert_se(r == -EUCLEAN);
+
+ r = log_device_full_errno(NULL, level, 0, "test level=%d errno=0: %m", level);
+ assert_se(r == 0);
+
+ r = log_device_full_errno(NULL, level, SYNTHETIC_ERRNO(ENODATA), "test level=%d errno=S(ENODATA): %m", level);
+ assert_se(r == -ENODATA);
+ }
+}
+
+int main(int argc, char **argv) {
+ test_setup_logging(LOG_INFO);
+
+ test_log_device_full();
+ return 0;
+}
static void test_subsystem_filter(sd_device *device) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- const char *syspath, *subsystem, *p, *s;
+ const char *syspath, *subsystem;
sd_device *d;
- log_info("/* %s */", __func__);
+ log_device_info(device, "/* %s */", __func__);
assert_se(sd_device_get_syspath(device, &syspath) >= 0);
assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
assert_se(sd_device_enumerator_new(&e) >= 0);
assert_se(sd_device_enumerator_add_match_subsystem(e, subsystem, false) >= 0);
FOREACH_DEVICE(e, d) {
+ const char *p, *s;
+
assert_se(sd_device_get_syspath(d, &p) >= 0);
assert_se(sd_device_get_subsystem(d, &s) >= 0);
- log_info("Sending device subsystem:%s syspath:%s", s, p);
+ log_device_debug(d, "Sending device subsystem:%s syspath:%s", s, p);
+ assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
+ }
+
+ log_device_info(device, "Sending device subsystem:%s syspath:%s", subsystem, syspath);
+ assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0);
+ assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100);
+}
+
+static void test_tag_filter(sd_device *device) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ const char *syspath;
+ sd_device *d;
+
+ log_device_info(device, "/* %s */", __func__);
+
+ assert_se(sd_device_get_syspath(device, &syspath) >= 0);
+
+ assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
+
+ assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
+ assert_se(sd_device_monitor_filter_add_match_tag(monitor_client, "TEST_SD_DEVICE_MONITOR") >= 0);
+ assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ FOREACH_DEVICE(e, d) {
+ const char *p;
+
+ assert_se(sd_device_get_syspath(d, &p) >= 0);
+
+ log_device_debug(d, "Sending device syspath:%s", p);
assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
}
- log_info("Sending device subsystem:%s syspath:%s", subsystem, syspath);
+ log_device_info(device, "Sending device syspath:%s", syspath);
assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0);
assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100);
+
+}
+
+static void test_sysattr_filter(sd_device *device, const char *sysattr) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ const char *syspath, *subsystem, *sysattr_value;
+ sd_device *d;
+
+ log_device_info(device, "/* %s(%s) */", __func__, sysattr);
+
+ assert_se(sd_device_get_syspath(device, &syspath) >= 0);
+ assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
+ assert_se(sd_device_get_sysattr_value(device, sysattr, &sysattr_value) >= 0);
+
+ assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
+
+ assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
+ /* The sysattr filter is not implemented in BPF yet, so the below device_monito_send_device()
+ * may cause EAGAIN. So, let's also filter devices with subsystem. */
+ assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0);
+ assert_se(sd_device_monitor_filter_add_match_sysattr(monitor_client, sysattr, sysattr_value, true) >= 0);
+ assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_add_match_sysattr(e, sysattr, sysattr_value, false) >= 0);
+ FOREACH_DEVICE(e, d) {
+ const char *p;
+
+ assert_se(sd_device_get_syspath(d, &p) >= 0);
+
+ log_device_debug(d, "Sending device syspath:%s", p);
+ assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
+ }
+
+ log_device_info(device, "Sending device syspath:%s", syspath);
+ assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0);
+ assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100);
+
+}
+
+static void test_parent_filter(sd_device *device) {
+ _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ const char *syspath, *subsystem;
+ sd_device *parent, *d;
+ int r;
+
+ log_device_info(device, "/* %s */", __func__);
+
+ assert_se(sd_device_get_syspath(device, &syspath) >= 0);
+ assert_se(sd_device_get_subsystem(device, &subsystem) >= 0);
+ r = sd_device_get_parent(device, &parent);
+ if (r < 0)
+ return (void) log_device_info(device, "Device does not have parent, skipping.");
+
+ assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_server), "sender") >= 0);
+
+ assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0);
+ assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0);
+ /* The parent filter is not implemented in BPF yet, so the below device_monito_send_device()
+ * may cause EAGAIN. So, let's also filter devices with subsystem. */
+ assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0);
+ assert_se(sd_device_monitor_filter_add_match_parent(monitor_client, parent, true) >= 0);
+ assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0);
+ assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0);
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ FOREACH_DEVICE(e, d) {
+ const char *p;
+
+ assert_se(sd_device_get_syspath(d, &p) >= 0);
+
+ log_device_debug(d, "Sending device syspath:%s", p);
+ assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0);
+ }
+
+ log_device_info(device, "Sending device syspath:%s", syspath);
+ assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0);
+ assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100);
+
}
static void test_sd_device_monitor_filter_remove(sd_device *device) {
assert_se(sd_device_new_from_syspath(&loopback, "/sys/class/net/lo") >= 0);
assert_se(device_add_property(loopback, "ACTION", "add") >= 0);
assert_se(device_add_property(loopback, "SEQNUM", "10") >= 0);
+ assert_se(device_add_tag(loopback, "TEST_SD_DEVICE_MONITOR", true) >= 0);
test_send_receive_one(loopback, false, false, false);
test_send_receive_one(loopback, true, false, false);
test_send_receive_one(loopback, true, true, true);
test_subsystem_filter(loopback);
+ test_tag_filter(loopback);
+ test_sysattr_filter(loopback, "ifindex");
test_sd_device_monitor_filter_remove(loopback);
test_device_copy_properties(loopback);
test_send_receive_one(sda, false, true, true);
test_send_receive_one(sda, true, true, true);
+ test_parent_filter(sda);
+
return 0;
}
zero(s->child.siginfo);
if (waitid(P_PID, s->child.pid, &s->child.siginfo,
WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options) < 0)
- return -errno;
+ return negative_errno();
if (s->child.siginfo.si_pid != 0) {
bool zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
int sd_genl_socket_open(sd_netlink **ret) {
return netlink_open_family(ret, NETLINK_GENERIC);
}
-static int lookup_id(sd_netlink *nl, sd_genl_family_t family, uint16_t *id);
static int genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
return 0;
}
-int sd_genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint8_t cmd, sd_netlink_message **ret) {
- uint16_t id;
- int r;
-
- r = lookup_id(nl, family, &id);
- if (r < 0)
- return r;
-
- return genl_message_new(nl, family, id, cmd, ret);
-}
-
static int lookup_id(sd_netlink *nl, sd_genl_family_t family, uint16_t *id) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
uint16_t u;
return 0;
}
+int sd_genl_message_new(sd_netlink *nl, sd_genl_family_t family, uint8_t cmd, sd_netlink_message **ret) {
+ uint16_t id = 0; /* Unnecessary initialization to appease gcc */
+ int r;
+
+ r = lookup_id(nl, family, &id);
+ if (r < 0)
+ return r;
+
+ return genl_message_new(nl, family, id, cmd, ret);
+}
+
int nlmsg_type_to_genl_family(const sd_netlink *nl, uint16_t type, sd_genl_family_t *ret) {
void *p;
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) {
+ int r;
+
assert_return(udev_monitor, -EINVAL);
- return sd_device_monitor_filter_add_match_subsystem_devtype(udev_monitor->monitor, subsystem, devtype);
+ r = sd_device_monitor_filter_add_match_subsystem_devtype(udev_monitor->monitor, subsystem, devtype);
+ return r < 0 ? r : 0;
}
/**
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) {
+ int r;
+
assert_return(udev_monitor, -EINVAL);
- return sd_device_monitor_filter_add_match_tag(udev_monitor->monitor, tag);
+ r = sd_device_monitor_filter_add_match_tag(udev_monitor->monitor, tag);
+ return r < 0 ? r : 0;
}
/**
r = locale_read_data(c, m);
if (r < 0) {
log_error_errno(r, "Failed to read locale data: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to read locale data");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read locale data");
}
/* Merge with the current settings */
r = x11_read_data(c, m);
if (r < 0) {
log_error_errno(r, "Failed to read x11 keyboard layout data: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to read x11 keyboard layout data");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Failed to read x11 keyboard layout data");
}
if (streq_ptr(layout, c->x11_layout) &&
(model && !string_is_safe(model)) ||
(variant && !string_is_safe(variant)) ||
(options && !string_is_safe(options)))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keyboard data");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Received invalid keyboard data");
r = verify_xkb_rmlvo(model, layout, variant, options);
if (r < 0) {
strempty(model), strempty(layout), strempty(variant), strempty(options));
if (r == -EOPNOTSUPP)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system.");
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid.");
}
return r;
if (!uid_is_valid(uid))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
if (leader < 0 || leader == 1 || leader == getpid_cached())
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
if (isempty(type))
t = _SESSION_TYPE_INVALID;
vtnr < m->seat0->position_count &&
m->seat0->positions[vtnr] &&
m->seat0->positions[vtnr]->class != SESSION_GREETER)
- return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session");
+ return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session");
if (hashmap_size(m->sessions) >= m->sessions_max)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED,
if (r < 0)
return r;
if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
- if (!streq(unit_name, SPECIAL_REBOOT_TARGET) && (flags & SD_LOGIND_KEXEC_REBOOT))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
+ if (!streq(unit_name, SPECIAL_REBOOT_TARGET) && (flags & SD_LOGIND_REBOOT_VIA_KEXEC))
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec is only applicable with reboot operations");
} else {
/* Old style method: no flags parameter, but interactive bool passed as boolean in
* payload. Let's convert this argument to the new-style flags parameter for our internal
flags = interactive ? SD_LOGIND_INTERACTIVE : 0;
}
- if ((flags & SD_LOGIND_KEXEC_REBOOT) && kexec_loaded())
+ if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
unit_name = SPECIAL_KEXEC_TARGET;
/* Don't allow multiple jobs being executed at the same time */
target = SPECIAL_POWEROFF_TARGET;
else if (streq(m->scheduled_shutdown_type, "reboot"))
target = SPECIAL_REBOOT_TARGET;
+ else if (streq(m->scheduled_shutdown_type, "kexec"))
+ target = SPECIAL_KEXEC_TARGET;
else if (streq(m->scheduled_shutdown_type, "halt"))
target = SPECIAL_HALT_TARGET;
else
action = "org.freedesktop.login1.power-off";
action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions";
action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit";
- } else if (streq(type, "reboot")) {
+ } else if (STR_IN_SET(type, "reboot", "kexec")) {
action = "org.freedesktop.login1.reboot";
action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions";
action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit";
action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions";
action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit";
} else
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, action, action_multiple_sessions,
action_ignore_inhibit, 0, error);
r = efi_reboot_to_firmware_supported();
if (r == -EOPNOTSUPP)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
if (r < 0)
return r;
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
} else
/* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to on */
use_efi = false;
if (r < 0)
log_warning_errno(r, "Failed to determine whether reboot to boot loader menu is supported: %m");
if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
use_efi = true;
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
} else
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to on */
use_efi = false;
if (r < 0)
log_warning_errno(r, "Failed to determine whether reboot into boot loader entry is supported: %m");
if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_ENTRY_ONESHOT))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
use_efi = true;
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m");
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
} else
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to on */
use_efi = false;
return r;
if (to <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid virtual terminal");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid virtual terminal");
r = check_polkit_chvt(message, s->manager, error);
if (r < 0)
return r;
if (uid != 0 && uid != s->user->user_record->uid)
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set idle hint");
r = session_set_idle_hint(s, b);
if (r == -ENOTTY)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical sessions.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical sessions.");
if (r < 0)
return r;
return r;
if (uid != 0 && uid != s->user->user_record->uid)
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint");
session_set_locked_hint(s, b);
return r;
if (uid != 0 && (force || uid != s->user->user_record->uid))
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
r = session_set_controller(s, sd_bus_message_get_sender(message), force, true);
if (r < 0)
assert(s);
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
session_drop_controller(s);
"Invalid session type '%s'", t);
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You must be in control of this session to set type");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You must be in control of this session to set type");
session_set_type(s, type);
return r;
if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
dev = makedev(major, minor);
sd = hashmap_get(s->devices, &dev);
* The caller should use dup() if it requires more
* than one fd (it would be functionally
* equivalent). */
- return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken");
+ return sd_bus_error_set(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken");
r = session_device_new(s, dev, true, &sd);
if (r < 0)
return r;
if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
dev = makedev(major, minor);
sd = hashmap_get(s->devices, &dev);
if (!sd)
- return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
+ return sd_bus_error_set(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
session_device_free(sd);
session_save(s);
return r;
if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
- return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
dev = makedev(major, minor);
sd = hashmap_get(s->devices, &dev);
if (!sd)
- return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
+ return sd_bus_error_set(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
session_device_complete_pause(sd);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not a valid device name %s, refusing.", name);
if (!s->seat)
- return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Your session has no seat, refusing.");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_YOUR_DEVICE, "Your session has no seat, refusing.");
if (s->seat->active != s)
- return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Session is not in foreground, refusing.");
+ return sd_bus_error_set(error, BUS_ERROR_NOT_YOUR_DEVICE, "Session is not in foreground, refusing.");
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
return r;
if (uid != 0 && uid != s->user->user_record->uid)
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change brightness.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change brightness.");
r = sd_device_new_from_subsystem_sysname(&d, subsystem, name);
if (r < 0)
assert(image);
if (m->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = bus_verify_polkit_async(
message,
assert(m);
if (m->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = sd_bus_message_read(message, "sb", &new_name, &read_only);
if (r < 0)
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_verify_polkit_async(
message,
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
break;
}
default:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r == EXIT_NOT_FOUND)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information");
if (r != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
break;
}
default:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
}
return bus_reply_pair_array(message, l);
if (r < 0)
return r;
if (!strv_env_is_valid(env))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
const char *details[] = {
"machine", m->name,
assert(m);
if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines.");
r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_file_or_directory);
if (r < 0)
return r;
if (!path_is_absolute(src) || !path_is_normalized(src))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and normalized.");
if (isempty(dest))
dest = src;
else if (!path_is_absolute(dest) || !path_is_normalized(dest))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized.");
r = bus_verify_polkit_async(
message,
if (r < 0)
return r;
if (uid != 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied.");
propagate_directory = strjoina("/run/systemd/nspawn/propagate/", m->name);
r = bind_mount_in_namespace(m->leader,
assert(m);
if (m->manager->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines.");
r = sd_bus_message_read(message, "ss", &src, &dest);
if (r < 0)
return r;
if (!path_is_absolute(src))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute.");
if (isempty(dest))
dest = src;
else if (!path_is_absolute(dest))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute.");
r = bus_verify_polkit_async(
message,
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r != EXIT_SUCCESS)
- return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
fd = receive_one_fd(pair[0], MSG_DONTWAIT);
if (fd < 0)
}
default:
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines.");
}
return sd_bus_reply_method_return(message, "h", fd);
return sd_bus_reply_method_return(message, "u", UINT32_C(0));
if (m->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines.");
r = machine_get_uid_shift(m, &shift);
if (r == -ENXIO)
if (r < 0)
return r;
if (!hostname_is_valid(name, 0))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
r = sd_bus_message_read_array(message, 'y', &v, &n);
if (r < 0)
else if (n == 16)
memcpy(&id, v, n);
else
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory);
if (r < 0)
else {
c = machine_class_from_string(class);
if (c < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
}
if (leader == 1)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
if (!isempty(root_directory) && !path_is_absolute(root_directory))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
if (leader == 0) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
assert(message);
if (m->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = sd_bus_message_read(message, "s", &mm);
if (r < 0)
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_verify_polkit_async(
message,
r = btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, limit);
if (r == -ENOTTY)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
if (machine->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
r = machine_translate_uid(machine, uid, &converted);
if (r == -ESRCH)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
if (machine->class != MACHINE_CONTAINER)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
r = machine_translate_gid(machine, gid, &converted);
if (r == -ESRCH)
o->pid = 0;
if (si->si_code != CLD_EXITED) {
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ r = sd_bus_error_set(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
goto fail;
}
if (si->si_status == EXIT_SUCCESS)
r = 0;
else if (read(o->errno_fd, &r, sizeof(r)) != sizeof(r)) { /* Try to acquire error code for failed operation */
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");
+ r = sd_bus_error_set(&error, SD_BUS_ERROR_FAILED, "Child failed.");
goto fail;
}
static unsigned arg_lines = 10;
static void operational_state_to_color(const char *name, const char *state, const char **on, const char **off) {
- assert(on);
- assert(off);
-
if (STRPTR_IN_SET(state, "routable", "enslaved") ||
(streq_ptr(name, "lo") && streq_ptr(state, "carrier"))) {
- *on = ansi_highlight_green();
- *off = ansi_normal();
+ if (on)
+ *on = ansi_highlight_green();
+ if (off)
+ *off = ansi_normal();
} else if (streq_ptr(state, "degraded")) {
- *on = ansi_highlight_yellow();
- *off = ansi_normal();
- } else
- *on = *off = "";
+ if (on)
+ *on = ansi_highlight_yellow();
+ if (off)
+ *off = ansi_normal();
+ } else {
+ if (on)
+ *on = "";
+ if (off)
+ *off = "";
+ }
}
static void setup_state_to_color(const char *state, const char **on, const char **off) {
- assert(on);
- assert(off);
-
if (streq_ptr(state, "configured")) {
- *on = ansi_highlight_green();
- *off = ansi_normal();
+ if (on)
+ *on = ansi_highlight_green();
+ if (off)
+ *off = ansi_normal();
} else if (streq_ptr(state, "configuring")) {
- *on = ansi_highlight_yellow();
- *off = ansi_normal();
+ if (on)
+ *on = ansi_highlight_yellow();
+ if (off)
+ *off = ansi_normal();
} else if (STRPTR_IN_SET(state, "failed", "linger")) {
- *on = ansi_highlight_red();
- *off = ansi_normal();
- } else
- *on = *off = "";
+ if (on)
+ *on = ansi_highlight_red();
+ if (off)
+ *off = ansi_normal();
+ } else {
+ if (on)
+ *on = "";
+ if (off)
+ *off = "";
+ }
}
typedef struct VxLanInfo {
for (int i = 0; i < c; i++) {
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL;
- const char *on_color_operational, *off_color_operational,
- *on_color_setup, *off_color_setup;
+ const char *on_color_operational, *on_color_setup;
_cleanup_free_ char *t = NULL;
(void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state);
- operational_state_to_color(links[i].name, operational_state, &on_color_operational, &off_color_operational);
+ operational_state_to_color(links[i].name, operational_state, &on_color_operational, NULL);
r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state);
if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */
setup_state = strdup("unmanaged");
- setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
+ setup_state_to_color(setup_state, &on_color_setup, NULL);
t = link_get_type_string(links[i].sd_device, links[i].iftype);
static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
_cleanup_free_ char *operational_state = NULL;
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
- const char *on_color_operational, *off_color_operational;
+ const char *on_color_operational;
_cleanup_(table_unrefp) Table *table = NULL;
TableCell *cell;
int r;
assert(rtnl);
(void) sd_network_get_operational_state(&operational_state);
- operational_state_to_color(NULL, operational_state, &on_color_operational, &off_color_operational);
+ operational_state_to_color(NULL, operational_state, &on_color_operational, NULL);
table = table_new("dot", "key", "value");
if (!table)
return log_oom();
}
- if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
+ if (sd_bus_is_ready(m->bus) <= 0) {
log_debug("Not connected to system bus, requesting product UUID later.");
return 0;
}
#include "radv-internal.h"
#include "web-util.h"
+bool link_dhcp6_with_address_enabled(Link *link) {
+ if (!link_dhcp6_enabled(link))
+ return false;
+
+ return link->network->dhcp6_use_address;
+}
+
bool link_dhcp6_pd_is_enabled(Link *link) {
assert(link);
DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free);
+bool link_dhcp6_with_address_enabled(Link *link);
bool link_dhcp6_pd_is_enabled(Link *link);
int dhcp6_pd_remove(Link *link);
int dhcp6_configure(Link *link);
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
if (!route_only && dns_name_is_root(name))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
r = dns_name_normalize(name, 0, &str);
if (r < 0)
assert(link->manager);
assert(properties);
- if (!link->manager->bus)
+ if (sd_bus_is_ready(link->manager->bus) <= 0)
return 0;
p = link_bus_path(link);
break;
}
- if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_ipv4ll_enabled(link)) &&
+ if ((link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) || link_ipv4ll_enabled(link)) &&
!link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address &&
!link->ipv4ll_address_configured)
/* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */
return r;
if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) {
- log_link_debug(link, "State is %s, dropping config", link_state_to_string(link->state));
+ log_link_debug(link, "State is %s, dropping foreign config", link_state_to_string(link->state));
r = link_drop_foreign_config(link);
if (r < 0)
return r;
return r;
if (!IN_SET(link->state, LINK_STATE_UNMANAGED, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) {
- log_link_debug(link, "State is %s, dropping config", link_state_to_string(link->state));
+ log_link_debug(link, "State is %s, dropping foreign config", link_state_to_string(link->state));
r = link_drop_foreign_config(link);
if (r < 0)
return r;
assert(manager);
assert(properties);
- if (!manager->bus)
+ if (sd_bus_is_ready(manager->bus) <= 0)
return 0;
return sd_bus_emit_properties_changed_strv(
if (r < 0)
return r;
- if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
- log_debug("Not connected to system bus, setting hostname later.");
+ if (sd_bus_is_ready(m->bus) <= 0) {
+ log_debug("Not connected to system bus, setting system hostname later.");
return 0;
}
"sb",
hostname,
false);
-
if (r < 0)
return log_error_errno(r, "Could not set transient hostname: %m");
if (r < 0)
return r;
- if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
- log_debug("Not connected to system bus, setting timezone later.");
+ if (sd_bus_is_ready(m->bus) <= 0) {
+ log_debug("Not connected to system bus, setting system timezone later.");
return 0;
}
Hashmap *polkit_registry;
int ethtool_fd;
- bool enumerating:1;
- bool dirty:1;
- bool restarting:1;
+ bool enumerating;
+ bool dirty;
+ bool restarting;
bool manage_foreign_routes;
Set *dirty_links;
if (!link_ipv6_accept_ra_enabled(link))
return 0;
- if (!link->ndisc) {
- r = sd_ndisc_new(&link->ndisc);
- if (r < 0)
- return r;
+ if (link->ndisc)
+ return 0; /* Already configured. */
- r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0);
- if (r < 0)
- return r;
- }
+ r = sd_ndisc_new(&link->ndisc);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_attach_event(link->ndisc, link->manager->event, 0);
+ if (r < 0)
+ return r;
r = sd_ndisc_set_mac(link->ndisc, &link->hw_addr.addr.ether);
if (r < 0)
if (r <= 0)
return r;
- if (arg_booted)
- return sd_booted() <= 0;
+ if (arg_booted) {
+ r = sd_booted();
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine whether we are booted with systemd, assuming we aren't: %m");
+ else
+ log_debug("The system %s booted with systemd.", r ? "was" : "was not");
+
+ return r <= 0;
+ }
if (arg_ready)
our_env[i++] = (char*) "READY=1";
return 0;
}
-DEFINE_MAIN_FUNCTION(run);
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE (run);
int *errnop, int *h_errnop,
int32_t *ttlp) {
- _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
- _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
- struct gaih_addrtuple *r_tuple = NULL, *r_tuple_first = NULL;
_cleanup_(varlink_unrefp) Varlink *link = NULL;
- const char *canonical = NULL, *error_id = NULL;
- JsonVariant *entry, *rparams;
- size_t l, ms, idx, c = 0;
- char *r_name;
+ _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
+ _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
+ JsonVariant *rparams, *entry;
int r;
PROTECT_ERRNO;
* DNSSEC errors and suchlike. (We don't use UNAVAIL in this case so that the nsswitch.conf
* configuration can distinguish such executed but negative replies from complete failure to
* talk to resolved). */
+ const char *error_id;
r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
if (r < 0)
goto fail;
if (json_variant_is_blank_object(p.addresses))
goto not_found;
+ size_t n_addresses = 0;
JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
AddressParameters q = {};
goto fail;
}
- c++;
+ n_addresses++;
}
- canonical = p.name ?: name;
+ const char *canonical = p.name ?: name;
+ size_t l = strlen(canonical);
+ size_t idx, ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * n_addresses;
- l = strlen(canonical);
- ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
if (buflen < ms) {
UNPROTECT_ERRNO;
*errnop = ERANGE;
}
/* First, append name */
- r_name = buffer;
- memcpy(r_name, canonical, l+1);
- idx = ALIGN(l+1);
+ char *r_name = buffer;
+ memcpy(r_name, canonical, l + 1);
+ idx = ALIGN(l + 1);
/* Second, append addresses */
- r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
+ struct gaih_addrtuple *r_tuple = NULL,
+ *r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
AddressParameters q = {};
idx += ALIGN(sizeof(struct gaih_addrtuple));
}
- assert(r_tuple);
+ assert(r_tuple); /* We had at least one address, so r_tuple must be set */
r_tuple->next = NULL; /* Override last next pointer */
assert(idx == ms);
int32_t *ttlp,
char **canonp) {
- _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
- _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
- char *r_name, *r_aliases, *r_addr, *r_addr_list;
_cleanup_(varlink_unrefp) Varlink *link = NULL;
- const char *canonical, *error_id = NULL;
- size_t l, idx, ms, alen, i = 0, c = 0;
- JsonVariant *entry, *rparams;
+ _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
+ _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
+ JsonVariant *rparams, *entry;
int r;
PROTECT_ERRNO;
if (r < 0)
goto fail;
+ const char *error_id;
r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
if (r < 0)
goto fail;
if (json_variant_is_blank_object(p.addresses))
goto not_found;
+ size_t n_addresses = 0;
JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
AddressParameters q = {};
goto fail;
}
- c++;
+ n_addresses++;
}
- canonical = p.name ?: name;
+ const char *canonical = p.name ?: name;
- alen = FAMILY_ADDRESS_SIZE(af);
- l = strlen(canonical);
+ size_t alen = FAMILY_ADDRESS_SIZE(af);
+ size_t l = strlen(canonical);
- ms = ALIGN(l+1) + c*ALIGN(alen) + (c+2) * sizeof(char*);
+ size_t idx, ms = ALIGN(l + 1) + n_addresses * ALIGN(alen) + (n_addresses + 2) * sizeof(char*);
if (buflen < ms) {
UNPROTECT_ERRNO;
}
/* First, append name */
- r_name = buffer;
+ char *r_name = buffer;
memcpy(r_name, canonical, l+1);
idx = ALIGN(l+1);
/* Second, create empty aliases array */
- r_aliases = buffer + idx;
+ char *r_aliases = buffer + idx;
((char**) r_aliases)[0] = NULL;
idx += sizeof(char*);
/* Third, append addresses */
- r_addr = buffer + idx;
+ char *r_addr = buffer + idx;
+ size_t i = 0;
JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
AddressParameters q = {};
i++;
}
- assert(i == c);
- idx += c * ALIGN(alen);
+ assert(i == n_addresses);
+ idx += n_addresses * ALIGN(alen);
/* Fourth, append address pointer array */
- r_addr_list = buffer + idx;
- for (i = 0; i < c; i++)
+ char *r_addr_list = buffer + idx;
+ for (i = 0; i < n_addresses; i++)
((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
((char**) r_addr_list)[i] = NULL;
- idx += (c+1) * sizeof(char*);
+ idx += (n_addresses + 1) * sizeof(char*);
assert(idx == ms);
}
static const JsonDispatch name_parameters_dispatch_table[] = {
- { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_ifindex, offsetof(NameParameters, ifindex), 0 },
- { "name", JSON_VARIANT_UNSIGNED, json_dispatch_string, offsetof(NameParameters, name), JSON_MANDATORY },
+ { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_ifindex, offsetof(NameParameters, ifindex), 0 },
+ { "name", JSON_VARIANT_STRING, json_dispatch_string, offsetof(NameParameters, name), JSON_MANDATORY },
{}
};
int *errnop, int *h_errnop,
int32_t *ttlp) {
- _cleanup_(resolve_address_reply_destroy) ResolveAddressReply p = {};
- _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
- char *r_name, *r_aliases, *r_addr, *r_addr_list;
_cleanup_(varlink_unrefp) Varlink *link = NULL;
- JsonVariant *entry, *rparams;
- const char *n, *error_id;
- unsigned c = 0, i = 0;
- size_t ms = 0, idx;
+ _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
+ _cleanup_(resolve_address_reply_destroy) ResolveAddressReply p = {};
+ JsonVariant *rparams, *entry;
int r;
PROTECT_ERRNO;
if (r < 0)
goto fail;
+ const char* error_id;
r = varlink_call(link, "io.systemd.Resolve.ResolveAddress", cparams, &rparams, &error_id, NULL);
if (r < 0)
goto fail;
if (json_variant_is_blank_object(p.names))
goto not_found;
+ size_t ms = 0, idx;
+
JSON_VARIANT_ARRAY_FOREACH(entry, p.names) {
_cleanup_(name_parameters_destroy) NameParameters q = {};
ms += ALIGN(strlen(q.name) + 1);
}
- ms += ALIGN(len) + /* the address */
- 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
- json_variant_elements(p.names) * sizeof(char*); /* pointers to aliases, plus trailing NULL */
+ size_t n_names = json_variant_elements(p.names);
+ ms += ALIGN(len) + /* the address */
+ 2 * sizeof(char*) + /* pointer to the address, plus trailing NULL */
+ n_names * sizeof(char*); /* pointers to aliases, plus trailing NULL */
if (buflen < ms) {
UNPROTECT_ERRNO;
}
/* First, place address */
- r_addr = buffer;
+ char *r_addr = buffer;
memcpy(r_addr, addr, len);
idx = ALIGN(len);
/* Second, place address list */
- r_addr_list = buffer + idx;
+ char *r_addr_list = buffer + idx;
((char**) r_addr_list)[0] = r_addr;
((char**) r_addr_list)[1] = NULL;
idx += sizeof(char*) * 2;
- /* Third, reserve space for the aliases array */
- r_aliases = buffer + idx;
- idx += sizeof(char*) * c;
+ /* Third, reserve space for the aliases array, plus trailing NULL */
+ char *r_aliases = buffer + idx;
+ idx += sizeof(char*) * n_names;
/* Fourth, place aliases */
- i = 0;
- r_name = buffer + idx;
+ char *r_name = buffer + idx;
+
+ size_t i = 0;
JSON_VARIANT_ARRAY_FOREACH(entry, p.names) {
_cleanup_(name_parameters_destroy) NameParameters q = {};
- size_t l;
- char *z;
r = json_dispatch(entry, name_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
if (r < 0)
goto fail;
- l = strlen(q.name);
- z = buffer + idx;
- memcpy(z, n, l+1);
+ size_t l = strlen(q.name);
+ char *z = buffer + idx;
+ memcpy(z, q.name, l + 1);
if (i > 0)
- ((char**) r_aliases)[i-1] = z;
+ ((char**) r_aliases)[i - 1] = z;
i++;
- idx += ALIGN(l+1);
+ idx += ALIGN(l + 1);
}
+ ((char**) r_aliases)[n_names - 1] = NULL;
- ((char**) r_aliases)[c-1] = NULL;
assert(idx == ms);
result->h_name = r_name;
return 0;
}
-static int monitor_cgroup_contexts_handler(sd_event_source *s, uint64_t usec, void *userdata) {
- _cleanup_set_free_ Set *targets = NULL;
+static int monitor_swap_contexts_handler(sd_event_source *s, uint64_t usec, void *userdata) {
Manager *m = userdata;
usec_t usec_now;
int r;
if (r < 0)
return log_error_errno(r, "Failed to reset event timer: %m");
- r = sd_event_source_set_time_relative(s, INTERVAL_USEC);
+ r = sd_event_source_set_time_relative(s, SWAP_INTERVAL_USEC);
if (r < 0)
return log_error_errno(r, "Failed to set relative time for timer: %m");
return log_error_errno(r, "Failed to acquire varlink connection: %m");
}
- /* Update the cgroups used for detection/action */
- r = update_monitored_cgroup_contexts(&m->monitored_swap_cgroup_contexts);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_debug_errno(r, "Failed to update monitored swap cgroup contexts, ignoring: %m");
-
- r = update_monitored_cgroup_contexts(&m->monitored_mem_pressure_cgroup_contexts);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_debug_errno(r, "Failed to update monitored memory pressure cgroup contexts, ignoring: %m");
-
- r = update_monitored_cgroup_contexts_candidates(
- m->monitored_mem_pressure_cgroup_contexts, &m->monitored_mem_pressure_cgroup_contexts_candidates);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_debug_errno(r, "Failed to update monitored memory pressure candidate cgroup contexts, ignoring: %m");
-
+ /* We still try to acquire swap information for oomctl even if no units want swap monitoring */
r = oomd_system_context_acquire("/proc/swaps", &m->system_context);
- /* If there aren't units depending on swap actions, the only error we exit on is ENOMEM.
+ /* If there are no units depending on swap actions, the only error we exit on is ENOMEM.
* Allow ENOENT in the event that swap is disabled on the system. */
- if (r == -ENOMEM || (r < 0 && r != -ENOENT && !hashmap_isempty(m->monitored_swap_cgroup_contexts)))
- return log_error_errno(r, "Failed to acquire system context: %m");
- else if (r == -ENOENT)
+ if (r == -ENOENT) {
zero(m->system_context);
+ return 0;
+ } else if (r == -ENOMEM || (r < 0 && !hashmap_isempty(m->monitored_swap_cgroup_contexts)))
+ return log_error_errno(r, "Failed to acquire system context: %m");
- if (oomd_memory_reclaim(m->monitored_mem_pressure_cgroup_contexts))
- m->last_reclaim_at = usec_now;
+ /* Return early if nothing is requesting swap monitoring */
+ if (hashmap_isempty(m->monitored_swap_cgroup_contexts))
+ return 0;
- /* If we're still recovering from a kill, don't try to kill again yet */
- if (m->post_action_delay_start > 0) {
- if (m->post_action_delay_start + POST_ACTION_DELAY_USEC > usec_now)
- return 0;
- else
- m->post_action_delay_start = 0;
- }
-
- r = oomd_pressure_above(m->monitored_mem_pressure_cgroup_contexts, m->default_mem_pressure_duration_usec, &targets);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_debug_errno(r, "Failed to check if memory pressure exceeded limits, ignoring: %m");
- else if (r == 1) {
- /* Check if there was reclaim activity in the given interval. The concern is the following case:
- * Pressure climbed, a lot of high-frequency pages were reclaimed, and we killed the offending
- * cgroup. Even after this, well-behaved processes will fault in recently resident pages and
- * this will cause pressure to remain high. Thus if there isn't any reclaim pressure, no need
- * to kill something (it won't help anyways). */
- if ((usec_now - m->last_reclaim_at) <= RECLAIM_DURATION_USEC) {
- OomdCGroupContext *t;
-
- SET_FOREACH(t, targets) {
- _cleanup_free_ char *selected = NULL;
- char ts[FORMAT_TIMESPAN_MAX];
-
- log_debug("Memory pressure for %s is %lu.%02lu%% > %lu.%02lu%% for > %s with reclaim activity",
- t->path,
- LOAD_INT(t->memory_pressure.avg10), LOAD_FRAC(t->memory_pressure.avg10),
- LOAD_INT(t->mem_pressure_limit), LOAD_FRAC(t->mem_pressure_limit),
- format_timespan(ts, sizeof ts,
- m->default_mem_pressure_duration_usec,
- USEC_PER_SEC));
-
- r = oomd_kill_by_pgscan_rate(m->monitored_mem_pressure_cgroup_contexts_candidates, t->path, m->dry_run, &selected);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0)
- log_notice_errno(r, "Failed to kill any cgroup(s) under %s based on pressure: %m", t->path);
- else {
- /* Don't act on all the high pressure cgroups at once; return as soon as we kill one */
- m->post_action_delay_start = usec_now;
- if (selected)
- log_notice("Killed %s due to memory pressure for %s being %lu.%02lu%% > %lu.%02lu%%"
- " for > %s with reclaim activity",
- selected, t->path,
- LOAD_INT(t->memory_pressure.avg10), LOAD_FRAC(t->memory_pressure.avg10),
- LOAD_INT(t->mem_pressure_limit), LOAD_FRAC(t->mem_pressure_limit),
- format_timespan(ts, sizeof ts,
- m->default_mem_pressure_duration_usec,
- USEC_PER_SEC));
- return 0;
- }
- }
- }
- }
+ /* Note that m->monitored_swap_cgroup_contexts does not need to be updated every interval because only the
+ * system context is used for deciding whether the swap threshold is hit. m->monitored_swap_cgroup_contexts
+ * is only used to decide which cgroups to kill (and even then only the resource usages of its descendent
+ * nodes are the ones that matter). */
if (oomd_swap_free_below(&m->system_context, 10000 - m->swap_used_limit_permyriad)) {
_cleanup_hashmap_free_ Hashmap *candidates = NULL;
_cleanup_free_ char *selected = NULL;
+ uint64_t threshold;
log_debug("Swap used (%"PRIu64") / total (%"PRIu64") is more than " PERMYRIAD_AS_PERCENT_FORMAT_STR,
m->system_context.swap_used, m->system_context.swap_total,
if (r < 0)
log_debug_errno(r, "Failed to get monitored swap cgroup candidates, ignoring: %m");
- r = oomd_kill_by_swap_usage(candidates, m->dry_run, &selected);
+ threshold = m->system_context.swap_total * THRESHOLD_SWAP_USED_PERCENT / 100;
+ r = oomd_kill_by_swap_usage(candidates, threshold, m->dry_run, &selected);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
log_notice_errno(r, "Failed to kill any cgroup(s) based on swap: %m");
else {
- m->post_action_delay_start = usec_now;
if (selected)
log_notice("Killed %s due to swap used (%"PRIu64") / total (%"PRIu64") being more than "
PERMYRIAD_AS_PERCENT_FORMAT_STR,
return 0;
}
-static int monitor_cgroup_contexts(Manager *m) {
+static void clear_candidate_hashmapp(Manager **m) {
+ if (*m)
+ hashmap_clear((*m)->monitored_mem_pressure_cgroup_contexts_candidates);
+}
+
+static int monitor_memory_pressure_contexts_handler(sd_event_source *s, uint64_t usec, void *userdata) {
+ /* Don't want to use stale candidate data. Setting this will clear the candidate hashmap on return unless we
+ * update the candidate data (in which case clear_candidates will be NULL). */
+ _cleanup_(clear_candidate_hashmapp) Manager *clear_candidates = userdata;
+ _cleanup_set_free_ Set *targets = NULL;
+ bool in_post_action_delay = false;
+ Manager *m = userdata;
+ usec_t usec_now;
+ int r;
+
+ assert(s);
+ assert(userdata);
+
+ /* Reset timer */
+ r = sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &usec_now);
+ if (r < 0)
+ return log_error_errno(r, "Failed to reset event timer: %m");
+
+ r = sd_event_source_set_time_relative(s, MEM_PRESSURE_INTERVAL_USEC);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set relative time for timer: %m");
+
+ /* Reconnect if our connection dropped */
+ if (!m->varlink) {
+ r = acquire_managed_oom_connect(m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire varlink connection: %m");
+ }
+
+ /* Return early if nothing is requesting memory pressure monitoring */
+ if (hashmap_isempty(m->monitored_mem_pressure_cgroup_contexts))
+ return 0;
+
+ /* Update the cgroups used for detection/action */
+ r = update_monitored_cgroup_contexts(&m->monitored_mem_pressure_cgroup_contexts);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to update monitored memory pressure cgroup contexts, ignoring: %m");
+
+ r = update_monitored_cgroup_contexts_candidates(
+ m->monitored_mem_pressure_cgroup_contexts, &m->monitored_mem_pressure_cgroup_contexts_candidates);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to update monitored memory pressure candidate cgroup contexts, ignoring: %m");
+
+ /* Since pressure counters are lagging, we need to wait a bit after a kill to ensure we don't read stale
+ * values and go on a kill storm. */
+ if (m->mem_pressure_post_action_delay_start > 0) {
+ if (m->mem_pressure_post_action_delay_start + POST_ACTION_DELAY_USEC > usec_now)
+ in_post_action_delay = true;
+ else
+ 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);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to check if memory pressure exceeded limits, ignoring: %m");
+ else if (r == 1 && !in_post_action_delay) {
+ OomdCGroupContext *t;
+ SET_FOREACH(t, targets) {
+ _cleanup_free_ char *selected = NULL;
+ char ts[FORMAT_TIMESPAN_MAX];
+
+ /* Check if there was reclaim activity in the given interval. The concern is the following case:
+ * Pressure climbed, a lot of high-frequency pages were reclaimed, and we killed the offending
+ * cgroup. Even after this, well-behaved processes will fault in recently resident pages and
+ * this will cause pressure to remain high. Thus if there isn't any reclaim pressure, no need
+ * to kill something (it won't help anyways). */
+ if ((now(CLOCK_MONOTONIC) - t->last_had_mem_reclaim) > RECLAIM_DURATION_USEC)
+ continue;
+
+ log_debug("Memory pressure for %s is %lu.%02lu%% > %lu.%02lu%% for > %s with reclaim activity",
+ t->path,
+ LOAD_INT(t->memory_pressure.avg10), LOAD_FRAC(t->memory_pressure.avg10),
+ LOAD_INT(t->mem_pressure_limit), LOAD_FRAC(t->mem_pressure_limit),
+ format_timespan(ts, sizeof ts,
+ m->default_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);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to update monitored memory pressure candidate cgroup contexts, ignoring: %m");
+ else
+ clear_candidates = NULL;
+
+ r = oomd_kill_by_pgscan_rate(m->monitored_mem_pressure_cgroup_contexts_candidates, t->path, m->dry_run, &selected);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_notice_errno(r, "Failed to kill any cgroup(s) under %s based on pressure: %m", t->path);
+ else {
+ /* Don't act on all the high pressure cgroups at once; return as soon as we kill one */
+ m->mem_pressure_post_action_delay_start = usec_now;
+ if (selected)
+ log_notice("Killed %s due to memory pressure for %s being %lu.%02lu%% > %lu.%02lu%%"
+ " for > %s with reclaim activity",
+ selected, t->path,
+ LOAD_INT(t->memory_pressure.avg10), LOAD_FRAC(t->memory_pressure.avg10),
+ LOAD_INT(t->mem_pressure_limit), LOAD_FRAC(t->mem_pressure_limit),
+ format_timespan(ts, sizeof ts,
+ m->default_mem_pressure_duration_usec,
+ USEC_PER_SEC));
+ return 0;
+ }
+ }
+ } else {
+ /* If any monitored cgroup is over their pressure limit, get all the kill candidates for every
+ * monitored cgroup. This saves CPU cycles from doing it every interval by only doing it when a kill
+ * might happen.
+ * Candidate cgroup data will continue to get updated during the post-action delay period in case
+ * pressure continues to be high after a kill. */
+ OomdCGroupContext *c;
+ HASHMAP_FOREACH(c, m->monitored_mem_pressure_cgroup_contexts) {
+ if (c->mem_pressure_limit_hit_start == 0)
+ continue;
+
+ r = update_monitored_cgroup_contexts_candidates(
+ m->monitored_mem_pressure_cgroup_contexts, &m->monitored_mem_pressure_cgroup_contexts_candidates);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ log_debug_errno(r, "Failed to update monitored memory pressure candidate cgroup contexts, ignoring: %m");
+ else {
+ clear_candidates = NULL;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int monitor_swap_contexts(Manager *m) {
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ int r;
+
+ assert(m);
+ assert(m->event);
+
+ r = sd_event_add_time(m->event, &s, CLOCK_MONOTONIC, 0, 0, monitor_swap_contexts_handler, m);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_exit_on_failure(s, true);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(s, SD_EVENT_ON);
+ if (r < 0)
+ return r;
+
+ (void) sd_event_source_set_description(s, "oomd-swap-timer");
+
+ m->swap_context_event_source = TAKE_PTR(s);
+ return 0;
+}
+
+static int monitor_memory_pressure_contexts(Manager *m) {
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
int r;
assert(m);
assert(m->event);
- r = sd_event_add_time(m->event, &s, CLOCK_MONOTONIC, 0, 0, monitor_cgroup_contexts_handler, m);
+ r = sd_event_add_time(m->event, &s, CLOCK_MONOTONIC, 0, 0, monitor_memory_pressure_contexts_handler, m);
if (r < 0)
return r;
if (r < 0)
return r;
- (void) sd_event_source_set_description(s, "oomd-timer");
+ (void) sd_event_source_set_description(s, "oomd-memory-pressure-timer");
- m->cgroup_context_event_source = TAKE_PTR(s);
+ m->mem_pressure_context_event_source = TAKE_PTR(s);
return 0;
}
assert(m);
varlink_close_unref(m->varlink);
- sd_event_source_unref(m->cgroup_context_event_source);
+ sd_event_source_unref(m->swap_context_event_source);
+ sd_event_source_unref(m->mem_pressure_context_event_source);
sd_event_unref(m->event);
bus_verify_polkit_async_registry_free(m->polkit_registry);
if (r < 0)
return r;
- r = monitor_cgroup_contexts(m);
+ r = monitor_memory_pressure_contexts(m);
+ if (r < 0)
+ return r;
+
+ r = monitor_swap_contexts(m);
if (r < 0)
return r;
#include "varlink.h"
/* Polling interval for monitoring stats */
-#define INTERVAL_USEC (1 * USEC_PER_SEC)
-
-/* Used to weight the averages */
-#define AVERAGE_SIZE_DECAY 4
+#define SWAP_INTERVAL_USEC 150000 /* 0.15 seconds */
+/* Pressure counters are lagging (~2 seconds) compared to swap so polling too frequently just wastes CPU */
+#define MEM_PRESSURE_INTERVAL_USEC (1 * USEC_PER_SEC)
/* Take action if 10s of memory pressure > 60 for more than 30s. We use the "full" value from PSI so this is the
* percentage of time all tasks were delayed (i.e. unproductive).
#define DEFAULT_MEM_PRESSURE_LIMIT_PERCENT 60
#define DEFAULT_SWAP_USED_LIMIT_PERCENT 90
+/* Only tackle candidates with large swap usage. */
+#define THRESHOLD_SWAP_USED_PERCENT 5
+
#define RECLAIM_DURATION_USEC (30 * USEC_PER_SEC)
#define POST_ACTION_DELAY_USEC (15 * USEC_PER_SEC)
OomdSystemContext system_context;
- usec_t last_reclaim_at;
- usec_t post_action_delay_start;
+ usec_t mem_pressure_post_action_delay_start;
- sd_event_source *cgroup_context_event_source;
+ sd_event_source *swap_context_event_source;
+ sd_event_source *mem_pressure_context_event_source;
Varlink *varlink;
};
if (ctx->memory_pressure.avg10 > ctx->mem_pressure_limit) {
usec_t diff;
- if (ctx->last_hit_mem_pressure_limit == 0)
- ctx->last_hit_mem_pressure_limit = now(CLOCK_MONOTONIC);
+ if (ctx->mem_pressure_limit_hit_start == 0)
+ ctx->mem_pressure_limit_hit_start = now(CLOCK_MONOTONIC);
- diff = now(CLOCK_MONOTONIC) - ctx->last_hit_mem_pressure_limit;
+ diff = now(CLOCK_MONOTONIC) - ctx->mem_pressure_limit_hit_start;
if (diff >= duration) {
r = set_put(targets, ctx);
if (r < 0)
return -ENOMEM;
}
} else
- ctx->last_hit_mem_pressure_limit = 0;
+ ctx->mem_pressure_limit_hit_start = 0;
}
if (!set_isempty(targets)) {
return 0;
}
-bool oomd_memory_reclaim(Hashmap *h) {
- uint64_t pgscan = 0, pgscan_of = 0, last_pgscan = 0, last_pgscan_of = 0;
- OomdCGroupContext *ctx;
-
- assert(h);
-
- /* If sum of all the current pgscan values are greater than the sum of all the last_pgscan values,
- * there was reclaim activity. Used along with pressure checks to decide whether to take action. */
+uint64_t oomd_pgscan_rate(const OomdCGroupContext *c) {
+ uint64_t last_pgscan;
- HASHMAP_FOREACH(ctx, h) {
- uint64_t sum;
+ assert(c);
- sum = pgscan + ctx->pgscan;
- if (sum < pgscan || sum < ctx->pgscan)
- pgscan_of++; /* count overflows */
- pgscan = sum;
-
- sum = last_pgscan + ctx->last_pgscan;
- if (sum < last_pgscan || sum < ctx->last_pgscan)
- last_pgscan_of++; /* count overflows */
- last_pgscan = sum;
+ /* If last_pgscan > pgscan, assume the cgroup was recreated and reset last_pgscan to zero.
+ * pgscan is monotonic and in practice should not decrease (except in the recreation case). */
+ last_pgscan = c->last_pgscan;
+ if (c->last_pgscan > c->pgscan) {
+ log_debug("Last pgscan %"PRIu64" greater than current pgscan %"PRIu64" for %s. Using last pgscan of zero.",
+ c->last_pgscan, c->pgscan, c->path);
+ last_pgscan = 0;
}
- /* overflow counts are the same, return sums comparison */
- if (last_pgscan_of == pgscan_of)
- return pgscan > last_pgscan;
-
- return pgscan_of > last_pgscan_of;
+ return c->pgscan - last_pgscan;
}
bool oomd_swap_free_below(const OomdSystemContext *ctx, int threshold_permyriad) {
return ret;
}
-int oomd_kill_by_swap_usage(Hashmap *h, bool dry_run, char **ret_selected) {
+int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, char **ret_selected) {
_cleanup_free_ OomdCGroupContext **sorted = NULL;
int n, r, ret = 0;
if (n < 0)
return n;
- /* 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. */
+ /* 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++) {
- /* Skip over cgroups with no resource usage.
- * Continue break since there might be "avoid" cgroups at the end. */
- if (sorted[i]->swap_usage == 0)
+ /* 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)
continue;
r = oomd_cgroup_kill(sorted[i]->path, true, dry_run);
if (old_ctx) {
curr_ctx->last_pgscan = old_ctx->pgscan;
curr_ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
- curr_ctx->last_hit_mem_pressure_limit = old_ctx->last_hit_mem_pressure_limit;
+ curr_ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
+ curr_ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
}
+ if (oomd_pgscan_rate(curr_ctx) > 0)
+ curr_ctx->last_had_mem_reclaim = now(CLOCK_MONOTONIC);
+
r = hashmap_put(new_h, curr_ctx->path, curr_ctx);
if (r < 0)
return r;
ctx->last_pgscan = old_ctx->pgscan;
ctx->mem_pressure_limit = old_ctx->mem_pressure_limit;
- ctx->last_hit_mem_pressure_limit = old_ctx->last_hit_mem_pressure_limit;
+ ctx->mem_pressure_limit_hit_start = old_ctx->mem_pressure_limit_hit_start;
+ ctx->last_had_mem_reclaim = old_ctx->last_had_mem_reclaim;
+
+ if (oomd_pgscan_rate(ctx) > 0)
+ ctx->last_had_mem_reclaim = now(CLOCK_MONOTONIC);
}
}
ManagedOOMPreference preference;
- /* These are only used by oomd_pressure_above for acting on high memory pressure. */
+ /* These are only used for acting on high memory pressure. */
loadavg_t mem_pressure_limit;
- usec_t mem_pressure_duration_usec;
- usec_t last_hit_mem_pressure_limit;
+ usec_t mem_pressure_limit_hit_start;
+ usec_t last_had_mem_reclaim;
};
struct OomdSystemContext {
/* 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.
- * `last_hit_mem_pressure_limit` is updated accordingly for each entry when the limit is exceeded, and when it returns
+ * `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 -ENOMEM for allocation errors. */
int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret);
-/* Sum up current OomdCGroupContexts' pgscan values and last interval's pgscan values in `h`. Returns true if the
- * current sum is higher than the last interval's sum (there was some reclaim activity). */
-bool oomd_memory_reclaim(Hashmap *h);
-
/* Returns true if the amount of swap free is below the permyriad of swap specified by `threshold_permyriad`. */
bool oomd_swap_free_below(const OomdSystemContext *ctx, int threshold_permyriad);
+/* Returns pgscan - last_pgscan, accounting for corner cases. */
+uint64_t oomd_pgscan_rate(const OomdCGroupContext *c);
+
/* The compare functions will sort from largest to smallest, putting all the contexts with "avoid" at the end
* (after the smallest values). */
static inline int compare_pgscan_rate_and_memory_usage(OomdCGroupContext * const *c1, OomdCGroupContext * const *c2) {
- uint64_t last1, last2;
+ uint64_t diff1, diff2;
int r;
assert(c1);
if (r != 0)
return r;
- /* If last_pgscan > pgscan, assume the cgroup was recreated and reset last_pgscan to zero. */
- last2 = (*c2)->last_pgscan;
- if ((*c2)->last_pgscan > (*c2)->pgscan) {
- log_info("Last pgscan %" PRIu64 "greater than current pgscan %" PRIu64 "for %s. Using last pgscan of zero.",
- (*c2)->last_pgscan, (*c2)->pgscan, (*c2)->path);
- last2 = 0;
- }
-
- last1 = (*c1)->last_pgscan;
- if ((*c1)->last_pgscan > (*c1)->pgscan) {
- log_info("Last pgscan %" PRIu64 "greater than current pgscan %" PRIu64 "for %s. Using last pgscan of zero.",
- (*c1)->last_pgscan, (*c1)->pgscan, (*c1)->path);
- last1 = 0;
- }
-
- r = CMP((*c2)->pgscan - last2, (*c1)->pgscan - last1);
+ diff1 = oomd_pgscan_rate(*c1);
+ diff2 = oomd_pgscan_rate(*c2);
+ r = CMP(diff2, diff1);
if (r != 0)
return r;
* everything in `h` is a candidate.
* Returns the killed cgroup in ret_selected. */
int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char **ret_selected);
-int oomd_kill_by_swap_usage(Hashmap *h, bool dry_run, char **ret_selected);
+int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, char **ret_selected);
int oomd_cgroup_context_acquire(const char *path, OomdCGroupContext **ret);
int oomd_system_context_acquire(const char *proc_swaps_path, OomdSystemContext *ret);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
+ if (arg_mem_pressure_usec > 0 && arg_mem_pressure_usec < 1 * USEC_PER_SEC)
+ 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");
_cleanup_hashmap_free_ Hashmap *h1 = NULL, *h2 = NULL;
_cleanup_(oomd_cgroup_context_freep) OomdCGroupContext *ctx = NULL;
_cleanup_free_ char *cgroup = NULL;
+ ManagedOOMPreference root_pref;
OomdCGroupContext *c1, *c2;
bool test_xattrs;
- int r;
+ int root_xattrs, r;
if (geteuid() != 0)
return (void) log_tests_skipped("not root");
ctx = oomd_cgroup_context_free(ctx);
/* Test the root cgroup */
+ /* Root cgroup is live and not made on demand like the cgroup the test runs in. It can have varying
+ * xattrs set already so let's read in the booleans first to get the final preference value. */
+ root_xattrs = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, "", "user.oomd_omit");
+ root_pref = root_xattrs > 0 ? MANAGED_OOM_PREFERENCE_OMIT : MANAGED_OOM_PREFERENCE_NONE;
+ root_xattrs = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, "", "user.oomd_avoid");
+ root_pref = root_xattrs > 0 ? MANAGED_OOM_PREFERENCE_AVOID : MANAGED_OOM_PREFERENCE_NONE;
assert_se(oomd_cgroup_context_acquire("", &ctx) == 0);
assert_se(streq(ctx->path, "/"));
assert_se(ctx->current_memory_usage > 0);
- assert_se(ctx->preference == MANAGED_OOM_PREFERENCE_NONE);
+ assert_se(ctx->preference == root_pref);
/* Test hashmap inserts */
assert_se(h1 = hashmap_new(&oomd_cgroup_ctx_hash_ops));
assert_se(oomd_insert_cgroup_context(NULL, h1, cgroup) == -EEXIST);
/* make sure certain values from h1 get updated in h2 */
- c1->pgscan = 5555;
+ c1->pgscan = UINT64_MAX;
c1->mem_pressure_limit = 6789;
- c1->last_hit_mem_pressure_limit = 42;
+ c1->mem_pressure_limit_hit_start = 42;
+ 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);
c1 = hashmap_get(h1, cgroup);
assert_se(c1);
assert_se(c2);
assert_se(c1 != c2);
- assert_se(c2->last_pgscan == 5555);
+ assert_se(c2->last_pgscan == UINT64_MAX);
assert_se(c2->mem_pressure_limit == 6789);
- assert_se(c2->last_hit_mem_pressure_limit == 42);
+ assert_se(c2->mem_pressure_limit_hit_start == 42);
+ assert_se(c2->last_had_mem_reclaim == 888); /* assumes the live pgscan is less than UINT64_MAX */
/* Assert that avoid/omit are not set if the cgroup is not owned by root */
if (test_xattrs) {
char **paths = STRV_MAKE("/0.slice",
"/1.slice");
- OomdCGroupContext ctx_old[3] = {
+ OomdCGroupContext ctx_old[2] = {
{ .path = paths[0],
.mem_pressure_limit = 5,
- .last_hit_mem_pressure_limit = 777,
+ .mem_pressure_limit_hit_start = 777,
+ .last_had_mem_reclaim = 888,
.pgscan = 57 },
{ .path = paths[1],
.mem_pressure_limit = 6,
- .last_hit_mem_pressure_limit = 888,
+ .mem_pressure_limit_hit_start = 888,
+ .last_had_mem_reclaim = 888,
.pgscan = 42 },
};
- OomdCGroupContext ctx_new[3] = {
+ OomdCGroupContext ctx_new[2] = {
{ .path = paths[0],
- .pgscan = 100 },
+ .pgscan = 57 },
{ .path = paths[1],
.pgscan = 101 },
};
assert_se(c_new = hashmap_get(h_new, "/0.slice"));
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->last_hit_mem_pressure_limit == c_new->last_hit_mem_pressure_limit);
+ assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
+ assert_se(c_old->last_had_mem_reclaim == c_new->last_had_mem_reclaim);
assert_se(c_old = hashmap_get(h_old, "/1.slice"));
assert_se(c_new = hashmap_get(h_new, "/1.slice"));
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->last_hit_mem_pressure_limit == c_new->last_hit_mem_pressure_limit);
+ assert_se(c_old->mem_pressure_limit_hit_start == c_new->mem_pressure_limit_hit_start);
+ assert_se(c_new->last_had_mem_reclaim > c_old->last_had_mem_reclaim);
}
static void test_oomd_system_context_acquire(void) {
assert_se(oomd_pressure_above(h1, 0 /* duration */, &t1) == 1);
assert_se(set_contains(t1, &ctx[0]) == true);
assert_se(c = hashmap_get(h1, "/herp.slice"));
- assert_se(c->last_hit_mem_pressure_limit > 0);
+ assert_se(c->mem_pressure_limit_hit_start > 0);
/* Low memory pressure */
assert_se(h2 = hashmap_new(&string_hash_ops));
assert_se(oomd_pressure_above(h2, 0 /* duration */, &t2) == 0);
assert_se(t2 == NULL);
assert_se(c = hashmap_get(h2, "/derp.slice"));
- assert_se(c->last_hit_mem_pressure_limit == 0);
+ 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(set_contains(t3, &ctx[0]) == true);
assert_se(set_size(t3) == 1);
assert_se(c = hashmap_get(h1, "/herp.slice"));
- assert_se(c->last_hit_mem_pressure_limit > 0);
+ assert_se(c->mem_pressure_limit_hit_start > 0);
assert_se(c = hashmap_get(h1, "/derp.slice"));
- assert_se(c->last_hit_mem_pressure_limit == 0);
-}
-
-static void test_oomd_memory_reclaim(void) {
- _cleanup_hashmap_free_ Hashmap *h1 = NULL;
- char **paths = STRV_MAKE("/0.slice",
- "/1.slice",
- "/2.slice",
- "/3.slice",
- "/4.slice");
-
- OomdCGroupContext ctx[5] = {
- { .path = paths[0],
- .last_pgscan = 100,
- .pgscan = 100 },
- { .path = paths[1],
- .last_pgscan = 100,
- .pgscan = 100 },
- { .path = paths[2],
- .last_pgscan = 77,
- .pgscan = 33 },
- { .path = paths[3],
- .last_pgscan = UINT64_MAX,
- .pgscan = 100 },
- { .path = paths[4],
- .last_pgscan = 100,
- .pgscan = UINT64_MAX },
- };
-
- assert_se(h1 = hashmap_new(&string_hash_ops));
- assert_se(hashmap_put(h1, paths[0], &ctx[0]) >= 0);
- assert_se(hashmap_put(h1, paths[1], &ctx[1]) >= 0);
- assert_se(oomd_memory_reclaim(h1) == false);
-
- assert_se(hashmap_put(h1, paths[2], &ctx[2]) >= 0);
- assert_se(oomd_memory_reclaim(h1) == false);
-
- assert_se(hashmap_put(h1, paths[4], &ctx[4]) >= 0);
- assert_se(oomd_memory_reclaim(h1) == true);
-
- assert_se(hashmap_put(h1, paths[3], &ctx[3]) >= 0);
- assert_se(oomd_memory_reclaim(h1) == false);
+ assert_se(c->mem_pressure_limit_hit_start == 0);
}
static void test_oomd_swap_free_below(void) {
test_oomd_update_cgroup_contexts_between_hashmaps();
test_oomd_system_context_acquire();
test_oomd_pressure_above();
- test_oomd_memory_reclaim();
test_oomd_swap_free_below();
test_oomd_sort_cgroups();
cryptsetup open --type=luks2 --key-file=$D/empty-password ${LOOP}p7 $VOLUME
mkdir $D/mount
mount -t ext4 /dev/mapper/$VOLUME $D/mount
+ # Use deferred closing on the mapper and autoclear on the loop, so they are cleaned up on umount
+ cryptsetup close --deferred $VOLUME
+ losetup -d $LOOP
diff -r $D/mount/def $D/definitions > /dev/null
umount $D/mount
- cryptsetup close $VOLUME
- losetup -d $LOOP
else
echo "### Skipping Format=/Encrypt=/CopyFiles= test, lacking privileges or missing cryptsetup/diff/losetup"
fi
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_verify_polkit_async(
message,
r = btrfs_subvol_set_subtree_quota_limit("/var/lib/portables", 0, limit);
if (r == -ENOTTY)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Quota is only supported on btrfs.");
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to adjust quota limit: %m");
}
if (m->n_operations >= OPERATIONS_MAX)
- return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = bus_image_acquire(m,
message,
if (r < 0)
return r;
if (!FILE_SIZE_VALID_OR_INFINITY(limit))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range");
r = bus_image_acquire(m,
message,
o->pid = 0;
if (si->si_code != CLD_EXITED) {
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ r = sd_bus_error_set(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
goto fail;
}
if (si->si_status == EXIT_SUCCESS)
r = 0;
else if (read(o->errno_fd, &r, sizeof(r)) != sizeof(r)) { /* Try to acquire error code for failed operation */
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");
+ r = sd_bus_error_set(&error, SD_BUS_ERROR_FAILED, "Child failed.");
goto fail;
}
SD_RESOLVED_NO_TRUST_ANCHOR|
SD_RESOLVED_NO_NETWORK|
ok))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */
*flags |= SD_RESOLVED_PROTOCOLS_ALL;
return r;
if (ifindex < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
return r;
if (ifindex < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
r = validate_and_mangle_flags(NULL, &flags, 0, error);
if (r < 0)
return r;
if (ifindex < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
r = dns_name_is_valid(name);
if (r < 0)
if (!dns_type_is_valid_query(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified resource record type %" PRIu16 " may not be used in a query.", type);
if (dns_type_is_zone_transer(type))
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Zone transfers not permitted via this programming interface.");
if (dns_type_is_obsolete(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type);
return r;
if (ifindex < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid domain '%s'", domain);
if (name && !type)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type.");
r = validate_and_mangle_flags(name, &flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS, error);
if (r < 0)
assert(m);
if (m->mdns_support != RESOLVE_SUPPORT_YES)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for MulticastDNS is disabled");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for MulticastDNS is disabled");
service = new0(DnssdService, 1);
if (!service)
return r;
if (isempty(key))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Keys in DNS-SD TXT RRs can't be empty");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Keys in DNS-SD TXT RRs can't be empty");
if (!ascii_is_valid(key))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TXT key '%s' contains non-ASCII symbols", key);
if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL)) {
uint32_t left_ttl;
+ assert(current > 0);
+
/* Let's determine how much time is left for this cache entry. Note that we round down, but
* clamp this to be 1s at minimum, since we usually want records to remain cached better too
* short a time than too long a time, but otoh don't want to return 0 ever, since that has
bool nxdomain = false;
DnsCacheItem *j, *first, *nsec = NULL;
bool have_authenticated = false, have_non_authenticated = false, have_confidential = false, have_non_confidential = false;
- usec_t current;
+ usec_t current = 0;
int found_rcode = -1;
DnssecResult dnssec_result = -1;
int have_dnssec_result = -1;
goto miss;
}
- if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL))
+ if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL)) {
+ /* 'current' is always passed to answer_add_clamp_ttl(), but is only used conditionally.
+ * We'll do the same assert there to make sure that it was initialized properly. */
current = now(clock_boottime_or_monotonic());
+ assert(current > 0);
+ }
LIST_FOREACH(by_key, j, first) {
/* If the caller doesn't allow us to answer questions from cache data learned from
int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
uint8_t wire_format[DNS_WIRE_FORMAT_HOSTNAME_MAX];
- gcry_md_hd_t md = NULL;
+ _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
gcry_error_t err;
size_t hash_size;
int algorithm;
gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
result = gcry_md_read(md, 0);
- if (!result) {
- r = -EIO;
- goto finish;
- }
+ if (!result)
+ return -EIO;
for (k = 0; k < nsec3->nsec3.iterations; k++) {
uint8_t tmp[hash_size];
gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
result = gcry_md_read(md, 0);
- if (!result) {
- r = -EIO;
- goto finish;
- }
+ if (!result)
+ return -EIO;
}
memcpy(ret, result, hash_size);
- r = (int) hash_size;
-
-finish:
- gcry_md_close(md);
- return r;
+ return (int) hash_size;
}
static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
if (c->error_code != 0)
return DNS_TRANSACTION_ERRNO;
- SET_FOREACH(t, c->transactions) {
+ SET_FOREACH(t, c->transactions)
switch (t->state) {
break;
}
- }
return state;
}
int rcode,
bool tc, /* set the Truncated bit? */
bool aa, /* set the Authoritative Answer bit? */
+ bool rd, /* set the Recursion Desired bit? */
bool add_opt, /* add an OPT RR to this packet? */
bool edns0_do, /* set the EDNS0 DNSSEC OK bit? */
bool ad, /* set the DNSSEC authenticated data bit? */
0 /* opcode */,
aa /* aa */,
tc /* tc */,
- 1 /* rd */,
+ rd /* rd */,
1 /* ra */,
ad /* ad */,
cd /* cd */,
rcode,
truncated,
dns_query_fully_authoritative(q),
+ DNS_PACKET_RD(q->request_packet),
!!q->request_packet->opt,
edns0_do,
DNS_PACKET_AD(q->request_packet) && dns_query_fully_authenticated(q),
rcode,
truncated,
false,
+ DNS_PACKET_RD(p),
!!p->opt,
DNS_PACKET_DO(p),
DNS_PACKET_AD(p) && authenticated,
"lan\0"
"intranet\0"
"internal\0"
- "private\0";
+ "private\0"
+
+ /* Defined by RFC 8375. The most official choice. */
+ "home.arpa\0";
const char *name;
int r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
if (!route_only && dns_name_is_root(name))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
if (route_only) {
prefixed = strjoin("~", name);
int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
- size_t i;
int r;
fd = open(root, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (fd < 0)
return log_error_errno(errno, "Failed to open root file system: %m");
- for (i = 0; i < ELEMENTSOF(table); i ++) {
+ for (size_t i = 0; i < ELEMENTSOF(table); i++) {
if (faccessat(fd, table[i].dir, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
continue;
return -errno;
}
- if (uid_is_valid(uid) || gid_is_valid(gid)) {
+ if (uid_is_valid(uid) || gid_is_valid(gid))
if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir);
- }
continue;
}
return -errno;
}
- if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (uid != UID_INVALID || gid != UID_INVALID)
if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
return log_error_errno(errno, "Failed to chown directory at %s/%s: %m", root, table[i].dir);
- }
}
return 0;
#include "memory-util.h"
#include "missing_syscall.h"
#include "path-util.h"
+#include "string-table.h"
+
+static const char *const bpf_cgroup_attach_type_table[__MAX_BPF_ATTACH_TYPE] = {
+ [BPF_CGROUP_INET_INGRESS] = "ingress",
+ [BPF_CGROUP_INET_EGRESS] = "egress",
+ [BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
+ [BPF_CGROUP_SOCK_OPS] = "sock_ops",
+ [BPF_CGROUP_DEVICE] = "device",
+ [BPF_CGROUP_INET4_BIND] = "bind4",
+ [BPF_CGROUP_INET6_BIND] = "bind6",
+ [BPF_CGROUP_INET4_CONNECT] = "connect4",
+ [BPF_CGROUP_INET6_CONNECT] = "connect6",
+ [BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
+ [BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
+ [BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
+ [BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
+ [BPF_CGROUP_SYSCTL] = "sysctl",
+ [BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
+ [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
+ [BPF_CGROUP_GETSOCKOPT] = "getsockopt",
+ [BPF_CGROUP_SETSOCKOPT] = "setsockopt",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bpf_cgroup_attach_type, int);
+
+ /* struct bpf_prog_info info must be initialized since its value is both input and output
+ * for BPF_OBJ_GET_INFO_BY_FD syscall. */
+static int bpf_program_get_info_by_fd(int prog_fd, struct bpf_prog_info *info, uint32_t info_len) {
+ union bpf_attr attr;
+
+ /* Explicitly memset to zero since some compilers may produce non-zero-initialized padding when
+ * structured initialization is used.
+ * Refer to https://github.com/systemd/systemd/issues/18164
+ */
+ zero(attr);
+ attr.info.bpf_fd = prog_fd;
+ attr.info.info_len = info_len;
+ attr.info.info = PTR_TO_UINT64(info);
+
+ if (bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) < 0)
+ return -errno;
+
+ return 0;
+}
int bpf_program_new(uint32_t prog_type, BPFProgram **ret) {
_cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
return 0;
}
+int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret) {
+ _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL;
+ struct bpf_prog_info info = {};
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ p = new(BPFProgram, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (BPFProgram) {
+ .prog_type = BPF_PROG_TYPE_UNSPEC,
+ .n_ref = 1,
+ .kernel_fd = -1,
+ };
+
+ r = bpf_program_load_from_bpf_fs(p, path);
+ if (r < 0)
+ return r;
+
+ r = bpf_program_get_info_by_fd(p->kernel_fd, &info, sizeof(info));
+ if (r < 0)
+ return r;
+
+ p->prog_type = info.type;
+ *ret = TAKE_PTR(p);
+
+ return 0;
+}
+
static BPFProgram *bpf_program_free(BPFProgram *p) {
assert(p);
return 0;
}
+
+int bpf_program_pin(int prog_fd, const char *bpffs_path) {
+ union bpf_attr attr;
+
+ zero(attr);
+ attr.pathname = PTR_TO_UINT64((void *) bpffs_path);
+ attr.bpf_fd = prog_fd;
+
+ if (bpf(BPF_OBJ_PIN, &attr, sizeof(attr)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int bpf_program_get_id_by_fd(int prog_fd, uint32_t *ret_id) {
+ struct bpf_prog_info info = {};
+ int r;
+
+ assert(ret_id);
+
+ r = bpf_program_get_info_by_fd(prog_fd, &info, sizeof(info));
+ if (r < 0)
+ return r;
+
+ *ret_id = info.id;
+
+ return 0;
+};
};
int bpf_program_new(uint32_t prog_type, BPFProgram **ret);
-BPFProgram *bpf_program_unref(BPFProgram *p);
+int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret);
BPFProgram *bpf_program_ref(BPFProgram *p);
+BPFProgram *bpf_program_unref(BPFProgram *p);
int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count);
int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size);
int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags);
int bpf_program_cgroup_detach(BPFProgram *p);
+int bpf_program_pin(int prog_fd, const char *bpffs_path);
+int bpf_program_get_id_by_fd(int prog_fd, uint32_t *ret_id);
int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags);
int bpf_map_update_element(int fd, const void *key, void *value);
int bpf_map_lookup_element(int fd, const void *key, void *value);
+int bpf_cgroup_attach_type_from_string(const char *str) _pure_;
+const char *bpf_cgroup_attach_type_to_string(int attach_type) _const_;
+
DEFINE_TRIVIAL_CLEANUP_FUNC(BPFProgram*, bpf_program_unref);
return r;
if (ifindex <= 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
*ret = ifindex;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
if (sz != FAMILY_ADDRESS_SIZE(family))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
if (ret_family)
*ret_family = family;
if (r < 0)
return r;
- if (!dns_server_address_valid(family, &a))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
+ if (!dns_server_address_valid(family, &a)) {
+ r = sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
+ assert(r < 0);
+ return r;
+ }
if (extended) {
r = sd_bus_message_read(message, "q", &port);
return 1;
}
+ if (streq(field, "BPFProgram")) {
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
+ else {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&eq, &word, ":", 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s: %m", field);
+
+ r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, word, eq);
+ }
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
return 0;
}
(void) stat_warn_permissions(filename, &st);
mtime = timespec_load(&st.st_mtim);
- }
+ } else
+ mtime = 0;
for (;;) {
_cleanup_free_ char *buf = NULL;
if (r < 0)
return r;
+ r = sd_device_enumerator_add_match_subsystem(e, "block", true);
+ if (r < 0)
+ return r;
+
r = sd_device_enumerator_add_match_parent(e, d);
if (r < 0)
return r;
+ r = sd_device_enumerator_add_match_sysattr(e, "partition", NULL, true);
+ if (r < 0)
+ return r;
+
*ret = TAKE_PTR(e);
return 0;
}
-static int device_is_partition(sd_device *d, blkid_partition pp) {
+static int device_is_partition(sd_device *d, sd_device *expected_parent, blkid_partition pp) {
+ const char *v, *parent_syspath, *expected_parent_syspath;
blkid_loff_t bsize, bstart;
uint64_t size, start;
int partno, bpartno, r;
- const char *ss, *v;
+ sd_device *parent;
assert(d);
+ assert(expected_parent);
assert(pp);
- r = sd_device_get_subsystem(d, &ss);
+ r = sd_device_get_subsystem(d, &v);
if (r < 0)
return r;
- if (!streq(ss, "block"))
+ if (!streq(v, "block"))
return false;
- r = sd_device_get_sysattr_value(d, "partition", &v);
- if (r == -ENOENT || /* Not a partition device */
- ERRNO_IS_PRIVILEGE(r)) /* Not ready to access? */
+ if (sd_device_get_devtype(d, &v) < 0 || !streq(v, "partition"))
return false;
+
+ r = sd_device_get_parent(d, &parent);
+ if (r < 0)
+ return false; /* Doesn't have a parent? No relevant to us */
+
+ r = sd_device_get_syspath(parent, &parent_syspath); /* Check parent of device of this action */
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_syspath(expected_parent, &expected_parent_syspath); /* Check parent of device we are looking for */
+ if (r < 0)
+ return r;
+
+ if (!path_equal(parent_syspath, expected_parent_syspath))
+ return false; /* Has a different parent than what we need, not interesting to us */
+
+ r = sd_device_get_sysattr_value(d, "partition", &v);
if (r < 0)
return r;
r = safe_atoi(v, &partno);
return r;
FOREACH_DEVICE(e, q) {
- r = device_is_partition(q, pp);
+ r = device_is_partition(q, parent, pp);
if (r < 0)
return r;
if (r > 0) {
}
static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
- const char *parent1_path, *parent2_path;
struct wait_data *w = userdata;
- sd_device *pp;
int r;
assert(w);
if (device_for_action(device, SD_DEVICE_REMOVE))
return 0;
- r = sd_device_get_parent(device, &pp);
- if (r < 0)
- return 0; /* Doesn't have a parent? No relevant to us */
-
- r = sd_device_get_syspath(pp, &parent1_path); /* Check parent of device of this action */
- if (r < 0)
- goto finish;
-
- r = sd_device_get_syspath(w->parent_device, &parent2_path); /* Check parent of device we are looking for */
- if (r < 0)
- goto finish;
-
- if (!path_equal(parent1_path, parent2_path))
- return 0; /* Has a different parent than what we need, not interesting to us */
-
- r = device_is_partition(device, w->blkidp);
+ r = device_is_partition(device, w->parent_device, w->blkidp);
if (r < 0)
goto finish;
if (r == 0) /* Not the one we need */
if (r < 0)
return r;
+ r = sd_device_monitor_filter_add_match_parent(monitor, parent, true);
+ if (r < 0)
+ return r;
+
+ r = sd_device_monitor_filter_add_match_sysattr(monitor, "partition", NULL, true);
+ if (r < 0)
+ return r;
+
r = sd_device_monitor_attach_event(monitor, event);
if (r < 0)
return r;
#ifdef GPT_USR_NATIVE
sd_id128_t usr_uuid = SD_ID128_NULL, usr_verity_uuid = SD_ID128_NULL;
#endif
- bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
+ bool is_gpt, is_mbr, multiple_generic = false,
+ generic_rw = false; /* initialize to appease gcc */
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
sd_id128_t generic_uuid = SD_ID128_NULL;
const char *pttype = NULL, *sysname = NULL;
blkid_partlist pl;
- int r, generic_nr, n_partitions;
+ int r, generic_nr = -1, n_partitions;
struct stat st;
usec_t deadline;
return -ENOMEM;
}
+ assert(generic_nr >= 0);
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
.found = true,
.rw = generic_rw,
if (!source || source_prefixlen == 0)
return -EINVAL;
- h = iptc_init("nat");
- if (!h)
- return -errno;
+ r = fw_iptables_init_nat(&h);
+ if (r < 0)
+ return r;
sz = XT_ALIGN(sizeof(struct ipt_entry)) +
XT_ALIGN(sizeof(struct ipt_entry_target)) +
if (remote_port <= 0)
return -EINVAL;
- h = iptc_init("nat");
- if (!h)
- return -errno;
+ r = fw_iptables_init_nat(&h);
+ if (r < 0)
+ return r;
sz = XT_ALIGN(sizeof(struct ipt_entry)) +
XT_ALIGN(sizeof(struct ipt_entry_match)) +
return 0;
}
+
+int fw_iptables_init_nat(struct xtc_handle **ret) {
+ _cleanup_(iptc_freep) struct xtc_handle *h = NULL;
+
+ h = iptc_init("nat");
+ if (!h)
+ return log_debug_errno(errno, "Failed to init \"nat\" table: %s", iptc_strerror(errno));
+
+ if (ret)
+ *ret = TAKE_PTR(h);
+
+ return 0;
+}
const union in_addr_union *previous_remote);
#if HAVE_LIBIPTC
+struct xtc_handle;
int fw_iptables_add_masquerade(
bool add,
const union in_addr_union *remote,
uint16_t remote_port,
const union in_addr_union *previous_remote);
+
+int fw_iptables_init_nat(struct xtc_handle **ret);
#endif
}
int table_add_many_internal(Table *t, TableDataType first_type, ...) {
- TableDataType type;
- va_list ap;
TableCell *last_cell = NULL;
+ va_list ap;
int r;
assert(t);
assert(first_type >= 0);
assert(first_type < _TABLE_DATA_TYPE_MAX);
- type = first_type;
-
va_start(ap, first_type);
- for (;;) {
+
+ for (TableDataType type = first_type;; type = va_arg(ap, TableDataType)) {
const void *data;
union {
uint64_t size;
size_t w = va_arg(ap, size_t);
r = table_set_minimum_width(t, last_cell, w);
- break;
+ goto check;
}
case TABLE_SET_MAXIMUM_WIDTH: {
size_t w = va_arg(ap, size_t);
r = table_set_maximum_width(t, last_cell, w);
- break;
+ goto check;
}
case TABLE_SET_WEIGHT: {
unsigned w = va_arg(ap, unsigned);
r = table_set_weight(t, last_cell, w);
- break;
+ goto check;
}
case TABLE_SET_ALIGN_PERCENT: {
unsigned p = va_arg(ap, unsigned);
r = table_set_align_percent(t, last_cell, p);
- break;
+ goto check;
}
case TABLE_SET_ELLIPSIZE_PERCENT: {
unsigned p = va_arg(ap, unsigned);
r = table_set_ellipsize_percent(t, last_cell, p);
- break;
+ goto check;
}
case TABLE_SET_COLOR: {
const char *c = va_arg(ap, const char*);
r = table_set_color(t, last_cell, c);
- break;
+ goto check;
}
case TABLE_SET_RGAP_COLOR: {
const char *c = va_arg(ap, const char*);
r = table_set_rgap_color(t, last_cell, c);
- break;
+ goto check;
}
case TABLE_SET_BOTH_COLORS: {
}
r = table_set_rgap_color(t, last_cell, c);
- break;
+ goto check;
}
case TABLE_SET_URL: {
const char *u = va_arg(ap, const char*);
r = table_set_url(t, last_cell, u);
- break;
+ goto check;
}
case TABLE_SET_UPPERCASE: {
int u = va_arg(ap, int);
r = table_set_uppercase(t, last_cell, u);
- break;
+ goto check;
}
case _TABLE_DATA_TYPE_MAX:
assert_not_reached("Uh? Unexpected data type.");
}
- if (type < _TABLE_DATA_TYPE_MAX)
- r = table_add_cell(t, &last_cell, type, data);
-
+ r = table_add_cell(t, &last_cell, type, data);
+ check:
if (r < 0) {
va_end(ap);
return r;
}
-
- type = va_arg(ap, TableDataType);
}
}
{ "v243", NAMING_V243 },
{ "v245", NAMING_V245 },
{ "v247", NAMING_V247 },
+ { "v249", NAMING_V249 },
/* … add more schemes here, as the logic to name devices is updated … */
};
NAMING_LABEL_NOPREFIX = 1 << 7, /* Don't prepend ID_NET_LABEL_ONBOARD with interface type prefix */
NAMING_NSPAWN_LONG_HASH = 1 << 8, /* Shorten nspawn interfaces by including 24bit hash, instead of simple truncation */
NAMING_BRIDGE_NO_SLOT = 1 << 9, /* Don't use PCI hotplug slot information if the corresponding device is a PCI bridge */
+ NAMING_SLOT_FUNCTION_ID = 1 << 10, /* Use function_id if present to identify PCI hotplug slots */
/* And now the masks that combine the features above */
NAMING_V238 = 0,
NAMING_V243 = NAMING_V241 | NAMING_NETDEVSIM | NAMING_LABEL_NOPREFIX,
NAMING_V245 = NAMING_V243 | NAMING_NSPAWN_LONG_HASH,
NAMING_V247 = NAMING_V245 | NAMING_BRIDGE_NO_SLOT,
+ NAMING_V249 = NAMING_V247 | NAMING_SLOT_FUNCTION_ID,
_NAMING_SCHEME_FLAGS_INVALID = -EINVAL,
} NamingSchemeFlags;
size_t *ret_suitable_key_size) {
size_t suitable_key_size;
- RSA *rsa;
+ const RSA *rsa;
int bits;
assert_se(pkey);
if (isempty(p))
return false;
- if (!in_charset(p, ALPHANUMERICAL "-_?;&%="))
+ if (!in_charset(p, ALPHANUMERICAL ".~/-_?;&%="))
return false;
return true;
int conf_files_cat(const char *root, const char *name) {
_cleanup_strv_free_ char **dirs = NULL, **files = NULL;
_cleanup_free_ char *path = NULL;
- char **prefixes, **prefix;
+ char **prefix, **prefixes = NULL; /* explicit initialization to appease gcc */
bool is_collection;
const char *extension;
char **t;
r = guess_type(&name, &prefixes, &is_collection, &extension);
if (r < 0)
return r;
+ assert(prefixes);
+ assert(extension);
STRV_FOREACH(prefix, prefixes) {
assert(endswith(*prefix, "/"));
if (r < 0)
return r;
- qr = sym_QRcode_encodeString(string, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
+ qr = sym_QRcode_encodeString(string, 0, QR_ECLEVEL_L, QR_MODE_8, 1);
if (!qr)
return -ENOMEM;
int find_hibernate_location(HibernateLocation **ret_hibernate_location) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_(hibernate_location_freep) HibernateLocation *hibernate_location = NULL;
- dev_t sys_resume;
+ dev_t sys_resume = 0; /* Unnecessary initialization to appease gcc */
uint64_t sys_offset = 0;
bool resume_match = false;
int r;
assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
+ r = dlopen_tpm2();
+ if (r < 0)
+ return log_error_errno(r, "TPM2 support is not installed.");
+
/* So here's what we do here: We connect to the TPM2 chip. As we do when sealing we generate a
* "primary" key on the TPM2 chip, with the same parameters as well as a PCR-bound policy
* session. Given we pass the same parameters, this will result in the same "primary" key, and same
assert(v->fd >= 0);
/* We generally prefer recv()/send() (mostly because of MSG_NOSIGNAL) but also want to be compatible
- * with non-socket IO, hence fall back automatically */
- if (!v->prefer_read_write) {
+ * with non-socket IO, hence fall back automatically.
+ *
+ * Use a local variable to help gcc figure out that we set 'n' in all cases. */
+ bool prefer_write = v->prefer_read_write;
+ if (!prefer_write) {
n = send(v->fd, v->output_buffer + v->output_buffer_index, v->output_buffer_size, MSG_DONTWAIT|MSG_NOSIGNAL);
if (n < 0 && errno == ENOTSOCK)
- v->prefer_read_write = true;
+ prefer_write = v->prefer_read_write = true;
}
- if (v->prefer_read_write)
+ if (prefer_write)
n = write(v->fd, v->output_buffer + v->output_buffer_index, v->output_buffer_size);
if (n < 0) {
if (errno == EAGAIN)
rs = v->input_buffer_allocated - (v->input_buffer_index + v->input_buffer_size);
- if (!v->prefer_read_write) {
+ bool prefer_read = v->prefer_read_write;
+ if (!prefer_read) {
n = recv(v->fd, v->input_buffer + v->input_buffer_index + v->input_buffer_size, rs, MSG_DONTWAIT);
if (n < 0 && errno == ENOTSOCK)
- v->prefer_read_write = true;
+ prefer_read = v->prefer_read_write = true;
}
- if (v->prefer_read_write)
+ if (prefer_read)
n = read(v->fd, v->input_buffer + v->input_buffer_index + v->input_buffer_size, rs);
if (n < 0) {
if (errno == EAGAIN)
return 0;
SET_FLAG(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS, arg_check_inhibitors > 0);
- SET_FLAG(flags, SD_LOGIND_KEXEC_REBOOT, a == ACTION_KEXEC);
+ SET_FLAG(flags, SD_LOGIND_REBOOT_VIA_KEXEC, a == ACTION_KEXEC);
method_with_flags = strjoina(actions[a].method, "WithFlags");
return 1;
+ } else if (streq(name, "BPFProgram")) {
+ const char *a, *p;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_read(m, "(ss)", &a, &p)) > 0)
+ bus_print_property_valuef(name, expected_value, value, "%s:%s", a, p);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ return 1;
}
break;
method = verb_to_method(argv[0]);
job_type = verb_to_job_type(argv[0]);
mode = arg_job_mode;
- }
+ } else
+ method = job_type = mode = NULL;
+
one_name = NULL;
}
} else {
int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype);
int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag);
+int sd_device_monitor_filter_add_match_sysattr(sd_device_monitor *m, const char *sysattr, const char *value, int match);
+int sd_device_monitor_filter_add_match_parent(sd_device_monitor *m, sd_device *device, int match);
int sd_device_monitor_filter_update(sd_device_monitor *m);
int sd_device_monitor_filter_remove(sd_device_monitor *m);
_SD_ENUM_FORCE_S64(DHCP_LEASE_SERVER_TYPE),
} sd_dhcp_lease_server_type_t;
-int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
-int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1);
-int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2);
-int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);
-int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type_t what, const struct in_addr **addr);
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
-int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
-int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
-int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
-int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
-int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
-int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
-int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
+int sd_dhcp_lease_get_address(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_lifetime(const sd_dhcp_lease *lease, uint32_t *lifetime);
+int sd_dhcp_lease_get_t1(const sd_dhcp_lease *lease, uint32_t *t1);
+int sd_dhcp_lease_get_t2(const sd_dhcp_lease *lease, uint32_t *t2);
+int sd_dhcp_lease_get_broadcast(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_netmask(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_router(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_next_server(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_server_identifier(const sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_servers(const sd_dhcp_lease *lease, sd_dhcp_lease_server_type_t what, const struct in_addr **addr);
+int sd_dhcp_lease_get_dns(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_ntp(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_sip(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_pop3(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_smtp(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_lpr(const sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_mtu(const sd_dhcp_lease *lease, uint16_t *mtu);
+int sd_dhcp_lease_get_domainname(const sd_dhcp_lease *lease, const char **domainname);
+int sd_dhcp_lease_get_search_domains(const sd_dhcp_lease *lease, char ***domains);
+int sd_dhcp_lease_get_hostname(const sd_dhcp_lease *lease, const char **hostname);
+int sd_dhcp_lease_get_root_path(const sd_dhcp_lease *lease, const char **root_path);
+int sd_dhcp_lease_get_routes(const sd_dhcp_lease *lease, sd_dhcp_route ***routes);
+int sd_dhcp_lease_get_vendor_specific(const sd_dhcp_lease *lease, const void **data, size_t *data_len);
+int sd_dhcp_lease_get_client_id(const sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
+int sd_dhcp_lease_get_timezone(const sd_dhcp_lease *lease, const char **timezone);
-int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
-int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);
-int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway);
-int sd_dhcp_route_get_option(sd_dhcp_route *route);
+int sd_dhcp_route_get_destination(const sd_dhcp_route *route, struct in_addr *destination);
+int sd_dhcp_route_get_destination_prefix_length(const sd_dhcp_route *route, uint8_t *length);
+int sd_dhcp_route_get_gateway(const sd_dhcp_route *route, struct in_addr *gateway);
+int sd_dhcp_route_get_option(const sd_dhcp_route *route);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref);
[['src/test/test-fstab-util.c']],
- [['src/test/test-random-util.c']],
+ [['src/test/test-random-util.c'],
+ [],
+ [libm],
+ [], '', 'timeout=120'],
[['src/test/test-format-table.c']],
libblkid],
core_includes],
+ [['src/test/test-bpf-foreign-programs.c'],
+ [libcore,
+ libshared],
+ [],
+ core_includes],
+
[['src/test/test-watch-pid.c'],
[libcore,
libshared],
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <fcntl.h>
+#include <linux/bpf_insn.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "bpf-foreign.h"
+#include "load-fragment.h"
+#include "manager.h"
+#include "process-util.h"
+#include "rlimit-util.h"
+#include "rm-rf.h"
+#include "service.h"
+#include "tests.h"
+#include "unit.h"
+#include "virt.h"
+
+struct Test {
+ const char *option_name;
+ enum bpf_prog_type prog_type;
+ enum bpf_attach_type attach_type;
+ const char *bpffs_path;
+};
+
+typedef struct Test Test;
+
+#define BPFFS_PATH(prog_suffix) ("/sys/fs/bpf/test-bpf-foreing-" # prog_suffix)
+static const Test single_prog[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ },
+};
+static const Test path_split_test[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("path:split:test"),
+ },
+};
+
+static const Test same_prog_same_hook[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
+ .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
+ .bpffs_path = BPFFS_PATH("trivial-sock"),
+ },
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
+ .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
+ .bpffs_path = BPFFS_PATH("trivial-sock"),
+ }
+};
+
+static const Test multi_prog_same_hook[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
+ .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
+ .bpffs_path = BPFFS_PATH("trivial-sock-0"),
+ },
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK,
+ .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
+ .bpffs_path = BPFFS_PATH("trivial-sock-1"),
+ }
+};
+
+static const Test same_prog_multi_hook[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ },
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_EGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ }
+};
+
+static const Test same_prog_multi_option_0[] = {
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ },
+ {
+ .option_name = "IPIngressFilterPath",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_INGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ }
+};
+
+static const Test same_prog_multi_option_1[] = {
+ {
+ .option_name = "IPEgressFilterPath",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_EGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ },
+ {
+ .option_name = "BPFProgram",
+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
+ .attach_type = BPF_CGROUP_INET_EGRESS,
+ .bpffs_path = BPFFS_PATH("trivial-skb"),
+ }
+};
+#undef BPFFS_PATH
+
+static int bpf_foreign_test_to_string(enum bpf_attach_type attach_type, const char *bpffs_path, char **ret_str) {
+ const char *s = NULL;
+
+ assert_se(bpffs_path);
+ assert_se(ret_str);
+
+ assert_se(s = bpf_cgroup_attach_type_to_string(attach_type));
+ assert_se(*ret_str = strjoin(s, ":", bpffs_path));
+
+ return 0;
+}
+
+static char **unlink_paths_and_free(char **paths) {
+ char **i;
+
+ STRV_FOREACH(i, paths)
+ (void) unlink(*i);
+
+ return strv_free(paths);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(char **, unlink_paths_and_free);
+
+static int pin_programs(Unit *u, CGroupContext *cc, const Test *test_suite, size_t test_suite_size, char ***paths_ret) {
+ _cleanup_(unlink_paths_and_freep) char **bpffs_paths = NULL;
+ static const struct bpf_insn trivial[] = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN()
+ };
+ char log_buf[0xffff];
+ int r;
+
+ assert_se(paths_ret);
+
+ for (size_t i = 0; i < test_suite_size; i++) {
+ _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
+ _cleanup_free_ char *str = NULL;
+
+ r = bpf_foreign_test_to_string(test_suite[i].attach_type, test_suite[i].bpffs_path, &str);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert program to string");
+
+ r = bpf_program_new(test_suite[i].prog_type, &prog);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create program '%s'", str);
+
+ r = bpf_program_add_instructions(prog, trivial, ELEMENTSOF(trivial));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add trivial instructions for '%s'", str);
+
+ r = bpf_program_load_kernel(prog, log_buf, ELEMENTSOF(log_buf));
+ if (r < 0)
+ return log_error_errno(r, "Failed to load BPF program '%s'", str);
+
+ if (strv_contains(bpffs_paths, test_suite[i].bpffs_path))
+ continue;
+
+ r = strv_extend(&bpffs_paths, test_suite[i].bpffs_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to put path into a vector: %m");
+
+ r = bpf_program_pin(prog->kernel_fd, test_suite[i].bpffs_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pin BPF program '%s'", str);
+ }
+
+ *paths_ret = TAKE_PTR(bpffs_paths);
+ return 0;
+}
+
+static int test_bpf_cgroup_programs(Manager *m, const char *unit_name, const Test *test_suite, size_t test_suite_size) {
+ _cleanup_(unlink_paths_and_freep) char **bpffs_paths = NULL;
+ _cleanup_(unit_freep) Unit *u = NULL;
+ CGroupContext *cc = NULL;
+ int cld_code, r;
+
+ assert_se(u = unit_new(m, sizeof(Service)));
+ assert_se(unit_add_name(u, unit_name) == 0);
+ assert_se(cc = unit_get_cgroup_context(u));
+
+ r = pin_programs(u, cc, test_suite, test_suite_size, &bpffs_paths);
+ if (r < 0)
+ return log_error_errno(r, "Failed to pin programs: %m");
+
+ for (size_t i = 0; i < test_suite_size; i++) {
+ if (streq(test_suite[i].option_name, "BPFProgram")) {
+ _cleanup_free_ char *option = NULL;
+ r = bpf_foreign_test_to_string(test_suite[i].attach_type, test_suite[i].bpffs_path, &option);
+ if (r < 0)
+ return log_error_errno(r, "Failed to compose option string: %m");
+ r = config_parse_bpf_foreign_program(
+ u->id, "filename", 1, "Service", 1, test_suite[i].option_name, 0, option, cc, u);
+
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse option string '%s': %m", option);
+ } else if (STR_IN_SET(test_suite[i].option_name, "IPIngressFilterPath", "IPEgressFilterPath")) {
+ const char *option = test_suite[i].bpffs_path;
+ void *paths = NULL;
+
+ if (streq(test_suite[i].option_name, "IPIngressFilterPath"))
+ paths = &cc->ip_filters_ingress;
+ else
+ paths = &cc->ip_filters_egress;
+
+ r = config_parse_ip_filter_bpf_progs(
+ u->id, "filename", 1, "Service", 1, test_suite[i].option_name, 0, option, paths, u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse option string '%s': %m", option);
+ }
+ }
+
+ r = config_parse_exec(
+ u->id,
+ "filename",
+ 1,
+ "Service",
+ 1,
+ "ExecStart",
+ SERVICE_EXEC_START,
+ "-/bin/ping -c 5 127.0.0.1 -W 1",
+ SERVICE(u)->exec_command,
+ u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse ExecStart");
+
+ SERVICE(u)->type = SERVICE_ONESHOT;
+ u->load_state = UNIT_LOADED;
+
+ r = unit_start(u);
+ if (r < 0)
+ return log_error_errno(r, "Unit start failed %m");
+
+ while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED)) {
+ r = sd_event_run(m->event, UINT64_MAX);
+ if (r < 0)
+ return log_error_errno(errno, "Event run failed %m");
+ }
+
+ cld_code = SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code;
+ if (cld_code != CLD_EXITED)
+ return log_error_errno(SYNTHETIC_ERRNO(EBUSY),
+ "ExecStart didn't exited, code='%s'", sigchld_code_to_string(cld_code));
+
+ if (SERVICE(u)->state != SERVICE_DEAD)
+ return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Service is not dead");
+
+ return r;
+}
+
+int main(int argc, char *argv[]) {
+ _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
+ _cleanup_(manager_freep) Manager *m = NULL;
+ _cleanup_free_ char *unit_dir = NULL;
+ struct rlimit rl;
+ int r;
+
+ test_setup_logging(LOG_DEBUG);
+
+ if (detect_container() > 0)
+ return log_tests_skipped("test-bpf fails inside LXC and Docker containers: https://github.com/systemd/systemd/issues/9666");
+
+ if (getuid() != 0)
+ return log_tests_skipped("not running as root");
+
+ assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
+ rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
+ (void) setrlimit_closest(RLIMIT_MEMLOCK, &rl);
+
+ if (!can_memlock())
+ return log_tests_skipped("Can't use mlock(), skipping.");
+
+ r = cg_all_unified();
+ if (r <= 0)
+ return log_tests_skipped_errno(r, "Unified hierarchy is required, skipping.");
+
+ r = enter_cgroup_subroot(NULL);
+ if (r == -ENOMEDIUM)
+ return log_tests_skipped("cgroupfs not available");
+
+ assert_se(get_testdata_dir("units", &unit_dir) >= 0);
+ assert_se(set_unit_path(unit_dir) >= 0);
+ assert_se(runtime_dir = setup_fake_runtime_dir());
+
+ assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
+ assert_se(manager_startup(m, NULL, NULL) >= 0);
+
+ assert_se(test_bpf_cgroup_programs(m,
+ "single_prog.service", single_prog, ELEMENTSOF(single_prog)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "multi_prog_same_hook.service",
+ multi_prog_same_hook, ELEMENTSOF(multi_prog_same_hook)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "same_prog_multi_hook.service",
+ same_prog_multi_hook, ELEMENTSOF(same_prog_multi_hook)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "same_prog_multi_option_0.service",
+ same_prog_multi_option_0, ELEMENTSOF(same_prog_multi_option_0)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "same_prog_multi_option_1.service",
+ same_prog_multi_option_1, ELEMENTSOF(same_prog_multi_option_1)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "same_prog_same_hook.service",
+ same_prog_same_hook,
+ ELEMENTSOF(same_prog_same_hook)) >= 0);
+ assert_se(test_bpf_cgroup_programs(m,
+ "path_split_test.service",
+ path_split_test,
+ ELEMENTSOF(path_split_test)) >= 0);
+ return 0;
+}
nobody = getpwnam(NOBODY_USER_NAME);
if (!nobody)
- return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Could not find nobody user: %m");
+ return log_warning_errno(SYNTHETIC_ERRNO(ENOENT), "Couldn't find 'nobody' user: %m");
test_uid = nobody->pw_uid;
test_gid = nobody->pw_gid;
- *run_ambient = false;
-
r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
-
- /* There's support for PR_CAP_AMBIENT if the prctl() call
- * succeeded or error code was something else than EINVAL. The
- * EINVAL check should be good enough to rule out false
- * positives. */
-
- if (r >= 0 || errno != EINVAL)
- *run_ambient = true;
+ /* There's support for PR_CAP_AMBIENT if the prctl() call succeeded or error code was something else
+ * than EINVAL. The EINVAL check should be good enough to rule out false positives. */
+ *run_ambient = r >= 0 || errno != EINVAL;
return 0;
}
}
int main(int argc, char *argv[]) {
- bool run_ambient;
+ bool run_ambient = false; /* avoid false maybe-uninitialized warning */
test_setup_logging(LOG_INFO);
static void test_cg_mask_to_string(void) {
test_cg_mask_to_string_one(0, NULL);
- test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices");
+ test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign");
test_cg_mask_to_string_one(CGROUP_MASK_CPU, "cpu");
test_cg_mask_to_string_one(CGROUP_MASK_CPUACCT, "cpuacct");
test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset");
assert_se(streq(t, "c"));
free(t);
assert_se(p == NULL);
+
+ p = original = "foobar=\"waldo\"maldo, baldo";
+ assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
+ assert_se(streq(t, "foobar"));
+ free(t);
+ assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
+ assert_se(streq(t, "maldo"));
+ free(t);
+ assert_se(extract_first_word(&p, &t, "=\", ", 0) > 0);
+ assert_se(streq(t, "baldo"));
+ free(t);
}
static void test_extract_first_word_and_warn(void) {
log_info("/* %s(backend=%s) */", __func__, firewall_backend_to_string(ctx->backend));
+#if HAVE_LIBIPTC
+ if (ctx->backend == FW_BACKEND_IPTABLES && fw_iptables_init_nat(NULL) < 0) {
+ log_debug("iptables backend is used, but nat table is not enabled, skipping tests");
+ return false;
+ }
+#endif
+
assert_se(fw_add_masquerade(&ctx, true, AF_INET, NULL, 0) == -EINVAL);
assert_se(fw_add_masquerade(&ctx, true, AF_INET, parse_addr("10.1.2.0", &u), 0) == -EINVAL);
assert_se(!path_equal_ptr("/a", "/b"));
assert_se(!path_equal_ptr("/a", NULL));
assert_se(!path_equal_ptr(NULL, "/a"));
+
+ assert_se(path_equal_filename("/a/c", "/b/c"));
+ assert_se(path_equal_filename("/a", "/a"));
+ assert_se(!path_equal_filename("/a/b", "/a/c"));
+ assert_se(!path_equal_filename("/b", "/c"));
}
static void test_path_equal_root(void) {
} else
log_warning("Could not get time from timedated and not operating locally, ignoring.");
- if (have_time)
- n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm));
-
+ n = have_time ? strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) : 0;
r = table_add_many(table,
TABLE_STRING, "Local time:",
- TABLE_STRING, have_time && n > 0 ? a : "n/a");
+ TABLE_STRING, n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
- if (have_time)
- n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm));
-
+ n = have_time ? strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) : 0;
r = table_add_many(table,
TABLE_STRING, "Universal time:",
- TABLE_STRING, have_time && n > 0 ? a : "n/a");
+ TABLE_STRING, n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
rtc_sec = (time_t) (i->rtc_time / USEC_PER_SEC);
n = strftime(a, sizeof a, "%a %Y-%m-%d %H:%M:%S", gmtime_r(&rtc_sec, &tm));
- }
-
+ } else
+ n = 0;
r = table_add_many(table,
TABLE_STRING, "RTC time:",
- TABLE_STRING, i->rtc_time > 0 && n > 0 ? a : "n/a");
+ TABLE_STRING, n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
- if (have_time)
- n = strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm));
-
r = table_add_cell(table, NULL, TABLE_STRING, "Time zone:");
if (r < 0)
return table_log_add_error(r);
- r = table_add_cell_stringf(table, NULL, "%s (%s)", strna(i->timezone), have_time && n > 0 ? a : "n/a");
+ n = have_time ? strftime(a, sizeof a, "%Z, %z", localtime_r(&sec, &tm)) : 0;
+ r = table_add_cell_stringf(table, NULL, "%s (%s)", strna(i->timezone), n > 0 ? a : "n/a");
if (r < 0)
return table_log_add_error(r);
-
/* Restore the $TZ */
r = set_unset_env("TZ", old_tz, true);
if (r < 0)
static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *error) {
sd_bus *bus = sd_bus_message_get_bus(m);
+ char buf[FORMAT_TIMESTAMP_MAX];
int relative, interactive, r;
Context *c = userdata;
int64_t utc;
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR,
"REALTIME="USEC_FMT, timespec_load(&ts),
- LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)));
+ LOG_MESSAGE("Changed local time to %s", strnull(format_timestamp(buf, sizeof(buf), timespec_load(&ts)))));
return sd_bus_reply_method_return(m, NULL);
}
return log_error_errno(r, "%s does not exist and cannot be created as the file system is read-only.", path);
if (k < 0)
return log_error_errno(k, "Failed to check if %s exists: %m", path);
- if (!k) {
- log_warning("\"%s\" already exists and is not a directory.", path);
- return -EEXIST;
- }
+ if (!k)
+ return log_warning_errno(SYNTHETIC_ERRNO(EEXIST),
+ "\"%s\" already exists and is not a directory.", path);
*creation = CREATION_EXISTING;
} else
}
if (r < 0)
return log_error_errno(r, "is_dir() failed on path %s: %m", path);
- if (r == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "'%s' already exists and is not a directory.",
- path);
+ if (r == 0) {
+ log_warning("\"%s\" already exists and is not a directory.", path);
+ return 0;
+ }
return path_set_perms(i, path);
}
return log_error_errno(r, "Failed to create device node \"%s\": %m", i->path);
creation = CREATION_FORCE;
} else {
- log_debug("%s is not a device node.", i->path);
+ log_warning("\"%s\" already exists is not a device node.", i->path);
return 0;
}
} else
/* Also log about this briefly. We do so at LOG_NOTICE level, as we fixed up the situation automatically, hence
* there's no immediate need for action by the user. However, in the interest of making things less confusing
* to the user, let's still inform the user that these snippets should really be updated. */
- log_syntax(NULL, LOG_NOTICE, fname, line, 0, "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", *path, n);
+ log_syntax(NULL, LOG_NOTICE, fname, line, 0,
+ "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.",
+ *path, n);
free_and_replace(*path, n);
_cleanup_fclose_ FILE *_f = NULL;
unsigned v = 0;
FILE *f;
- Item *i;
+ ItemArray *ia;
int r = 0;
assert(fn);
}
/* we have to determine age parameter for each entry of type X */
- ORDERED_HASHMAP_FOREACH(i, globs) {
- Item *j, *candidate_item = NULL;
-
- if (i->type != IGNORE_DIRECTORY_PATH)
- continue;
+ ORDERED_HASHMAP_FOREACH(ia, globs)
+ for (size_t ni = 0; ni < ia->n_items; ni++) {
+ ItemArray *ja;
+ Item *i = ia->items + ni, *candidate_item = NULL;
- ORDERED_HASHMAP_FOREACH(j, items) {
- if (!IN_SET(j->type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA))
+ if (i->type != IGNORE_DIRECTORY_PATH)
continue;
- if (path_equal(j->path, i->path)) {
- candidate_item = j;
- break;
- }
+ ORDERED_HASHMAP_FOREACH(ja, items)
+ for (size_t nj = 0; nj < ja->n_items; nj++) {
+ Item *j = ja->items + nj;
- if ((!candidate_item && path_startswith(i->path, j->path)) ||
- (candidate_item && path_startswith(j->path, candidate_item->path) && (fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)))
- candidate_item = j;
- }
+ if (!IN_SET(j->type, CREATE_DIRECTORY,
+ TRUNCATE_DIRECTORY,
+ CREATE_SUBVOLUME,
+ CREATE_SUBVOLUME_INHERIT_QUOTA,
+ CREATE_SUBVOLUME_NEW_QUOTA))
+ continue;
+
+ if (path_equal(j->path, i->path)) {
+ candidate_item = j;
+ break;
+ }
- if (candidate_item && candidate_item->age_set) {
- i->age = candidate_item->age;
- i->age_set = true;
+ if (candidate_item
+ ? (path_startswith(j->path, candidate_item->path) && fnmatch(i->path, j->path, FNM_PATHNAME | FNM_PERIOD) == 0)
+ : path_startswith(i->path, j->path) != NULL)
+ candidate_item = j;
+ }
+
+ if (candidate_item && candidate_item->age_set) {
+ i->age = candidate_item->age;
+ i->age_set = true;
+ }
}
- }
if (ferror(f)) {
log_error_errno(errno, "Failed to read from file %s: %m", fn);
#include "alloc-util.h"
#include "build.h"
#include "device-nodes.h"
+#include "extract-word.h"
#include "fd-util.h"
+#include "fileio.h"
#include "scsi_id.h"
#include "string-util.h"
+#include "strv.h"
#include "strxcpyx.h"
#include "udev-util.h"
strscpy(to, len, type);
}
-/*
- * get_value:
- *
- * buf points to an '=' followed by a quoted string ("foo") or a string ending
- * with a space or ','.
- *
- * Return a pointer to the NUL terminated string, returns NULL if no
- * matches.
- */
-static char *get_value(char **buffer) {
- static const char *quote_string = "\"\n";
- static const char *comma_string = ",\n";
- char *val;
- const char *end;
-
- if (**buffer == '"') {
- /*
- * skip leading quote, terminate when quote seen
- */
- (*buffer)++;
- end = quote_string;
- } else
- end = comma_string;
- val = strsep(buffer, end);
- if (val && end == quote_string)
- /*
- * skip trailing quote
- */
- (*buffer)++;
-
- while (isspace(**buffer))
- (*buffer)++;
-
- return val;
-}
-
-static int argc_count(char *opts) {
- int i = 0;
- while (*opts != '\0')
- if (*opts++ == ' ')
- i++;
- return i;
-}
-
/*
* get_file_options:
*
*/
static int get_file_options(const char *vendor, const char *model,
int *argc, char ***newargv) {
- _cleanup_free_ char *buffer = NULL;
+ _cleanup_free_ char *vendor_in = NULL, *model_in = NULL, *options_in = NULL; /* read in from file */
+ _cleanup_strv_free_ char **options_argv = NULL;
_cleanup_fclose_ FILE *f;
- char *buf;
- char *str1;
- char *vendor_in, *model_in, *options_in; /* read in from file */
- int lineno;
- int c;
- int retval = 0;
+ int lineno, r;
f = fopen(config_file, "re");
if (!f) {
}
}
- /*
- * Allocate a buffer rather than put it on the stack so we can
- * keep it around to parse any options (any allocated newargv
- * points into this buffer for its strings).
- */
- buffer = malloc(MAX_BUFFER_LEN);
- if (!buffer)
- return log_oom();
-
*newargv = NULL;
lineno = 0;
for (;;) {
+ _cleanup_free_ char *buffer = NULL, *key = NULL, *value = NULL;
+ const char *buf;
+
vendor_in = model_in = options_in = NULL;
- buf = fgets(buffer, MAX_BUFFER_LEN, f);
- if (!buf)
+ r = read_line(f, MAX_BUFFER_LEN, &buffer);
+ if (r < 0)
+ return log_error_errno(r, "read_line() on line %d of %s failed: %m", lineno, config_file);
+ if (r == 0)
break;
+ buf = buffer;
lineno++;
- if (buf[strlen(buffer) - 1] != '\n') {
- log_error("Config file line %d too long", lineno);
- break;
- }
while (isspace(*buf))
buf++;
if (*buf == '#')
continue;
- str1 = strsep(&buf, "=");
- if (str1 && strcaseeq(str1, "VENDOR")) {
- str1 = get_value(&buf);
- if (!str1) {
- retval = log_oom();
- break;
- }
- vendor_in = str1;
-
- str1 = strsep(&buf, "=");
- if (str1 && strcaseeq(str1, "MODEL")) {
- str1 = get_value(&buf);
- if (!str1) {
- retval = log_oom();
- break;
- }
- model_in = str1;
- str1 = strsep(&buf, "=");
- }
- }
+ r = extract_many_words(&buf, "=\",\n", 0, &key, &value, NULL);
+ if (r < 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error parsing config file line %d '%s'", lineno, buffer);
- if (str1 && strcaseeq(str1, "OPTIONS")) {
- str1 = get_value(&buf);
- if (!str1) {
- retval = log_oom();
- break;
+ if (strcaseeq(key, "VENDOR")) {
+ vendor_in = TAKE_PTR(value);
+
+ key = mfree(key);
+ r = extract_many_words(&buf, "=\",\n", 0, &key, &value, NULL);
+ if (r < 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error parsing config file line %d '%s'", lineno, buffer);
+
+ if (strcaseeq(key, "MODEL")) {
+ model_in = TAKE_PTR(value);
+
+ key = mfree(key);
+ r = extract_many_words(&buf, "=\",\n", 0, &key, &value, NULL);
+ if (r < 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error parsing config file line %d '%s'", lineno, buffer);
}
- options_in = str1;
}
+ if (strcaseeq(key, "OPTIONS"))
+ options_in = TAKE_PTR(value);
+
/*
* Only allow: [vendor=foo[,model=bar]]options=stuff
*/
- if (!options_in || (!vendor_in && model_in)) {
- log_error("Error parsing config file line %d '%s'", lineno, buffer);
- retval = -1;
- break;
- }
+ if (!options_in || (!vendor_in && model_in))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Error parsing config file line %d '%s'", lineno, buffer);
if (!vendor) {
if (!vendor_in)
break;
*/
break;
}
- }
- if (retval == 0) {
- if (vendor_in != NULL || model_in != NULL ||
- options_in != NULL) {
- /*
- * Something matched. Allocate newargv, and store
- * values found in options_in.
- */
- strcpy(buffer, options_in);
- c = argc_count(buffer) + 2;
- *newargv = calloc(c, sizeof(**newargv));
- if (!*newargv)
- retval = log_oom();
- else {
- *argc = c;
- c = 0;
- /*
- * argv[0] at 0 is skipped by getopt, but
- * store the buffer address there for
- * later freeing
- */
- (*newargv)[c] = buffer;
- for (c = 1; c < *argc; c++)
- (*newargv)[c] = strsep(&buffer, " \t");
- buffer = NULL;
- }
- } else {
- /* No matches */
- retval = 1;
- }
+ vendor_in = mfree(vendor_in);
+ model_in = mfree(model_in);
+ options_in = mfree(options_in);
+
}
- return retval;
+
+ if (vendor_in == NULL && model_in == NULL && options_in == NULL)
+ return 1; /* No matches */
+
+ /*
+ * Something matched. Allocate newargv, and store
+ * values found in options_in.
+ */
+ options_argv = strv_split(options_in, " \t");
+ if (!options_argv)
+ return log_oom();
+ r = strv_prepend(&options_argv, ""); /* getopt skips over argv[0] */
+ if (r < 0)
+ return r;
+ *newargv = TAKE_PTR(options_argv);
+ *argc = strv_length(*newargv);
+
+ return 0;
}
static void help(void) {
}
static int per_dev_options(struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) {
+ _cleanup_strv_free_ char **newargv = NULL;
int retval;
int newargc;
- char **newargv = NULL;
int option;
*good_bad = all_good;
}
}
- if (newargv) {
- free(newargv[0]);
- free(newargv);
- }
return retval;
}
}
int main(int argc, char **argv) {
+ _cleanup_strv_free_ char **newargv = NULL;
int retval = 0;
char maj_min_dev[MAX_PATH_LEN];
int newargc;
- char **newargv = NULL;
log_set_target(LOG_TARGET_AUTO);
udev_parse_config();
retval = scsi_id(maj_min_dev);
exit:
- if (newargv) {
- free(newargv[0]);
- free(newargv);
- }
log_close();
return retval;
}
#include <linux/pci_regs.h>
#include "alloc-util.h"
+#include "device-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
return strneq(p + 2, "04", 2);
}
+static int parse_hotplug_slot_from_function_id(sd_device *dev, const char *slots, uint32_t *ret) {
+ uint64_t function_id;
+ char path[PATH_MAX];
+ const char *attr;
+ int r;
+
+ /* The <sysname>/function_id attribute is unique to the s390 PCI driver. If present, we know
+ * that the slot's directory name for this device is /sys/bus/pci/XXXXXXXX/ where XXXXXXXX is
+ * the fixed length 8 hexadecimal character string representation of function_id. Therefore we
+ * can short cut here and just check for the existence of the slot directory. As this directory
+ * has to exist, we're emitting a debug message for the unlikely case it's not found. Note that
+ * the domain part doesn't belong to the slot name here because there's a 1-to-1 relationship
+ * between PCI function and its hotplug slot. */
+
+ assert(dev);
+ assert(slots);
+ assert(ret);
+
+ if (!naming_scheme_has(NAMING_SLOT_FUNCTION_ID))
+ return 0;
+
+ if (sd_device_get_sysattr_value(dev, "function_id", &attr) < 0)
+ return 0;
+
+ r = safe_atou64(attr, &function_id);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to parse function_id, ignoring: %s", attr);
+
+ if (function_id <= 0 || function_id > UINT32_MAX)
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL),
+ "Invalid function id (0x%"PRIx64"), ignoring.",
+ function_id);
+
+ if (!snprintf_ok(path, sizeof path, "%s/%08"PRIx64, slots, function_id))
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENAMETOOLONG),
+ "PCI slot path is too long, ignoring.");
+
+ if (access(path, F_OK) < 0)
+ return log_device_debug_errno(dev, errno, "Cannot access %s, ignoring: %m", path);
+
+ *ret = (uint32_t) function_id;
+ return 1;
+}
+
static int dev_pci_slot(sd_device *dev, struct netnames *names) {
- unsigned long dev_port = 0;
- unsigned domain, bus, slot, func;
- int hotplug_slot = -1;
- size_t l;
- char *s;
const char *sysname, *attr, *port_name = NULL, *syspath;
_cleanup_(sd_device_unrefp) sd_device *pci = NULL;
- sd_device *hotplug_slot_dev;
- char slots[PATH_MAX];
_cleanup_closedir_ DIR *dir = NULL;
- struct dirent *dent;
+ unsigned domain, bus, slot, func;
+ sd_device *hotplug_slot_dev;
+ unsigned long dev_port = 0;
+ uint32_t hotplug_slot = 0;
+ char slots[PATH_MAX], *s;
+ size_t l;
int r;
r = sd_device_get_sysname(names->pcidev, &sysname);
hotplug_slot_dev = names->pcidev;
while (hotplug_slot_dev) {
- if (sd_device_get_sysname(hotplug_slot_dev, &sysname) < 0)
- continue;
+ struct dirent *dent;
+
+ r = parse_hotplug_slot_from_function_id(hotplug_slot_dev, slots, &hotplug_slot);
+ if (r < 0)
+ return 0;
+ if (r > 0) {
+ domain = 0; /* See comments in parse_hotplug_slot_from_function_id(). */
+ break;
+ }
+
+ r = sd_device_get_sysname(hotplug_slot_dev, &sysname);
+ if (r < 0)
+ return log_device_debug_errno(hotplug_slot_dev, r, "Failed to get sysname: %m");
FOREACH_DIRENT_ALL(dent, dir, break) {
- int i;
- char str[PATH_MAX];
_cleanup_free_ char *address = NULL;
+ char str[PATH_MAX];
+ uint32_t i;
if (dot_or_dot_dot(dent->d_name))
continue;
- r = safe_atoi(dent->d_name, &i);
+ r = safe_atou32(dent->d_name, &i);
if (r < 0 || i <= 0)
continue;
* devices that will try to claim the same index and that would create name
* collision. */
if (naming_scheme_has(NAMING_BRIDGE_NO_SLOT) && is_pci_bridge(hotplug_slot_dev))
- hotplug_slot = 0;
+ return 0;
break;
}
}
- if (hotplug_slot >= 0)
+ if (hotplug_slot > 0)
break;
if (sd_device_get_parent_with_subsystem_devtype(hotplug_slot_dev, "pci", NULL, &hotplug_slot_dev) < 0)
break;
l = sizeof(names->pci_slot);
if (domain > 0)
l = strpcpyf(&s, l, "P%d", domain);
- l = strpcpyf(&s, l, "s%d", hotplug_slot);
+ l = strpcpyf(&s, l, "s%"PRIu32, hotplug_slot);
if (func > 0 || is_pci_multifunction(names->pcidev))
l = strpcpyf(&s, l, "f%d", func);
if (port_name)
udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str);
if (names.pci_slot[0] &&
- snprintf(str, sizeof str, "%s%s%s", prefix, names.pci_slot, names.bcma_core))
+ snprintf_ok(str, sizeof str, "%s%s%s", prefix, names.pci_slot, names.bcma_core))
udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str);
return 0;
}
# only found from the console during the poweroff.
rm -f /tmp/honorfirstshutdown.log >/dev/null
-do_test "$@" 52 > /tmp/honorfirstshutdown.log
+do_test "$@" 52 >/tmp/honorfirstshutdown.log
--- /dev/null
+service
+Accept=
+AccuracySec=
+After=
+Alias=
+AllowedCPUs=
+AllowedMemoryNodes=
+AllowIsolate=
+Also=
+AmbientCapabilities=
+AssertACPower=
+AssertArchitecture=
+AssertCapability=
+AssertControlGroupController=
+AssertDirectoryNotEmpty=
+AssertFileIsExecutable=
+AssertFileNotEmpty=
+AssertFirstBoot=
+AssertGroup=
+AssertHost=
+AssertKernelCommandLine=
+AssertKernelVersion=
+AssertNeedsUpdate=
+AssertPathExists=
+AssertPathExistsGlob=
+AssertPathIsDirectory=
+AssertPathIsMountPoint=
+AssertPathIsReadWrite=
+AssertPathIsSymbolicLink=
+AssertSecurity=
+AssertUser=
+AssertVirtualization=
+Backlog=
+Before=
+BindIPv6Only=
+BindPaths=
+BindReadOnlyPaths=
+BindToDevice=
+BindsTo=
+BlockIOAccounting=
+BlockIODeviceWeight=
+BlockIOReadBandwidth=
+BlockIOWeight=
+BlockIOWriteBandwidth=
+Broadcast=
+BusName=
+CoredumpFilter=
+CPUAccounting=
+CPUQuota=
+CPUShares=
+CPUWeight=
+CapabilityBoundingSet=
+CollectMode=
+ConditionACPower=
+ConditionArchitecture=
+ConditionCapability=
+ConditionControlGroupController=
+ConditionDirectoryNotEmpty=
+ConditionFileIsExecutable=
+ConditionFileNotEmpty=
+ConditionFirstBoot=
+ConditionGroup=
+ConditionHost=
+ConditionKernelCommandLine=
+ConditionKernelVersion=
+ConditionNeedsUpdate=
+ConditionPathExists=
+ConditionPathExistsGlob=
+ConditionPathIsDirectory=
+ConditionPathIsMountPoint=
+ConditionPathIsReadWrite=
+ConditionPathIsSymbolicLink=
+ConditionSecurity=
+ConditionUser=
+ConditionVirtualization=
+Conflicts=
+DefaultDependencies=
+DefaultInstance=
+DeferAcceptSec=
+Delegate=
+Description=
+DeviceAllow=
+DevicePolicy=
+DirectoryMode=
+DirectoryNotEmpty=
+Documentation=
+DynamicUser=
+ExecPaths=
+ExecReload=
+ExecCondition=
+ExecStart=
+ExecStartPost=
+ExecStartPre=
+ExecStop=
+ExecStopPost=
+ExecStopPre=
+ExitType=
+FailureAction=
+FileDescriptorName=
+FileDescriptorStoreMax=
+ForceUnmount=
+FreeBind=
+Group=
+GuessMainPID=
+IOAccounting=
+IODeviceWeight=
+IOReadBandwidthMax=
+IOReadIOPSMax=
+IOWeight=
+IOWriteBandwidthMax=
+IOWriteIOPSMax=
+IPAccounting=
+IPAddressAllow=
+IPAddressDeny=
+IPCNamespacePath=
+IPTOS=
+IPTTL=
+IgnoreOnIsolate=
+JobRunningTimeoutSec=
+JobTimeoutAction=
+JobTimeoutRebootArgument=
+JobTimeoutSec=
+JoinsNamespaceOf=
+KeepAlive=
+KeepAliveIntervalSec=
+KeepAliveProbes=
+KeepAliveTimeSec=
+KillMode=
+KillSignal=
+LazyUnmount=
+ListenDatagram=
+ListenFIFO=
+ListenMessageQueue=
+#ListenNetlink=
+ListenSequentialPacket=
+ListenSpecial=
+ListenStream=
+ListenUSBFunction=
+MakeDirectory=
+Mark=
+MaxConnections=
+MaxConnectionsPerSource=
+ManagedOOMSwap=
+ManagedOOMMemoryPressure=
+ManagedOOMMemoryPressureLimitPercent=
+ManagedOOMPreference=
+MemoryAccounting=
+MemoryHigh=
+MemoryLimit=
+MemoryLow=
+MemoryMax=
+MemorySwapMax=
+MessageQueueMaxMessages=
+MessageQueueMessageSize=
+MountAPIVFS=
+NetworkNamespacePath=
+NoDelay=
+NoExecPaths=
+NoNewPrivileges=
+NonBlocking=
+NotifyAccess=
+OnActiveSec=
+OnBootSec=
+OnCalendar=
+OnFailure=
+OnFailureJobMode=
+OnStartupSec=
+OnUnitActiveSec=
+OnUnitInactiveSec=
+Options=
+PAMName=
+PIDFile=
+PartOf=
+PassCredentials=
+PassSecurity=
+PassPacketInfo=
+PathChanged=
+PathExists=
+PathExistsGlob=
+PathModified=
+PermissionsStartOnly=
+Persistent=
+PipeSize=
+Priority=
+PropagatesReloadTo=
+RandomizedDelaySec=
+FixedRandomDelay=
+RebootArgument=
+ReceiveBuffer=
+RefuseManualStart=
+RefuseManualStop=
+ReloadPropagatedFrom=
+RemainAfterElapse=
+RemainAfterExit=
+RemoveOnStop=
+RequiredBy=
+Requires=
+RequiresMountsFor=
+Requisite=
+Restart=
+RestartForceExitStatus=
+RestartKillSignal=
+RestartPreventExitStatus=
+RestartSec=
+ReusePort=
+RootDirectory=
+RootDirectoryStartOnly=
+RootImage=
+RootHash=
+RootHashSignature=
+RootVerity=
+ExtensionImages=
+RuntimeMaxSec=
+SELinuxContextFromNet=
+SecureBits=
+SendBuffer=
+SendSIGHUP=
+SendSIGKILL=
+Service=
+Slice=
+SloppyOptions=
+SmackLabel=
+SmackLabelIPIn=
+SmackLabelIPOut=
+SocketGroup=
+SocketMode=
+SocketProtocol=
+SocketUser=
+Sockets=
+SourcePath=
+StartLimitAction=
+StartLimitBurst=
+StartLimitIntervalSec=
+StartupBlockIOWeight=
+StartupCPUShares=
+StartupCPUWeight=
+StartupIOWeight=
+StopWhenUnneeded=
+SuccessAction=
+SuccessExitStatus=
+SupplementaryGroups=
+Symlinks=
+TCPCongestion=
+TasksAccounting=
+TasksMax=
+TimeoutIdleSec=
+TimeoutSec=
+TimeoutStartSec=
+TimeoutStopSec=
+TimeoutAbortSec=
+Transparent=
+TriggerLimitBurst=
+TriggerLimitIntervalSec=
+Type=
+USBFunctionDescriptors=
+USBFunctionStrings=
+Unit=
+User=
+WakeSystem=
+WantedBy=
+Wants=
+WatchdogSec=
+What=
+Where=
+WorkingDirectory=
+Writable=
+fsck.mode=
+fsck.repair=
+fstab=
+locale.LANG=
+locale.LANGUAGE=
+locale.LC_ADDRESS=
+locale.LC_COLLATE=
+locale.LC_CTYPE=
+locale.LC_IDENTIFICATION=
+locale.LC_MEASUREMENT=
+locale.LC_MESSAGES=
+locale.LC_MONETARY=
+locale.LC_NAME=
+locale.LC_NUMERIC=
+locale.LC_PAPER=
+locale.LC_TELEPHONE=
+locale.LC_TIME=
+luks.crypttab=
+luks.key=
+luks.name=
+luks.options=
+luks.uuid=
+luks=
+modules_load=
+mount.usr=
+mount.usrflags=
+mount.usrfstype=
+net.ifnames=
+plymouth.enable=
+quotacheck.mode=
+rd.fstab=
+rd.luks.crypttab=
+rd.luks.key=
+rd.luks.name=
+rd.luks.options=
+rd.luks.uuid=
+rd.luks=
+rd.modules_load=
+rd.systemd.gpt_auto=
+rd.systemd.unit=
+rd.systemd.verity=
+rd.udev.children_max=
+rd.udev.event_timeout=
+rd.udev.exec_delay=
+rd.udev.log_level=
+resume=
+resumeflags=
+root=
+rootflags=
+rootfstype=
+roothash=
+systemd.default_standard_error=
+systemd.default_standard_output=
+systemd.default_timeout_start_sec=
+systemd.firstboot=
+systemd.gpt_auto=
+systemd.journald.forward_to_console=
+systemd.journald.forward_to_kmsg=
+systemd.journald.forward_to_syslog=
+systemd.journald.forward_to_wall=
+systemd.log_level=
+systemd.log_location=
+systemd.log_target=
+systemd.machine_id=
+systemd.mask=
+systemd.restore_state=
+systemd.service_watchdogs=
+systemd.setenv=
+systemd.unit=
+systemd.verity=
+systemd.verity_root_data=
+systemd.verity_root_hash=
+systemd.verity_root_options=
+systemd.volatile=
+systemd.wants=
+systemd.watchdog_device=
+udev.children_max=
+udev.event_timeout=
+udev.exec_delay=
+udev.log_level=
+vconsole.font=
+vconsole.font_map=
+vconsole.font_unimap=
+vconsole.keymap=
+vconsole.keymap_toggle=
+ID_MODEL=
+ID_MODEL_FROM_DATABASE=
+SYSTEMD_ALIAS=
+SYSTEMD_MOUNT_OPTIONS=
+SYSTEMD_MOUNT_WHERE=
+SYSTEMD_READY=
+SYSTEMD_USER_WANTS=
+SYSTEMD_WANTS=
+link_priority=
+static_node=
+string_escape=
+ARP=
+ARPAllTargets=
+ARPIPTargets=
+ARPIntervalSec=
+ARPValidate=
+ActiveSlave=
+AdSelect=
+Address=
+AddressAutoconfiguration=
+AgeingTimeSec=
+Alias=
+AllSlavesActive=
+AllowLocalRemote=
+AllowPortToBeRoot=
+AllowedIPs=
+Anonymize=
+Architecture=
+AutoJoin=
+AutoNegotiation=
+BindCarrier=
+BitsPerSecond=
+Bond=
+Bridge=
+Broadcast=
+Cache=
+CacheFromLocalhost=
+ClientIdentifier=
+ConfigureWithoutCarrier=
+CopyDSCP=
+Cost=
+CriticalConnection=
+DHCP=
+DHCPServer=
+DNS=
+DNSLifetimeSec=
+DNSSEC=
+DNSSECNegativeTrustAnchors=
+DNSStubListener=
+DNSStubListenerExtra=
+DUIDRawData=
+DUIDType=
+DefaultLeaseTimeSec=
+DefaultPVID=
+Description=
+Destination=
+DestinationPort=
+DiscoverPathMTU=
+Domains=
+DownDelaySec=
+Driver=
+Duplex=
+DuplicateAddressDetection=
+EgressUntagged=
+EmitDNS=
+EmitDomains=
+EmitLLDP=
+EmitNTP=
+EmitRouter=
+EmitTimezone=
+EncapsulationLimit=
+Endpoint=
+FDBAgeingSec=
+FailOverMACPolicy=
+FallbackDNS=
+FallbackNTP=
+FastLeave=
+FirewallMark=
+Flags=
+FlowLabel=
+ForwardDelaySec=
+From=
+FwMark=
+GVRP=
+Gateway=
+GatewayOnLink=
+GenericReceiveOffload=
+GenericSegmentationOffload=
+GratuitousARP=
+Group=
+GroupForwardMask=
+GroupPolicyExtension=
+HairPin=
+MulticastToUnicast=
+HelloTimeSec=
+HomeAddress=
+Host=
+Hostname=
+IAID=
+IPForward=
+IPMasquerade=
+IPv4LLRoute=
+IPv4ProxyARP=
+IPv6AcceptRA=
+IPv6DuplicateAddressDetection=
+IPv6FlowLabel=
+IPv6HopLimit=
+IPv6Preference=
+IPv6PrefixDelegation=
+IPv6PrivacyExtensions=
+IPv6ProxyNDP=
+IPv6ProxyNDPAddress=
+IPv6Token=
+Id=
+IncomingInterface=
+Independent=
+InitialAdvertisedReceiveWindow=
+InitialCongestionWindow=
+InputKey=
+InvertRule=
+KernelCommandLine=
+KernelVersion=
+Key=
+Kind=
+L2MissNotification=
+L3MissNotification=
+LACPTransmitRate=
+LLDP=
+LLMNR=
+Label=
+LargeReceiveOffload=
+LearnPacketIntervalSec=
+LinkLocalAddressing=
+ListenPort=
+Local=
+LooseBinding=
+MACAddress=
+MACAddressPolicy=
+MACVLAN=
+MIIMonitorSec=
+MTUBytes=
+MVRP=
+MacLearning=
+ManageTemporaryAddress=
+Managed=
+MaxAgeSec=
+MaxLeaseTimeSec=
+MaximumFDBEntries=
+Metric=
+MinLinks=
+Mode=
+MultiQueue=
+MulticastDNS=
+MulticastQuerier=
+MulticastSnooping=
+NTP=
+Name=
+NamePolicy=
+OnLink=
+OneQueue=
+OriginalName=
+OtherInformation=
+OutgoingInterface=
+OutputKey=
+PVID=
+PacketInfo=
+PacketsPerSlave=
+Path=
+Peer=
+PersistentKeepalive=
+PollIntervalMaxSec=
+PollIntervalMinSec=
+PoolOffset=
+PoolSize=
+Port=
+PortRange=
+PreferredLifetime=
+PreferredLifetimeSec=
+PreferredSource=
+Prefix=
+PrefixRoute=
+PresharedKey=
+PrimaryReselectPolicy=
+PrimarySlave=
+Priority=
+PrivateKey=
+Protocol=
+PublicKey=
+QuickAck=
+RapidCommit=
+ReduceARPProxy=
+Remote=
+RemoteChecksumRx=
+RemoteChecksumTx=
+ReorderHeader=
+RequestBroadcast=
+RequiredForOnline=
+ResendIGMP=
+RootDistanceMaxSec=
+RouteMetric=
+RouteShortCircuit=
+RouteTable=
+RouterLifetimeSec=
+RouterPreference=
+STP=
+Scope=
+SendHostname=
+Source=
+SuppressPrefixLength=
+TCP6SegmentationOffload=
+TCPSegmentationOffload=
+TOS=
+TTL=
+Table=
+Timezone=
+To=
+TransmitHashPolicy=
+Tunnel=
+TxtData=
+TxtText=
+Type=
+TypeOfService=
+UDP6ZeroChecksumRx=
+UDP6ZeroChecksumTx=
+UDPChecksum=
+UDPSegmentationOffload=
+UnicastFlood=
+Unmanaged=
+UpDelaySec=
+UseBPDU=
+UseDNS=
+UseDomains=
+UseHostname=
+UseMTU=
+UseNTP=
+UseRoutes=
+UseTimezone=
+User=
+VLAN=
+VLANFiltering=
+VLANId=
+VNetHeader=
+VRF=
+VXLAN=
+ValidLifetimeSec=
+VendorClassIdentifier=
+Virtualization=
+WakeOnLan=
+Weight=
+CODE_FILE=
+CODE_FUNC=
+CODE_LINE=
+COREDUMP_UNIT=
+COREDUMP_USER_UNIT=
+ERRNO=
+MESSAGE=
+MESSAGE_ID=
+OBJECT_AUDIT_LOGINUID=
+OBJECT_AUDIT_SESSION=
+OBJECT_CMDLINE=
+OBJECT_COMM=
+OBJECT_EXE=
+OBJECT_GID=
+OBJECT_PID=
+OBJECT_SYSTEMD_CGROUP=
+OBJECT_SYSTEMD_OWNER_UID=
+OBJECT_SYSTEMD_SESSION=
+OBJECT_SYSTEMD_UNIT=
+OBJECT_SYSTEMD_USER_UNIT=
+OBJECT_UID=
+PRIORITY=
+SYSLOG_FACILITY=
+SYSLOG_IDENTIFIER=
+SYSLOG_PID=
+_AUDIT_LOGINUID=
+_AUDIT_SESSION=
+_BOOT_ID=
+_CAP_EFFECTIVE=
+_CMDLINE=
+_COMM=
+_EXE=
+_GID=
+_HOSTNAME=
+_KERNEL_DEVICE=
+_KERNEL_SUBSYSTEM=
+_LINE_BREAK=
+_MACHINE_ID=
+_PID=
+_SELINUX_CONTEXT=
+_SOURCE_REALTIME_TIMESTAMP=
+_STREAM_ID=
+_SYSTEMD_CGROUP=
+_SYSTEMD_INVOCATION_ID=
+_SYSTEMD_OWNER_UID=
+_SYSTEMD_SESSION=
+_SYSTEMD_SLICE=
+_SYSTEMD_UNIT=
+_SYSTEMD_USER_UNIT=
+_TRANSPORT=
+_UDEV_DEVLINK=
+_UDEV_DEVNODE=
+_UDEV_SYSNAME=
+_UID=
+__CURSOR=
+__MONOTONIC_TIMESTAMP=
+__REALTIME_TIMESTAMP=
+class=
+type=
+cipher=
+hash=
+header=
+key-slot=
+keyfile-offset=
+keyfile-size=
+offset=
+size=
+skip=
+tcrypt-keyfile=
+timeout=
+tries=
+x-systemd.after=
+x-systemd.before=
+x-systemd.device-timeout=
+x-systemd.idle-timeout=
+x-systemd.mount-timeout=
+x-systemd.requires-mounts-for=
+x-systemd.requires=
+CPUAffinity=
+CapabilityBoundingSet=
+CrashChangeVT=
+CrashReboot=
+CrashShell=
+CtrlAltDelBurstAction=
+DefaultBlockIOAccounting=
+DefaultCPUAccounting=
+DefaultEnvironment=
+DefaultIPAccounting=
+DefaultLimitAS=
+DefaultLimitCORE=
+DefaultLimitCPU=
+DefaultLimitDATA=
+DefaultLimitFSIZE=
+DefaultLimitLOCKS=
+DefaultLimitMEMLOCK=
+DefaultLimitMSGQUEUE=
+DefaultLimitNICE=
+DefaultLimitNOFILE=
+DefaultLimitNPROC=
+DefaultLimitRSS=
+DefaultLimitRTPRIO=
+DefaultLimitRTTIME=
+DefaultLimitSIGPENDING=
+DefaultLimitSTACK=
+DefaultMemoryAccounting=
+DefaultRestartSec=
+DefaultStandardError=
+DefaultStandardOutput=
+DefaultStartLimitBurst=
+DefaultStartLimitIntervalSec=
+DefaultTasksAccounting=
+DefaultTasksMax=
+DefaultTimeoutStartSec=
+DefaultTimeoutStopSec=
+DefaultTimeoutAbortSec=
+DefaultTimerAccuracySec=
+DumpCore=
+HibernateMode=
+HibernateState=
+HybridSleepMode=
+HybridSleepState=
+LogColor=
+LogLevel=
+LogLocation=
+LogTarget=
+RuntimeWatchdogSec=
+ShowStatus=
+RebootWatchdogSec=
+ShutdownWatchdogSec=
+KExecWatchdogSec=
+SuspendMode=
+SuspendState=
+SystemCallArchitectures=
+TimerSlackNSec=
+WatchdogDevice=
+-N=
+-c=
+-e=
+-t=
+ANSI_COLOR=
+AppArmorProfile=
+BUG_REPORT_URL=
+BUILD_ID=
+Bind=
+BindReadOnly=
+Boot=
+Bridge=
+CHASSIS=
+CPE_NAME=
+CPUAffinity=
+CPUSchedulingPolicy=
+CPUSchedulingPriority=
+CPUSchedulingResetOnFork=
+CacheDirectory=
+CacheDirectoryMode=
+Capability=
+Compress=
+ConfigurationDirectory=
+ConfigurationDirectoryMode=
+DEPLOYMENT=
+DropCapability=
+Environment=
+EnvironmentFile=
+ExternalSizeMax=
+FONT=
+FONT_MAP=
+FONT_UNIMAP=
+ForwardToConsole=
+ForwardToKMsg=
+ForwardToSyslog=
+ForwardToWall=
+HOME_URL=
+HandleHibernateKey=
+HandleLidSwitch=
+HandleLidSwitchDocked=
+HandleLidSwitchExternalPower=
+HandlePowerKey=
+HandleSuspendKey=
+HibernateKeyIgnoreInhibited=
+HoldoffTimeoutSec=
+ICON_NAME=
+ID=
+ID_LIKE=
+IOSchedulingClass=
+IOSchedulingPriority=
+IPVLAN=
+IdleAction=
+IdleActionSec=
+IgnoreSIGPIPE=
+InaccessiblePaths=
+InhibitDelayMaxSec=
+InhibitorsMax=
+Interface=
+JournalSizeMax=
+KEYMAP=
+KEYMAP_TOGGLE=
+KeepFree=
+KeyringMode=
+ProtectProc=
+ProcSubset=
+KillExcludeUsers=
+KillOnlyUsers=
+KillSignal=
+WatchdogSignal=
+KillUserProcesses=
+LOCATION=
+LidSwitchIgnoreInhibited=
+LimitAS=
+LimitCORE=
+LimitCPU=
+LimitDATA=
+LimitFSIZE=
+LimitLOCKS=
+LimitMEMLOCK=
+LimitMSGQUEUE=
+LimitNICE=
+LimitNOFILE=
+LimitNPROC=
+LimitRSS=
+LimitRTPRIO=
+LimitRTTIME=
+LimitSIGPENDING=
+LimitSTACK=
+LineMax=
+LockPersonality=
+LogExtraFields=
+LogLevelMax=
+LogRateLimitIntervalSec=
+LogRateLimitBurst=
+LogsDirectory=
+LogsDirectoryMode=
+MACVLAN=
+MachineID=
+MaxFileSec=
+MaxLevelConsole=
+MaxLevelKMsg=
+MaxLevelStore=
+MaxLevelSyslog=
+MaxLevelWall=
+MaxRetentionSec=
+MaxUse=
+MemoryDenyWriteExecute=
+MountFlags=
+NAME=
+NAutoVTs=
+Nice=
+NoNewPrivileges=
+NotifyReady=
+OOMScoreAdjust=
+Overlay=
+OverlayReadOnly=
+PRETTY_HOSTNAME=
+PRETTY_NAME=
+PRIVACY_POLICY_URL=
+Parameters=
+PassEnvironment=
+Personality=
+PivotRoot=
+Port=
+PowerKeyIgnoreInhibited=
+Private=
+PrivateIPC=
+PrivateDevices=
+PrivateNetwork=
+PrivateTmp=
+PrivateUsers=
+PrivateUsersChown=
+ProcessSizeMax=
+ProcessTwo=
+ProtectControlGroups=
+ProtectHome=
+ProtectKernelModules=
+ProtectKernelTunables=
+ProtectSystem=
+RateLimitBurst=
+RateLimitIntervalSec=
+ReadKMsg=
+ReadOnly=
+ReadOnlyPaths=
+ReadWriteOnly=
+ReadWritePaths=
+RemoveIPC=
+ReserveVT=
+RestrictAddressFamilies=
+RestrictNamespaces=
+RestrictRealtime=
+RestrictSUIDSGID=
+RuntimeDirectory=
+RuntimeDirectoryInodesMax=
+RuntimeDirectoryMode=
+RuntimeDirectoryPreserve=
+RuntimeDirectorySize=
+RuntimeKeepFree=
+RuntimeMaxFileSize=
+RuntimeMaxFiles=
+RuntimeMaxUse=
+SELinuxContext=
+SUPPORT_URL=
+Seal=
+ServerCertificateFile=
+ServerKeyFile=
+SessionsMax=
+SmackProcessLabel=
+SplitMode=
+StandardError=
+StandardInput=
+StandardInputData=
+StandardInputText=
+StandardOutput=
+StateDirectory=
+StateDirectoryMode=
+Storage=
+SuspendKeyIgnoreInhibited=
+SyncIntervalSec=
+SyslogFacility=
+SyslogIdentifier=
+SyslogLevel=
+SyslogLevelPrefix=
+SystemCallArchitectures=
+SystemCallErrorNumber=
+SystemCallFilter=
+SystemKeepFree=
+SystemMaxFileSize=
+SystemMaxFiles=
+SystemMaxUse=
+TTYPath=
+TTYReset=
+TTYVHangup=
+TTYVTDisallocate=
+TemporaryFileSystem=
+TimerSlackNSec=
+TrustedCertificateFile=
+UMask=
+URL=
+UnsetEnvironment=
+User=
+UserTasksMax=
+UtmpIdentifier=
+UtmpMode=
+VARIANT=
+VARIANT_ID=
+VERSION=
+VERSION_CODENAME=
+VERSION_ID=
+VirtualEthernet=
+VirtualEthernetExtra=
+Volatile=
+WorkingDirectory=
+Zone=
--- /dev/null
+automount
+[Automount]
+DirectoryMode=
+TimeoutIdleSec=
+Where=
--- /dev/null
+mount
+[Mount]
+AllowedCPUs=
+AllowedMemoryNodes=
+AmbientCapabilities=
+AppArmorProfile=
+BPFProgram=
+BindPaths=
+BindReadOnlyPaths=
+BlockIOAccounting=
+BlockIODeviceWeight=
+BlockIOReadBandwidth=
+BlockIOWeight=
+BlockIOWriteBandwidth=
+CPUAccounting=
+CPUAffinity=
+CPUQuota=
+CPUQuotaPeriodSec=
+CPUSchedulingPolicy=
+CPUSchedulingPriority=
+CPUSchedulingResetOnFork=
+CPUShares=
+CPUWeight=
+CacheDirectory=
+CacheDirectoryMode=
+Capabilities=
+CapabilityBoundingSet=
+ConfigurationDirectory=
+ConfigurationDirectoryMode=
+CoredumpFilter=
+DefaultMemoryLow=
+DefaultMemoryMin=
+Delegate=
+DeviceAllow=
+DevicePolicy=
+DirectoryMode=
+DisableControllers=
+DynamicUser=
+Environment=
+EnvironmentFile=
+ExecPaths=
+ExtensionImages=
+FinalKillSignal=
+ForceUnmount=
+Group=
+IOAccounting=
+IODeviceLatencyTargetSec=
+IODeviceWeight=
+IOReadBandwidthMax=
+IOReadIOPSMax=
+IOSchedulingClass=
+IOSchedulingPriority=
+IOWeight=
+IOWriteBandwidthMax=
+IOWriteIOPSMax=
+IPAccounting=
+IPAddressAllow=
+IPAddressDeny=
+IPCNamespacePath=
+IPEgressFilterPath=
+IPIngressFilterPath=
+IgnoreSIGPIPE=
+InaccessibleDirectories=
+InaccessiblePaths=
+KeyringMode=
+KillMode=
+KillSignal=
+LazyUnmount=
+LimitAS=
+LimitCORE=
+LimitCPU=
+LimitDATA=
+LimitFSIZE=
+LimitLOCKS=
+LimitMEMLOCK=
+LimitMSGQUEUE=
+LimitNICE=
+LimitNOFILE=
+LimitNPROC=
+LimitRSS=
+LimitRTPRIO=
+LimitRTTIME=
+LimitSIGPENDING=
+LimitSTACK=
+LoadCredential=
+LockPersonality=
+LogExtraFields=
+LogLevelMax=
+LogNamespace=
+LogRateLimitBurst=
+LogRateLimitIntervalSec=
+LogsDirectory=
+LogsDirectoryMode=
+ManagedOOMMemoryPressure=
+ManagedOOMMemoryPressureLimit=
+ManagedOOMPreference=
+ManagedOOMSwap=
+MemoryAccounting=
+MemoryDenyWriteExecute=
+MemoryHigh=
+MemoryLimit=
+MemoryLow=
+MemoryMax=
+MemoryMin=
+MemorySwapMax=
+MountAPIVFS=
+MountFlags=
+MountImages=
+NUMAMask=
+NUMAPolicy=
+NetClass=
+NetworkNamespacePath=
+Nice=
+NoExecPaths=
+NoNewPrivileges=
+OOMScoreAdjust=
+Options=
+PAMName=
+PassEnvironment=
+Personality=
+PrivateDevices=
+PrivateIPC=
+PrivateMounts=
+PrivateNetwork=
+PrivateTmp=
+PrivateUsers=
+ProcSubset=
+ProtectClock=
+ProtectControlGroups=
+ProtectHome=
+ProtectHostname=
+ProtectKernelLogs=
+ProtectKernelModules=
+ProtectKernelTunables=
+ProtectProc=
+ProtectSystem=
+ReadOnlyDirectories=
+ReadOnlyPaths=
+ReadWriteDirectories=
+ReadWriteOnly=
+ReadWritePaths=
+RemoveIPC=
+RestartKillSignal=
+RestrictAddressFamilies=
+RestrictNamespaces=
+RestrictRealtime=
+RestrictSUIDSGID=
+RootDirectory=
+RootHash=
+RootHashSignature=
+RootImage=
+RootImageOptions=
+RootVerity=
+RuntimeDirectory=
+RuntimeDirectoryMode=
+RuntimeDirectoryPreserve=
+SELinuxContext=
+SecureBits=
+SendSIGHUP=
+SendSIGKILL=
+SetCredential=
+Slice=
+SloppyOptions=
+SmackProcessLabel=
+StandardError=
+StandardInput=
+StandardInputData=
+StandardInputText=
+StandardOutput=
+StartupBlockIOWeight=
+StartupCPUShares=
+StartupCPUWeight=
+StartupIOWeight=
+StateDirectory=
+StateDirectoryMode=
+SupplementaryGroups=
+SyslogFacility=
+SyslogIdentifier=
+SyslogLevel=
+SyslogLevelPrefix=
+SystemCallArchitectures=
+SystemCallErrorNumber=
+SystemCallFilter=
+SystemCallLog=
+TTYPath=
+TTYReset=
+TTYVHangup=
+TTYVTDisallocate=
+TasksAccounting=
+TasksMax=
+TemporaryFileSystem=
+TimeoutCleanSec=
+TimeoutSec=
+TimerSlackNSec=
+Type=
+UMask=
+UnsetEnvironment=
+User=
+UtmpIdentifier=
+UtmpMode=
+WatchdogSignal=
+What=
+Where=
+WorkingDirectory=
--- /dev/null
+path
+[Path]
+DirectoryMode=
+DirectoryNotEmpty=
+MakeDirectory=
+PathChanged=
+PathExists=
+PathExistsGlob=
+PathModified=
+Unit=
scope
+[Scope]
+AllowedCPUs=
+AllowedMemoryNodes=
+BPFProgram=
+BlockIOAccounting=
+BlockIODeviceWeight=
+BlockIOReadBandwidth=
+BlockIOWeight=
+BlockIOWriteBandwidth=
+CPUAccounting=
+CPUQuota=
+CPUQuotaPeriodSec=
+CPUShares=
+CPUWeight=
+DefaultMemoryLow=
+DefaultMemoryMin=
+Delegate=
+DeviceAllow=
+DevicePolicy=
+DisableControllers=
+FinalKillSignal=
+IOAccounting=
+IODeviceLatencyTargetSec=
+IODeviceWeight=
+IOReadBandwidthMax=
+IOReadIOPSMax=
+IOWeight=
+IOWriteBandwidthMax=
+IOWriteIOPSMax=
+IPAccounting=
+IPAddressAllow=
+IPAddressDeny=
+IPEgressFilterPath=
+IPIngressFilterPath=
+KillMode=
+KillSignal=
+ManagedOOMMemoryPressure=
+ManagedOOMMemoryPressureLimit=
+ManagedOOMPreference=
+ManagedOOMSwap=
+MemoryAccounting=
+MemoryHigh=
+MemoryLimit=
+MemoryLow=
+MemoryMax=
+MemoryMin=
+MemorySwapMax=
+NetClass=
+RestartKillSignal=
RuntimeMaxSec=
+SendSIGHUP=
+SendSIGKILL=
+Slice=
+StartupBlockIOWeight=
+StartupCPUShares=
+StartupCPUWeight=
+StartupIOWeight=
+TasksAccounting=
+TasksMax=
+TimeoutStopSec=
+WatchdogSignal=
service
-Accept=
-AccuracySec=
+[Unit]
After=
-Alias=
-AllowedCPUs=
-AllowedMemoryNodes=
AllowIsolate=
-Also=
-AmbientCapabilities=
AssertACPower=
AssertArchitecture=
+AssertCPUs=
AssertCapability=
AssertControlGroupController=
AssertDirectoryNotEmpty=
+AssertEnvironment=
AssertFileIsExecutable=
AssertFileNotEmpty=
AssertFirstBoot=
AssertHost=
AssertKernelCommandLine=
AssertKernelVersion=
+AssertMemory=
AssertNeedsUpdate=
AssertPathExists=
AssertPathExistsGlob=
AssertPathIsDirectory=
+AssertPathIsEncrypted=
AssertPathIsMountPoint=
AssertPathIsReadWrite=
AssertPathIsSymbolicLink=
AssertSecurity=
AssertUser=
AssertVirtualization=
-Backlog=
+BPFProgram=
Before=
-BindIPv6Only=
-BindPaths=
-BindReadOnlyPaths=
-BindToDevice=
+BindTo=
BindsTo=
-BlockIOAccounting=
-BlockIODeviceWeight=
-BlockIOReadBandwidth=
-BlockIOWeight=
-BlockIOWriteBandwidth=
-Broadcast=
-BusName=
-CoredumpFilter=
-CPUAccounting=
-CPUQuota=
-CPUShares=
-CPUWeight=
-CapabilityBoundingSet=
CollectMode=
ConditionACPower=
ConditionArchitecture=
+ConditionCPUs=
ConditionCapability=
ConditionControlGroupController=
ConditionDirectoryNotEmpty=
+ConditionEnvironment=
ConditionFileIsExecutable=
ConditionFileNotEmpty=
ConditionFirstBoot=
ConditionHost=
ConditionKernelCommandLine=
ConditionKernelVersion=
+ConditionMemory=
ConditionNeedsUpdate=
ConditionPathExists=
ConditionPathExistsGlob=
ConditionPathIsDirectory=
+ConditionPathIsEncrypted=
ConditionPathIsMountPoint=
ConditionPathIsReadWrite=
ConditionPathIsSymbolicLink=
ConditionVirtualization=
Conflicts=
DefaultDependencies=
-DefaultInstance=
-DeferAcceptSec=
-Delegate=
Description=
-DeviceAllow=
-DevicePolicy=
-DirectoryMode=
-DirectoryNotEmpty=
Documentation=
-DynamicUser=
-ExecPaths=
-ExecReload=
-ExecCondition=
-ExecStart=
-ExecStartPost=
-ExecStartPre=
-ExecStop=
-ExecStopPost=
-ExecStopPre=
-ExitType=
FailureAction=
-FileDescriptorName=
-FileDescriptorStoreMax=
-ForceUnmount=
-FreeBind=
-Group=
-GuessMainPID=
-IOAccounting=
-IODeviceWeight=
-IOReadBandwidthMax=
-IOReadIOPSMax=
-IOWeight=
-IOWriteBandwidthMax=
-IOWriteIOPSMax=
-IPAccounting=
-IPAddressAllow=
-IPAddressDeny=
-IPCNamespacePath=
-IPTOS=
-IPTTL=
+FailureActionExitStatus=
IgnoreOnIsolate=
+IgnoreOnSnapshot=
JobRunningTimeoutSec=
JobTimeoutAction=
JobTimeoutRebootArgument=
JobTimeoutSec=
JoinsNamespaceOf=
-KeepAlive=
-KeepAliveIntervalSec=
-KeepAliveProbes=
-KeepAliveTimeSec=
-KillMode=
-KillSignal=
-LazyUnmount=
-ListenDatagram=
-ListenFIFO=
-ListenMessageQueue=
-#ListenNetlink=
-ListenSequentialPacket=
-ListenSpecial=
-ListenStream=
-ListenUSBFunction=
-MakeDirectory=
-Mark=
-MaxConnections=
-MaxConnectionsPerSource=
-ManagedOOMSwap=
-ManagedOOMMemoryPressure=
-ManagedOOMMemoryPressureLimitPercent=
-ManagedOOMPreference=
-MemoryAccounting=
-MemoryHigh=
-MemoryLimit=
-MemoryLow=
-MemoryMax=
-MemorySwapMax=
-MessageQueueMaxMessages=
-MessageQueueMessageSize=
-MountAPIVFS=
-NetworkNamespacePath=
-NoDelay=
-NoExecPaths=
-NoNewPrivileges=
-NonBlocking=
-NotifyAccess=
-OnActiveSec=
-OnBootSec=
-OnCalendar=
OnFailure=
+OnFailureIsolate=
OnFailureJobMode=
-OnStartupSec=
-OnUnitActiveSec=
-OnUnitInactiveSec=
-Options=
-PAMName=
-PIDFile=
PartOf=
-PassCredentials=
-PassSecurity=
-PassPacketInfo=
-PathChanged=
-PathExists=
-PathExistsGlob=
-PathModified=
-PermissionsStartOnly=
-Persistent=
-PipeSize=
-Priority=
+PropagateReloadFrom=
+PropagateReloadTo=
PropagatesReloadTo=
-RandomizedDelaySec=
-FixedRandomDelay=
RebootArgument=
-ReceiveBuffer=
RefuseManualStart=
RefuseManualStop=
ReloadPropagatedFrom=
-RemainAfterElapse=
-RemainAfterExit=
-RemoveOnStop=
-RequiredBy=
Requires=
RequiresMountsFor=
+RequiresOverridable=
Requisite=
-Restart=
-RestartForceExitStatus=
-RestartKillSignal=
-RestartPreventExitStatus=
-RestartSec=
-ReusePort=
-RootDirectory=
-RootDirectoryStartOnly=
-RootImage=
-RootHash=
-RootHashSignature=
-RootVerity=
-ExtensionImages=
-RuntimeMaxSec=
-SELinuxContextFromNet=
-SecureBits=
-SendBuffer=
-SendSIGHUP=
-SendSIGKILL=
-Service=
-Slice=
-SloppyOptions=
-SmackLabel=
-SmackLabelIPIn=
-SmackLabelIPOut=
-SocketGroup=
-SocketMode=
-SocketProtocol=
-SocketUser=
-Sockets=
+RequisiteOverridable=
SourcePath=
StartLimitAction=
StartLimitBurst=
+StartLimitInterval=
StartLimitIntervalSec=
-StartupBlockIOWeight=
-StartupCPUShares=
-StartupCPUWeight=
-StartupIOWeight=
StopWhenUnneeded=
SuccessAction=
-SuccessExitStatus=
-SupplementaryGroups=
-Symlinks=
-TCPCongestion=
-TasksAccounting=
-TasksMax=
-TimeoutIdleSec=
-TimeoutSec=
-TimeoutStartSec=
-TimeoutStopSec=
-TimeoutAbortSec=
-Transparent=
-TriggerLimitBurst=
-TriggerLimitIntervalSec=
-Type=
-USBFunctionDescriptors=
-USBFunctionStrings=
-Unit=
-User=
-WakeSystem=
-WantedBy=
+SuccessActionExitStatus=
Wants=
-WatchdogSec=
-What=
-Where=
-WorkingDirectory=
-Writable=
-fsck.mode=
-fsck.repair=
-fstab=
-locale.LANG=
-locale.LANGUAGE=
-locale.LC_ADDRESS=
-locale.LC_COLLATE=
-locale.LC_CTYPE=
-locale.LC_IDENTIFICATION=
-locale.LC_MEASUREMENT=
-locale.LC_MESSAGES=
-locale.LC_MONETARY=
-locale.LC_NAME=
-locale.LC_NUMERIC=
-locale.LC_PAPER=
-locale.LC_TELEPHONE=
-locale.LC_TIME=
-luks.crypttab=
-luks.key=
-luks.name=
-luks.options=
-luks.uuid=
-luks=
-modules_load=
-mount.usr=
-mount.usrflags=
-mount.usrfstype=
-net.ifnames=
-plymouth.enable=
-quotacheck.mode=
-rd.fstab=
-rd.luks.crypttab=
-rd.luks.key=
-rd.luks.name=
-rd.luks.options=
-rd.luks.uuid=
-rd.luks=
-rd.modules_load=
-rd.systemd.gpt_auto=
-rd.systemd.unit=
-rd.systemd.verity=
-rd.udev.children_max=
-rd.udev.event_timeout=
-rd.udev.exec_delay=
-rd.udev.log_level=
-resume=
-resumeflags=
-root=
-rootflags=
-rootfstype=
-roothash=
-systemd.default_standard_error=
-systemd.default_standard_output=
-systemd.default_timeout_start_sec=
-systemd.firstboot=
-systemd.gpt_auto=
-systemd.journald.forward_to_console=
-systemd.journald.forward_to_kmsg=
-systemd.journald.forward_to_syslog=
-systemd.journald.forward_to_wall=
-systemd.log_level=
-systemd.log_location=
-systemd.log_target=
-systemd.machine_id=
-systemd.mask=
-systemd.restore_state=
-systemd.service_watchdogs=
-systemd.setenv=
-systemd.unit=
-systemd.verity=
-systemd.verity_root_data=
-systemd.verity_root_hash=
-systemd.verity_root_options=
-systemd.volatile=
-systemd.wants=
-systemd.watchdog_device=
-udev.children_max=
-udev.event_timeout=
-udev.exec_delay=
-udev.log_level=
-vconsole.font=
-vconsole.font_map=
-vconsole.font_unimap=
-vconsole.keymap=
-vconsole.keymap_toggle=
-ID_MODEL=
-ID_MODEL_FROM_DATABASE=
-SYSTEMD_ALIAS=
-SYSTEMD_MOUNT_OPTIONS=
-SYSTEMD_MOUNT_WHERE=
-SYSTEMD_READY=
-SYSTEMD_USER_WANTS=
-SYSTEMD_WANTS=
-link_priority=
-static_node=
-string_escape=
-ARP=
-ARPAllTargets=
-ARPIPTargets=
-ARPIntervalSec=
-ARPValidate=
-ActiveSlave=
-AdSelect=
-Address=
-AddressAutoconfiguration=
-AgeingTimeSec=
+[Install]
Alias=
-AllSlavesActive=
-AllowLocalRemote=
-AllowPortToBeRoot=
-AllowedIPs=
-Anonymize=
-Architecture=
-AutoJoin=
-AutoNegotiation=
-BindCarrier=
-BitsPerSecond=
-Bond=
-Bridge=
-Broadcast=
-Cache=
-CacheFromLocalhost=
-ClientIdentifier=
-ConfigureWithoutCarrier=
-CopyDSCP=
-Cost=
-CriticalConnection=
-DHCP=
-DHCPServer=
-DNS=
-DNSLifetimeSec=
-DNSSEC=
-DNSSECNegativeTrustAnchors=
-DNSStubListener=
-DNSStubListenerExtra=
-DUIDRawData=
-DUIDType=
-DefaultLeaseTimeSec=
-DefaultPVID=
-Description=
-Destination=
-DestinationPort=
-DiscoverPathMTU=
-Domains=
-DownDelaySec=
-Driver=
-Duplex=
-DuplicateAddressDetection=
-EgressUntagged=
-EmitDNS=
-EmitDomains=
-EmitLLDP=
-EmitNTP=
-EmitRouter=
-EmitTimezone=
-EncapsulationLimit=
-Endpoint=
-FDBAgeingSec=
-FailOverMACPolicy=
-FallbackDNS=
-FallbackNTP=
-FastLeave=
-FirewallMark=
-Flags=
-FlowLabel=
-ForwardDelaySec=
-From=
-FwMark=
-GVRP=
-Gateway=
-GatewayOnLink=
-GenericReceiveOffload=
-GenericSegmentationOffload=
-GratuitousARP=
-Group=
-GroupForwardMask=
-GroupPolicyExtension=
-HairPin=
-MulticastToUnicast=
-HelloTimeSec=
-HomeAddress=
-Host=
-Hostname=
-IAID=
-IPForward=
-IPMasquerade=
-IPv4LLRoute=
-IPv4ProxyARP=
-IPv6AcceptRA=
-IPv6DuplicateAddressDetection=
-IPv6FlowLabel=
-IPv6HopLimit=
-IPv6Preference=
-IPv6PrefixDelegation=
-IPv6PrivacyExtensions=
-IPv6ProxyNDP=
-IPv6ProxyNDPAddress=
-IPv6Token=
-Id=
-IncomingInterface=
-Independent=
-InitialAdvertisedReceiveWindow=
-InitialCongestionWindow=
-InputKey=
-InvertRule=
-KernelCommandLine=
-KernelVersion=
-Key=
-Kind=
-L2MissNotification=
-L3MissNotification=
-LACPTransmitRate=
-LLDP=
-LLMNR=
-Label=
-LargeReceiveOffload=
-LearnPacketIntervalSec=
-LinkLocalAddressing=
-ListenPort=
-Local=
-LooseBinding=
-MACAddress=
-MACAddressPolicy=
-MACVLAN=
-MIIMonitorSec=
-MTUBytes=
-MVRP=
-MacLearning=
-ManageTemporaryAddress=
-Managed=
-MaxAgeSec=
-MaxLeaseTimeSec=
-MaximumFDBEntries=
-Metric=
-MinLinks=
-Mode=
-MultiQueue=
-MulticastDNS=
-MulticastQuerier=
-MulticastSnooping=
-NTP=
-Name=
-NamePolicy=
-OnLink=
-OneQueue=
-OriginalName=
-OtherInformation=
-OutgoingInterface=
-OutputKey=
-PVID=
-PacketInfo=
-PacketsPerSlave=
-Path=
-Peer=
-PersistentKeepalive=
-PollIntervalMaxSec=
-PollIntervalMinSec=
-PoolOffset=
-PoolSize=
-Port=
-PortRange=
-PreferredLifetime=
-PreferredLifetimeSec=
-PreferredSource=
-Prefix=
-PrefixRoute=
-PresharedKey=
-PrimaryReselectPolicy=
-PrimarySlave=
-Priority=
-PrivateKey=
-Protocol=
-PublicKey=
-QuickAck=
-RapidCommit=
-ReduceARPProxy=
-Remote=
-RemoteChecksumRx=
-RemoteChecksumTx=
-ReorderHeader=
-RequestBroadcast=
-RequiredForOnline=
-ResendIGMP=
-RootDistanceMaxSec=
-RouteMetric=
-RouteShortCircuit=
-RouteTable=
-RouterLifetimeSec=
-RouterPreference=
-STP=
-Scope=
-SendHostname=
-Source=
-SuppressPrefixLength=
-TCP6SegmentationOffload=
-TCPSegmentationOffload=
-TOS=
-TTL=
-Table=
-Timezone=
-To=
-TransmitHashPolicy=
-Tunnel=
-TxtData=
-TxtText=
-Type=
-TypeOfService=
-UDP6ZeroChecksumRx=
-UDP6ZeroChecksumTx=
-UDPChecksum=
-UDPSegmentationOffload=
-UnicastFlood=
-Unmanaged=
-UpDelaySec=
-UseBPDU=
-UseDNS=
-UseDomains=
-UseHostname=
-UseMTU=
-UseNTP=
-UseRoutes=
-UseTimezone=
-User=
-VLAN=
-VLANFiltering=
-VLANId=
-VNetHeader=
-VRF=
-VXLAN=
-ValidLifetimeSec=
-VendorClassIdentifier=
-Virtualization=
-WakeOnLan=
-Weight=
-CODE_FILE=
-CODE_FUNC=
-CODE_LINE=
-COREDUMP_UNIT=
-COREDUMP_USER_UNIT=
-ERRNO=
-MESSAGE=
-MESSAGE_ID=
-OBJECT_AUDIT_LOGINUID=
-OBJECT_AUDIT_SESSION=
-OBJECT_CMDLINE=
-OBJECT_COMM=
-OBJECT_EXE=
-OBJECT_GID=
-OBJECT_PID=
-OBJECT_SYSTEMD_CGROUP=
-OBJECT_SYSTEMD_OWNER_UID=
-OBJECT_SYSTEMD_SESSION=
-OBJECT_SYSTEMD_UNIT=
-OBJECT_SYSTEMD_USER_UNIT=
-OBJECT_UID=
-PRIORITY=
-SYSLOG_FACILITY=
-SYSLOG_IDENTIFIER=
-SYSLOG_PID=
-_AUDIT_LOGINUID=
-_AUDIT_SESSION=
-_BOOT_ID=
-_CAP_EFFECTIVE=
-_CMDLINE=
-_COMM=
-_EXE=
-_GID=
-_HOSTNAME=
-_KERNEL_DEVICE=
-_KERNEL_SUBSYSTEM=
-_LINE_BREAK=
-_MACHINE_ID=
-_PID=
-_SELINUX_CONTEXT=
-_SOURCE_REALTIME_TIMESTAMP=
-_STREAM_ID=
-_SYSTEMD_CGROUP=
-_SYSTEMD_INVOCATION_ID=
-_SYSTEMD_OWNER_UID=
-_SYSTEMD_SESSION=
-_SYSTEMD_SLICE=
-_SYSTEMD_UNIT=
-_SYSTEMD_USER_UNIT=
-_TRANSPORT=
-_UDEV_DEVLINK=
-_UDEV_DEVNODE=
-_UDEV_SYSNAME=
-_UID=
-__CURSOR=
-__MONOTONIC_TIMESTAMP=
-__REALTIME_TIMESTAMP=
-class=
-type=
-cipher=
-hash=
-header=
-key-slot=
-keyfile-offset=
-keyfile-size=
-offset=
-size=
-skip=
-tcrypt-keyfile=
-timeout=
-tries=
-x-systemd.after=
-x-systemd.before=
-x-systemd.device-timeout=
-x-systemd.idle-timeout=
-x-systemd.mount-timeout=
-x-systemd.requires-mounts-for=
-x-systemd.requires=
-CPUAffinity=
-CapabilityBoundingSet=
-CrashChangeVT=
-CrashReboot=
-CrashShell=
-CtrlAltDelBurstAction=
-DefaultBlockIOAccounting=
-DefaultCPUAccounting=
-DefaultEnvironment=
-DefaultIPAccounting=
-DefaultLimitAS=
-DefaultLimitCORE=
-DefaultLimitCPU=
-DefaultLimitDATA=
-DefaultLimitFSIZE=
-DefaultLimitLOCKS=
-DefaultLimitMEMLOCK=
-DefaultLimitMSGQUEUE=
-DefaultLimitNICE=
-DefaultLimitNOFILE=
-DefaultLimitNPROC=
-DefaultLimitRSS=
-DefaultLimitRTPRIO=
-DefaultLimitRTTIME=
-DefaultLimitSIGPENDING=
-DefaultLimitSTACK=
-DefaultMemoryAccounting=
-DefaultRestartSec=
-DefaultStandardError=
-DefaultStandardOutput=
-DefaultStartLimitBurst=
-DefaultStartLimitIntervalSec=
-DefaultTasksAccounting=
-DefaultTasksMax=
-DefaultTimeoutStartSec=
-DefaultTimeoutStopSec=
-DefaultTimeoutAbortSec=
-DefaultTimerAccuracySec=
-DumpCore=
-HibernateMode=
-HibernateState=
-HybridSleepMode=
-HybridSleepState=
-LogColor=
-LogLevel=
-LogLocation=
-LogTarget=
-RuntimeWatchdogSec=
-ShowStatus=
-RebootWatchdogSec=
-ShutdownWatchdogSec=
-KExecWatchdogSec=
-SuspendMode=
-SuspendState=
-SystemCallArchitectures=
-TimerSlackNSec=
-WatchdogDevice=
--N=
--c=
--e=
--t=
-ANSI_COLOR=
+Also=
+DefaultInstance=
+RequiredBy=
+WantedBy=
+[Service]
+AllowedCPUs=
+AllowedMemoryNodes=
+AmbientCapabilities=
AppArmorProfile=
-BUG_REPORT_URL=
-BUILD_ID=
-Bind=
-BindReadOnly=
-Boot=
-Bridge=
-CHASSIS=
-CPE_NAME=
+BindPaths=
+BindReadOnlyPaths=
+BlockIOAccounting=
+BlockIODeviceWeight=
+BlockIOReadBandwidth=
+BlockIOWeight=
+BlockIOWriteBandwidth=
+BusName=
+BusPolicy=
+CPUAccounting=
CPUAffinity=
+CPUQuota=
+CPUQuotaPeriodSec=
CPUSchedulingPolicy=
CPUSchedulingPriority=
CPUSchedulingResetOnFork=
+CPUShares=
+CPUWeight=
CacheDirectory=
CacheDirectoryMode=
-Capability=
-Compress=
+Capabilities=
+CapabilityBoundingSet=
ConfigurationDirectory=
ConfigurationDirectoryMode=
-DEPLOYMENT=
-DropCapability=
+CoredumpFilter=
+DefaultMemoryLow=
+DefaultMemoryMin=
+Delegate=
+DeviceAllow=
+DevicePolicy=
+DisableControllers=
+DynamicUser=
Environment=
EnvironmentFile=
-ExternalSizeMax=
-FONT=
-FONT_MAP=
-FONT_UNIMAP=
-ForwardToConsole=
-ForwardToKMsg=
-ForwardToSyslog=
-ForwardToWall=
-HOME_URL=
-HandleHibernateKey=
-HandleLidSwitch=
-HandleLidSwitchDocked=
-HandleLidSwitchExternalPower=
-HandlePowerKey=
-HandleSuspendKey=
-HibernateKeyIgnoreInhibited=
-HoldoffTimeoutSec=
-ICON_NAME=
-ID=
-ID_LIKE=
+ExecCondition=
+ExecPaths=
+ExecReload=
+ExecStart=
+ExecStartPost=
+ExecStartPre=
+ExecStop=
+ExecStopPost=
+ExitType=
+ExtensionImages=
+FailureAction=
+FileDescriptorStoreMax=
+FinalKillSignal=
+Group=
+GuessMainPID=
+IOAccounting=
+IODeviceLatencyTargetSec=
+IODeviceWeight=
+IOReadBandwidthMax=
+IOReadIOPSMax=
IOSchedulingClass=
IOSchedulingPriority=
-IPVLAN=
-IdleAction=
-IdleActionSec=
+IOWeight=
+IOWriteBandwidthMax=
+IOWriteIOPSMax=
+IPAccounting=
+IPAddressAllow=
+IPAddressDeny=
+IPCNamespacePath=
+IPEgressFilterPath=
+IPIngressFilterPath=
IgnoreSIGPIPE=
+InaccessibleDirectories=
InaccessiblePaths=
-InhibitDelayMaxSec=
-InhibitorsMax=
-Interface=
-JournalSizeMax=
-KEYMAP=
-KEYMAP_TOGGLE=
-KeepFree=
KeyringMode=
-ProtectProc=
-ProcSubset=
-KillExcludeUsers=
-KillOnlyUsers=
+KillMode=
KillSignal=
-WatchdogSignal=
-KillUserProcesses=
-LOCATION=
-LidSwitchIgnoreInhibited=
LimitAS=
LimitCORE=
LimitCPU=
LimitRTTIME=
LimitSIGPENDING=
LimitSTACK=
-LineMax=
+LoadCredential=
LockPersonality=
LogExtraFields=
LogLevelMax=
-LogRateLimitIntervalSec=
+LogNamespace=
LogRateLimitBurst=
+LogRateLimitIntervalSec=
LogsDirectory=
LogsDirectoryMode=
-MACVLAN=
-MachineID=
-MaxFileSec=
-MaxLevelConsole=
-MaxLevelKMsg=
-MaxLevelStore=
-MaxLevelSyslog=
-MaxLevelWall=
-MaxRetentionSec=
-MaxUse=
+ManagedOOMMemoryPressure=
+ManagedOOMMemoryPressureLimit=
+ManagedOOMPreference=
+ManagedOOMSwap=
+MemoryAccounting=
MemoryDenyWriteExecute=
+MemoryHigh=
+MemoryLimit=
+MemoryLow=
+MemoryMax=
+MemoryMin=
+MemorySwapMax=
+MountAPIVFS=
MountFlags=
-NAME=
-NAutoVTs=
+MountImages=
+NUMAMask=
+NUMAPolicy=
+NetClass=
+NetworkNamespacePath=
Nice=
+NoExecPaths=
NoNewPrivileges=
-NotifyReady=
+NonBlocking=
+NotifyAccess=
+OOMPolicy=
OOMScoreAdjust=
-Overlay=
-OverlayReadOnly=
-PRETTY_HOSTNAME=
-PRETTY_NAME=
-PRIVACY_POLICY_URL=
-Parameters=
+PAMName=
+PIDFile=
PassEnvironment=
+PermissionsStartOnly=
Personality=
-PivotRoot=
-Port=
-PowerKeyIgnoreInhibited=
-Private=
-PrivateIPC=
PrivateDevices=
+PrivateIPC=
+PrivateMounts=
PrivateNetwork=
PrivateTmp=
PrivateUsers=
-PrivateUsersChown=
-ProcessSizeMax=
-ProcessTwo=
+ProcSubset=
+ProtectClock=
ProtectControlGroups=
ProtectHome=
+ProtectHostname=
+ProtectKernelLogs=
ProtectKernelModules=
ProtectKernelTunables=
+ProtectProc=
ProtectSystem=
-RateLimitBurst=
-RateLimitIntervalSec=
-ReadKMsg=
-ReadOnly=
+ReadOnlyDirectories=
ReadOnlyPaths=
-ReadWriteOnly=
+ReadWriteDirectories=
ReadWritePaths=
+RebootArgument=
+RemainAfterExit=
RemoveIPC=
-ReserveVT=
+Restart=
+RestartForceExitStatus=
+RestartKillSignal=
+RestartPreventExitStatus=
+RestartSec=
RestrictAddressFamilies=
RestrictNamespaces=
RestrictRealtime=
RestrictSUIDSGID=
+RootDirectory=
+RootDirectoryStartOnly=
+RootHash=
+RootHashSignature=
+RootImage=
+RootImageOptions=
+RootVerity=
RuntimeDirectory=
-RuntimeDirectoryInodesMax=
RuntimeDirectoryMode=
RuntimeDirectoryPreserve=
-RuntimeDirectorySize=
-RuntimeKeepFree=
-RuntimeMaxFileSize=
-RuntimeMaxFiles=
-RuntimeMaxUse=
+RuntimeMaxSec=
SELinuxContext=
-SUPPORT_URL=
-Seal=
-ServerCertificateFile=
-ServerKeyFile=
-SessionsMax=
+SecureBits=
+SendSIGHUP=
+SendSIGKILL=
+SetCredential=
+Slice=
SmackProcessLabel=
-SplitMode=
+Sockets=
StandardError=
StandardInput=
StandardInputData=
StandardInputText=
StandardOutput=
+StartLimitAction=
+StartLimitBurst=
+StartLimitInterval=
+StartupBlockIOWeight=
+StartupCPUShares=
+StartupCPUWeight=
+StartupIOWeight=
StateDirectory=
StateDirectoryMode=
-Storage=
-SuspendKeyIgnoreInhibited=
-SyncIntervalSec=
+SuccessExitStatus=
+SupplementaryGroups=
+SysVStartPriority=
SyslogFacility=
SyslogIdentifier=
SyslogLevel=
SystemCallArchitectures=
SystemCallErrorNumber=
SystemCallFilter=
-SystemKeepFree=
-SystemMaxFileSize=
-SystemMaxFiles=
-SystemMaxUse=
+SystemCallLog=
TTYPath=
TTYReset=
TTYVHangup=
TTYVTDisallocate=
+TasksAccounting=
+TasksMax=
TemporaryFileSystem=
+TimeoutAbortSec=
+TimeoutCleanSec=
+TimeoutSec=
+TimeoutStartFailureMode=
+TimeoutStartSec=
+TimeoutStopFailureMode=
+TimeoutStopSec=
TimerSlackNSec=
-TrustedCertificateFile=
+Type=
UMask=
-URL=
+USBFunctionDescriptors=
+USBFunctionStrings=
UnsetEnvironment=
User=
-UserTasksMax=
UtmpIdentifier=
UtmpMode=
-VARIANT=
-VARIANT_ID=
-VERSION=
-VERSION_CODENAME=
-VERSION_ID=
-VirtualEthernet=
-VirtualEthernetExtra=
-Volatile=
+WatchdogSec=
+WatchdogSignal=
WorkingDirectory=
-Zone=
--- /dev/null
+slice
+[Slice]
+AllowedCPUs=
+AllowedMemoryNodes=
+BPFProgram=
+BlockIOAccounting=
+BlockIODeviceWeight=
+BlockIOReadBandwidth=
+BlockIOWeight=
+BlockIOWriteBandwidth=
+CPUAccounting=
+CPUQuota=
+CPUQuotaPeriodSec=
+CPUShares=
+CPUWeight=
+DefaultMemoryLow=
+DefaultMemoryMin=
+Delegate=
+DeviceAllow=
+DevicePolicy=
+DisableControllers=
+IOAccounting=
+IODeviceLatencyTargetSec=
+IODeviceWeight=
+IOReadBandwidthMax=
+IOReadIOPSMax=
+IOWeight=
+IOWriteBandwidthMax=
+IOWriteIOPSMax=
+IPAccounting=
+IPAddressAllow=
+IPAddressDeny=
+IPEgressFilterPath=
+IPIngressFilterPath=
+ManagedOOMMemoryPressure=
+ManagedOOMMemoryPressureLimit=
+ManagedOOMPreference=
+ManagedOOMSwap=
+MemoryAccounting=
+MemoryHigh=
+MemoryLimit=
+MemoryLow=
+MemoryMax=
+MemoryMin=
+MemorySwapMax=
+NetClass=
+Slice=
+StartupBlockIOWeight=
+StartupCPUShares=
+StartupCPUWeight=
+StartupIOWeight=
+TasksAccounting=
+TasksMax=
--- /dev/null
+socket
+[Socket]
+Accept=
+AllowedCPUs=
+AllowedMemoryNodes=
+AmbientCapabilities=
+AppArmorProfile=
+BPFProgram=
+Backlog=
+BindIPv6Only=
+BindPaths=
+BindReadOnlyPaths=
+BindToDevice=
+BlockIOAccounting=
+BlockIODeviceWeight=
+BlockIOReadBandwidth=
+BlockIOWeight=
+BlockIOWriteBandwidth=
+Broadcast=
+CPUAccounting=
+CPUAffinity=
+CPUQuota=
+CPUQuotaPeriodSec=
+CPUSchedulingPolicy=
+CPUSchedulingPriority=
+CPUSchedulingResetOnFork=
+CPUShares=
+CPUWeight=
+CacheDirectory=
+CacheDirectoryMode=
+Capabilities=
+CapabilityBoundingSet=
+ConfigurationDirectory=
+ConfigurationDirectoryMode=
+CoredumpFilter=
+DefaultMemoryLow=
+DefaultMemoryMin=
+DeferAcceptSec=
+Delegate=
+DeviceAllow=
+DevicePolicy=
+DirectoryMode=
+DisableControllers=
+DynamicUser=
+Environment=
+EnvironmentFile=
+ExecPaths=
+ExecStartPost=
+ExecStartPre=
+ExecStopPost=
+ExecStopPre=
+ExtensionImages=
+FileDescriptorName=
+FinalKillSignal=
+FlushPending=
+FreeBind=
+Group=
+IOAccounting=
+IODeviceLatencyTargetSec=
+IODeviceWeight=
+IOReadBandwidthMax=
+IOReadIOPSMax=
+IOSchedulingClass=
+IOSchedulingPriority=
+IOWeight=
+IOWriteBandwidthMax=
+IOWriteIOPSMax=
+IPAccounting=
+IPAddressAllow=
+IPAddressDeny=
+IPCNamespacePath=
+IPEgressFilterPath=
+IPIngressFilterPath=
+IPTOS=
+IPTTL=
+IgnoreSIGPIPE=
+InaccessibleDirectories=
+InaccessiblePaths=
+KeepAlive=
+KeepAliveIntervalSec=
+KeepAliveProbes=
+KeepAliveTimeSec=
+KeyringMode=
+KillMode=
+KillSignal=
+LimitAS=
+LimitCORE=
+LimitCPU=
+LimitDATA=
+LimitFSIZE=
+LimitLOCKS=
+LimitMEMLOCK=
+LimitMSGQUEUE=
+LimitNICE=
+LimitNOFILE=
+LimitNPROC=
+LimitRSS=
+LimitRTPRIO=
+LimitRTTIME=
+LimitSIGPENDING=
+LimitSTACK=
+ListenDatagram=
+ListenFIFO=
+ListenMessageQueue=
+ListenNetlink=
+ListenSequentialPacket=
+ListenSpecial=
+ListenStream=
+ListenUSBFunction=
+LoadCredential=
+LockPersonality=
+LogExtraFields=
+LogLevelMax=
+LogNamespace=
+LogRateLimitBurst=
+LogRateLimitIntervalSec=
+LogsDirectory=
+LogsDirectoryMode=
+ManagedOOMMemoryPressure=
+ManagedOOMMemoryPressureLimit=
+ManagedOOMPreference=
+ManagedOOMSwap=
+Mark=
+MaxConnections=
+MaxConnectionsPerSource=
+MemoryAccounting=
+MemoryDenyWriteExecute=
+MemoryHigh=
+MemoryLimit=
+MemoryLow=
+MemoryMax=
+MemoryMin=
+MemorySwapMax=
+MessageQueueMaxMessages=
+MessageQueueMessageSize=
+MountAPIVFS=
+MountFlags=
+MountImages=
+NUMAMask=
+NUMAPolicy=
+NetClass=
+NetworkNamespacePath=
+Nice=
+NoDelay=
+NoExecPaths=
+NoNewPrivileges=
+OOMScoreAdjust=
+PAMName=
+PassCredentials=
+PassEnvironment=
+PassPacketInfo=
+PassSecurity=
+Personality=
+PipeSize=
+Priority=
+PrivateDevices=
+PrivateIPC=
+PrivateMounts=
+PrivateNetwork=
+PrivateTmp=
+PrivateUsers=
+ProcSubset=
+ProtectClock=
+ProtectControlGroups=
+ProtectHome=
+ProtectHostname=
+ProtectKernelLogs=
+ProtectKernelModules=
+ProtectKernelTunables=
+ProtectProc=
+ProtectSystem=
+ReadOnlyDirectories=
+ReadOnlyPaths=
+ReadWriteDirectories=
+ReadWritePaths=
+ReceiveBuffer=
+RemoveIPC=
+RemoveOnStop=
+RestartKillSignal=
+RestrictAddressFamilies=
+RestrictNamespaces=
+RestrictRealtime=
+RestrictSUIDSGID=
+ReusePort=
+RootDirectory=
+RootHash=
+RootHashSignature=
+RootImage=
+RootImageOptions=
+RootVerity=
+RuntimeDirectory=
+RuntimeDirectoryMode=
+RuntimeDirectoryPreserve=
+SELinuxContext=
+SELinuxContextFromNet=
+SecureBits=
+SendBuffer=
+SendSIGHUP=
+SendSIGKILL=
+Service=
+SetCredential=
+Slice=
+SmackLabel=
+SmackLabelIPIn=
+SmackLabelIPOut=
+SmackProcessLabel=
+SocketGroup=
+SocketMode=
+SocketProtocol=
+SocketUser=
+StandardError=
+StandardInput=
+StandardInputData=
+StandardInputText=
+StandardOutput=
+StartupBlockIOWeight=
+StartupCPUShares=
+StartupCPUWeight=
+StartupIOWeight=
+StateDirectory=
+StateDirectoryMode=
+SupplementaryGroups=
+Symlinks=
+SyslogFacility=
+SyslogIdentifier=
+SyslogLevel=
+SyslogLevelPrefix=
+SystemCallArchitectures=
+SystemCallErrorNumber=
+SystemCallFilter=
+SystemCallLog=
+TCPCongestion=
+TTYPath=
+TTYReset=
+TTYVHangup=
+TTYVTDisallocate=
+TasksAccounting=
+TasksMax=
+TemporaryFileSystem=
+TimeoutCleanSec=
+TimeoutSec=
+TimerSlackNSec=
+Timestamping=
+Transparent=
+TriggerLimitBurst=
+TriggerLimitIntervalSec=
+UMask=
+UnsetEnvironment=
+User=
+UtmpIdentifier=
+UtmpMode=
+WatchdogSignal=
+WorkingDirectory=
+Writable=
--- /dev/null
+swap
+[Swap]
+AllowedCPUs=
+AllowedMemoryNodes=
+AmbientCapabilities=
+AppArmorProfile=
+BPFProgram=
+BindPaths=
+BindReadOnlyPaths=
+BlockIOAccounting=
+BlockIODeviceWeight=
+BlockIOReadBandwidth=
+BlockIOWeight=
+BlockIOWriteBandwidth=
+CPUAccounting=
+CPUAffinity=
+CPUQuota=
+CPUQuotaPeriodSec=
+CPUSchedulingPolicy=
+CPUSchedulingPriority=
+CPUSchedulingResetOnFork=
+CPUShares=
+CPUWeight=
+CacheDirectory=
+CacheDirectoryMode=
+Capabilities=
+CapabilityBoundingSet=
+ConfigurationDirectory=
+ConfigurationDirectoryMode=
+CoredumpFilter=
+DefaultMemoryLow=
+DefaultMemoryMin=
+Delegate=
+DeviceAllow=
+DevicePolicy=
+DisableControllers=
+DynamicUser=
+Environment=
+EnvironmentFile=
+ExecPaths=
+ExtensionImages=
+FinalKillSignal=
+Group=
+IOAccounting=
+IODeviceLatencyTargetSec=
+IODeviceWeight=
+IOReadBandwidthMax=
+IOReadIOPSMax=
+IOSchedulingClass=
+IOSchedulingPriority=
+IOWeight=
+IOWriteBandwidthMax=
+IOWriteIOPSMax=
+IPAccounting=
+IPAddressAllow=
+IPAddressDeny=
+IPCNamespacePath=
+IPEgressFilterPath=
+IPIngressFilterPath=
+IgnoreSIGPIPE=
+InaccessibleDirectories=
+InaccessiblePaths=
+KeyringMode=
+KillMode=
+KillSignal=
+LimitAS=
+LimitCORE=
+LimitCPU=
+LimitDATA=
+LimitFSIZE=
+LimitLOCKS=
+LimitMEMLOCK=
+LimitMSGQUEUE=
+LimitNICE=
+LimitNOFILE=
+LimitNPROC=
+LimitRSS=
+LimitRTPRIO=
+LimitRTTIME=
+LimitSIGPENDING=
+LimitSTACK=
+LoadCredential=
+LockPersonality=
+LogExtraFields=
+LogLevelMax=
+LogNamespace=
+LogRateLimitBurst=
+LogRateLimitIntervalSec=
+LogsDirectory=
+LogsDirectoryMode=
+ManagedOOMMemoryPressure=
+ManagedOOMMemoryPressureLimit=
+ManagedOOMPreference=
+ManagedOOMSwap=
+MemoryAccounting=
+MemoryDenyWriteExecute=
+MemoryHigh=
+MemoryLimit=
+MemoryLow=
+MemoryMax=
+MemoryMin=
+MemorySwapMax=
+MountAPIVFS=
+MountFlags=
+MountImages=
+NUMAMask=
+NUMAPolicy=
+NetClass=
+NetworkNamespacePath=
+Nice=
+NoExecPaths=
+NoNewPrivileges=
+OOMScoreAdjust=
+Options=
+PAMName=
+PassEnvironment=
+Personality=
+Priority=
+PrivateDevices=
+PrivateIPC=
+PrivateMounts=
+PrivateNetwork=
+PrivateTmp=
+PrivateUsers=
+ProcSubset=
+ProtectClock=
+ProtectControlGroups=
+ProtectHome=
+ProtectHostname=
+ProtectKernelLogs=
+ProtectKernelModules=
+ProtectKernelTunables=
+ProtectProc=
+ProtectSystem=
+ReadOnlyDirectories=
+ReadOnlyPaths=
+ReadWriteDirectories=
+ReadWritePaths=
+RemoveIPC=
+RestartKillSignal=
+RestrictAddressFamilies=
+RestrictNamespaces=
+RestrictRealtime=
+RestrictSUIDSGID=
+RootDirectory=
+RootHash=
+RootHashSignature=
+RootImage=
+RootImageOptions=
+RootVerity=
+RuntimeDirectory=
+RuntimeDirectoryMode=
+RuntimeDirectoryPreserve=
+SELinuxContext=
+SecureBits=
+SendSIGHUP=
+SendSIGKILL=
+SetCredential=
+Slice=
+SmackProcessLabel=
+StandardError=
+StandardInput=
+StandardInputData=
+StandardInputText=
+StandardOutput=
+StartupBlockIOWeight=
+StartupCPUShares=
+StartupCPUWeight=
+StartupIOWeight=
+StateDirectory=
+StateDirectoryMode=
+SupplementaryGroups=
+SyslogFacility=
+SyslogIdentifier=
+SyslogLevel=
+SyslogLevelPrefix=
+SystemCallArchitectures=
+SystemCallErrorNumber=
+SystemCallFilter=
+SystemCallLog=
+TTYPath=
+TTYReset=
+TTYVHangup=
+TTYVTDisallocate=
+TasksAccounting=
+TasksMax=
+TemporaryFileSystem=
+TimeoutCleanSec=
+TimeoutSec=
+TimerSlackNSec=
+UMask=
+UnsetEnvironment=
+User=
+UtmpIdentifier=
+UtmpMode=
+WatchdogSignal=
+What=
+WorkingDirectory=
--- /dev/null
+timer
+[Timer]
+AccuracySec=
+FixedRandomDelay=
+OnActiveSec=
+OnBootSec=
+OnCalendar=
+OnClockChange=
+OnStartupSec=
+OnTimezoneChange=
+OnUnitActiveSec=
+OnUnitInactiveSec=
+Persistent=
+RandomizedDelaySec=
+RemainAfterElapse=
+Unit=
+WakeSystem=
--- /dev/null
+service
+[Service]
+LoadCredential=passwd.hashed-password.root
Description=Test for StandardOutput=append:
[Service]
-ExecStartPre=sh -c 'printf "hello\n" > /tmp/test-exec-standardoutput-output'
-ExecStartPre=sh -c 'printf "hello\nhello\n" > /tmp/test-exec-standardoutput-expected'
+ExecStartPre=sh -c 'printf "hello\n" >/tmp/test-exec-standardoutput-output'
+ExecStartPre=sh -c 'printf "hello\nhello\n" >/tmp/test-exec-standardoutput-expected'
StandardInput=data
StandardInputText=hello
StandardOutput=append:/tmp/test-exec-standardoutput-output
Description=Test for StandardOutput=file:
[Service]
-ExecStartPre=sh -c 'printf "nooo\nhello\n" > /tmp/test-exec-standardoutput-output'
-ExecStartPre=sh -c 'printf "hello\nello\n" > /tmp/test-exec-standardoutput-expected'
+ExecStartPre=sh -c 'printf "nooo\nhello\n" >/tmp/test-exec-standardoutput-output'
+ExecStartPre=sh -c 'printf "hello\nello\n" >/tmp/test-exec-standardoutput-expected'
StandardInput=data
StandardInputText=hello
StandardOutput=file:/tmp/test-exec-standardoutput-output
Description=Test for StandardOutput=truncate:
[Service]
-ExecStartPre=sh -c 'printf "hello\n" > /tmp/test-exec-standardoutput-output'
-ExecStartPre=sh -c 'printf "hi\n" > /tmp/test-exec-standardoutput-expected'
+ExecStartPre=sh -c 'printf "hello\n" >/tmp/test-exec-standardoutput-output'
+ExecStartPre=sh -c 'printf "hi\n" >/tmp/test-exec-standardoutput-expected'
StandardInput=data
StandardInputText=hi
StandardOutput=truncate:/tmp/test-exec-standardoutput-output
ln -s ../usr/lib/os-release $initdir/etc/os-release
touch $initdir/etc/machine-id $initdir/etc/resolv.conf
touch $initdir/opt/some_file
- echo MARKER=1 >> $initdir/usr/lib/os-release
- echo -e "[Service]\nExecStartPre=cat /usr/lib/os-release\nExecStart=sleep 120" > $initdir/usr/lib/systemd/system/app0.service
+ echo MARKER=1 >>$initdir/usr/lib/os-release
+ echo -e "[Service]\nExecStartPre=cat /usr/lib/os-release\nExecStart=sleep 120" >$initdir/usr/lib/systemd/system/app0.service
cp $initdir/usr/lib/systemd/system/app0.service $initdir/usr/lib/systemd/system/app0-foo.service
mksquashfs $initdir $oldinitdir/usr/share/minimal_0.raw
veritysetup format $oldinitdir/usr/share/minimal_0.raw $oldinitdir/usr/share/minimal_0.verity | \
- grep '^Root hash:' | cut -f2 | tr -d '\n' > $oldinitdir/usr/share/minimal_0.roothash
+ grep '^Root hash:' | cut -f2 | tr -d '\n' >$oldinitdir/usr/share/minimal_0.roothash
sed -i "s/MARKER=1/MARKER=2/g" $initdir/usr/lib/os-release
rm $initdir/usr/lib/systemd/system/app0-foo.service
mksquashfs $initdir $oldinitdir/usr/share/minimal_1.raw
veritysetup format $oldinitdir/usr/share/minimal_1.raw $oldinitdir/usr/share/minimal_1.verity | \
- grep '^Root hash:' | cut -f2 | tr -d '\n' > $oldinitdir/usr/share/minimal_1.roothash
+ grep '^Root hash:' | cut -f2 | tr -d '\n' >$oldinitdir/usr/share/minimal_1.roothash
# Rolling distros like Arch do not set VERSION_ID
local version_id=""
export initdir=$TESTDIR/app0
mkdir -p $initdir/usr/lib/extension-release.d $initdir/usr/lib/systemd/system $initdir/opt
- grep "^ID=" $os_release > $initdir/usr/lib/extension-release.d/extension-release.app0
- echo "${version_id}" >> $initdir/usr/lib/extension-release.d/extension-release.app0
- cat <<EOF > $initdir/usr/lib/systemd/system/app0.service
+ grep "^ID=" $os_release >$initdir/usr/lib/extension-release.d/extension-release.app0
+ echo "${version_id}" >>$initdir/usr/lib/extension-release.d/extension-release.app0
+ cat <<EOF >$initdir/usr/lib/systemd/system/app0.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/script0.sh
EOF
- cat <<EOF > $initdir/opt/script0.sh
+ cat <<EOF >$initdir/opt/script0.sh
#!/bin/bash
set -e
test -e /usr/lib/os-release
cat /usr/lib/extension-release.d/extension-release.app0
EOF
chmod +x $initdir/opt/script0.sh
- echo MARKER=1 > $initdir/usr/lib/systemd/system/some_file
+ echo MARKER=1 >$initdir/usr/lib/systemd/system/some_file
mksquashfs $initdir $oldinitdir/usr/share/app0.raw
export initdir=$TESTDIR/app1
mkdir -p $initdir/usr/lib/extension-release.d $initdir/usr/lib/systemd/system $initdir/opt
- grep "^ID=" $os_release > $initdir/usr/lib/extension-release.d/extension-release.app1
- echo "${version_id}" >> $initdir/usr/lib/extension-release.d/extension-release.app1
- cat <<EOF > $initdir/usr/lib/systemd/system/app1.service
+ grep "^ID=" $os_release >$initdir/usr/lib/extension-release.d/extension-release.app1
+ echo "${version_id}" >>$initdir/usr/lib/extension-release.d/extension-release.app1
+ cat <<EOF >$initdir/usr/lib/systemd/system/app1.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/script1.sh
EOF
- cat <<EOF > $initdir/opt/script1.sh
+ cat <<EOF >$initdir/opt/script1.sh
#!/bin/bash
set -e
test -e /usr/lib/os-release
cat /usr/lib/extension-release.d/extension-release.app1
EOF
chmod +x $initdir/opt/script1.sh
- echo MARKER=1 > $initdir/usr/lib/systemd/system/other_file
+ echo MARKER=1 >$initdir/usr/lib/systemd/system/other_file
mksquashfs $initdir $oldinitdir/usr/share/app1.raw
)
}
# Let's add the ASan DSO's path to the dynamic linker's cache. This is pretty
# unnecessary for gcc & libasan, however, for clang this is crucial, as its
# runtime ASan DSO is in a non-standard (library) path.
- echo "${ASAN_RT_PATH%/*}" > /etc/ld.so.conf.d/asan-path-override.conf
+ echo "${ASAN_RT_PATH%/*}" >/etc/ld.so.conf.d/asan-path-override.conf
ldconfig
fi
echo DefaultEnvironment=\$DEFAULT_ENVIRONMENT >>/etc/systemd/system.conf
# they're uninstrumented (like dmsetup). Let's add a simple rule which sets
# LD_PRELOAD to the ASan RT library to fix this.
mkdir -p /etc/udev/rules.d
-cat > /etc/udev/rules.d/00-set-LD_PRELOAD.rules << INNER_EOF
+cat >/etc/udev/rules.d/00-set-LD_PRELOAD.rules <<INNER_EOF
SUBSYSTEM=="block", ENV{LD_PRELOAD}="$ASAN_RT_PATH"
INNER_EOF
chmod 0644 /etc/udev/rules.d/00-set-LD_PRELOAD.rules
[[ "$LOOKS_LIKE_SUSE" ]] && setup_suse
# enable debug logging in PID1
- echo LogLevel=debug >> $initdir/etc/systemd/system.conf
+ echo LogLevel=debug >>$initdir/etc/systemd/system.conf
# store coredumps in journal
- echo Storage=journal >> $initdir/etc/systemd/coredump.conf
+ echo Storage=journal >>$initdir/etc/systemd/coredump.conf
}
get_ldpath() {
inst_any /etc/os-release /usr/lib/os-release
inst /etc/localtime
# we want an empty environment
- > $initdir/etc/environment
- > $initdir/etc/machine-id
- > $initdir/etc/resolv.conf
+ >$initdir/etc/environment
+ >$initdir/etc/machine-id
+ >$initdir/etc/resolv.conf
# set the hostname
- echo systemd-testsuite > $initdir/etc/hostname
+ echo systemd-testsuite >$initdir/etc/hostname
# let's set up just one image with the traditional verbose output
if [ ${IMAGE_NAME} != "basic" ]; then
# Set default TERM from vt220 to linux, so at least basic key shortcuts work
local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d"
mkdir -p "$_getty_override"
- echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf"
+ echo -e "[Service]\nEnvironment=TERM=linux" >"$_getty_override/default-TERM.conf"
- cat > "$initdir/etc/motd" << EOF
+ cat >"$initdir/etc/motd" <<EOF
To adjust the terminal size use:
export COLUMNS=xx
export LINES=yy
# setup policy for Type=dbus test
mkdir -p $initdir/etc/dbus-1/system.d
- cat > $initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf <<EOF
+ cat >$initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf <<EOF
<?xml version="1.0"?>
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
fi
[ -d "$initdir/.kernelmodseen" ] && \
- > "$initdir/.kernelmodseen/${1##*/}"
+ >"$initdir/.kernelmodseen/${1##*/}"
inst_simple "$1" "/lib/modules/$KERNEL_VER/${1##*/lib/modules/$KERNEL_VER/}" \
|| return $?
for _modname in $(eval $_filtercmd); do
case $_modname in
*.ko) "$2" "$_modname" && echo "$_modname";;
- *.ko.gz) gzip -dc "$_modname" > $initdir/$$.ko
+ *.ko.gz) gzip -dc "$_modname" >$initdir/$$.ko
$2 $initdir/$$.ko && echo "$_modname"
rm -f $initdir/$$.ko
;;
- *.ko.xz) xz -dc "$_modname" > $initdir/$$.ko
+ *.ko.xz) xz -dc "$_modname" >$initdir/$$.ko
$2 $initdir/$$.ko && echo "$_modname"
rm -f $initdir/$$.ko
;;
fi
local hook_defined=1
- if declare -f -F test_append_files > /dev/null; then
+ if declare -f -F test_append_files >/dev/null; then
hook_defined=$?
fi
[Service]
Type=oneshot
ExecStart=test -f /tmp/test-specifier-j-%j
-ExecStart=sh -c 'echo OK > /testok'
+ExecStart=sh -c 'echo OK >/testok'
set -o pipefail
if ! test -x /usr/lib/systemd/tests/testdata/units/test-honor-first-shutdown.sh ; then
- echo "honor-first-shutdown script not found - FAIL" > /testok
+ echo "honor-first-shutdown script not found - FAIL" >/testok
exit 0
fi
systemctl enable test-honor-first-shutdown.service
systemctl start test-honor-first-shutdown.service
-echo OK > /testok
+echo OK >/testok
exit 0
systemctl start --no-block hello-after-sleep.target
-systemctl list-jobs > /root/list-jobs.txt
+systemctl list-jobs >/root/list-jobs.txt
while ! grep 'sleep\.service.*running' /root/list-jobs.txt; do
- systemctl list-jobs > /root/list-jobs.txt
+ systemctl list-jobs >/root/list-jobs.txt
done
grep 'hello\.service.*waiting' /root/list-jobs.txt
END_SEC=$(date -u '+%s')
ELAPSED=$(($END_SEC-$START_SEC))
-[ "$ELAPSED" -lt 3 ]
+test "$ELAPSED" -lt 3
# sleep should still be running, hello not.
-systemctl list-jobs > /root/list-jobs.txt
+systemctl list-jobs >/root/list-jobs.txt
grep 'sleep\.service.*running' /root/list-jobs.txt
grep 'hello\.service' /root/list-jobs.txt && exit 1
systemctl stop sleep.service hello-after-sleep.target
# Some basic testing that --show-transaction does something useful
-! systemctl is-active systemd-importd
+systemctl is-active systemd-importd && { echo 'unexpected success'; exit 1; }
systemctl -T start systemd-importd
systemctl is-active systemd-importd
systemctl --show-transaction stop systemd-importd
-! systemctl is-active systemd-importd
+systemctl is-active systemd-importd && { echo 'unexpected success'; exit 1; }
# Test for a crash when enqueuing a JOB_NOP when other job already exists
systemctl start --no-block hello-after-sleep.target
systemctl start unstoppable.service
# Test waiting for a started unit(s) to terminate again
-cat <<EOF > /run/systemd/system/wait2.service
+cat <<EOF >/run/systemd/system/wait2.service
[Unit]
Description=Wait for 2 seconds
[Service]
ExecStart=/bin/sh -ec 'sleep 2'
EOF
-cat <<EOF > /run/systemd/system/wait5fail.service
+cat <<EOF >/run/systemd/system/wait5fail.service
[Unit]
Description=Wait for 5 seconds and fail
[Service]
# wait5fail fails, so systemctl should fail
START_SEC=$(date -u '+%s')
-! systemctl start --wait wait2.service wait5fail.service || exit 1
+systemctl start --wait wait2.service wait5fail.service && { echo 'unexpected success'; exit 1; }
END_SEC=$(date -u '+%s')
ELAPSED=$(($END_SEC-$START_SEC))
[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
grep -q '^__CURSOR=' /output
grep -q '^MESSAGE=foo$' /output
grep -q '^PRIORITY=6$' /output
-! grep -q '^FOO=' /output
-! grep -q '^SYSLOG_FACILITY=' /output
+grep '^FOO=' /output && { echo 'unexpected success'; exit 1; }
+grep '^SYSLOG_FACILITY=' /output && { echo 'unexpected success'; exit 1; }
# `-b all` negates earlier use of -b (-b and -m are otherwise exclusive)
-journalctl -b -1 -b all -m > /dev/null
+journalctl -b -1 -b all -m >/dev/null
# -b always behaves like -b0
-journalctl -q -b-1 -b0 | head -1 > /expected
-journalctl -q -b-1 -b | head -1 > /output
+journalctl -q -b-1 -b0 | head -1 >/expected
+journalctl -q -b-1 -b | head -1 >/output
cmp /expected /output
# ... even when another option follows (both of these should fail due to -m)
-{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 > /expected
-{ journalctl -ball -b -m 2>&1 || :; } | head -1 > /output
+{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 >/expected
+{ journalctl -ball -b -m 2>&1 || :; } | head -1 >/output
cmp /expected /output
# https://github.com/systemd/systemd/issues/13708
SocketMode=0660
EOF
-cat <<'EOF' > /run/systemd/system/test12@.service
+cat <<'EOF' >/run/systemd/system/test12@.service
[Unit]
Description=Test service
[Service]
local _root="/var/lib/machines/testsuite-13.norbind-path"
rm -rf "$_root"
mkdir -p /tmp/binddir/subdir
- echo -n "outer" > /tmp/binddir/subdir/file
+ echo -n "outer" >/tmp/binddir/subdir/file
mount -t tmpfs tmpfs /tmp/binddir/subdir
- echo -n "inner" > /tmp/binddir/subdir/file
+ echo -n "inner" >/tmp/binddir/subdir/file
/usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
systemd-nspawn $SUSE_OPTS--register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
}
if [ -n "${BUILD_ID:+set}" ] && [ "${BUILD_ID}" != "${container_host_build_id}" ]; then exit 1; fi
if [ -n "${VARIANT_ID:+set}" ] && [ "${VARIANT_ID}" != "${container_host_variant_id}" ]; then exit 1; fi
cd /tmp; (cd /run/host; md5sum os-release) | md5sum -c
-if echo test >> /run/host/os-release; then exit 1; fi
+if echo test >>/run/host/os-release; then exit 1; fi
'
local _os_release_source="/etc/os-release"
elif [ -L "${_os_release_source}" ] && rm /etc/os-release; then
# Ensure that /etc always wins if available
cp /usr/lib/os-release /etc
- echo MARKER=1 >> /etc/os-release
+ echo MARKER=1 >>/etc/os-release
fi
systemd-nspawn $SUSE_OPTS--register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
function check_machinectl_bind {
local _cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep 0.5; done; exit 1;'
- cat <<EOF > /run/systemd/system/nspawn_machinectl_bind.service
+ cat <<EOF >/run/systemd/system/nspawn_machinectl_bind.service
[Service]
Type=notify
ExecStart=systemd-nspawn $SUSE_OPTS -D /testsuite-13.nc-container --notify-ready=no /bin/sh -x -e -c "$_cmd"
echo "
[Service]
ExecCondition=/bin/echo $dropin
- " > /usr/lib/systemd/system/$dropin/override.conf
+ " >/usr/lib/systemd/system/$dropin/override.conf
systemctl daemon-reload
check_ok a-b-c ExecCondition "/bin/echo $dropin"
done
sleep .5
done
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="foobar.service"
EOF
udevadm control --reload
sleep .5
done
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="waldo.service"
EOF
udevadm control --reload
mkdir -p /run/udev/rules.d/
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
ACTION=="remove", GOTO="lo_end"
SUBSYSTEM=="net", KERNEL=="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/lo"
setup() {
mkdir -p "${test_rule%/*}"
cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bckp
- echo 'KERNEL=="lo", SUBSYSTEM=="net", PROGRAM=="/bin/sleep 60"' > "${test_rule}"
- echo "event_timeout=30" >> /etc/udev/udev.conf
- echo "timeout_signal=SIGABRT" >> /etc/udev/udev.conf
+ echo 'KERNEL=="lo", SUBSYSTEM=="net", PROGRAM=="/bin/sleep 60"' >"${test_rule}"
+ echo "event_timeout=30" >>/etc/udev/udev.conf
+ echo "timeout_signal=SIGABRT" >>/etc/udev/udev.conf
systemctl restart systemd-udevd.service
}
run_test() {
since="$(date +%T)"
- echo add > /sys/class/net/lo/uevent
+ echo add >/sys/class/net/lo/uevent
for n in {1..20}; do
sleep 5
mkdir -p /run/udev/rules.d/
-! test -f /run/udev/tags/added/c1:3 &&
- ! test -f /run/udev/tags/changed/c1:3 &&
- udevadm info /dev/null | grep -q -v 'E: TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q -v 'E: TAGS=.*:changed:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:changed:.*'
+test ! -f /run/udev/tags/added/c1:3
+test ! -f /run/udev/tags/changed/c1:3
+udevadm info /dev/null | grep -E 'E: (TAGS|CURRENT_TAGS)=.*:(added|changed):' && exit 1
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", TAG+="added"
ACTION=="change", SUBSYSTEM=="mem", KERNEL=="null", TAG+="changed"
EOF
udevadm control --reload
udevadm trigger -c add /dev/null
-while : ; do
- test -f /run/udev/tags/added/c1:3 &&
- ! test -f /run/udev/tags/changed/c1:3 &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q -v 'E: TAGS=.*:changed:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:changed:.*' &&
- break
-
+while test ! -f /run/udev/tags/added/c1:3 ||
+ test -f /run/udev/tags/changed/c1:3 ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' ||
+ udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' ||
+ udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
+do
sleep .5
done
udevadm control --reload
udevadm trigger -c change /dev/null
-while : ; do
- test -f /run/udev/tags/added/c1:3 &&
- test -f /run/udev/tags/changed/c1:3 &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' &&
- udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*' &&
- break
-
+while test ! -f /run/udev/tags/added/c1:3 ||
+ test ! -f /run/udev/tags/changed/c1:3 ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' ||
+ udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
+do
sleep .5
done
udevadm control --reload
udevadm trigger -c add /dev/null
-while : ; do
- test -f /run/udev/tags/added/c1:3 &&
- test -f /run/udev/tags/changed/c1:3 &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' &&
- udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' &&
- udevadm info /dev/null | grep -q -v 'E: CURRENT_TAGS=.*:changed:.*' &&
- break
-
+while test ! -f /run/udev/tags/added/c1:3 ||
+ test ! -f /run/udev/tags/changed/c1:3 ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:added:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:added:.*' ||
+ ! udevadm info /dev/null | grep -q 'E: TAGS=.*:changed:.*' ||
+ udevadm info /dev/null | grep -q 'E: CURRENT_TAGS=.*:changed:.*'
+do
sleep .5
done
mkdir -p /run/udev/rules.d/
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+cat >/run/udev/rules.d/50-testsuite.rules <<EOF
SUBSYSTEM=="mem", KERNEL=="null", OPTIONS="log_level=debug"
ACTION=="add", SUBSYSTEM=="mem", KERNEL=="null", IMPORT{program}="/bin/echo -e HOGE=aa\\\\x20\\\\x20\\\\x20bb\nFOO=\\\\x20aaa\\\\x20\n\n\n"
EOF
set -o pipefail
systemd-run --wait -p FailureAction=poweroff true
-! systemd-run --wait -p SuccessAction=poweroff false
+systemd-run --wait -p SuccessAction=poweroff false && { echo 'unexpected success'; exit 1; }
if ! test -f /firstphase ; then
- echo OK > /firstphase
+ echo OK >/firstphase
systemd-run --wait -p SuccessAction=reboot true
else
- echo OK > /testok
+ echo OK >/testok
systemd-run --wait -p FailureAction=poweroff false
fi
echo "Skipping TEST-19-DELEGATE, as the kernel doesn't actually support cgroup v2" >&2
fi
-echo OK > /testok
+echo OK >/testok
exit 0
sleep infinity &
disown
-echo \$MAINPID > /run/mainpidsh/pid
+echo \$MAINPID >/run/mainpidsh/pid
EOF
chmod +x /tmp/test20-mainpid.sh
sleep infinity &
disown
-echo \$MAINPID > /run/mainpidsh2/pid
+echo \$MAINPID >/run/mainpidsh2/pid
chown 1001:1001 /run/mainpidsh2/pid
EOF
chmod +x /tmp/test20-mainpid2.sh
EOF
chmod 755 /dev/shm/test20-mainpid3.sh
-# This has to fail, as we shouldn't accept the dangerous PID file, and then inotify-wait on it to be corrected which we never do
-! systemd-run --unit=test20-mainpidsh3.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh3 -p PIDFile=/run/mainpidsh3/pid -p DynamicUser=1 -p TimeoutStartSec=2s /dev/shm/test20-mainpid3.sh
+# This has to fail, as we shouldn't accept the dangerous PID file, and then
+# inotify-wait on it to be corrected which we never do.
+systemd-run --unit=test20-mainpidsh3.service \
+ -p StandardOutput=tty \
+ -p StandardError=tty \
+ -p Type=forking \
+ -p RuntimeDirectory=mainpidsh3 \
+ -p PIDFile=/run/mainpidsh3/pid \
+ -p DynamicUser=1 \
+ -p TimeoutStartSec=2s \
+ /dev/shm/test20-mainpid3.sh \
+ && { echo 'unexpected success'; exit 1; }
# Test that this failed due to timeout, and not some other error
-test `systemctl show -P Result test20-mainpidsh3.service` = timeout
+test $(systemctl show -P Result test20-mainpidsh3.service) = timeout
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create -
-! test -e /tmp/test
+test ! -e /tmp/test
e /tmp/e/2/* 0755 daemon daemon - -
EOF
-! test -d /tmp/e/1
+test ! -d /tmp/e/1
test -d /tmp/e/2
test $(stat -c %U:%G:%a /tmp/e/2) = "root:root:777"
touch /tmp/e/3/f1
chmod 644 /tmp/e/3/f1
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF
e /tmp/e/3/* 0755 daemon daemon - -
EOF
test -d /tmp/C/2
test $(stat -c %U:%G:%a /tmp/C/2/f1) = "daemon:daemon:755"
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF
C /tmp/C/3 0755 daemon daemon - /tmp/C/3-origin
EOF
EOF
### '1' should exist and be empty
-test -f /tmp/f/1; ! test -s /tmp/f/1
+test -f /tmp/f/1; test ! -s /tmp/f/1
test $(stat -c %U:%G:%a /tmp/f/1) = "root:root:644"
test $(stat -c %U:%G:%a /tmp/f/2) = "root:root:644"
EOF
# file should be empty
-! test -s /tmp/f/1
+test ! -s /tmp/f/1
test $(stat -c %U:%G:%a /tmp/f/1) = "daemon:daemon:666"
### But we shouldn't try to set perms on an existing file which is not a
mkfifo /tmp/f/fifo
chmod 644 /tmp/f/fifo
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/fifo 0666 daemon daemon - This string should not be written
EOF
ln -s missing /tmp/f/dangling
ln -s /tmp/file-owned-by-root /tmp/f/symlink
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/dangling 0644 daemon daemon - -
f /tmp/f/symlink 0644 daemon daemon - -
EOF
-! test -e /tmp/f/missing
+test ! -e /tmp/f/missing
test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
### Handle read-only filesystem gracefully: we shouldn't fail if the target
systemd-tmpfiles --create - <<EOF
f /tmp/f/ro-fs/foo 0644 - - - - This string should not be written
EOF
-test -f /tmp/f/ro-fs/foo; ! test -s /tmp/f/ro-fs/foo
+test -f /tmp/f/ro-fs/foo; test ! -s /tmp/f/ro-fs/foo
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/ro-fs/foo 0666 - - - -
EOF
test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/ro-fs/bar 0644 - - - -
EOF
-! test -e /tmp/f/ro-fs/bar
+test ! -e /tmp/f/ro-fs/bar
### 'f' shouldn't follow unsafe paths.
mkdir /tmp/f/daemon
ln -s /root /tmp/f/daemon/unsafe-symlink
chown -R --no-dereference daemon:daemon /tmp/f/daemon
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/f/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
EOF
-! test -e /tmp/f/daemon/unsafe-symlink/exploit
+test ! -e /tmp/f/daemon/unsafe-symlink/exploit
#
# 'F'
F /tmp/F/truncated-with-content 0666 daemon daemon - new content
EOF
-test -f /tmp/F/created; ! test -s /tmp/F/created
+test -f /tmp/F/created; test ! -s /tmp/F/created
test -f /tmp/F/created-with-content
test "$(< /tmp/F/created-with-content)" = "new content"
-test -f /tmp/F/truncated; ! test -s /tmp/F/truncated
+test -f /tmp/F/truncated; test ! -s /tmp/F/truncated
test $(stat -c %U:%G:%a /tmp/F/truncated) = "daemon:daemon:666"
test -s /tmp/F/truncated-with-content
test $(stat -c %U:%G:%a /tmp/F/truncated-with-content) = "daemon:daemon:666"
### unspecified in the other cases.
mkfifo /tmp/F/fifo
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/fifo 0644 - - - -
EOF
ln -s missing /tmp/F/dangling
ln -s /tmp/file-owned-by-root /tmp/F/symlink
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/F/dangling 0644 daemon daemon - -
f /tmp/F/symlink 0644 daemon daemon - -
EOF
-! test -e /tmp/F/missing
+test ! -e /tmp/F/missing
test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
### Handle read-only filesystem gracefully: we shouldn't fail if the target
systemd-tmpfiles --create - <<EOF
F /tmp/F/ro-fs/foo 0644 - - - -
EOF
-test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
+test -f /tmp/F/ro-fs/foo; test ! -s /tmp/F/ro-fs/foo
echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/ro-fs/foo 0644 - - - -
EOF
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/ro-fs/foo 0644 - - - - This string should not be written
EOF
-test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
+test -f /tmp/F/ro-fs/foo
+grep -q 'truncating is not allowed' /tmp/F/ro-fs/foo
# Trying to change the perms should fail.
>/tmp/F/rw-fs/foo
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/ro-fs/foo 0666 - - - -
EOF
test $(stat -c %U:%G:%a /tmp/F/ro-fs/foo) = "root:root:644"
### Try to create a new file.
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/ro-fs/bar 0644 - - - -
EOF
-! test -e /tmp/F/ro-fs/bar
+test ! -e /tmp/F/ro-fs/bar
### 'F' shouldn't follow unsafe paths.
mkdir /tmp/F/daemon
ln -s /root /tmp/F/daemon/unsafe-symlink
chown -R --no-dereference daemon:daemon /tmp/F/daemon
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
F /tmp/F/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
EOF
-! test -e /tmp/F/daemon/unsafe-symlink/exploit
+test ! -e /tmp/F/daemon/unsafe-symlink/exploit
#
# 'w'
systemd-tmpfiles --create - <<EOF
w /tmp/w/unexistent 0644 - - - new content
EOF
-! test -e /tmp/w/unexistent
+test ! -e /tmp/w/unexistent
### no argument given -> fails.
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
w /tmp/w/unexistent 0644 - - - -
EOF
ln -s /root /tmp/w/daemon/unsafe-symlink
chown -R --no-dereference daemon:daemon /tmp/w/daemon
-! systemd-tmpfiles --create - <<EOF
+systemd-tmpfiles --create - <<EOF && { echo 'unexpected success'; exit 1; }
f /tmp/w/daemon/unsafe-symlink/exploit 0644 daemon daemon - -
EOF
-! test -e /tmp/w/daemon/unsafe-symlink/exploit
+test ! -e /tmp/w/daemon/unsafe-symlink/exploit
test -p /tmp/p/fifo1
test $(stat -c %U:%G:%a /tmp/p/fifo1) = "root:root:666"
-# it should refuse to overwrite an existing file
-! systemd-tmpfiles --create - <<EOF
+# Refuse to overwrite an existing file. Error is not propagated.
+systemd-tmpfiles --create - <<EOF
p /tmp/p/f1 0666 - - - -
EOF
test -d /var/tmp/foobar-test-06/important
test_snippet --remove
-! test -f /var/tmp/foobar-test-06
-! test -f /var/tmp/foobar-test-06/important
+test ! -f /var/tmp/foobar-test-06
+test ! -f /var/tmp/foobar-test-06/important
test_snippet --create
test -d /var/tmp/foobar-test-06
test_snippet --create --remove
test -d /var/tmp/foobar-test-06
test -d /var/tmp/foobar-test-06/important
-! test -f /var/tmp/foobar-test-06/something-else
+test ! -f /var/tmp/foobar-test-06/something-else
r /tmp/test-prefix/file
EOF
-! test -f /tmp/test-prefix/file
-! test -f /tmp/test-prefix
+test ! -f /tmp/test-prefix/file
+test ! -f /tmp/test-prefix
mkdir /tmp/test-prefix
touch /tmp/test-prefix/file
r /tmp/test-prefix
EOF
-! test -f /tmp/test-prefix/file
-! test -f /tmp/test-prefix
+test ! -f /tmp/test-prefix/file
+test ! -f /tmp/test-prefix
# Verify the command fails to write to a root-owned subdirectory under an
# unprivileged user's directory when it's not part of the prefix, as expected
# by the unsafe_transition function.
-! echo 'd /tmp/user/root/test' | systemd-tmpfiles --create -
-! test -e /tmp/user/root/test
-! echo 'd /user/root/test' | systemd-tmpfiles --root=/tmp --create -
-! test -e /tmp/user/root/test
+echo 'd /tmp/user/root/test' | systemd-tmpfiles --create - \
+ && { echo 'unexpected success'; exit 1; }
+test ! -e /tmp/user/root/test
+echo 'd /user/root/test' | systemd-tmpfiles --root=/tmp --create - \
+ && { echo 'unexpected success'; exit 1; }
+test ! -e /tmp/user/root/test
# Verify the above works when all user-owned directories are in the prefix.
echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create -
--- /dev/null
+#! /bin/bash
+
+set -e
+set -x
+
+rm -fr /tmp/x
+mkdir /tmp/x
+
+#
+# 'x'
+#
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test ! -f /tmp/x/z1
+test ! -f /tmp/x/z2
+
+#
+# 'X'
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test ! -f /tmp/x/1/x1
+test ! -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test ! -f /tmp/x/z1
+test ! -f /tmp/x/z2
+
+#
+# 'x' with glob
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+x /tmp/x/[1345]
+x /tmp/x/z*
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -f /tmp/x/1/x1
+test -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+#
+# 'X' with glob
+#
+
+mkdir -p /tmp/x/{1,2}
+touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
+
+systemd-tmpfiles --clean - <<EOF
+d /tmp/x - - - 0
+X /tmp/x/[1345]
+X /tmp/x/?[12]
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test ! -f /tmp/x/1/x1
+test ! -f /tmp/x/1/x2
+test ! -d /tmp/x/2
+test ! -f /tmp/x/2/x1
+test ! -f /tmp/x/2/x2
+test -f /tmp/x/z1
+test -f /tmp/x/z2
+
+#
+# 'x' with 'r'
+#
+
+mkdir -p /tmp/x/{1,2}/a
+touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
+
+systemd-tmpfiles --clean - <<EOF
+# x/X is not supposed to influence r
+x /tmp/x/1/a
+X /tmp/x/2/a
+r /tmp/x/1
+r /tmp/x/2
+EOF
+
+find /tmp/x | sort
+test -d /tmp/x/1
+test -d /tmp/x/1/a
+test -f /tmp/x/1/a/x1
+test -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
+
+#
+# 'x' with 'R'
+#
+
+mkdir -p /tmp/x/{1,2}/a
+touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
+
+systemd-tmpfiles --remove - <<EOF
+# X is not supposed to influence R
+X /tmp/x/1/a
+X /tmp/x/2/a
+R /tmp/x/1
+EOF
+
+find /tmp/x | sort
+test ! -d /tmp/x/1
+test ! -d /tmp/x/1/a
+test ! -f /tmp/x/1/a/x1
+test ! -f /tmp/x/1/a/x2
+test -f /tmp/x/2/a/y1
+test -f /tmp/x/2/a/y2
# And now, do the same with Type=exec, where the latter two should fail
systemd-run --unit=four -p Type=exec /bin/sleep infinity
-! systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity
-! systemd-run --unit=six -p Type=exec /tmp/brokenbinary
+systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity && { echo 'unexpected success'; exit 1; }
+systemd-run --unit=six -p Type=exec /tmp/brokenbinary && { echo 'unexpected success'; exit 1; }
systemd-run --unit=seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity
# Both TERM and SIGINT happen to have the same number on all architectures
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
# Test removal
machinectl remove testimage
-! test -f /var/lib/machines/testimage.raw
-! machinectl image-status testimage
+test ! -f /var/lib/machines/testimage.raw
+machinectl image-status testimage && { echo 'unexpected success'; exit 1; }
# Test export of clone
machinectl export-raw testimage3 /var/tmp/testimage3.raw
machinectl rename testimage3 testimage4
test -f /var/lib/machines/testimage4.raw
machinectl image-status testimage4
-! test -f /var/lib/machines/testimage3.raw
-! machinectl image-status testimage3
+test ! -f /var/lib/machines/testimage3.raw
+machinectl image-status testimage3 && { echo 'unexpected success'; exit 1; }
cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw
# Test export of rename
# Test removal
machinectl remove testimage4
-! test -f /var/lib/machines/testimage4.raw
-! machinectl image-status testimage4
+test ! -f /var/lib/machines/testimage4.raw
+machinectl image-status testimage4 && { echo 'unexpected success'; exit 1; }
# → And now, let's test directory trees ← #
mv /var/tmp/testimage.raw /var/tmp/scratch/
touch /var/tmp/scratch/anotherfile
mkdir /var/tmp/scratch/adirectory
-echo "piep" > /var/tmp/scratch/adirectory/athirdfile
+echo "piep" >/var/tmp/scratch/adirectory/athirdfile
# Test import-fs
machinectl import-fs /var/tmp/scratch/
# Test removal
machinectl remove scratch
-! test -f /var/lib/machines/scratch
-! machinectl image-status scratch
+test ! -f /var/lib/machines/scratch
+machinectl image-status scratchi && { echo 'unexpected success'; exit 1; }
# Test clone
machinectl clone scratch2 scratch3
# Test removal
machinectl remove scratch2
-! test -f /var/lib/machines/scratch2
-! machinectl image-status scratch2
+test ! -f /var/lib/machines/scratch2
+machinectl image-status scratch2 && { echo 'unexpected success'; exit 1; }
# Test rename
machinectl rename scratch3 scratch4
test -d /var/lib/machines/scratch4
machinectl image-status scratch4
-! test -f /var/lib/machines/scratch3
-! machinectl image-status scratch3
+test ! -f /var/lib/machines/scratch3
+machinectl image-status scratch3 && { echo 'unexpected success'; exit 1; }
diff -r /var/tmp/scratch/ /var/lib/machines/scratch4
# Test removal
machinectl remove scratch4
-! test -f /var/lib/machines/scratch4
-! machinectl image-status scratch4
+test ! -f /var/lib/machines/scratch4
+machinectl image-status scratch4 && { echo 'unexpected success'; exit 1; }
# Test import-tar hyphen/stdin pipe behavior
cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5
# Test removal
machinectl remove scratch5
-! test -f /var/lib/machines/scratch5
-! machinectl image-status scratch5
+test ! -f /var/lib/machines/scratch5
+machinectl image-status scratch5 && { echo 'unexpected success'; exit 1; }
-echo OK > /testok
+echo OK >/testok
exit 0
systemctl unset-environment FOO PATH
# Check that one is gone and the other reverted to the built-in
-! (systemctl show-environment | grep -q '^FOO=$')
-! (systemctl show-environment | grep -q '^PATH=.*testaddition$')
+systemctl show-environment | grep '^FOO=$' && exit 1
+systemctl show-environment | grep '^PATH=.*testaddition$' && exit 1
systemctl show-environment | grep -q '^PATH='
-echo OK > /testok
+echo OK >/testok
exit 0
umount /tmp/rootdir
umount /tmp/app1
-echo OK > /testok
+echo OK >/testok
exit 0
systemd-run --on-timezone-change touch /tmp/timezone-changed
systemd-run --on-clock-change touch /tmp/clock-changed
-! test -f /tmp/timezone-changed
-! test -f /tmp/clock-changed
+test ! -f /tmp/timezone-changed
+test ! -f /tmp/clock-changed
timedatectl set-timezone Europe/Kiev
-while ! test -f /tmp/timezone-changed ; do sleep .5 ; done
+while test ! -f /tmp/timezone-changed ; do sleep .5 ; done
timedatectl set-time 2018-1-1
-while ! test -f /tmp/clock-changed ; do sleep .5 ; done
+while test ! -f /tmp/clock-changed ; do sleep .5 ; done
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
exit 1
fi
-echo OK > /testok
+echo OK >/testok
exit 0
set -ex
set -o pipefail
-cat > /etc/systemd/system/testservice.service <<EOF
+cat >/etc/systemd/system/testservice.service <<EOF
[Service]
ConfigurationDirectory=testservice
RuntimeDirectory=testservice
systemctl daemon-reload
-! test -e /etc/testservice
-! test -e /run/testservice
-! test -e /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
+test ! -e /var/lib/testservice
+test ! -e /var/cache/testservice
+test ! -e /var/log/testservice
systemctl start testservice
test -d /var/cache/testservice
test -d /var/log/testservice
-! systemctl clean testservice
+systemctl clean testservice && { echo 'unexpected success'; exit 1; }
systemctl stop testservice
systemctl clean testservice --what=configuration
-! test -e /etc/testservice
+test ! -e /etc/testservice
test -d /run/testservice
test -d /var/lib/testservice
test -d /var/cache/testservice
systemctl clean testservice
-! test -e /etc/testservice
-! test -e /run/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
test -d /var/lib/testservice
-! test -e /var/cache/testservice
+test ! -e /var/cache/testservice
test -d /var/log/testservice
systemctl clean testservice --what=logs
-! test -e /etc/testservice
-! test -e /run/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
test -d /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
+test ! -e /var/cache/testservice
+test ! -e /var/log/testservice
systemctl clean testservice --what=all
-! test -e /etc/testservice
-! test -e /run/testservice
-! test -e /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
+test ! -e /var/lib/testservice
+test ! -e /var/cache/testservice
+test ! -e /var/log/testservice
-cat > /etc/systemd/system/testservice.service <<EOF
+cat >/etc/systemd/system/testservice.service <<EOF
[Service]
DynamicUser=yes
ConfigurationDirectory=testservice
systemctl daemon-reload
-! test -e /etc/testservice
-! test -e /run/testservice
-! test -e /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
+test ! -e /etc/testservice
+test ! -e /run/testservice
+test ! -e /var/lib/testservice
+test ! -e /var/cache/testservice
+test ! -e /var/log/testservice
systemctl restart testservice
test -L /var/cache/testservice
test -L /var/log/testservice
-! systemctl clean testservice
+systemctl clean testservice && { echo 'unexpected success'; exit 1; }
systemctl stop testservice
systemctl clean testservice --what=configuration
-! test -d /etc/testservice
+test ! -d /etc/testservice
test -d /run/private/testservice
test -d /var/lib/private/testservice
test -d /var/cache/private/testservice
systemctl clean testservice
-! test -d /etc/testservice
-! test -d /run/private/testservice
+test ! -d /etc/testservice
+test ! -d /run/private/testservice
test -d /var/lib/private/testservice
-! test -d /var/cache/private/testservice
+test ! -d /var/cache/private/testservice
test -d /var/log/private/testservice
-! test -L /run/testservice
+test ! -L /run/testservice
test -L /var/lib/testservice
-! test -L /var/cache/testservice
+test ! -L /var/cache/testservice
test -L /var/log/testservice
systemctl clean testservice --what=logs
-! test -d /etc/testservice
-! test -d /run/private/testservice
+test ! -d /etc/testservice
+test ! -d /run/private/testservice
test -d /var/lib/private/testservice
-! test -d /var/cache/private/testservice
-! test -d /var/log/private/testservice
-! test -L /run/testservice
+test ! -d /var/cache/private/testservice
+test ! -d /var/log/private/testservice
+test ! -L /run/testservice
test -L /var/lib/testservice
-! test -L /var/cache/testservice
-! test -L /var/log/testservice
+test ! -L /var/cache/testservice
+test ! -L /var/log/testservice
systemctl clean testservice --what=all
-! test -d /etc/testservice
-! test -d /run/private/testservice
-! test -d /var/lib/private/testservice
-! test -d /var/cache/private/testservice
-! test -d /var/log/private/testservice
-! test -L /run/testservice
-! test -L /var/lib/testservice
-! test -L /var/cache/testservice
-! test -L /var/log/testservice
-
-cat > /etc/systemd/system/tmp-hoge.mount <<EOF
+test ! -d /etc/testservice
+test ! -d /run/private/testservice
+test ! -d /var/lib/private/testservice
+test ! -d /var/cache/private/testservice
+test ! -d /var/log/private/testservice
+test ! -L /run/testservice
+test ! -L /var/lib/testservice
+test ! -L /var/cache/testservice
+test ! -L /var/log/testservice
+
+cat >/etc/systemd/system/tmp-hoge.mount <<EOF
[Mount]
What=tmpfs
Type=tmpfs
systemctl daemon-reload
-! test -e /etc/hoge
-! test -e /run/hoge
-! test -e /var/lib/hoge
-! test -e /var/cache/hoge
-! test -e /var/log/hoge
+test ! -e /etc/hoge
+test ! -e /run/hoge
+test ! -e /var/lib/hoge
+test ! -e /var/cache/hoge
+test ! -e /var/log/hoge
systemctl start tmp-hoge.mount
test -d /var/cache/hoge
test -d /var/log/hoge
-! systemctl clean tmp-hoge.mount
+systemctl clean tmp-hoge.mount && { echo 'unexpected success'; exit 1; }
test -d /etc/hoge
test -d /run/hoge
systemctl stop tmp-hoge.mount
test -d /etc/hoge
-! test -d /run/hoge
+test ! -d /run/hoge
test -d /var/lib/hoge
test -d /var/cache/hoge
test -d /var/log/hoge
systemctl clean tmp-hoge.mount --what=configuration
-! test -d /etc/hoge
-! test -d /run/hoge
+test ! -d /etc/hoge
+test ! -d /run/hoge
test -d /var/lib/hoge
test -d /var/cache/hoge
test -d /var/log/hoge
systemctl clean tmp-hoge.mount
-! test -d /etc/hoge
-! test -d /run/hoge
+test ! -d /etc/hoge
+test ! -d /run/hoge
test -d /var/lib/hoge
-! test -d /var/cache/hoge
+test ! -d /var/cache/hoge
test -d /var/log/hoge
systemctl clean tmp-hoge.mount --what=logs
-! test -d /etc/hoge
-! test -d /run/hoge
+test ! -d /etc/hoge
+test ! -d /run/hoge
test -d /var/lib/hoge
-! test -d /var/cache/hoge
-! test -d /var/log/hoge
+test ! -d /var/cache/hoge
+test ! -d /var/log/hoge
systemctl clean tmp-hoge.mount --what=all
-! test -d /etc/hoge
-! test -d /run/hoge
-! test -d /var/lib/hoge
-! test -d /var/cache/hoge
-! test -d /var/log/hoge
+test ! -d /etc/hoge
+test ! -d /run/hoge
+test ! -d /var/lib/hoge
+test ! -d /var/cache/hoge
+test ! -d /var/log/hoge
-cat > /etc/systemd/system/testservice.socket <<EOF
+cat >/etc/systemd/system/testservice.socket <<EOF
[Socket]
ListenSequentialPacket=/run/testservice.socket
RemoveOnStop=yes
systemctl daemon-reload
-! test -e /etc/testsocket
-! test -e /run/testsocket
-! test -e /var/lib/testsocket
-! test -e /var/cache/testsocket
-! test -e /var/log/testsocket
+test ! -e /etc/testsocket
+test ! -e /run/testsocket
+test ! -e /var/lib/testsocket
+test ! -e /var/cache/testsocket
+test ! -e /var/log/testsocket
systemctl start testservice.socket
test -d /etc/testsocket
-! test -d /run/testsocket
+test -d /run/testsocket
test -d /var/lib/testsocket
test -d /var/cache/testsocket
test -d /var/log/testsocket
-! systemctl clean testservice.socket
+systemctl clean testservice.socket && { echo 'unexpected success'; exit 1; }
systemctl stop testservice.socket
test -d /etc/testsocket
-! test -d /run/testsocket
+test ! -d /run/testsocket
test -d /var/lib/testsocket
test -d /var/cache/testsocket
test -d /var/log/testsocket
systemctl clean testservice.socket --what=configuration
-! test -e /etc/testsocket
-! test -d /run/testsocket
+test ! -e /etc/testsocket
+test ! -d /run/testsocket
test -d /var/lib/testsocket
test -d /var/cache/testsocket
test -d /var/log/testsocket
systemctl clean testservice.socket
-! test -e /etc/testsocket
-! test -e /run/testsocket
+test ! -e /etc/testsocket
+test ! -e /run/testsocket
test -d /var/lib/testsocket
-! test -e /var/cache/testsocket
+test ! -e /var/cache/testsocket
test -d /var/log/testsocket
systemctl clean testservice.socket --what=logs
-! test -e /etc/testsocket
-! test -e /run/testsocket
+test ! -e /etc/testsocket
+test ! -e /run/testsocket
test -d /var/lib/testsocket
-! test -e /var/cache/testsocket
-! test -e /var/log/testsocket
+test ! -e /var/cache/testsocket
+test ! -e /var/log/testsocket
systemctl clean testservice.socket --what=all
-! test -e /etc/testsocket
-! test -e /run/testsocket
-! test -e /var/lib/testsocket
-! test -e /var/cache/testsocket
-! test -e /var/log/testsocket
+test ! -e /etc/testsocket
+test ! -e /run/testsocket
+test ! -e /var/lib/testsocket
+test ! -e /var/cache/testsocket
+test ! -e /var/log/testsocket
-echo OK > /testok
+echo OK >/testok
exit 0
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz touch /var/lib/zzz/test
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
-! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
+ && { echo 'unexpected success'; exit 1; }
test -d /var/lib/zzz
-! test -L /var/lib/zzz
-! test -e /var/lib/private/zzz
+test ! -L /var/lib/zzz
+test ! -e /var/lib/private/zzz
test -f /var/lib/zzz/test
-! test -f /var/lib/zzz/test-missing
+test ! -f /var/lib/zzz/test-missing
# Convert to DynamicUser=1
systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test
-! systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
+ && { echo 'unexpected success'; exit 1; }
test -L /var/lib/zzz
test -d /var/lib/private/zzz
test -f /var/lib/zzz/test
-! test -f /var/lib/zzz/test-missing
+test ! -f /var/lib/zzz/test-missing
# Convert back
systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
-! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
+ && { echo 'unexpected success'; exit 1; }
test -d /var/lib/zzz
-! test -L /var/lib/zzz
-! test -e /var/lib/private/zzz
+test ! -L /var/lib/zzz
+test ! -e /var/lib/private/zzz
test -f /var/lib/zzz/test
-! test -f /var/lib/zzz/test-missing
+test ! -f /var/lib/zzz/test-missing
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
# the --sync wait until the synchronization is complete
echo "Force journald to write all queued messages"
journalctl --sync
- journalctl -u $unit --cursor-file="$journalCursorFile" > "$journalLog"
+ journalctl -u $unit --cursor-file="$journalCursorFile" >"$journalLog"
}
checkNUMA() {
}
writePID1NUMAPolicy() {
- echo [Manager] > $confDir/numa.conf
- echo NUMAPolicy=$1 >> $confDir/numa.conf
- echo NUMAMask=$2>> $confDir/numa.conf
+ echo [Manager] >$confDir/numa.conf
+ echo NUMAPolicy=$1 >>$confDir/numa.conf
+ echo NUMAMask=$2 >>$confDir/numa.conf
}
writeTestUnit() {
mkdir -p $testUnitFile.d/
- echo [Service] > $testUnitFile
- echo ExecStart=/bin/sleep 3600 >> $testUnitFile
+ echo [Service] >$testUnitFile
+ echo ExecStart=/bin/sleep 3600 >>$testUnitFile
}
writeTestUnitNUMAPolicy() {
- echo [Service] > $testUnitNUMAConf
- echo NUMAPolicy=$1 >> $testUnitNUMAConf
- echo NUMAMask=$2>> $testUnitNUMAConf
+ echo [Service] >$testUnitNUMAConf
+ echo NUMAPolicy=$1 >>$testUnitNUMAConf
+ echo NUMAMask=$2 >>$testUnitNUMAConf
systemctl daemon-reload
}
systemctlCheckNUMAProperties() {
local LOGFILE="$(mktemp)"
- systemctl show -p NUMAPolicy $1 > "$LOGFILE"
+ systemctl show -p NUMAPolicy $1 >"$LOGFILE"
grep "NUMAPolicy=$2" "$LOGFILE"
- > "$LOGFILE"
+ >"$LOGFILE"
if [ -n "$3" ]; then
- systemctl show -p NUMAMask $1 > "$LOGFILE"
+ systemctl show -p NUMAMask $1 >"$LOGFILE"
grep "NUMAMask=$3" "$LOGFILE"
fi
}
echo "Unit file CPUAffinity=NUMA support"
writeTestUnitNUMAPolicy "bind" "0"
- echo "CPUAffinity=numa" >> $testUnitNUMAConf
+ echo "CPUAffinity=numa" >>$testUnitNUMAConf
systemctl daemon-reload
systemctl start $testUnit
systemctlCheckNUMAProperties $testUnit "bind" "0"
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
systemctl restart tmp-aaa.mount
test -e /run/hoge/foo
-! test -e /tmp/aaa/bbb
+test ! -e /tmp/aaa/bbb
-echo OK > /testok
+echo OK >/testok
exit 0
echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):"
echo -n " - freeze from outside: "
- echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
+ echo 1 >/sys/fs/cgroup/"${slice}"/cgroup.freeze
# Give kernel some time to freeze the slice
sleep 1
echo "[ OK ]"
echo -n " - thaw from outside: "
- echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
+ echo 0 >/sys/fs/cgroup/"${slice}"/cgroup.freeze
sleep 1
check_freezer_state "${unit}" "running"
echo -n " - thaw from outside while inner service is frozen: "
systemctl freeze "$unit"
check_freezer_state "${unit}" "frozen"
- echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
- echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
+ echo 1 >/sys/fs/cgroup/"${slice}"/cgroup.freeze
+ echo 0 >/sys/fs/cgroup/"${slice}"/cgroup.freeze
check_freezer_state "${slice}" "running"
check_freezer_state "${unit}" "frozen"
echo "[ OK ]"
test_preserve_state
}
-echo OK > /testok
+echo OK >/testok
exit 0
SERVICE_NAME="${SERVICE_PATH##*/}"
echo "[#1] Failing ExecReload= should not kill the service"
-cat > "$SERVICE_PATH" << EOF
+cat >"$SERVICE_PATH" <<EOF
[Service]
ExecStart=/bin/sleep infinity
ExecReload=/bin/false
systemctl start $SERVICE_NAME
systemctl status $SERVICE_NAME
# The reload SHOULD fail but SHOULD NOT affect the service state
-! systemctl reload $SERVICE_NAME
+systemctl reload $SERVICE_NAME && { echo 'unexpected success'; exit 1; }
systemctl status $SERVICE_NAME
systemctl stop $SERVICE_NAME
echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
-cat > "$SERVICE_PATH" << EOF
+cat >"$SERVICE_PATH" <<EOF
[Service]
ExecStart=/bin/sleep infinity
ExecReload=/bin/true
systemctl start $SERVICE_NAME
systemctl status $SERVICE_NAME
# The reload SHOULD fail but SHOULD NOT affect the service state
-! systemctl reload $SERVICE_NAME
+systemctl reload $SERVICE_NAME && { echo 'unexpected success'; exit 1; }
systemctl status $SERVICE_NAME
systemctl stop $SERVICE_NAME
echo "[#3] Failing ExecReload=- should not affect reload's exit code"
-cat > "$SERVICE_PATH" << EOF
+cat >"$SERVICE_PATH" <<EOF
[Service]
ExecStart=/bin/sleep infinity
ExecReload=-/bin/false
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
systemd-analyze log-target console
# test one: Restart=on-failure should restart the service
-! systemd-run --unit=one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1"
+systemd-run --unit=one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1" \
+ && { echo 'unexpected success'; exit 1; }
for ((secs=0; secs<$MAX_SECS; secs++)); do
[[ "$(systemctl show one.service -P NRestarts)" -le 0 ]] || break
# test two: make sure StartLimitBurst correctly limits the number of restarts
# and restarts execution of the unit from the first ExecStart=
-! systemd-run --unit=two -p StartLimitIntervalSec=120 -p StartLimitBurst=3 -p Type=oneshot -p Restart=on-failure -p ExecStart="/bin/bash -c \"printf a >> $TMP_FILE\"" /bin/bash -c "exit 1"
+systemd-run --unit=two \
+ -p StartLimitIntervalSec=120 \
+ -p StartLimitBurst=3 \
+ -p Type=oneshot \
+ -p Restart=on-failure \
+ -p ExecStart="/bin/bash -c \"printf a >> $TMP_FILE\"" /bin/bash -c "exit 1" \
+ && { echo 'unexpected success'; exit 1; }
# wait for at least 3 restarts
for ((secs=0; secs<$MAX_SECS; secs++)); do
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple1' true
test -f /run/simple1
-! systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple2' false
+systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple2' false \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/simple2
systemd-run --unit=exec1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec1' sleep 1
test -f /run/exec1
-! systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false'
+systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false' \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/exec2
-cat > /tmp/forking1.sh <<EOF
+cat >/tmp/forking1.sh <<EOF
#!/usr/bin/env bash
set -eux
systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking1' /tmp/forking1.sh
test -f /run/forking1
-cat > /tmp/forking2.sh <<EOF
+cat >/tmp/forking2.sh <<EOF
#!/usr/bin/env bash
set -eux
EOF
chmod +x /tmp/forking2.sh
-! systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh
+systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/forking2
systemd-run --unit=oneshot1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot1' true
test -f /run/oneshot1
-! systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot2' false
+systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot2' false \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/oneshot2
systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus1' \
|| :
test -f /run/dbus1
-! systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus2' true
+systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus2' true
test -f /run/dbus2
-cat > /tmp/notify1.sh <<EOF
+cat >/tmp/notify1.sh <<EOF
#!/usr/bin/env bash
set -eux
systemd-run --unit=notify1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify1' /tmp/notify1.sh
test -f /run/notify1
-! systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify2' true
+systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify2' true \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/notify2
systemd-run --unit=idle1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle1' true
test -f /run/idle1
-! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle2' false
+systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle2' false \
+ && { echo 'unexpected success'; exit 1; }
test -f /run/idle2
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \
-p PrivateUsers=yes -p ProtectHome=read-only \
-P bash -c '
- test -e /home/testuser/works.txt
- ! touch /home/testuser/blocked.txt
- '
+ test -e /home/testuser/works.txt || exit 10
+ touch /home/testuser/blocked.txt && exit 11
+ ' \
+ && { echo 'unexpected success'; exit 1; }
test ! -e /home/testuser/blocked.txt
# Check that tmpfs hides the whole directory
# namespace (no CAP_SETGID in the parent namespace to write the additional
# mapping of the user supplied group and thus cannot change groups to an
# unmapped group ID)
-! runas testuser systemd-run --wait --user --unit=test-group-fail \
+runas testuser systemd-run --wait --user --unit=test-group-fail \
-p PrivateUsers=yes -p Group=daemon \
- -P true
+ -P true \
+ && { echo 'unexpected success'; exit 1; }
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
systemd-run -p LogNamespace=foobar echo "hello world"
journalctl --namespace=foobar --sync
-journalctl --namespace=foobar > /tmp/hello-world
-journalctl > /tmp/no-hello-world
+journalctl -o cat --namespace=foobar >/tmp/hello-world
+journalctl -o cat >/tmp/no-hello-world
-grep "hello world" /tmp/hello-world
-! grep "hello world" /tmp/no-hello-world
+grep "^hello world$" /tmp/hello-world
+grep "^hello world$" /tmp/no-hello-world && { echo 'unexpected success'; exit 1; }
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
# Check if homectl is installed, and if it isn't bail out early instead of failing
if ! test -x /usr/bin/homectl ; then
- echo OK > /testok
+ echo OK >/testok
exit 0
fi
inspect() {
- # As updating disk-size-related attributes can take some time on
- # some filesystems, let's drop these fields before comparing the
- # outputs to avoid unexpected fails. To see the full outputs of both
- # homectl & userdbctl (for debugging purposes) drop the fields just
- # before the comparison.
- homectl inspect $1 | tee /tmp/a
- userdbctl user $1 | tee /tmp/b
-
- local PATTERN='/^\s*Disk (Size|Free|Floor|Ceiling):/d'
- diff <(sed -r "$PATTERN" /tmp/a) <(sed -r "$PATTERN" /tmp/b)
- rm /tmp/a /tmp/b
+ # As updating disk-size-related attributes can take some time on some
+ # filesystems, let's drop these fields before comparing the outputs to
+ # avoid unexpected fails. To see the full outputs of both homectl &
+ # userdbctl (for debugging purposes) drop the fields just before the
+ # comparison.
+ homectl inspect $1 | tee /tmp/a
+ userdbctl user $1 | tee /tmp/b
+
+ diff -I '/^\s*Disk (Size|Free|Floor|Ceiling):/' /tmp/{a,b}
+ rm /tmp/{a,b}
}
systemd-analyze log-level debug
PASSWORD=xEhErW0ndafV4s homectl deactivate test-user
inspect test-user
-! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \
+ && { echo 'unexpected success'; exit 1; }
PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
-! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \
+ && { echo 'unexpected success'; exit 1; }
homectl remove test-user
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
#!/usr/bin/env bash
sleep infinity &
-echo $! > /leakedtestpid
+echo $! >/leakedtestpid
wait $!
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -ex
-cat > /run/systemd/system/testservice-48.target <<EOF
+cat >/run/systemd/system/testservice-48.target <<EOF
[Unit]
Wants=testservice-48.service
EOF
# May 07 23:12:20 systemd-testsuite testsuite-48.sh[53]: ef53
sleep 3.1
-cat > /run/systemd/system/testservice-48.service <<EOF
+cat >/run/systemd/system/testservice-48.service <<EOF
[Service]
ExecStart=/bin/sleep infinity
EOF
sleep 3.1
-cat > /run/systemd/system/testservice-48.service <<EOF
+cat >/run/systemd/system/testservice-48.service <<EOF
[Service]
ExecStart=/bin/sleep infinity
EOF
sleep 3.1
-cat > /run/systemd/system/testservice-48.target <<EOF
+cat >/run/systemd/system/testservice-48.target <<EOF
[Unit]
Conflicts=shutdown.target
Wants=testservice-48.service
systemctl start testservice-48.target
-cat > /run/systemd/system/testservice-48.service <<EOF
+cat >/run/systemd/system/testservice-48.service <<EOF
[Service]
ExecStart=/bin/sleep infinity
EOF
systemctl is-active testservice-48.service
-echo OK > /testok
+echo OK >/testok
exit 0
#!/usr/bin/env bash
set -ex
-echo "MARKER_FIXED" > /run/testservice-49-fixed
+echo "MARKER_FIXED" >/run/testservice-49-fixed
mkdir -p /run/inaccessible
systemctl start testsuite-49-namespaced.service
systemctl bind --mkdir testsuite-49-namespaced.service /run/testservice-49-fixed /run/inaccessible/testfile_fixed && exit 1
set -e
-echo "MARKER_RUNTIME" > /run/testservice-49-runtime
+echo "MARKER_RUNTIME" >/run/testservice-49-runtime
systemctl bind --mkdir testsuite-49-namespaced.service /run/testservice-49-runtime /tmp/testfile_runtime
systemctl is-active testsuite-49-non-namespaced.service && exit 1
set -e
-echo OK > /testok
+echo OK >/testok
exit 0
systemd-dissect --json=short ${image}.raw | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
systemd-dissect ${image}.raw | grep -q -F "MARKER=1"
-systemd-dissect ${image}.raw | grep -q -F -f $os_release
+systemd-dissect ${image}.raw | grep -q -F -f <(sed 's/"//g' $os_release)
mv ${image}.verity ${image}.fooverity
mv ${image}.roothash ${image}.foohash
systemd-dissect --json=short ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "MARKER=1"
-systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f $os_release
+systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f <(sed 's/"//g' $os_release)
mv ${image}.fooverity ${image}.verity
mv ${image}.foohash ${image}.roothash
systemd-dissect --json=short --root-hash ${roothash} ${image}.gpt | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'$ROOT_UUID'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'$architecture'","verity":"yes","node":'
systemd-dissect --json=short --root-hash ${roothash} ${image}.gpt | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'$VERITY_UUID'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'$architecture'","verity":null,"node":'
systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
-systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f $os_release
+systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f <(sed 's/"//g' $os_release)
systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f $os_release
# Adding a new mounts at runtime works if the unit is in the active state,
# so use Type=notify to make sure there's no race condition in the test
-cat > /run/systemd/system/testservice-50d.service <<EOF
+cat >/run/systemd/system/testservice-50d.service <<EOF
[Service]
RuntimeMaxSec=300
Type=notify
-p DynamicUser=1 \
--wait \
--pipe \
- cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' > /tmp/ts54-concat
+ cat '${CREDENTIALS_DIRECTORY}/passwd' '${CREDENTIALS_DIRECTORY}/shadow' '${CREDENTIALS_DIRECTORY}/dog' >/tmp/ts54-concat
( cat /etc/passwd /etc/shadow && echo -n wuff ) | cmp /tmp/ts54-concat
rm /tmp/ts54-concat
# Verify that the creds are immutable
-! systemd-run -p LoadCredential=passwd:/etc/passwd \
+systemd-run -p LoadCredential=passwd:/etc/passwd \
-p DynamicUser=1 \
--wait \
- touch '${CREDENTIALS_DIRECTORY}/passwd'
-! systemd-run -p LoadCredential=passwd:/etc/passwd \
+ touch '${CREDENTIALS_DIRECTORY}/passwd' \
+ && { echo 'unexpected success'; exit 1; }
+systemd-run -p LoadCredential=passwd:/etc/passwd \
-p DynamicUser=1 \
--wait \
- rm '${CREDENTIALS_DIRECTORY}/passwd'
+ rm '${CREDENTIALS_DIRECTORY}/passwd' \
+ && { echo 'unexpected success'; exit 1; }
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
systemd-analyze log-target console
# Loose checks to ensure the environment has the necessary features for systemd-oomd
-[[ -e /proc/pressure ]] || echo "no PSI" >> /skipped
+[[ -e /proc/pressure ]] || echo "no PSI" >>/skipped
cgroup_type=$(stat -fc %T /sys/fs/cgroup/)
if [[ "$cgroup_type" != *"cgroup2"* ]] && [[ "$cgroup_type" != *"0x63677270"* ]]; then
- echo "no cgroup2" >> /skipped
+ echo "no cgroup2" >>/skipped
fi
if [ ! -f /usr/lib/systemd/systemd-oomd ] && [ ! -f /lib/systemd/systemd-oomd ]; then
- echo "no oomd" >> /skipped
+ echo "no oomd" >>/skipped
fi
[[ -e /skipped ]] && exit 0 || true
rm -rf /etc/systemd/system/testsuite-55-testbloat.service.d
-echo "DefaultMemoryPressureDurationSec=5s" >> /etc/systemd/oomd.conf
+echo "DefaultMemoryPressureDurationSec=5s" >>/etc/systemd/oomd.conf
systemctl start testsuite-55-testchill.service
systemctl start testsuite-55-testbloat.service
sleep 120 # wait for systemd-oomd kill cool down and elevated memory pressure to come down
mkdir -p /etc/systemd/system/testsuite-55-testbloat.service.d/
- echo "[Service]" > /etc/systemd/system/testsuite-55-testbloat.service.d/override.conf
- echo "ManagedOOMPreference=avoid" >> /etc/systemd/system/testsuite-55-testbloat.service.d/override.conf
+ echo "[Service]" >/etc/systemd/system/testsuite-55-testbloat.service.d/override.conf
+ echo "ManagedOOMPreference=avoid" >>/etc/systemd/system/testsuite-55-testbloat.service.d/override.conf
systemctl daemon-reload
systemctl start testsuite-55-testchill.service
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
systemd-run --wait --unit=two -p ExitType=cgroup -p ExecCondition=true /tmp/test56-exit-cgroup.sh
# false exec condition: systemd-run should exit immediately with status code: 1
-! systemd-run --wait --unit=three -p ExitType=cgroup -p ExecCondition=false /tmp/test56-exit-cgroup.sh
+systemd-run --wait --unit=three -p ExitType=cgroup -p ExecCondition=false /tmp/test56-exit-cgroup.sh \
+ && { echo 'unexpected success'; exit 1; }
# service should exit uncleanly
(sleep 1; systemctl kill --signal 9 four) &
-! systemd-run --wait --unit=four -p ExitType=cgroup /tmp/test56-exit-cgroup.sh
+systemd-run --wait --unit=four -p ExitType=cgroup /tmp/test56-exit-cgroup.sh \
+ && { echo 'unexpected success'; exit 1; }
# Multiple level process tree, parent process exits quickly
# service should exit uncleanly
(sleep 1; systemctl kill --signal 9 six) &
-! systemd-run --wait --unit=six -p ExitType=cgroup /tmp/test56-exit-cgroup-parentless.sh
+systemd-run --wait --unit=six -p ExitType=cgroup /tmp/test56-exit-cgroup-parentless.sh \
+ && { echo 'unexpected success'; exit 1; }
# Multiple level process tree, parent process exits uncleanly but last process exits cleanly
chmod +x /tmp/test56-exit-cgroup-unclean.sh
# service should exit uncleanly after 1 second
-! systemd-run --wait --unit=eight -p ExitType=cgroup /tmp/test56-exit-cgroup-unclean.sh
+systemd-run --wait --unit=eight -p ExitType=cgroup /tmp/test56-exit-cgroup-unclean.sh \
+ && { echo 'unexpected success'; exit 1; }
systemd-analyze log-level info
-echo OK > /testok
+echo OK >/testok
exit 0
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
-set -e
+set -eu
-which perl &>/dev/null || exit 77
+SOURCE_ROOT="${1:?Missing argument: project source root}"
+BUILD_ROOT="${2:?Missing argument: project build root}"
+
+command -v gawk &>/dev/null || exit 77
function generate_directives() {
- perl -aF'/[\s,]+/' -ne '
- if (my ($s, $d) = ($F[0] =~ /^([^\s\.]+)\.([^\s\.]+)$/)) { $d{$s}{"$d="} = 1; }
- END { while (my ($key, $value) = each %d) {
- printf "[%s]\n%s\n", $key, join("\n", keys(%$value))
- }}' "$1"
+ gawk -v sec_rx="${2:-""}" -v unit_type="${3:-""}" '
+ match($0, /^([^ \t\.]+)\.([^ \t\.,]+)/, m) {
+ # res[section][directive] = 1
+ res[m[1]][m[2]] = 1;
+ }
+ END {
+ if (unit_type)
+ print unit_type
+
+ for (section in res) {
+ if (sec_rx && section !~ sec_rx)
+ continue
+
+ print "[" section "]";
+ for (directive in res[section]) {
+ print directive "=";
+ }
+ }
+ }
+ ' "$1"
}
ret=0
if ! diff \
- <(generate_directives "$1"/src/network/networkd-network-gperf.gperf | sort) \
- <(cat "$1"/test/fuzz/fuzz-network-parser/directives.network | sort); then
+ <(generate_directives "$SOURCE_ROOT"/src/network/networkd-network-gperf.gperf | sort) \
+ <(sort "$SOURCE_ROOT"/test/fuzz/fuzz-network-parser/directives.network); then
echo "Looks like test/fuzz/fuzz-network-parser/directives.network hasn't been updated"
ret=1
fi
if ! diff \
- <(generate_directives "$1"/src/network/netdev/netdev-gperf.gperf | sort) \
- <(cat "$1"/test/fuzz/fuzz-netdev-parser/directives.netdev | sort); then
+ <(generate_directives "$SOURCE_ROOT"/src/network/netdev/netdev-gperf.gperf | sort) \
+ <(sort "$SOURCE_ROOT"/test/fuzz/fuzz-netdev-parser/directives.netdev); then
echo "Looks like test/fuzz/fuzz-netdev-parser/directives.netdev hasn't been updated"
ret=1
fi
if ! diff \
- <(generate_directives "$1"/src/udev/net/link-config-gperf.gperf | sort) \
- <(cat "$1"/test/fuzz/fuzz-link-parser/directives.link | sort) ; then
+ <(generate_directives "$SOURCE_ROOT"/src/udev/net/link-config-gperf.gperf | sort) \
+ <(sort "$SOURCE_ROOT"/test/fuzz/fuzz-link-parser/directives.link) ; then
echo "Looks like test/fuzz/fuzz-link-parser/directives.link hasn't been updated"
ret=1
fi
+for section in Automount Mount Path Scope Slice Socket Swap Timer; do
+ if ! diff \
+ <(generate_directives "$BUILD_ROOT"/src/core/load-fragment-gperf.gperf "$section" "${section,,}" | sort) \
+ <(sort "$SOURCE_ROOT/test/fuzz/fuzz-unit-file/directives.${section,,}") ; then
+ echo "Looks like test/fuzz/fuzz-unit-file/directives.${section,,} hasn't been updated"
+ ret=1
+ fi
+done
+
+if ! diff \
+ <(generate_directives "$BUILD_ROOT"/src/core/load-fragment-gperf.gperf "(Service|Unit|Install)" "service" | sort) \
+ <(sort "$SOURCE_ROOT/test/fuzz/fuzz-unit-file/directives.service") ; then
+ echo "Looks like test/fuzz/fuzz-unit-file/directives.service hasn't been updated"
+ ret=1
+fi
+
exit $ret
['systemd-networkd.service', 'ENABLE_NETWORKD'],
['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD'],
['systemd-nspawn@.service', ''],
- ['systemd-oomd.service', 'ENABLE_OOMD'],
+ ['systemd-oomd.service', 'ENABLE_OOMD',
+ 'dbus-org.freedesktop.oom1.service'],
['systemd-portabled.service', 'ENABLE_PORTABLED',
'dbus-org.freedesktop.portable1.service'],
['systemd-userdbd.service', 'ENABLE_USERDB'],
[Unit]
Description=Cryptsetup Units Slice
-Documentation=man:systemd.special(7)
+Documentation=man:systemd-cryptsetup@.service(8)
DefaultDependencies=no
# Optionally, pick up basic fields from credentials passed to the service
# manager. This is useful for importing this data from nspawn's
# --set-credential= switch.
-LoadCredential=passwd.hashed-password.root
-LoadCredential=passwd.plaintext-password.root
-LoadCredential=passwd.shell.root
-LoadCredential=firstboot.locale
-LoadCredential=firstboot.locale-messages
-LoadCredential=firstboot.keymap
-LoadCredential=firstboot.timezone
+# FIXME: temporarily disabled as it causes asserts on v247/v248, see:
+# https://github.com/systemd/systemd/issues/19178
+#LoadCredential=passwd.hashed-password.root
+#LoadCredential=passwd.plaintext-password.root
+#LoadCredential=passwd.shell.root
+#LoadCredential=firstboot.locale
+#LoadCredential=firstboot.locale-messages
+#LoadCredential=firstboot.keymap
+#LoadCredential=firstboot.timezone
# Optionally, pick up a root password and shell for the root user from a
# credential passed to the service manager. This is useful for importing this
# data from nspawn's --set-credential= switch.
-LoadCredential=passwd.hashed-password.root
-LoadCredential=passwd.plaintext-password.root
-LoadCredential=passwd.shell.root
+# FIXME: temporarily disabled as it causes asserts on v247/v248, see:
+# https://github.com/systemd/systemd/issues/19178
+#LoadCredential=passwd.hashed-password.root
+#LoadCredential=passwd.plaintext-password.root
+#LoadCredential=passwd.shell.root