fstab-generator: disable default deps if x-systemd.{wanted,required}-by= is used
systemd System and Service Manager
-CHANGES WITH 255 in spe:
+CHANGES WITH 256 in spe:
Announcements of Future Feature Removals and Incompatible Changes:
+ * Previously, systemd-networkd did not explicitly remove any bridge VLAN
+ IDs assigned on bridge master and ports. Since v256, if a .network
+ file for an interface has at least one valid settings in [BridgeVLAN]
+ section, then all assigned VLAN IDs on the interface that are not
+ configured in the .network file are removed.
+
CHANGES WITH 255:
Announcements of Future Feature Removals and Incompatible Changes:
Features:
+* add a new ExecStart= flag that inserts the configured user's shell as first
+ word in the comand line. (maybe use character '.'). Usecase: tool such as
+ uid0 can use that to spawn the target user's default shell.
+
+* varlink: figure out how to do docs for our varlink interfaces. Idea: install
+ interface files augmented with docs in /usr/share/ somewhere. And have
+ functionality in varlinkctl to merge interface info extracted from binaries
+ with interface info on disk. And store the doc strings only in the latter.
+
+* introduce mntid_t, and make it 64bit, as apparently the kernel switched to
+ 64bit mount ids
+
+* use udev rule networkd ownership property to take ownership of network
+ interfaces nspawn creates
+
* add a kernel cmdline switch (and cred?) for marking a system to be
"headless", in which case we never open /dev/console for reading, only for
writing. This would then mean: systemd-firstboot would process creds but not
file system paths to enable on start.
• make systemd-fstab-generator look for a system credential encoding root= or
usr=
- • systemd-homed: when initializing, look for a credential
- systemd.homed.register or so with JSON user records to automatically
- register if not registered yet. Use case: deploy a system, and add an
- account one can directly log into.
• in gpt-auto-generator: check partition uuids against such uuids supplied via
sd-stub credentials. That way, we can support parallel OS installations with
pre-built kernels.
- support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt
- maybe pre-create ~/.cache as subvol so that it can have separate quota
easily?
- - 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, mimicking 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.
- store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with
systemd-cryptsetup, so that it can unlock homed volumes
- maybe make all *.home files owned by `systemd-home` user or so, so that we
-device scsi-hd,drive=hd,bootindex=1 \
-device vhost-vsock-pci,id=vhost-vsock-pci0,guest-cid=42 \
-smbios type=11,value=io.systemd.credential:vmm.notify_socket=vsock:2:1234 \
- -smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=$(echo "f~ /root/.ssh/authorized_keys 700 root root - $(ssh-add -L | base64 -w 0)" | base64 -w 0)
+ -smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=$(echo "f~ /root/.ssh/authorized_keys 600 root root - $(ssh-add -L | base64 -w 0)" | base64 -w 0)
```
A process on the host can listen for the notification, for example:
devices sysfs path are actually backed by sysfs. Relaxing this verification
is useful for testing purposes.
+`udevadm` and `systemd-hwdb`:
+
+* `SYSTEMD_HWDB_UPDATE_BYPASS=` — If set to "1", execution of hwdb updates is skipped
+ when `udevadm hwdb --update` or `systemd-hwdb update` are invoked. This can
+ be useful if either of these tools are invoked unconditionally as a child
+ process by another tool, such as package managers running either of these
+ tools in a postinstall script.
+
`nss-systemd`:
* `$SYSTEMD_NSS_BYPASS_SYNTHETIC=1` — if set, `nss-systemd` won't synthesize
EVDEV_ABS_35=52:1747:17
EVDEV_ABS_36=45:954:14
+#########################################
+# Bosto
+#########################################
+
+# Bosto BT-12HD series
+evdev:input:b0003v0ED1p7821*
+ EVDEV_ABS_00=::2271
+ EVDEV_ABS_01=::5080
+
#########################################
# Dell
#########################################
sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC5:*
sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC7:*
sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CC8:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CB2:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:skuOCB4:*
+sensor:modalias:platform:HID-SENSOR-200073:dmi:*svnDell*:sku0CB3:*
ACCEL_LOCATION=base
# Dell Venue 8 Pro 3845
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/binfmt.d/*.conf</filename></para>
- <para><filename>/run/binfmt.d/*.conf</filename></para>
- <para><filename>/usr/lib/binfmt.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/binfmt.d/*.conf</filename></member>
+ <member><filename>/run/binfmt.d/*.conf</filename></member>
+ <member><filename>/usr/lib/binfmt.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/coredump.conf</filename></para>
- <para><filename>/etc/systemd/coredump.conf.d/*.conf</filename></para>
- <para><filename>/run/systemd/coredump.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/coredump.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/coredump.conf</filename></member>
+ <member><filename>/etc/systemd/coredump.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/coredump.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/coredump.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
see above and below.</para></listitem>
<listitem><para>The key may be acquired via a PKCS#11 compatible hardware security token or
- smartcard. In this case an encrypted key is stored on disk/removable media, acquired via
- <constant>AF_UNIX</constant>, or stored in the LUKS2 JSON token metadata header. The encrypted key is
- then decrypted by the PKCS#11 token with an RSA key stored on it, and then used to unlock the encrypted
- volume. Use the <option>pkcs11-uri=</option> option described below to use this mechanism.</para></listitem>
+ smartcard. In this case a saved key used in unlock process is stored on disk/removable media, acquired via
+ <constant>AF_UNIX</constant>, or stored in the LUKS2 JSON token metadata header. For RSA, the saved key
+ is an encrypted volume key. The encrypted volume key is then decrypted by the PKCS#11 token with an RSA
+ private key stored on it, and used to unlock the encrypted volume. For elliptic-curve (EC) cryptography,
+ the saved key is the public key generated in enrollment process. The public key is then used to derive
+ a shared secret with a private key stored in the PKCS#11 token. The derived shared secret is then used
+ to unlock the volume. Use the <option>pkcs11-uri=</option> option described below to use this mechanism.
+ </para></listitem>
<listitem><para>Similarly, the key may be acquired via a FIDO2 compatible hardware security token
(which must implement the "hmac-secret" extension). In this case a key generated randomly during
<term><option>pkcs11-uri=</option></term>
<listitem><para>Takes either the special value <literal>auto</literal> or an <ulink
- url="https://tools.ietf.org/html/rfc7512">RFC7512 PKCS#11 URI</ulink> pointing to a private RSA key
+ url="https://tools.ietf.org/html/rfc7512">RFC7512 PKCS#11 URI</ulink> pointing to a private key
which is used to decrypt the encrypted key specified in the third column of the line. This is useful
for unlocking encrypted volumes through PKCS#11 compatible security tokens or smartcards. See below
for an example how to set up this mechanism for unlocking a LUKS2 volume with a YubiKey security
security token metadata in its LUKS2 JSON token section. In this mode the URI and the encrypted key
are automatically read from the LUKS2 JSON token header. Use
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- as simple tool for enrolling PKCS#11 security tokens or smartcards in a way compatible with
+ as a simple tool for enrolling PKCS#11 security tokens or smartcards in a way compatible with
<literal>auto</literal>. In this mode the third column of the line should remain empty (that is,
specified as <literal>-</literal>).</para>
- <para>The specified URI can refer directly to a private RSA key stored on a token or alternatively
- just to a slot or token, in which case a search for a suitable private RSA key will be performed. In
- this case if multiple suitable objects are found the token is refused. The encrypted key configured
- in the third column of the line is passed as is (i.e. in binary form, unprocessed) to RSA
- decryption. The resulting decrypted key is then Base64 encoded before it is used to unlock the LUKS
- volume.</para>
+ <para>The specified URI can refer directly to a private key stored on a token or alternatively
+ just to a slot or token, in which case a search for a suitable private key will be performed. In
+ this case if multiple suitable objects are found the token is refused. The keyfile configured
+ in the third column of the line is used as is (i.e. in binary form, unprocessed). The resulting
+ decrypted key (for RSA) or derived shared secret (for ECC) is then Base64 encoded before it is used
+ to unlock the LUKS volume.</para>
<para>Use <command>systemd-cryptenroll --pkcs11-token-uri=list</command> to list all suitable PKCS#11
security tokens currently plugged in, along with their URIs.</para>
<title>Yubikey-based PKCS#11 Volume Unlocking Example</title>
<para>The PKCS#11 logic allows hooking up any compatible security token that is capable of storing RSA
- decryption keys for unlocking an encrypted volume. Here's an example how to set up a Yubikey security
- token for this purpose on a LUKS2 volume, using <citerefentry
+ or EC cryptographic keys for unlocking an encrypted volume. Here's an example how to set up a Yubikey
+ security token for this purpose on a LUKS2 volume, using <citerefentry
project='debian'><refentrytitle>ykmap</refentrytitle><manvolnum>1</manvolnum></citerefentry> from the
yubikey-manager project to initialize the token and
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/dnssec-trust-anchors.d/*.positive</filename></para>
- <para><filename>/run/dnssec-trust-anchors.d/*.positive</filename></para>
- <para><filename>/usr/lib/dnssec-trust-anchors.d/*.positive</filename></para>
- <para><filename>/etc/dnssec-trust-anchors.d/*.negative</filename></para>
- <para><filename>/run/dnssec-trust-anchors.d/*.negative</filename></para>
- <para><filename>/usr/lib/dnssec-trust-anchors.d/*.negative</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/dnssec-trust-anchors.d/*.positive</filename></member>
+ <member><filename>/run/dnssec-trust-anchors.d/*.positive</filename></member>
+ <member><filename>/usr/lib/dnssec-trust-anchors.d/*.positive</filename></member>
+ <member><filename>/etc/dnssec-trust-anchors.d/*.negative</filename></member>
+ <member><filename>/run/dnssec-trust-anchors.d/*.negative</filename></member>
+ <member><filename>/usr/lib/dnssec-trust-anchors.d/*.negative</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>~/.config/environment.d/*.conf</filename></para>
- <para><filename>/etc/environment.d/*.conf</filename></para>
- <para><filename>/run/environment.d/*.conf</filename></para>
- <para><filename>/usr/lib/environment.d/*.conf</filename></para>
- <para><filename>/etc/environment</filename></para>
+ <para><simplelist>
+ <member><filename>~/.config/environment.d/*.conf</filename></member>
+ <member><filename>/etc/environment.d/*.conf</filename></member>
+ <member><filename>/run/environment.d/*.conf</filename></member>
+ <member><filename>/usr/lib/environment.d/*.conf</filename></member>
+ <member><filename>/etc/environment</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<refnamediv>
<refname>homectl</refname>
+ <refname>systemd-homed-firstboot.service</refname>
<refpurpose>Create, remove, change or inspect home directories</refpurpose>
</refnamediv>
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><command>firstboot</command></term>
+
+ <listitem><para>This command is supposed to be invoked during the initial boot of the system. It
+ checks whether any regular home area exists so far, and if not queries the user interactively on the
+ console for user name and password and creates one. Alternatively, if one or more service credentials
+ whose name starts with <literal>home.create.</literal> are passed to the command (containing a user
+ record in JSON format) these users are automatically created at boot.</para>
+
+ <para>This command is invoked by the <filename>systemd-homed-firstboot.service</filename> service
+ unit.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Credentials</title>
+
+ <para>When invoked with the <command>firstboot</command> command, <command>homectl</command> supports the
+ service credentials logic as implemented by
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ details). The following credentials are used when passed in:</para>
+
+ <variablelist class='system-credentials'>
+ <varlistentry>
+ <term><varname>home.create.*</varname></term>
+
+ <listitem><para>If one or more credentials whose names begin with <literal>home.create.</literal>,
+ followed by a valid UNIX username are passed, a new home area is created, one for each specified user
+ record.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Kernel Command Line</title>
+
+ <variablelist class='kernel-commandline-options'>
+ <varlistentry>
+ <term><varname>systemd.firstboot=</varname></term>
+
+ <listitem><para>This boolean will disable the effect of <command>homectl firstboot</command>
+ command. It's primarily interpreted by
+ <citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/homed.conf</filename></para>
- <para><filename>/etc/systemd/homed.conf.d/*.conf</filename></para>
- <para><filename>/run/systemd/homed.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/homed.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/homed.conf</filename></member>
+ <member><filename>/etc/systemd/homed.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/homed.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/homed.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/journal-remote.conf</filename></para>
- <para><filename>/etc/systemd/journal-remote.conf.d/*.conf</filename></para>
- <para><filename>/run/systemd/journal-remote.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/journal-remote.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/journal-remote.conf</filename></member>
+ <member><filename>/etc/systemd/journal-remote.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/journal-remote.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/journal-remote.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/journald.conf</filename></para>
- <para><filename>/etc/systemd/journald.conf.d/*.conf</filename></para>
- <para><filename>/run/systemd/journald.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/journald.conf.d/*.conf</filename></para>
- <para><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename></para>
- <para><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></para>
- <para><filename>/run/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/journald.conf</filename></member>
+ <member><filename>/etc/systemd/journald.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/journald.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/journald.conf.d/*.conf</filename></member>
+ <member><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf</filename></member>
+ <member><filename>/etc/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/journald@<replaceable>NAMESPACE</replaceable>.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<listitem><para>Takes a boolean argument, defaults to on. If off,
<citerefentry><refentrytitle>systemd-firstboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ and
+ <citerefentry><refentrytitle>systemd-homed-firstboot.service</refentrytitle><manvolnum>1</manvolnum></citerefentry>
will not query the user for basic system settings, even if the system boots up for the first time and
the relevant settings are not initialized yet. Not to be confused with
<varname>systemd.condition-first-boot=</varname> (see below), which overrides the result of the
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/logind.conf</filename></para>
- <para><filename>/etc/systemd/logind.conf.d/*.conf</filename></para>
- <para><filename>/run/systemd/logind.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/logind.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/logind.conf</filename></member>
+ <member><filename>/etc/systemd/logind.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/logind.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/logind.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/modules-load.d/*.conf</filename></para>
- <para><filename>/run/modules-load.d/*.conf</filename></para>
- <para><filename>/usr/lib/modules-load.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/modules-load.d/*.conf</filename></member>
+ <member><filename>/run/modules-load.d/*.conf</filename></member>
+ <member><filename>/usr/lib/modules-load.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>
+ <command>mask</command>
+ <replaceable>FILE</replaceable>…
+ </term>
+ <listitem><para>Mask network configuration files, which include <filename>.network</filename>,
+ <filename>.netdev</filename>, and <filename>.link</filename> files. A symlink of the given name will
+ be created under <filename>/etc/</filename> or <filename>/run/</filename>, depending on
+ whether <option>--runtime</option> is specified, that points to <filename>/dev/null</filename>.
+ If a non-empty config file with the specified name exists under the target directory or a directory
+ with higher priority (e.g. <option>--runtime</option> is used while an existing config resides
+ in <filename>/etc/</filename>), the operation is aborted.</para>
+
+ <para>This command honors <option>--no-reload</option> in the same way as <command>edit</command>.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <command>unmask</command>
+ <replaceable>FILE</replaceable>…
+ </term>
+ <listitem><para>Unmask network configuration files, i.e. reverting the effect of <command>mask</command>.
+ Note that this command operates regardless of the scope of the directory, i.e. <option>--runtime</option>
+ is of no effect.</para>
+
+ <para>This command honors <option>--no-reload</option> in the same way as <command>edit</command>
+ and <command>mask</command>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<term><option>--no-reload</option></term>
<listitem>
- <para>When used with <command>edit</command>,
+ <para>When used with <command>edit</command>, <command>mask</command>, or <command>unmask</command>,
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
or
<citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- will not be reloaded after the editing finishes.</para>
+ will not be reloaded after the operation finishes.</para>
<xi:include href="version-info.xml" xpointer="v254"/>
</listitem>
<term><option>--runtime</option></term>
<listitem>
- <para>When used with <command>edit</command>, edit the file under <filename>/run/</filename>
- instead of <filename>/etc/</filename>.</para>
+ <para>When used with <command>edit</command> or <command>mask</command>,
+ operate on the file under <filename>/run/</filename> instead of <filename>/etc/</filename>.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/networkd.conf</filename></para>
- <para><filename>/etc/systemd/networkd.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/networkd.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/networkd.conf</filename></member>
+ <member><filename>/etc/systemd/networkd.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/networkd.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<xi:include href="version-info.xml" xpointer="v246"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ManageForeignNextHops=</varname></term>
+ <listitem><para>A boolean. When true, <command>systemd-networkd</command> will remove nexthops
+ that are not configured in .network files (except for routes with protocol
+ <literal>kernel</literal>). When false, it will
+ not remove any foreign nexthops, keeping them even if they are not configured in a .network file.
+ Defaults to yes.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>RouteTable=</varname></term>
<listitem><para>Defines the route table name. Takes a whitespace-separated list of the pairs of
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/oomd.conf</filename></para>
- <para><filename>/etc/systemd/oomd.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/oomd.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/oomd.conf</filename></member>
+ <member><filename>/etc/systemd/oomd.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/oomd.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<para><function>ResetFailed()</function> resets the "failed" state of all units.</para>
<para><function>ListUnits()</function> returns an array of all currently loaded units. Note that
- units may be known by multiple names at the same name, and hence there might be more unit names loaded
+ units may be known by multiple names at the same time, and hence there might be more unit names loaded
than actual units behind them. The array consists of structures with the following elements:
<itemizedlist>
<listitem><para>The primary unit name as string</para></listitem>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/os-release</filename></para>
- <para><filename>/usr/lib/os-release</filename></para>
- <para><filename>/etc/initrd-release</filename></para>
- <para><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename></para>
+ <para><simplelist>
+ <member><filename>/etc/os-release</filename></member>
+ <member><filename>/usr/lib/os-release</filename></member>
+ <member><filename>/etc/initrd-release</filename></member>
+ <member><filename>/usr/lib/extension-release.d/extension-release.<replaceable>IMAGE</replaceable></filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><literallayout><filename>/etc/repart.d/*.conf</filename>
-<filename>/run/repart.d/*.conf</filename>
-<filename>/usr/lib/repart.d/*.conf</filename>
- </literallayout></para>
+ <para><simplelist>
+ <member><filename>/etc/repart.d/*.conf</filename></member>
+ <member><filename>/run/repart.d/*.conf</filename></member>
+ <member><filename>/usr/lib/repart.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/resolved.conf</filename></para>
- <para><filename>/etc/systemd/resolved.conf.d/*.conf</filename></para>
- <para><filename>/run/systemd/resolved.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/resolved.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/resolved.conf</filename></member>
+ <member><filename>/etc/systemd/resolved.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/resolved.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/resolved.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
'ENABLE_RESOLVE'],
['environment.d', '5', [], 'ENABLE_ENVIRONMENT_D'],
['file-hierarchy', '7', [], ''],
- ['homectl', '1', [], 'ENABLE_HOMED'],
+ ['homectl', '1', ['systemd-homed-firstboot.service'], 'ENABLE_HOMED'],
['homed.conf', '5', ['homed.conf.d'], 'ENABLE_HOMED'],
['hostname', '5', [], ''],
['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'],
''],
['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
['udevadm', '8', [], ''],
+ ['uid0', '1', [], ''],
['ukify', '1', [], 'ENABLE_UKIFY'],
['user@.service',
'5',
for more information about D-Bus IPC.</para>
<para>See
- <literallayout><citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_object</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_object_manager</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_object_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_fallback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_fallback_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_filter</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_add_node_enumerator</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_call_method_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_interfaces_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_interfaces_added_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_interfaces_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_interfaces_removed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_object_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_object_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_properties_changed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_properties_changed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_signalv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_signal_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_emit_signal_tov</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_bus_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_creds_mask</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_current_slot</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_n_queued_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_name_machine_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_property_string</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_property_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_property_trivial</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_scope</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_tid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_get_unique_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_interface_name_is_valid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_is_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_is_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_is_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_list_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_at_end</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_close_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_copy</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_dump</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_enter_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_exit_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_cookie</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_signature</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_new_signal_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_open_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_peek_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_read_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_read_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_rewind</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_seal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_set_expect_reply</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_skip</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_message_verify_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_query_sender_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_query_sender_privilege</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_reply_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_reply_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_send_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_propertyv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_set_watch_bind</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-<citerefentry><refentrytitle>sd_bus_slot_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_set_destroy_callback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_slot_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-</literallayout>
+ <simplelist>
+ <member><citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_add_object</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_add_object_manager</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_add_object_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_add_fallback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_add_fallback_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_add_filter</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_add_node_enumerator</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_call_method_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_interfaces_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_interfaces_added_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_interfaces_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_interfaces_removed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_object_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_object_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_properties_changed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_properties_changed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_signalv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_signal_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_emit_signal_tov</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_bus_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_creds_mask</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_current_slot</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_n_queued_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_name_machine_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_property_string</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_property_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_property_trivial</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_scope</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_tid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_get_unique_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_interface_name_is_valid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_is_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_is_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_is_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_list_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_at_end</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_close_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_copy</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_dump</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_enter_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_exit_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_get_cookie</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_get_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_get_errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_get_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_get_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_get_signature</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_new_signal_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_open_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_peek_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_read_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_read_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_rewind</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_seal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_set_expect_reply</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_skip</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_message_verify_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_negotiate_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_query_sender_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_query_sender_privilege</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_reply_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_reply_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_send_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_propertyv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_set_watch_bind</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_bus_slot_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_slot_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_slot_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_slot_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_slot_set_destroy_callback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_slot_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ </simplelist>
for more information about the functions available.</para>
</refsect1>
<filename>libudev.h</filename>.</para>
<para>See
- <literallayout><citerefentry><refentrytitle>sd_device_get_syspath</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-<citerefentry><refentrytitle>sd_device_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-</literallayout>
+ <simplelist>
+ <member><citerefentry><refentrytitle>sd_device_get_syspath</refentrytitle><manvolnum>3</manvolnum></citerefentry>,</member>
+ <member><citerefentry><refentrytitle>sd_device_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ </simplelist>
for more information about the functions available.</para>
</refsect1>
</refsect1>
<refsect1>
- <title>Core OS Command Line Arguments</title>
+ <title>Strings</title>
<para>The following strings are supported:</para>
<ulink url="https://systemd.io/CREDENTIALS">System and Service Credentials</ulink> for
details.</para>
- <xi:include href="version-info.xml" xpointer="v254"/></listitem>
+ <xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry>
<varlistentry>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/sysctl.d/*.conf</filename></para>
- <para><filename>/run/sysctl.d/*.conf</filename></para>
- <para><filename>/usr/lib/sysctl.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/sysctl.d/*.conf</filename></member>
+ <member><filename>/run/sysctl.d/*.conf</filename></member>
+ <member><filename>/usr/lib/sysctl.d/*.conf</filename></member>
+ </simplelist></para>
<programlisting>key.name.under.proc.sys = some value
key/name/under/proc/sys = some value
for more details. The patterns are matched against the primary names of
units currently in memory, and patterns which do not match anything
are silently skipped. For example:
- <programlisting># systemctl stop sshd@*.service</programlisting>
+ <programlisting># systemctl stop "sshd@*.service"</programlisting>
will stop all <filename>sshd@.service</filename> instances. Note that alias names of units, and units that aren't
in memory are not considered for glob expansion.
</para>
<term><option>--firmware-setup</option></term>
<listitem>
- <para>When used with the <command>reboot</command> command, indicate to the system's firmware to
- reboot into the firmware setup interface. Note that this functionality is not available on all
- systems.</para>
+ <para>When used with the <command>reboot</command>, <command>poweroff</command>, or
+ <command>halt</command> command, indicate to the system's firmware to reboot into the firmware
+ setup interface for the next boot. Note that this functionality is not available on all systems.
+ </para>
<xi:include href="version-info.xml" xpointer="v220"/>
</listitem>
<term><option>--boot-loader-menu=<replaceable>timeout</replaceable></option></term>
<listitem>
- <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to
- show the boot loader menu on the following boot. Takes a time value as parameter — indicating the
- menu timeout. Pass zero in order to disable the menu timeout. Note that not all boot loaders
- support this functionality.</para>
+ <para>When used with the <command>reboot</command>, <command>poweroff</command>, or
+ <command>halt</command> command, indicate to the system's boot loader to show the boot loader menu
+ on the following boot. Takes a time value as parameter — indicating the menu timeout. Pass zero
+ in order to disable the menu timeout. Note that not all boot loaders support this functionality.
+ </para>
<xi:include href="version-info.xml" xpointer="v242"/>
</listitem>
<term><option>--boot-loader-entry=<replaceable>ID</replaceable></option></term>
<listitem>
- <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to
- boot into a specific boot loader entry on the following boot. Takes a boot loader entry identifier
- as argument, or <literal>help</literal> in order to list available entries. Note that not all boot
- loaders support this functionality.</para>
+ <para>When used with the <command>reboot</command>, <command>poweroff</command>, or
+ <command>halt</command> command, indicate to the system's boot loader to boot into a specific
+ boot loader entry on the following boot. Takes a boot loader entry identifier as argument,
+ or <literal>help</literal> in order to list available entries. Note that not all boot loaders
+ support this functionality.</para>
<xi:include href="version-info.xml" xpointer="v242"/>
</listitem>
</refnamediv>
<refsynopsisdiv>
- <para><filename>systemd-ask-password-console.service</filename></para>
- <para><filename>systemd-ask-password-console.path</filename></para>
- <para><filename>systemd-ask-password-wall.service</filename></para>
- <para><filename>systemd-ask-password-wall.path</filename></para>
+ <para><simplelist>
+ <member><filename>systemd-ask-password-console.service</filename></member>
+ <member><filename>systemd-ask-password-console.path</filename></member>
+ <member><filename>systemd-ask-password-wall.service</filename></member>
+ <member><filename>systemd-ask-password-wall.path</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
supports tokens and credentials of the following kind to be enrolled:</para>
<orderedlist>
- <listitem><para>PKCS#11 security tokens and smartcards that may carry an RSA key pair (e.g. various
- YubiKeys)</para></listitem>
+ <listitem><para>PKCS#11 security tokens and smartcards that may carry an RSA or EC key pair (e.g.
+ various YubiKeys)</para></listitem>
<listitem><para>FIDO2 security tokens that implement the <literal>hmac-secret</literal> extension (most
FIDO2 keys, including YubiKeys)</para></listitem>
smartcard URI referring to the token. Alternatively the special value <literal>auto</literal> may
be specified, in order to automatically determine the URI of a currently plugged in security token
(of which there must be exactly one). The special value <literal>list</literal> may be used to
- enumerate all suitable PKCS#11 tokens currently plugged in. The security token must contain an RSA
- key pair which is used to encrypt the randomly generated key that is used to unlock the LUKS2
- volume. The encrypted key is then stored in the LUKS2 JSON token header area.</para>
+ enumerate all suitable PKCS#11 tokens currently plugged in.</para>
+
+ <para>The PKCS#11 token must contain an RSA or EC key pair which will be used to unlock a LUKS2 volume.
+ For RSA, a randomly generated volume key is encrypted with a public key in the token, and stored in
+ the LUKS2 JSON token header area. To unlock a volume, the stored encrypted volume key will be decrypted
+ with a private key in the token. For ECC, ECDH algorithm is used: we generate a pair of EC keys in
+ the same EC group, then derive a shared secret using the generated private key and the public key
+ in the token. The derived shared secret is used as a volume key. The generated public key is
+ stored in the LUKS2 JSON token header area. The generated private key is erased. To unlock a volume,
+ we derive the shared secret with the stored public key and a private key in the token.</para>
<para>In order to unlock a LUKS2 volume with an enrolled PKCS#11 security token, specify the
<option>pkcs11-uri=</option> option in the respective <filename>/etc/crypttab</filename> line:</para>
</refnamediv>
<refsynopsisdiv>
- <para><filename>systemd-fsck@.service</filename></para>
- <para><filename>systemd-fsck-root.service</filename></para>
- <para><filename>systemd-fsck-usr.service</filename></para>
- <para><filename>/usr/lib/systemd/systemd-fsck</filename></para>
+ <para><simplelist>
+ <member><filename>systemd-fsck@.service</filename></member>
+ <member><filename>systemd-fsck-root.service</filename></member>
+ <member><filename>systemd-fsck-usr.service</filename></member>
+ <member><filename>/usr/lib/systemd/systemd-fsck</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>systemd-journald.service</filename></para>
- <para><filename>systemd-journald.socket</filename></para>
- <para><filename>systemd-journald-dev-log.socket</filename></para>
- <para><filename>systemd-journald-audit.socket</filename></para>
- <para><filename>systemd-journald@.service</filename></para>
- <para><filename>systemd-journald@.socket</filename></para>
- <para><filename>systemd-journald-varlink@.socket</filename></para>
- <para><filename>/usr/lib/systemd/systemd-journald</filename></para>
+ <para><simplelist>
+ <member><filename>systemd-journald.service</filename></member>
+ <member><filename>systemd-journald.socket</filename></member>
+ <member><filename>systemd-journald-dev-log.socket</filename></member>
+ <member><filename>systemd-journald-audit.socket</filename></member>
+ <member><filename>systemd-journald@.service</filename></member>
+ <member><filename>systemd-journald@.socket</filename></member>
+ <member><filename>systemd-journald-varlink@.socket</filename></member>
+ <member><filename>/usr/lib/systemd/systemd-journald</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<term><option>--slice-inherit</option></term>
<listitem><para>Make the new <filename>.service</filename> or <filename>.scope</filename> unit part
- of the inherited slice. This option can be combined with <option>--slice=</option>.</para>
-
- <para>An inherited slice is located within <command>systemd-run</command> slice. Example: if
- <command>systemd-run</command> slice is <filename>foo.slice</filename>, and the
- <option>--slice=</option> argument is <filename>bar</filename>, the unit will be placed under the
+ of the slice the <command>systemd-run</command> itself has been invoked in. This option may be
+ combined with <option>--slice=</option>, in which case the slice specified via
+ <option>--slice=</option> is placed within the slice the <command>systemd-run</command> command is
+ invoked in.</para>
+
+ <para>Example: consider <command>systemd-run</command> being invoked in the slice
+ <filename>foo.slice</filename>, and the <option>--slice=</option> argument is
+ <filename>bar</filename>. The unit will then be placed under
<filename>foo-bar.slice</filename>.</para>
<xi:include href="version-info.xml" xpointer="v246"/>
<xi:include href="version-info.xml" xpointer="v236"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--ignore-failure</option></term>
+
+ <listitem><para>By default, if the specified command fails the invoked unit will be marked failed
+ (though possibly still unloaded, see <option>--collect=</option>, above), and this is reported in the
+ logs. If this switch is specified this is suppressed and any non-success exit status/code of the
+ command is treated as success.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--background=<replaceable>COLOR</replaceable></option></term>
+
+ <listitem><para>Change the terminal background color to the specified ANSI color as long as the
+ session lasts. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as
+ <literal>40</literal>, <literal>41</literal>, …, <literal>47</literal>, <literal>48;2;…</literal>,
+ <literal>48;5;…</literal>. See <ulink
+ url="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">ANSI
+ Escape Code (Wikipedia)</ulink> for details.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
<xi:include href="user-system-options.xml" xpointer="user" />
<xi:include href="user-system-options.xml" xpointer="system" />
<xi:include href="user-system-options.xml" xpointer="host" />
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-mount</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>uid0</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/sleep.conf</filename></para>
- <para><filename>/etc/systemd/sleep.conf.d/*.conf</filename></para>
- <para><filename>/run/systemd/sleep.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/sleep.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/sleep.conf</filename></member>
+ <member><filename>/etc/systemd/sleep.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/sleep.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/sleep.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/usr/lib/systemd/boot/efi/linuxx64.efi.stub</filename></para>
- <para><filename>/usr/lib/systemd/boot/efi/linuxia32.efi.stub</filename></para>
- <para><filename>/usr/lib/systemd/boot/efi/linuxaa64.efi.stub</filename></para>
- <para><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename></para>
- <para><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.cred</filename></para>
- <para><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.raw</filename></para>
- <para><filename><replaceable>ESP</replaceable>/loader/addons/*.addon.efi</filename></para>
- <para><filename><replaceable>ESP</replaceable>/loader/credentials/*.cred</filename></para>
+ <para><simplelist>
+ <member><filename>/usr/lib/systemd/boot/efi/linuxx64.efi.stub</filename></member>
+ <member><filename>/usr/lib/systemd/boot/efi/linuxia32.efi.stub</filename></member>
+ <member><filename>/usr/lib/systemd/boot/efi/linuxaa64.efi.stub</filename></member>
+ <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename></member>
+ <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.cred</filename></member>
+ <member><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.raw</filename></member>
+ <member><filename><replaceable>ESP</replaceable>/loader/addons/*.addon.efi</filename></member>
+ <member><filename><replaceable>ESP</replaceable>/loader/credentials/*.cred</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>systemd-suspend.service</filename></para>
- <para><filename>systemd-hibernate.service</filename></para>
- <para><filename>systemd-hybrid-sleep.service</filename></para>
- <para><filename>systemd-suspend-then-hibernate.service</filename></para>
- <para><filename>/usr/lib/systemd/system-sleep</filename></para>
+ <para><simplelist>
+ <member><filename>systemd-suspend.service</filename></member>
+ <member><filename>systemd-hibernate.service</filename></member>
+ <member><filename>systemd-hybrid-sleep.service</filename></member>
+ <member><filename>systemd-suspend-then-hibernate.service</filename></member>
+ <member><filename>/usr/lib/systemd/system-sleep</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<arg choice="plain">COMMAND</arg>
</cmdsynopsis>
- <para><literallayout><filename>systemd-sysext.service</filename></literallayout></para>
+ <para><filename>systemd-sysext.service</filename></para>
<cmdsynopsis>
<command>systemd-confext</command>
<arg choice="plain">COMMAND</arg>
</cmdsynopsis>
- <para><literallayout><filename>systemd-confext.service</filename></literallayout></para>
-
+ <para><filename>systemd-confext.service</filename></para>
</refsynopsisdiv>
<refsect1>
</cmdsynopsis>
<para>System units:
-<literallayout><filename>systemd-tmpfiles-setup.service</filename>
-<filename>systemd-tmpfiles-setup-dev-early.service</filename>
-<filename>systemd-tmpfiles-setup-dev.service</filename>
-<filename>systemd-tmpfiles-clean.service</filename>
-<filename>systemd-tmpfiles-clean.timer</filename></literallayout></para>
+ <simplelist>
+ <member><filename>systemd-tmpfiles-setup.service</filename></member>
+ <member><filename>systemd-tmpfiles-setup-dev-early.service</filename></member>
+ <member><filename>systemd-tmpfiles-setup-dev.service</filename></member>
+ <member><filename>systemd-tmpfiles-clean.service</filename></member>
+ <member><filename>systemd-tmpfiles-clean.timer</filename></member>
+ </simplelist>
+ </para>
<para>User units:
-<literallayout><filename>systemd-tmpfiles-setup.service</filename>
-<filename>systemd-tmpfiles-clean.service</filename>
-<filename>systemd-tmpfiles-clean.timer</filename></literallayout></para>
+ <simplelist>
+ <member><filename>systemd-tmpfiles-setup.service</filename></member>
+ <member><filename>systemd-tmpfiles-clean.service</filename></member>
+ <member><filename>systemd-tmpfiles-clean.timer</filename></member>
+ </simplelist>
+ </para>
</refsynopsisdiv>
<refsect1>
<command>&USER_ENV_GENERATOR_DIR;/some-generator</command>
</cmdsynopsis>
- <para>
- <literallayout><filename>/run/systemd/system-environment-generators/*</filename>
-<filename>/etc/systemd/system-environment-generators/*</filename>
-<filename>/usr/local/lib/systemd/system-environment-generators/*</filename>
-<filename>&SYSTEM_ENV_GENERATOR_DIR;/*</filename></literallayout>
- </para>
-
- <para>
- <literallayout><filename>/run/systemd/user-environment-generators/*</filename>
-<filename>/etc/systemd/user-environment-generators/*</filename>
-<filename>/usr/local/lib/systemd/user-environment-generators/*</filename>
-<filename>&USER_ENV_GENERATOR_DIR;/*</filename></literallayout>
- </para>
+ <para><simplelist>
+ <member><filename>/run/systemd/system-environment-generators/*</filename></member>
+ <member><filename>/etc/systemd/system-environment-generators/*</filename></member>
+ <member><filename>/usr/local/lib/systemd/system-environment-generators/*</filename></member>
+ <member><filename>&SYSTEM_ENV_GENERATOR_DIR;/*</filename></member>
+ </simplelist></para>
+
+ <para><simplelist>
+ <member><filename>/run/systemd/user-environment-generators/*</filename></member>
+ <member><filename>/etc/systemd/user-environment-generators/*</filename></member>
+ <member><filename>/usr/local/lib/systemd/user-environment-generators/*</filename></member>
+ <member><filename>&USER_ENV_GENERATOR_DIR;/*</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<varlistentry>
<term><varname>SetLoginEnvironment=</varname></term>
- <listitem><para>Takes a boolean parameter that controls whether to set <varname>$HOME</varname>,
- <varname>$LOGNAME</varname>, and <varname>$SHELL</varname> environment variables. If unset, this is
- controlled by whether <varname>User=</varname> is set. If true, they will always be set for system services,
- i.e. even when the default user <literal>root</literal> is used. If false, the mentioned variables are not set
- by systemd, no matter whether <varname>User=</varname> is used or not. This option normally has no effect
- on user services, since these variables are typically inherited from user manager's own environment anyway.</para>
+ <listitem><para>Takes a boolean parameter that controls whether to set the <varname>$HOME</varname>,
+ <varname>$LOGNAME</varname>, and <varname>$SHELL</varname> environment variables. If not set, this
+ defaults to true if <varname>User=</varname>, <varname>DynamicUser=</varname> or
+ <varname>PAMName=</varname> are set, false otherwise. If set to true, the variables will always be
+ set for system services, i.e. even when the default user <literal>root</literal> is used. If set to
+ false, the mentioned variables are not set by the service manager, no matter whether
+ <varname>User=</varname>, <varname>DynamicUser=</varname>, or <varname>PAMName=</varname> are used or
+ not. This option normally has no effect on services of the per-user service manager, since in that
+ case these variables are typically inherited from user manager's own environment anyway.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<arg choice="option"><replaceable>late-dir</replaceable></arg>
</cmdsynopsis>
- <para>
- <literallayout><filename>/run/systemd/system-generators/*</filename>
-<filename>/etc/systemd/system-generators/*</filename>
-<filename>/usr/local/lib/systemd/system-generators/*</filename>
-<filename>&SYSTEM_GENERATOR_DIR;/*</filename></literallayout>
- </para>
-
- <para>
- <literallayout><filename>/run/systemd/user-generators/*</filename>
-<filename>/etc/systemd/user-generators/*</filename>
-<filename>/usr/local/lib/systemd/user-generators/*</filename>
-<filename>&USER_GENERATOR_DIR;/*</filename></literallayout>
- </para>
+ <para><simplelist>
+ <member><filename>/run/systemd/system-generators/*</filename></member>
+ <member><filename>/etc/systemd/system-generators/*</filename></member>
+ <member><filename>/usr/local/lib/systemd/system-generators/*</filename></member>
+ <member><filename>&SYSTEM_GENERATOR_DIR;/*</filename></member>
+ </simplelist></para>
+
+ <para><simplelist>
+ <member><filename>/run/systemd/user-generators/*</filename></member>
+ <member><filename>/etc/systemd/user-generators/*</filename></member>
+ <member><filename>/usr/local/lib/systemd/user-generators/*</filename></member>
+ <member><filename>&USER_GENERATOR_DIR;/*</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
particular version of systemd).</para>
</refsect1>
+ <refsect1>
+ <title>Limiting the use of specific sysfs attributes</title>
+
+ <para>When creating names for network cards, some naming schemes use data from sysfs populated
+ by the kernel. This means that although a specific naming scheme in udev is picked,
+ the network card's name can still change when a new kernel version adds a new sysfs attribute.
+ For example if kernel starts setting the <constant>phys_port_name</constant>, udev will append the
+ "<constant>n</constant><replaceable>phys_port_name</replaceable>" suffix to the device name.</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>ID_NET_NAME_ALLOW=<replaceable>BOOL</replaceable></varname></term>
+
+ <listitem><para>This udev property sets a fallback policy for reading a sysfs attribute.
+ If set to <constant>0</constant> udev will not read any sysfs attribute by default, unless it is
+ explicitly allowlisted, see below. If set to <constant>1</constant> udev can use any sysfs attribute
+ unless it is explicitly forbidden. The default value is <constant>1</constant>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ID_NET_NAME_ALLOW_<replaceable>sysfsattr</replaceable>=<replaceable>BOOL</replaceable></varname></term>
+
+ <listitem><para>This udev property explicitly states if udev shall use the specified
+ <replaceable>sysfsattr</replaceable>, when composing the device name.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>With these options, users can set an allowlist or denylist for sysfs attributes. To create
+ an allowlist, the user needs to set <varname>ID_NET_NAME_ALLOW=0</varname> for the device and then list
+ the allowed attributes with the
+ <varname>ID_NET_NAME_ALLOW_<replaceable>sysfsattr</replaceable>=1</varname>
+ options. In case of a denylist, the user needs to provide the list of denied attributes with
+ the <varname>ID_NET_NAME_ALLOW_<replaceable>sysfsattr</replaceable>=0</varname> options.</para>
+ </refsect1>
+
<refsect1>
<title>Examples</title>
ID_NET_NAME_MAC=enx026d3c00000a
ID_NET_NAME_PATH=encf5f0</programlisting>
</example>
+
+ <example>
+ <title>Set an allowlist for reading sysfs attributes for network card naming</title>
+
+ <programlisting><filename>/etc/udev/hwdb.d/50-net-naming-allowlist.hwdb</filename>
+net:naming:drvirtio_net:*
+ ID_NET_NAME_ALLOW=0
+ ID_NET_NAME_ALLOW_ACPI_INDEX=1
+ ID_NET_NAME_ALLOW_ADDR_ASSIGN_TYPE=1
+ ID_NET_NAME_ALLOW_ADDRESS=1
+ ID_NET_NAME_ALLOW_ARI_ENABLED=1
+ ID_NET_NAME_ALLOW_DEV_PORT=1
+ ID_NET_NAME_ALLOW_FUNCTION_ID=1
+ ID_NET_NAME_ALLOW_IFLINK=1
+ ID_NET_NAME_ALLOW_INDEX=1
+ ID_NET_NAME_ALLOW_LABEL=1
+ ID_NET_NAME_ALLOW_PHYS_PORT_NAME=1
+ ID_NET_NAME_ALLOW_TYPE=1</programlisting>
+ </example>
+
+ <example>
+ <title>Set a denylist so that specified sysfs attribute are ignored</title>
+
+ <programlisting><filename>/etc/udev/hwdb.d/50-net-naming-denylist.hwdb</filename>
+net:naming:drvirtio_net:*
+ ID_NET_NAME_ALLOW=1
+ ID_NET_NAME_ALLOW_DEV_PORT=0
+ ID_NET_NAME_ALLOW_PHYS_PORT_NAME=0
+ </programlisting>
+ </example>
</refsect1>
<refsect1>
<varlistentry>
<term><varname>Id=</varname></term>
<listitem>
- <para>The id of the next hop. Takes an integer in the range 1…4294967295. If unspecified,
- then automatically chosen by kernel.</para>
+ <para>The id of the next hop. Takes an integer in the range 1…4294967295.
+ This is mandatory if <varname>ManageForeignNextHops=no</varname> is specified in
+ <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Otherwise, if unspecified, an unused ID will be automatically picked.</para>
<xi:include href="version-info.xml" xpointer="v244"/>
</listitem>
</varlistentry>
</variablelist>
- </refsect1>
+ </refsect1>
- <refsect1>
+ <refsect1>
<title>[IPv6Prefix] Section Options</title>
<para>One or more [IPv6Prefix] sections contain the IPv6 prefixes that are announced via Router
Advertisements. See <ulink url="https://tools.ietf.org/html/rfc4861">RFC 4861</ulink> for further
</listitem>
</varlistentry>
</variablelist>
- </refsect1>
+ </refsect1>
- <refsect1>
+ <refsect1>
<title>[IPv6RoutePrefix] Section Options</title>
<para>One or more [IPv6RoutePrefix] sections contain the IPv6
prefix routes that are announced via Router Advertisements. See
</varlistentry>
</variablelist>
- </refsect1>
+ </refsect1>
- <refsect1>
+ <refsect1>
<title>[IPv6PREF64Prefix] Section Options</title>
<para>One or more [IPv6PREF64Prefix] sections contain the IPv6 PREF64 (or NAT64) prefixes that are announced via Router
Advertisements. See <ulink url="https://tools.ietf.org/html/rfc8781">RFC 8781</ulink> for further
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
</variablelist>
- </refsect1>
+ </refsect1>
- <refsect1>
+ <refsect1>
<title>[Bridge] Section Options</title>
- <para>The [Bridge] section accepts the following keys:</para>
- <variablelist class='network-directives'>
- <varlistentry>
- <term><varname>UnicastFlood=</varname></term>
- <listitem>
- <para>Takes a boolean. Controls whether the bridge should flood
- traffic for which an FDB entry is missing and the destination
- is unknown through this port. When unset, the kernel's default will be used.
- </para>
-
- <xi:include href="version-info.xml" xpointer="v223"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>MulticastFlood=</varname></term>
- <listitem>
- <para>Takes a boolean. Controls whether the bridge should flood
- traffic for which an MDB entry is missing and the destination
- is unknown through this port. When unset, the kernel's default will be used.
- </para>
-
- <xi:include href="version-info.xml" xpointer="v242"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>MulticastToUnicast=</varname></term>
- <listitem>
- <para>Takes a boolean. Multicast to unicast works on top of the multicast snooping feature of
- the bridge. Which means unicast copies are only delivered to hosts which are interested in it.
- When unset, the kernel's default will be used.
- </para>
-
- <xi:include href="version-info.xml" xpointer="v240"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>NeighborSuppression=</varname></term>
- <listitem>
- <para>Takes a boolean. Configures whether ARP and ND neighbor suppression is enabled for
- this port. When unset, the kernel's default will be used.
- </para>
-
- <xi:include href="version-info.xml" xpointer="v242"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Learning=</varname></term>
- <listitem>
- <para>Takes a boolean. Configures whether MAC address learning is enabled for
- this port. When unset, the kernel's default will be used.
- </para>
-
- <xi:include href="version-info.xml" xpointer="v242"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>HairPin=</varname></term>
- <listitem>
- <para>Takes a boolean. Configures whether traffic may be sent back out of the port on which it
- was received. When this flag is false, then the bridge will not forward traffic back out of the
- receiving port. When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v223"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Isolated=</varname></term>
- <listitem>
- <para>Takes a boolean. Configures whether this port is isolated or not. Within a bridge,
- isolated ports can only communicate with non-isolated ports. When set to true, this port can only
- communicate with other ports whose Isolated setting is false. When set to false, this port
- can communicate with any other ports. When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v251"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>UseBPDU=</varname></term>
- <listitem>
- <para>Takes a boolean. Configures whether STP Bridge Protocol Data Units will be
- processed by the bridge port. When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v223"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>FastLeave=</varname></term>
- <listitem>
- <para>Takes a boolean. This flag allows the bridge to immediately stop multicast
- traffic on a port that receives an IGMP Leave message. It is only used with
- IGMP snooping if enabled on the bridge. When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v223"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>AllowPortToBeRoot=</varname></term>
- <listitem>
- <para>Takes a boolean. Configures whether a given port is allowed to
- become a root port. Only used when STP is enabled on the bridge.
- When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v223"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>ProxyARP=</varname></term>
- <listitem>
- <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port.
- When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v243"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>ProxyARPWiFi=</varname></term>
- <listitem>
- <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port
- which meets extended requirements by IEEE 802.11 and Hotspot 2.0 specifications.
- When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v243"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>MulticastRouter=</varname></term>
- <listitem>
- <para>Configures this port for having multicast routers attached. A port with a multicast
- router will receive all multicast traffic. Takes one of <literal>no</literal>
- to disable multicast routers on this port, <literal>query</literal> to let the system detect
- the presence of routers, <literal>permanent</literal> to permanently enable multicast traffic
- forwarding on this port, or <literal>temporary</literal> to enable multicast routers temporarily
- on this port, not depending on incoming queries. When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v243"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Cost=</varname></term>
- <listitem>
- <para>Sets the "cost" of sending packets of this interface.
- Each port in a bridge may have a different speed and the cost
- is used to decide which link to use. Faster interfaces
- should have lower costs. It is an integer value between 1 and
- 65535.</para>
-
- <xi:include href="version-info.xml" xpointer="v218"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Priority=</varname></term>
- <listitem>
- <para>Sets the "priority" of sending packets on this interface.
- Each port in a bridge may have a different priority which is used
- to decide which link to use. Lower value means higher priority.
- It is an integer value between 0 to 63. Networkd does not set any
- default, meaning the kernel default value of 32 is used.</para>
-
- <xi:include href="version-info.xml" xpointer="v234"/>
- </listitem>
- </varlistentry>
- </variablelist>
+ <para>The [Bridge] section accepts the following keys:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>UnicastFlood=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Controls whether the bridge should flood
+ traffic for which an FDB entry is missing and the destination
+ is unknown through this port. When unset, the kernel's default will be used.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v223"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>MulticastFlood=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Controls whether the bridge should flood
+ traffic for which an MDB entry is missing and the destination
+ is unknown through this port. When unset, the kernel's default will be used.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v242"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>MulticastToUnicast=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Multicast to unicast works on top of the multicast snooping feature of
+ the bridge. Which means unicast copies are only delivered to hosts which are interested in it.
+ When unset, the kernel's default will be used.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v240"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>NeighborSuppression=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Configures whether ARP and ND neighbor suppression is enabled for
+ this port. When unset, the kernel's default will be used.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v242"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Learning=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Configures whether MAC address learning is enabled for
+ this port. When unset, the kernel's default will be used.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v242"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>HairPin=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Configures whether traffic may be sent back out of the port on which it
+ was received. When this flag is false, then the bridge will not forward traffic back out of the
+ receiving port. When unset, the kernel's default will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v223"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Isolated=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Configures whether this port is isolated or not. Within a bridge,
+ isolated ports can only communicate with non-isolated ports. When set to true, this port can only
+ communicate with other ports whose Isolated setting is false. When set to false, this port
+ can communicate with any other ports. When unset, the kernel's default will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v251"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>UseBPDU=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Configures whether STP Bridge Protocol Data Units will be
+ processed by the bridge port. When unset, the kernel's default will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v223"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>FastLeave=</varname></term>
+ <listitem>
+ <para>Takes a boolean. This flag allows the bridge to immediately stop multicast
+ traffic on a port that receives an IGMP Leave message. It is only used with
+ IGMP snooping if enabled on the bridge. When unset, the kernel's default will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v223"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>AllowPortToBeRoot=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Configures whether a given port is allowed to
+ become a root port. Only used when STP is enabled on the bridge.
+ When unset, the kernel's default will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v223"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ProxyARP=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port.
+ When unset, the kernel's default will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v243"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ProxyARPWiFi=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Configures whether proxy ARP to be enabled on this port
+ which meets extended requirements by IEEE 802.11 and Hotspot 2.0 specifications.
+ When unset, the kernel's default will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v243"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>MulticastRouter=</varname></term>
+ <listitem>
+ <para>Configures this port for having multicast routers attached. A port with a multicast
+ router will receive all multicast traffic. Takes one of <literal>no</literal>
+ to disable multicast routers on this port, <literal>query</literal> to let the system detect
+ the presence of routers, <literal>permanent</literal> to permanently enable multicast traffic
+ forwarding on this port, or <literal>temporary</literal> to enable multicast routers temporarily
+ on this port, not depending on incoming queries. When unset, the kernel's default will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v243"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Cost=</varname></term>
+ <listitem>
+ <para>Sets the "cost" of sending packets of this interface.
+ Each port in a bridge may have a different speed and the cost
+ is used to decide which link to use. Faster interfaces
+ should have lower costs. It is an integer value between 1 and
+ 65535.</para>
+
+ <xi:include href="version-info.xml" xpointer="v218"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Priority=</varname></term>
+ <listitem>
+ <para>Sets the "priority" of sending packets on this interface.
+ Each port in a bridge may have a different priority which is used
+ to decide which link to use. Lower value means higher priority.
+ It is an integer value between 0 to 63. Networkd does not set any
+ default, meaning the kernel default value of 32 is used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v234"/>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
+
<refsect1>
<title>[BridgeFDB] Section Options</title>
- <para>The [BridgeFDB] section manages the forwarding database table of a port and accepts the following
- keys. Specify several [BridgeFDB] sections to configure several static MAC table entries.</para>
- <variablelist class='network-directives'>
- <varlistentry>
- <term><varname>MACAddress=</varname></term>
- <listitem>
- <para>As in the [Network] section. This key is mandatory.</para>
+ <para>The [BridgeFDB] section manages the forwarding database table of a port and accepts the following
+ keys. Specify several [BridgeFDB] sections to configure several static MAC table entries.</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>MACAddress=</varname></term>
+ <listitem>
+ <para>As in the [Network] section. This key is mandatory.</para>
<xi:include href="version-info.xml" xpointer="v219"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Destination=</varname></term>
- <listitem>
- <para>Takes an IP address of the destination VXLAN tunnel endpoint.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Destination=</varname></term>
+ <listitem>
+ <para>Takes an IP address of the destination VXLAN tunnel endpoint.</para>
<xi:include href="version-info.xml" xpointer="v243"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>VLANId=</varname></term>
- <listitem>
- <para>The VLAN ID for the new static MAC table entry. If
- omitted, no VLAN ID information is appended to the new static MAC
- table entry.</para>
-
- <xi:include href="version-info.xml" xpointer="v219"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>VNI=</varname></term>
- <listitem>
- <para>The VXLAN Network Identifier (or VXLAN Segment ID) to use to connect to
- the remote VXLAN tunnel endpoint. Takes a number in the range 1…16777215.
- Defaults to unset.</para>
-
- <xi:include href="version-info.xml" xpointer="v243"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>AssociatedWith=</varname></term>
- <listitem>
- <para>Specifies where the address is associated with. Takes one of <literal>use</literal>,
- <literal>self</literal>, <literal>master</literal> or <literal>router</literal>.
- <literal>use</literal> means the address is in use. User space can use this option to
- indicate to the kernel that the fdb entry is in use. <literal>self</literal> means
- the address is associated with the port drivers fdb. Usually hardware. <literal>master</literal>
- means the address is associated with master devices fdb. <literal>router</literal> means
- the destination address is associated with a router. Note that it's valid if the referenced
- device is a VXLAN type device and has route shortcircuit enabled. Defaults to <literal>self</literal>.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>VLANId=</varname></term>
+ <listitem>
+ <para>The VLAN ID for the new static MAC table entry. If
+ omitted, no VLAN ID information is appended to the new static MAC
+ table entry.</para>
+
+ <xi:include href="version-info.xml" xpointer="v219"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>VNI=</varname></term>
+ <listitem>
+ <para>The VXLAN Network Identifier (or VXLAN Segment ID) to use to connect to
+ the remote VXLAN tunnel endpoint. Takes a number in the range 1…16777215.
+ Defaults to unset.</para>
<xi:include href="version-info.xml" xpointer="v243"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>OutgoingInterface=</varname></term>
- <listitem>
- <para>Specifies the name or index of the outgoing interface for the VXLAN device driver to
- reach the remote VXLAN tunnel endpoint. Defaults to unset.</para>
-
- <xi:include href="version-info.xml" xpointer="v249"/>
- </listitem>
- </varlistentry>
- </variablelist>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>AssociatedWith=</varname></term>
+ <listitem>
+ <para>Specifies where the address is associated with. Takes one of <literal>use</literal>,
+ <literal>self</literal>, <literal>master</literal> or <literal>router</literal>.
+ <literal>use</literal> means the address is in use. User space can use this option to
+ indicate to the kernel that the fdb entry is in use. <literal>self</literal> means
+ the address is associated with the port drivers fdb. Usually hardware. <literal>master</literal>
+ means the address is associated with master devices fdb. <literal>router</literal> means
+ the destination address is associated with a router. Note that it's valid if the referenced
+ device is a VXLAN type device and has route shortcircuit enabled. Defaults to <literal>self</literal>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v243"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>OutgoingInterface=</varname></term>
+ <listitem>
+ <para>Specifies the name or index of the outgoing interface for the VXLAN device driver to
+ reach the remote VXLAN tunnel endpoint. Defaults to unset.</para>
+
+ <xi:include href="version-info.xml" xpointer="v249"/>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
+
<refsect1>
<title>[BridgeMDB] Section Options</title>
- <para>The [BridgeMDB] section manages the multicast membership entries forwarding database table of a port and accepts the following
- keys. Specify several [BridgeMDB] sections to configure several permanent multicast membership entries.</para>
+ <para>The [BridgeMDB] section manages the multicast membership entries forwarding database table of a port and accepts the following
+ keys. Specify several [BridgeMDB] sections to configure several permanent multicast membership entries.</para>
- <variablelist class='network-directives'>
- <varlistentry>
- <term><varname>MulticastGroupAddress=</varname></term>
- <listitem>
- <para>Specifies the IPv4 or IPv6 multicast group address to add. This setting is mandatory.</para>
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>MulticastGroupAddress=</varname></term>
+ <listitem>
+ <para>Specifies the IPv4 or IPv6 multicast group address to add. This setting is mandatory.</para>
<xi:include href="version-info.xml" xpointer="v247"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>VLANId=</varname></term>
- <listitem>
- <para>The VLAN ID for the new entry. Valid ranges are 0 (no VLAN) to 4094. Optional, defaults to 0.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>VLANId=</varname></term>
+ <listitem>
+ <para>The VLAN ID for the new entry. Valid ranges are 0 (no VLAN) to 4094. Optional, defaults to 0.</para>
<xi:include href="version-info.xml" xpointer="v247"/>
- </listitem>
- </varlistentry>
- </variablelist>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
<refsect1>
<title>[LLDP] Section Options</title>
- <para>The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the following
- keys:</para>
- <variablelist class='network-directives'>
- <varlistentry>
- <term><varname>MUDURL=</varname></term>
- <listitem>
- <para>When configured, the specified Manufacturer Usage Descriptions (MUD) URL will be sent in
- LLDP packets. The syntax and semantics are the same as for <varname>MUDURL=</varname> in the
- [DHCPv4] section described above.</para>
-
- <para>The MUD URLs received via LLDP packets are saved and can be read using the
- <function>sd_lldp_neighbor_get_mud_url()</function> function.</para>
-
- <xi:include href="version-info.xml" xpointer="v246"/>
- </listitem>
- </varlistentry>
- </variablelist>
+ <para>The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the following
+ keys:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>MUDURL=</varname></term>
+ <listitem>
+ <para>When configured, the specified Manufacturer Usage Descriptions (MUD) URL will be sent in
+ LLDP packets. The syntax and semantics are the same as for <varname>MUDURL=</varname> in the
+ [DHCPv4] section described above.</para>
+
+ <para>The MUD URLs received via LLDP packets are saved and can be read using the
+ <function>sd_lldp_neighbor_get_mud_url()</function> function.</para>
+
+ <xi:include href="version-info.xml" xpointer="v246"/>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
<refsect1>
<title>[CAN] Section Options</title>
- <para>The [CAN] section manages the Controller Area Network (CAN bus) and accepts the
- following keys:</para>
- <variablelist class='network-directives'>
- <varlistentry>
- <term><varname>BitRate=</varname></term>
- <listitem>
- <para>The bitrate of CAN device in bits per second. The usual SI prefixes (K, M) with the base of 1000 can
- be used here. Takes a number in the range 1…4294967295.</para>
-
- <xi:include href="version-info.xml" xpointer="v239"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>SamplePoint=</varname></term>
- <listitem>
- <para>Optional sample point in percent with one decimal (e.g. <literal>75%</literal>,
- <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>). This will be ignored when
- <varname>BitRate=</varname> is unspecified.</para>
+ <para>The [CAN] section manages the Controller Area Network (CAN bus) and accepts the
+ following keys:</para>
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>BitRate=</varname></term>
+ <listitem>
+ <para>The bitrate of CAN device in bits per second. The usual SI prefixes (K, M) with the base of 1000 can
+ be used here. Takes a number in the range 1…4294967295.</para>
+
+ <xi:include href="version-info.xml" xpointer="v239"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>SamplePoint=</varname></term>
+ <listitem>
+ <para>Optional sample point in percent with one decimal (e.g. <literal>75%</literal>,
+ <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>). This will be ignored when
+ <varname>BitRate=</varname> is unspecified.</para>
<xi:include href="version-info.xml" xpointer="v239"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>TimeQuantaNSec=</varname></term>
- <term><varname>PropagationSegment=</varname></term>
- <term><varname>PhaseBufferSegment1=</varname></term>
- <term><varname>PhaseBufferSegment2=</varname></term>
- <term><varname>SyncJumpWidth=</varname></term>
- <listitem>
- <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
- synchronization jump width, which allow one to define the CAN bit-timing in a hardware
- independent format as proposed by the Bosch CAN 2.0 Specification.
- <varname>TimeQuantaNSec=</varname> takes a timespan in nanoseconds.
- <varname>PropagationSegment=</varname>, <varname>PhaseBufferSegment1=</varname>,
- <varname>PhaseBufferSegment2=</varname>, and <varname>SyncJumpWidth=</varname> take number
- of time quantum specified in <varname>TimeQuantaNSec=</varname> and must be an unsigned
- integer in the range 0…4294967295. These settings except for
- <varname>SyncJumpWidth=</varname> will be ignored when <varname>BitRate=</varname> is
- specified.</para>
-
- <xi:include href="version-info.xml" xpointer="v250"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>DataBitRate=</varname></term>
- <term><varname>DataSamplePoint=</varname></term>
- <listitem>
- <para>The bitrate and sample point for the data phase, if CAN-FD is used. These settings are
- analogous to the <varname>BitRate=</varname> and <varname>SamplePoint=</varname> keys.</para>
-
- <xi:include href="version-info.xml" xpointer="v246"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>DataTimeQuantaNSec=</varname></term>
- <term><varname>DataPropagationSegment=</varname></term>
- <term><varname>DataPhaseBufferSegment1=</varname></term>
- <term><varname>DataPhaseBufferSegment2=</varname></term>
- <term><varname>DataSyncJumpWidth=</varname></term>
- <listitem>
- <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
- synchronization jump width for the data phase, if CAN-FD is used. These settings are
- analogous to the <varname>TimeQuantaNSec=</varname> or related settings.</para>
-
- <xi:include href="version-info.xml" xpointer="v250"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>FDMode=</varname></term>
- <listitem>
- <para>Takes a boolean. When <literal>yes</literal>, CAN-FD mode is enabled for the interface.
- Note, that a bitrate and optional sample point should also be set for the CAN-FD data phase using
- the <varname>DataBitRate=</varname> and <varname>DataSamplePoint=</varname> keys, or
- <varname>DataTimeQuanta=</varname> and related settings.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>TimeQuantaNSec=</varname></term>
+ <term><varname>PropagationSegment=</varname></term>
+ <term><varname>PhaseBufferSegment1=</varname></term>
+ <term><varname>PhaseBufferSegment2=</varname></term>
+ <term><varname>SyncJumpWidth=</varname></term>
+ <listitem>
+ <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
+ synchronization jump width, which allow one to define the CAN bit-timing in a hardware
+ independent format as proposed by the Bosch CAN 2.0 Specification.
+ <varname>TimeQuantaNSec=</varname> takes a timespan in nanoseconds.
+ <varname>PropagationSegment=</varname>, <varname>PhaseBufferSegment1=</varname>,
+ <varname>PhaseBufferSegment2=</varname>, and <varname>SyncJumpWidth=</varname> take number
+ of time quantum specified in <varname>TimeQuantaNSec=</varname> and must be an unsigned
+ integer in the range 0…4294967295. These settings except for
+ <varname>SyncJumpWidth=</varname> will be ignored when <varname>BitRate=</varname> is
+ specified.</para>
+
+ <xi:include href="version-info.xml" xpointer="v250"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>DataBitRate=</varname></term>
+ <term><varname>DataSamplePoint=</varname></term>
+ <listitem>
+ <para>The bitrate and sample point for the data phase, if CAN-FD is used. These settings are
+ analogous to the <varname>BitRate=</varname> and <varname>SamplePoint=</varname> keys.</para>
<xi:include href="version-info.xml" xpointer="v246"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>FDNonISO=</varname></term>
- <listitem>
- <para>Takes a boolean. When <literal>yes</literal>, non-ISO CAN-FD mode is enabled for the
- interface. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>DataTimeQuantaNSec=</varname></term>
+ <term><varname>DataPropagationSegment=</varname></term>
+ <term><varname>DataPhaseBufferSegment1=</varname></term>
+ <term><varname>DataPhaseBufferSegment2=</varname></term>
+ <term><varname>DataSyncJumpWidth=</varname></term>
+ <listitem>
+ <para>Specifies the time quanta, propagation segment, phase buffer segment 1 and 2, and the
+ synchronization jump width for the data phase, if CAN-FD is used. These settings are
+ analogous to the <varname>TimeQuantaNSec=</varname> or related settings.</para>
+
+ <xi:include href="version-info.xml" xpointer="v250"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>FDMode=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, CAN-FD mode is enabled for the interface.
+ Note, that a bitrate and optional sample point should also be set for the CAN-FD data phase using
+ the <varname>DataBitRate=</varname> and <varname>DataSamplePoint=</varname> keys, or
+ <varname>DataTimeQuanta=</varname> and related settings.</para>
+
+ <xi:include href="version-info.xml" xpointer="v246"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>FDNonISO=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, non-ISO CAN-FD mode is enabled for the
+ interface. When unset, the kernel's default will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v246"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>RestartSec=</varname></term>
+ <listitem>
+ <para>Automatic restart delay time. If set to a non-zero value, a restart of the CAN controller will be
+ triggered automatically in case of a bus-off condition after the specified delay time. Subsecond delays can
+ be specified using decimals (e.g. <literal>0.1s</literal>) or a <literal>ms</literal> or
+ <literal>us</literal> postfix. Using <literal>infinity</literal> or <literal>0</literal> will turn the
+ automatic restart off. By default automatic restart is disabled.</para>
+
+ <xi:include href="version-info.xml" xpointer="v239"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Termination=</varname></term>
+ <listitem>
+ <para>Takes a boolean or a termination resistor value in ohm in the range 0…65535. When
+ <literal>yes</literal>, the termination resistor is set to 120 ohm. When
+ <literal>no</literal> or <literal>0</literal> is set, the termination resistor is disabled.
+ When unset, the kernel's default will be used.</para>
<xi:include href="version-info.xml" xpointer="v246"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>RestartSec=</varname></term>
- <listitem>
- <para>Automatic restart delay time. If set to a non-zero value, a restart of the CAN controller will be
- triggered automatically in case of a bus-off condition after the specified delay time. Subsecond delays can
- be specified using decimals (e.g. <literal>0.1s</literal>) or a <literal>ms</literal> or
- <literal>us</literal> postfix. Using <literal>infinity</literal> or <literal>0</literal> will turn the
- automatic restart off. By default automatic restart is disabled.</para>
-
- <xi:include href="version-info.xml" xpointer="v239"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Termination=</varname></term>
- <listitem>
- <para>Takes a boolean or a termination resistor value in ohm in the range 0…65535. When
- <literal>yes</literal>, the termination resistor is set to 120 ohm. When
- <literal>no</literal> or <literal>0</literal> is set, the termination resistor is disabled.
- When unset, the kernel's default will be used.</para>
-
- <xi:include href="version-info.xml" xpointer="v246"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>TripleSampling=</varname></term>
- <listitem>
- <para>Takes a boolean. When <literal>yes</literal>, three samples (instead of one) are used to determine
- the value of a received bit by majority rule. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>TripleSampling=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, three samples (instead of one) are used to determine
+ the value of a received bit by majority rule. When unset, the kernel's default will be used.</para>
<xi:include href="version-info.xml" xpointer="v242"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>BusErrorReporting=</varname></term>
- <listitem>
- <para>Takes a boolean. When <literal>yes</literal>, reporting of CAN bus errors is activated
- (those include single bit, frame format, and bit stuffing errors, unable to send dominant bit,
- unable to send recessive bit, bus overload, active error announcement, error occurred on
- transmission). When unset, the kernel's default will be used. Note: in case of a CAN bus with a
- single CAN device, sending a CAN frame may result in a huge number of CAN bus errors.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>BusErrorReporting=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, reporting of CAN bus errors is activated
+ (those include single bit, frame format, and bit stuffing errors, unable to send dominant bit,
+ unable to send recessive bit, bus overload, active error announcement, error occurred on
+ transmission). When unset, the kernel's default will be used. Note: in case of a CAN bus with a
+ single CAN device, sending a CAN frame may result in a huge number of CAN bus errors.</para>
<xi:include href="version-info.xml" xpointer="v248"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>ListenOnly=</varname></term>
- <listitem>
- <para>Takes a boolean. When <literal>yes</literal>, listen-only mode is enabled. When the
- interface is in listen-only mode, the interface neither transmit CAN frames nor send ACK
- bit. Listen-only mode is important to debug CAN networks without interfering with the
- communication or acknowledge the CAN frame. When unset, the kernel's default will be used.
- </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ListenOnly=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, listen-only mode is enabled. When the
+ interface is in listen-only mode, the interface neither transmit CAN frames nor send ACK
+ bit. Listen-only mode is important to debug CAN networks without interfering with the
+ communication or acknowledge the CAN frame. When unset, the kernel's default will be used.
+ </para>
<xi:include href="version-info.xml" xpointer="v246"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>Loopback=</varname></term>
- <listitem>
- <para>Takes a boolean. When <literal>yes</literal>, loopback mode is enabled. When the
- loopback mode is enabled, the interface treats messages transmitted by itself as received
- messages. The loopback mode is important to debug CAN networks. When unset, the kernel's
- default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Loopback=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, loopback mode is enabled. When the
+ loopback mode is enabled, the interface treats messages transmitted by itself as received
+ messages. The loopback mode is important to debug CAN networks. When unset, the kernel's
+ default will be used.</para>
<xi:include href="version-info.xml" xpointer="v250"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>OneShot=</varname></term>
- <listitem>
- <para>Takes a boolean. When <literal>yes</literal>, one-shot mode is enabled. When unset,
- the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>OneShot=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, one-shot mode is enabled. When unset,
+ the kernel's default will be used.</para>
<xi:include href="version-info.xml" xpointer="v250"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>PresumeAck=</varname></term>
- <listitem>
- <para>Takes a boolean. When <literal>yes</literal>, the interface will ignore missing CAN
- ACKs. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>PresumeAck=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, the interface will ignore missing CAN
+ ACKs. When unset, the kernel's default will be used.</para>
<xi:include href="version-info.xml" xpointer="v250"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>ClassicDataLengthCode=</varname></term>
- <listitem>
- <para>Takes a boolean. When <literal>yes</literal>, the interface will handle the 4bit data
- length code (DLC). When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>ClassicDataLengthCode=</varname></term>
+ <listitem>
+ <para>Takes a boolean. When <literal>yes</literal>, the interface will handle the 4bit data
+ length code (DLC). When unset, the kernel's default will be used.</para>
<xi:include href="version-info.xml" xpointer="v250"/>
- </listitem>
- </varlistentry>
- </variablelist>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
<refsect1>
<title>[IPoIB] Section Options</title>
- <para>The [IPoIB] section manages the IP over Infiniband and accepts the following keys:</para>
- <variablelist class='network-directives'>
- <xi:include href="systemd.netdev.xml" xpointer="ipoib_mode" />
- <xi:include href="systemd.netdev.xml" xpointer="ipoib_umcast" />
- </variablelist>
+ <para>The [IPoIB] section manages the IP over Infiniband and accepts the following keys:</para>
+
+ <variablelist class='network-directives'>
+ <xi:include href="systemd.netdev.xml" xpointer="ipoib_mode" />
+ <xi:include href="systemd.netdev.xml" xpointer="ipoib_umcast" />
+ </variablelist>
</refsect1>
<refsect1>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>clsact</literal>
or <literal>ingress</literal>. This is mandatory.</para>
- <xi:include href="version-info.xml" xpointer="v244"/>
+ <xi:include href="version-info.xml" xpointer="v244"/>
</listitem>
</varlistentry>
<refsect1>
<title>[BridgeVLAN] Section Options</title>
- <para>The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts the
- following keys. Specify several [BridgeVLAN] sections to configure several VLAN entries. The
- <varname>VLANFiltering=</varname> option has to be enabled, see the [Bridge] section in
- <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
-
- <variablelist class='network-directives'>
- <varlistentry>
- <term><varname>VLAN=</varname></term>
- <listitem>
- <para>The VLAN ID allowed on the port. This can be either a single ID or a range M-N. Takes an
- integer in the range 1…4094. This setting can be specified multiple times. If an empty string is
- assigned, then the all previous assignments are cleared.</para>
-
- <xi:include href="version-info.xml" xpointer="v231"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>EgressUntagged=</varname></term>
- <listitem>
- <para>The VLAN ID specified here will be used to untag frames on egress. Configuring
- <varname>EgressUntagged=</varname> implicates the use of <varname>VLAN=</varname> above and will
- enable the VLAN ID for ingress as well. This can be either a single ID or a range M-N. This
- setting can be specified multiple times. If an empty string is assigned, then the all previous
- assignments are cleared.</para>
-
- <xi:include href="version-info.xml" xpointer="v231"/>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><varname>PVID=</varname></term>
- <listitem>
- <para>The port VLAN ID specified here is assigned to all untagged frames at ingress. Takes an
- VLAN ID or negative boolean value (e.g. <literal>no</literal>). When false, the currently
- assigned port VLAN ID will be dropped. Configuring <varname>PVID=</varname> implicates the use of
- <varname>VLAN=</varname> setting in the above and will enable the VLAN ID for ingress as well.
- Defaults to unset, and will keep the assigned port VLAN ID if exists.</para>
-
- <xi:include href="version-info.xml" xpointer="v231"/>
- </listitem>
- </varlistentry>
- </variablelist>
+ <para>
+ The [BridgeVLAN] section manages the VLAN ID configurations of a bridge master or port, and accepts the
+ following keys. To make the settings in this section take an effect,
+ <varname>VLANFiltering=</varname> option has to be enabled on the bridge master, see the [Bridge]
+ section in
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ If at least one valid settings specified in this section in a .network file for an interface, all
+ assigned VLAN IDs on the interface that are not configured in the .network file will be removed. If
+ VLAN IDs on an interface need to be managed by other tools, then the settings in this section cannot
+ be used in the matching .network file.
+ </para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>VLAN=</varname></term>
+ <listitem>
+ <para>The VLAN ID allowed on the port. This can be either a single ID or a range M-N. Takes an
+ integer in the range 1…4094. This setting can be specified multiple times. If an empty string is
+ assigned, then the all previous assignments are cleared.</para>
+
+ <xi:include href="version-info.xml" xpointer="v231"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>EgressUntagged=</varname></term>
+ <listitem>
+ <para>The VLAN ID specified here will be used to untag frames on egress. Configuring
+ <varname>EgressUntagged=</varname> implicates the use of <varname>VLAN=</varname> above and will
+ enable the VLAN ID for ingress as well. This can be either a single ID or a range M-N. This
+ setting can be specified multiple times. If an empty string is assigned, then the all previous
+ assignments are cleared.</para>
+
+ <xi:include href="version-info.xml" xpointer="v231"/>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>PVID=</varname></term>
+ <listitem>
+ <para>The port VLAN ID specified here is assigned to all untagged frames at ingress. Takes an
+ VLAN ID or negative boolean value (e.g. <literal>no</literal>). When false, the currently
+ assigned port VLAN ID will be dropped. Configuring <varname>PVID=</varname> implicates the use of
+ <varname>VLAN=</varname> setting in the above and will enable the VLAN ID for ingress as well.
+ Defaults to unset, and will keep the assigned port VLAN ID if exists.</para>
+
+ <xi:include href="version-info.xml" xpointer="v231"/>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></para>
- <para><filename>/run/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></para>
- <para><filename>/var/lib/machines/<replaceable>machine</replaceable>.nspawn</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></member>
+ <member><filename>/run/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></member>
+ <member><filename>/var/lib/machines/<replaceable>machine</replaceable>.nspawn</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><literallayout>
-<filename>/etc/pcrlock.d/*.pcrlock</filename>
-<filename>/etc/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
-<filename>/run/pcrlock.d/*.pcrlock</filename>
-<filename>/run/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
-<filename>/var/lib/pcrlock.d/*.pcrlock</filename>
-<filename>/var/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
-<filename>/usr/local/pcrlock.d/*.pcrlock</filename>
-<filename>/usr/local/pcrlock.d/*.pcrlock.d/*.pcrlock</filename>
-<filename>/usr/lib/pcrlock.d/*.pcrlock</filename>
-<filename>/usr/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></literallayout></para>
+ <para><simplelist>
+ <member><filename>/etc/pcrlock.d/*.pcrlock</filename></member>
+ <member><filename>/etc/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+ <member><filename>/run/pcrlock.d/*.pcrlock</filename></member>
+ <member><filename>/run/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+ <member><filename>/var/lib/pcrlock.d/*.pcrlock</filename></member>
+ <member><filename>/var/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+ <member><filename>/usr/local/pcrlock.d/*.pcrlock</filename></member>
+ <member><filename>/usr/local/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+ <member><filename>/usr/lib/pcrlock.d/*.pcrlock</filename></member>
+ <member><filename>/usr/lib/pcrlock.d/*.pcrlock.d/*.pcrlock</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/system-preset/*.preset</filename></para>
- <para><filename>/run/systemd/system-preset/*.preset</filename></para>
- <para><filename>/usr/lib/systemd/system-preset/*.preset</filename></para>
- <para><filename>/etc/systemd/user-preset/*.preset</filename></para>
- <para><filename>/run/systemd/user-preset/*.preset</filename></para>
- <para><filename>/usr/lib/systemd/user-preset/*.preset</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/system-preset/*.preset</filename></member>
+ <member><filename>/run/systemd/system-preset/*.preset</filename></member>
+ <member><filename>/usr/lib/systemd/system-preset/*.preset</filename></member>
+ <member><filename>/etc/systemd/user-preset/*.preset</filename></member>
+ <member><filename>/run/systemd/user-preset/*.preset</filename></member>
+ <member><filename>/usr/lib/systemd/user-preset/*.preset</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<xi:include href="version-info.xml" xpointer="v254"/>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>home.create.*</varname></term>
+ <listitem>
+ <para>Creates a home area for the specified user with the user record data passed in. For details see
+ <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect2>
<title>System Unit Search Path</title>
- <para><literallayout><filename>/etc/systemd/system.control/*</filename>
-<filename>/run/systemd/system.control/*</filename>
-<filename>/run/systemd/transient/*</filename>
-<filename>/run/systemd/generator.early/*</filename>
-<filename>/etc/systemd/system/*</filename>
-<filename>/etc/systemd/system.attached/*</filename>
-<filename>/run/systemd/system/*</filename>
-<filename>/run/systemd/system.attached/*</filename>
-<filename>/run/systemd/generator/*</filename>
-<filename index='false'>…</filename>
-<filename>/usr/lib/systemd/system/*</filename>
-<filename>/run/systemd/generator.late/*</filename></literallayout></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/system.control/*</filename></member>
+ <member><filename>/run/systemd/system.control/*</filename></member>
+ <member><filename>/run/systemd/transient/*</filename></member>
+ <member><filename>/run/systemd/generator.early/*</filename></member>
+ <member><filename>/etc/systemd/system/*</filename></member>
+ <member><filename>/etc/systemd/system.attached/*</filename></member>
+ <member><filename>/run/systemd/system/*</filename></member>
+ <member><filename>/run/systemd/system.attached/*</filename></member>
+ <member><filename>/run/systemd/generator/*</filename></member>
+ <member><filename index='false'>…</filename></member>
+ <member><filename>/usr/lib/systemd/system/*</filename></member>
+ <member><filename>/run/systemd/generator.late/*</filename></member>
+ </simplelist></para>
</refsect2>
<refsect2>
<title>User Unit Search Path</title>
- <para><literallayout><filename>~/.config/systemd/user.control/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/user.control/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/transient/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/generator.early/*</filename>
-<filename>~/.config/systemd/user/*</filename>
-<filename>$XDG_CONFIG_DIRS/systemd/user/*</filename>
-<filename>/etc/systemd/user/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/user/*</filename>
-<filename>/run/systemd/user/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/generator/*</filename>
-<filename>$XDG_DATA_HOME/systemd/user/*</filename>
-<filename>$XDG_DATA_DIRS/systemd/user/*</filename>
-<filename index='false'>…</filename>
-<filename>/usr/lib/systemd/user/*</filename>
-<filename>$XDG_RUNTIME_DIR/systemd/generator.late/*</filename></literallayout></para>
+ <para><simplelist>
+ <member><filename>~/.config/systemd/user.control/*</filename></member>
+ <member><filename>$XDG_RUNTIME_DIR/systemd/user.control/*</filename></member>
+ <member><filename>$XDG_RUNTIME_DIR/systemd/transient/*</filename></member>
+ <member><filename>$XDG_RUNTIME_DIR/systemd/generator.early/*</filename></member>
+ <member><filename>~/.config/systemd/user/*</filename></member>
+ <member><filename>$XDG_CONFIG_DIRS/systemd/user/*</filename></member>
+ <member><filename>/etc/systemd/user/*</filename></member>
+ <member><filename>$XDG_RUNTIME_DIR/systemd/user/*</filename></member>
+ <member><filename>/run/systemd/user/*</filename></member>
+ <member><filename>$XDG_RUNTIME_DIR/systemd/generator/*</filename></member>
+ <member><filename>$XDG_DATA_HOME/systemd/user/*</filename></member>
+ <member><filename>$XDG_DATA_DIRS/systemd/user/*</filename></member>
+ <member><filename index='false'>…</filename></member>
+ <member><filename>/usr/lib/systemd/user/*</filename></member>
+ <member><filename>$XDG_RUNTIME_DIR/systemd/generator.late/*</filename></member>
+ </simplelist></para>
</refsect2>
</refsynopsisdiv>
</refnamediv>
<refsynopsisdiv>
- <para><literallayout><filename>/etc/sysupdate.d/*.conf</filename>
-<filename>/run/sysupdate.d/*.conf</filename>
-<filename>/usr/lib/sysupdate.d/*.conf</filename>
- </literallayout></para>
+ <para><simplelist>
+ <member><filename>/etc/sysupdate.d/*.conf</filename></member>
+ <member><filename>/run/sysupdate.d/*.conf</filename></member>
+ <member><filename>/usr/lib/sysupdate.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/sysusers.d/*.conf</filename></para>
- <para><filename>/run/sysusers.d/*.conf</filename></para>
- <para><filename>/usr/lib/sysusers.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/sysusers.d/*.conf</filename></member>
+ <member><filename>/run/sysusers.d/*.conf</filename></member>
+ <member><filename>/usr/lib/sysusers.d/*.conf</filename></member>
+ </simplelist></para>
<programlisting>
#Type Name ID GECOS Home directory Shell
</refnamediv>
<refsynopsisdiv>
- <para><filename>/etc/systemd/timesyncd.conf</filename></para>
- <para><filename>/etc/systemd/timesyncd.conf.d/*.conf</filename></para>
- <para><filename>/run/systemd/timesyncd.conf.d/*.conf</filename></para>
- <para><filename>/usr/lib/systemd/timesyncd.conf.d/*.conf</filename></para>
+ <para><simplelist>
+ <member><filename>/etc/systemd/timesyncd.conf</filename></member>
+ <member><filename>/etc/systemd/timesyncd.conf.d/*.conf</filename></member>
+ <member><filename>/run/systemd/timesyncd.conf.d/*.conf</filename></member>
+ <member><filename>/usr/lib/systemd/timesyncd.conf.d/*.conf</filename></member>
+ </simplelist></para>
</refsynopsisdiv>
<refsect1>
<refnamediv>
<refname>tmpfiles.d</refname>
- <refpurpose>Configuration for creation, deletion and cleaning of
- volatile and temporary files</refpurpose>
+ <refpurpose>Configuration for creation, deletion, and cleaning of files and directories</refpurpose>
</refnamediv>
<refsynopsisdiv>
- <para><literallayout><filename>/etc/tmpfiles.d/*.conf</filename>
-<filename>/run/tmpfiles.d/*.conf</filename>
-<filename>/usr/lib/tmpfiles.d/*.conf</filename>
- </literallayout></para>
-
- <para><literallayout><filename>~/.config/user-tmpfiles.d/*.conf</filename>
-<filename>$XDG_RUNTIME_DIR/user-tmpfiles.d/*.conf</filename>
-<filename>~/.local/share/user-tmpfiles.d/*.conf</filename>
-<filename index='false'>…</filename>
-<filename>/usr/share/user-tmpfiles.d/*.conf</filename>
- </literallayout></para>
+ <para><simplelist>
+ <member><filename>/etc/tmpfiles.d/*.conf</filename></member>
+ <member><filename>/run/tmpfiles.d/*.conf</filename></member>
+ <member><filename>/usr/lib/tmpfiles.d/*.conf</filename></member>
+ </simplelist></para>
+
+ <para><simplelist>
+ <member><filename>~/.config/user-tmpfiles.d/*.conf</filename></member>
+ <member><filename>$XDG_RUNTIME_DIR/user-tmpfiles.d/*.conf</filename></member>
+ <member><filename>~/.local/share/user-tmpfiles.d/*.conf</filename></member>
+ <member><filename index='false'>…</filename></member>
+ <member><filename>/usr/share/user-tmpfiles.d/*.conf</filename></member>
+ </simplelist></para>
<programlisting>#Type Path Mode User Group Age Argument
f /file/to/create mode user group - content
--- /dev/null
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="uid0"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>uid0</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>uid0</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>uid0</refname>
+ <refpurpose>Elevate privileges</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>uid0</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="opt" rep="repeat">COMMAND</arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>uid0</command> may be used to temporarily and interactively acquire elavated or different
+ privileges. It serves a similar purpose as <citerefentry
+ project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>, but
+ operates differently in a couple of key areas:</para>
+
+ <itemizedlist>
+ <listitem><para>No execution or security context credentials are inherited from the caller into the
+ invoked commands, as they are invoked from a fresh, isolated service forked off the service
+ manager.</para></listitem>
+
+ <listitem><para>Authentication takes place via <ulink
+ url="https://www.freedesktop.org/wiki/Software/polkit">polkit</ulink>, thus isolating the
+ authentication prompt from the terminal (if possible).</para></listitem>
+
+ <listitem><para>An independent pseudo-tty is allocated for the invoked command, detaching its lifecycle and
+ isolating it for security.</para></listitem>
+
+ <listitem><para>No SetUID/SetGID file access bit functionality is used for the implementation.</para></listitem>
+ </itemizedlist>
+
+ <para>Altogether this should provide a safer and more robust alternative to the <command>sudo</command>
+ mechanism, in particular in OS environments where SetUID/SetGID support is not available (for example by
+ setting the <varname>NoNewPrivileges=</varname> variable in
+ <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para>
+
+ <para>Any session invoked via <command>uid0</command> will run through the
+ <literal>systemd-uid0</literal> PAM stack.</para>
+
+ <para>Note that <command>uid0</command> is implemented as an alternative multi-call invocation of
+ <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+
+ <para>The following options are understood:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><option>--no-ask-password</option></term>
+
+ <listitem><para>Do not query the user for authentication for privileged operations.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--unit=</option></term>
+
+ <listitem><para>Use this unit name instead of an automatically generated one.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--property=</option></term>
+
+ <listitem><para>Sets a property on the service unit that is created. This option takes an assignment
+ in the same format as
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+ <command>set-property</command> command.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--description=</option></term>
+
+ <listitem><para>Provide a description for the service unit that is invoked. If not specified,
+ the command itself will be used as a description. See <varname>Description=</varname> in
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--slice=</option></term>
+
+ <listitem><para>Make the new <filename>.service</filename> unit part of the specified slice, instead
+ of <filename>user.slice</filename>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--slice-inherit</option></term>
+
+ <listitem><para>Make the new <filename>.service</filename> unit part of the slice the
+ <command>uid0</command> itself has been invoked in. This option may be combined with
+ <option>--slice=</option>, in which case the slice specified via <option>--slice=</option> is placed
+ within the slice the <command>uid0</command> command is invoked in.</para>
+
+ <para>Example: consider <command>uid0</command> being invoked in the slice
+ <filename>foo.slice</filename>, and the <option>--slice=</option> argument is
+ <filename>bar</filename>. The unit will then be placed under
+ <filename>foo-bar.slice</filename>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--user=</option></term>
+ <term><option>-u</option></term>
+ <term><option>--group=</option></term>
+ <term><option>-g</option></term>
+
+ <listitem><para>Switches to the specified user/group instead of root.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--nice=</option></term>
+
+ <listitem><para>Runs the invoked session with the specified nice level.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--chdir=</option></term>
+ <term><option>-D</option></term>
+
+ <listitem><para>Runs the invoked session with the specified working directory. If not specified
+ defaults to the client's current working directory if switching to the root user, or the target
+ user's home directory otherwise.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
+
+ <listitem><para>Runs the invoked session with the specified environment variable set. This parameter
+ may be used more than once to set multiple variables. When <literal>=</literal> and
+ <replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
+ invoking environment will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--background=<replaceable>COLOR</replaceable></option></term>
+
+ <listitem><para>Change the terminal background color to the specified ANSI color as long as the
+ session lasts. If not specified, the background will be tinted in a reddish tone when operating as
+ root, and in a yellowish tone when operating under another UID, as reminder of the changed
+ privileges. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as
+ <literal>40</literal>, <literal>41</literal>, …, <literal>47</literal>, <literal>48;2;…</literal>,
+ <literal>48;5;…</literal>. See <ulink
+ url="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">ANSI
+ Escape Code (Wikipedia)</ulink> for details. Set to an empty string to disable.</para>
+
+ <para>Example: <literal>--background=44</literal> for a blue background.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
+ <xi:include href="user-system-options.xml" xpointer="machine" />
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+
+ <para>All command line arguments after the first non-option argument become part of the command line of
+ the launched process. If no command line is specified an interactive shell is invoked. The shell to
+ invoke may be controlled via <option>--setenv=SHELL=…</option> and currently defaults to the
+ <emphasis>originating user's</emphasis> shell (i.e. not the target user's!) if operating locally, or
+ <filename>/bin/sh</filename> when operating with <option>--machine=</option>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>On success, 0 is returned. If <command>uid0</command> failed to start the session or the specified command fails, a
+ non-zero return value will be returned.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry project='man-pages'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<para>Also see the description of <option>-j</option>/<option>--json=</option> and
<option>--section=</option>.</para>
+
+ <para>Other tools that may be useful for inspect UKIs:
+ <citerefentry project='man-pages'><refentrytitle>llvm-objdump</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <option>-p</option> and <command>pe-inspect</command>.
+ <!-- TODO: add link to pe-inspect man page when it gets one -->
+ </para>
</refsect2>
</refsect1>
error('ln does not support --relative (added in coreutils 8.16)')
endif
-############################################################
+#####################################################################
gperf = find_program('gperf')
conf.set('GPERF_LEN_TYPE', gperf_len_type,
description : 'The type of gperf "len" parameter')
-############################################################
+#####################################################################
if not cc.has_header('sys/capability.h')
error('POSIX caps headers not found')
cc.has_header(header))
endforeach
-############################################################
+#####################################################################
fallback_hostname = get_option('fallback-hostname')
if fallback_hostname == '' or fallback_hostname[0] == '.' or fallback_hostname[0] == '-'
conf.set10('SYSTEMD_SLOW_TESTS_DEFAULT', slow_tests)
-############################################################
+#####################################################################
pymod = import('python')
python = pymod.find_installation('python3', required : true, modules : ['jinja2'])
python_39 = python.language_version().version_compare('>=3.9')
-############################################################
+#####################################################################
if conf.get('BPF_FRAMEWORK') == 1
bpf_clang_flags = [
want_ukify = get_option('ukify').require(python_39, error_message : 'Python >= 3.9 required').allowed()
conf.set10('ENABLE_UKIFY', want_ukify)
-############################################################
+#####################################################################
check_version_history_py = find_program('tools/check-version-history.py')
elf2efi_py = find_program('tools/elf2efi.py')
update_syscall_tables_sh = find_program('tools/update-syscall-tables.sh')
xml_helper_py = find_program('tools/xml_helper.py')
-############################################################
+#####################################################################
version_tag = get_option('version-tag')
version_h = vcs_tag(
shared_lib_tag = meson.project_version()
endif
-############################################################
+#####################################################################
if get_option('b_coverage')
userspace_c_args += ['-include', 'src/basic/coverage.h']
endif
-############################################################
+#####################################################################
config_h = configure_file(
output : 'config.h',
man_page_depends = []
-############################################################
+#####################################################################
simple_tests = []
libsystemd_tests = []
c_args : static_libudev_pic ? [] : ['-fno-PIC'],
pic : static_libudev_pic)
-############################################################
+#####################################################################
runtest_env = custom_target(
'systemd-runtest.env',
test_cflags += cc.first_supported_argument('-Wno-maybe-uninitialized')
endif
-############################################################
+#####################################################################
executable_template = {
'include_directories' : includes,
'dependencies' : userspace,
}
-############################################################
+#####################################################################
# systemd-analyze requires 'libcore'
subdir('src/core')
alias_target('devel', libsystemd_pc, libudev_pc, systemd_pc, udev_pc)
-############################################################
+#####################################################################
foreach test : simple_tests
executables += test_template + { 'sources' : [test] }
alias_target('fuzzers', fuzzer_exes)
-############################################################
+#####################################################################
test_dlopen = executables_by_name.get('test-dlopen')
endif
endforeach
-############################################################
+#####################################################################
ukify = custom_target(
'ukify',
libexecdir / 'ukify'))
endif
-############################################################
+#####################################################################
subdir('rules.d')
subdir('test')
-############################################################
+#####################################################################
subdir('docs/sysvinit')
subdir('docs/var-log')
install_emptydir(systemdstatedir)
-############################################################
+#####################################################################
# Ensure that changes to the docs/ directory do not break the
# basic Github pages build. But only run it in developer mode,
'--destination', project_build_root / '_site'])
endif
-############################################################
+#####################################################################
check_help = find_program('tools/check-help.sh')
check_version = find_program('tools/check-version.sh')
endif
endforeach
-############################################################
+#####################################################################
if git.found()
all_files = run_command(
'HEAD'])
endif
-############################################################
+#####################################################################
check_api_docs_sh = find_program('tools/check-api-docs.sh')
run_target(
command : [export_dbus_interfaces_py, '@OUTPUT@', dbus_programs])
endif
-############################################################
+#####################################################################
alt_time_epoch = run_command('date', '-Is', '-u', '-d', '@@0@'.format(time_epoch),
check : true).stdout().strip()
Images=system
[Output]
-OutputDirectory=mkosi.output
+@OutputDirectory=mkosi.output
BuildDirectory=mkosi.builddir
CacheDirectory=mkosi.cache
@Incremental=yes
@QemuMem=2G
@RuntimeSize=8G
-# Make sure we don't trigger systemd-firstboot prompting for the root password.
-Credentials=passwd.plaintext-password.root=
KernelCommandLineExtra=systemd.crash_shell
systemd.log_level=debug
systemd.log_ratelimit_kmsg=0
selinux=0
enforcing=0
systemd.early_core_pattern=/core
+ systemd.firstboot=no
fi
if [ ! -f "$BUILDDIR"/build.ninja ]; then
- sysvinit_path=$(realpath /etc/init.d)
+ [[ -d /etc/rc.d/init.d ]] && sysvinit_path="/etc/rc.d/init.d" || sysvinit_path="/etc/init.d"
if [ "$ID" = "centos" ] && [ "$VERSION" = "8" ]; then
UKIFY="disabled"
-D tests=unsafe
-D slow-tests="${SLOW_TESTS:-false}"
-D create-log-dirs=false
- -D pamconfdir=no
+ -D pamconfdir=/usr/lib/pam.d/
-D utmp=true
-D hibernate=true
-D ldconfig=true
BuildPackages=
acl
- diffutils
- gawk
binutils
clang
+ diffutils
+ gawk
+ gdb
gettext
git
gperf
pkgconf
rsync
sed
+ strace
tar
zstd
strace
systemd
tmux
+ tar
tree
udev
util-linux
integritysetup
iproute
iproute-tc
+ kernel-core
libcap-ng-utils
netcat
openssh-server
[Match]
Distribution=centos
+
+[Content]
+Packages=
+ kernel-modules # For squashfs support
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Release=8
-
-[Content]
-Packages=
- kernel-core-4.18.0-521.el8
- kernel-modules-4.18.0-521.el8 # For squashfs support
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Release=9
-
-[Content]
-Packages=
- kernel-core
- kernel-modules
btrfs-progs
compsize
f2fs-tools
- kernel-core
+ glibc-langpack-en
alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1
alternatives --set python3 /usr/bin/python3.9
fi
+
+mkdir -p /usr/lib/sysusers.d
+cat >/usr/lib/sysusers.d/testuser.conf <<EOF
+u testuser 4711 "Test User" /home/testuser
+EOF
+mkdir -p /usr/lib/tmpfiles.d
+cat >/usr/lib/tmpfiles.d/testuser.conf <<EOF
+q /home/testuser 0700 4711 4711
+EOF
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-11-14 21:25+0000\n"
-"PO-Revision-Date: 2023-08-03 17:21+0000\n"
+"PO-Revision-Date: 2023-12-11 13:43+0000\n"
"Last-Translator: Oğuz Ersen <oguz@ersen.moe>\n"
-"Language-Team: Turkish <https://translate.fedoraproject.org/projects/systemd/master/tr/>\n"
+"Language-Team: Turkish <https://translate.fedoraproject.org/projects/systemd/"
+"master/tr/>\n"
"Language: tr\n"
"MIME-Version: 1.0\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.18.2\n"
+"X-Generator: Weblate 5.2.1\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#, c-format
msgid "Home of user %s is currently absent, please plug in the necessary storage device or backing file system."
msgstr ""
+"%s kullanıcısının ev dizini şu anda mevcut değil, lütfen gerekli depolama "
+"aygıtını veya içeren dosya sistemini bağlayın."
#: src/home/pam_systemd_home.c:292
#, c-format
msgid "Too frequent login attempts for user %s, try again later."
msgstr ""
+"%s kullanıcısı için çok sık oturum açma denemesi, daha sonra tekrar deneyin."
#: src/home/pam_systemd_home.c:304
msgid "Password: "
#, c-format
msgid "Password incorrect or not sufficient for authentication of user %s."
msgstr ""
+"Parola yanlış veya %s kullanıcısının kimlik doğrulaması için yeterli değil."
#: src/home/pam_systemd_home.c:307
msgid "Sorry, try again: "
-msgstr ""
+msgstr "Üzgünüm, tekrar deneyin: "
#: src/home/pam_systemd_home.c:329
msgid "Recovery key: "
-msgstr ""
+msgstr "Kurtarma anahtarı: "
#: src/home/pam_systemd_home.c:331
#, c-format
msgid "Password/recovery key incorrect or not sufficient for authentication of user %s."
msgstr ""
+"Parola/kurtarma anahtarı yanlış veya %s kullanıcısının kimlik doğrulaması "
+"için yeterli değil."
#: src/home/pam_systemd_home.c:332
msgid "Sorry, reenter recovery key: "
-msgstr ""
+msgstr "Üzgünüm, kurtarma anahtarını yeniden girin: "
#: src/home/pam_systemd_home.c:352
#, c-format
msgid "Security token of user %s not inserted."
-msgstr ""
+msgstr "%s kullanıcısının güvenlik belirteci girilmedi."
#: src/home/pam_systemd_home.c:353 src/home/pam_systemd_home.c:356
msgid "Try again with password: "
-msgstr ""
+msgstr "Parola ile tekrar deneyin: "
#: src/home/pam_systemd_home.c:355
#, c-format
msgid "Password incorrect or not sufficient, and configured security token of user %s not inserted."
msgstr ""
+"Parola yanlış veya yeterli değil ve %s kullanıcısının yapılandırılan "
+"güvenlik belirteci girilmedi."
#: src/home/pam_systemd_home.c:376
msgid "Security token PIN: "
-msgstr ""
+msgstr "Güvenlik belirteci PIN kodu: "
#: src/home/pam_systemd_home.c:393
#, c-format
msgid "Please authenticate physically on security token of user %s."
msgstr ""
+"Lütfen %s kullanıcısının güvenlik belirteci ile fiziksel olarak kimlik "
+"doğrulaması yapın."
#: src/home/pam_systemd_home.c:404
#, c-format
msgid "Please confirm presence on security token of user %s."
-msgstr ""
+msgstr "Lütfen %s kullanıcısının güvenlik belirtecinin varlığını doğrulayın."
#: src/home/pam_systemd_home.c:415
#, c-format
msgid "Please verify user on security token of user %s."
msgstr ""
+"Lütfen %s kullanıcısının güvenlik belirtecindeki kullanıcıyı doğrulayın."
#: src/home/pam_systemd_home.c:424
msgid "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)"
msgstr ""
+"Güvenlik belirteci PIN kodu kilitli, lütfen önce kilidini açın. (İpucu: "
+"Çıkarma ve yeniden takma yeterli olabilir.)"
#: src/home/pam_systemd_home.c:432
#, c-format
msgid "Security token PIN incorrect for user %s."
-msgstr ""
+msgstr "Güvenlik belirteci PIN kodu %s kullanıcısı için yanlış."
#: src/home/pam_systemd_home.c:433 src/home/pam_systemd_home.c:452
#: src/home/pam_systemd_home.c:471
msgid "Sorry, retry security token PIN: "
-msgstr ""
+msgstr "Üzgünüm, güvenlik belirteci PIN kodunu yeniden deneyin: "
#: src/home/pam_systemd_home.c:451
#, c-format
msgid "Security token PIN of user %s incorrect (only a few tries left!)"
msgstr ""
+"%s kullanıcısının güvenlik belirteci PIN kodu yanlış (sadece birkaç deneme "
+"kaldı!)"
#: src/home/pam_systemd_home.c:470
#, c-format
msgid "Security token PIN of user %s incorrect (only one try left!)"
msgstr ""
+"%s kullanıcısının güvenlik belirteci PIN kodu yanlış (sadece bir deneme "
+"kaldı!)"
#: src/home/pam_systemd_home.c:616
#, c-format
msgid "Home of user %s is currently not active, please log in locally first."
msgstr ""
+"%s kullanıcısının ev dizini şu anda etkin değil, lütfen önce yerel olarak "
+"oturum açın."
#: src/home/pam_systemd_home.c:618
#, c-format
msgid "Home of user %s is currently locked, please unlock locally first."
msgstr ""
+"%s kullanıcısının ev dizini şu anda kilitli, lütfen önce yerel olarak "
+"kilidini açın."
#: src/home/pam_systemd_home.c:645
#, c-format
msgid "Too many unsuccessful login attempts for user %s, refusing."
msgstr ""
+"%s kullanıcısı için çok fazla başarısız oturum açma denemesi, reddediliyor."
#: src/home/pam_systemd_home.c:868
msgid "User record is blocked, prohibiting access."
-msgstr ""
+msgstr "Kullanıcı kaydı engellendi, erişim engelleniyor."
#: src/home/pam_systemd_home.c:872
msgid "User record is not valid yet, prohibiting access."
-msgstr ""
+msgstr "Kullanıcı kaydı henüz geçerli değil, erişim engelleniyor."
#: src/home/pam_systemd_home.c:876
msgid "User record is not valid anymore, prohibiting access."
-msgstr ""
+msgstr "Kullanıcı kaydı artık geçerli değil, erişim engelleniyor."
#: src/home/pam_systemd_home.c:881 src/home/pam_systemd_home.c:932
msgid "User record not valid, prohibiting access."
-msgstr ""
+msgstr "Kullanıcı kaydı geçerli değil, erişim engelleniyor."
#: src/home/pam_systemd_home.c:893
#, c-format
msgid "Too many logins, try again in %s."
-msgstr ""
+msgstr "Çok fazla oturum açıldı, %s içinde tekrar deneyin."
#: src/home/pam_systemd_home.c:904
msgid "Password change required."
SUBSYSTEM=="tty", KERNEL=="ptmx", GROUP="tty", MODE="0666"
SUBSYSTEM=="tty", KERNEL=="tty", GROUP="tty", MODE="0666"
-SUBSYSTEM=="tty", KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620"
-SUBSYSTEM=="tty", KERNEL=="sclp_line[0-9]*", GROUP="tty", MODE="0620"
-SUBSYSTEM=="tty", KERNEL=="ttysclp[0-9]*", GROUP="tty", MODE="0620"
-SUBSYSTEM=="tty", KERNEL=="3270/tty[0-9]*", GROUP="tty", MODE="0620"
+SUBSYSTEM=="tty", KERNEL=="tty[0-9]*|hvc[0-9]*|sclp_line[0-9]*|ttysclp[0-9]*|3270/tty[0-9]*", GROUP="tty", MODE="0620"
SUBSYSTEM=="vc", KERNEL=="vcs*|vcsa*", GROUP="tty"
KERNEL=="tty[A-Z]*[0-9]|ttymxc[0-9]*|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout"
ACTION=="remove", GOTO="net_end"
SUBSYSTEM!="net", GOTO="net_end"
+IMPORT{builtin}="hwdb 'net:naming:dr$env{ID_NET_DRIVER}:'"
+
IMPORT{builtin}="net_id"
SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
* Treat explicit empty path to mean that nothing should be appended. */
old = getenv("SYSTEMD_UNIT_PATH");
if (!streq_ptr(old, "") &&
- !strextend_with_separator(&joined, ":", old ?: ""))
+ !strextend_with_separator(&joined, ":", strempty(old)))
return -ENOMEM;
assert_se(set_unit_path(joined) >= 0);
assert(u);
- ExecCommand *exec =
- u->type == UNIT_SOCKET ? SOCKET(u)->control_command :
- u->type == UNIT_MOUNT ? MOUNT(u)->control_command :
- u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL;
- RET_GATHER(r, verify_executable(u, exec, root));
+ if (u->type == UNIT_MOUNT)
+ FOREACH_ARRAY(i, MOUNT(u)->exec_command, ELEMENTSOF(MOUNT(u)->exec_command))
+ RET_GATHER(r, verify_executable(u, i, root));
if (u->type == UNIT_SERVICE)
FOREACH_ARRAY(i, SERVICE(u)->exec_command, ELEMENTSOF(SERVICE(u)->exec_command))
- RET_GATHER(r, verify_executable(u, *i, root));
+ LIST_FOREACH(command, j, *i)
+ RET_GATHER(r, verify_executable(u, j, root));
if (u->type == UNIT_SOCKET)
FOREACH_ARRAY(i, SOCKET(u)->exec_command, ELEMENTSOF(SOCKET(u)->exec_command))
- RET_GATHER(r, verify_executable(u, *i, root));
+ LIST_FOREACH(command, j, *i)
+ RET_GATHER(r, verify_executable(u, j, root));
+
+ if (u->type == UNIT_SWAP)
+ FOREACH_ARRAY(i, SWAP(u)->exec_command, ELEMENTSOF(SWAP(u)->exec_command))
+ RET_GATHER(r, verify_executable(u, i, root));
return r;
}
" -LIBCRYPTSETUP"
#endif
+#if HAVE_LIBCRYPTSETUP_PLUGINS
+ " +LIBCRYPTSETUP_PLUGINS"
+#else
+ " -LIBCRYPTSETUP_PLUGINS"
+#endif
+
#if HAVE_LIBFDISK
" +LIBFDISK"
#else
_cleanup_free_ char *controller = NULL;
int enabled = 0;
- errno = 0;
if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
+ if (ferror(f))
+ return -errno;
+
if (feof(f))
break;
- if (ferror(f))
- return errno_or_else(EIO);
-
return -EBADMSG;
}
return strv_env_replace_consume(l, p);
}
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) {
+ int r;
+
+ assert(l);
+ assert(key);
+
+ if (!env_name_is_valid(key))
+ return -EINVAL;
+
+ if (!valuef) {
+ strv_env_unset(*l, key);
+ return 0;
+ }
+
+ _cleanup_free_ char *value = NULL;
+ va_list ap;
+ va_start(ap, valuef);
+ r = vasprintf(&value, valuef, ap);
+ va_end(ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ char *p = strjoin(key, "=", value);
+ if (!p)
+ return -ENOMEM;
+
+ return strv_env_replace_consume(l, p);
+}
+
int _strv_env_assign_many(char ***l, ...) {
va_list ap;
int r;
int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value);
+int strv_env_assignf(char ***l, const char *key, const char *valuef, ...) _printf_(3, 4);
int _strv_env_assign_many(char ***l, ...) _sentinel_;
#define strv_env_assign_many(l, ...) _strv_env_assign_many(l, __VA_ARGS__, NULL)
*ret = a.ether;
return 0;
}
+
+void ether_addr_mark_random(struct ether_addr *addr) {
+ assert(addr);
+
+ /* see eth_random_addr in the kernel */
+ addr->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
+ addr->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
+}
extern const struct hash_ops ether_addr_hash_ops;
extern const struct hash_ops ether_addr_hash_ops_free;
+
+void ether_addr_mark_random(struct ether_addr *addr);
return x;
}
-void iovec_array_free(struct iovec *iovec, size_t n) {
- FOREACH_ARRAY(i, iovec, n)
+void iovec_array_free(struct iovec *iovec, size_t n_iovec) {
+ assert(iovec || n_iovec == 0);
+
+ FOREACH_ARRAY(i, iovec, n_iovec)
free(i->iov_base);
free(iovec);
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
-void iovec_array_free(struct iovec *iovec, size_t n);
+void iovec_array_free(struct iovec *iovec, size_t n_iovec);
}
int parse_mtu(int family, const char *s, uint32_t *ret) {
- uint64_t u;
- size_t m;
+ uint64_t u, m;
int r;
r = parse_size(s, 1024, &u);
if (u > UINT32_MAX)
return -ERANGE;
- if (family == AF_INET6)
+ switch (family) {
+ case AF_INET:
+ m = IPV4_MIN_MTU; /* This is 68 */
+ break;
+ case AF_INET6:
m = IPV6_MIN_MTU; /* This is 1280 */
- else
- m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */
+ break;
+ default:
+ m = 0;
+ }
if (u < m)
return -ERANGE;
return r;
}
+int pidref_set_parent(PidRef *ret) {
+ _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
+ pid_t ppid;
+ int r;
+
+ assert(ret);
+
+ /* Acquires a pidref to our parent process. Deals with the fact that parent processes might exit, and
+ * we get reparented to other processes, with our old parent's PID already being recycled. */
+
+ ppid = getppid();
+ for (;;) {
+ r = pidref_set_pid(&parent, ppid);
+ if (r < 0)
+ return r;
+
+ if (parent.fd < 0) /* If pidfds are not available, then we are done */
+ break;
+
+ pid_t now_ppid = getppid();
+ if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
+ break;
+
+ /* Otherwise let's try again with the new ppid */
+ ppid = now_ppid;
+ pidref_done(&parent);
+ }
+
+ *ret = TAKE_PIDREF(parent);
+ return 0;
+}
+
void pidref_done(PidRef *pidref) {
assert(pidref);
int pidref_set_pidfd(PidRef *pidref, int fd);
int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/
int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
-
+int pidref_set_parent(PidRef *ret);
static inline int pidref_set_self(PidRef *pidref) {
return pidref_set_pid(pidref, 0);
}
#include "errno-util.h"
#include "extract-word.h"
#include "fd-util.h"
+#include "fileio.h"
#include "format-util.h"
#include "macro.h"
#include "missing_resource.h"
+#include "process-util.h"
#include "rlimit-util.h"
#include "string-table.h"
+#include "strv.h"
#include "time-util.h"
int setrlimit_closest(int resource, const struct rlimit *rlim) {
return 1;
}
+
+int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret) {
+
+ static const char * const prefix_table[_RLIMIT_MAX] = {
+ [RLIMIT_CPU] = "Max cpu time",
+ [RLIMIT_FSIZE] = "Max file size",
+ [RLIMIT_DATA] = "Max data size",
+ [RLIMIT_STACK] = "Max stack size",
+ [RLIMIT_CORE] = "Max core file size",
+ [RLIMIT_RSS] = "Max resident set",
+ [RLIMIT_NPROC] = "Max processes",
+ [RLIMIT_NOFILE] = "Max open files",
+ [RLIMIT_MEMLOCK] = "Max locked memory",
+ [RLIMIT_AS] = "Max address space",
+ [RLIMIT_LOCKS] = "Max file locks",
+ [RLIMIT_SIGPENDING] = "Max pending signals",
+ [RLIMIT_MSGQUEUE] = "Max msgqueue size",
+ [RLIMIT_NICE] = "Max nice priority",
+ [RLIMIT_RTPRIO] = "Max realtime priority",
+ [RLIMIT_RTTIME] = "Max realtime timeout",
+ };
+
+ int r;
+
+ assert(resource >= 0);
+ assert(resource < _RLIMIT_MAX);
+ assert(pid >= 0);
+ assert(ret);
+
+ if (pid == 0 || pid == getpid_cached())
+ return RET_NERRNO(getrlimit(resource, ret));
+
+ r = RET_NERRNO(prlimit(pid, resource, /* new_limit= */ NULL, ret));
+ if (!ERRNO_IS_NEG_PRIVILEGE(r))
+ return r;
+
+ /* We don't have access? Then try to go via /proc/$PID/limits. Weirdly that's world readable in
+ * contrast to querying the data via prlimit() */
+
+ const char *p = procfs_file_alloca(pid, "limits");
+ _cleanup_free_ char *limits = NULL;
+
+ r = read_full_virtual_file(p, &limits, NULL);
+ if (r < 0)
+ return -EPERM; /* propagate original permission error if we can't access the limits file */
+
+ _cleanup_strv_free_ char **l = NULL;
+ l = strv_split(limits, "\n");
+ if (!l)
+ return -ENOMEM;
+
+ STRV_FOREACH(i, strv_skip(l, 1)) {
+ _cleanup_free_ char *soft = NULL, *hard = NULL;
+ uint64_t sv, hv;
+ const char *e;
+
+ e = startswith(*i, prefix_table[resource]);
+ if (!e)
+ continue;
+
+ if (*e != ' ')
+ continue;
+
+ e += strspn(e, WHITESPACE);
+
+ size_t n;
+ n = strcspn(e, WHITESPACE);
+ if (n == 0)
+ continue;
+
+ soft = strndup(e, n);
+ if (!soft)
+ return -ENOMEM;
+
+ e += n;
+ if (*e != ' ')
+ continue;
+
+ e += strspn(e, WHITESPACE);
+ n = strcspn(e, WHITESPACE);
+ if (n == 0)
+ continue;
+
+ hard = strndup(e, n);
+ if (!hard)
+ return -ENOMEM;
+
+ if (streq(soft, "unlimited"))
+ sv = RLIM_INFINITY;
+ else {
+ r = safe_atou64(soft, &sv);
+ if (r < 0)
+ return r;
+ }
+
+ if (streq(hard, "unlimited"))
+ hv = RLIM_INFINITY;
+ else {
+ r = safe_atou64(hard, &hv);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = (struct rlimit) {
+ .rlim_cur = sv,
+ .rlim_max = hv,
+ };
+
+ return 0;
+ }
+
+ return -ENOTRECOVERABLE;
+}
int rlimit_nofile_bump(int limit);
int rlimit_nofile_safe(void);
+
+int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret);
}
static const char *const static_signal_table[] = {
- [SIGHUP] = "HUP",
- [SIGINT] = "INT",
- [SIGQUIT] = "QUIT",
- [SIGILL] = "ILL",
- [SIGTRAP] = "TRAP",
- [SIGABRT] = "ABRT",
- [SIGBUS] = "BUS",
- [SIGFPE] = "FPE",
- [SIGKILL] = "KILL",
- [SIGUSR1] = "USR1",
- [SIGSEGV] = "SEGV",
- [SIGUSR2] = "USR2",
- [SIGPIPE] = "PIPE",
- [SIGALRM] = "ALRM",
- [SIGTERM] = "TERM",
+ [SIGHUP] = "HUP",
+ [SIGINT] = "INT",
+ [SIGQUIT] = "QUIT",
+ [SIGILL] = "ILL",
+ [SIGTRAP] = "TRAP",
+ [SIGABRT] = "ABRT",
+ [SIGBUS] = "BUS",
+ [SIGFPE] = "FPE",
+ [SIGKILL] = "KILL",
+ [SIGUSR1] = "USR1",
+ [SIGSEGV] = "SEGV",
+ [SIGUSR2] = "USR2",
+ [SIGPIPE] = "PIPE",
+ [SIGALRM] = "ALRM",
+ [SIGTERM] = "TERM",
#ifdef SIGSTKFLT
[SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
#endif
- [SIGCHLD] = "CHLD",
- [SIGCONT] = "CONT",
- [SIGSTOP] = "STOP",
- [SIGTSTP] = "TSTP",
- [SIGTTIN] = "TTIN",
- [SIGTTOU] = "TTOU",
- [SIGURG] = "URG",
- [SIGXCPU] = "XCPU",
- [SIGXFSZ] = "XFSZ",
+ [SIGCHLD] = "CHLD",
+ [SIGCONT] = "CONT",
+ [SIGSTOP] = "STOP",
+ [SIGTSTP] = "TSTP",
+ [SIGTTIN] = "TTIN",
+ [SIGTTOU] = "TTOU",
+ [SIGURG] = "URG",
+ [SIGXCPU] = "XCPU",
+ [SIGXFSZ] = "XFSZ",
[SIGVTALRM] = "VTALRM",
- [SIGPROF] = "PROF",
- [SIGWINCH] = "WINCH",
- [SIGIO] = "IO",
- [SIGPWR] = "PWR",
- [SIGSYS] = "SYS"
+ [SIGPROF] = "PROF",
+ [SIGWINCH] = "WINCH",
+ [SIGIO] = "IO",
+ [SIGPWR] = "PWR",
+ [SIGSYS] = "SYS"
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
static inline void siphash24_compress_boolean(bool in, struct siphash *state) {
uint8_t i = in;
-
siphash24_compress(&i, sizeof i, state);
}
static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) {
- siphash24_compress(&in, sizeof in, state);
+ uint64_t u = htole64(in);
+ siphash24_compress(&u, sizeof u, state);
}
static inline void siphash24_compress_safe(const void *in, size_t inlen, struct siphash *state) {
int getpeercred(int fd, struct ucred *ucred) {
socklen_t n = sizeof(struct ucred);
struct ucred u;
- int r;
assert(fd >= 0);
assert(ucred);
- r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n);
- if (r < 0)
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &u, &n) < 0)
return -errno;
if (n != sizeof(struct ucred))
if (!s)
return -ENOMEM;
- if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) {
+ s[n] = 0;
break;
+ }
if (errno != ERANGE)
return -errno;
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "hexdecoct.h"
#include "inotify-util.h"
#include "io-util.h"
#include "log.h"
return 0;
}
+
+void termios_disable_echo(struct termios *termios) {
+ assert(termios);
+
+ termios->c_lflag &= ~(ICANON|ECHO);
+ termios->c_cc[VMIN] = 1;
+ termios->c_cc[VTIME] = 0;
+}
+
+typedef enum BackgroundColorState {
+ BACKGROUND_TEXT,
+ BACKGROUND_ESCAPE,
+ BACKGROUND_BRACKET,
+ BACKGROUND_FIRST_ONE,
+ BACKGROUND_SECOND_ONE,
+ BACKGROUND_SEMICOLON,
+ BACKGROUND_R,
+ BACKGROUND_G,
+ BACKGROUND_B,
+ BACKGROUND_RED,
+ BACKGROUND_GREEN,
+ BACKGROUND_BLUE,
+} BackgroundColorState;
+
+typedef struct BackgroundColorContext {
+ BackgroundColorState state;
+ uint32_t red, green, blue;
+ unsigned red_bits, green_bits, blue_bits;
+} BackgroundColorContext;
+
+static int scan_background_color_response(
+ BackgroundColorContext *context,
+ const char *buf,
+ size_t size) {
+
+ assert(context);
+ assert(buf || size == 0);
+
+ for (size_t i = 0; i < size; i++) {
+ char c = buf[i];
+
+ switch (context->state) {
+
+ case BACKGROUND_TEXT:
+ context->state = c == '\x1B' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_ESCAPE:
+ context->state = c == ']' ? BACKGROUND_BRACKET : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_BRACKET:
+ context->state = c == '1' ? BACKGROUND_FIRST_ONE : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_FIRST_ONE:
+ context->state = c == '1' ? BACKGROUND_SECOND_ONE : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_SECOND_ONE:
+ context->state = c == ';' ? BACKGROUND_SEMICOLON : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_SEMICOLON:
+ context->state = c == 'r' ? BACKGROUND_R : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_R:
+ context->state = c == 'g' ? BACKGROUND_G : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_G:
+ context->state = c == 'b' ? BACKGROUND_B : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_B:
+ context->state = c == ':' ? BACKGROUND_RED : BACKGROUND_TEXT;
+ break;
+
+ case BACKGROUND_RED:
+ if (c == '/')
+ context->state = context->red_bits > 0 ? BACKGROUND_GREEN : BACKGROUND_TEXT;
+ else {
+ int d = unhexchar(c);
+ if (d < 0 || context->red_bits >= sizeof(context->red)*8)
+ context->state = BACKGROUND_TEXT;
+ else {
+ context->red = (context->red << 4) | d;
+ context->red_bits += 4;
+ }
+ }
+ break;
+
+ case BACKGROUND_GREEN:
+ if (c == '/')
+ context->state = context->green_bits > 0 ? BACKGROUND_BLUE : BACKGROUND_TEXT;
+ else {
+ int d = unhexchar(c);
+ if (d < 0 || context->green_bits >= sizeof(context->green)*8)
+ context->state = BACKGROUND_TEXT;
+ else {
+ context->green = (context->green << 4) | d;
+ context->green_bits += 4;
+ }
+ }
+ break;
+
+ case BACKGROUND_BLUE:
+ if (c == '\x07') {
+ if (context->blue_bits > 0)
+ return 1; /* success! */
+
+ context->state = BACKGROUND_TEXT;
+ } else {
+ int d = unhexchar(c);
+ if (d < 0 || context->blue_bits >= sizeof(context->blue)*8)
+ context->state = BACKGROUND_TEXT;
+ else {
+ context->blue = (context->blue << 4) | d;
+ context->blue_bits += 4;
+ }
+ }
+ break;
+ }
+
+ /* Reset any colors we might have picked up */
+ if (context->state == BACKGROUND_TEXT) {
+ /* reset color */
+ context->red = context->green = context->blue = 0;
+ context->red_bits = context->green_bits = context->blue_bits = 0;
+ }
+ }
+
+ return 0; /* all good, but not enough data yet */
+}
+
+int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue) {
+ int r;
+
+ assert(ret_red);
+ assert(ret_green);
+ assert(ret_blue);
+
+ if (!colors_enabled())
+ return -EOPNOTSUPP;
+
+ if (isatty(STDOUT_FILENO) < 1 || isatty(STDIN_FILENO) < 1)
+ return -EOPNOTSUPP;
+
+ if (streq_ptr(getenv("TERM"), "linux")) {
+ /* Linux console is black */
+ *ret_red = *ret_green = *ret_blue = 0.0;
+ return 0;
+ }
+
+ struct termios old_termios;
+ if (tcgetattr(STDIN_FILENO, &old_termios) < 0)
+ return -errno;
+
+ struct termios new_termios = old_termios;
+ termios_disable_echo(&new_termios);
+
+ if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_termios) < 0)
+ return -errno;
+
+ r = loop_write(STDOUT_FILENO, "\x1B]11;?\x07", SIZE_MAX);
+ if (r < 0)
+ goto finish;
+
+ usec_t end = usec_add(now(CLOCK_MONOTONIC), 100 * USEC_PER_MSEC);
+ char buf[256];
+ size_t buf_full = 0;
+ BackgroundColorContext context = {};
+
+ for (;;) {
+ usec_t n = now(CLOCK_MONOTONIC);
+
+ if (n >= end) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ r = fd_wait_for_event(STDIN_FILENO, POLLIN, usec_sub_unsigned(end, n));
+ if (r < 0)
+ goto finish;
+
+ ssize_t l;
+ l = read(STDIN_FILENO, buf, sizeof(buf) - buf_full);
+ if (l < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ buf_full += l;
+ assert(buf_full <= sizeof(buf));
+
+ r = scan_background_color_response(&context, buf, buf_full);
+ if (r < 0)
+ goto finish;
+ if (r > 0) {
+ assert(context.red_bits > 0);
+ *ret_red = (double) context.red / ((UINT64_C(1) << context.red_bits) - 1);
+ assert(context.green_bits > 0);
+ *ret_green = (double) context.green / ((UINT64_C(1) << context.green_bits) - 1);
+ assert(context.blue_bits > 0);
+ *ret_blue = (double) context.blue / ((UINT64_C(1) << context.blue_bits) - 1);
+ r = 0;
+ goto finish;
+ }
+ }
+
+finish:
+ (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_termios);
+ return r;
+}
#include <stdio.h>
#include <syslog.h>
#include <sys/types.h>
+#include <termios.h>
#include "macro.h"
#include "time-util.h"
bool dev_console_colors_enabled(void);
static inline bool colors_enabled(void) {
-
/* Returns true if colors are considered supported on our stdout. */
return get_color_mode() != COLOR_OFF;
}
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
+
+void termios_disable_echo(struct termios *termios);
+
+int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue);
* ⚠️ Note we are not using plain nanosleep() here, since that operates on CLOCK_REALTIME, not
* CLOCK_MONOTONIC! */
+ if (usec == 0)
+ return 0;
+
// FIXME: use RET_NERRNO() macro here. Currently, this header cannot include errno-util.h.
return clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL) < 0 ? -errno : 0;
}
return false;
}
+int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) {
+ uid_t uid_base, uid_shift, uid_range;
+ int r;
+
+ assert(f);
+ assert(ret_base);
+ assert(ret_shift);
+ assert(ret_range);
+
+ errno = 0;
+ r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
+ if (r == EOF)
+ return errno_or_else(ENOMSG);
+ assert(r >= 0);
+ if (r != 3)
+ return -EBADMSG;
+
+ *ret_base = uid_base;
+ *ret_shift = uid_shift;
+ *ret_range = uid_range;
+
+ return 0;
+}
+
int uid_range_load_userns(UidRange **ret, const char *path) {
_cleanup_(uid_range_freep) UidRange *range = NULL;
_cleanup_fclose_ FILE *f = NULL;
for (;;) {
uid_t uid_base, uid_shift, uid_range;
- int k;
-
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
- if (k == EOF) {
- if (ferror(f))
- return errno_or_else(EIO);
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r == -ENOMSG)
break;
- }
- if (k != 3)
- return -EBADMSG;
+ if (r < 0)
+ return r;
r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false);
if (r < 0)
return uid_range_covers(range, uid, 1);
}
+int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range);
+
int uid_range_load_userns(UidRange **ret, const char *path);
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "uid-range.h"
#include "virt.h"
enum {
static int userns_has_mapping(const char *name) {
_cleanup_fclose_ FILE *f = NULL;
- uid_t a, b, c;
+ uid_t base, shift, range;
int r;
f = fopen(name, "re");
return errno == ENOENT ? false : -errno;
}
- errno = 0;
- r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &a, &b, &c);
- if (r == EOF) {
- if (ferror(f))
- return log_debug_errno(errno_or_else(EIO), "Failed to read %s: %m", name);
-
- log_debug("%s is empty, we're in an uninitialized user namespace", name);
+ r = uid_map_read_one(f, &base, &shift, &range);
+ if (r == -ENOMSG) {
+ log_debug("%s is empty, we're in an uninitialized user namespace.", name);
return true;
}
- if (r != 3)
- return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse %s: %m", name);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read %s: %m", name);
- if (a == 0 && b == 0 && c == UINT32_MAX) {
+ if (base == 0 && shift == 0 && range == UINT32_MAX) {
/* The kernel calls mappings_overlap() and does not allow overlaps */
log_debug("%s has a full 1:1 mapping", name);
return false;
}
/* Anything else implies that we are in a user namespace */
- log_debug("Mapping found in %s, we're in a user namespace", name);
+ log_debug("Mapping found in %s, we're in a user namespace.", name);
return true;
}
return 0;
}
+static int update_efi_boot_binaries(const char *esp_path, const char *source_path) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *p = NULL;
+ int r, ret = 0;
+
+ r = chase_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path);
+
+ FOREACH_DIRENT(de, d, break) {
+ _cleanup_close_ int fd = -EBADF;
+ _cleanup_free_ char *v = NULL;
+
+ if (!endswith_no_case(de->d_name, ".efi"))
+ continue;
+
+ fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
+
+ r = get_file_version(fd, &v);
+ if (r == -ESRCH)
+ continue; /* No version information */
+ if (r < 0)
+ return r;
+ if (startswith(v, "systemd-boot ")) {
+ _cleanup_free_ char *dest_path = NULL;
+
+ dest_path = path_join(p, de->d_name);
+ if (!dest_path)
+ return log_oom();
+
+ RET_GATHER(ret, copy_file_with_version_check(source_path, dest_path, /* force = */ false));
+ }
+ }
+
+ return ret;
+}
static int copy_one_file(const char *esp_path, const char *name, bool force) {
char *root = IN_SET(arg_install_source, ARG_INSTALL_SOURCE_AUTO, ARG_INSTALL_SOURCE_IMAGE) ? arg_root : NULL;
if (r < 0)
return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", v, esp_path);
- r = copy_file_with_version_check(source_path, default_dest_path, force);
- if (r < 0 && ret == 0)
- ret = r;
+ RET_GATHER(ret, copy_file_with_version_check(source_path, default_dest_path, force));
+
+ /* If we were installed under any other name in /EFI/BOOT, make sure we update those binaries
+ * as well. */
+ if (!force)
+ RET_GATHER(ret, update_efi_boot_binaries(esp_path, source_path));
}
return ret;
if (!endswith_no_case(de->d_name, ".efi"))
continue;
- if (!startswith_no_case(de->d_name, "boot"))
- continue;
-
fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
static int enumerate_binaries(
const char *esp_path,
const char *path,
- const char *prefix,
char **previous,
bool *is_first) {
if (!endswith_no_case(de->d_name, ".efi"))
continue;
- if (prefix && !startswith_no_case(de->d_name, prefix))
- continue;
-
filename = path_join(p, de->d_name);
if (!filename)
return log_oom();
printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition));
printf("\n");
- r = enumerate_binaries(esp_path, "EFI/systemd", NULL, &last, &is_first);
+ r = enumerate_binaries(esp_path, "EFI/systemd", &last, &is_first);
if (r < 0)
goto fail;
- k = enumerate_binaries(esp_path, "EFI/BOOT", "boot", &last, &is_first);
+ k = enumerate_binaries(esp_path, "EFI/BOOT", &last, &is_first);
if (k < 0) {
r = k;
goto fail;
c->restrict_network_interfaces_is_allow_list = is_allow_list;
STRV_FOREACH(s, l) {
- if (!ifname_valid(*s)) {
+ if (!ifname_valid_full(*s, IFNAME_VALID_ALTERNATIVE)) {
log_full(LOG_WARNING, "Invalid interface name, ignoring: %s", *s);
continue;
}
static BUS_DEFINE_PROPERTY_GET(property_get_cpu_sched_priority, "i", ExecContext, exec_context_get_cpu_sched_priority);
static BUS_DEFINE_PROPERTY_GET(property_get_coredump_filter, "t", ExecContext, exec_context_get_coredump_filter);
static BUS_DEFINE_PROPERTY_GET(property_get_timer_slack_nsec, "t", ExecContext, exec_context_get_timer_slack_nsec);
+static BUS_DEFINE_PROPERTY_GET(property_get_set_login_environment, "b", ExecContext, exec_context_get_set_login_environment);
static int property_get_environment_files(
sd_bus *bus,
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("SetLoginEnvironment", "b", bus_property_get_tristate, offsetof(ExecContext, set_login_environment), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SetLoginEnvironment", "b", property_get_set_login_environment, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SetCredential", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
r = bus_verify_manage_units_async_full(
u,
is_image ? "mount-image" : "bind-mount",
- CAP_SYS_ADMIN,
N_("Authentication is required to mount on '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
verb,
- CAP_SYS_ADMIN,
polkit_message_for_job[job_type],
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
jtype,
- CAP_SYS_ADMIN,
polkit_message_for_job[type],
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"kill",
- CAP_KILL,
N_("Authentication is required to send a UNIX signal to the processes of '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"reset-failed",
- CAP_SYS_ADMIN,
N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"set-property",
- CAP_SYS_ADMIN,
N_("Authentication is required to set properties on '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"ref",
- CAP_SYS_ADMIN,
- NULL,
- false,
+ /* polkit_message= */ NULL,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
"clean",
- CAP_DAC_OVERRIDE,
N_("Authentication is required to delete files and directories associated with '$(unit)'."),
- true,
message,
error);
if (r < 0)
r = bus_verify_manage_units_async_full(
u,
perm,
- CAP_SYS_ADMIN,
N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."),
- true,
message,
error);
if (r < 0)
int bus_verify_manage_units_async_full(
Unit *u,
const char *verb,
- int capability,
const char *polkit_message,
- bool interactive,
sd_bus_message *call,
sd_bus_error *error) {
return bus_verify_polkit_async(
call,
- capability,
"org.freedesktop.systemd1.manage-units",
details,
- interactive,
- UID_INVALID,
&u->manager->polkit_registry,
error);
}
static inline int bus_set_transient_usec_fix_0(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) {
return bus_set_transient_usec_internal(u, name, p, true, message, flags, error);
}
-int bus_verify_manage_units_async_full(Unit *u, const char *verb, int capability, const char *polkit_message, bool interactive, sd_bus_message *call, sd_bus_error *error);
+int bus_verify_manage_units_async_full(Unit *u, const char *verb, const char *polkit_message, sd_bus_message *call, sd_bus_error *error);
int bus_read_mount_options(sd_bus_message *message, sd_bus_error *error, MountOptions **ret_options, char **ret_format_str, const char *separator);
}
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.manage-units",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
}
int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.manage-unit-files",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
}
int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.reload-daemon",
+ /* details= */ NULL,
+ &m->polkit_registry, error);
}
int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.set-environment",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
}
int bus_verify_bypass_dump_ratelimit_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.bypass-dump-ratelimit", NULL, false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(
+ call,
+ "org.freedesktop.systemd1.bypass-dump-ratelimit",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
}
uint64_t manager_bus_n_queued_write(Manager *m) {
.status_message_formats = {
.starting_stopping = {
[0] = "Expecting device %s...",
+ [1] = "Waiting for device %s to disappear...",
},
.finished_start_job = {
[JOB_DONE] = "Found device %s.",
"Failed to determine user credentials for root: %m");
}
- bool set_user_login_env = c->set_login_environment >= 0 ? c->set_login_environment : (c->user || c->dynamic_user);
+ bool set_user_login_env = exec_context_get_set_login_environment(c);
if (username) {
x = strjoin("USER=", username);
* systemd-vconsole-setup.service also takes the lock to avoid being interrupted. We open a new fd
* that will be closed automatically, and operate on it for convenience. */
lock_fd = lock_dev_console();
- if (lock_fd < 0)
- return (void) log_debug_errno(lock_fd,
- "Failed to lock /dev/console: %m");
+ if (ERRNO_IS_NEG_PRIVILEGE(lock_fd))
+ log_debug_errno(lock_fd, "No privileges to lock /dev/console, proceeding without: %m");
+ else if (lock_fd < 0)
+ return (void) log_debug_errno(lock_fd, "Failed to lock /dev/console: %m");
if (context->tty_vhangup)
(void) terminal_vhangup_fd(fd);
assert(c);
/* First, reset the TTY (possibly kicking everybody else from the TTY) */
- exec_context_tty_reset(c, NULL);
+ exec_context_tty_reset(c, /* parameters= */ NULL);
/* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path
* configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed
if (!path)
return;
- fd = open(path, O_PATH|O_CLOEXEC);
+ fd = open(path, O_PATH|O_CLOEXEC); /* Pin the inode */
if (fd < 0)
return (void) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to open TTY inode of '%s' to adjust ownership/access mode, ignoring: %m",
r = fchmod_and_chown(fd, TTY_MODE, 0, TTY_GID);
if (r < 0)
- log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s, ignoring: %m", path);
+ log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s to " UID_FMT ":" GID_FMT ", ignoring: %m", path, (uid_t) 0, (gid_t) TTY_GID);
}
int exec_context_get_clean_directories(
return (uint64_t) MAX(r, 0);
}
+bool exec_context_get_set_login_environment(const ExecContext *c) {
+ assert(c);
+
+ if (c->set_login_environment >= 0)
+ return c->set_login_environment;
+
+ return c->user || c->dynamic_user || c->pam_name;
+}
+
char** exec_context_get_syscall_filter(const ExecContext *c) {
_cleanup_strv_free_ char **l = NULL;
}
void exec_shared_runtime_done(ExecSharedRuntime *rt) {
- if (!rt)
- return;
+ assert(rt);
if (rt->manager)
(void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
}
static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
- exec_shared_runtime_done(rt);
+ if (!rt)
+ return NULL;
+ exec_shared_runtime_done(rt);
return mfree(rt);
}
return r;
}
- if (exec_needs_network_namespace(c)) {
+ if (exec_needs_network_namespace(c))
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0)
return -errno;
- }
- if (exec_needs_ipc_namespace(c)) {
+ if (exec_needs_ipc_namespace(c))
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ipcns_storage_socket) < 0)
return -errno;
- }
r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
if (r < 0)
int exec_context_get_cpu_sched_policy(const ExecContext *c);
int exec_context_get_cpu_sched_priority(const ExecContext *c);
uint64_t exec_context_get_timer_slack_nsec(const ExecContext *c);
+bool exec_context_get_set_login_environment(const ExecContext *c);
char** exec_context_get_syscall_filter(const ExecContext *c);
char** exec_context_get_syscall_archs(const ExecContext *c);
char** exec_context_get_syscall_log(const ExecContext *c);
Manager *m;
JobType t;
Unit *u;
+ bool wait_only;
int r;
- /* While we execute this operation the job might go away (for
- * example: because it finishes immediately or is replaced by
- * a new, conflicting job.) To make sure we don't access a
- * freed job later on we store the id here, so that we can
- * verify the job is still valid. */
+ /* While we execute this operation the job might go away (for example: because it finishes immediately
+ * or is replaced by a new, conflicting job). To make sure we don't access a freed job later on we
+ * store the id here, so that we can verify the job is still valid. */
assert(j);
assert(*j);
switch (t) {
case JOB_START:
r = unit_start(u, a);
+ wait_only = r == -EBADR; /* If the unit type does not support starting, then simply wait. */
break;
case JOB_RESTART:
_fallthrough_;
case JOB_STOP:
r = unit_stop(u);
+ wait_only = r == -EBADR; /* If the unit type does not support stopping, then simply wait. */
break;
case JOB_RELOAD:
r = unit_reload(u);
+ wait_only = false; /* A clear error is generated if reload is not supported. */
break;
default:
assert_not_reached();
}
- /* Log if the job still exists and the start/stop/reload function actually did something. Note that this means
- * for units for which there's no 'activating' phase (i.e. because we transition directly from 'inactive' to
- * 'active') we'll possibly skip the "Starting..." message. */
+ /* Log if the job still exists and the start/stop/reload function actually did something or we're
+ * only waiting for unit status change (common for device units). The latter ensures that job start
+ * messages for device units are correctly shown. Note that if the job disappears too quickly, e.g.
+ * for units for which there's no 'activating' phase (i.e. because we transition directly from
+ * 'inactive' to 'active'), we'll possibly skip the "Starting..." message. */
*j = manager_get_job(m, id);
- if (*j && r > 0)
+ if (*j && (r > 0 || wait_only))
job_emit_start_message(u, id, t);
- return r;
+ return wait_only ? 0 : r;
}
int job_run_and_invalidate(Job *j) {
case JOB_START:
case JOB_STOP:
case JOB_RESTART:
- r = job_perform_on_unit(&j);
-
- /* If the unit type does not support starting/stopping, then simply wait. */
- if (r == -EBADR)
- r = 0;
- break;
-
case JOB_RELOAD:
r = job_perform_on_unit(&j);
break;
break;
}
- if (!ifname_valid(word)) {
+ if (!ifname_valid_full(word, IFNAME_VALID_ALTERNATIVE)) {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", word);
continue;
}
HASHMAP_FOREACH(u, m->units) {
- if (fdset_size(fds) <= 0)
+ if (fdset_isempty(fds))
break;
if (!UNIT_VTABLE(u)->distribute_fds)
if (!found)
log_warning("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid);
- if (fdset_size(fds) > 0)
+ if (!fdset_isempty(fds))
log_warning("Got extra auxiliary fds with notification message, closing them.");
return 0;
manager_check_basic_target(m);
- if (hashmap_size(m->jobs) > 0) {
+ if (!hashmap_isempty(m->jobs)) {
if (m->jobs_in_progress_event_source)
/* Ignore any failure, this is only for feedback */
(void) sd_event_source_set_time(m->jobs_in_progress_event_source,
}
/* Are there any failed units? If so, we are in degraded mode */
- if (set_size(m->failed_units) > 0)
+ if (!set_isempty(m->failed_units))
return MANAGER_DEGRADED;
return MANAGER_RUNNING;
#endif
/* Thresholds for logging at INFO level about resource consumption */
-#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC)
-#define MENTIONWORTHY_IO_BYTES (1024 * 1024ULL)
-#define MENTIONWORTHY_IP_BYTES (0ULL)
+#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC)
+#define MENTIONWORTHY_MEMORY_BYTES (64 * U64_MB)
+#define MENTIONWORTHY_IO_BYTES (1 * U64_MB)
+#define MENTIONWORTHY_IP_BYTES UINT64_C(0)
-/* Thresholds for logging at INFO level about resource consumption */
-#define NOTICEWORTHY_CPU_NSEC (10*60 * NSEC_PER_SEC) /* 10 minutes */
-#define NOTICEWORTHY_IO_BYTES (10 * 1024 * 1024ULL) /* 10 MB */
-#define NOTICEWORTHY_IP_BYTES (128 * 1024 * 1024ULL) /* 128 MB */
+/* Thresholds for logging at NOTICE level about resource consumption */
+#define NOTICEWORTHY_CPU_NSEC (10 * NSEC_PER_MINUTE)
+#define NOTICEWORTHY_MEMORY_BYTES (512 * U64_MB)
+#define NOTICEWORTHY_IO_BYTES (10 * U64_MB)
+#define NOTICEWORTHY_IP_BYTES (128 * U64_MB)
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
}
static int unit_log_resources(Unit *u) {
- struct iovec iovec[1 + 2 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4];
- bool any_traffic = false, have_ip_accounting = false, any_io = false, have_io_accounting = false;
- _cleanup_free_ char *igress = NULL, *egress = NULL, *rr = NULL, *wr = NULL;
- int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */
- size_t n_message_parts = 0, n_iovec = 0;
- char* message_parts[1 + 2 + 2 + 2 + 1], *t;
- nsec_t nsec = NSEC_INFINITY;
- uint64_t memory_peak = UINT64_MAX, memory_swap_peak = UINT64_MAX;
- int r;
- const char* const ip_fields[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
- [CGROUP_IP_INGRESS_BYTES] = "IP_METRIC_INGRESS_BYTES",
- [CGROUP_IP_INGRESS_PACKETS] = "IP_METRIC_INGRESS_PACKETS",
- [CGROUP_IP_EGRESS_BYTES] = "IP_METRIC_EGRESS_BYTES",
- [CGROUP_IP_EGRESS_PACKETS] = "IP_METRIC_EGRESS_PACKETS",
- };
- const char* const io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
- [CGROUP_IO_READ_BYTES] = "IO_METRIC_READ_BYTES",
- [CGROUP_IO_WRITE_BYTES] = "IO_METRIC_WRITE_BYTES",
- [CGROUP_IO_READ_OPERATIONS] = "IO_METRIC_READ_OPERATIONS",
- [CGROUP_IO_WRITE_OPERATIONS] = "IO_METRIC_WRITE_OPERATIONS",
+
+ static const struct {
+ const char *journal_field;
+ const char *message_suffix;
+ } memory_fields[_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1] = {
+ [CGROUP_MEMORY_PEAK] = { "MEMORY_PEAK", "memory peak" },
+ [CGROUP_MEMORY_SWAP_PEAK] = { "MEMORY_SWAP_PEAK", "memory swap peak" },
+ }, ip_fields[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
+ [CGROUP_IP_INGRESS_BYTES] = { "IP_METRIC_INGRESS_BYTES", "incoming IP traffic" },
+ [CGROUP_IP_EGRESS_BYTES] = { "IP_METRIC_EGRESS_BYTES", "outgoing IP traffic" },
+ [CGROUP_IP_INGRESS_PACKETS] = { "IP_METRIC_INGRESS_PACKETS", NULL },
+ [CGROUP_IP_EGRESS_PACKETS] = { "IP_METRIC_EGRESS_PACKETS", NULL },
+ }, io_fields[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
+ [CGROUP_IO_READ_BYTES] = { "IO_METRIC_READ_BYTES", "read from disk" },
+ [CGROUP_IO_WRITE_BYTES] = { "IO_METRIC_WRITE_BYTES", "written to disk" },
+ [CGROUP_IO_READ_OPERATIONS] = { "IO_METRIC_READ_OPERATIONS", NULL },
+ [CGROUP_IO_WRITE_OPERATIONS] = { "IO_METRIC_WRITE_OPERATIONS", NULL },
};
+ struct iovec *iovec = NULL;
+ size_t n_iovec = 0;
+ _cleanup_free_ char *message = NULL, *t = NULL;
+ nsec_t cpu_nsec = NSEC_INFINITY;
+ int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */
+
assert(u);
+ CLEANUP_ARRAY(iovec, n_iovec, iovec_array_free);
+
+ iovec = new(struct iovec, 1 + (_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1) +
+ _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4);
+ if (!iovec)
+ return log_oom();
+
/* Invoked whenever a unit enters failed or dead state. Logs information about consumed resources if resource
* accounting was enabled for a unit. It does this in two ways: a friendly human readable string with reduced
* information and the complete data in structured fields. */
- (void) unit_get_cpu_usage(u, &nsec);
- if (nsec != NSEC_INFINITY) {
+ (void) unit_get_cpu_usage(u, &cpu_nsec);
+ if (cpu_nsec != NSEC_INFINITY) {
/* Format the CPU time for inclusion in the structured log message */
- if (asprintf(&t, "CPU_USAGE_NSEC=%" PRIu64, nsec) < 0) {
- r = log_oom();
- goto finish;
- }
- iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
+ if (asprintf(&t, "CPU_USAGE_NSEC=%" PRIu64, cpu_nsec) < 0)
+ return log_oom();
+ iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
/* Format the CPU time for inclusion in the human language message string */
- t = strjoin("consumed ", FORMAT_TIMESPAN(nsec / NSEC_PER_USEC, USEC_PER_MSEC), " CPU time");
- if (!t) {
- r = log_oom();
- goto finish;
- }
-
- message_parts[n_message_parts++] = t;
+ if (strextendf_with_separator(&message, ", ",
+ "Consumed %s CPU time",
+ FORMAT_TIMESPAN(cpu_nsec / NSEC_PER_USEC, USEC_PER_MSEC)) < 0)
+ return log_oom();
log_level = raise_level(log_level,
- nsec > MENTIONWORTHY_CPU_NSEC,
- nsec > NOTICEWORTHY_CPU_NSEC);
+ cpu_nsec > MENTIONWORTHY_CPU_NSEC,
+ cpu_nsec > NOTICEWORTHY_CPU_NSEC);
}
- (void) unit_get_memory_accounting(u, CGROUP_MEMORY_PEAK, &memory_peak);
- if (memory_peak != UINT64_MAX) {
- /* Format peak memory for inclusion in the structured log message */
- if (asprintf(&t, "MEMORY_PEAK=%" PRIu64, memory_peak) < 0) {
- r = log_oom();
- goto finish;
- }
- iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
+ for (CGroupMemoryAccountingMetric metric = 0; metric <= _CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST; metric++) {
+ uint64_t v = UINT64_MAX;
- /* Format peak memory for inclusion in the human language message string */
- t = strjoin(FORMAT_BYTES(memory_peak), " memory peak");
- if (!t) {
- r = log_oom();
- goto finish;
- }
- message_parts[n_message_parts++] = t;
- }
+ assert(memory_fields[metric].journal_field);
+ assert(memory_fields[metric].message_suffix);
- (void) unit_get_memory_accounting(u, CGROUP_MEMORY_SWAP_PEAK, &memory_swap_peak);
- if (memory_swap_peak != UINT64_MAX) {
- /* Format peak swap memory for inclusion in the structured log message */
- if (asprintf(&t, "MEMORY_SWAP_PEAK=%" PRIu64, memory_swap_peak) < 0) {
- r = log_oom();
- goto finish;
- }
- iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
+ (void) unit_get_memory_accounting(u, metric, &v);
+ if (v == UINT64_MAX)
+ continue;
- /* Format peak swap memory for inclusion in the human language message string */
- t = strjoin(FORMAT_BYTES(memory_swap_peak), " memory swap peak");
- if (!t) {
- r = log_oom();
- goto finish;
- }
- message_parts[n_message_parts++] = t;
+ if (asprintf(&t, "%s=%" PRIu64, memory_fields[metric].journal_field, v) < 0)
+ return log_oom();
+ iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
+
+ if (strextendf_with_separator(&message, ", ", "%s %s",
+ FORMAT_BYTES(v), memory_fields[metric].message_suffix) < 0)
+ return log_oom();
+
+ log_level = raise_level(log_level,
+ v > MENTIONWORTHY_MEMORY_BYTES,
+ v > NOTICEWORTHY_MEMORY_BYTES);
}
for (CGroupIOAccountingMetric k = 0; k < _CGROUP_IO_ACCOUNTING_METRIC_MAX; k++) {
uint64_t value = UINT64_MAX;
- assert(io_fields[k]);
+ assert(io_fields[k].journal_field);
(void) unit_get_io_accounting(u, k, k > 0, &value);
if (value == UINT64_MAX)
continue;
- have_io_accounting = true;
- if (value > 0)
- any_io = true;
-
/* Format IO accounting data for inclusion in the structured log message */
- if (asprintf(&t, "%s=%" PRIu64, io_fields[k], value) < 0) {
- r = log_oom();
- goto finish;
- }
- iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
+ if (asprintf(&t, "%s=%" PRIu64, io_fields[k].journal_field, value) < 0)
+ return log_oom();
+ iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
/* Format the IO accounting data for inclusion in the human language message string, but only
* for the bytes counters (and not for the operations counters) */
- if (k == CGROUP_IO_READ_BYTES) {
- assert(!rr);
- rr = strjoin("read ", strna(FORMAT_BYTES(value)), " from disk");
- if (!rr) {
- r = log_oom();
- goto finish;
- }
- } else if (k == CGROUP_IO_WRITE_BYTES) {
- assert(!wr);
- wr = strjoin("written ", strna(FORMAT_BYTES(value)), " to disk");
- if (!wr) {
- r = log_oom();
- goto finish;
- }
- }
+ if (io_fields[k].message_suffix) {
+ if (strextendf_with_separator(&message, ", ", "%s %s",
+ FORMAT_BYTES(value), io_fields[k].message_suffix) < 0)
+ return log_oom();
- if (IN_SET(k, CGROUP_IO_READ_BYTES, CGROUP_IO_WRITE_BYTES))
log_level = raise_level(log_level,
value > MENTIONWORTHY_IO_BYTES,
value > NOTICEWORTHY_IO_BYTES);
- }
-
- if (have_io_accounting) {
- if (any_io) {
- if (rr)
- message_parts[n_message_parts++] = TAKE_PTR(rr);
- if (wr)
- message_parts[n_message_parts++] = TAKE_PTR(wr);
-
- } else {
- char *k;
-
- k = strdup("no IO");
- if (!k) {
- r = log_oom();
- goto finish;
- }
-
- message_parts[n_message_parts++] = k;
}
}
for (CGroupIPAccountingMetric m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
uint64_t value = UINT64_MAX;
- assert(ip_fields[m]);
+ assert(ip_fields[m].journal_field);
(void) unit_get_ip_accounting(u, m, &value);
if (value == UINT64_MAX)
continue;
- have_ip_accounting = true;
- if (value > 0)
- any_traffic = true;
-
/* Format IP accounting data for inclusion in the structured log message */
- if (asprintf(&t, "%s=%" PRIu64, ip_fields[m], value) < 0) {
- r = log_oom();
- goto finish;
- }
- iovec[n_iovec++] = IOVEC_MAKE_STRING(t);
-
- /* Format the IP accounting data for inclusion in the human language message string, but only for the
- * bytes counters (and not for the packets counters) */
- if (m == CGROUP_IP_INGRESS_BYTES) {
- assert(!igress);
- igress = strjoin("received ", strna(FORMAT_BYTES(value)), " IP traffic");
- if (!igress) {
- r = log_oom();
- goto finish;
- }
- } else if (m == CGROUP_IP_EGRESS_BYTES) {
- assert(!egress);
- egress = strjoin("sent ", strna(FORMAT_BYTES(value)), " IP traffic");
- if (!egress) {
- r = log_oom();
- goto finish;
- }
- }
+ if (asprintf(&t, "%s=%" PRIu64, ip_fields[m].journal_field, value) < 0)
+ return log_oom();
+ iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
+
+ /* Format the IP accounting data for inclusion in the human language message string, but only
+ * for the bytes counters (and not for the packets counters) */
+ if (ip_fields[m].message_suffix) {
+ if (strextendf_with_separator(&message, ", ", "%s %s",
+ FORMAT_BYTES(value), ip_fields[m].message_suffix) < 0)
+ return log_oom();
- if (IN_SET(m, CGROUP_IP_INGRESS_BYTES, CGROUP_IP_EGRESS_BYTES))
log_level = raise_level(log_level,
value > MENTIONWORTHY_IP_BYTES,
value > NOTICEWORTHY_IP_BYTES);
- }
-
- /* This check is here because it is the earliest point following all possible log_level assignments. If
- * log_level is assigned anywhere after this point, move this check. */
- if (!unit_log_level_test(u, log_level)) {
- r = 0;
- goto finish;
- }
-
- if (have_ip_accounting) {
- if (any_traffic) {
- if (igress)
- message_parts[n_message_parts++] = TAKE_PTR(igress);
- if (egress)
- message_parts[n_message_parts++] = TAKE_PTR(egress);
-
- } else {
- char *k;
-
- k = strdup("no IP traffic");
- if (!k) {
- r = log_oom();
- goto finish;
- }
-
- message_parts[n_message_parts++] = k;
}
}
+ /* This check is here because it is the earliest point following all possible log_level assignments.
+ * (If log_level is assigned anywhere after this point, move this check.) */
+ if (!unit_log_level_test(u, log_level))
+ return 0;
+
/* Is there any accounting data available at all? */
if (n_iovec == 0) {
- r = 0;
- goto finish;
- }
-
- if (n_message_parts == 0)
- t = strjoina("MESSAGE=", u->id, ": Completed.");
- else {
- _cleanup_free_ char *joined = NULL;
-
- message_parts[n_message_parts] = NULL;
-
- joined = strv_join(message_parts, ", ");
- if (!joined) {
- r = log_oom();
- goto finish;
- }
-
- joined[0] = ascii_toupper(joined[0]);
- t = strjoina("MESSAGE=", u->id, ": ", joined, ".");
+ assert(!message);
+ return 0;
}
- /* The following four fields we allocate on the stack or are static strings, we hence don't want to free them,
- * and hence don't increase n_iovec for them */
- iovec[n_iovec] = IOVEC_MAKE_STRING(t);
- iovec[n_iovec + 1] = IOVEC_MAKE_STRING("MESSAGE_ID=" SD_MESSAGE_UNIT_RESOURCES_STR);
-
- t = strjoina(u->manager->unit_log_field, u->id);
- iovec[n_iovec + 2] = IOVEC_MAKE_STRING(t);
-
- t = strjoina(u->manager->invocation_log_field, u->invocation_id_string);
- iovec[n_iovec + 3] = IOVEC_MAKE_STRING(t);
+ t = strjoin("MESSAGE=", u->id, ": ", message ?: "Completed", ".");
+ if (!t)
+ return log_oom();
+ iovec[n_iovec++] = IOVEC_MAKE_STRING(TAKE_PTR(t));
- log_unit_struct_iovec(u, log_level, iovec, n_iovec + 4);
- r = 0;
+ if (!set_iovec_string_field(iovec, &n_iovec, "MESSAGE_ID=", SD_MESSAGE_UNIT_RESOURCES_STR))
+ return log_oom();
-finish:
- free_many_charp(message_parts, n_message_parts);
+ if (!set_iovec_string_field(iovec, &n_iovec, u->manager->unit_log_field, u->id))
+ return log_oom();
- for (size_t i = 0; i < n_iovec; i++)
- free(iovec[i].iov_base);
+ if (!set_iovec_string_field(iovec, &n_iovec, u->manager->invocation_log_field, u->invocation_id_string))
+ return log_oom();
- return r;
+ log_unit_struct_iovec(u, log_level, iovec, n_iovec);
+ return 0;
}
static void unit_update_on_console(Unit *u) {
#include "terminal-util.h"
#include "tpm2-pcr.h"
#include "tpm2-util.h"
+#include "user-util.h"
+#include "varlink.h"
+#include "varlink-io.systemd.Credentials.h"
#include "verbs.h"
typedef enum TranscodeMode {
static usec_t arg_not_after = USEC_INFINITY;
static bool arg_pretty = false;
static bool arg_quiet = false;
+static bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
if (arg_tpm2_public_key_pcr_mask == UINT32_MAX)
arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_BOOT;
+ r = varlink_invocation(VARLINK_ALLOW_ACCEPT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
+ arg_varlink = r;
+
return 1;
}
return dispatch_verb(argc, argv, verbs, NULL);
}
+typedef struct MethodEncryptParameters {
+ const char *name;
+ const char *text;
+ struct iovec data;
+ uint64_t timestamp;
+ uint64_t not_after;
+} MethodEncryptParameters;
+
+static void method_encrypt_parameters_done(MethodEncryptParameters *p) {
+ assert(p);
+
+ iovec_done_erase(&p->data);
+}
+
+static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodEncryptParameters, name), 0 },
+ { "text", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodEncryptParameters, text), 0 },
+ { "data", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodEncryptParameters, data), 0 },
+ { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodEncryptParameters, timestamp), 0 },
+ { "notAfter", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodEncryptParameters, not_after), 0 },
+ {}
+ };
+ _cleanup_(method_encrypt_parameters_done) MethodEncryptParameters p = {
+ .timestamp = UINT64_MAX,
+ .not_after = UINT64_MAX,
+ };
+ _cleanup_(iovec_done) struct iovec output = {};
+ int r;
+
+ assert(link);
+
+ json_variant_sensitive(parameters);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (p.name && !credential_name_valid(p.name))
+ return varlink_error_invalid_parameter_name(link, "name");
+ /* Specifying both or neither the text string and the binary data is not allowed */
+ if (!!p.text == !!p.data.iov_base)
+ return varlink_error_invalid_parameter_name(link, "data");
+ if (p.timestamp == UINT64_MAX)
+ p.timestamp = now(CLOCK_REALTIME);
+ if (p.not_after != UINT64_MAX && p.not_after < p.timestamp)
+ return varlink_error_invalid_parameter_name(link, "notAfter");
+
+ r = encrypt_credential_and_warn(
+ arg_with_key,
+ p.name,
+ p.timestamp,
+ p.not_after,
+ arg_tpm2_device,
+ arg_tpm2_pcr_mask,
+ arg_tpm2_public_key,
+ arg_tpm2_public_key_pcr_mask,
+ p.text ?: p.data.iov_base, p.text ? strlen(p.text) : p.data.iov_len,
+ &output.iov_base, &output.iov_len);
+ if (r < 0)
+ return r;
+
+ _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL;
+
+ r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("blob", &output)));
+ if (r < 0)
+ return r;
+
+ /* Let's also mark the (theoretically encrypted) reply as sensitive, in case the NULL encryption scheme was used. */
+ json_variant_sensitive(reply);
+
+ return varlink_reply(link, reply);
+}
+
+typedef struct MethodDecryptParameters {
+ const char *name;
+ struct iovec blob;
+ uint64_t timestamp;
+} MethodDecryptParameters;
+
+static void method_decrypt_parameters_done(MethodDecryptParameters *p) {
+ assert(p);
+
+ iovec_done_erase(&p->blob);
+}
+
+static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodDecryptParameters, name), 0 },
+ { "blob", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodDecryptParameters, blob), 0 },
+ { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodDecryptParameters, timestamp), 0 },
+ {}
+ };
+ _cleanup_(method_decrypt_parameters_done) MethodDecryptParameters p = {
+ .timestamp = UINT64_MAX,
+ };
+ _cleanup_(iovec_done_erase) struct iovec output = {};
+ int r;
+
+ assert(link);
+
+ /* Let's also mark the (theoretically encrypted) input as sensitive, in case the NULL encryption scheme was used. */
+ json_variant_sensitive(parameters);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (p.name && !credential_name_valid(p.name))
+ return varlink_error_invalid_parameter_name(link, "name");
+ if (!p.blob.iov_base)
+ return varlink_error_invalid_parameter_name(link, "blob");
+ if (p.timestamp == UINT64_MAX)
+ p.timestamp = now(CLOCK_REALTIME);
+
+ r = decrypt_credential_and_warn(
+ p.name,
+ p.timestamp,
+ arg_tpm2_device,
+ arg_tpm2_signature,
+ p.blob.iov_base, p.blob.iov_len,
+ &output.iov_base, &output.iov_len);
+ if (r == -EBADMSG)
+ return varlink_error(link, "io.systemd.Credentials.BadFormat", NULL);
+ if (r == -EREMOTE)
+ return varlink_error(link, "io.systemd.Credentials.NameMismatch", NULL);
+ if (r == -ESTALE)
+ return varlink_error(link, "io.systemd.Credentials.TimeMismatch", NULL);
+ if (r < 0)
+ return r;
+
+ _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL;
+
+ r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("data", &output)));
+ if (r < 0)
+ return r;
+
+ json_variant_sensitive(reply);
+
+ return varlink_reply(link, reply);
+}
+
static int run(int argc, char *argv[]) {
int r;
if (r <= 0)
return r;
+ if (arg_varlink) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
+
+ /* Invocation as Varlink service */
+
+ r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_INHERIT_USERDATA);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+ r = varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_Credentials);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+ r = varlink_server_bind_method_many(
+ varlink_server,
+ "io.systemd.Credentials.Encrypt", vl_method_encrypt,
+ "io.systemd.Credentials.Decrypt", vl_method_decrypt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind Varlink methods: %m");
+
+ r = varlink_server_loop_auto(varlink_server);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run Varlink event loop: %m");
+
+ return 0;
+ }
+
return creds_main(argc, argv);
}
#include "memory-util.h"
#include "openssl-util.h"
#include "pkcs11-util.h"
-#include "random-util.h"
int enroll_pkcs11(
struct crypt_device *cd,
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_free_ char *keyslot_as_string = NULL;
- size_t decrypted_key_size, encrypted_key_size;
- _cleanup_free_ void *encrypted_key = NULL;
+ size_t decrypted_key_size, saved_key_size;
+ _cleanup_free_ void *saved_key = NULL;
_cleanup_(X509_freep) X509 *cert = NULL;
ssize_t base64_encoded_size;
const char *node;
- EVP_PKEY *pkey;
int keyslot, r;
assert_se(cd);
if (r < 0)
return r;
- pkey = X509_get0_pubkey(cert);
- if (!pkey)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
-
- r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to determine RSA public key size.");
-
- log_debug("Generating %zu bytes random key.", decrypted_key_size);
-
- decrypted_key = malloc(decrypted_key_size);
- if (!decrypted_key)
- return log_oom();
-
- r = crypto_random_bytes(decrypted_key, decrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to generate random key: %m");
-
- r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
+ r = x509_generate_volume_keys(cert, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size);
if (r < 0)
- return log_error_errno(r, "Failed to encrypt key: %m");
+ return log_error_errno(r, "Failed to generate volume keys: %m");
/* Let's base64 encode the key to use, for compat with homed (and it's easier to type it in by
* keyboard, if that might ever end up being necessary.) */
JSON_BUILD_PAIR("type", JSON_BUILD_CONST_STRING("systemd-pkcs11")),
JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
JSON_BUILD_PAIR("pkcs11-uri", JSON_BUILD_STRING(uri)),
- JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size))));
+ JSON_BUILD_PAIR("pkcs11-key", JSON_BUILD_BASE64(saved_key, saved_key_size))));
if (r < 0)
return log_error_errno(r, "Failed to prepare PKCS#11 JSON token object: %m");
if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
pager_open(arg_pager_flags);
- if (arg_json_format_flags & JSON_FORMAT_OFF)
- printf(" Name: %s%s%s\n", ansi_highlight(), bn, ansi_normal());
+ if (arg_json_format_flags & JSON_FORMAT_OFF) {
+ printf(" Name: %s%s%s\n",
+ ansi_highlight(), bn, ansi_normal());
- if (ioctl(d->fd, BLKGETSIZE64, &size) < 0)
- log_debug_errno(errno, "Failed to query size of loopback device: %m");
- else if (arg_json_format_flags & JSON_FORMAT_OFF)
- printf(" Size: %s\n", FORMAT_BYTES(size));
+ printf(" Size: %s\n",
+ FORMAT_BYTES(m->image_size));
- if (arg_json_format_flags & JSON_FORMAT_OFF) {
- printf(" Sec. Size: %" PRIu32 "\n", m->sector_size);
+ printf(" Sec. Size: %" PRIu32 "\n",
+ m->sector_size);
printf(" Arch.: %s\n",
strna(architecture_to_string(dissected_image_architecture(m))));
return log_oom();
table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
- (void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100);
+ (void) table_set_align_percent(t, table_get_cell(t, 0, 9), 100);
+
+ /* Hide the device path if this is a loopback device that is not relinquished, since that means the
+ * device node is not going to be useful the instant our command exits */
+ if ((!d || d->created) && (arg_json_format_flags & JSON_FORMAT_OFF))
+ table_hide_column_from_display(t, 8);
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
DissectedPartition *p = m->partitions + i;
if (r < 0)
return bus_log_parse_error(r);
- r = bus_wait_for_jobs_one(w, object, false, NULL);
+ r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL);
if (r < 0)
return log_error_errno(r, "Failed to wait for systemd-vconsole-setup.service/restart: %m");
return 0;
if (r < 0)
return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
if (r > 0 && !enabled) {
- log_debug("Found systemd.firstboot=no kernel command line argument, terminating.");
- return 0; /* disabled */
+ log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
+ arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false;
}
}
else if (feof(f))
r = 0;
else
- r = log_warning_errno(SYNTHETIC_ERRNO(errno), "Failed to parse progress pipe data");
+ r = log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse progress pipe data.");
break;
}
__atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
})
+#define U64_KB UINT64_C(1024)
+#define U64_MB (UINT64_C(1024) * U64_KB)
+#define U64_GB (UINT64_C(1024) * U64_MB)
+
#undef MAX
#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b))
#define __MAX(aq, a, bq, b) \
return 0;
}
-static int process_resume(const char *device) {
+static int process_resume(const HibernateInfo *info) {
_cleanup_free_ char *device_unit = NULL;
int r;
- assert(device);
+ assert(info);
- r = unit_name_from_path(device, ".device", &device_unit);
+ r = unit_name_from_path(info->device, ".device", &device_unit);
if (r < 0)
- return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", device);
+ return log_error_errno(r, "Failed to generate device unit name from path '%s': %m", info->device);
- r = write_drop_in(arg_dest, device_unit, 40, "device-timeout",
- "# Automatically generated by systemd-hibernate-resume-generator\n\n"
- "[Unit]\n"
- "JobTimeoutSec=infinity\n");
+ /* If hibernate info is acquired from EFI variable, don't wait forever by default. Otherwise, if
+ * swap device is not present and HibernateLocation was not correctly cleared, we end up blocking
+ * the boot process infinitely. */
+ r = write_drop_in_format(arg_dest, device_unit, 40, "device-timeout",
+ "# Automatically generated by systemd-hibernate-resume-generator\n\n"
+ "[Unit]\n"
+ "JobTimeoutSec=%s\n",
+ info->cmdline ? "infinity" : "2min");
if (r < 0)
log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
r = generator_write_timeouts(arg_dest,
- device,
- device,
+ info->device,
+ info->device,
arg_resume_options ?: arg_root_options,
NULL);
if (r < 0)
if (r < 0)
return r;
- return process_resume(info.device);
+ return process_resume(&info);
}
DEFINE_MAIN_GENERATOR_FUNCTION(run);
#include "memory-util.h"
#include "openssl-util.h"
#include "pkcs11-util.h"
-#include "random-util.h"
#include "strv.h"
static int add_pkcs11_encrypted_key(
}
int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) {
- _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL;
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL, *saved_key = NULL;
_cleanup_(erase_and_freep) char *pin = NULL;
- size_t decrypted_key_size, encrypted_key_size;
+ size_t decrypted_key_size, saved_key_size;
_cleanup_(X509_freep) X509 *cert = NULL;
- EVP_PKEY *pkey;
int r;
assert(v);
if (r < 0)
return r;
- pkey = X509_get0_pubkey(cert);
- if (!pkey)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
-
- r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to extract RSA key size from X509 certificate.");
-
- log_debug("Generating %zu bytes random key.", decrypted_key_size);
-
- decrypted_key = malloc(decrypted_key_size);
- if (!decrypted_key)
- return log_oom();
-
- r = crypto_random_bytes(decrypted_key, decrypted_key_size);
- if (r < 0)
- return log_error_errno(r, "Failed to generate random key: %m");
-
- r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
+ r = x509_generate_volume_keys(cert, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size);
if (r < 0)
- return log_error_errno(r, "Failed to encrypt key: %m");
+ return log_error_errno(r, "Failed to generate volume keys: %m");
/* Add the token URI to the public part of the record. */
r = add_pkcs11_token_uri(v, uri);
r = add_pkcs11_encrypted_key(
v,
uri,
- encrypted_key, encrypted_key_size,
+ saved_key, saved_key_size,
decrypted_key, decrypted_key_size);
if (r < 0)
return r;
#include "cap-list.h"
#include "capability-util.h"
#include "cgroup-util.h"
+#include "creds-util.h"
#include "dns-domain.h"
#include "env-util.h"
#include "fd-util.h"
#include "percent-util.h"
#include "pkcs11-util.h"
#include "pretty-print.h"
+#include "proc-cmdline.h"
#include "process-util.h"
+#include "recurse-dir.h"
#include "rlimit-util.h"
#include "spawn-polkit-agent.h"
#include "terminal-util.h"
#include "user-record-show.h"
#include "user-record-util.h"
#include "user-util.h"
+#include "userdb.h"
#include "verbs.h"
static PagerFlags arg_pager_flags = 0;
} arg_export_format = EXPORT_FORMAT_FULL;
static uint64_t arg_capability_bounding_set = UINT64_MAX;
static uint64_t arg_capability_ambient_set = UINT64_MAX;
+static bool arg_prompt_new_user = false;
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, json_variant_unrefp);
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, json_variant_unrefp);
return 1;
}
-static int acquire_new_home_record(UserRecord **ret) {
+static int acquire_new_home_record(JsonVariant *input, UserRecord **ret) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(user_record_unrefp) UserRecord *hr = NULL;
int r;
if (arg_identity) {
unsigned line, column;
+ if (input)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Two identity records specified, refusing.");
+
r = json_parse_file(
streq(arg_identity, "-") ? stdin : NULL,
streq(arg_identity, "-") ? "<stdin>" : arg_identity, JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
- }
+ } else
+ v = json_variant_ref(input);
r = apply_identity_changes(&v);
if (r < 0)
if (!hr)
return log_oom();
- r = user_record_load(hr, v, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG|USER_RECORD_PERMISSIVE);
+ r = user_record_load(
+ hr,
+ v,
+ USER_RECORD_REQUIRE_REGULAR|
+ USER_RECORD_ALLOW_SECRET|
+ USER_RECORD_ALLOW_PRIVILEGED|
+ USER_RECORD_ALLOW_PER_MACHINE|
+ USER_RECORD_STRIP_BINDING|
+ USER_RECORD_STRIP_STATUS|
+ USER_RECORD_STRIP_SIGNATURE|
+ USER_RECORD_LOG|
+ USER_RECORD_PERMISSIVE);
if (r < 0)
return r;
}
}
-static int create_home(int argc, char *argv[], void *userdata) {
+static int create_home_common(JsonVariant *input) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(user_record_unrefp) UserRecord *hr = NULL;
int r;
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
- if (argc >= 2) {
- /* If a username was specified, use it */
-
- if (valid_user_group_name(argv[1], 0))
- r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
- else {
- _cleanup_free_ char *un = NULL, *rr = NULL;
-
- /* Before we consider the user name invalid, let's check if we can split it? */
- r = split_user_name_realm(argv[1], &un, &rr);
- if (r < 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]);
-
- if (rr) {
- r = json_variant_set_field_string(&arg_identity_extra, "realm", rr);
- if (r < 0)
- return log_error_errno(r, "Failed to set realm field: %m");
- }
-
- r = json_variant_set_field_string(&arg_identity_extra, "userName", un);
- }
- if (r < 0)
- return log_error_errno(r, "Failed to set userName field: %m");
- } else {
- /* If neither a username nor an identity have been specified we cannot operate. */
- if (!arg_identity)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required.");
- }
-
- r = acquire_new_home_record(&hr);
+ r = acquire_new_home_record(input, &hr);
if (r < 0)
return r;
return 0;
}
+static int create_home(int argc, char *argv[], void *userdata) {
+ int r;
+
+ if (argc >= 2) {
+ /* If a username was specified, use it */
+
+ if (valid_user_group_name(argv[1], 0))
+ r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]);
+ else {
+ _cleanup_free_ char *un = NULL, *rr = NULL;
+
+ /* Before we consider the user name invalid, let's check if we can split it? */
+ r = split_user_name_realm(argv[1], &un, &rr);
+ if (r < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]);
+
+ if (rr) {
+ r = json_variant_set_field_string(&arg_identity_extra, "realm", rr);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set realm field: %m");
+ }
+
+ r = json_variant_set_field_string(&arg_identity_extra, "userName", un);
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to set userName field: %m");
+ } else {
+ /* If neither a username nor an identity have been specified we cannot operate. */
+ if (!arg_identity)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required.");
+ }
+
+ return create_home_common(/* input= */ NULL);
+}
+
static int remove_home(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r, ret = 0;
return 0;
}
+static int create_from_credentials(void) {
+ _cleanup_close_ int fd = -EBADF;
+ int ret = 0, n_created = 0, r;
+
+ fd = open_credentials_dir();
+ if (IN_SET(fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */
+ return 0;
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to open credentials directory: %m");
+
+ _cleanup_free_ DirectoryEntries *des = NULL;
+ r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate credentials: %m");
+
+ FOREACH_ARRAY(i, des->entries, des->n_entries) {
+ _cleanup_(json_variant_unrefp) JsonVariant *identity = NULL;
+ struct dirent *de = *i;
+ const char *e;
+
+ if (de->d_type != DT_REG)
+ continue;
+
+ e = startswith(de->d_name, "home.create.");
+ if (!e)
+ continue;
+
+ if (!valid_user_group_name(e, 0)) {
+ log_notice("Skipping over credential with name that is not a suitable user name: %s", de->d_name);
+ continue;
+ }
+
+ r = json_parse_file_at(
+ /* f= */ NULL,
+ fd,
+ de->d_name,
+ /* flags= */ 0,
+ &identity,
+ /* ret_line= */ NULL,
+ /* ret_column= */ NULL);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse user record in credential '%s', ignoring: %m", de->d_name);
+ continue;
+ }
+
+ JsonVariant *un;
+ un = json_variant_by_key(identity, "userName");
+ if (un) {
+ if (!json_variant_is_string(un)) {
+ log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de->d_name);
+ continue;
+ }
+
+ if (!streq(json_variant_string(un), e)) {
+ log_warning("User record from credential '%s' contains 'userName' field (%s) that doesn't match credential name (%s), ignoring.", de->d_name, json_variant_string(un), e);
+ continue;
+ }
+ } else {
+ r = json_variant_set_field_string(&identity, "userName", e);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to set userName field: %m");
+ }
+
+ log_notice("Processing user '%s' from credentials.", e);
+
+ r = create_home_common(identity);
+ if (r >= 0)
+ n_created++;
+
+ RET_GATHER(ret, r);
+ }
+
+ return ret < 0 ? ret : n_created;
+}
+
+static int has_regular_user(void) {
+ _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
+ int r;
+
+ r = userdb_all(USERDB_SUPPRESS_SHADOW, &iterator);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create user enumerator: %m");
+
+ for (;;) {
+ _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
+
+ r = userdb_iterator_get(iterator, &ur);
+ if (r == -ESRCH)
+ break;
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate users: %m");
+
+ if (user_record_disposition(ur) == USER_REGULAR)
+ return true;
+ }
+
+ return false;
+}
+
+static int create_interactively(void) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_free_ char *username = NULL;
+ int r;
+
+ if (!arg_prompt_new_user) {
+ log_debug("Prompting for user creation was not requested.");
+ return 0;
+ }
+
+ r = acquire_bus(&bus);
+ if (r < 0)
+ return r;
+
+ (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
+
+ (void) reset_terminal_fd(STDIN_FILENO, /* switch_to_text= */ false);
+
+ for (;;) {
+ username = mfree(username);
+
+ r = ask_string(&username,
+ "%s Please enter user name to create (empty to skip): ",
+ special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
+ if (r < 0)
+ return log_error_errno(r, "Failed to query user for username: %m");
+
+ if (isempty(username)) {
+ log_info("No data entered, skipping.");
+ return 0;
+ }
+
+ if (!valid_user_group_name(username, /* flags= */ 0)) {
+ log_notice("Specified user name is not a valid UNIX user name, try again: %s", username);
+ continue;
+ }
+
+ r = userdb_by_name(username, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL);
+ if (r == -ESRCH)
+ break;
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if specified user '%s' already exists: %m", username);
+
+ log_notice("Specified user '%s' exists already, try again.", username);
+ }
+
+ r = json_variant_set_field_string(&arg_identity_extra, "userName", username);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set userName field: %m");
+
+ return create_home_common(/* input= */ NULL);
+}
+
+static int verb_firstboot(int argc, char *argv[], void *userdata) {
+ int r;
+
+ /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot
+ * tool. */
+
+ bool enabled;
+ r = proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
+ if (r > 0 && !enabled) {
+ log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts.");
+ arg_prompt_new_user = false;
+ }
+
+ r = create_from_credentials();
+ if (r < 0)
+ return r;
+ if (r > 0) /* Already created users from credentials */
+ return 0;
+
+ r = has_regular_user();
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ log_info("Regular user already present in user database, skipping user creation.");
+ return 0;
+ }
+
+ return create_interactively();
+}
+
static int drop_from_identity(const char *field) {
int r;
" deactivate-all Deactivate all active home areas\n"
" rebalance Rebalance free space between home areas\n"
" with USER [COMMAND…] Run shell or command with access to a home area\n"
+ " firstboot Run first-boot home area creation wizard\n"
"\n%4$sOptions:%5$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -E When specified once equals -j --export-format=\n"
" stripped, when specified twice equals\n"
" -j --export-format=minimal\n"
+ " --prompt-new-user firstboot: Query user interactively for user\n"
+ " to create\n"
"\n%4$sGeneral User Record Properties:%5$s\n"
" -c --real-name=REALNAME Real name for user\n"
" --realm=REALM Realm to create user in\n"
ARG_FIDO2_CRED_ALG,
ARG_CAPABILITY_BOUNDING_SET,
ARG_CAPABILITY_AMBIENT_SET,
+ ARG_PROMPT_NEW_USER,
};
static const struct option options[] = {
{ "rebalance-weight", required_argument, NULL, ARG_REBALANCE_WEIGHT },
{ "capability-bounding-set", required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET },
{ "capability-ambient-set", required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET },
+ { "prompt-new-user", no_argument, NULL, ARG_PROMPT_NEW_USER },
{}
};
break;
}
+ case ARG_PROMPT_NEW_USER:
+ arg_prompt_new_user = true;
+ break;
+
case '?':
return -EINVAL;
{ "lock-all", VERB_ANY, 1, 0, lock_all_homes },
{ "deactivate-all", VERB_ANY, 1, 0, deactivate_all_homes },
{ "rebalance", VERB_ANY, 1, 0, rebalance },
+ { "firstboot", VERB_ANY, 1, 0, verb_firstboot },
{}
};
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.remove-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.create-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.remove-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.authenticate-home",
- NULL,
- true,
+ /* details= */ NULL,
+ /* interactive= */ false,
h->uid,
&h->manager->polkit_registry,
error);
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.update-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.resize-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&h->manager->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.passwd-home",
- NULL,
- true,
+ /* details= */ NULL,
+ /* interactive= */ false,
h->uid,
&h->manager->polkit_registry,
error);
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.create-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.home1.create-home",
- NULL,
- true,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
if (!S_ISBLK(st.st_mode))
return -ENOTBLK;
- return RET_NERRNO(ioctl(fd, BLKGETSIZE64, ret));
+ return blockdev_get_device_size(fd, ret);
}
static int block_get_size_by_path(const char *path, uint64_t *ret) {
if (!IN_SET(errno, ENOTTY, EINVAL))
return log_error_errno(errno, "Failed to get block device metrics of %s: %m", n);
- if (ioctl(setup->loop->fd, BLKGETSIZE64, &size) < 0)
- return log_error_errno(r, "Failed to read block device size of %s: %m", n);
-
if (fstat(setup->loop->fd, &st) < 0)
return log_error_errno(r, "Failed to stat block device %s: %m", n);
assert(S_ISBLK(st.st_mode));
offset *= 512U;
}
+
+ size = setup->loop->device_size;
} else {
#if HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
if (flock(setup->image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
- if (ioctl(setup->image_fd, BLKGETSIZE64, &block_device_size) < 0)
- return log_error_errno(errno, "Failed to read block device size: %m");
+ r = blockdev_get_device_size(setup->image_fd, &block_device_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read block device size: %m");
if (h->disk_size == UINT64_MAX) {
} else
log_info("Operating on whole block device %s.", ip);
- if (ioctl(image_fd, BLKGETSIZE64, &old_image_size) < 0)
- return log_error_errno(errno, "Failed to determine size of original block device: %m");
+ r = blockdev_get_device_size(image_fd, &old_image_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine size of original block device: %m");
if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
context_read_etc_hostname(c);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.set-hostname",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (name && !hostname_is_valid(name, 0))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.set-static-hostname",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid location '%s'", name);
}
- /* Since the pretty hostname should always be changed at the
- * same time as the static one, use the same policy action for
- * both... */
+ /* Since the pretty hostname should always be changed at the same time as the static one, use the
+ * same policy action for both... */
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.get-product-uuid",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.get-hardware-serial",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&c->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.hostname1.get-description",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&c->polkit_registry,
error);
if (r == 0)
}
static int verb_update(int argc, char *argv[], void *userdata) {
+ if (hwdb_bypass())
+ return 0;
+
return hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, false);
}
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.import",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.import",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.export",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&t->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
msg,
- CAP_SYS_ADMIN,
"org.freedesktop.import1.pull",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--since= must be before --until=.");
- if (!!arg_cursor + !!arg_after_cursor + !!arg_since_set > 1)
+ if (!!arg_cursor + !!arg_after_cursor + !!arg_cursor_file + !!arg_since_set > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Please specify only one of --since=, --cursor=, and --after-cursor=.");
+ "Please specify only one of --since=, --cursor=, --cursor-file=, and --after-cursor=.");
if (arg_follow && arg_reverse)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
client_context_flush_regular(s);
- assert(prioq_size(s->client_contexts_lru) == 0);
- assert(hashmap_size(s->client_contexts) == 0);
+ assert(prioq_isempty(s->client_contexts_lru));
+ assert(hashmap_isempty(s->client_contexts));
s->client_contexts_lru = prioq_free(s->client_contexts_lru);
s->client_contexts = hashmap_free(s->client_contexts);
/* Try to restore streams, but don't bother if this fails */
(void) server_restore_streams(s, fds);
- if (fdset_size(fds) > 0) {
+ if (!fdset_isempty(fds)) {
log_warning("%u unknown file descriptors passed, closing.", fdset_size(fds));
fds = fdset_free(fds);
}
import argparse
import os
-import runpy
import shlex
+import types
from shutil import which
from pathlib import Path
from typing import Optional
return [*microcode, *opts.initrd, *initrd]
-def call_ukify(opts):
- # Punish me harder.
- # We want this:
- # ukify = importlib.machinery.SourceFileLoader('ukify', UKIFY).load_module()
- # but it throws a DeprecationWarning.
- # https://stackoverflow.com/questions/67631/how-can-i-import-a-module-dynamically-given-the-full-path
- # https://github.com/python/cpython/issues/65635
- # offer "explanations", but to actually load a python file without a .py extension,
- # the "solution" is 4+ incomprehensible lines.
- # The solution with runpy gives a dictionary, which isn't great, but will do.
- ukify = runpy.run_path(UKIFY, run_name='ukify')
+def load_module(name: str, path: str) -> types.ModuleType:
+ module = types.ModuleType(name)
+ text = open(path).read()
+ exec(compile(text, path, 'exec'), module.__dict__)
+ return module
+
+
+def call_ukify(opts) -> None:
+ ukify = load_module('ukify', UKIFY)
# Create "empty" namespace. We want to override just a few settings, so it
# doesn't make sense to configure everything. We pretend to parse an empty
# argument set to prepopulate the namespace with the defaults.
- opts2 = ukify['create_parser']().parse_args(['build'])
+ opts2 = ukify.create_parser().parse_args(['build'])
opts2.config = uki_conf_location()
opts2.uname = opts.kernel_version
if BOOT_STUB:
opts2.stub = BOOT_STUB
- # opts2.summary = True
-
- ukify['apply_config'](opts2)
- ukify['finalize_options'](opts2)
- ukify['check_inputs'](opts2)
- ukify['make_uki'](opts2)
+ ukify.apply_config(opts2)
+ ukify.finalize_options(opts2)
+ ukify.check_inputs(opts2)
+ ukify.make_uki(opts2)
log(f'{opts2.output} has been created')
return;
/* still referenced? */
- if (hashmap_size(track->names) > 0)
+ if (!hashmap_isempty(track->names))
return;
/* Nothing to call? */
test_setup_logging(LOG_DEBUG);
- if (cg_unified() == -ENOMEDIUM)
+ if (IN_SET(cg_unified(), -ENOMEDIUM, -ENOENT))
return log_tests_skipped("/sys/fs/cgroup/ not available");
r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL);
#include "device-private.h"
#include "device-util.h"
#include "macro.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
if (getuid() != 0)
return log_tests_skipped("not root");
+ if (path_is_mount_point("/sys", NULL, 0) <= 0)
+ return log_tests_skipped("/sys is not mounted");
+
if (path_is_read_only_fs("/sys") > 0)
return log_tests_skipped("Running in container");
#include "sd-device.h"
#include "device-util.h"
+#include "tests.h"
#define handle_error_errno(error, msg) \
({ \
int r;
r = sd_device_new_from_syspath(&loopback, "/sys/class/net/lo");
+ if (r == -ENODEV)
+ return log_tests_skipped("Loopback device not found");
if (r < 0)
return handle_error_errno(r, "Failed to create loopback device object");
#include "errno-util.h"
#include "fd-util.h"
#include "hashmap.h"
+#include "mountpoint-util.h"
#include "nulstr-util.h"
#include "path-util.h"
#include "rm-rf.h"
}
}
-DEFINE_TEST_MAIN(LOG_INFO);
+static int intro(void) {
+ if (path_is_mount_point("/sys", NULL, 0) <= 0)
+ return log_tests_skipped("/sys is not mounted");
+
+ return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
return log_error_errno(r, "Failed to import file '%s': %m", *f);
}
- if (ordered_hashmap_size(h) <= 0) {
+ if (ordered_hashmap_isempty(h)) {
log_info("No items in catalog.");
return 0;
- } else
- log_debug("Found %u items in catalog.", ordered_hashmap_size(h));
+ }
+
+ log_debug("Found %u items in catalog.", ordered_hashmap_size(h));
items = new(CatalogItem, ordered_hashmap_size(h));
if (!items)
#define DEFAULT_COMPRESS_THRESHOLD (512ULL)
#define MIN_COMPRESS_THRESHOLD (8ULL)
-#define U64_KB UINT64_C(1024)
-#define U64_MB (UINT64_C(1024) * U64_KB)
-#define U64_GB (UINT64_C(1024) * U64_MB)
-
/* This is the minimum journal file size */
#define JOURNAL_FILE_SIZE_MIN (512 * U64_KB) /* 512 KiB */
#define JOURNAL_COMPACT_SIZE_MAX ((uint64_t) UINT32_MAX) /* 4 GiB */
if (r < 0)
return r;
- for (unsigned i = 0; i < n_files; i++) {
- JournalFile *f = (JournalFile *)files[i];
+ FOREACH_ARRAY(_f, files, n_files) {
+ JournalFile *f = (JournalFile*) *_f;
bool found;
r = next_beyond_location(j, f, direction);
#include "format-util.h"
#include "log.h"
#include "missing_syscall.h"
+#include "mountpoint-util.h"
#include "process-util.h"
#include "string-util.h"
#include "strv.h"
}
static int intro(void) {
+ if (IN_SET(cg_unified(), -ENOENT, -ENOMEDIUM))
+ return log_tests_skipped("cgroupfs is not mounted");
+
log_info("/* Information printed is from the live system */");
return EXIT_SUCCESS;
}
return r;
if (nlmsg_type == RTM_NEWNEXTHOP)
- (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
+ (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
nhm = NLMSG_DATA((*ret)->hdr);
assert(nl);
assert(ret);
- if (ordered_set_size(nl->rqueue) <= 0) {
+ if (ordered_set_isempty(nl->rqueue)) {
/* Try to read a new message */
r = socket_read_message(nl);
if (r == -ENOBUFS) /* FIXME: ignore buffer overruns for now */
assert_return(nl, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- if (ordered_set_size(nl->rqueue) > 0)
+ if (!ordered_set_isempty(nl->rqueue))
return 0;
r = netlink_poll(nl, false, timeout_usec);
assert_return(nl, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- return ordered_set_size(nl->rqueue) == 0 ? POLLIN : 0;
+ return ordered_set_isempty(nl->rqueue) ? POLLIN : 0;
}
int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
assert_return(timeout_usec, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- if (ordered_set_size(nl->rqueue) > 0) {
+ if (!ordered_set_isempty(nl->rqueue)) {
*timeout_usec = 0;
return 1;
}
#include <stdlib.h>
#include "libudev.h"
+#include "tests.h"
#define handle_error_errno(error, msg) \
({ \
int r;
loopback = udev_device_new_from_syspath(NULL, "/sys/class/net/lo");
- if (!loopback)
+ if (!loopback) {
+ if (errno == ENODEV)
+ return log_tests_skipped_errno(errno, "Loopback device not found");
+
return handle_error_errno(errno, "Failed to create loopback device object");
+ }
entry = udev_device_get_properties_list_entry(loopback);
udev_list_entry_foreach(e, entry)
return sd_bus_reply_method_return(m, NULL);
}
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.locale1.set-locale",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (vc_context_equal(&c->vc, &in) && !x_needs_update)
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.locale1.set-keyboard",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (x11_context_equal(&c->x11_from_vc, &in) && x11_context_equal(&c->x11_from_xorg, &in) && !convert)
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_ADMIN,
"org.freedesktop.locale1.set-keyboard",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
static int return_test_polkit(
sd_bus_message *message,
- int capability,
const char *action,
const char **details,
uid_t good_user,
bool challenge;
int r;
- r = bus_test_polkit(message, capability, action, details, good_user, &challenge, e);
+ r = bus_test_polkit(message, action, details, good_user, &challenge, e);
if (r < 0)
return r;
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.lock-sessions",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
if (!pw)
return errno_or_else(ENOENT);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
uid == auth_uid ? "org.freedesktop.login1.set-self-linger" :
"org.freedesktop.login1.set-user-linger",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
} else if (!seat_name_is_valid(seat)) /* Note that a seat does not have to exist yet for this operation to succeed */
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat name %s is not valid", seat);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.attach-device",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.flush-devices",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
interactive = flags & SD_LOGIND_INTERACTIVE;
if (multiple_sessions) {
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_BOOT,
a->polkit_action_multiple_sessions,
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED,
"Access denied to root due to active block inhibitor");
- r = bus_verify_polkit_async(message,
- CAP_SYS_BOOT,
+ r = bus_verify_polkit_async_full(
+ message,
a->polkit_action_ignore_inhibit,
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
}
if (!multiple_sessions && !blocked) {
- r = bus_verify_polkit_async(message,
- CAP_SYS_BOOT,
+ r = bus_verify_polkit_async_full(
+ message,
a->polkit_action,
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_BOOT,
a->polkit_action,
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
}
if (multiple_sessions) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(
+ message,
+ a->polkit_action_multiple_sessions,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ &challenge,
+ error);
if (r < 0)
return r;
}
if (blocked) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(
+ message,
+ a->polkit_action_ignore_inhibit,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ &challenge,
+ error);
if (r < 0)
return r;
/* If neither inhibit nor multiple sessions
* apply then just check the normal policy */
- r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(
+ message,
+ a->polkit_action,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
+ &challenge,
+ error);
if (r < 0)
return r;
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"Reboot parameter not supported in containers, refusing.");
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-reboot-parameter",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-reboot-parameter",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return return_test_polkit(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-parameter",
- NULL,
- UID_INVALID,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
error);
}
/* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to on */
use_efi = false;
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-reboot-to-firmware-setup",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-reboot-to-firmware-setup",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return return_test_polkit(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-firmware-setup",
- NULL,
- UID_INVALID,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
error);
}
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to on */
use_efi = false;
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-reboot-to-boot-loader-menu",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-reboot-to-boot-loader-menu",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return return_test_polkit(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-boot-loader-menu",
- NULL,
- UID_INVALID,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
error);
}
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to on */
use_efi = false;
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-reboot-to-boot-loader-entry",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-reboot-to-boot-loader-entry",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return return_test_polkit(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.set-reboot-to-boot-loader-entry",
- NULL,
- UID_INVALID,
+ /* details= */ NULL,
+ /* good_user= */ UID_INVALID,
error);
}
m->enable_wall_messages == enable_wall_messages)
goto done;
- r = bus_verify_polkit_async(message,
- CAP_SYS_ADMIN,
- "org.freedesktop.login1.set-wall-message",
- NULL,
- false,
- UID_INVALID,
- &m->polkit_registry,
- error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.login1.set-wall-message",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_BOOT,
w == INHIBIT_SHUTDOWN ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-shutdown" : "org.freedesktop.login1.inhibit-delay-shutdown") :
w == INHIBIT_SLEEP ? (mm == INHIBIT_BLOCK ? "org.freedesktop.login1.inhibit-block-sleep" : "org.freedesktop.login1.inhibit-delay-sleep") :
w == INHIBIT_IDLE ? "org.freedesktop.login1.inhibit-block-idle" :
w == INHIBIT_HANDLE_REBOOT_KEY ? "org.freedesktop.login1.inhibit-handle-reboot-key" :
w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" :
"org.freedesktop.login1.inhibit-handle-lid-switch",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
#if ENABLE_POLKIT
return bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.chvt",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&manager->polkit_registry,
error);
#else
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&s->manager->polkit_registry,
error);
if (r < 0)
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
s->user->user_record->uid,
&s->manager->polkit_registry,
error);
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.lock-sessions",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
s->user->user_record->uid,
&s->manager->polkit_registry,
error);
if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
s->user->user_record->uid,
&s->manager->polkit_registry,
error);
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
u->user_record->uid,
&u->manager->polkit_registry,
error);
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
+ /* interactive= */ false,
u->user_record->uid,
&u->manager->polkit_registry,
error);
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-images",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_KILL,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->manager->polkit_registry,
error);
if (r < 0)
#include "string-table.h"
#include "terminal-util.h"
#include "tmpfile-util.h"
+#include "uid-range.h"
#include "unit-name.h"
#include "user-util.h"
uid_t uid_base, uid_shift, uid_range;
gid_t gid_base, gid_shift, gid_range;
_cleanup_fclose_ FILE *f = NULL;
- int k, r;
+ int r;
assert(m);
assert(ret);
}
/* Read the first line. There's at least one. */
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EBADMSG;
- }
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r < 0)
+ return r;
/* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
if (uid_base != 0)
/* Read the first line. There's at least one. */
errno = 0;
- k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
+ r = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
+ if (r == EOF)
+ return errno_or_else(ENOMSG);
+ assert(r >= 0);
+ if (r != 3)
return -EBADMSG;
- }
/* If there's more than one line, then we don't support this file. */
r = safe_fgetc(f, NULL);
_cleanup_fclose_ FILE *f = NULL;
const char *p;
+ int r;
/* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
assert_cc(sizeof(uid_t) == sizeof(gid_t));
for (;;) {
uid_t uid_base, uid_shift, uid_range, converted;
- int k;
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
- if (k < 0 && feof(f))
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r == -ENOMSG)
break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
+ if (r < 0)
+ return r;
/* The private user namespace is disabled, ignoring. */
if (uid_shift == 0)
_cleanup_fclose_ FILE *f = NULL;
const char *p;
+ int r;
/* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
assert_cc(sizeof(uid_t) == sizeof(gid_t));
for (;;) {
uid_t uid_base, uid_shift, uid_range, converted;
- int k;
- errno = 0;
- k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
- if (k < 0 && feof(f))
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r == -ENOMSG)
break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
+ if (r < 0)
+ return r;
if (uid < uid_base || uid >= uid_base + uid_range)
continue;
if (ret_host_uid)
*ret_host_uid = converted;
+
return 0;
}
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.machine1.manage-machines",
details,
- false,
- UID_INVALID,
&m->polkit_registry,
error);
if (r < 0)
if (r < 0)
return bus_log_parse_error(r);
- r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL);
+ r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, NULL);
if (r < 0)
return r;
}
if (r < 0)
return bus_log_parse_error(r);
- r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL);
+ r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, NULL);
if (r < 0)
return r;
}
if (r < 0)
return bus_log_parse_error(r);
- r = bus_wait_for_jobs_one(w, object, arg_quiet, NULL);
+ r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR, NULL);
if (r < 0)
return r;
}
'wait-online/wait-online.c',
)
-networkctl_sources = files('networkctl.c')
+networkctl_sources = files(
+ 'networkctl.c',
+ 'networkctl-config-file.c'
+)
network_generator_sources = files(
'generator/main.c',
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <unistd.h>
+
+#include "sd-daemon.h"
+#include "sd-device.h"
+#include "sd-netlink.h"
+#include "sd-network.h"
+
+#include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-util.h"
+#include "bus-wait-for-jobs.h"
+#include "conf-files.h"
+#include "edit-util.h"
+#include "mkdir-label.h"
+#include "netlink-util.h"
+#include "networkctl.h"
+#include "networkctl-config-file.h"
+#include "pager.h"
+#include "path-lookup.h"
+#include "path-util.h"
+#include "pretty-print.h"
+#include "selinux-util.h"
+#include "strv.h"
+#include "virt.h"
+
+typedef enum ReloadFlags {
+ RELOAD_NETWORKD = 1 << 0,
+ RELOAD_UDEVD = 1 << 1,
+} ReloadFlags;
+
+static int get_config_files_by_name(
+ const char *name,
+ bool allow_masked,
+ char **ret_path,
+ char ***ret_dropins) {
+
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(name);
+ assert(ret_path);
+
+ STRV_FOREACH(i, NETWORK_DIRS) {
+ _cleanup_free_ char *p = NULL;
+
+ p = path_join(*i, name);
+ if (!p)
+ return -ENOMEM;
+
+ r = RET_NERRNO(access(p, F_OK));
+ if (r >= 0) {
+ if (!allow_masked) {
+ r = null_or_empty_path(p);
+ if (r < 0)
+ return log_debug_errno(r,
+ "Failed to check if network config '%s' is masked: %m",
+ name);
+ if (r > 0)
+ return -ERFKILL;
+ }
+
+ path = TAKE_PTR(p);
+ break;
+ }
+
+ if (r != -ENOENT)
+ log_debug_errno(r, "Failed to determine whether '%s' exists, ignoring: %m", p);
+ }
+
+ if (!path)
+ return -ENOENT;
+
+ if (ret_dropins) {
+ _cleanup_free_ char *dropin_dirname = NULL;
+
+ dropin_dirname = strjoin(name, ".d");
+ if (!dropin_dirname)
+ return -ENOMEM;
+
+ r = conf_files_list_dropins(ret_dropins, dropin_dirname, /* root = */ NULL, NETWORK_DIRS);
+ if (r < 0)
+ return r;
+ }
+
+ *ret_path = TAKE_PTR(path);
+
+ return 0;
+}
+
+static int get_dropin_by_name(
+ const char *name,
+ char * const *dropins,
+ char **ret) {
+
+ assert(name);
+ assert(ret);
+
+ STRV_FOREACH(i, dropins)
+ if (path_equal_filename(*i, name)) {
+ _cleanup_free_ char *d = NULL;
+
+ d = strdup(*i);
+ if (!d)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(d);
+ return 1;
+ }
+
+ *ret = NULL;
+ return 0;
+}
+
+static int get_network_files_by_link(
+ sd_netlink **rtnl,
+ const char *link,
+ char **ret_path,
+ char ***ret_dropins) {
+
+ _cleanup_strv_free_ char **dropins = NULL;
+ _cleanup_free_ char *path = NULL;
+ int r, ifindex;
+
+ assert(rtnl);
+ assert(link);
+ assert(ret_path);
+ assert(ret_dropins);
+
+ ifindex = rtnl_resolve_interface_or_warn(rtnl, link);
+ if (ifindex < 0)
+ return ifindex;
+
+ r = sd_network_link_get_network_file(ifindex, &path);
+ if (r == -ENODATA)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Link '%s' has no associated network file.", link);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get network file for link '%s': %m", link);
+
+ r = sd_network_link_get_network_file_dropins(ifindex, &dropins);
+ if (r < 0 && r != -ENODATA)
+ return log_error_errno(r, "Failed to get network drop-ins for link '%s': %m", link);
+
+ *ret_path = TAKE_PTR(path);
+ *ret_dropins = TAKE_PTR(dropins);
+
+ return 0;
+}
+
+static int get_link_files_by_link(const char *link, char **ret_path, char ***ret_dropins) {
+ _cleanup_(sd_device_unrefp) sd_device *device = NULL;
+ _cleanup_strv_free_ char **dropins_split = NULL;
+ _cleanup_free_ char *p = NULL;
+ const char *path, *dropins;
+ int r;
+
+ assert(link);
+ assert(ret_path);
+ assert(ret_dropins);
+
+ r = sd_device_new_from_ifname(&device, link);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create sd-device object for link '%s': %m", link);
+
+ r = sd_device_get_property_value(device, "ID_NET_LINK_FILE", &path);
+ if (r == -ENOENT)
+ return log_error_errno(r, "Link '%s' has no associated link file.", link);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get link file for link '%s': %m", link);
+
+ r = sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &dropins);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to get link drop-ins for link '%s': %m", link);
+ if (r >= 0) {
+ r = strv_split_full(&dropins_split, dropins, ":", EXTRACT_CUNESCAPE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse link drop-ins for link '%s': %m", link);
+ }
+
+ p = strdup(path);
+ if (!p)
+ return log_oom();
+
+ *ret_path = TAKE_PTR(p);
+ *ret_dropins = TAKE_PTR(dropins_split);
+
+ return 0;
+}
+
+static int get_config_files_by_link_config(
+ const char *link_config,
+ sd_netlink **rtnl,
+ char **ret_path,
+ char ***ret_dropins,
+ ReloadFlags *ret_reload) {
+
+ _cleanup_strv_free_ char **dropins = NULL, **link_config_split = NULL;
+ _cleanup_free_ char *path = NULL;
+ const char *ifname, *type;
+ ReloadFlags reload;
+ size_t n;
+ int r;
+
+ assert(link_config);
+ assert(rtnl);
+ assert(ret_path);
+ assert(ret_dropins);
+
+ link_config_split = strv_split(link_config, ":");
+ if (!link_config_split)
+ return log_oom();
+
+ n = strv_length(link_config_split);
+ if (n == 0 || isempty(link_config_split[0]))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No link name is given.");
+ if (n > 2)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid link config '%s'.", link_config);
+
+ ifname = link_config_split[0];
+ type = n == 2 ? link_config_split[1] : "network";
+
+ if (streq(type, "network")) {
+ if (!networkd_is_running())
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
+ "Cannot get network file for link if systemd-networkd is not running.");
+
+ r = get_network_files_by_link(rtnl, ifname, &path, &dropins);
+ if (r < 0)
+ return r;
+
+ reload = RELOAD_NETWORKD;
+ } else if (streq(type, "link")) {
+ r = get_link_files_by_link(ifname, &path, &dropins);
+ if (r < 0)
+ return r;
+
+ reload = RELOAD_UDEVD;
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid config type '%s' for link '%s'.", type, ifname);
+
+ *ret_path = TAKE_PTR(path);
+ *ret_dropins = TAKE_PTR(dropins);
+
+ if (ret_reload)
+ *ret_reload = reload;
+
+ return 0;
+}
+
+static int add_config_to_edit(
+ EditFileContext *context,
+ const char *path,
+ char * const *dropins) {
+
+ _cleanup_free_ char *new_path = NULL, *dropin_path = NULL, *old_dropin = NULL;
+ _cleanup_strv_free_ char **comment_paths = NULL;
+ int r;
+
+ assert(context);
+ assert(path);
+
+ /* If we're supposed to edit main config file in /run/, but a config with the same name is present
+ * under /etc/, we bail out since the one in /etc/ always overrides that in /run/. */
+ if (arg_runtime && !arg_drop_in && path_startswith(path, "/etc"))
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Cannot edit runtime config file: overridden by %s", path);
+
+ if (path_startswith(path, "/usr") || arg_runtime != !!path_startswith(path, "/run")) {
+ _cleanup_free_ char *name = NULL;
+
+ r = path_extract_filename(path, &name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract filename from '%s': %m", path);
+
+ new_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], name);
+ if (!new_path)
+ return log_oom();
+ }
+
+ if (!arg_drop_in)
+ return edit_files_add(context, new_path ?: path, path, NULL);
+
+ bool need_new_dropin;
+
+ r = get_dropin_by_name(arg_drop_in, dropins, &old_dropin);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire drop-in '%s': %m", arg_drop_in);
+ if (r > 0) {
+ /* See the explanation above */
+ if (arg_runtime && path_startswith(old_dropin, "/etc"))
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Cannot edit runtime config file: overridden by %s", old_dropin);
+
+ need_new_dropin = path_startswith(old_dropin, "/usr") || arg_runtime != !!path_startswith(old_dropin, "/run");
+ } else
+ need_new_dropin = true;
+
+ if (!need_new_dropin)
+ /* An existing drop-in is found in the correct scope. Let's edit it directly. */
+ dropin_path = TAKE_PTR(old_dropin);
+ else {
+ /* No drop-in was found or an existing drop-in is in a different scope. Let's create a new
+ * drop-in file. */
+ dropin_path = strjoin(new_path ?: path, ".d/", arg_drop_in);
+ if (!dropin_path)
+ return log_oom();
+ }
+
+ comment_paths = strv_new(path);
+ if (!comment_paths)
+ return log_oom();
+
+ r = strv_extend_strv(&comment_paths, dropins, /* filter_duplicates = */ false);
+ if (r < 0)
+ return log_oom();
+
+ return edit_files_add(context, dropin_path, old_dropin, comment_paths);
+}
+
+static int udevd_reload(sd_bus *bus) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+ const char *job_path;
+ int r;
+
+ assert(bus);
+
+ r = bus_wait_for_jobs_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Could not watch jobs: %m");
+
+ r = bus_call_method(bus,
+ bus_systemd_mgr,
+ "ReloadUnit",
+ &error,
+ &reply,
+ "ss",
+ "systemd-udevd.service",
+ "replace");
+ if (r < 0)
+ return log_error_errno(r, "Failed to reload systemd-udevd: %s", bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "o", &job_path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = bus_wait_for_jobs_one(w, job_path, /* flags = */ 0, NULL);
+ if (r == -ENOEXEC) {
+ log_debug("systemd-udevd is not running, skipping reload.");
+ return 0;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to reload systemd-udevd: %m");
+
+ return 1;
+}
+
+static int reload_daemons(ReloadFlags flags) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ int r, ret = 1;
+
+ if (arg_no_reload)
+ return 0;
+
+ if (flags == 0)
+ return 0;
+
+ if (!sd_booted() || running_in_chroot() > 0) {
+ log_debug("System is not booted with systemd or is running in chroot, skipping reload.");
+ return 0;
+ }
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0)
+ return log_error_errno(r, "Failed to connect to system bus: %m");
+
+ if (FLAGS_SET(flags, RELOAD_UDEVD))
+ RET_GATHER(ret, udevd_reload(bus));
+
+ if (FLAGS_SET(flags, RELOAD_NETWORKD)) {
+ if (networkd_is_running()) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL);
+ if (r < 0)
+ RET_GATHER(ret, log_error_errno(r, "Failed to reload systemd-networkd: %s", bus_error_message(&error, r)));
+ } else
+ log_debug("systemd-networkd is not running, skipping reload.");
+ }
+
+ return ret;
+}
+
+int verb_edit(int argc, char *argv[], void *userdata) {
+ _cleanup_(edit_file_context_done) EditFileContext context = {
+ .marker_start = DROPIN_MARKER_START,
+ .marker_end = DROPIN_MARKER_END,
+ .remove_parent = !!arg_drop_in,
+ };
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ ReloadFlags reload = 0;
+ int r;
+
+ if (!on_tty())
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit network config files if not on a tty.");
+
+ r = mac_selinux_init();
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
+ _cleanup_strv_free_ char **dropins = NULL;
+ _cleanup_free_ char *path = NULL;
+ const char *link_config;
+
+ link_config = startswith(*name, "@");
+ if (link_config) {
+ ReloadFlags flags;
+
+ r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, &flags);
+ if (r < 0)
+ return r;
+
+ reload |= flags;
+
+ r = add_config_to_edit(&context, path, dropins);
+ if (r < 0)
+ return r;
+
+ continue;
+ }
+
+ if (ENDSWITH_SET(*name, ".network", ".netdev"))
+ reload |= RELOAD_NETWORKD;
+ else if (endswith(*name, ".link"))
+ reload |= RELOAD_UDEVD;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
+
+ r = get_config_files_by_name(*name, /* allow_masked = */ false, &path, &dropins);
+ if (r == -ERFKILL)
+ return log_error_errno(r, "Network config '%s' is masked.", *name);
+ if (r == -ENOENT) {
+ if (arg_drop_in)
+ return log_error_errno(r, "Cannot find network config '%s'.", *name);
+
+ log_debug("No existing network config '%s' found, creating a new file.", *name);
+
+ path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name);
+ if (!path)
+ return log_oom();
+
+ r = edit_files_add(&context, path, NULL, NULL);
+ if (r < 0)
+ return r;
+ continue;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+
+ r = add_config_to_edit(&context, path, dropins);
+ if (r < 0)
+ return r;
+ }
+
+ r = do_edit_files_and_install(&context);
+ if (r < 0)
+ return r;
+
+ return reload_daemons(reload);
+}
+
+int verb_cat(int argc, char *argv[], void *userdata) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ int r, ret = 0;
+
+ pager_open(arg_pager_flags);
+
+ bool first = true;
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
+ _cleanup_strv_free_ char **dropins = NULL;
+ _cleanup_free_ char *path = NULL;
+ const char *link_config;
+
+ link_config = startswith(*name, "@");
+ if (link_config) {
+ r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, /* ret_reload = */ NULL);
+ if (r < 0)
+ return RET_GATHER(ret, r);
+ } else {
+ r = get_config_files_by_name(*name, /* allow_masked = */ false, &path, &dropins);
+ if (r == -ENOENT) {
+ RET_GATHER(ret, log_error_errno(r, "Cannot find network config file '%s'.", *name));
+ continue;
+ }
+ if (r == -ERFKILL) {
+ RET_GATHER(ret, log_debug_errno(r, "Network config '%s' is masked, ignoring.", *name));
+ continue;
+ }
+ if (r < 0) {
+ log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+ return RET_GATHER(ret, r);
+ }
+ }
+
+ if (!first)
+ putchar('\n');
+
+ r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
+ if (r < 0)
+ return RET_GATHER(ret, r);
+
+ first = false;
+ }
+
+ return ret;
+}
+
+int verb_mask(int argc, char *argv[], void *userdata) {
+ ReloadFlags flags = 0;
+ int r;
+
+ r = mac_selinux_init();
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
+ _cleanup_free_ char *config_path = NULL, *symlink_path = NULL;
+ ReloadFlags reload;
+
+ /* We update the real 'flags' at last, since the operation can be skipped. */
+ if (ENDSWITH_SET(*name, ".network", ".netdev"))
+ reload = RELOAD_NETWORKD;
+ else if (endswith(*name, ".link"))
+ reload = RELOAD_UDEVD;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
+
+ r = get_config_files_by_name(*name, /* allow_masked = */ true, &config_path, /* ret_dropins = */ NULL);
+ if (r == -ENOENT)
+ log_warning("No existing network config '%s' found, proceeding anyway.", *name);
+ else if (r < 0)
+ return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+ else if (!path_startswith(config_path, "/usr")) {
+ r = null_or_empty_path(config_path);
+ if (r < 0)
+ return log_error_errno(r,
+ "Failed to check if '%s' is masked: %m", config_path);
+ if (r > 0) {
+ log_debug("%s is already masked, skipping.", config_path);
+ continue;
+ }
+
+ /* At this point, we have found a config under mutable dir (/run/ or /etc/),
+ * so masking through /run/ (--runtime) is not possible. If it's under /etc/,
+ * then it doesn't work without --runtime either. */
+ if (arg_runtime || path_startswith(config_path, "/etc"))
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
+ "Cannot mask network config %s: %s exists",
+ *name, config_path);
+ }
+
+ symlink_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name);
+ if (!symlink_path)
+ return log_oom();
+
+ (void) mkdir_parents_label(symlink_path, 0755);
+
+ if (symlink("/dev/null", symlink_path) < 0)
+ return log_error_errno(errno,
+ "Failed to create symlink '%s' to /dev/null: %m", symlink_path);
+
+ flags |= reload;
+ log_info("Successfully created symlink '%s' to /dev/null.", symlink_path);
+ }
+
+ return reload_daemons(flags);
+}
+
+int verb_unmask(int argc, char *argv[], void *userdata) {
+ ReloadFlags flags = 0;
+ int r;
+
+ STRV_FOREACH(name, strv_skip(argv, 1)) {
+ _cleanup_free_ char *path = NULL;
+ ReloadFlags reload;
+
+ if (ENDSWITH_SET(*name, ".network", ".netdev"))
+ reload = RELOAD_NETWORKD;
+ else if (endswith(*name, ".link"))
+ reload = RELOAD_UDEVD;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
+
+ r = get_config_files_by_name(*name, /* allow_masked = */ true, &path, /* ret_dropins = */ NULL);
+ if (r == -ENOENT) {
+ log_debug_errno(r, "Network configuration '%s' doesn't exist, skipping.", *name);
+ continue;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
+
+ r = null_or_empty_path(path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if '%s' is masked: %m", path);
+ if (r == 0)
+ continue;
+
+ if (path_startswith(path, "/usr"))
+ return log_error_errno(r, "Cannot unmask network config under /usr/: %s", path);
+
+ if (unlink(path) < 0) {
+ if (errno == ENOENT)
+ continue;
+
+ return log_error_errno(errno, "Failed to remove '%s': %m", path);
+ }
+
+ flags |= reload;
+ log_info("Successfully removed masked network config '%s'.", path);
+ }
+
+ return reload_daemons(flags);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int verb_edit(int argc, char *argv[], void *userdata);
+int verb_cat(int argc, char *argv[], void *userdata);
+
+int verb_mask(int argc, char *argv[], void *userdata);
+int verb_unmask(int argc, char *argv[], void *userdata);
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-locator.h"
-#include "bus-wait-for-jobs.h"
-#include "conf-files.h"
#include "device-util.h"
-#include "edit-util.h"
#include "escape.h"
#include "ether-addr-util.h"
#include "ethtool-util.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "network-util.h"
+#include "networkctl.h"
+#include "networkctl-config-file.h"
#include "pager.h"
#include "parse-argument.h"
#include "parse-util.h"
#include "udev-util.h"
#include "unit-def.h"
#include "verbs.h"
-#include "virt.h"
#include "wifi-util.h"
/* Kernel defines MODULE_NAME_LEN as 64 - sizeof(unsigned long). So, 64 is enough. */
/* use 128 kB for receive socket kernel queue, we shouldn't need more here */
#define RCVBUF_SIZE (128*1024)
-static PagerFlags arg_pager_flags = 0;
-static bool arg_legend = true;
-static bool arg_no_reload = false;
-static bool arg_all = false;
-static bool arg_stats = false;
-static bool arg_full = false;
-static bool arg_runtime = false;
-static unsigned arg_lines = 10;
-static char *arg_drop_in = NULL;
-static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
+PagerFlags arg_pager_flags = 0;
+bool arg_legend = true;
+bool arg_no_reload = false;
+bool arg_all = false;
+bool arg_stats = false;
+bool arg_full = false;
+bool arg_runtime = false;
+unsigned arg_lines = 10;
+char *arg_drop_in = NULL;
+JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
STATIC_DESTRUCTOR_REGISTER(arg_drop_in, freep);
return 0;
}
-static bool networkd_is_running(void) {
+bool networkd_is_running(void) {
static int cached = -1;
int r;
return cached;
}
-static int acquire_bus(sd_bus **ret) {
+int acquire_bus(sd_bus **ret) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
return 0;
}
-typedef enum ReloadFlags {
- RELOAD_NETWORKD = 1 << 0,
- RELOAD_UDEVD = 1 << 1,
-} ReloadFlags;
-
-static int get_config_files_by_name(const char *name, char **ret_path, char ***ret_dropins) {
- _cleanup_free_ char *path = NULL;
- int r;
-
- assert(name);
- assert(ret_path);
-
- STRV_FOREACH(i, NETWORK_DIRS) {
- _cleanup_free_ char *p = NULL;
-
- p = path_join(*i, name);
- if (!p)
- return -ENOMEM;
-
- r = RET_NERRNO(access(p, F_OK));
- if (r >= 0) {
- path = TAKE_PTR(p);
- break;
- }
-
- if (r != -ENOENT)
- log_debug_errno(r, "Failed to determine whether '%s' exists, ignoring: %m", p);
- }
-
- if (!path)
- return -ENOENT;
-
- if (ret_dropins) {
- _cleanup_free_ char *dropin_dirname = NULL;
-
- dropin_dirname = strjoin(name, ".d");
- if (!dropin_dirname)
- return -ENOMEM;
-
- r = conf_files_list_dropins(ret_dropins, dropin_dirname, /* root = */ NULL, NETWORK_DIRS);
- if (r < 0)
- return r;
- }
-
- *ret_path = TAKE_PTR(path);
-
- return 0;
-}
-
-static int get_dropin_by_name(
- const char *name,
- char * const *dropins,
- char **ret) {
-
- assert(name);
- assert(ret);
-
- STRV_FOREACH(i, dropins)
- if (path_equal_filename(*i, name)) {
- _cleanup_free_ char *d = NULL;
-
- d = strdup(*i);
- if (!d)
- return -ENOMEM;
-
- *ret = TAKE_PTR(d);
- return 1;
- }
-
- *ret = NULL;
- return 0;
-}
-
-static int get_network_files_by_link(
- sd_netlink **rtnl,
- const char *link,
- char **ret_path,
- char ***ret_dropins) {
-
- _cleanup_strv_free_ char **dropins = NULL;
- _cleanup_free_ char *path = NULL;
- int r, ifindex;
-
- assert(rtnl);
- assert(link);
- assert(ret_path);
- assert(ret_dropins);
-
- ifindex = rtnl_resolve_interface_or_warn(rtnl, link);
- if (ifindex < 0)
- return ifindex;
-
- r = sd_network_link_get_network_file(ifindex, &path);
- if (r == -ENODATA)
- return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
- "Link '%s' has no associated network file.", link);
- if (r < 0)
- return log_error_errno(r, "Failed to get network file for link '%s': %m", link);
-
- r = sd_network_link_get_network_file_dropins(ifindex, &dropins);
- if (r < 0 && r != -ENODATA)
- return log_error_errno(r, "Failed to get network drop-ins for link '%s': %m", link);
-
- *ret_path = TAKE_PTR(path);
- *ret_dropins = TAKE_PTR(dropins);
-
- return 0;
-}
-
-static int get_link_files_by_link(const char *link, char **ret_path, char ***ret_dropins) {
- _cleanup_(sd_device_unrefp) sd_device *device = NULL;
- _cleanup_strv_free_ char **dropins_split = NULL;
- _cleanup_free_ char *p = NULL;
- const char *path, *dropins;
- int r;
-
- assert(link);
- assert(ret_path);
- assert(ret_dropins);
-
- r = sd_device_new_from_ifname(&device, link);
- if (r < 0)
- return log_error_errno(r, "Failed to create sd-device object for link '%s': %m", link);
-
- r = sd_device_get_property_value(device, "ID_NET_LINK_FILE", &path);
- if (r == -ENOENT)
- return log_error_errno(r, "Link '%s' has no associated link file.", link);
- if (r < 0)
- return log_error_errno(r, "Failed to get link file for link '%s': %m", link);
-
- r = sd_device_get_property_value(device, "ID_NET_LINK_FILE_DROPINS", &dropins);
- if (r < 0 && r != -ENOENT)
- return log_error_errno(r, "Failed to get link drop-ins for link '%s': %m", link);
- if (r >= 0) {
- r = strv_split_full(&dropins_split, dropins, ":", EXTRACT_CUNESCAPE);
- if (r < 0)
- return log_error_errno(r, "Failed to parse link drop-ins for link '%s': %m", link);
- }
-
- p = strdup(path);
- if (!p)
- return log_oom();
-
- *ret_path = TAKE_PTR(p);
- *ret_dropins = TAKE_PTR(dropins_split);
-
- return 0;
-}
-
-static int get_config_files_by_link_config(
- const char *link_config,
- sd_netlink **rtnl,
- char **ret_path,
- char ***ret_dropins,
- ReloadFlags *ret_reload) {
-
- _cleanup_strv_free_ char **dropins = NULL, **link_config_split = NULL;
- _cleanup_free_ char *path = NULL;
- const char *ifname, *type;
- ReloadFlags reload;
- size_t n;
- int r;
-
- assert(link_config);
- assert(rtnl);
- assert(ret_path);
- assert(ret_dropins);
-
- link_config_split = strv_split(link_config, ":");
- if (!link_config_split)
- return log_oom();
-
- n = strv_length(link_config_split);
- if (n == 0 || isempty(link_config_split[0]))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No link name is given.");
- if (n > 2)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid link config '%s'.", link_config);
-
- ifname = link_config_split[0];
- type = n == 2 ? link_config_split[1] : "network";
-
- if (streq(type, "network")) {
- if (!networkd_is_running())
- return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
- "Cannot get network file for link if systemd-networkd is not running.");
-
- r = get_network_files_by_link(rtnl, ifname, &path, &dropins);
- if (r < 0)
- return r;
-
- reload = RELOAD_NETWORKD;
- } else if (streq(type, "link")) {
- r = get_link_files_by_link(ifname, &path, &dropins);
- if (r < 0)
- return r;
-
- reload = RELOAD_UDEVD;
- } else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Invalid config type '%s' for link '%s'.", type, ifname);
-
- *ret_path = TAKE_PTR(path);
- *ret_dropins = TAKE_PTR(dropins);
-
- if (ret_reload)
- *ret_reload = reload;
-
- return 0;
-}
-
-static int add_config_to_edit(
- EditFileContext *context,
- const char *path,
- char * const *dropins) {
-
- _cleanup_free_ char *new_path = NULL, *dropin_path = NULL, *old_dropin = NULL;
- _cleanup_strv_free_ char **comment_paths = NULL;
- int r;
-
- assert(context);
- assert(path);
-
- /* If we're supposed to edit main config file in /run/, but a config with the same name is present
- * under /etc/, we bail out since the one in /etc/ always overrides that in /run/. */
- if (arg_runtime && !arg_drop_in && path_startswith(path, "/etc"))
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "Cannot edit runtime config file: overridden by %s", path);
-
- if (path_startswith(path, "/usr") || arg_runtime != !!path_startswith(path, "/run")) {
- _cleanup_free_ char *name = NULL;
-
- r = path_extract_filename(path, &name);
- if (r < 0)
- return log_error_errno(r, "Failed to extract filename from '%s': %m", path);
-
- new_path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], name);
- if (!new_path)
- return log_oom();
- }
-
- if (!arg_drop_in)
- return edit_files_add(context, new_path ?: path, path, NULL);
-
- bool need_new_dropin;
-
- r = get_dropin_by_name(arg_drop_in, dropins, &old_dropin);
- if (r < 0)
- return log_error_errno(r, "Failed to acquire drop-in '%s': %m", arg_drop_in);
- if (r > 0) {
- /* See the explanation above */
- if (arg_runtime && path_startswith(old_dropin, "/etc"))
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST),
- "Cannot edit runtime config file: overridden by %s", old_dropin);
-
- need_new_dropin = path_startswith(old_dropin, "/usr") || arg_runtime != !!path_startswith(old_dropin, "/run");
- } else
- need_new_dropin = true;
-
- if (!need_new_dropin)
- /* An existing drop-in is found in the correct scope. Let's edit it directly. */
- dropin_path = TAKE_PTR(old_dropin);
- else {
- /* No drop-in was found or an existing drop-in is in a different scope. Let's create a new
- * drop-in file. */
- dropin_path = strjoin(new_path ?: path, ".d/", arg_drop_in);
- if (!dropin_path)
- return log_oom();
- }
-
- comment_paths = strv_new(path);
- if (!comment_paths)
- return log_oom();
-
- r = strv_extend_strv(&comment_paths, dropins, /* filter_duplicates = */ false);
- if (r < 0)
- return log_oom();
-
- return edit_files_add(context, dropin_path, old_dropin, comment_paths);
-}
-
-static int udevd_reload(sd_bus *bus) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
- const char *job_path;
- int r;
-
- assert(bus);
-
- r = bus_wait_for_jobs_new(bus, &w);
- if (r < 0)
- return log_error_errno(r, "Could not watch jobs: %m");
-
- r = bus_call_method(bus,
- bus_systemd_mgr,
- "ReloadUnit",
- &error,
- &reply,
- "ss",
- "systemd-udevd.service",
- "replace");
- if (r < 0)
- return log_error_errno(r, "Failed to reload systemd-udevd: %s", bus_error_message(&error, r));
-
- r = sd_bus_message_read(reply, "o", &job_path);
- if (r < 0)
- return bus_log_parse_error(r);
-
- r = bus_wait_for_jobs_one(w, job_path, /* quiet = */ true, NULL);
- if (r == -ENOEXEC) {
- log_debug("systemd-udevd is not running, skipping reload.");
- return 0;
- }
- if (r < 0)
- return log_error_errno(r, "Failed to reload systemd-udevd: %m");
-
- return 1;
-}
-
-static int verb_edit(int argc, char *argv[], void *userdata) {
- _cleanup_(edit_file_context_done) EditFileContext context = {
- .marker_start = DROPIN_MARKER_START,
- .marker_end = DROPIN_MARKER_END,
- .remove_parent = !!arg_drop_in,
- };
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
- ReloadFlags reload = 0;
- int r;
-
- if (!on_tty())
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit network config files if not on a tty.");
-
- r = mac_selinux_init();
- if (r < 0)
- return r;
-
- STRV_FOREACH(name, strv_skip(argv, 1)) {
- _cleanup_strv_free_ char **dropins = NULL;
- _cleanup_free_ char *path = NULL;
- const char *link_config;
-
- link_config = startswith(*name, "@");
- if (link_config) {
- ReloadFlags flags;
-
- r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, &flags);
- if (r < 0)
- return r;
-
- reload |= flags;
-
- r = add_config_to_edit(&context, path, dropins);
- if (r < 0)
- return r;
-
- continue;
- }
-
- if (ENDSWITH_SET(*name, ".network", ".netdev"))
- reload |= RELOAD_NETWORKD;
- else if (endswith(*name, ".link"))
- reload |= RELOAD_UDEVD;
- else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid network config name '%s'.", *name);
-
- r = get_config_files_by_name(*name, &path, &dropins);
- if (r == -ENOENT) {
- if (arg_drop_in)
- return log_error_errno(r, "Cannot find network config '%s'.", *name);
-
- log_debug("No existing network config '%s' found, creating a new file.", *name);
-
- path = path_join(NETWORK_DIRS[arg_runtime ? 1 : 0], *name);
- if (!path)
- return log_oom();
-
- r = edit_files_add(&context, path, NULL, NULL);
- if (r < 0)
- return r;
- continue;
- }
- if (r < 0)
- return log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
-
- r = add_config_to_edit(&context, path, dropins);
- if (r < 0)
- return r;
- }
-
- r = do_edit_files_and_install(&context);
- if (r < 0)
- return r;
-
- if (arg_no_reload)
- return 0;
-
- if (!sd_booted() || running_in_chroot() > 0) {
- log_debug("System is not booted with systemd or is running in chroot, skipping reload.");
- return 0;
- }
-
- _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
-
- r = sd_bus_open_system(&bus);
- if (r < 0)
- return log_error_errno(r, "Failed to connect to system bus: %m");
-
- if (FLAGS_SET(reload, RELOAD_UDEVD)) {
- r = udevd_reload(bus);
- if (r < 0)
- return r;
- }
-
- if (FLAGS_SET(reload, RELOAD_NETWORKD)) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-
- if (!networkd_is_running()) {
- log_debug("systemd-networkd is not running, skipping reload.");
- return 0;
- }
-
- r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL);
- if (r < 0)
- return log_error_errno(r, "Failed to reload systemd-networkd: %s", bus_error_message(&error, r));
- }
-
- return 0;
-}
-
-static int verb_cat(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
- int r, ret = 0;
-
- pager_open(arg_pager_flags);
-
- STRV_FOREACH(name, strv_skip(argv, 1)) {
- _cleanup_strv_free_ char **dropins = NULL;
- _cleanup_free_ char *path = NULL;
- const char *link_config;
-
- link_config = startswith(*name, "@");
- if (link_config) {
- r = get_config_files_by_link_config(link_config, &rtnl, &path, &dropins, /* ret_reload = */ NULL);
- if (r < 0)
- return RET_GATHER(ret, r);
- } else {
- r = get_config_files_by_name(*name, &path, &dropins);
- if (r == -ENOENT) {
- RET_GATHER(ret, log_error_errno(r, "Cannot find network config file '%s'.", *name));
- continue;
- }
- if (r < 0) {
- log_error_errno(r, "Failed to get the path of network config '%s': %m", *name);
- return RET_GATHER(ret, r);
- }
- }
-
- r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
- if (r < 0)
- return RET_GATHER(ret, r);
- }
-
- return ret;
-}
-
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
" reload Reload .network and .netdev files\n"
" edit FILES|DEVICES... Edit network configuration files\n"
" cat FILES|DEVICES... Show network configuration files\n"
+ " mask FILES... Mask network configuration files\n"
+ " unmask FILES... Unmask network configuration files\n"
"\nOptions:\n"
" -h --help Show this help\n"
" --version Show package version\n"
{ "reload", 1, 1, VERB_ONLINE_ONLY, verb_reload },
{ "edit", 2, VERB_ANY, 0, verb_edit },
{ "cat", 2, VERB_ANY, 0, verb_cat },
+ { "mask", 2, VERB_ANY, 0, verb_mask },
+ { "unmask", 2, VERB_ANY, 0, verb_unmask },
{}
};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+
+#include "output-mode.h"
+#include "pager.h"
+
+extern PagerFlags arg_pager_flags;
+extern bool arg_legend;
+extern bool arg_no_reload;
+extern bool arg_all;
+extern bool arg_stats;
+extern bool arg_full;
+extern bool arg_runtime;
+extern unsigned arg_lines;
+extern char *arg_drop_in;
+extern JsonFormatFlags arg_json_format_flags;
+
+bool networkd_is_running(void);
+int acquire_bus(sd_bus **ret);
}
if (r < 0)
- log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, IP address %s, ignoring",
- add? "add" : "delete",
+ log_warning_errno(r, "Failed to %s NFT set: family %s, table %s, set %s, IP address %s, ignoring: %m",
+ add ? "add" : "delete",
nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set,
IN_ADDR_PREFIX_TO_STRING(address->family, &address->in_addr, address->prefixlen));
else
}
static int address_process_request(Request *req, Link *link, Address *address) {
- struct Address *existing;
+ Address *existing;
struct ifa_cacheinfo c;
int r;
if (address->flags != filtered_flags) {
_cleanup_free_ char *str = NULL;
- (void) address_flags_to_string_alloc(filtered_flags, address->family, &str);
+ (void) address_flags_to_string_alloc(address->flags ^ filtered_flags, address->family, &str);
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: unexpected address flags \"%s\" were configured. "
"Ignoring [Address] section from line %u.",
addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
}
-static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid) {
+static int add_single(sd_netlink_message *m, uint16_t id, bool untagged, bool is_pvid, char **str) {
assert(m);
assert(id < BRIDGE_VLAN_BITMAP_MAX);
+
+ if (DEBUG_LOGGING)
+ (void) strextendf_with_separator(str, ",", "%u%s%s%s%s%s", id,
+ (untagged || is_pvid) ? "(" : "",
+ untagged ? "untagged" : "",
+ (untagged && is_pvid) ? "," : "",
+ is_pvid ? "pvid" : "",
+ (untagged || is_pvid) ? ")" : "");
+
return sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
&(struct bridge_vlan_info) {
.vid = id,
sizeof(struct bridge_vlan_info));
}
-static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged) {
+static int add_range(sd_netlink_message *m, uint16_t begin, uint16_t end, bool untagged, char **str) {
int r;
assert(m);
assert(end < BRIDGE_VLAN_BITMAP_MAX);
if (begin == end)
- return add_single(m, begin, untagged, /* is_pvid = */ false);
+ return add_single(m, begin, untagged, /* is_pvid = */ false, str);
+
+ if (DEBUG_LOGGING)
+ (void) strextendf_with_separator(str, ",", "%u-%u%s", begin, end, untagged ? "(untagged)" : "");
r = sd_netlink_message_append_data(m, IFLA_BRIDGE_VLAN_INFO,
&(struct bridge_vlan_info) {
}
static int bridge_vlan_append_set_info(Link *link, sd_netlink_message *m) {
+ _cleanup_free_ char *str = NULL;
uint16_t pvid, begin = UINT16_MAX;
bool untagged, pvid_is_untagged;
int r;
if (begin != UINT16_MAX) {
assert(begin < k);
- r = add_range(m, begin, k - 1, untagged);
+ r = add_range(m, begin, k - 1, untagged, &str);
if (r < 0)
return r;
begin = UINT16_MAX;
}
- r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true);
+ r = add_single(m, pvid, pvid_is_untagged, /* is_pvid = */ true, &str);
if (r < 0)
return r;
if (begin != UINT16_MAX) {
assert(begin < k);
- r = add_range(m, begin, k - 1, untagged);
+ r = add_range(m, begin, k - 1, untagged, &str);
if (r < 0)
return r;
continue;
/* Tagging flag is changed from the previous bits. Finish them. */
- r = add_range(m, begin, k - 1, untagged);
+ r = add_range(m, begin, k - 1, untagged, &str);
if (r < 0)
return r;
* the above loop, we run 0…4095. */
assert_cc(BRIDGE_VLAN_BITMAP_MAX > VLANID_MAX);
assert(begin == UINT16_MAX);
+
+ log_link_debug(link, "Setting Bridge VLAN IDs: %s", strna(str));
return 0;
}
static int bridge_vlan_append_del_info(Link *link, sd_netlink_message *m) {
+ _cleanup_free_ char *str = NULL;
uint16_t pvid, begin = UINT16_MAX;
int r;
if (begin != UINT16_MAX) {
assert(begin < k);
- r = add_range(m, begin, k - 1, /* untagged = */ false);
+ r = add_range(m, begin, k - 1, /* untagged = */ false, &str);
if (r < 0)
return r;
/* No pending bit sequence. */
assert(begin == UINT16_MAX);
+
+ log_link_debug(link, "Removing Bridge VLAN IDs: %s", strna(str));
return 0;
}
Network.SpeedMeterIntervalSec, config_parse_sec, 0, offsetof(Manager, speed_meter_interval_usec)
Network.ManageForeignRoutingPolicyRules, config_parse_bool, 0, offsetof(Manager, manage_foreign_rules)
Network.ManageForeignRoutes, config_parse_bool, 0, offsetof(Manager, manage_foreign_routes)
+Network.ManageForeignNextHops, config_parse_bool, 0, offsetof(Manager, manage_foreign_nexthops)
Network.RouteTable, config_parse_route_table_names, 0, 0
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Manager, ipv6_privacy_extensions)
DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp_duid)
JSON_BUILD_PAIR_STRING("ConfigState", state)));
}
-static int nexthops_append_json(Set *nexthops, JsonVariant **v) {
+static int nexthops_append_json(Manager *manager, int ifindex, JsonVariant **v) {
_cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
NextHop *nexthop;
int r;
+ assert(manager);
assert(v);
- SET_FOREACH(nexthop, nexthops) {
+ HASHMAP_FOREACH(nexthop, manager->nexthops_by_id) {
_cleanup_(json_variant_unrefp) JsonVariant *e = NULL;
+ if (nexthop->ifindex != ifindex)
+ continue;
+
r = nexthop_build_json(nexthop, &e);
if (r < 0)
return r;
if (r < 0)
return r;
- r = nexthops_append_json(link->nexthops, &v);
+ r = nexthops_append_json(link->manager, link->ifindex, &v);
if (r < 0)
return r;
if (r < 0)
return r;
- r = nexthops_append_json(manager->nexthops, &v);
+ r = nexthops_append_json(manager, /* ifindex = */ 0, &v);
if (r < 0)
return r;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server: %s", *i);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-ntp-servers",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-ntp-servers",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-dns-servers",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-dns-servers",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
goto finalize;
if (r == 0) {
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-domains",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-domains",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-default-route",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-default-route",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-llmnr",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-llmnr",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-mdns",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-mdns",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-dns-over-tls",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-dns-over-tls",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-dnssec",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-dnssec",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return r;
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.set-dnssec-negative-trust-anchors",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.set-dnssec-negative-trust-anchors",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.revert-ntp",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.revert-ntp",
+ /* details= */ NULL,
+ &l->manager->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.revert-dns",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.revert-dns",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
"Interface %s is not managed by systemd-networkd",
l->ifname);
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.forcerenew",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.forcerenew",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
"Interface %s is not managed by systemd-networkd",
l->ifname);
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.renew",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.renew",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
assert(message);
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.reconfigure",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.reconfigure",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
link_dns_settings_clear(link);
link->routes = set_free(link->routes);
- link->nexthops = set_free(link->nexthops);
link->neighbors = set_free(link->neighbors);
link->addresses = set_free(link->addresses);
link->qdiscs = set_free(link->qdiscs);
Link *link;
assert(m);
- assert(ifindex > 0);
+
+ if (ifindex <= 0)
+ return -EINVAL;
link = hashmap_get(m->links_by_index, INT_TO_PTR(ifindex));
if (!link)
if (r < 0)
return r;
+ r = link_configure_mtu(link);
+ if (r < 0)
+ return r;
+
if (link->iftype == ARPHRD_CAN) {
/* let's shortcut things for CAN which doesn't need most of what's done below. */
r = link_request_to_set_can(link);
if (r < 0)
return r;
- r = link_configure_mtu(link);
- if (r < 0)
- return r;
-
r = link_request_to_set_addrgen_mode(link);
if (r < 0)
return r;
return 0;
if (network) {
+ _cleanup_free_ char *joined = strv_join(network->dropins, ", ");
+
if (link->state == LINK_STATE_INITIALIZED)
- log_link_info(link, "Configuring with %s.", network->filename);
+ log_link_info(link, "Configuring with %s%s%s%s.",
+ network->filename,
+ isempty(joined) ? "" : " (dropins: ",
+ joined,
+ isempty(joined) ? "" : ")");
else
- log_link_info(link, "Reconfiguring with %s.", network->filename);
+ log_link_info(link, "Reconfiguring with %s%s%s%s.",
+ network->filename,
+ isempty(joined) ? "" : " (dropins: ",
+ joined,
+ isempty(joined) ? "" : ")");
} else
log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO,
"Unmanaging interface.");
Set *addresses;
Set *neighbors;
Set *routes;
- Set *nexthops;
Set *qdiscs;
Set *tclasses;
Manager *manager = userdata;
int r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.network1.reload",
- NULL, true, UID_INVALID,
- &manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.network1.reload",
+ /* details= */ NULL,
+ &manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
.online_state = _LINK_ONLINE_STATE_INVALID,
.manage_foreign_routes = true,
.manage_foreign_rules = true,
+ .manage_foreign_nexthops = true,
.ethtool_fd = -EBADF,
.dhcp_duid.type = DUID_TYPE_EN,
.dhcp6_duid.type = DUID_TYPE_EN,
* set_free() must be called after the above sd_netlink_unref(). */
m->routes = set_free(m->routes);
- m->nexthops = set_free(m->nexthops);
m->nexthops_by_id = hashmap_free(m->nexthops_by_id);
sd_event_source_unref(m->speed_meter_event_source);
assert(m);
assert(m->rtnl);
+ if (!m->manage_foreign_nexthops)
+ return 0;
+
r = sd_rtnl_message_new_nexthop(m->rtnl, &req, RTM_GETNEXTHOP, 0, 0);
if (r < 0)
return r;
bool restarting;
bool manage_foreign_routes;
bool manage_foreign_rules;
+ bool manage_foreign_nexthops;
Set *dirty_links;
Set *new_wlan_ifindices;
/* Manage nexthops by id. */
Hashmap *nexthops_by_id;
- /* Manager stores nexthops without RTA_OIF attribute. */
- Set *nexthops;
-
/* Manager stores routes without RTA_OIF attribute. */
unsigned route_remove_messages;
Set *routes;
dup = set_remove(neighbors, neighbor);
if (dup) {
log_warning("%s: Duplicated neighbor settings for %s is specified at line %u and %u, "
- "dropping the address setting specified at line %u.",
+ "dropping the neighbor setting specified at line %u.",
dup->section->filename,
IN_ADDR_TO_STRING(neighbor->family, &neighbor->in_addr),
neighbor->section->line,
dup->section->line, dup->section->line);
- /* neighbor_free() will drop the address from neighbors_by_section. */
+ /* neighbor_free() will drop the neighbor from neighbors_by_section. */
neighbor_free(dup);
}
config_section_free(nexthop->section);
- if (nexthop->link) {
- set_remove(nexthop->link->nexthops, nexthop);
-
- if (nexthop->link->manager && nexthop->id > 0)
- hashmap_remove(nexthop->link->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
- }
-
if (nexthop->manager) {
- set_remove(nexthop->manager->nexthops, nexthop);
-
- if (nexthop->id > 0)
- hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
+ assert(nexthop->id > 0);
+ hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
}
hashmap_free_free(nexthop->group);
DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop, nexthop_free);
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ nexthop_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ NextHop,
+ nexthop_free);
+
static int nexthop_new(NextHop **ret) {
_cleanup_(nexthop_freep) NextHop *nexthop = NULL;
static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
assert(nexthop);
+ assert(state);
- siphash24_compress(&nexthop->protocol, sizeof(nexthop->protocol), state);
siphash24_compress(&nexthop->id, sizeof(nexthop->id), state);
- siphash24_compress(&nexthop->blackhole, sizeof(nexthop->blackhole), state);
- siphash24_compress(&nexthop->family, sizeof(nexthop->family), state);
+}
- switch (nexthop->family) {
- case AF_INET:
- case AF_INET6:
- siphash24_compress(&nexthop->gw, FAMILY_ADDRESS_SIZE(nexthop->family), state);
+static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
+ assert(a);
+ assert(b);
- break;
- default:
- /* treat any other address family as AF_UNSPEC */
- break;
- }
+ return CMP(a->id, b->id);
}
-static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
+static int nexthop_compare_full(const NextHop *a, const NextHop *b) {
int r;
+ assert(a);
+ assert(b);
+
+ /* This compares detailed configs, except for ID and ifindex. */
+
r = CMP(a->protocol, b->protocol);
if (r != 0)
return r;
- r = CMP(a->id, b->id);
+ r = CMP(a->flags, b->flags);
+ if (r != 0)
+ return r;
+
+ r = CMP(hashmap_size(a->group), hashmap_size(b->group));
if (r != 0)
return r;
+ if (!hashmap_isempty(a->group)) {
+ struct nexthop_grp *ga;
+
+ HASHMAP_FOREACH(ga, a->group) {
+ struct nexthop_grp *gb;
+
+ gb = hashmap_get(b->group, UINT32_TO_PTR(ga->id));
+ if (!gb)
+ return CMP(ga, gb);
+
+ r = CMP(ga->weight, gb->weight);
+ if (r != 0)
+ return r;
+ }
+ }
+
r = CMP(a->blackhole, b->blackhole);
if (r != 0)
return r;
if (r != 0)
return r;
- if (IN_SET(a->family, AF_INET, AF_INET6))
- return memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+ if (IN_SET(a->family, AF_INET, AF_INET6)) {
+ r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+ if (r != 0)
+ return r;
+ }
return 0;
}
-DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
- nexthop_hash_ops,
- NextHop,
- nexthop_hash_func,
- nexthop_compare_func,
- nexthop_free);
-
-static bool nexthop_equal(const NextHop *a, const NextHop *b) {
- if (a == b)
- return true;
-
- if (!a || !b)
- return false;
-
- return nexthop_compare_func(a, b) == 0;
-}
-
static int nexthop_dup(const NextHop *src, NextHop **ret) {
_cleanup_(nexthop_freep) NextHop *dest = NULL;
struct nexthop_grp *nhg;
/* unset all pointers */
dest->manager = NULL;
- dest->link = NULL;
dest->network = NULL;
dest->section = NULL;
dest->group = NULL;
return 0;
}
-int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) {
+static bool nexthop_bound_to_link(const NextHop *nexthop) {
+ assert(nexthop);
+ return !nexthop->blackhole && hashmap_isempty(nexthop->group);
+}
+
+int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret) {
NextHop *nh;
assert(manager);
return 0;
}
-static bool nexthop_owned_by_link(const NextHop *nexthop) {
- return !nexthop->blackhole && hashmap_isempty(nexthop->group);
-}
-
-static int nexthop_get(Manager *manager, Link *link, NextHop *in, NextHop **ret) {
+static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) {
NextHop *nexthop;
- Set *nexthops;
+ int ifindex;
+ assert(link);
+ assert(link->manager);
assert(in);
- if (nexthop_owned_by_link(in)) {
- if (!link)
- return -ENOENT;
+ if (in->id > 0)
+ return nexthop_get_by_id(link->manager, in->id, ret);
- nexthops = link->nexthops;
- } else {
- if (!manager)
- return -ENOENT;
+ ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0;
- nexthops = manager->nexthops;
- }
+ HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
+ if (nexthop->ifindex != ifindex)
+ continue;
+ if (nexthop_compare_full(nexthop, in) != 0)
+ continue;
- nexthop = set_get(nexthops, in);
- if (nexthop) {
if (ret)
*ret = nexthop;
return 0;
}
- if (in->id > 0)
+ return -ENOENT;
+}
+
+static int nexthop_get_request_by_id(Manager *manager, uint32_t id, Request **ret) {
+ Request *req;
+
+ assert(manager);
+
+ req = ordered_set_get(
+ manager->request_queue,
+ &(Request) {
+ .type = REQUEST_TYPE_NEXTHOP,
+ .userdata = (void*) &(const NextHop) { .id = id },
+ .hash_func = (hash_func_t) nexthop_hash_func,
+ .compare_func = (compare_func_t) nexthop_compare_func,
+ });
+ if (!req)
return -ENOENT;
- /* Also find nexthop configured without ID. */
- SET_FOREACH(nexthop, nexthops) {
- uint32_t id;
- bool found;
+ if (ret)
+ *ret = req;
+ return 0;
+}
- id = nexthop->id;
- nexthop->id = 0;
- found = nexthop_equal(nexthop, in);
- nexthop->id = id;
+static int nexthop_get_request(Link *link, const NextHop *in, Request **ret) {
+ Request *req;
+ int ifindex;
+
+ assert(link);
+ assert(link->manager);
+ assert(in);
+
+ if (in->id > 0)
+ return nexthop_get_request_by_id(link->manager, in->id, ret);
+
+ ifindex = nexthop_bound_to_link(in) ? link->ifindex : 0;
- if (!found)
+ ORDERED_SET_FOREACH(req, link->manager->request_queue) {
+ if (req->type != REQUEST_TYPE_NEXTHOP)
+ continue;
+
+ NextHop *nexthop = ASSERT_PTR(req->userdata);
+ if (nexthop->ifindex != ifindex)
+ continue;
+ if (nexthop_compare_full(nexthop, in) != 0)
continue;
if (ret)
- *ret = nexthop;
+ *ret = req;
return 0;
}
return -ENOENT;
}
-static int nexthop_add(Manager *manager, Link *link, NextHop *nexthop) {
+static int nexthop_add_new(Manager *manager, uint32_t id, NextHop **ret) {
+ _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
int r;
- assert(nexthop);
- assert(nexthop->id > 0);
-
- if (nexthop_owned_by_link(nexthop)) {
- assert(link);
+ assert(manager);
+ assert(id > 0);
- r = set_ensure_put(&link->nexthops, &nexthop_hash_ops, nexthop);
- if (r < 0)
- return r;
- if (r == 0)
- return -EEXIST;
+ r = nexthop_new(&nexthop);
+ if (r < 0)
+ return r;
- nexthop->link = link;
+ nexthop->id = id;
- manager = link->manager;
- } else {
- assert(manager);
+ r = hashmap_ensure_put(&manager->nexthops_by_id, &nexthop_hash_ops, UINT32_TO_PTR(nexthop->id), nexthop);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EEXIST;
- r = set_ensure_put(&manager->nexthops, &nexthop_hash_ops, nexthop);
- if (r < 0)
- return r;
- if (r == 0)
- return -EEXIST;
+ nexthop->manager = manager;
- nexthop->manager = manager;
- }
+ if (ret)
+ *ret = nexthop;
- return hashmap_ensure_put(&manager->nexthops_by_id, NULL, UINT32_TO_PTR(nexthop->id), nexthop);
+ TAKE_PTR(nexthop);
+ return 0;
}
static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) {
_cleanup_set_free_ Set *ids = NULL;
Network *network;
- uint32_t id;
int r;
assert(manager);
if (nexthop->id > 0)
return 0;
+ /* If ManageForeignNextHops=no, nexthop with id == 0 should be already filtered by
+ * nexthop_section_verify(). */
+ assert(manager->manage_foreign_nexthops);
+
/* Find the lowest unused ID. */
ORDERED_HASHMAP_FOREACH(network, manager->networks) {
}
}
- for (id = 1; id < UINT32_MAX; id++) {
- if (manager_get_nexthop_by_id(manager, id, NULL) >= 0)
+ for (uint32_t id = 1; id < UINT32_MAX; id++) {
+ if (nexthop_get_by_id(manager, id, NULL) >= 0)
+ continue;
+ if (nexthop_get_request_by_id(manager, id, NULL) >= 0)
continue;
if (set_contains(ids, UINT32_TO_PTR(id)))
continue;
- break;
+
+ nexthop->id = id;
+ return 0;
}
- nexthop->id = id;
- return 0;
+ return -EBUSY;
}
-static void log_nexthop_debug(const NextHop *nexthop, const char *str, const Link *link) {
+static void log_nexthop_debug(const NextHop *nexthop, const char *str, Manager *manager) {
_cleanup_free_ char *state = NULL, *group = NULL, *flags = NULL;
struct nexthop_grp *nhg;
+ Link *link = NULL;
assert(nexthop);
assert(str);
-
- /* link may be NULL. */
+ assert(manager);
if (!DEBUG_LOGGING)
return;
+ (void) link_get_by_index(manager, nexthop->ifindex, &link);
(void) network_config_state_to_string_alloc(nexthop->state, &state);
(void) route_flags_to_string_alloc(nexthop->flags, &flags);
static int nexthop_remove(NextHop *nexthop) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
Manager *manager;
- Link *link;
+ Link *link = NULL;
+ Request *req;
int r;
- assert(nexthop);
- assert(nexthop->manager || (nexthop->link && nexthop->link->manager));
+ manager = ASSERT_PTR(ASSERT_PTR(nexthop)->manager);
/* link may be NULL. */
- link = nexthop->link;
- manager = nexthop->manager ?: nexthop->link->manager;
+ (void) link_get_by_index(manager, nexthop->ifindex, &link);
if (nexthop->id == 0) {
log_link_debug(link, "Cannot remove nexthop without valid ID, ignoring.");
return 0;
}
- log_nexthop_debug(nexthop, "Removing", link);
+ log_nexthop_debug(nexthop, "Removing", manager);
r = sd_rtnl_message_new_nexthop(manager->rtnl, &m, RTM_DELNEXTHOP, AF_UNSPEC, RTPROT_UNSPEC);
if (r < 0)
link_ref(link); /* link may be NULL, link_ref() is OK with that */
nexthop_enter_removing(nexthop);
+ if (nexthop_get_request_by_id(manager, nexthop->id, &req) >= 0)
+ nexthop_enter_removing(req->userdata);
+
return 0;
}
int r;
assert(nexthop);
+ assert(nexthop->id > 0);
assert(IN_SET(nexthop->family, AF_UNSPEC, AF_INET, AF_INET6));
assert(link);
assert(link->manager);
assert(link->ifindex > 0);
assert(req);
- log_nexthop_debug(nexthop, "Configuring", link);
+ log_nexthop_debug(nexthop, "Configuring", link->manager);
r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &m, RTM_NEWNEXTHOP, nexthop->family, nexthop->protocol);
if (r < 0)
return r;
- if (nexthop->id > 0) {
- r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id);
- if (r < 0)
- return r;
- }
+ r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id);
+ if (r < 0)
+ return r;
if (!hashmap_isempty(nexthop->group)) {
_cleanup_free_ struct nexthop_grp *group = NULL;
if (r < 0)
return r;
} else {
- r = sd_netlink_message_append_u32(m, NHA_OIF, link->ifindex);
+ assert(nexthop->ifindex == link->ifindex);
+
+ r = sd_netlink_message_append_u32(m, NHA_OIF, nexthop->ifindex);
if (r < 0)
return r;
if (!link_is_ready_to_configure(link, false))
return false;
- if (nexthop_owned_by_link(nexthop)) {
+ if (nexthop_bound_to_link(nexthop)) {
+ assert(nexthop->ifindex == link->ifindex);
+
/* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated
* when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of
* kernel. */
HASHMAP_FOREACH(nhg, nexthop->group) {
NextHop *g;
- if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0)
+ if (nexthop_get_by_id(link->manager, nhg->id, &g) < 0)
return false;
if (!nexthop_exists(g))
}
static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) {
+ NextHop *existing;
int r;
assert(req);
assert(link);
+ assert(link->manager);
assert(nexthop);
if (!nexthop_is_ready_to_configure(link, nexthop))
return log_link_warning_errno(link, r, "Failed to configure nexthop");
nexthop_enter_configuring(nexthop);
+ if (nexthop_get_by_id(link->manager, nexthop->id, &existing) >= 0)
+ nexthop_enter_configuring(existing);
+
return 1;
}
-static int link_request_nexthop(Link *link, NextHop *nexthop) {
- NextHop *existing;
+static int link_request_nexthop(Link *link, const NextHop *nexthop) {
+ _cleanup_(nexthop_freep) NextHop *tmp = NULL;
+ NextHop *existing = NULL;
int r;
assert(link);
+ assert(link->manager);
assert(nexthop);
assert(nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN);
- if (nexthop_get(link->manager, link, nexthop, &existing) < 0) {
- _cleanup_(nexthop_freep) NextHop *tmp = NULL;
+ if (nexthop_get_request(link, nexthop, NULL) >= 0)
+ return 0; /* already requested, skipping. */
- r = nexthop_dup(nexthop, &tmp);
- if (r < 0)
- return r;
+ r = nexthop_dup(nexthop, &tmp);
+ if (r < 0)
+ return r;
+ if (nexthop_get(link, nexthop, &existing) < 0) {
r = nexthop_acquire_id(link->manager, tmp);
if (r < 0)
return r;
+ } else {
+ /* Copy ID */
+ assert(tmp->id == 0 || tmp->id == existing->id);
+ tmp->id = existing->id;
- r = nexthop_add(link->manager, link, tmp);
- if (r < 0)
- return r;
+ /* Copy state for logging below. */
+ tmp->state = existing->state;
+ }
- existing = TAKE_PTR(tmp);
- } else
- existing->source = nexthop->source;
+ if (nexthop_bound_to_link(tmp))
+ tmp->ifindex = link->ifindex;
- log_nexthop_debug(existing, "Requesting", link);
+ log_nexthop_debug(tmp, "Requesting", link->manager);
r = link_queue_request_safe(link, REQUEST_TYPE_NEXTHOP,
- existing, NULL,
+ tmp,
+ nexthop_free,
nexthop_hash_func,
nexthop_compare_func,
nexthop_process_request,
if (r <= 0)
return r;
- nexthop_enter_requesting(existing);
+ nexthop_enter_requesting(tmp);
+ if (existing)
+ nexthop_enter_requesting(existing);
+
+ TAKE_PTR(tmp);
return 1;
}
return 0;
}
-static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *except) {
+static bool nexthop_can_update(const NextHop *assigned_nexthop, const NextHop *requested_nexthop) {
+ assert(assigned_nexthop);
+ assert(assigned_nexthop->manager);
+ assert(requested_nexthop);
+ assert(requested_nexthop->network);
+
+ /* A group nexthop cannot be replaced with a non-group nexthop, and vice versa.
+ * See replace_nexthop_grp() and replace_nexthop_single() in net/ipv4/nexthop.c of the kernel. */
+ if (hashmap_isempty(assigned_nexthop->group) != hashmap_isempty(requested_nexthop->group))
+ return false;
+
+ /* There are several more conditions if we can replace a group nexthop, e.g. hash threshold and
+ * resilience. But, currently we do not support to modify that. Let's add checks for them in the
+ * future when we support to configure them.*/
+
+ /* When a nexthop is replaced with a blackhole nexthop, and a group nexthop has multiple nexthops
+ * including this nexthop, then the kernel refuses to replace the existing nexthop.
+ * So, here, for simplicity, let's unconditionally refuse to replace a non-blackhole nexthop with
+ * a blackhole nexthop. See replace_nexthop() in net/ipv4/nexthop.c of the kernel. */
+ if (!assigned_nexthop->blackhole && requested_nexthop->blackhole)
+ return false;
+
+ return true;
+}
+
+static void link_mark_nexthops(Link *link, bool foreign) {
NextHop *nexthop;
- Link *link;
+ Link *other;
- assert(manager);
+ assert(link);
+ assert(link->manager);
/* First, mark all nexthops. */
- SET_FOREACH(nexthop, manager->nexthops) {
+ HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
/* do not touch nexthop created by the kernel */
if (nexthop->protocol == RTPROT_KERNEL)
continue;
if (!nexthop_exists(nexthop))
continue;
+ /* Ignore nexthops bound to other links. */
+ if (nexthop->ifindex > 0 && nexthop->ifindex != link->ifindex)
+ continue;
+
nexthop_mark(nexthop);
}
/* Then, unmark all nexthops requested by active links. */
- HASHMAP_FOREACH(link, manager->links_by_index) {
- if (link == except)
+ HASHMAP_FOREACH(other, link->manager->links_by_index) {
+ if (!foreign && other == link)
continue;
- if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
continue;
- HASHMAP_FOREACH(nexthop, link->network->nexthops_by_section) {
+ HASHMAP_FOREACH(nexthop, other->network->nexthops_by_section) {
NextHop *existing;
- if (nexthop_get(manager, NULL, nexthop, &existing) >= 0)
- nexthop_unmark(existing);
+ if (nexthop_get(other, nexthop, &existing) < 0)
+ continue;
+
+ if (!nexthop_can_update(existing, nexthop))
+ continue;
+
+ /* Found matching static configuration. Keep the existing nexthop. */
+ nexthop_unmark(existing);
}
}
}
-static int manager_drop_marked_nexthops(Manager *manager) {
+int link_drop_nexthops(Link *link, bool foreign) {
NextHop *nexthop;
int r = 0;
- assert(manager);
+ assert(link);
+ assert(link->manager);
+
+ link_mark_nexthops(link, foreign);
- SET_FOREACH(nexthop, manager->nexthops) {
+ HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
if (!nexthop_is_marked(nexthop))
continue;
return r;
}
-int link_drop_foreign_nexthops(Link *link) {
+void link_foreignize_nexthops(Link *link) {
NextHop *nexthop;
- int r = 0;
assert(link);
assert(link->manager);
- assert(link->network);
-
- /* First, mark all nexthops. */
- SET_FOREACH(nexthop, link->nexthops) {
- /* do not touch nexthop created by the kernel */
- if (nexthop->protocol == RTPROT_KERNEL)
- continue;
-
- /* Do not remove nexthops we configured. */
- if (nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN)
- continue;
-
- /* Ignore nexthops not assigned yet or already removed. */
- if (!nexthop_exists(nexthop))
- continue;
-
- nexthop_mark(nexthop);
- }
-
- /* Then, unmark all nexthops requested by active links. */
- HASHMAP_FOREACH(nexthop, link->network->nexthops_by_section) {
- NextHop *existing;
- if (nexthop_get(NULL, link, nexthop, &existing) >= 0)
- nexthop_unmark(existing);
- }
+ link_mark_nexthops(link, /* foreign = */ false);
- /* Finally, remove all marked rules. */
- SET_FOREACH(nexthop, link->nexthops) {
+ HASHMAP_FOREACH(nexthop, link->manager->nexthops_by_id) {
if (!nexthop_is_marked(nexthop))
continue;
- RET_GATHER(r, nexthop_remove(nexthop));
+ nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN;
}
+}
- manager_mark_nexthops(link->manager, /* foreign = */ true, NULL);
+static int nexthop_update_group(NextHop *nexthop, const struct nexthop_grp *group, size_t size) {
+ _cleanup_hashmap_free_free_ Hashmap *h = NULL;
+ size_t n_group;
+ int r;
- return RET_GATHER(r, manager_drop_marked_nexthops(link->manager));
-}
+ assert(nexthop);
+ assert(group || size == 0);
-int link_drop_managed_nexthops(Link *link) {
- NextHop *nexthop;
- int r = 0;
+ if (size == 0 || size % sizeof(struct nexthop_grp) != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "rtnl: received nexthop message with invalid nexthop group size, ignoring.");
- assert(link);
- assert(link->manager);
+ if ((uintptr_t) group % alignof(struct nexthop_grp) != 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "rtnl: received nexthop message with invalid alignment, ignoring.");
- SET_FOREACH(nexthop, link->nexthops) {
- /* do not touch nexthop created by the kernel */
- if (nexthop->protocol == RTPROT_KERNEL)
- continue;
+ n_group = size / sizeof(struct nexthop_grp);
+ for (size_t i = 0; i < n_group; i++) {
+ _cleanup_free_ struct nexthop_grp *nhg = NULL;
- /* Do not touch addresses managed by kernel or other tools. */
- if (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN)
+ if (group[i].id == 0) {
+ log_debug("rtnl: received nexthop message with invalid ID in group, ignoring.");
continue;
+ }
- /* Ignore nexthops not assigned yet or already removing. */
- if (!nexthop_exists(nexthop))
+ if (group[i].weight > 254) {
+ log_debug("rtnl: received nexthop message with invalid weight in group, ignoring.");
continue;
+ }
- RET_GATHER(r, nexthop_remove(nexthop));
- }
-
- manager_mark_nexthops(link->manager, /* foreign = */ false, link);
-
- return RET_GATHER(r, manager_drop_marked_nexthops(link->manager));
-}
-
-void link_foreignize_nexthops(Link *link) {
- NextHop *nexthop;
-
- assert(link);
-
- SET_FOREACH(nexthop, link->nexthops)
- nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN;
-
- manager_mark_nexthops(link->manager, /* foreign = */ false, link);
+ nhg = newdup(struct nexthop_grp, group + i, 1);
+ if (!nhg)
+ return log_oom();
- SET_FOREACH(nexthop, link->manager->nexthops) {
- if (!nexthop_is_marked(nexthop))
+ r = hashmap_ensure_put(&h, NULL, UINT32_TO_PTR(nhg->id), nhg);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_debug_errno(r, "Failed to store nexthop group, ignoring: %m");
continue;
-
- nexthop->source = NETWORK_CONFIG_SOURCE_FOREIGN;
+ }
+ if (r > 0)
+ TAKE_PTR(nhg);
}
+
+ hashmap_free_free(nexthop->group);
+ nexthop->group = TAKE_PTR(h);
+ return 0;
}
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
- _cleanup_(nexthop_freep) NextHop *tmp = NULL;
_cleanup_free_ void *raw_group = NULL;
- NextHop *nexthop = NULL;
size_t raw_group_size;
- uint32_t ifindex;
uint16_t type;
- Link *link = NULL;
+ uint32_t id, ifindex;
+ NextHop *nexthop = NULL;
+ Request *req = NULL;
+ bool is_new = false;
int r;
assert(rtnl);
return 0;
}
- r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
- if (r < 0 && r != -ENODATA) {
- log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
+ r = sd_netlink_message_read_u32(message, NHA_ID, &id);
+ if (r == -ENODATA) {
+ log_warning_errno(r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
return 0;
- } else if (r >= 0) {
- if (ifindex <= 0) {
- log_warning("rtnl: received nexthop message with invalid ifindex %"PRIu32", ignoring.", ifindex);
- return 0;
- }
-
- r = link_get_by_index(m, ifindex, &link);
- if (r < 0) {
- if (!m->enumerating)
- log_warning("rtnl: received nexthop message for link (%"PRIu32") we do not know about, ignoring", ifindex);
- return 0;
- }
- }
-
- r = nexthop_new(&tmp);
- if (r < 0)
- return log_oom();
-
- r = sd_rtnl_message_get_family(message, &tmp->family);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get nexthop family, ignoring: %m");
+ } else if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
return 0;
- } else if (!IN_SET(tmp->family, AF_UNSPEC, AF_INET, AF_INET6)) {
- log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
+ } else if (id == 0) {
+ log_warning("rtnl: received nexthop message with invalid nexthop ID, ignoring: %m");
return 0;
}
- r = sd_rtnl_message_nexthop_get_protocol(message, &tmp->protocol);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get nexthop protocol, ignoring: %m");
- return 0;
- }
+ (void) nexthop_get_by_id(m, id, &nexthop);
+ (void) nexthop_get_request_by_id(m, id, &req);
- r = sd_rtnl_message_nexthop_get_flags(message, &tmp->flags);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get nexthop flags, ignoring: %m");
- return 0;
- }
+ if (type == RTM_DELNEXTHOP) {
+ if (nexthop) {
+ nexthop_enter_removed(nexthop);
+ log_nexthop_debug(nexthop, "Forgetting removed", m);
+ nexthop_free(nexthop);
+ } else
+ log_nexthop_debug(&(const NextHop) { .id = id }, "Kernel removed unknown", m);
+
+ if (req)
+ nexthop_enter_removed(req->userdata);
- r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group);
- if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
return 0;
- } else if (r >= 0) {
- struct nexthop_grp *group = raw_group;
- size_t n_group;
+ }
- if (raw_group_size == 0 || raw_group_size % sizeof(struct nexthop_grp) != 0) {
- log_link_warning(link, "rtnl: received nexthop message with invalid nexthop group size, ignoring.");
+ /* If we did not know the nexthop, then save it. */
+ if (!nexthop) {
+ r = nexthop_add_new(m, id, &nexthop);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to add received nexthop, ignoring: %m");
return 0;
}
- assert((uintptr_t) group % alignof(struct nexthop_grp) == 0);
+ is_new = true;
+ }
- n_group = raw_group_size / sizeof(struct nexthop_grp);
- for (size_t i = 0; i < n_group; i++) {
- _cleanup_free_ struct nexthop_grp *nhg = NULL;
+ /* Also update information that cannot be obtained through netlink notification. */
+ if (req && req->waiting_reply) {
+ NextHop *n = ASSERT_PTR(req->userdata);
- if (group[i].id == 0) {
- log_link_warning(link, "rtnl: received nexthop message with invalid ID in group, ignoring.");
- return 0;
- }
- if (group[i].weight > 254) {
- log_link_warning(link, "rtnl: received nexthop message with invalid weight in group, ignoring.");
- return 0;
- }
+ nexthop->source = n->source;
+ }
- nhg = newdup(struct nexthop_grp, group + i, 1);
- if (!nhg)
- return log_oom();
+ r = sd_rtnl_message_get_family(message, &nexthop->family);
+ if (r < 0)
+ log_debug_errno(r, "rtnl: could not get nexthop family, ignoring: %m");
- r = hashmap_ensure_put(&tmp->group, NULL, UINT32_TO_PTR(nhg->id), nhg);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to store nexthop group, ignoring: %m");
- return 0;
- }
- if (r > 0)
- TAKE_PTR(nhg);
- }
- }
+ r = sd_rtnl_message_nexthop_get_protocol(message, &nexthop->protocol);
+ if (r < 0)
+ log_debug_errno(r, "rtnl: could not get nexthop protocol, ignoring: %m");
- if (tmp->family != AF_UNSPEC) {
- r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, tmp->family, &tmp->gw);
- if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
- return 0;
- }
+ r = sd_rtnl_message_nexthop_get_flags(message, &nexthop->flags);
+ if (r < 0)
+ log_debug_errno(r, "rtnl: could not get nexthop flags, ignoring: %m");
+
+ r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group);
+ if (r == -ENODATA)
+ nexthop->group = hashmap_free_free(nexthop->group);
+ else if (r < 0)
+ log_debug_errno(r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
+ else
+ (void) nexthop_update_group(nexthop, raw_group, raw_group_size);
+
+ if (nexthop->family != AF_UNSPEC) {
+ r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, nexthop->family, &nexthop->gw);
+ if (r == -ENODATA)
+ nexthop->gw = IN_ADDR_NULL;
+ else if (r < 0)
+ log_debug_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
}
r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE);
- if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
- return 0;
- }
- tmp->blackhole = r;
+ if (r < 0)
+ log_debug_errno(r, "rtnl: could not get NHA_BLACKHOLE attribute, ignoring: %m");
+ else
+ nexthop->blackhole = r;
- r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
- if (r == -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received nexthop message without NHA_ID attribute, ignoring: %m");
- return 0;
- } else if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
- return 0;
- } else if (tmp->id == 0) {
- log_link_warning(link, "rtnl: received nexthop message with invalid nexthop ID, ignoring: %m");
- return 0;
- }
+ r = sd_netlink_message_read_u32(message, NHA_OIF, &ifindex);
+ if (r == -ENODATA)
+ nexthop->ifindex = 0;
+ else if (r < 0)
+ log_debug_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
+ else if (ifindex > INT32_MAX)
+ log_debug_errno(r, "rtnl: received invalid NHA_OIF attribute, ignoring: %m");
+ else
+ nexthop->ifindex = (int) ifindex;
/* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not
* set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */
- if (!nexthop_owned_by_link(tmp))
- link = NULL;
+ if (!nexthop_bound_to_link(nexthop))
+ nexthop->ifindex = 0;
- (void) nexthop_get(m, link, tmp, &nexthop);
-
- switch (type) {
- case RTM_NEWNEXTHOP:
- if (nexthop) {
- nexthop->flags = tmp->flags;
- nexthop_enter_configured(nexthop);
- log_nexthop_debug(tmp, "Received remembered", link);
- } else {
- nexthop_enter_configured(tmp);
- log_nexthop_debug(tmp, "Remembering", link);
-
- r = nexthop_add(m, link, tmp);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not remember foreign nexthop, ignoring: %m");
- return 0;
- }
-
- TAKE_PTR(tmp);
- }
-
- break;
- case RTM_DELNEXTHOP:
- if (nexthop) {
- nexthop_enter_removed(nexthop);
- if (nexthop->state == 0) {
- log_nexthop_debug(nexthop, "Forgetting", link);
- nexthop_free(nexthop);
- } else
- log_nexthop_debug(nexthop, "Removed", link);
- } else
- log_nexthop_debug(tmp, "Kernel removed unknown", link);
- break;
-
- default:
- assert_not_reached();
- }
+ nexthop_enter_configured(nexthop);
+ if (req)
+ nexthop_enter_configured(req->userdata);
+ log_nexthop_debug(nexthop, is_new ? "Remembering" : "Received remembered", m);
return 1;
}
if (section_is_invalid(nh->section))
return -EINVAL;
+ if (!nh->network->manager->manage_foreign_nexthops && nh->id == 0)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: [NextHop] section without specifying Id= is not supported "
+ "if ManageForeignNextHops=no is set in networkd.conf. "
+ "Ignoring [NextHop] section from line %u.",
+ nh->section->filename, nh->section->line);
+
if (!hashmap_isempty(nh->group)) {
if (in_addr_is_set(nh->family, &nh->gw))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
typedef struct NextHop {
Network *network;
Manager *manager;
- Link *link;
ConfigSection *section;
NetworkConfigSource source;
NetworkConfigState state;
uint8_t protocol;
-
+ int ifindex;
uint32_t id;
bool blackhole;
int family;
void network_drop_invalid_nexthops(Network *network);
-int link_drop_managed_nexthops(Link *link);
-int link_drop_foreign_nexthops(Link *link);
+int link_drop_nexthops(Link *link, bool foreign);
+static inline int link_drop_foreign_nexthops(Link *link) {
+ return link_drop_nexthops(link, /* foreign = */ true);
+}
+static inline int link_drop_managed_nexthops(Link *link) {
+ return link_drop_nexthops(link, /* foreign = */ false);
+}
void link_foreignize_nexthops(Link *link);
int link_request_static_nexthops(Link *link, bool only_ipv4);
-int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret);
+int nexthop_get_by_id(Manager *manager, uint32_t id, NextHop **ret);
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(NextHop, nexthop);
assert(req);
assert(state);
- siphash24_compress_boolean(req->link, state);
- if (req->link)
- siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state);
-
siphash24_compress(&req->type, sizeof(req->type), state);
+ if (req->type != REQUEST_TYPE_NEXTHOP) {
+ siphash24_compress_boolean(req->link, state);
+ if (req->link)
+ siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state);
+ }
+
siphash24_compress(&req->hash_func, sizeof(req->hash_func), state);
siphash24_compress(&req->compare_func, sizeof(req->compare_func), state);
assert(a);
assert(b);
- r = CMP(!!a->link, !!b->link);
+ r = CMP(a->type, b->type);
if (r != 0)
return r;
- if (a->link) {
- r = CMP(a->link->ifindex, b->link->ifindex);
+ if (a->type != REQUEST_TYPE_NEXTHOP) {
+ r = CMP(!!a->link, !!b->link);
if (r != 0)
return r;
- }
- r = CMP(a->type, b->type);
- if (r != 0)
- return r;
+ if (a->link) {
+ r = CMP(a->link->ifindex, b->link->ifindex);
+ if (r != 0)
+ return r;
+ }
+ }
r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func));
if (r != 0)
struct nexthop_grp *nhg;
NextHop *nh;
- r = manager_get_nexthop_by_id(manager, route->nexthop_id, &nh);
+ r = nexthop_get_by_id(manager, route->nexthop_id, &nh);
if (r < 0)
return r;
return r;
route_apply_nexthop(c->routes[0], nh, UINT8_MAX);
- c->links[0] = nh->link;
+ (void) link_get_by_index(manager, nh->ifindex, c->links);
*ret = TAKE_PTR(c);
return 1;
HASHMAP_FOREACH(nhg, nh->group) {
NextHop *h;
- r = manager_get_nexthop_by_id(manager, nhg->id, &h);
+ r = nexthop_get_by_id(manager, nhg->id, &h);
if (r < 0)
return r;
return r;
route_apply_nexthop(c->routes[i], h, nhg->weight);
- c->links[i] = h->link;
+ (void) link_get_by_index(manager, h->ifindex, c->links + i);
i++;
}
struct nexthop_grp *nhg;
NextHop *nh;
- if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
+ if (nexthop_get_by_id(link->manager, route->nexthop_id, &nh) < 0)
return false;
if (!nexthop_exists(nh))
HASHMAP_FOREACH(nhg, nh->group) {
NextHop *g;
- if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0)
+ if (nexthop_get_by_id(link->manager, nhg->id, &g) < 0)
return false;
if (!nexthop_exists(g))
break;
}
case REQUEST_TYPE_SET_LINK_MTU: {
- Request req_ipoib = {
- .link = link,
- .type = REQUEST_TYPE_SET_LINK_IPOIB,
- };
+ if (ordered_set_contains(link->manager->request_queue,
+ &(const Request) {
+ .link = link,
+ .type = REQUEST_TYPE_SET_LINK_IPOIB,
+ }))
+ return false;
- return !ordered_set_contains(link->manager->request_queue, &req_ipoib);
+ /* Changing FD mode may affect MTU. */
+ if (ordered_set_contains(link->manager->request_queue,
+ &(const Request) {
+ .link = link,
+ .type = REQUEST_TYPE_SET_LINK_CAN,
+ }))
+ return false;
}
default:
break;
int link_request_to_set_mtu(Link *link, uint32_t mtu) {
const char *origin;
- uint32_t min_mtu;
+ uint32_t min_mtu, max_mtu;
Request *req;
int r;
mtu = min_mtu;
}
- if (mtu > link->max_mtu) {
+ max_mtu = link->max_mtu;
+ if (link->iftype == ARPHRD_CAN)
+ /* The maximum MTU may be changed when FD mode is changed.
+ * See https://docs.kernel.org/networking/can.html#can-fd-flexible-data-rate-driver-support
+ * MTU = 16 (CAN_MTU) => Classical CAN device
+ * MTU = 72 (CANFD_MTU) => CAN FD capable device
+ * So, even if the current maximum is 16, we should not reduce the requested value now. */
+ max_mtu = MAX(max_mtu, 72u);
+
+ if (mtu > max_mtu) {
log_link_warning(link, "Reducing the requested MTU %"PRIu32" to the interface's maximum MTU %"PRIu32".",
- mtu, link->max_mtu);
- mtu = link->max_mtu;
+ mtu, max_mtu);
+ mtu = max_mtu;
}
if (link->mtu == mtu)
#include <netinet/in.h>
#include <linux/if.h>
+#include <linux/if_arp.h>
#include "missing_network.h"
#include "networkd-link.h"
#include "string-table.h"
#include "sysctl-util.h"
-static int link_update_ipv6_sysctl(Link *link) {
+static bool link_is_configured_for_family(Link *link, int family) {
assert(link);
+ if (!link->network)
+ return false;
+
if (link->flags & IFF_LOOPBACK)
+ return false;
+
+ /* CAN devices do not support IP layer. Most of the functions below are never called for CAN devices,
+ * but link_set_ipv6_mtu() may be called after setting interface MTU, and warn about the failure. For
+ * safety, let's unconditionally check if the interface is not a CAN device. */
+ if (IN_SET(family, AF_INET, AF_INET6) && link->iftype == ARPHRD_CAN)
+ return false;
+
+ if (family == AF_INET6 && !socket_ipv6_is_supported())
+ return false;
+
+ return true;
+}
+
+static int link_update_ipv6_sysctl(Link *link) {
+ assert(link);
+
+ if (!link_is_configured_for_family(link, AF_INET6))
return 0;
if (!link_ipv6_enabled(link))
static int link_set_proxy_arp(Link *link) {
assert(link);
- if (link->flags & IFF_LOOPBACK)
- return 0;
-
- if (!link->network)
+ if (!link_is_configured_for_family(link, AF_INET))
return 0;
if (link->network->proxy_arp < 0)
assert(link);
assert(IN_SET(family, AF_INET, AF_INET6));
- if (family == AF_INET6 && !socket_ipv6_is_supported())
- return false;
-
- if (link->flags & IFF_LOOPBACK)
- return false;
-
- if (!link->network)
+ if (!link_is_configured_for_family(link, family))
return false;
return link->network->ip_forward & (family == AF_INET ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_IPV6);
static int link_set_ipv4_rp_filter(Link *link) {
assert(link);
- if (link->flags & IFF_LOOPBACK)
- return 0;
-
- if (!link->network)
+ if (!link_is_configured_for_family(link, AF_INET))
return 0;
if (link->network->ipv4_rp_filter < 0)
assert(link);
assert(link->manager);
- if (!socket_ipv6_is_supported())
- return 0;
-
- if (link->flags & IFF_LOOPBACK)
- return 0;
-
- if (!link->network)
+ if (!link_is_configured_for_family(link, AF_INET6))
return 0;
val = link->network->ipv6_privacy_extensions;
static int link_set_ipv6_accept_ra(Link *link) {
assert(link);
- /* Make this a NOP if IPv6 is not available */
- if (!socket_ipv6_is_supported())
- return 0;
-
- if (link->flags & IFF_LOOPBACK)
- return 0;
-
- if (!link->network)
+ if (!link_is_configured_for_family(link, AF_INET6))
return 0;
return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0");
static int link_set_ipv6_dad_transmits(Link *link) {
assert(link);
- /* Make this a NOP if IPv6 is not available */
- if (!socket_ipv6_is_supported())
- return 0;
-
- if (link->flags & IFF_LOOPBACK)
- return 0;
-
- if (!link->network)
+ if (!link_is_configured_for_family(link, AF_INET6))
return 0;
if (link->network->ipv6_dad_transmits < 0)
static int link_set_ipv6_hop_limit(Link *link) {
assert(link);
- /* Make this a NOP if IPv6 is not available */
- if (!socket_ipv6_is_supported())
- return 0;
-
- if (link->flags & IFF_LOOPBACK)
- return 0;
-
- if (!link->network)
+ if (!link_is_configured_for_family(link, AF_INET6))
return 0;
if (link->network->ipv6_hop_limit <= 0)
assert(link);
- if (!socket_ipv6_is_supported())
- return 0;
-
- if (link->flags & IFF_LOOPBACK)
- return 0;
-
- if (!link->network)
+ if (!link_is_configured_for_family(link, AF_INET6))
return 0;
if (link->network->ipv6_proxy_ndp >= 0)
assert(link);
- /* Make this a NOP if IPv6 is not available */
- if (!socket_ipv6_is_supported())
- return 0;
-
- if (link->flags & IFF_LOOPBACK)
- return 0;
-
- if (!link->network)
+ if (!link_is_configured_for_family(link, AF_INET6))
return 0;
if (link->network->ipv6_mtu == 0)
static int link_set_ipv4_accept_local(Link *link) {
assert(link);
- if (link->flags & IFF_LOOPBACK)
+ if (!link_is_configured_for_family(link, AF_INET))
return 0;
if (link->network->ipv4_accept_local < 0)
static int link_set_ipv4_route_localnet(Link *link) {
assert(link);
- if (link->flags & IFF_LOOPBACK)
+ if (!link_is_configured_for_family(link, AF_INET))
return 0;
if (link->network->ipv4_route_localnet < 0)
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0);
}
+static int link_set_ipv4_promote_secondaries(Link *link) {
+ assert(link);
+
+ if (!link_is_configured_for_family(link, AF_INET))
+ return 0;
+
+ /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
+ * changes between leases. The kernel will remove all secondary IP addresses of an interface
+ * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
+ * secondary IP and when the primary one expires it relies on the kernel to promote the
+ * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
+ return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true);
+}
+
int link_set_sysctl(Link *link) {
int r;
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m");
- /* If promote_secondaries is not set, DHCP will work only as long as the IP address does not
- * changes between leases. The kernel will remove all secondary IP addresses of an interface
- * otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
- * secondary IP and when the primary one expires it relies on the kernel to promote the
- * secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
- r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true);
+ r = link_set_ipv4_promote_secondaries(link);
if (r < 0)
log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m");
#SpeedMeterIntervalSec=10sec
#ManageForeignRoutingPolicyRules=yes
#ManageForeignRoutes=yes
+#ManageForeignNextHops=yes
#RouteTable=
#IPv6PrivacyExtensions=no
"cgroup.stat",
"cgroup.subtree_control",
"cgroup.threads",
+ "memory.oom.group",
+ "memory.reclaim",
"notify_on_release",
"tasks")
if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0)
assert_cc(ETH_ALEN <= sizeof(result));
memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
- /* see eth_random_addr in the kernel */
- mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
- mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
+ ether_addr_mark_random(mac);
return 0;
}
if (r < 0)
return bus_log_parse_error(r);
- r = bus_wait_for_jobs_one(w, object, false, NULL);
+ r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL);
if (r < 0)
return r;
static size_t arg_n_extra_nodes = 0;
static char **arg_sysctl = NULL;
static ConsoleMode arg_console_mode = _CONSOLE_MODE_INVALID;
-static MachineCredential *arg_credentials = NULL;
-static size_t arg_n_credentials = 0;
+static MachineCredentialContext arg_credentials = {};
static char **arg_bind_user = NULL;
static bool arg_suppress_sync = false;
static char *arg_settings_filename = NULL;
#if HAVE_SECCOMP
STATIC_DESTRUCTOR_REGISTER(arg_seccomp, seccomp_releasep);
#endif
+STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
STATIC_DESTRUCTOR_REGISTER(arg_cpu_set, cpu_set_reset);
STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep);
break;
case ARG_SET_CREDENTIAL:
- r = machine_credential_set(&arg_credentials, &arg_n_credentials, optarg);
+ r = machine_credential_set(&arg_credentials, optarg);
if (r < 0)
return r;
break;
case ARG_LOAD_CREDENTIAL:
- r = machine_credential_load(&arg_credentials, &arg_n_credentials, optarg);
+ r = machine_credential_load(&arg_credentials, optarg);
if (r < 0)
return r;
if (mknod(to, st.st_mode, st.st_rdev) < 0) {
/* Explicitly warn the user when /dev is already populated. */
if (errno == EEXIST)
- log_notice("%s/dev is pre-mounted and pre-populated. If a pre-mounted /dev is provided it needs to be an unpopulated file system.", dest);
+ log_notice("%s/dev/ is pre-mounted and pre-populated. If a pre-mounted /dev/ is provided it needs to be an unpopulated file system.", dest);
if (errno != EPERM)
return log_error_errno(errno, "mknod(%s) failed: %m", to);
const char *q;
int r;
- if (arg_n_credentials <= 0)
+ if (arg_credentials.n_credentials == 0)
return 0;
r = userns_mkdir(root, "/run/host", 0755, 0, 0);
if (r < 0)
return r;
- for (size_t i = 0; i < arg_n_credentials; i++) {
+ FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) {
_cleanup_free_ char *j = NULL;
_cleanup_close_ int fd = -EBADF;
- j = path_join(q, arg_credentials[i].id);
+ j = path_join(q, cred->id);
if (!j)
return log_oom();
if (fd < 0)
return log_error_errno(errno, "Failed to create credential file %s: %m", j);
- r = loop_write(fd, arg_credentials[i].data, arg_credentials[i].size);
+ r = loop_write(fd, cred->data, cred->size);
if (r < 0)
return log_error_errno(r, "Failed to write credential to file %s: %m", j);
if (asprintf(envp + n_env++, "container_uuid=%s", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0)
return log_oom();
- if (fdset_size(fds) > 0) {
+ if (!fdset_isempty(fds)) {
r = fdset_cloexec(fds, false);
if (r < 0)
return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
if (asprintf(envp + n_env++, "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
return log_oom();
- if (arg_n_credentials > 0) {
+ if (arg_credentials.n_credentials > 0) {
envp[n_env] = strdup("CREDENTIALS_DIRECTORY=/run/host/credentials");
if (!envp[n_env])
return log_oom();
* don't read the other limits from PID 1 but prefer the static table above. */
};
- int rl;
+ int rl, r;
for (rl = 0; rl < _RLIMIT_MAX; rl++) {
/* Let's only fill in what the user hasn't explicitly configured anyway */
if (IN_SET(rl, RLIMIT_NPROC, RLIMIT_SIGPENDING)) {
/* For these two let's read the limits off PID 1. See above for an explanation. */
- if (prlimit(1, rl, NULL, &buffer) < 0)
- return log_error_errno(errno, "Failed to read resource limit RLIMIT_%s of PID 1: %m", rlimit_to_string(rl));
+ r = pid_getrlimit(1, rl, &buffer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read resource limit RLIMIT_%s of PID 1: %m", rlimit_to_string(rl));
v = &buffer;
} else if (rl == RLIMIT_NOFILE) {
/* Always take an exclusive lock on our own ephemeral copy. */
r = image_path_lock(np, LOCK_EX|LOCK_NB, &tree_global_lock, &tree_local_lock);
if (r < 0) {
- r = log_error_errno(r, "Failed to create image lock: %m");
+ log_error_errno(r, "Failed to create image lock: %m");
goto finish;
}
} else {
r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
if (r == -EBUSY) {
- r = log_error_errno(r, "Disk image %s is currently busy.", arg_image);
+ log_error_errno(r, "Disk image %s is currently busy.", arg_image);
goto finish;
}
if (r < 0) {
- r = log_error_errno(r, "Failed to create image lock: %m");
+ log_error_errno(r, "Failed to create image lock: %m");
goto finish;
}
expose_port_free_all(arg_expose_ports);
rlimit_free_all(arg_rlimit);
device_node_array_free(arg_extra_nodes, arg_n_extra_nodes);
- machine_credential_free_all(arg_credentials, arg_n_credentials);
if (r < 0)
return r;
if (r < 0)
log_debug_errno(r, "Failed to set user.oomd_kill on kill: %m");
- return set_size(pids_killed) != 0;
+ return !set_isempty(pids_killed);
}
typedef void (*dump_candidate_func)(const OomdCGroupContext *ctx, FILE *f, const char *prefix);
return log_error_errno(r, "Failed to open main block device " DEVNUM_FORMAT_STR ": %m",
DEVNUM_FORMAT_VAL(main_devno));
- if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0)
- return log_error_errno(errno, "Failed to query size of \"%s\" (before resize): %m",
+ r = blockdev_get_device_size(main_devfd, &size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query size of \"%s\" (before resize): %m",
main_devpath);
log_debug("%s is %"PRIu64" bytes", main_devpath, size);
if (r < 0)
return log_error_errno(r, "crypt_resize() of %s failed: %m", devpath);
- if (ioctl(main_devfd, BLKGETSIZE64, &size) != 0)
- log_warning_errno(errno, "Failed to query size of \"%s\" (after resize): %m",
- devpath);
+ r = blockdev_get_device_size(main_devfd, &size);
+ if (r < 0)
+ log_warning_errno(r, "Failed to query size of \"%s\" (after resize): %m", devpath);
else
log_debug("%s is now %"PRIu64" bytes", main_devpath, size);
return log_error_errno(r, "Failed to open block device " DEVNUM_FORMAT_STR ": %m",
DEVNUM_FORMAT_VAL(devno));
- if (ioctl(devfd, BLKGETSIZE64, &size) != 0)
- return log_error_errno(errno, "Failed to query size of \"%s\": %m", devpath);
+ r = blockdev_get_device_size(devfd, &size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query size of \"%s\": %m", devpath);
log_debug("Resizing \"%s\" to %"PRIu64" bytes...", arg_target, size);
if (S_ISREG(st.st_mode))
size = st.st_size;
else if (S_ISBLK(st.st_mode)) {
- if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
- return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
+ r = blockdev_get_device_size(source_fd, &size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine size of block device to copy from: %m");
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", opened);
assert(loop_device);
- if (ioctl(*fd, BLKGETSIZE64, ¤t_size) < 0)
- return log_error_errno(errno, "Failed to determine size of block device %s: %m", node);
+ r = blockdev_get_device_size(*fd, ¤t_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine size of block device %s: %m", node);
} else {
r = stat_verify_regular(&st);
if (r < 0)
return r;
if (!TPM2_PCR_INDEX_VALID(p.pcr))
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "pcr")));
+ return varlink_error_invalid_parameter_name(link, "pcr");
if (p.text) {
/* Specifying both the text string and the binary data is not allowed */
if (p.data.iov_base)
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "data")));
+ return varlink_error_invalid_parameter_name(link, "data");
r = extend_now(p.pcr, p.text, strlen(p.text), _TPM2_USERSPACE_EVENT_TYPE_INVALID);
} else if (p.data.iov_base)
r = extend_now(p.pcr, p.data.iov_base, p.data.iov_len, _TPM2_USERSPACE_EVENT_TYPE_INVALID);
else
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "text")));
+ return varlink_error_invalid_parameter_name(link, "text");
if (r < 0)
return r;
#include "blockdev-util.h"
#include "build.h"
#include "chase.h"
+#include "color-util.h"
#include "conf-files.h"
#include "efi-api.h"
#include "env-util.h"
return event_log_validate_fully_recognized(el);
}
-static void hsv_to_rgb(
- double h, double s, double v,
- uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) {
-
- double c, x, m, r, g, b;
-
- assert(s >= 0 && s <= 100);
- assert(v >= 0 && v <= 100);
- assert(ret_r);
- assert(ret_g);
- assert(ret_b);
-
- c = (s / 100.0) * (v / 100.0);
- x = c * (1 - fabs(fmod(h / 60.0, 2) - 1));
- m = (v / 100) - c;
-
- if (h >= 0 && h < 60)
- r = c, g = x, b = 0.0;
- else if (h >= 60 && h < 120)
- r = x, g = c, b = 0.0;
- else if (h >= 120 && h < 180)
- r = 0.0, g = c, b = x;
- else if (h >= 180 && h < 240)
- r = 0.0, g = x, b = c;
- else if (h >= 240 && h < 300)
- r = x, g = 0.0, b = c;
- else
- r = c, g = 0.0, b = x;
-
- *ret_r = (uint8_t) ((r + m) * 255);
- *ret_g = (uint8_t) ((g + m) * 255);
- *ret_b = (uint8_t) ((b + m) * 255);
-}
-
#define ANSI_TRUE_COLOR_MAX (7U + 3U + 1U + 3U + 1U + 3U + 2U)
static const char *ansi_true_color(uint8_t r, uint8_t g, uint8_t b, char ret[static ANSI_TRUE_COLOR_MAX]) {
break;
case ARG_PCRLOCK:
- if (isempty(optarg) || streq(optarg, "-"))
+ if (empty_or_dash(optarg))
arg_pcrlock_path = mfree(arg_pcrlock_path);
else {
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_pcrlock_path);
break;
case ARG_POLICY:
- if (isempty(optarg) || streq(optarg, "-"))
+ if (empty_or_dash(optarg))
arg_policy_path = mfree(arg_policy_path);
else {
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_policy_path);
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.portable1.attach-images",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.portable1.manage-images",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.portable1.attach-images",
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
if (mode == BUS_IMAGE_AUTHENTICATE_ALL) {
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
polkit_action,
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
if (mode == BUS_IMAGE_AUTHENTICATE_BY_PATH) {
r = bus_verify_polkit_async(
message,
- CAP_SYS_ADMIN,
polkit_action,
- NULL,
- false,
- UID_INVALID,
+ /* details= */ NULL,
&m->polkit_registry,
error);
if (r < 0)
DNS_TYPE_RRSIG);
}
-bool dns_type_is_zone_transer(uint16_t type) {
+bool dns_type_is_zone_transfer(uint16_t type) {
/* Zone transfers, either normal or incremental */
bool dns_type_may_wildcard(uint16_t type);
bool dns_type_apex_only(uint16_t type);
bool dns_type_needs_authentication(uint16_t type);
-bool dns_type_is_zone_transer(uint16_t type);
+bool dns_type_is_zone_transfer(uint16_t type);
int dns_type_to_af(uint16_t type);
bool dns_class_is_pseudo(uint16_t class);
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))
+ if (dns_type_is_zone_transfer(type))
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);
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_SYS_ADMIN,
- "org.freedesktop.resolve1.register-service",
- NULL, false, UID_INVALID,
- &m->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.register-service",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
while ((key = hashmap_first_key(c->by_key)))
dns_cache_remove_by_key(c, key);
- assert(hashmap_size(c->by_key) == 0);
- assert(prioq_size(c->by_expiry) == 0);
+ assert(hashmap_isempty(c->by_key));
+ assert(prioq_isempty(c->by_expiry));
c->by_key = hashmap_free(c->by_key);
c->by_expiry = prioq_free(c->by_expiry);
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
DnsCacheItem *i;
- if (prioq_size(c->by_expiry) <= 0)
+ if (prioq_isempty(c->by_expiry))
break;
if (prioq_size(c->by_expiry) + add < CACHE_MAX)
}
static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
const char *n;
int r;
while (!dns_name_is_root(name)) {
const char *z = name;
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
size_t n = 0;
if (allow_compression)
assert(scope);
- if (hashmap_size(scope->manager->dnssd_services) == 0)
+ if (hashmap_isempty(scope->manager->dnssd_services))
return 0;
scope->announced = false;
return;
}
- if (dns_type_is_zone_transer(dns_question_first_key(p->question)->type)) {
+ if (dns_type_is_zone_transfer(dns_question_first_key(p->question)->type)) {
log_debug("Got request for zone transfer, refusing.");
dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false);
return;
if (r == 0)
continue;
- return FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+ return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
}
return true;
/* We found the transaction that was supposed to find the SOA RR for us. It was
* successful, but found no RR for us. This means we are not at a zone cut. In this
* case, we require authentication if the SOA lookup was authenticated too. */
- return FLAGS_SET(t->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
+ return FLAGS_SET(dt->answer_query_flags, SD_RESOLVED_AUTHENTICATED);
}
return true;
* trust anchor defined at all. This enables easy overriding
* of negative trust anchors. */
- if (set_size(d->negative_by_name) > 0)
+ if (!set_isempty(d->negative_by_name))
return 0;
r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
while ((i = hashmap_first(z->by_key)))
dns_zone_item_remove_and_free(z, i);
- assert(hashmap_size(z->by_key) == 0);
- assert(hashmap_size(z->by_name) == 0);
+ assert(hashmap_isempty(z->by_key));
+ assert(hashmap_isempty(z->by_name));
z->by_key = hashmap_free(z->by_key);
z->by_name = hashmap_free(z->by_name);
m = s->manager;
- r = bus_verify_polkit_async(message, CAP_SYS_ADMIN,
- "org.freedesktop.resolve1.unregister-service",
- NULL, false, s->originator,
- &m->polkit_registry, error);
+ r = bus_verify_polkit_async_full(
+ message,
+ "org.freedesktop.resolve1.unregister-service",
+ /* details= */ NULL,
+ /* interactive= */ false,
+ /* good_user= */ s->originator,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-dns-servers",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-dns-servers",
+ /* details= */ NULL,
+ &l->manager->polkit_registry, error);
if (r < 0)
goto finalize;
if (r == 0) {
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-domains",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-domains",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-default-route",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-default-route",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-llmnr",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-llmnr",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-mdns",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-mdns",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSOverTLS setting: %s", dns_over_tls);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-dns-over-tls",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-dns-over-tls",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-dnssec",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-dnssec",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
return -ENOMEM;
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.set-dnssec-negative-trust-anchors",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
if (r < 0)
return r;
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.resolve1.revert",
- NULL, true, UID_INVALID,
- &l->manager->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.resolve1.revert",
+ /* details= */ NULL,
+ &l->manager->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) {
_cleanup_free_ char *h = NULL, *n = NULL, *m = NULL;
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
const char *p;
int r;
#elif HAVE_LIBIDN
int k;
#endif
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
const char *p, *decoded;
int r;
'sources' : files('run.c'),
},
]
+
+install_emptydir(bindir)
+
+meson.add_install_script(sh, '-c',
+ ln_s.format(bindir / 'systemd-run',
+ bindir / 'uid0'))
+
+custom_target(
+ 'systemd-uid0',
+ input : 'systemd-uid0.in',
+ output : 'systemd-uid0',
+ command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
+ install : pamconfdir != 'no',
+ install_dir : pamconfdir)
#include "bus-unit-util.h"
#include "bus-wait-for-jobs.h"
#include "calendarspec.h"
+#include "color-util.h"
#include "env-util.h"
#include "escape.h"
#include "exit-status.h"
static char *arg_working_directory = NULL;
static bool arg_shell = false;
static char **arg_cmdline = NULL;
+static char *arg_exec_path = NULL;
+static bool arg_ignore_failure = false;
+static char *arg_background = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_timer_property, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
if (r < 0)
return log_oom();
- printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
- "\n%sRun the specified command in a transient scope or service.%s\n\n"
+ printf("%1$s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
+ "\n%5$sRun the specified command in a transient scope or service.%6$s\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-ask-password Do not prompt for password\n"
" -p --property=NAME=VALUE Set service or scope unit property\n"
" --description=TEXT Description for unit\n"
" --slice=SLICE Run in the specified slice\n"
- " --slice-inherit Inherit the slice\n"
+ " --slice-inherit Inherit the slice from the caller\n"
" --expand-environment=BOOL Control expansion of environment variables\n"
" --no-block Do not wait until operation finished\n"
" -r --remain-after-exit Leave service around until explicitly stopped\n"
" -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n"
" -q --quiet Suppress information messages during runtime\n"
" -G --collect Unload unit after it ran, even when failed\n"
- " -S --shell Invoke a $SHELL interactively\n\n"
- "Path options:\n"
- " --path-property=NAME=VALUE Set path unit property\n\n"
- "Socket options:\n"
- " --socket-property=NAME=VALUE Set socket unit property\n\n"
- "Timer options:\n"
+ " -S --shell Invoke a $SHELL interactively\n"
+ " --ignore-failure Ignore the exit status of the invoked process\n"
+ " --background=COLOR Set ANSI color for background\n"
+ "\n%3$sPath options:%4$s\n"
+ " --path-property=NAME=VALUE Set path unit property\n"
+ "\n%3$sSocket options:%4$s\n"
+ " --socket-property=NAME=VALUE Set socket unit property\n"
+ "\n%3$sTimer options:%4$s\n"
" --on-active=SECONDS Run after SECONDS delay\n"
" --on-boot=SECONDS Run SECONDS after machine was booted up\n"
" --on-startup=SECONDS Run SECONDS after systemd activation\n"
" --on-timezone-change Run when the timezone changes\n"
" --on-clock-change Run when the realtime clock jumps\n"
" --timer-property=NAME=VALUE Set timer unit property\n"
+ "\nSee the %2$s for details.\n",
+ program_invocation_short_name,
+ link,
+ ansi_underline(), ansi_normal(),
+ ansi_highlight(), ansi_normal());
+
+ return 0;
+}
+
+static int help_sudo_mode(void) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("uid0", "1", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n"
+ "\n%sElevate privileges interactively.%s\n\n"
+ " -h --help Show this help\n"
+ " -V --version Show package version\n"
+ " --no-ask-password Do not prompt for password\n"
+ " --machine=CONTAINER Operate on local container\n"
+ " --unit=UNIT Run under the specified unit name\n"
+ " --property=NAME=VALUE Set service or scope unit property\n"
+ " --description=TEXT Description for unit\n"
+ " --slice=SLICE Run in the specified slice\n"
+ " --slice-inherit Inherit the slice\n"
+ " -u --user=USER Run as system user\n"
+ " -g --group=GROUP Run as system group\n"
+ " --nice=NICE Nice level\n"
+ " -D --chdir=PATH Set working directory\n"
+ " --setenv=NAME[=VALUE] Set environment variable\n"
+ " --background=COLOR Set ANSI color for background\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
return 0;
}
+static char **make_login_shell_cmdline(const char *shell) {
+ _cleanup_free_ char *argv0 = NULL;
+
+ assert(shell);
+
+ argv0 = strjoin("-", shell); /* The - is how shells determine if they shall be consider login shells */
+ if (!argv0)
+ return NULL;
+
+ return strv_new(argv0);
+}
+
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_WAIT,
ARG_WORKING_DIRECTORY,
ARG_SHELL,
+ ARG_IGNORE_FAILURE,
+ ARG_BACKGROUND,
};
static const struct option options[] = {
{ "working-directory", required_argument, NULL, ARG_WORKING_DIRECTORY },
{ "same-dir", no_argument, NULL, 'd' },
{ "shell", no_argument, NULL, 'S' },
+ { "ignore-failure", no_argument, NULL, ARG_IGNORE_FAILURE },
+ { "background", no_argument, NULL, ARG_BACKGROUND },
{},
};
break;
case ARG_DESCRIPTION:
- r = free_and_strdup(&arg_description, optarg);
+ r = free_and_strdup_warn(&arg_description, optarg);
if (r < 0)
return r;
break;
arg_shell = true;
break;
+ case ARG_IGNORE_FAILURE:
+ arg_ignore_failure = true;
+ break;
+
+ case ARG_BACKGROUND:
+ r = free_and_strdup_warn(&arg_background, optarg);
+ if (r < 0)
+ return r;
+ break;
+
case '?':
return -EINVAL;
return log_error_errno(r, "Failed to get current working directory: %m");
}
- if (!arg_service_type) {
- arg_service_type = strdup("exec");
- if (!arg_service_type)
- return log_oom();
- }
+ if (!arg_service_type)
+ arg_service_type = "exec";
arg_wait = true;
}
return 1;
}
+static int parse_argv_sudo_mode(int argc, char *argv[]) {
+
+ enum {
+ ARG_NO_ASK_PASSWORD = 0x100,
+ ARG_HOST,
+ ARG_MACHINE,
+ ARG_UNIT,
+ ARG_PROPERTY,
+ ARG_DESCRIPTION,
+ ARG_SLICE,
+ ARG_SLICE_INHERIT,
+ ARG_NICE,
+ ARG_SETENV,
+ ARG_BACKGROUND,
+ };
+
+ /* If invoked as "uid0" binary, let's expose a more sudo-like interface. We add various extensions
+ * though (but limit the extension to long options). */
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+ { "machine", required_argument, NULL, ARG_MACHINE },
+ { "unit", required_argument, NULL, ARG_UNIT },
+ { "property", required_argument, NULL, ARG_PROPERTY },
+ { "description", required_argument, NULL, ARG_DESCRIPTION },
+ { "slice", required_argument, NULL, ARG_SLICE },
+ { "slice-inherit", no_argument, NULL, ARG_SLICE_INHERIT },
+ { "user", required_argument, NULL, 'u' },
+ { "group", required_argument, NULL, 'g' },
+ { "nice", required_argument, NULL, ARG_NICE },
+ { "chdir", required_argument, NULL, 'D' },
+ { "setenv", required_argument, NULL, ARG_SETENV },
+ { "background", required_argument, NULL, ARG_BACKGROUND },
+ {},
+ };
+
+ int r, c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
+ while ((c = getopt_long(argc, argv, "+hVu:g:D:", options, NULL)) >= 0)
+
+ switch (c) {
+
+ case 'h':
+ return help_sudo_mode();
+
+ case 'V':
+ return version();
+
+ case ARG_NO_ASK_PASSWORD:
+ arg_ask_password = false;
+ break;
+
+ case ARG_MACHINE:
+ arg_transport = BUS_TRANSPORT_MACHINE;
+ arg_host = optarg;
+ break;
+
+ case ARG_UNIT:
+ arg_unit = optarg;
+ break;
+
+ case ARG_PROPERTY:
+ if (strv_extend(&arg_property, optarg) < 0)
+ return log_oom();
+
+ break;
+
+ case ARG_DESCRIPTION:
+ r = free_and_strdup_warn(&arg_description, optarg);
+ if (r < 0)
+ return r;
+ break;
+
+ case ARG_SLICE:
+ arg_slice = optarg;
+ break;
+
+ case ARG_SLICE_INHERIT:
+ arg_slice_inherit = true;
+ break;
+
+ case 'u':
+ arg_exec_user = optarg;
+ break;
+
+ case 'g':
+ arg_exec_group = optarg;
+ break;
+
+ case ARG_NICE:
+ r = parse_nice(optarg, &arg_nice);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse nice value: %s", optarg);
+
+ arg_nice_set = true;
+ break;
+
+ case 'D':
+ r = parse_path_argument(optarg, true, &arg_working_directory);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case ARG_SETENV:
+ r = strv_env_replace_strdup_passthrough(&arg_environment, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
+
+ break;
+
+ case ARG_BACKGROUND:
+ r = free_and_strdup_warn(&arg_background, optarg);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+
+ if (!arg_working_directory) {
+ if (arg_exec_user) {
+ /* When switching to a specific user, also switch to its home directory. */
+ arg_working_directory = strdup("~");
+ if (!arg_working_directory)
+ return log_oom();
+ } else {
+ /* When switching to root without this being specified, then stay in the current directory */
+ r = safe_getcwd(&arg_working_directory);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get current working directory: %m");
+ }
+ }
+
+ arg_service_type = "exec";
+ arg_quiet = true;
+ arg_wait = true;
+ arg_aggressive_gc = true;
+
+ arg_stdio = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT;
+ arg_expand_environment = false;
+ arg_send_sighup = true;
+
+ _cleanup_strv_free_ char **l = NULL;
+ if (argc > optind)
+ l = strv_copy(argv + optind);
+ else {
+ const char *e;
+
+ e = strv_env_get(arg_environment, "SHELL");
+ if (e)
+ arg_exec_path = strdup(e);
+ else {
+ if (arg_transport == BUS_TRANSPORT_LOCAL) {
+ r = get_shell(&arg_exec_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine shell: %m");
+ } else
+ arg_exec_path = strdup("/bin/sh");
+ }
+ if (!arg_exec_path)
+ return log_oom();
+
+ l = make_login_shell_cmdline(arg_exec_path);
+ }
+ if (!l)
+ return log_oom();
+
+ strv_free_and_replace(arg_cmdline, l);
+
+ if (!arg_slice) {
+ arg_slice = strdup("user.slice");
+ if (!arg_slice)
+ return log_oom();
+ }
+
+ _cleanup_free_ char *un = NULL;
+ un = getusername_malloc();
+ if (!un)
+ return log_oom();
+
+ /* Set a bunch of environment variables in a roughly sudo-compatible way */
+ r = strv_env_assign(&arg_environment, "SUDO_USER", un);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set $SUDO_USER environment variable: %m");
+
+ r = strv_env_assignf(&arg_environment, "SUDO_UID", UID_FMT, getuid());
+ if (r < 0)
+ return log_error_errno(r, "Failed to set $SUDO_UID environment variable: %m");
+
+ r = strv_env_assignf(&arg_environment, "SUDO_GID", GID_FMT, getgid());
+ if (r < 0)
+ return log_error_errno(r, "Failed to set $SUDO_GID environment variable: %m");
+
+ if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_UID=" UID_FMT, getuid()) < 0)
+ return log_oom();
+
+ if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_GID=" GID_FMT, getgid()) < 0)
+ return log_oom();
+
+ if (strv_extendf(&arg_property, "LogExtraFields=ELEVATED_USER=%s", un) < 0)
+ return log_oom();
+
+ if (strv_extend(&arg_property, "PAMName=systemd-uid0") < 0)
+ return log_oom();
+
+ if (!arg_background && arg_stdio == ARG_STDIO_PTY) {
+ double red, green, blue;
+
+ r = get_default_background_color(&red, &green, &blue);
+ if (r < 0)
+ log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
+ else {
+ double h, s, v;
+
+ rgb_to_hsv(red, green, blue, &h, &s, &v);
+
+ if (!arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0"))
+ h = 0; /* red */
+ else
+ h = 60 /* yellow */;
+
+ if (v > 50) /* If the background is bright, then pull down saturation */
+ s = 25;
+ else /* otherwise pump it up */
+ s = 75;
+
+ v = MAX(30, v); /* Make sure we don't hide the color in black */
+
+ uint8_t r8, g8, b8;
+ hsv_to_rgb(h, s, v, &r8, &g8, &b8);
+
+ if (asprintf(&arg_background, "48;2;%u;%u;%u", r8, g8, b8) < 0)
+ return log_oom();
+ }
+ }
+
+ return 1;
+}
+
static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) {
int r;
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(m, "s", arg_cmdline[0]);
+ r = sd_bus_message_append(m, "s", arg_exec_path ?: arg_cmdline[0]);
if (r < 0)
return bus_log_create_error(r);
if (use_ex_prop)
r = sd_bus_message_append_strv(
m,
- STRV_MAKE(arg_expand_environment > 0 ? NULL : "no-env-expand"));
+ STRV_MAKE(arg_expand_environment > 0 ? NULL : "no-env-expand",
+ arg_ignore_failure ? "ignore-failure" : NULL));
else
- r = sd_bus_message_append(m, "b", false);
+ r = sd_bus_message_append(m, "b", arg_ignore_failure);
if (r < 0)
return bus_log_create_error(r);
r = bus_wait_for_jobs_one(w,
object,
- arg_quiet,
+ arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
if (r < 0)
return r;
/* Make sure to process any TTY events before we process bus events */
(void) pty_forward_set_priority(c.forward, SD_EVENT_PRIORITY_IMPORTANT);
+
+ if (!isempty(arg_background))
+ (void) pty_forward_set_background_color(c.forward, arg_background);
}
path = unit_dbus_path_from_name(service);
if (r < 0)
return bus_log_parse_error(r);
- r = bus_wait_for_jobs_one(w, object, arg_quiet, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
+ r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
+ arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
if (r < 0)
return r;
if (r < 0)
return bus_log_parse_error(r);
- r = bus_wait_for_jobs_one(w, object, arg_quiet, arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
+ r = bus_wait_for_jobs_one(w, object, arg_quiet ? 0 : BUS_WAIT_JOBS_LOG_ERROR,
+ arg_runtime_scope == RUNTIME_SCOPE_USER ? STRV_MAKE_CONST("--user") : NULL);
if (r < 0)
return r;
}
static bool shall_make_executable_absolute(void) {
+ if (arg_exec_path)
+ return false;
if (strv_isempty(arg_cmdline))
return false;
if (arg_transport != BUS_TRANSPORT_LOCAL)
log_parse_environment();
log_open();
- r = parse_argv(argc, argv);
+ if (invoked_as(argv, "uid0"))
+ r = parse_argv_sudo_mode(argc, argv);
+ else
+ r = parse_argv(argc, argv);
if (r <= 0)
return r;
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# This file is part of systemd.
+#
+# Used by uid0 sessions
+
+{% if ENABLE_HOMED %}
+-account sufficient pam_systemd_home.so
+{% endif %}
+account required pam_unix.so
+
+{% if HAVE_SELINUX %}
+session required pam_selinux.so close
+session required pam_selinux.so open
+{% endif %}
+session required pam_loginuid.so
+session optional pam_keyinit.so force revoke
+session required pam_namespace.so
+{% if ENABLE_HOMED %}
+-session optional pam_systemd_home.so
+{% endif %}
+session optional pam_umask.so silent
+session optional pam_systemd.so
+session required pam_unix.so
(void) loop_write(ttyfd, ANSI_NORMAL, SIZE_MAX);
new_termios = old_termios;
- new_termios.c_lflag &= ~(ICANON|ECHO);
- new_termios.c_cc[VMIN] = 1;
- new_termios.c_cc[VTIME] = 0;
+ termios_disable_echo(&new_termios);
r = RET_NERRNO(tcsetattr(ttyfd, TCSADRAIN, &new_termios));
if (r < 0)
return 0;
}
+int blockdev_get_device_size(int fd, uint64_t *ret) {
+ uint64_t sz = 0;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ /* This is just a type-safe wrapper around BLKGETSIZE64 that gets us around having to include messy linux/fs.h in various clients */
+
+ if (ioctl(fd, BLKGETSIZE64, &sz) < 0)
+ return -errno;
+
+ *ret = sz;
+ return 0;
+}
+
int blockdev_get_root(int level, dev_t *ret) {
_cleanup_free_ char *p = NULL;
dev_t devno;
int blockdev_reread_partition_table(sd_device *dev);
int blockdev_get_sector_size(int fd, uint32_t *ret);
+int blockdev_get_device_size(int fd, uint64_t *ret);
int blockdev_get_root(int level, dev_t *ret);
int bus_test_polkit(
sd_bus_message *call,
- int capability,
const char *action,
const char **details,
uid_t good_user,
if (r != 0)
return r;
- r = sd_bus_query_sender_privilege(call, capability);
+ r = sd_bus_query_sender_privilege(call, -1);
if (r < 0)
return r;
if (r > 0)
* <- async_polkit_defer(q)
*/
-int bus_verify_polkit_async(
+int bus_verify_polkit_async_full(
sd_bus_message *call,
- int capability,
const char *action,
const char **details,
- bool interactive,
+ bool interactive, /* Use only for legacy method calls that have a separate "allow_interactive_authentication" field */
uid_t good_user,
Hashmap **registry,
sd_bus_error *ret_error) {
}
#endif
- r = sd_bus_query_sender_privilege(call, capability);
+ r = sd_bus_query_sender_privilege(call, -1);
if (r < 0)
return r;
if (r > 0)
#include "sd-bus.h"
#include "hashmap.h"
+#include "user-util.h"
-int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
+int bus_test_polkit(sd_bus_message *call, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
+
+int bus_verify_polkit_async_full(sd_bus_message *call, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
+static inline int bus_verify_polkit_async(sd_bus_message *call, const char *action, const char **details, Hashmap **registry, sd_bus_error *ret_error) {
+ return bus_verify_polkit_async_full(call, action, details, false, UID_INVALID, registry, ret_error);
+}
-int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error);
Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry);
service_shell_quoted ?: "<service>");
}
-static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
+static int check_wait_response(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) {
assert(d);
assert(d->name);
assert(d->result);
- if (!quiet) {
+ if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_ERROR)) {
if (streq(d->result, "canceled"))
log_error("Job for %s canceled.", strna(d->name));
else if (streq(d->result, "timeout"))
return -EOPNOTSUPP;
else if (streq(d->result, "once"))
return -ESTALE;
- else if (STR_IN_SET(d->result, "done", "skipped"))
+ else if (streq(d->result, "done")) {
+ if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
+ log_info("Job for %s finished.", strna(d->name));
return 0;
+ } else if (streq(d->result, "skipped")) {
+ if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS))
+ log_info("Job for %s was skipped.", strna(d->name));
+ return 0;
+ }
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
"Unexpected job result, assuming server side newer than us: %s", d->result);
}
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) {
+int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) {
int r = 0;
assert(d);
return log_error_errno(q, "Failed to wait for response: %m");
if (d->name && d->result) {
- q = check_wait_response(d, quiet, extra_args);
+ q = check_wait_response(d, flags, extra_args);
/* Return the first error as it is most likely to be
* meaningful. */
if (q < 0 && r == 0)
return set_put_strdup(&d->jobs, path);
}
-int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet, const char* const* extra_args) {
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, WaitJobsFlags flags, const char* const* extra_args) {
int r;
r = bus_wait_for_jobs_add(d, path);
if (r < 0)
return log_oom();
- return bus_wait_for_jobs(d, quiet, extra_args);
+ return bus_wait_for_jobs(d, flags, extra_args);
}
#include "macro.h"
+typedef enum WaitJobsFlags {
+ BUS_WAIT_JOBS_LOG_ERROR = 1 << 0,
+ BUS_WAIT_JOBS_LOG_SUCCESS = 1 << 1,
+} WaitJobsFlags;
+
typedef struct BusWaitForJobs BusWaitForJobs;
int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret);
BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d);
int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path);
-int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args);
-int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet, const char* const* extra_args);
+int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args);
+int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, WaitJobsFlags flags, const char* const* extra_args);
DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free);
{ "cgroup.procs", true },
{ "cgroup.subtree_control", true },
{ "cgroup.threads", false },
+ { "memory.oom.group", false },
+ { "memory.reclaim", false },
{},
};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <math.h>
+
+#include "color-util.h"
+#include "macro.h"
+
+void rgb_to_hsv(double r, double g, double b,
+ double *ret_h, double *ret_s, double *ret_v) {
+
+ assert(r >= 0 && r <= 1);
+ assert(g >= 0 && g <= 1);
+ assert(b >= 0 && b <= 1);
+ assert(ret_h);
+ assert(ret_s);
+ assert(ret_v);
+
+ double max_color = fmax(r, fmax(g, b));
+ double min_color = fmin(r, fmin(g, b));
+ double delta = max_color - min_color;
+
+ *ret_v = max_color * 100.0;
+
+ if (max_color > 0)
+ *ret_s = delta / max_color * 100.0;
+ else {
+ *ret_s = 0;
+ *ret_h = NAN;
+ return;
+ }
+
+ if (delta > 0) {
+ if (r >= max_color)
+ *ret_h = 60 * fmod((g - b) / delta, 6);
+ else if (g >= max_color)
+ *ret_h = 60 * (((b - r) / delta) + 2);
+ else if (b >= max_color)
+ *ret_h = 60 * (((r - g) / delta) + 4);
+
+ *ret_h = fmod(*ret_h, 360);
+ } else
+ *ret_h = NAN;
+}
+
+void hsv_to_rgb(double h, double s, double v,
+ uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) {
+
+ double c, x, m, r, g, b;
+
+ assert(s >= 0 && s <= 100);
+ assert(v >= 0 && v <= 100);
+ assert(ret_r);
+ assert(ret_g);
+ assert(ret_b);
+
+ h = fmod(h, 360);
+ c = (s / 100.0) * (v / 100.0);
+ x = c * (1 - fabs(fmod(h / 60.0, 2) - 1));
+ m = (v / 100) - c;
+
+ if (h >= 0 && h < 60)
+ r = c, g = x, b = 0.0;
+ else if (h >= 60 && h < 120)
+ r = x, g = c, b = 0.0;
+ else if (h >= 120 && h < 180)
+ r = 0.0, g = c, b = x;
+ else if (h >= 180 && h < 240)
+ r = 0.0, g = x, b = c;
+ else if (h >= 240 && h < 300)
+ r = x, g = 0.0, b = c;
+ else
+ r = c, g = 0.0, b = x;
+
+ *ret_r = (uint8_t) ((r + m) * 255);
+ *ret_g = (uint8_t) ((g + m) * 255);
+ *ret_b = (uint8_t) ((b + m) * 255);
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+
+void rgb_to_hsv(double r, double g, double b,
+ double *ret_h, double *ret_s, double *ret_v);
+
+void hsv_to_rgb(
+ double h, double s, double v,
+ uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b);
return get_credentials_dir_internal("ENCRYPTED_CREDENTIALS_DIRECTORY", ret);
}
+int open_credentials_dir(void) {
+ const char *d;
+ int r;
+
+ r = get_credentials_dir(&d);
+ if (r < 0)
+ return r;
+
+ return RET_NERRNO(open(d, O_CLOEXEC|O_DIRECTORY));
+}
+
int read_credential(const char *name, void **ret, size_t *ret_size) {
_cleanup_free_ char *fn = NULL;
const char *d;
int get_credentials_dir(const char **ret);
int get_encrypted_credentials_dir(const char **ret);
+int open_credentials_dir(void);
+
/* Where creds have been passed to the system */
#define SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@system"
#define ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@encrypted"
#include <unistd.h>
#include "alloc-util.h"
+#include "blockdev-util.h"
#include "btrfs-util.h"
#include "chase.h"
#include "chattr-util.h"
read_only = true;
}
- if (ioctl(block_fd, BLKGETSIZE64, &size) < 0)
- log_debug_errno(errno, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
+ r = blockdev_get_device_size(block_fd, &size);
+ if (r < 0)
+ log_debug_errno(r, "Failed to issue BLKGETSIZE64 on device %s/%s, ignoring: %m", path ?: strnull(parent), filename);
block_fd = safe_close(block_fd);
}
#if HAVE_BLKID
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
_cleanup_close_ int fd = -EBADF;
+ struct stat st;
int r;
assert(path);
if (fd < 0)
return -errno;
- r = fd_verify_regular(fd);
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ r = stat_verify_regular(&st);
if (r < 0)
return r;
if (r < 0)
return r;
+ m->image_size = st.st_size;
+
r = probe_sector_size(fd, &m->sector_size);
if (r < 0)
return r;
if (node_fd < 0)
return log_debug_errno(errno, "Failed to open node device %s: %m", node_path);
- if (ioctl(node_fd, BLKGETSIZE64, &size) != 0)
- return log_debug_errno(errno, "Failed to get block device size of %s: %m", node_path);
+ r = blockdev_get_device_size(node_fd, &size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get block device size of %s: %m", node_path);
if (mount_fd < 0) {
assert(mount_path);
if (r > 0)
ok = true;
}
- if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT)) {
+ if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT) && m->image_name) {
r = extension_has_forbidden_content(where);
if (r < 0)
return r;
assert(m);
for (; n_meta_initialized < _META_MAX; n_meta_initialized ++) {
- if (!paths[n_meta_initialized]) {
- fds[2*n_meta_initialized] = fds[2*n_meta_initialized+1] = -EBADF;
- continue;
- }
+ assert(paths[n_meta_initialized]);
if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
r = -errno;
for (unsigned k = 0; k < _META_MAX; k++) {
_cleanup_close_ int fd = -ENOENT;
- if (!paths[k])
- continue;
+ assert(paths[k]);
fds[2*k] = safe_close(fds[2*k]);
switch (k) {
case META_SYSEXT_RELEASE:
+ if (!m->image_name)
+ goto next;
+
/* As per the os-release spec, if the image is an extension it will have a
* file named after the image name in extension-release.d/ - we use the image
* name and try to resolve it with the extension-release helpers, as
break;
case META_CONFEXT_RELEASE:
+ if (!m->image_name)
+ goto next;
+
/* As above */
r = open_extension_release(
t,
if (r < 0)
goto inner_fail;
- continue;
+ goto next;
}
default:
if (fd < 0) {
log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
- fds[2*k+1] = safe_close(fds[2*k+1]);
- continue;
+ goto next;
}
r = copy_bytes(fd, fds[2*k+1], UINT64_MAX, 0);
if (r < 0)
goto inner_fail;
+ next:
fds[2*k+1] = safe_close(fds[2*k+1]);
}
for (unsigned k = 0; k < _META_MAX; k++) {
_cleanup_fclose_ FILE *f = NULL;
- if (!paths[k])
- continue;
+ assert(paths[k]);
fds[2*k+1] = safe_close(fds[2*k+1]);
r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
child = 0;
if (r < 0)
- return r;
+ goto finish;
n = read(error_pipe[0], &v, sizeof(v));
- if (n < 0)
- return -errno;
- if (n == sizeof(v))
- return v; /* propagate error sent to us from child */
- if (n != 0)
- return -EIO;
-
- if (r != EXIT_SUCCESS)
- return -EPROTO;
+ if (n < 0) {
+ r = -errno;
+ goto finish;
+ }
+ if (n == sizeof(v)) {
+ r = v; /* propagate error sent to us from child */
+ goto finish;
+ }
+ if (n != 0) {
+ r = -EIO;
+ goto finish;
+ }
+ if (r != EXIT_SUCCESS) {
+ r = -EPROTO;
+ goto finish;
+ }
free_and_replace(m->hostname, hostname);
m->machine_id = machine_id;
return r;
m->loop = loop_device_ref(loop);
+ m->image_size = m->loop->device_size;
m->sector_size = m->loop->sector_size;
r = dissect_image(m, loop->fd, loop->node, verity, mount_options, image_policy, flags);
DecryptedImage *decrypted_image;
uint32_t sector_size;
+ uint64_t image_size;
- /* Meta information extracted from /etc/os-release and similar */
char *image_name;
sd_id128_t image_uuid;
+
+ /* Meta information extracted from /etc/os-release and similar */
char *hostname;
sd_id128_t machine_id;
char **machine_info;
goto finish;
for (;;) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, label, sizeof label, flags);
if (r < 0)
y = b + strlen(b);
for (;;) {
- char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
if (x == NULL && y == NULL)
return 0;
assert(y);
for (;;) {
- char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
r = dns_label_unescape(&x, la, sizeof la, 0);
if (r < 0)
s = suffix;
for (;;) {
- char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+ char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
r = dns_label_unescape(&n, ln, sizeof ln, 0);
if (r < 0)
p = prefix;
for (;;) {
- char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
+ char ln[DNS_LABEL_MAX+1], lp[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, lp, sizeof lp, 0);
if (r < 0)
s = old_suffix;
for (;;) {
- char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
+ char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
if (!saved_before)
saved_before = n;
return false;
for (;;) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
/* This more or less implements RFC 6335, Section 5.1 */
return m;
for (;;) {
- char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
+ char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
const char *x, *y;
if (k >= n || k >= m) {
assert(ret);
for (;;) {
- char label[DNS_LABEL_MAX];
+ char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
typedef enum VerifyESPFlags {
VERIFY_ESP_SEARCHING = 1 << 0, /* Downgrade various "not found" logs to debug level */
VERIFY_ESP_UNPRIVILEGED_MODE = 1 << 1, /* Call into udev rather than blkid */
- VERIFY_ESP_RELAX_CHECKS = 1 << 2, /* Do not validate ESP partition */
+ VERIFY_ESP_SKIP_FSTYPE_CHECK = 1 << 2, /* Skip filesystem check */
+ VERIFY_ESP_SKIP_DEVICE_CHECK = 1 << 3, /* Skip device node check */
} VerifyESPFlags;
+static VerifyESPFlags verify_esp_flags_init(int unprivileged_mode, const char *env_name_for_relaxing) {
+ VerifyESPFlags flags = 0;
+ int r;
+
+ assert(env_name_for_relaxing);
+
+ if (unprivileged_mode < 0)
+ unprivileged_mode = geteuid() != 0;
+ if (unprivileged_mode)
+ flags |= VERIFY_ESP_UNPRIVILEGED_MODE;
+
+ r = getenv_bool(env_name_for_relaxing);
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $%s environment variable, assuming false.", env_name_for_relaxing);
+ else if (r > 0)
+ flags |= VERIFY_ESP_SKIP_FSTYPE_CHECK | VERIFY_ESP_SKIP_DEVICE_CHECK;
+
+ if (detect_container() > 0)
+ flags |= VERIFY_ESP_SKIP_DEVICE_CHECK;
+
+ return flags;
+}
+
static int verify_esp_blkid(
dev_t devid,
VerifyESPFlags flags,
dev_t *ret_devid,
VerifyESPFlags flags) {
- bool relax_checks, searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
- unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
+ bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
+ unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
_cleanup_free_ char *p = NULL;
_cleanup_close_ int pfd = -EBADF;
dev_t devid = 0;
* -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing
*/
- relax_checks =
- getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0 ||
- FLAGS_SET(flags, VERIFY_ESP_RELAX_CHECKS);
-
/* Non-root user can only check the status, so if an error occurred in the following, it does not cause any
* issues. Let's also, silence the error messages. */
(unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR,
r, "Failed to open parent directory of \"%s\": %m", path);
- if (!relax_checks) {
+ if (!FLAGS_SET(flags, VERIFY_ESP_SKIP_FSTYPE_CHECK)) {
_cleanup_free_ char *f = NULL;
struct statfs sfs;
"File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
}
- relax_checks =
- relax_checks ||
- detect_container() > 0;
-
- r = verify_fsroot_dir(pfd, p, flags, relax_checks ? NULL : &devid);
+ r = verify_fsroot_dir(pfd, p, flags, FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK) ? NULL : &devid);
if (r < 0)
return r;
/* In a container we don't have access to block devices, skip this part of the verification, we trust
* the container manager set everything up correctly on its own. */
- if (relax_checks)
+ if (FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK))
goto finish;
+ if (devnum_is_zero(devid))
+ return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+ SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+ "Could not determine backing block device of directory \"%s\" (btrfs RAID?).", p);
+
/* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we
* use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an
* emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell),
assert(rfd >= 0 || rfd == AT_FDCWD);
- if (unprivileged_mode < 0)
- unprivileged_mode = geteuid() != 0;
- flags = unprivileged_mode > 0 ? VERIFY_ESP_UNPRIVILEGED_MODE : 0;
-
- r = dir_fd_is_root_or_cwd(rfd);
- if (r < 0)
- return log_error_errno(r, "Failed to check if directory file descriptor is root: %m");
- if (r == 0)
- flags |= VERIFY_ESP_RELAX_CHECKS;
+ flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_ESP_CHECKS");
if (path)
return verify_esp(rfd, path, ret_path, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid, flags);
_cleanup_free_ char *p = NULL;
_cleanup_close_ int pfd = -EBADF;
bool searching = FLAGS_SET(flags, VERIFY_ESP_SEARCHING),
- unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE),
- relax_checks;
+ unprivileged_mode = FLAGS_SET(flags, VERIFY_ESP_UNPRIVILEGED_MODE);
dev_t devid = 0;
int r;
(unprivileged_mode && ERRNO_IS_PRIVILEGE(r)) ? LOG_DEBUG : LOG_ERR,
r, "Failed to open parent directory of \"%s\": %m", path);
- relax_checks =
- getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0 ||
- detect_container() > 0;
-
- r = verify_fsroot_dir(pfd, p, flags, relax_checks ? NULL : &devid);
+ r = verify_fsroot_dir(pfd, p, flags, FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK) ? NULL : &devid);
if (r < 0)
return r;
- if (relax_checks)
+ if (FLAGS_SET(flags, VERIFY_ESP_SKIP_DEVICE_CHECK))
goto finish;
+ if (devnum_is_zero(devid))
+ return log_full_errno(searching ? LOG_DEBUG : LOG_ERR,
+ SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV),
+ "Could not determine backing block device of directory \"%s\" (btrfs RAID?).%s",
+ p,
+ searching ? "" :
+ "\nHint: set $SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes environment variable "
+ "to bypass this and further verifications for the directory.");
+
if (unprivileged_mode)
r = verify_xbootldr_udev(devid, flags, ret_uuid);
else
sd_id128_t *ret_uuid,
dev_t *ret_devid) {
- VerifyESPFlags flags = 0;
+ VerifyESPFlags flags;
int r;
/* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */
assert(rfd >= 0 || rfd == AT_FDCWD);
- if (unprivileged_mode < 0)
- unprivileged_mode = geteuid() != 0;
- if (unprivileged_mode)
- flags |= VERIFY_ESP_UNPRIVILEGED_MODE;
+ flags = verify_esp_flags_init(unprivileged_mode, "SYSTEMD_RELAX_XBOOTLDR_CHECKS");
if (path)
return verify_xbootldr(rfd, path, flags, ret_path, ret_uuid, ret_devid);
#include "alloc-util.h"
#include "conf-files.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
return true;
return false;
}
+
+int hwdb_bypass(void) {
+ int r;
+
+ r = getenv_bool("SYSTEMD_HWDB_UPDATE_BYPASS");
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_HWDB_UPDATE_BYPASS, assuming no.");
+ if (r <= 0)
+ return false;
+
+ log_debug("$SYSTEMD_HWDB_UPDATE_BYPASS is enabled, skipping execution.");
+ return true;
+}
bool hwdb_should_reload(sd_hwdb *hwdb);
int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool compat);
int hwdb_query(const char *modalias, const char *root);
+int hwdb_bypass(void);
assert(config_path);
assert(lp);
- if (set_size(remove_symlinks_to) <= 0)
+ if (set_isempty(remove_symlinks_to))
return 0;
fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
if (!config_path)
return -ENXIO;
+ r = 0;
+
STRV_FOREACH(name, names) {
_cleanup_free_ char *path = NULL;
- int q;
if (!unit_name_is_valid(*name, UNIT_NAME_ANY)) {
- if (r == 0)
- r = -EINVAL;
+ RET_GATHER(r, -EINVAL);
continue;
}
if (!path)
return -ENOMEM;
- q = create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes);
- if (q < 0 && r >= 0)
- r = q;
+ RET_GATHER(r, create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes));
}
return r;
if (!dry_run && unlink(path) < 0) {
if (errno != ENOENT) {
- if (r >= 0)
- r = -errno;
+ RET_GATHER(r, -errno);
install_changes_add(changes, n_changes, -errno, path, NULL);
}
return q;
}
- q = remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes);
- if (r >= 0)
- r = q;
+ RET_GATHER(r, remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes));
return r;
}
* effect hence. And if not use classic LOOP_SET_STATUS64. */
uint64_t z;
- if (ioctl(fd, BLKGETSIZE64, &z) < 0)
- return -errno;
+ r = blockdev_get_device_size(fd, &z);
+ if (r < 0)
+ return r;
if (z != c->info.lo_sizelimit) {
log_debug("LOOP_CONFIGURE is broken, doesn't honour .info.lo_sizelimit. Falling back to LOOP_SET_STATUS64.");
assert_not_reached();
}
+ uint64_t device_size;
+ r = blockdev_get_device_size(loop_with_fd, &device_size);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get loopback device size: %m");
+
LoopDevice *d = new(LoopDevice, 1);
if (!d)
return log_oom_debug();
.uevent_seqnum_not_before = seqnum,
.timestamp_not_before = timestamp,
.sector_size = c->block_size,
+ .device_size = device_size,
+ .created = true,
};
*ret = TAKE_PTR(d);
if (r < 0)
return r;
+ uint64_t device_size;
+ r = blockdev_get_device_size(fd, &device_size);
+ if (r < 0)
+ return r;
+
r = sd_device_get_devnum(dev, &devnum);
if (r < 0)
return r;
.uevent_seqnum_not_before = UINT64_MAX,
.timestamp_not_before = USEC_INFINITY,
.sector_size = sector_size,
+ .device_size = device_size,
+ .created = false,
};
*ret = d;
return -EINVAL;
current_offset *= 512U;
- if (ioctl(partition_fd, BLKGETSIZE64, ¤t_size) < 0)
- return -EINVAL;
+ r = blockdev_get_device_size(partition_fd, ¤t_size);
+ if (r < 0)
+ return r;
if (size == UINT64_MAX && offset == UINT64_MAX)
return 0;
sd_device *dev;
char *backing_file;
bool relinquished;
+ bool created; /* If we created the device */
dev_t backing_devno; /* The backing file's dev_t */
ino_t backing_inode; /* The backing file's ino_t */
uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach, or 0 if we don't know */
uint64_t uevent_seqnum_not_before; /* uevent sequm right before we attached the loopback device, or UINT64_MAX if we don't know */
usec_t timestamp_not_before; /* CLOCK_MONOTONIC timestamp taken immediately before attaching the loopback device, or USEC_INFINITY if we don't know */
uint32_t sector_size;
+ uint64_t device_size;
};
/* Returns true if LoopDevice object is not actually a loopback device but some other block device we just wrap */
cred->size = 0;
}
-void machine_credential_free_all(MachineCredential *creds, size_t n) {
- assert(creds || n == 0);
+void machine_credential_context_done(MachineCredentialContext *ctx) {
+ assert(ctx);
- FOREACH_ARRAY(cred, creds, n)
+ FOREACH_ARRAY(cred, ctx->credentials, ctx->n_credentials)
machine_credential_done(cred);
- free(creds);
+ free(ctx->credentials);
}
-int machine_credential_set(MachineCredential **credentials, size_t *n_credentials, const char *cred_string) {
- _cleanup_free_ char *word = NULL, *data = NULL;
+bool machine_credentials_contains(const MachineCredentialContext *ctx, const char *id) {
+ assert(ctx);
+ assert(id);
+
+ FOREACH_ARRAY(cred, ctx->credentials, ctx->n_credentials)
+ if (streq(cred->id, id))
+ return true;
+
+ return false;
+}
+
+int machine_credential_set(MachineCredentialContext *ctx, const char *cred_str) {
+ _cleanup_(machine_credential_done) MachineCredential cred = {};
ssize_t l;
int r;
- const char *p = ASSERT_PTR(cred_string);
- assert(credentials && n_credentials);
- assert(*credentials || *n_credentials == 0);
+ assert(ctx);
- r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ const char *p = ASSERT_PTR(cred_str);
+
+ r = extract_first_word(&p, &cred.id, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse --set-credential= parameter: %m");
if (r == 0 || !p)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for --set-credential=: %s", cred_string);
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Missing value for --set-credential=: %s", cred_str);
- if (!credential_name_valid(word))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", word);
+ if (!credential_name_valid(cred.id))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", cred.id);
- FOREACH_ARRAY(cred, *credentials, *n_credentials)
- if (streq(cred->id, word))
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", word);
+ if (machine_credentials_contains(ctx, cred.id))
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", cred.id);
- l = cunescape(p, UNESCAPE_ACCEPT_NUL, &data);
+ l = cunescape(p, UNESCAPE_ACCEPT_NUL, &cred.data);
if (l < 0)
return log_error_errno(l, "Failed to unescape credential data: %s", p);
+ cred.size = l;
- if (!GREEDY_REALLOC(*credentials, *n_credentials + 1))
+ if (!GREEDY_REALLOC(ctx->credentials, ctx->n_credentials + 1))
return log_oom();
- (*credentials)[(*n_credentials)++] = (MachineCredential) {
- .id = TAKE_PTR(word),
- .data = TAKE_PTR(data),
- .size = l,
- };
+ ctx->credentials[ctx->n_credentials++] = TAKE_STRUCT(cred);
return 0;
}
-int machine_credential_load(MachineCredential **credentials, size_t *n_credentials, const char *cred_path) {
+int machine_credential_load(MachineCredentialContext *ctx, const char *cred_path) {
+ _cleanup_(machine_credential_done) MachineCredential cred = {};
+ _cleanup_free_ char *path_alloc = NULL;
ReadFullFileFlags flags = READ_FULL_FILE_SECURE;
- _cleanup_(erase_and_freep) char *data = NULL;
- _cleanup_free_ char *word = NULL, *j = NULL;
- const char *p = ASSERT_PTR(cred_path);
- size_t size;
int r;
- assert(credentials && n_credentials);
- assert(*credentials || *n_credentials == 0);
+ assert(ctx);
+
+ const char *p = ASSERT_PTR(cred_path);
- r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ r = extract_first_word(&p, &cred.id, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse --load-credential= parameter: %m");
if (r == 0 || !p)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing value for --load-credential=: %s", cred_path);
- if (!credential_name_valid(word))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", word);
+ if (!credential_name_valid(cred.id))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential name is not valid: %s", cred.id);
- FOREACH_ARRAY(cred, *credentials, *n_credentials)
- if (streq(cred->id, word))
- return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", word);
+ if (machine_credentials_contains(ctx, cred.id))
+ return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate credential '%s', refusing.", cred.id);
if (is_path(p) && path_is_valid(p))
flags |= READ_FULL_FILE_CONNECT_SOCKET;
r = get_credentials_dir(&e);
if (r < 0)
- return log_error_errno(r, "Credential not available (no credentials passed at all): %s", word);
+ return log_error_errno(r,
+ "Credential not available (no credentials passed at all): %s", cred.id);
- j = path_join(e, p);
- if (!j)
+ path_alloc = path_join(e, p);
+ if (!path_alloc)
return log_oom();
- p = j;
+ p = path_alloc;
} else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Credential source appears to be neither a valid path nor a credential name: %s", p);
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Credential source appears to be neither a valid path nor a credential name: %s", p);
r = read_full_file_full(AT_FDCWD, p, UINT64_MAX, SIZE_MAX,
flags,
NULL,
- &data, &size);
+ &cred.data, &cred.size);
if (r < 0)
return log_error_errno(r, "Failed to read credential '%s': %m", p);
- if (!GREEDY_REALLOC(*credentials, *n_credentials + 1))
+ if (!GREEDY_REALLOC(ctx->credentials, ctx->n_credentials + 1))
return log_oom();
- (*credentials)[(*n_credentials)++] = (MachineCredential) {
- .id = TAKE_PTR(word),
- .data = TAKE_PTR(data),
- .size = size,
- };
+ ctx->credentials[ctx->n_credentials++] = TAKE_STRUCT(cred);
return 0;
}
typedef struct MachineCredential {
char *id;
- void *data;
+ char *data;
size_t size;
} MachineCredential;
-void machine_credential_free_all(MachineCredential *creds, size_t n);
-int machine_credential_set(MachineCredential **credentials, size_t *n_credentials, const char *cred_string);
-int machine_credential_load(MachineCredential **credentials, size_t *n_credentials, const char *cred_path);
+typedef struct MachineCredentialContext {
+ MachineCredential *credentials;
+ size_t n_credentials;
+} MachineCredentialContext;
+
+void machine_credential_context_done(MachineCredentialContext *ctx);
+
+bool machine_credentials_contains(const MachineCredentialContext *ctx, const char *id);
+
+int machine_credential_set(MachineCredentialContext *ctx, const char *cred_str);
+int machine_credential_load(MachineCredentialContext *ctx, const char *cred_path);
'chown-recursive.c',
'clean-ipc.c',
'clock-util.c',
+ 'color-util.c',
'common-signal.c',
'compare-operator.c',
'condition.c',
'varlink.c',
'varlink-idl.c',
'varlink-io.systemd.c',
+ 'varlink-io.systemd.Credentials.c',
'varlink-io.systemd.Journal.c',
'varlink-io.systemd.ManagedOOM.c',
'varlink-io.systemd.PCRExtend.c',
assert(fd_newroot >= 0);
assert(path);
- /* Change into the new rootfs. */
- if (fchdir(fd_newroot) < 0)
- return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path);
-
/* Let the kernel tuck the new root under the old one. */
if (pivot_root(".", ".") < 0)
return log_debug_errno(errno, "Failed to pivot root to new rootfs '%s': %m", path);
assert(fd_newroot >= 0);
assert(path);
- /* Change into the new rootfs. */
- if (fchdir(fd_newroot) < 0)
- return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path);
-
/* Move the new root fs */
if (mount(".", "/", NULL, MS_MOVE, NULL) < 0)
return log_debug_errno(errno, "Failed to move new rootfs '%s': %m", path);
int mount_switch_root_full(const char *path, unsigned long mount_propagation_flag, bool force_ms_move) {
_cleanup_close_ int fd_newroot = -EBADF;
- int r;
+ int r, is_current_root;
assert(path);
assert(mount_propagation_flag_is_valid(mount_propagation_flag));
if (fd_newroot < 0)
return log_debug_errno(errno, "Failed to open new rootfs '%s': %m", path);
- if (!force_ms_move) {
- r = mount_switch_root_pivot(fd_newroot, path);
- if (r < 0) {
- log_debug_errno(r, "Failed to pivot into new rootfs '%s', will try to use MS_MOVE instead: %m", path);
- force_ms_move = true;
+ is_current_root = path_is_root_at(fd_newroot, NULL);
+ if (is_current_root < 0)
+ return log_debug_errno(is_current_root, "Failed to determine if target dir is our root already: %m");
+
+ /* Change into the new rootfs. */
+ if (fchdir(fd_newroot) < 0)
+ return log_debug_errno(errno, "Failed to chdir into new rootfs '%s': %m", path);
+
+ /* Make this a NOP if we are supposed to switch to our current root fs. After all, both pivot_root()
+ * and MS_MOVE don't like that. */
+ if (!is_current_root) {
+ if (!force_ms_move) {
+ r = mount_switch_root_pivot(fd_newroot, path);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to pivot into new rootfs '%s', will try to use MS_MOVE instead: %m", path);
+ force_ms_move = true;
+ }
+ }
+ if (force_ms_move) {
+ /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the rootfs is
+ * an initramfs in which case pivot_root() isn't supported. */
+ r = mount_switch_root_move(fd_newroot, path);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to switch to new rootfs '%s' with MS_MOVE: %m", path);
}
- }
- if (force_ms_move) {
- /* Failed to pivot_root() fallback to MS_MOVE. For example, this may happen if the rootfs is
- * an initramfs in which case pivot_root() isn't supported. */
- r = mount_switch_root_move(fd_newroot, path);
- if (r < 0)
- return log_debug_errno(r, "Failed to switch to new rootfs '%s' with MS_MOVE: %m", path);
}
/* Finally, let's establish the requested propagation flags. */
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "sd-device.h"
+
#include "alloc-util.h"
+#include "device-private.h"
#include "netif-naming-scheme.h"
#include "proc-cmdline.h"
#include "string-util.h"
};
DEFINE_STRING_TABLE_LOOKUP(alternative_names_policy, NamePolicy);
+
+static int naming_sysattr_allowed_by_default(sd_device *dev) {
+ int r;
+
+ assert(dev);
+
+ r = device_get_property_bool(dev, "ID_NET_NAME_ALLOW");
+ if (r == -ENOENT)
+ return true;
+
+ return r;
+}
+
+static int naming_sysattr_allowed(sd_device *dev, const char *sysattr) {
+ char *sysattr_property;
+ int r;
+
+ assert(dev);
+ assert(sysattr);
+
+ sysattr_property = strjoina("ID_NET_NAME_ALLOW_", sysattr);
+ ascii_strupper(sysattr_property);
+
+ r = device_get_property_bool(dev, sysattr_property);
+ if (r == -ENOENT)
+ /* If ID_NET_NAME_ALLOW is not set or set to 1 default is to allow */
+ return naming_sysattr_allowed_by_default(dev);
+
+ return r;
+}
+
+int device_get_sysattr_int_filtered(sd_device *device, const char *sysattr, int *ret_value) {
+ int r;
+
+ r = naming_sysattr_allowed(device, sysattr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ return device_get_sysattr_int(device, sysattr, ret_value);
+}
+
+int device_get_sysattr_unsigned_filtered(sd_device *device, const char *sysattr, unsigned *ret_value) {
+ int r;
+
+ r = naming_sysattr_allowed(device, sysattr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ return device_get_sysattr_unsigned(device, sysattr, ret_value);
+}
+
+int device_get_sysattr_bool_filtered(sd_device *device, const char *sysattr) {
+ int r;
+
+ r = naming_sysattr_allowed(device, sysattr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ return device_get_sysattr_bool(device, sysattr);
+}
+
+int device_get_sysattr_value_filtered(sd_device *device, const char *sysattr, const char **ret_value) {
+ int r;
+
+ r = naming_sysattr_allowed(device, sysattr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ return sd_device_get_sysattr_value(device, sysattr, ret_value);
+}
#include <stdbool.h>
+#include "sd-device.h"
+
#include "macro.h"
/* So here's the deal: net_id is supposed to be an exercise in providing stable names for network devices. However, we
const char *alternative_names_policy_to_string(NamePolicy p) _const_;
NamePolicy alternative_names_policy_from_string(const char *p) _pure_;
+
+int device_get_sysattr_int_filtered(sd_device *device, const char *sysattr, int *ret_value);
+int device_get_sysattr_unsigned_filtered(sd_device *device, const char *sysattr, unsigned *ret_value);
+int device_get_sysattr_bool_filtered(sd_device *device, const char *sysattr);
+int device_get_sysattr_value_filtered(sd_device *device, const char *sysattr, const char **ret_value);
#include "fd-util.h"
#include "hexdecoct.h"
#include "openssl-util.h"
+#include "random-util.h"
#include "string-util.h"
#if HAVE_OPENSSL
-/* For each error in the the OpenSSL thread error queue, log the provided message and the OpenSSL error
- * string. If there are no errors in the OpenSSL thread queue, this logs the message with "No openssl
+/* For each error in the OpenSSL thread error queue, log the provided message and the OpenSSL error
+ * string. If there are no errors in the OpenSSL thread queue, this logs the message with "No OpenSSL
* errors." This logs at level debug. Returns -EIO (or -ENOMEM). */
#define log_openssl_errors(fmt, ...) _log_openssl_errors(UNIQ, fmt, ##__VA_ARGS__)
#define _log_openssl_errors(u, fmt, ...) \
*ret_encrypt_key = TAKE_PTR(b);
*ret_encrypt_key_size = l;
-
return 0;
}
if (EVP_PKEY_derive(ctx, NULL, &shared_secret_size) <= 0)
return log_openssl_errors("Failed to get ECC shared secret size");
- _cleanup_free_ void *shared_secret = malloc(shared_secret_size);
+ _cleanup_(erase_and_freep) void *shared_secret = malloc(shared_secret_size);
if (!shared_secret)
return log_oom_debug();
_cleanup_free_ void *hash = NULL;
size_t hash_size;
- _cleanup_free_ char *enc;
+ _cleanup_free_ char *enc = NULL;
int r;
assert(s || len == 0);
return 0;
}
# endif
+
+static int ecc_pkey_generate_volume_keys(
+ EVP_PKEY *pkey,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ void **ret_saved_key,
+ size_t *ret_saved_key_size) {
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_new = NULL;
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+ _cleanup_free_ unsigned char *saved_key = NULL;
+ size_t decrypted_key_size, saved_key_size;
+ int nid = NID_undef;
+ int r;
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ _cleanup_free_ char *curve_name = NULL;
+ size_t len = 0;
+
+ if (EVP_PKEY_get_group_name(pkey, NULL, 0, &len) != 1 || len == 0)
+ return log_openssl_errors("Failed to determine PKEY group name length");
+
+ len++;
+ curve_name = new(char, len);
+ if (!curve_name)
+ return log_oom_debug();
+
+ if (EVP_PKEY_get_group_name(pkey, curve_name, len, &len) != 1)
+ return log_openssl_errors("Failed to get PKEY group name");
+
+ nid = OBJ_sn2nid(curve_name);
+#else
+ EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
+ if (!ec_key)
+ return log_openssl_errors("PKEY doesn't have EC_KEY associated");
+
+ if (EC_KEY_check_key(ec_key) != 1)
+ return log_openssl_errors("EC_KEY associated with PKEY is not valid");
+
+ nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+#endif
+
+ r = ecc_pkey_new(nid, &pkey_new);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to generate a new EC keypair: %m");
+
+ r = ecc_ecdh(pkey_new, pkey, &decrypted_key, &decrypted_key_size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to derive shared secret: %m");
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ /* EVP_PKEY_get1_encoded_public_key() always returns uncompressed format of EC points.
+ See https://github.com/openssl/openssl/discussions/22835 */
+ saved_key_size = EVP_PKEY_get1_encoded_public_key(pkey_new, &saved_key);
+ if (saved_key_size == 0)
+ return log_openssl_errors("Failed to convert the generated public key to SEC1 format");
+#else
+ EC_KEY *ec_key_new = EVP_PKEY_get0_EC_KEY(pkey_new);
+ if (!ec_key_new)
+ return log_openssl_errors("The generated key doesn't have associated EC_KEY");
+
+ if (EC_KEY_check_key(ec_key_new) != 1)
+ return log_openssl_errors("EC_KEY associated with the generated key is not valid");
+
+ saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new),
+ EC_KEY_get0_public_key(ec_key_new),
+ POINT_CONVERSION_UNCOMPRESSED,
+ NULL, 0, NULL);
+ if (saved_key_size == 0)
+ return log_openssl_errors("Failed to determine size of the generated public key");
+
+ saved_key = malloc(saved_key_size);
+ if (!saved_key)
+ return log_oom_debug();
+
+ saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new),
+ EC_KEY_get0_public_key(ec_key_new),
+ POINT_CONVERSION_UNCOMPRESSED,
+ saved_key, saved_key_size, NULL);
+ if (saved_key_size == 0)
+ return log_openssl_errors("Failed to convert the generated public key to SEC1 format");
+#endif
+
+ *ret_decrypted_key = TAKE_PTR(decrypted_key);
+ *ret_decrypted_key_size = decrypted_key_size;
+ *ret_saved_key = TAKE_PTR(saved_key);
+ *ret_saved_key_size = saved_key_size;
+ return 0;
+}
+
+static int rsa_pkey_generate_volume_keys(
+ EVP_PKEY *pkey,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ void **ret_saved_key,
+ size_t *ret_saved_key_size) {
+
+ _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+ _cleanup_free_ void *saved_key = NULL;
+ size_t decrypted_key_size, saved_key_size;
+ int r;
+
+ r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine RSA public key size.");
+
+ log_debug("Generating %zu bytes random key.", decrypted_key_size);
+
+ decrypted_key = malloc(decrypted_key_size);
+ if (!decrypted_key)
+ return log_oom_debug();
+
+ r = crypto_random_bytes(decrypted_key, decrypted_key_size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to generate random key: %m");
+
+ r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &saved_key, &saved_key_size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to encrypt random key: %m");
+
+ *ret_decrypted_key = TAKE_PTR(decrypted_key);
+ *ret_decrypted_key_size = decrypted_key_size;
+ *ret_saved_key = TAKE_PTR(saved_key);
+ *ret_saved_key_size = saved_key_size;
+ return 0;
+}
+
+int x509_generate_volume_keys(
+ X509 *cert,
+ void **ret_decrypted_key,
+ size_t *ret_decrypted_key_size,
+ void **ret_saved_key,
+ size_t *ret_saved_key_size) {
+
+ assert(cert);
+ assert(ret_decrypted_key);
+ assert(ret_decrypted_key_size);
+ assert(ret_saved_key);
+ assert(ret_saved_key_size);
+
+ EVP_PKEY *pkey = X509_get0_pubkey(cert);
+ if (!pkey)
+ return log_openssl_errors("Failed to extract public key from X.509 certificate.");
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ int type = EVP_PKEY_get_base_id(pkey);
+#else
+ int type = EVP_PKEY_base_id(pkey);
+#endif
+ switch (type) {
+
+ case EVP_PKEY_RSA:
+ return rsa_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
+
+ case EVP_PKEY_EC:
+ return ecc_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
+
+ case NID_undef:
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine a type of public key");
+
+ default:
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported public key type: %s", OBJ_nid2sn(type));
+ }
+}
#endif
int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) {
int ecc_ecdh(const EVP_PKEY *private_pkey, const EVP_PKEY *peer_pkey, void **ret_shared_secret, size_t *ret_shared_secret_size);
+int x509_generate_volume_keys(X509 *cert, void **ret_decrypted_key, size_t *ret_decrypted_key_size, void **ret_saved_key, size_t *ret_saved_key_size);
+
int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size);
int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size);
P11KitUri *search_uri,
CK_OBJECT_HANDLE *ret_object) {
- bool found_decrypt = false, found_class = false, found_key_type = false;
+ uint_fast8_t n_objects = 0;
+ bool found_class = false;
_cleanup_free_ CK_ATTRIBUTE *attributes_buffer = NULL;
- CK_ULONG n_attributes, a, n_objects;
- CK_ATTRIBUTE *attributes = NULL;
- CK_OBJECT_HANDLE objects[2];
- CK_RV rv, rv2;
- int r;
+ CK_OBJECT_HANDLE object, candidate;
+ static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
+ CK_BBOOL decrypt_value, derive_value;
+ CK_ATTRIBUTE optional_attributes[] = {
+ { CKA_DECRYPT, &decrypt_value, sizeof(decrypt_value) },
+ { CKA_DERIVE, &derive_value, sizeof(derive_value) }
+ };
+ CK_RV rv;
assert(m);
assert(search_uri);
assert(ret_object);
- r = dlopen_p11kit();
- if (r < 0)
- return r;
-
- attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes);
- for (a = 0; a < n_attributes; a++) {
+ CK_ULONG n_attributes;
+ CK_ATTRIBUTE *attributes = sym_p11_kit_uri_get_attributes(search_uri, &n_attributes);
+ for (CK_ULONG i = 0; i < n_attributes; i++) {
/* We use the URI's included match attributes, but make them more strict. This allows users
* to specify a token URL instead of an object URL and the right thing should happen if
* there's only one suitable key on the token. */
- switch (attributes[a].type) {
-
+ switch (attributes[i].type) {
case CKA_CLASS: {
CK_OBJECT_CLASS c;
- if (attributes[a].ulValueLen != sizeof(c))
+ if (attributes[i].ulValueLen != sizeof(c))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_CLASS attribute size.");
- memcpy(&c, attributes[a].pValue, sizeof(c));
+ memcpy(&c, attributes[i].pValue, sizeof(c));
if (c != CKO_PRIVATE_KEY)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Selected PKCS#11 object is not a private key, refusing.");
found_class = true;
break;
- }
+ }}
+ }
+
+ if (!found_class) {
+ /* Hmm, let's slightly extend the attribute list we search for */
- case CKA_DECRYPT: {
- CK_BBOOL b;
+ attributes_buffer = new(CK_ATTRIBUTE, n_attributes + 1);
+ if (!attributes_buffer)
+ return log_oom();
- if (attributes[a].ulValueLen != sizeof(b))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_DECRYPT attribute size.");
+ memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
- memcpy(&b, attributes[a].pValue, sizeof(b));
- if (!b)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Selected PKCS#11 object is not suitable for decryption, refusing.");
+ attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
+ .type = CKA_CLASS,
+ .pValue = (CK_OBJECT_CLASS*) &class,
+ .ulValueLen = sizeof(class),
+ };
+
+ attributes = attributes_buffer;
+ }
+
+ rv = m->C_FindObjectsInit(session, attributes, n_attributes);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
+
+ for (;;) {
+ CK_ULONG b;
+ rv = m->C_FindObjects(session, &candidate, 1, &b);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to find objects: %s", sym_p11_kit_strerror(rv));
- found_decrypt = true;
+ if (b == 0)
break;
+
+ bool can_decrypt = false, can_derive = false;
+ optional_attributes[0].ulValueLen = sizeof(decrypt_value);
+ optional_attributes[1].ulValueLen = sizeof(derive_value);
+
+ rv = m->C_GetAttributeValue(session, candidate, optional_attributes, ELEMENTSOF(optional_attributes));
+ if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to get attributes of a selected private key: %s", sym_p11_kit_strerror(rv));
+
+ if (optional_attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION && decrypt_value == CK_TRUE)
+ can_decrypt = true;
+
+ if (optional_attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION && derive_value == CK_TRUE)
+ can_derive = true;
+
+ if (can_decrypt || can_derive) {
+ n_objects++;
+ if (n_objects > 1)
+ break;
+ object = candidate;
}
+ }
- case CKA_KEY_TYPE: {
- CK_KEY_TYPE t;
+ rv = m->C_FindObjectsFinal(session);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
- if (attributes[a].ulValueLen != sizeof(t))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PKCS#11 CKA_KEY_TYPE attribute size.");
+ if (n_objects == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Failed to find selected private key suitable for decryption or derivation on token.");
- memcpy(&t, attributes[a].pValue, sizeof(t));
- if (t != CKK_RSA)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected PKCS#11 object is not an RSA key, refusing.");
+ if (n_objects > 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
+ "Configured private key URI matches multiple keys, refusing.");
- found_key_type = true;
- break;
- }}
+ *ret_object = object;
+ return 0;
+}
+
+static const char* object_class_to_string(CK_OBJECT_CLASS class) {
+ switch (class) {
+ case CKO_CERTIFICATE:
+ return "CKO_CERTIFICATE";
+ case CKO_PUBLIC_KEY:
+ return "CKO_PUBLIC_KEY";
+ case CKO_PRIVATE_KEY:
+ return "CKO_PRIVATE_KEY";
+ case CKO_SECRET_KEY:
+ return "CKO_SECRET_KEY";
+ default:
+ return NULL;
}
+}
- if (!found_decrypt || !found_class || !found_key_type) {
- /* Hmm, let's slightly extend the attribute list we search for */
+/* Returns an object with the given class and the same CKA_ID or CKA_LABEL as prototype */
+int pkcs11_token_find_related_object(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE prototype,
+ CK_OBJECT_CLASS class,
+ CK_OBJECT_HANDLE *ret_object ) {
- attributes_buffer = new(CK_ATTRIBUTE, n_attributes + !found_decrypt + !found_class + !found_key_type);
- if (!attributes_buffer)
+ _cleanup_free_ void *buffer = NULL;
+ CK_ATTRIBUTE attributes[] = {
+ { CKA_ID, NULL_PTR, 0 },
+ { CKA_LABEL, NULL_PTR, 0 }
+ };
+ CK_OBJECT_CLASS search_class = class;
+ CK_ATTRIBUTE search_attributes[2] = {
+ { CKA_CLASS, &search_class, sizeof(search_class) }
+ };
+ CK_ULONG n_objects;
+ CK_OBJECT_HANDLE objects[2];
+ CK_RV rv;
+
+ rv = m->C_GetAttributeValue(session, prototype, attributes, ELEMENTSOF(attributes));
+ if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve length of attributes: %s", sym_p11_kit_strerror(rv));
+
+ if (attributes[0].ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+ buffer = malloc(attributes[0].ulValueLen);
+ if (!buffer)
return log_oom();
- memcpy(attributes_buffer, attributes, sizeof(CK_ATTRIBUTE) * n_attributes);
+ attributes[0].pValue = buffer;
+ rv = m->C_GetAttributeValue(session, prototype, &attributes[0], 1);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve CKA_ID: %s", sym_p11_kit_strerror(rv));
- if (!found_decrypt) {
- static const CK_BBOOL yes = true;
+ search_attributes[1] = attributes[0];
- attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
- .type = CKA_DECRYPT,
- .pValue = (CK_BBOOL*) &yes,
- .ulValueLen = sizeof(yes),
- };
- }
+ } else if (attributes[1].ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+ buffer = malloc(attributes[1].ulValueLen);
+ if (!buffer)
+ return log_oom();
- if (!found_class) {
- static const CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
+ attributes[1].pValue = buffer;
+ rv = m->C_GetAttributeValue(session, prototype, &attributes[1], 1);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve CKA_LABEL: %s", sym_p11_kit_strerror(rv));
- attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
- .type = CKA_CLASS,
- .pValue = (CK_OBJECT_CLASS*) &class,
- .ulValueLen = sizeof(class),
- };
- }
+ search_attributes[1] = attributes[1];
- if (!found_key_type) {
- static const CK_KEY_TYPE type = CKK_RSA;
+ } else
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "The prototype does not have CKA_ID or CKA_LABEL");
- attributes_buffer[n_attributes++] = (CK_ATTRIBUTE) {
- .type = CKA_KEY_TYPE,
- .pValue = (CK_KEY_TYPE*) &type,
- .ulValueLen = sizeof(type),
- };
- }
+ rv = m->C_FindObjectsInit(session, search_attributes, 2);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
- attributes = attributes_buffer;
+ rv = m->C_FindObjects(session, objects, 2, &n_objects);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to find objects: %s", sym_p11_kit_strerror(rv));
+
+ rv = m->C_FindObjectsFinal(session);
+ if (rv != CKR_OK)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
+
+ if (n_objects == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
+ "Failed to find a related object with class %s", object_class_to_string(class));
+
+ if (n_objects > 1)
+ log_warning("Found multiple related objects with class %s, using the first object.",
+ object_class_to_string(class));
+
+ *ret_object = objects[0];
+ return 0;
+}
+
+#if HAVE_OPENSSL
+static int ecc_convert_to_compressed(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ const void *uncompressed_point,
+ size_t uncompressed_point_size,
+ void **ret_compressed_point,
+ size_t *ret_compressed_point_size) {
+
+ _cleanup_free_ void *ec_params_buffer = NULL;
+ CK_ATTRIBUTE ec_params_attr = { CKA_EC_PARAMS, NULL_PTR, 0 };
+ CK_RV rv;
+ int r;
+
+ rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1);
+ if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv));
+
+ if (ec_params_attr.ulValueLen != CK_UNAVAILABLE_INFORMATION) {
+ ec_params_buffer = malloc(ec_params_attr.ulValueLen);
+ if (!ec_params_buffer)
+ return log_oom();
+
+ ec_params_attr.pValue = ec_params_buffer;
+ rv = m->C_GetAttributeValue(session, object, &ec_params_attr, 1);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve CKA_EC_PARAMS from a private key: %s", sym_p11_kit_strerror(rv));
+ } else {
+ CK_OBJECT_HANDLE public_key;
+ r = pkcs11_token_find_related_object(m, session, object, CKO_PUBLIC_KEY, &public_key);
+ if (r < 0)
+ return log_error_errno(r, "Failed to find a public key for compressing a EC point");
+
+ ec_params_attr.ulValueLen = 0;
+ rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1);
+ if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve length of CKA_EC_PARAMS: %s", sym_p11_kit_strerror(rv));
+
+ if (ec_params_attr.ulValueLen == CK_UNAVAILABLE_INFORMATION)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "The public key does not have CKA_EC_PARAMS");
+
+ ec_params_buffer = malloc(ec_params_attr.ulValueLen);
+ if (!ec_params_buffer)
+ return log_oom();
+
+ ec_params_attr.pValue = ec_params_buffer;
+ rv = m->C_GetAttributeValue(session, public_key, &ec_params_attr, 1);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Failed to retrieve CKA_EC_PARAMS from a public key: %s", sym_p11_kit_strerror(rv));
}
- rv = m->C_FindObjectsInit(session, attributes, n_attributes);
+ _cleanup_(EC_GROUP_freep) EC_GROUP *group = NULL;
+ _cleanup_(EC_POINT_freep) EC_POINT *point = NULL;
+ _cleanup_(BN_CTX_freep) BN_CTX *bnctx = NULL;
+ _cleanup_free_ void *compressed_point = NULL;
+ size_t compressed_point_size;
+
+ const unsigned char *ec_params_value = ec_params_attr.pValue;
+ group = d2i_ECPKParameters(NULL, &ec_params_value, ec_params_attr.ulValueLen);
+ if (!group)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode CKA_EC_PARAMS");
+
+ point = EC_POINT_new(group);
+ if (!point)
+ return log_oom();
+
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ return log_oom();
+
+ if (EC_POINT_oct2point(group, point, uncompressed_point, uncompressed_point_size, bnctx) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to decode an uncompressed EC point");
+
+ compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, NULL, 0, bnctx);
+ if (compressed_point_size == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine size of a compressed EC point");
+
+ compressed_point = malloc(compressed_point_size);
+ if (!compressed_point)
+ return log_oom();
+
+ compressed_point_size = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, compressed_point, compressed_point_size, bnctx);
+ if (compressed_point_size == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert a EC point to compressed format");
+
+ *ret_compressed_point = TAKE_PTR(compressed_point);
+ *ret_compressed_point_size = compressed_point_size;
+ return 0;
+}
+#endif
+
+/* Since EC keys doesn't support encryption directly, we use ECDH protocol to derive shared secret here.
+ * We use PKCS#11 C_DeriveKey function to derive a shared secret with a private key stored in the token and
+ * a public key saved on enrollment. */
+static int pkcs11_token_decrypt_data_ecc(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ const void *encrypted_data,
+ size_t encrypted_data_size,
+ void **ret_decrypted_data,
+ size_t *ret_decrypted_data_size) {
+
+ static const CK_BBOOL yes = CK_TRUE, no = CK_FALSE;
+ static const CK_OBJECT_CLASS shared_secret_class = CKO_SECRET_KEY;
+ static const CK_KEY_TYPE shared_secret_type = CKK_GENERIC_SECRET;
+ static const CK_ATTRIBUTE shared_secret_template[] = {
+ { CKA_TOKEN, (void*) &no, sizeof(no) },
+ { CKA_CLASS, (void*) &shared_secret_class, sizeof(shared_secret_class) },
+ { CKA_KEY_TYPE, (void*) &shared_secret_type, sizeof(shared_secret_type) },
+ { CKA_SENSITIVE, (void*) &no, sizeof(no) },
+ { CKA_EXTRACTABLE, (void*) &yes, sizeof(yes) }
+ };
+ CK_ECDH1_DERIVE_PARAMS params = {
+ .kdf = CKD_NULL,
+ .pPublicData = (void*) encrypted_data,
+ .ulPublicDataLen = encrypted_data_size
+ };
+ CK_MECHANISM mechanism = {
+ .mechanism = CKM_ECDH1_DERIVE,
+ .pParameter = ¶ms,
+ .ulParameterLen = sizeof(params)
+ };
+ CK_OBJECT_HANDLE shared_secret_handle;
+ CK_SESSION_INFO session_info;
+ CK_MECHANISM_INFO mechanism_info;
+ CK_RV rv, rv2;
+#if HAVE_OPENSSL
+ _cleanup_free_ void *compressed_point = NULL;
+ int r;
+#endif
+
+ rv = m->C_GetSessionInfo(session, &session_info);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to initialize object find call: %s", sym_p11_kit_strerror(rv));
+ "Failed to get information about the PKCS#11 session: %s", sym_p11_kit_strerror(rv));
- rv = m->C_FindObjects(session, objects, ELEMENTSOF(objects), &n_objects);
- rv2 = m->C_FindObjectsFinal(session);
+ rv = m->C_GetMechanismInfo(session_info.slotID, CKM_ECDH1_DERIVE, &mechanism_info);
if (rv != CKR_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to find objects: %s", sym_p11_kit_strerror(rv));
+ "Failed to get information about CKM_ECDH1_DERIVE: %s", sym_p11_kit_strerror(rv));
+
+ if (!(mechanism_info.flags & CKF_EC_UNCOMPRESS)) {
+ if (mechanism_info.flags & CKF_EC_COMPRESS) {
+#if HAVE_OPENSSL
+ log_debug("CKM_ECDH1_DERIVE accepts compressed EC points only, trying to convert.");
+ size_t compressed_point_size;
+ r = ecc_convert_to_compressed(m, session, object, encrypted_data, encrypted_data_size, &compressed_point, &compressed_point_size);
+ if (r < 0)
+ return r;
+
+ params.pPublicData = compressed_point;
+ params.ulPublicDataLen = compressed_point_size;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "CKM_ECDH1_DERIVE does not support uncompressed format of EC points");
+#endif
+ } else
+ log_debug("Both CKF_EC_UNCOMPRESS and CKF_EC_COMPRESS are false for CKM_ECDH1_DERIVE, ignoring.");
+ }
+
+ rv = m->C_DeriveKey(session, &mechanism, object, (CK_ATTRIBUTE*) shared_secret_template, ELEMENTSOF(shared_secret_template), &shared_secret_handle);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to derive a shared secret: %s", sym_p11_kit_strerror(rv));
+
+ CK_ATTRIBUTE shared_secret_attr = { CKA_VALUE, NULL_PTR, 0};
+
+ rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1);
+ if (rv != CKR_OK) {
+ rv2 = m->C_DestroyObject(session, shared_secret_handle);
+ if (rv2 != CKR_OK)
+ log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2));
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve shared secret length: %s", sym_p11_kit_strerror(rv));
+ }
+
+ shared_secret_attr.pValue = malloc(shared_secret_attr.ulValueLen);
+ if (!shared_secret_attr.pValue)
+ return log_oom();
+
+ rv = m->C_GetAttributeValue(session, shared_secret_handle, &shared_secret_attr, 1);
+ rv2 = m->C_DestroyObject(session, shared_secret_handle);
if (rv2 != CKR_OK)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Failed to finalize object find call: %s", sym_p11_kit_strerror(rv));
- if (n_objects == 0)
- return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
- "Failed to find selected private key suitable for decryption on token.");
- if (n_objects > 1)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
- "Configured private key URI matches multiple keys, refusing.");
+ log_warning("Failed to destroy a shared secret, ignoring: %s", sym_p11_kit_strerror(rv2));
- *ret_object = objects[0];
+ if (rv != CKR_OK) {
+ erase_and_free(shared_secret_attr.pValue);
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve a shared secret: %s", sym_p11_kit_strerror(rv));
+ }
+
+ log_info("Successfully derived key with security token.");
+
+ *ret_decrypted_data = shared_secret_attr.pValue;
+ *ret_decrypted_data_size = shared_secret_attr.ulValueLen;
return 0;
}
-int pkcs11_token_decrypt_data(
+static int pkcs11_token_decrypt_data_rsa(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session,
CK_OBJECT_HANDLE object,
_cleanup_(erase_and_freep) CK_BYTE *dbuffer = NULL;
CK_ULONG dbuffer_size = 0;
CK_RV rv;
- int r;
-
- assert(m);
- assert(encrypted_data);
- assert(encrypted_data_size > 0);
- assert(ret_decrypted_data);
- assert(ret_decrypted_data_size);
-
- r = dlopen_p11kit();
- if (r < 0)
- return r;
rv = m->C_DecryptInit(session, (CK_MECHANISM*) &mechanism, object);
if (rv != CKR_OK)
return 0;
}
+int pkcs11_token_decrypt_data(
+ CK_FUNCTION_LIST *m,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ const void *encrypted_data,
+ size_t encrypted_data_size,
+ void **ret_decrypted_data,
+ size_t *ret_decrypted_data_size) {
+
+ CK_KEY_TYPE key_type;
+ CK_ATTRIBUTE key_type_template = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
+ CK_RV rv;
+
+ assert(m);
+ assert(encrypted_data);
+ assert(encrypted_data_size > 0);
+ assert(ret_decrypted_data);
+ assert(ret_decrypted_data_size);
+
+ rv = m->C_GetAttributeValue(session, object, &key_type_template, 1);
+ if (rv != CKR_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve private key type");
+
+ switch (key_type) {
+
+ case CKK_RSA:
+ return pkcs11_token_decrypt_data_rsa(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
+
+ case CKK_EC:
+ return pkcs11_token_decrypt_data_ecc(m, session, object, encrypted_data, encrypted_data_size, ret_decrypted_data, ret_decrypted_data_size);
+
+ default:
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported private key type: %lu", key_type);
+ }
+}
+
int pkcs11_token_acquire_rng(
CK_FUNCTION_LIST *m,
CK_SESSION_HANDLE session) {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#if HAVE_OPENSSL
+# include <openssl/x509.h>
+#endif
#include <stdbool.h>
#if HAVE_P11KIT
#include "ask-password-api.h"
#include "macro.h"
-#include "openssl-util.h"
#include "time-util.h"
bool pkcs11_uri_valid(const char *uri);
int pkcs11_token_login_by_pin(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, const CK_TOKEN_INFO *token_info, const char *token_label, const void *pin, size_t pin_size);
int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *key_name, const char *credential_name, usec_t until, AskPasswordFlags ask_password_flags, bool headless, char **ret_used_pin);
+int pkcs11_token_find_related_object(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE prototype, CK_OBJECT_CLASS class, CK_OBJECT_HANDLE *ret_object);
int pkcs11_token_find_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, P11KitUri *search_uri, CK_OBJECT_HANDLE *ret_object);
#if HAVE_OPENSSL
int pkcs11_token_read_x509_certificate(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, X509 **ret_cert);
#include "alloc-util.h"
#include "errno-util.h"
+#include "extract-word.h"
#include "fd-util.h"
+#include "io-util.h"
#include "log.h"
#include "macro.h"
#include "ptyfwd.h"
#include "stat-util.h"
+#include "strv.h"
#include "terminal-util.h"
#include "time-util.h"
+typedef enum AnsiColorState {
+ ANSI_COLOR_STATE_TEXT,
+ ANSI_COLOR_STATE_ESC,
+ ANSI_COLOR_STATE_CSI_SEQUENCE,
+ ANSI_COLOR_STATE_NEWLINE,
+ _ANSI_COLOR_STATE_MAX,
+ _ANSI_COLOR_STATE_INVALID = -EINVAL,
+} AnsiColorState;
+
struct PTYForward {
sd_event *event;
bool last_char_set:1;
char last_char;
- char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
+ char in_buffer[LINE_MAX], *out_buffer;
+ size_t out_buffer_size;
size_t in_buffer_full, out_buffer_full;
usec_t escape_timestamp;
PTYForwardHandler handler;
void *userdata;
+
+ char *background_color;
+ AnsiColorState ansi_color_state;
+ char *csi_sequence;
};
#define ESCAPE_USEC (1*USEC_PER_SEC)
/* STDIN/STDOUT should not be non-blocking normally, so let's reset it */
(void) fd_nonblock(f->output_fd, false);
+
+ if (colors_enabled())
+ (void) loop_write(f->output_fd, ANSI_NORMAL ANSI_ERASE_TO_END_OF_LINE, SIZE_MAX);
+
if (f->close_output_fd)
f->output_fd = safe_close(f->output_fd);
}
}
f->saved_stdout = f->saved_stdin = false;
+
+ f->out_buffer = mfree(f->out_buffer);
+ f->out_buffer_size = 0;
+ f->out_buffer_full = 0;
+ f->in_buffer_full = 0;
+
+ f->csi_sequence = mfree(f->csi_sequence);
+ f->ansi_color_state = _ANSI_COLOR_STATE_INVALID;
}
static int pty_forward_done(PTYForward *f, int rcode) {
return true;
}
+static char *background_color_sequence(PTYForward *f) {
+ assert(f);
+ assert(f->background_color);
+
+ /* This sets the background color to the desired one, and erase the rest of the line with it */
+
+ return strjoin("\x1B[", f->background_color, "m", ANSI_ERASE_TO_END_OF_LINE);
+}
+
+static int insert_string(PTYForward *f, size_t offset, const char *s) {
+ assert(f);
+ assert(offset <= f->out_buffer_full);
+ assert(s);
+
+ size_t l = strlen(s);
+ assert(l <= INT_MAX); /* Make sure we can still return this */
+
+ void *p = realloc(f->out_buffer, MAX(f->out_buffer_full + l, (size_t) LINE_MAX));
+ if (!p)
+ return -ENOMEM;
+
+ f->out_buffer = p;
+ f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer);
+
+ memmove(f->out_buffer + offset + l, f->out_buffer + offset, f->out_buffer_full - offset);
+ memcpy(f->out_buffer + offset, s, l);
+ f->out_buffer_full += l;
+
+ return (int) l;
+}
+
+static int insert_erase_newline(PTYForward *f, size_t offset) {
+ _cleanup_free_ char *s = NULL;
+
+ assert(f);
+ assert(f->background_color);
+
+ s = background_color_sequence(f);
+ if (!s)
+ return -ENOMEM;
+
+ return insert_string(f, offset, s);
+}
+
+static int is_csi_background_reset_sequence(const char *seq) {
+ enum {
+ COLOR_TOKEN_NO,
+ COLOR_TOKEN_START,
+ COLOR_TOKEN_8BIT,
+ COLOR_TOKEN_24BIT_R,
+ COLOR_TOKEN_24BIT_G,
+ COLOR_TOKEN_24BIT_B,
+ } token_state = COLOR_TOKEN_NO;
+
+ bool b = false;
+ int r;
+
+ assert(seq);
+
+ /* This parses CSI "m" sequences, and determines if they reset the background color. If so returns
+ * 1. This can then be used to insert another sequence that sets the color to the desired
+ * replacement. */
+
+ for (;;) {
+ _cleanup_free_ char *token = NULL;
+
+ r = extract_first_word(&seq, &token, ";", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ switch (token_state) {
+
+ case COLOR_TOKEN_NO:
+
+ if (STR_IN_SET(token, "", "0", "00", "49"))
+ b = true; /* These tokens set the background back to normal */
+ else if (STR_IN_SET(token, "40", "41", "42", "43", "44", "45", "46", "47", "48"))
+ b = false; /* And these tokens set them to something other than normal */
+
+ if (STR_IN_SET(token, "38", "48", "58"))
+ token_state = COLOR_TOKEN_START; /* These tokens mean an 8bit or 24bit color will follow */
+ break;
+
+ case COLOR_TOKEN_START:
+
+ if (STR_IN_SET(token, "5", "05"))
+ token_state = COLOR_TOKEN_8BIT; /* 8bit color */
+ else if (STR_IN_SET(token, "2", "02"))
+ token_state = COLOR_TOKEN_24BIT_R; /* 24bit color */
+ else
+ token_state = COLOR_TOKEN_NO; /* something weird? */
+ break;
+
+ case COLOR_TOKEN_24BIT_R:
+ token_state = COLOR_TOKEN_24BIT_G;
+ break;
+
+ case COLOR_TOKEN_24BIT_G:
+ token_state = COLOR_TOKEN_24BIT_B;
+ break;
+
+ case COLOR_TOKEN_8BIT:
+ case COLOR_TOKEN_24BIT_B:
+ token_state = COLOR_TOKEN_NO;
+ break;
+ }
+ }
+
+ return b;
+}
+
+static int insert_background_fix(PTYForward *f, size_t offset) {
+ assert(f);
+ assert(f->background_color);
+
+ if (!is_csi_background_reset_sequence(strempty(f->csi_sequence)))
+ return 0;
+
+ _cleanup_free_ char *s = NULL;
+ s = strjoin(";", f->background_color);
+ if (!s)
+ return -ENOMEM;
+
+ return insert_string(f, offset, s);
+}
+
+static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
+ int r;
+
+ assert(f);
+ assert(offset <= f->out_buffer_full);
+
+ if (!f->background_color)
+ return 0;
+
+ for (size_t i = offset; i < f->out_buffer_full; i++) {
+ char c = f->out_buffer[i];
+
+ switch (f->ansi_color_state) {
+
+ case ANSI_COLOR_STATE_TEXT:
+ if (c == '\n')
+ f->ansi_color_state = ANSI_COLOR_STATE_NEWLINE;
+ if (c == 0x1B) /* ESC */
+ f->ansi_color_state = ANSI_COLOR_STATE_ESC;
+ break;
+
+ case ANSI_COLOR_STATE_NEWLINE: {
+ /* Immediately after a newline insert an ANSI sequence to erase the line with a background color */
+
+ r = insert_erase_newline(f, i);
+ if (r < 0)
+ return r;
+
+ i += r;
+
+ f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+ break;
+ }
+
+ case ANSI_COLOR_STATE_ESC: {
+
+ if (c == '[')
+ f->ansi_color_state = ANSI_COLOR_STATE_CSI_SEQUENCE;
+ else
+ f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+
+ break;
+ }
+
+ case ANSI_COLOR_STATE_CSI_SEQUENCE: {
+
+ if (c >= 0x20 && c <= 0x3F) {
+ /* If this is a "parameter" or "intermediary" byte (i.e. ranges 0x20…0x2F and
+ * 0x30…0x3F) then we are still in the CSI sequence */
+
+ if (strlen_ptr(f->csi_sequence) >= 64) {
+ /* Safety check: lets not accept unbounded CSI sequences */
+
+ f->csi_sequence = mfree(f->csi_sequence);
+ f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+ } else if (!strextend(&f->csi_sequence, CHAR_TO_STR(c)))
+ return -ENOMEM;
+ } else {
+ /* Otherwise, the CSI sequence is over */
+
+ if (c == 'm') {
+ /* This is an "SGR" (Select Graphic Rendition) sequence. Patch in our background color. */
+ r = insert_background_fix(f, i);
+ if (r < 0)
+ return r;
+
+ i += r;
+ }
+
+ f->csi_sequence = mfree(f->csi_sequence);
+ f->ansi_color_state = ANSI_COLOR_STATE_TEXT;
+ }
+
+ break;
+ }
+
+ default:
+ assert_not_reached();
+ }
+ }
+
+ return 0;
+}
+
static int shovel(PTYForward *f) {
ssize_t k;
+ int r;
assert(f);
+ if (f->out_buffer_size == 0 && f->background_color) {
+ /* Erase the first line when we start */
+ f->out_buffer = background_color_sequence(f);
+ if (!f->out_buffer)
+ return pty_forward_done(f, log_oom());
+
+ f->out_buffer_full = strlen(f->out_buffer);
+ f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer);
+ }
+
+ if (f->out_buffer_size < LINE_MAX) {
+ /* Make sure we always have room for at least one "line" */
+ void *p = realloc(f->out_buffer, LINE_MAX);
+ if (!p)
+ return pty_forward_done(f, log_oom());
+
+ f->out_buffer = p;
+ f->out_buffer_size = MALLOC_SIZEOF_SAFE(p);
+ }
+
while ((f->stdin_readable && f->in_buffer_full <= 0) ||
(f->master_writable && f->in_buffer_full > 0) ||
(f->master_readable && f->out_buffer_full <= 0) ||
}
}
- if (f->master_readable && f->out_buffer_full < LINE_MAX) {
+ if (f->master_readable && f->out_buffer_full < MIN(f->out_buffer_size, (size_t) LINE_MAX)) {
- k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full);
+ k = read(f->master, f->out_buffer + f->out_buffer_full, f->out_buffer_size - f->out_buffer_full);
if (k < 0) {
/* Note that EIO on the master device might be caused by vhangup() or
}
} else {
f->read_from_master = true;
+ size_t scan_index = f->out_buffer_full;
f->out_buffer_full += (size_t) k;
+
+ r = pty_forward_ansi_process(f, scan_index);
+ if (r < 0)
+ return pty_forward_done(f, log_error_errno(r, "Failed to scan for ANSI sequences: %m"));
}
}
}
PTYForward *pty_forward_free(PTYForward *f) {
+ if (!f)
+ return NULL;
pty_forward_disconnect(f);
+ free(f->background_color);
return mfree(f);
}
return 0;
}
+
+int pty_forward_set_background_color(PTYForward *f, const char *color) {
+ assert(f);
+
+ return free_and_strdup(&f->background_color, color);
+}
int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height);
+int pty_forward_set_background_color(PTYForward *f, const char *color);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free);
if (!GREEDY_REALLOC(result, j + k + l + 1))
return -ENOMEM;
- memcpy(result + j, w, k);
- t = result + j + k;
+ t = mempcpy(result + j, w, k);
} else if (strchr(POSSIBLE_SPECIFIERS, *f))
/* Oops, an unknown specifier. */
return -EBADSLT;
#include "fd-util.h"
#include "fs-util.h"
#include "log.h"
+#include "mountpoint-util.h"
#include "namespace-util.h"
#include "path-util.h"
#include "process-util.h"
if (r < 0)
return bus_log_parse_error(r);
- r = bus_wait_for_jobs_one(w, object, false, NULL);
+ r = bus_wait_for_jobs_one(w, object, BUS_WAIT_JOBS_LOG_ERROR, NULL);
if (r < 0)
return r;
log_warning_errno(r, "Couldn't allocate a scope unit for this test, proceeding without.");
r = cg_pid_get_path(NULL, 0, &cgroup_root);
- if (r == -ENOMEDIUM)
+ if (IN_SET(r, -ENOMEDIUM, -ENOENT))
return log_warning_errno(r, "cg_pid_get_path(NULL, 0, ...) failed: %m");
assert(r >= 0);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.Credentials.h"
+
+static VARLINK_DEFINE_METHOD(
+ Encrypt,
+ VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(text, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(data, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(notAfter, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_OUTPUT(blob, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_METHOD(
+ Decrypt,
+ VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(blob, VARLINK_STRING, 0),
+ VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_OUTPUT(data, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_ERROR(BadFormat);
+static VARLINK_DEFINE_ERROR(NameMismatch);
+static VARLINK_DEFINE_ERROR(TimeMismatch);
+
+VARLINK_DEFINE_INTERFACE(
+ io_systemd_Credentials,
+ "io.systemd.Credentials",
+ &vl_method_Encrypt,
+ &vl_method_Decrypt,
+ &vl_error_BadFormat,
+ &vl_error_NameMismatch,
+ &vl_error_TimeMismatch);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_Credentials;
r = v->reply_callback(v, NULL, error, VARLINK_REPLY_ERROR|VARLINK_REPLY_LOCAL, v->userdata);
if (r < 0)
- log_debug_errno(r, "Reply callback returned error, ignoring: %m");
+ varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m");
return 1;
}
}
static int varlink_sanitize_parameters(JsonVariant **v) {
+ int r;
+
assert(v);
/* Varlink always wants a parameters list, hence make one if the caller doesn't want any */
if (!*v)
return json_variant_new_object(v, NULL, 0);
- else if (!json_variant_is_object(*v))
+ if (json_variant_is_null(*v)) {
+ JsonVariant *empty;
+
+ r = json_variant_new_object(&empty, NULL, 0);
+ if (r < 0)
+ return r;
+
+ json_variant_unref(*v);
+ *v = empty;
+ return 0;
+ }
+ if (!json_variant_is_object(*v))
return -EINVAL;
return 0;
} else if (streq(k, "parameters")) {
if (parameters)
goto invalid;
- if (!json_variant_is_object(e))
+ if (!json_variant_is_object(e) && !json_variant_is_null(e))
goto invalid;
parameters = json_variant_ref(e);
if (v->reply_callback) {
r = v->reply_callback(v, parameters, error, flags, v->userdata);
if (r < 0)
- log_debug_errno(r, "Reply callback returned error, ignoring: %m");
+ varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m");
}
varlink_clear_current(v);
assert(link);
if (json_variant_elements(parameters) != 0)
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER,
- JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR_VARIANT("parameter", json_variant_by_index(parameters, 0))));
+ return varlink_error_invalid_parameter(link, parameters);
product = strjoin("systemd (", program_invocation_short_name, ")");
if (!product)
} else if (streq(k, "parameters")) {
if (parameters)
goto invalid;
- if (!json_variant_is_object(e))
+ if (!json_variant_is_object(e) && !json_variant_is_null(e))
goto invalid;
parameters = json_variant_ref(e);
v->current_method = hashmap_get(v->server->symbols, method);
if (!v->current_method)
- log_debug("No interface description defined for method '%s', not validating.", method);
+ varlink_log(v, "No interface description defined for method '%s', not validating.", method);
else {
const char *bad_field;
r = varlink_idl_validate_method_call(v->current_method, parameters, &bad_field);
if (r < 0) {
- log_debug_errno(r, "Parameters for method %s() didn't pass validation on field '%s': %m", method, strna(bad_field));
+ varlink_log_errno(v, r, "Parameters for method %s() didn't pass validation on field '%s': %m", method, strna(bad_field));
- if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
- r = varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", bad_field)));
+ if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
+ r = varlink_error_invalid_parameter_name(v, bad_field);
if (r < 0)
return r;
}
if (!invalid) {
r = callback(v, parameters, flags, v->userdata);
if (r < 0) {
- log_debug_errno(r, "Callback for %s returned error: %m", method);
+ varlink_log_errno(v, r, "Callback for %s returned error: %m", method);
/* We got an error back from the callback. Propagate it to the client if the method call remains unanswered. */
- if (v->state == VARLINK_PROCESSED_METHOD)
- r = 0; /* already processed */
- else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
+ if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
r = varlink_error_errno(v, r);
if (r < 0)
return r;
}
}
}
- } else if (!FLAGS_SET(flags, VARLINK_METHOD_ONEWAY)) {
+ } else if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
r = varlink_errorb(v, VARLINK_ERROR_METHOD_NOT_FOUND, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("method", JSON_BUILD_STRING(method))));
if (r < 0)
return r;
- } else
- r = 0;
+ }
switch (v->state) {
assert_not_reached();
}
- return r;
+ return 1;
invalid:
r = -EINVAL;
r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
if (r < 0)
- log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
+ varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
}
r = varlink_enqueue_json(v, m);
VarlinkSymbol *symbol = hashmap_get(v->server->symbols, error_id);
if (!symbol)
- log_debug("No interface description defined for error '%s', not validating.", error_id);
+ varlink_log(v, "No interface description defined for error '%s', not validating.", error_id);
else {
const char *bad_field = NULL;
r = varlink_idl_validate_error(symbol, parameters, &bad_field);
if (r < 0)
- log_debug_errno(r, "Parameters for error %s didn't pass validation on field '%s', ignoring: %m", error_id, strna(bad_field));
+ varlink_log_errno(v, r, "Parameters for error %s didn't pass validation on field '%s', ignoring: %m", error_id, strna(bad_field));
}
r = varlink_enqueue_json(v, m);
return -EINVAL;
}
+int varlink_error_invalid_parameter_name(Varlink *v, const char *name) {
+ return varlink_errorb(
+ v,
+ VARLINK_ERROR_INVALID_PARAMETER,
+ JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameter", JSON_BUILD_STRING(name))));
+}
+
int varlink_error_errno(Varlink *v, int error) {
return varlink_errorb(
v,
r = varlink_idl_validate_method_reply(v->current_method, parameters, &bad_field);
if (r < 0)
- log_debug_errno(r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
+ varlink_log_errno(v, r, "Return parameters for method reply %s() didn't pass validation on field '%s', ignoring: %m", v->current_method->name, strna(bad_field));
}
r = varlink_enqueue_json(v, m);
r = json_dispatch_full(parameters, table, /* bad= */ NULL, /* flags= */ 0, userdata, &bad_field);
if (r < 0) {
if (bad_field)
- return varlink_errorb(v, VARLINK_ERROR_INVALID_PARAMETER,
- JSON_BUILD_OBJECT(JSON_BUILD_PAIR("parameter", JSON_BUILD_STRING(bad_field))));
+ return varlink_error_invalid_parameter_name(v, bad_field);
return r;
}
server->n_connections++;
if (FLAGS_SET(server->flags, VARLINK_SERVER_ACCOUNT_UID)) {
+ assert(uid_is_valid(ucred->uid));
+
r = hashmap_ensure_allocated(&server->by_uid, NULL);
if (r < 0)
- return log_debug_errno(r, "Failed to allocate UID hash table: %m");
+ return varlink_server_log_errno(server, r, "Failed to allocate UID hash table: %m");
c = PTR_TO_UINT(hashmap_get(server->by_uid, UID_TO_PTR(ucred->uid)));
r = hashmap_replace(server->by_uid, UID_TO_PTR(ucred->uid), UINT_TO_PTR(c + 1));
if (r < 0)
- return log_debug_errno(r, "Failed to increment counter in UID hash table: %m");
+ return varlink_server_log_errno(server, r, "Failed to increment counter in UID hash table: %m");
}
return 0;
if (varlink_symbol_in_interface(method, "org.varlink.service") ||
varlink_symbol_in_interface(method, "io.systemd"))
- return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Cannot bind server to '%s'.", method);
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EEXIST), "Cannot bind server to '%s'.", method);
m = strdup(method);
if (!m)
if (r == -ENOMEM)
return log_oom_debug();
if (r < 0)
- return log_debug_errno(r, "Failed to register callback: %m");
+ return varlink_server_log_errno(s, r, "Failed to register callback: %m");
if (r > 0)
TAKE_PTR(m);
assert_return(s, -EINVAL);
if (callback && s->connect_callback && callback != s->connect_callback)
- return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
s->connect_callback = callback;
return 0;
assert_return(s, -EINVAL);
if (callback && s->disconnect_callback && callback != s->disconnect_callback)
- return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBUSY), "A different callback was already set.");
s->disconnect_callback = callback;
return 0;
assert_return(interface->name, -EINVAL);
if (hashmap_contains(s->interfaces, interface->name))
- return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), "Duplicate registration of interface '%s'.", interface->name);
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EEXIST), "Duplicate registration of interface '%s'.", interface->name);
r = hashmap_ensure_put(&s->interfaces, &string_hash_ops, interface->name, (void*) interface);
if (r < 0)
return log_oom_debug();
if (v[n] != ' ')
- return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EINVAL),
"Failed to deserialize VarlinkServerSocket: %s: %m", value);
v = startswith(v + n + 1, "varlink-server-socket-fd=");
if (!v)
- return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EINVAL),
"Failed to deserialize VarlinkServerSocket fd %s: %m", value);
n = strcspn(v, " ");
fd = parse_fd(buf);
if (fd < 0)
- return log_debug_errno(fd, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf);
+ return varlink_server_log_errno(s, fd, "Unable to parse VarlinkServerSocket varlink-server-socket-fd=%s: %m", buf);
if (!fdset_contains(fds, fd))
- return log_debug_errno(SYNTHETIC_ERRNO(EBADF),
+ return varlink_server_log_errno(s, SYNTHETIC_ERRNO(EBADF),
"VarlinkServerSocket varlink-server-socket-fd= has unknown fd %d: %m", fd);
ss = new(VarlinkServerSocket, 1);
r = varlink_server_add_socket_event_source(s, ss, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
- return log_debug_errno(r, "Failed to add VarlinkServerSocket event source to the event loop: %m");
+ return varlink_server_log_errno(s, r, "Failed to add VarlinkServerSocket event source to the event loop: %m");
LIST_PREPEND(sockets, s->sockets, TAKE_PTR(ss));
return 0;
int varlink_error(Varlink *v, const char *error_id, JsonVariant *parameters);
int varlink_errorb(Varlink *v, const char *error_id, ...);
int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters);
+int varlink_error_invalid_parameter_name(Varlink *v, const char *name);
int varlink_error_errno(Varlink *v, int error);
/* Enqueue a "more" reply */
#define VARLINK_ERROR_METHOD_NOT_FOUND "org.varlink.service.MethodNotFound"
#define VARLINK_ERROR_METHOD_NOT_IMPLEMENTED "org.varlink.service.MethodNotImplemented"
#define VARLINK_ERROR_INVALID_PARAMETER "org.varlink.service.InvalidParameter"
-
-/* These are errors we came up with and squatted the namespace with */
#define VARLINK_ERROR_PERMISSION_DENIED "org.varlink.service.PermissionDenied"
#define VARLINK_ERROR_EXPECTED_MORE "org.varlink.service.ExpectedMore"
goto error;
}
+ /* This is primarily useful when running systemd in a VM, as it provides the user running the VM with
+ * a mechanism to pick up systemd's exit status in the VM. Note that we execute this as early as
+ * possible since otherwise we might shut down the VM before the AF_VSOCK buffers have been flushed.
+ * While this doesn't guarantee the message will arrive, in practice we do enough work after this
+ * that the message should always arrive on the host */
+ (void) sd_notifyf(0, "EXIT_STATUS=%i", arg_exit_code);
+
(void) cg_get_root_path(&cgroup);
bool in_container = detect_container() > 0;
if (!in_container)
sync_with_progress();
- /* This is primarily useful when running systemd in a VM, as it provides the user running the VM with
- * a mechanism to pick up systemd's exit status in the VM. */
- (void) sd_notifyf(0, "EXIT_STATUS=%i", arg_exit_code);
-
if (streq(arg_verb, "exit")) {
if (in_container) {
log_info("Exiting container.");
c = image_class_from_string(value);
if (!IN_SET(c, IMAGE_SYSEXT, IMAGE_CONFEXT))
- return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "class")));
+ return varlink_error_invalid_parameter_name(link, "class");
if (hierarchies) {
r = parse_env_extension_hierarchies(&h, image_class_info[c].name_env);
}
int verb_start_special(int argc, char *argv[], void *userdata) {
- bool termination_action; /* An action that terminates the manager, can be performed also by
- * signal. */
+ bool termination_action; /* An action that terminates the system, can be performed also by signal. */
enum action a;
int r;
return r;
}
- r = prepare_firmware_setup();
- if (r < 0)
- return r;
+ termination_action = IN_SET(a, ACTION_HALT, ACTION_POWEROFF, ACTION_REBOOT);
- r = prepare_boot_loader_menu();
- if (r < 0)
- return r;
+ if (termination_action) {
+ r = prepare_firmware_setup();
+ if (r < 0)
+ return r;
- r = prepare_boot_loader_entry();
- if (r < 0)
- return r;
+ r = prepare_boot_loader_menu();
+ if (r < 0)
+ return r;
+
+ r = prepare_boot_loader_entry();
+ if (r < 0)
+ return r;
+ }
if (a == ACTION_REBOOT) {
if (arg_reboot_argument) {
return r;
}
- termination_action = IN_SET(a,
- ACTION_HALT,
- ACTION_POWEROFF,
- ACTION_REBOOT);
if (termination_action && arg_force >= 2)
return halt_now(a);
if (!arg_no_block) {
const char* extra_args[4];
+ WaitJobsFlags flags = 0;
- r = bus_wait_for_jobs(w, arg_quiet, make_extra_args(extra_args));
+ SET_FLAG(flags, BUS_WAIT_JOBS_LOG_ERROR, !arg_quiet);
+ SET_FLAG(flags, BUS_WAIT_JOBS_LOG_SUCCESS, arg_show_transaction);
+ r = bus_wait_for_jobs(w, flags, make_extra_args(extra_args));
if (r < 0)
return r;
'test-cgroup.c',
'test-chase.c',
'test-clock.c',
+ 'test-color-util.c',
'test-compare-operator.c',
'test-condition.c',
'test-conf-files.c',
},
test_template + {
'sources' : files('../libsystemd/sd-device/test-sd-device-thread.c'),
- 'link_with' : libsystemd,
+ 'link_with' : [
+ libbasic,
+ libsystemd,
+ ],
'dependencies' : threads,
},
test_template + {
'sources' : files('../libudev/test-udev-device-thread.c'),
- 'link_with' : libudev,
+ 'link_with' : [
+ libbasic,
+ libudev,
+ ],
'dependencies' : threads,
},
test_template + {
assert_se(boot_config_load(&config, d, NULL) >= 0);
assert_se(config.n_entries == 2);
- // Test finding the first entry
+ /* Test finding the first entry */
BootEntry *entry = boot_config_find_entry(&config, "a-10.conf");
assert_se(entry && streq(entry->id, "a-10.conf"));
- // Test finding the second entry
+ /* Test finding the second entry */
entry = boot_config_find_entry(&config, "a-05.conf");
assert_se(entry && streq(entry->id, "a-05.conf"));
- // Test finding a non-existent entry
+ /* Test finding a non-existent entry */
entry = boot_config_find_entry(&config, "nonexistent.conf");
assert_se(entry == NULL);
- // Test case-insensitivity
+ /* Test case-insensitivity */
entry = boot_config_find_entry(&config, "A-10.CONF");
assert_se(entry && streq(entry->id, "a-10.conf"));
-
return 0;
}
int all, hybrid, systemd, r;
r = cg_unified();
- if (r == -ENOMEDIUM) {
+ if (IN_SET(r, -ENOENT, -ENOMEDIUM)) {
log_tests_skipped("cgroup not mounted");
return;
}
int i, r;
r = cg_get_keyed_attribute("cpu", "/init.scope", "no_such_file", STRV_MAKE("no_such_attr"), &val);
- if (r == -ENOMEDIUM || ERRNO_IS_PRIVILEGE(r)) {
+ if (IN_SET(r, -ENOMEDIUM, -ENOENT) || ERRNO_IS_PRIVILEGE(r)) {
log_info_errno(r, "Skipping most of %s, /sys/fs/cgroup not accessible: %m", __func__);
return;
}
int r;
r = cg_unified_cached(false);
- if (r == -ENOMEDIUM) {
- log_tests_skipped("cgroup not mounted");
+ if (IN_SET(r, -ENOMEDIUM, -ENOENT)) {
+ log_tests_skipped("cgroupfs is not mounted");
return;
}
assert_se(r >= 0);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "color-util.h"
+#include "tests.h"
+
+TEST(hsv_to_rgb) {
+ uint8_t r, g, b;
+
+ hsv_to_rgb(0, 0, 0, &r, &g, &b);
+ assert(r == 0 && g == 0 && b == 0);
+
+ hsv_to_rgb(60, 0, 0, &r, &g, &b);
+ assert(r == 0 && g == 0 && b == 0);
+
+ hsv_to_rgb(0, 0, 100, &r, &g, &b);
+ assert(r == 255 && g == 255 && b == 255);
+
+ hsv_to_rgb(0, 100, 100, &r, &g, &b);
+ assert(r == 255 && g == 0 && b == 0);
+
+ hsv_to_rgb(120, 100, 100, &r, &g, &b);
+ assert(r == 0 && g == 255 && b == 0);
+
+ hsv_to_rgb(240, 100, 100, &r, &g, &b);
+ assert(r == 0 && g == 0 && b == 255);
+
+ hsv_to_rgb(311, 52, 62, &r, &g, &b);
+ assert(r == 158 && g == 75 && b == 143);
+}
+
+TEST(rgb_to_hsv) {
+
+ double h, s, v;
+ rgb_to_hsv(0, 0, 0, &h, &s, &v);
+ assert(s <= 0);
+ assert(v <= 0);
+
+ rgb_to_hsv(1, 1, 1, &h, &s, &v);
+ assert(s <= 0);
+ assert(v >= 100);
+
+ rgb_to_hsv(1, 0, 0, &h, &s, &v);
+ assert(h >= 359 || h <= 1);
+ assert(s >= 100);
+ assert(v >= 100);
+
+ rgb_to_hsv(0, 1, 0, &h, &s, &v);
+ assert(h >= 119 && h <= 121);
+ assert(s >= 100);
+ assert(v >= 100);
+
+ rgb_to_hsv(0, 0, 1, &h, &s, &v);
+ assert(h >= 239 && h <= 241);
+ assert(s >= 100);
+ assert(v >= 100);
+
+ rgb_to_hsv(0.5, 0.6, 0.7, &h, &s, &v);
+ assert(h >= 209 && h <= 211);
+ assert(s >= 28 && s <= 31);
+ assert(v >= 69 && v <= 71);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);
int r;
r = cg_unified();
- if (r == -ENOMEDIUM) {
- log_tests_skipped("cgroup not mounted");
+ if (IN_SET(r, -ENOMEDIUM, -ENOENT)) {
+ log_tests_skipped("cgroupfs is not mounted");
return;
}
assert_se(r >= 0);
int r;
r = cg_unified();
- if (r == -ENOMEDIUM) {
- log_tests_skipped("cgroup not mounted");
+ if (IN_SET(r, -ENOMEDIUM, -ENOENT)) {
+ log_tests_skipped("cgroupfs is not mounted");
return;
}
assert_se(r >= 0);
assert_se(streq(a[0], "a=A"));
}
+TEST(strv_env_assignf) {
+ _cleanup_strv_free_ char **a = NULL;
+
+ assert_se(strv_env_assignf(&a, "a", "a") > 0);
+ assert_se(strv_env_assignf(&a, "a", "%c", 'a') == 0);
+
+ assert_se(strv_env_assignf(&a, "c", "xxx%iyyy", 5) > 0);
+ assert_se(strv_length(a) == 2);
+ assert_se(strv_equal(a, STRV_MAKE("a=a", "c=xxx5yyy")));
+ assert_se(strv_env_assignf(&a, "c", NULL) == 0);
+
+ assert_se(strv_env_assignf(&a, "b", "b") > 0);
+ assert_se(strv_env_assignf(&a, "a", "A") == 0);
+ assert_se(strv_env_assignf(&a, "b", NULL) == 0);
+
+ assert_se(strv_env_assignf(&a, "a=", "B") == -EINVAL);
+
+ assert_se(strv_length(a) == 1);
+ assert_se(streq(a[0], "a=A"));
+}
+
TEST(strv_env_assign_many) {
_cleanup_strv_free_ char **a = NULL;
assert_se(sd_bus_message_read(reply, "o", &object) >= 0);
- assert_se(bus_wait_for_jobs_one(w, object, /* quiet= */ false, /* extra_args= */ NULL) >= 0);
+ assert_se(bus_wait_for_jobs_one(w, object, /* flags= */ BUS_WAIT_JOBS_LOG_ERROR, /* extra_args= */ NULL) >= 0);
assert_se(sd_event_default(&e) >= 0);
assert_se(s);
assert_se(touch(s) >= 0);
- for (int force_ms_move = 0; force_ms_move < 2; force_ms_move++) {
+ struct {
+ const char *path;
+ bool force_ms_move;
+ } table[] = {
+ { t, false },
+ { t, true },
+ { "/", false },
+ { "/", true },
+ };
+
+ FOREACH_ARRAY(i, table, ELEMENTSOF(table)) {
r = safe_fork("(switch-root)",
FORK_RESET_SIGNALS |
FORK_CLOSE_ALL_FDS |
assert_se(r >= 0);
if (r == 0) {
- assert_se(make_mount_point(t) >= 0);
- assert_se(mount_switch_root_full(t, /* mount_propagation_flag= */ 0, force_ms_move) >= 0);
+ assert_se(make_mount_point(i->path) >= 0);
+ assert_se(mount_switch_root_full(i->path, /* mount_propagation_flag= */ 0, i->force_ms_move) >= 0);
- assert_se(access(ASSERT_PTR(strrchr(s, '/')), F_OK) >= 0); /* absolute */
- assert_se(access(ASSERT_PTR(strrchr(s, '/')) + 1, F_OK) >= 0); /* relative */
- assert_se(access(s, F_OK) < 0 && errno == ENOENT); /* doesn't exist in our new environment */
+ if (!path_equal(i->path, "/")) {
+ assert_se(access(ASSERT_PTR(strrchr(s, '/')), F_OK) >= 0); /* absolute */
+ assert_se(access(ASSERT_PTR(strrchr(s, '/')) + 1, F_OK) >= 0); /* relative */
+ assert_se(access(s, F_OK) < 0 && errno == ENOENT); /* doesn't exist in our new environment */
+ }
_exit(EXIT_SUCCESS);
}
assert_se(path_is_mount_point("/proc/1/", NULL, AT_SYMLINK_FOLLOW) == 0);
assert_se(path_is_mount_point("/proc/1/", NULL, 0) == 0);
- assert_se(path_is_mount_point("/sys", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/sys", NULL, 0) > 0);
- assert_se(path_is_mount_point("/sys/", NULL, AT_SYMLINK_FOLLOW) > 0);
- assert_se(path_is_mount_point("/sys/", NULL, 0) > 0);
-
/* we'll create a hierarchy of different kinds of dir/file/link
* layouts:
*
assert_se(parse_mtu(AF_UNSPEC, "4294967295", &mtu) >= 0 && mtu == 4294967295);
assert_se(parse_mtu(AF_UNSPEC, "500", &mtu) >= 0 && mtu == 500);
assert_se(parse_mtu(AF_UNSPEC, "1280", &mtu) >= 0 && mtu == 1280);
+ assert_se(parse_mtu(AF_UNSPEC, "4294967296", &mtu) == -ERANGE);
+ assert_se(parse_mtu(AF_UNSPEC, "68", &mtu) >= 0 && mtu == 68);
+ assert_se(parse_mtu(AF_UNSPEC, "67", &mtu) >= 0 && mtu == 67);
+ assert_se(parse_mtu(AF_UNSPEC, "0", &mtu) >= 0 && mtu == 0);
+ assert_se(parse_mtu(AF_UNSPEC, "", &mtu) == -EINVAL);
+
+ assert_se(parse_mtu(AF_INET, "1500", &mtu) >= 0 && mtu == 1500);
+ assert_se(parse_mtu(AF_INET, "1400", &mtu) >= 0 && mtu == 1400);
+ assert_se(parse_mtu(AF_INET, "65535", &mtu) >= 0 && mtu == 65535);
+ assert_se(parse_mtu(AF_INET, "65536", &mtu) >= 0 && mtu == 65536);
+ assert_se(parse_mtu(AF_INET, "4294967295", &mtu) >= 0 && mtu == 4294967295);
+ assert_se(parse_mtu(AF_INET, "500", &mtu) >= 0 && mtu == 500);
+ assert_se(parse_mtu(AF_INET, "1280", &mtu) >= 0 && mtu == 1280);
+ assert_se(parse_mtu(AF_INET, "4294967296", &mtu) == -ERANGE);
+ assert_se(parse_mtu(AF_INET, "68", &mtu) >= 0 && mtu == 68);
+ assert_se(parse_mtu(AF_INET, "67", &mtu) == -ERANGE);
+ assert_se(parse_mtu(AF_INET, "0", &mtu) == -ERANGE);
+ assert_se(parse_mtu(AF_INET, "", &mtu) == -EINVAL);
+
assert_se(parse_mtu(AF_INET6, "1280", &mtu) >= 0 && mtu == 1280);
assert_se(parse_mtu(AF_INET6, "1279", &mtu) == -ERANGE);
- assert_se(parse_mtu(AF_UNSPEC, "4294967296", &mtu) == -ERANGE);
assert_se(parse_mtu(AF_INET6, "4294967296", &mtu) == -ERANGE);
assert_se(parse_mtu(AF_INET6, "68", &mtu) == -ERANGE);
- assert_se(parse_mtu(AF_UNSPEC, "68", &mtu) >= 0 && mtu == 68);
- assert_se(parse_mtu(AF_UNSPEC, "67", &mtu) == -ERANGE);
- assert_se(parse_mtu(AF_UNSPEC, "0", &mtu) == -ERANGE);
- assert_se(parse_mtu(AF_UNSPEC, "", &mtu) == -EINVAL);
+ assert_se(parse_mtu(AF_INET6, "", &mtu) == -EINVAL);
}
TEST(parse_loadavg_fixed_point) {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/resource.h>
+#if HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
#include "alloc-util.h"
#include "capability-util.h"
#include "macro.h"
#include "missing_resource.h"
+#include "process-util.h"
#include "rlimit-util.h"
#include "string-util.h"
#include "tests.h"
#include "time-util.h"
+#include "user-util.h"
static void test_rlimit_parse_format_one(int resource, const char *string, rlim_t soft, rlim_t hard, int ret, const char *formatted) {
_cleanup_free_ char *f = NULL;
assert_se(old.rlim_max == new.rlim_max);
}
+TEST(pid_getrlimit) {
+ int r;
+
+ /* We fork off a child and read the parent's resource limit from there (i.e. our own), and compare
+ * with what getrlimit() gives us */
+
+ for (int resource = 0; resource < _RLIMIT_MAX; resource++) {
+ struct rlimit direct;
+
+ assert_se(getrlimit(resource, &direct) >= 0);
+
+ /* We fork off a child so that getrlimit() doesn't work anymore */
+ r = safe_fork("(getrlimit)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, /* ret_pid= */ NULL);
+ assert_se(r >= 0);
+
+ if (r == 0) {
+ struct rlimit indirect;
+ /* child */
+
+ /* Drop privs, so that prlimit() doesn't work anymore */
+ (void) setresgid(GID_NOBODY, GID_NOBODY, GID_NOBODY);
+ (void) setresuid(UID_NOBODY, UID_NOBODY, UID_NOBODY);
+
+ assert_se(pid_getrlimit(getppid(), resource, &indirect) >= 0);
+
+#if HAVE_VALGRIND_VALGRIND_H
+ /* Valgrind fakes some changes in RLIMIT_NOFILE getrlimit() returns, work around that */
+ if (RUNNING_ON_VALGRIND && resource == RLIMIT_NOFILE) {
+ log_info("Skipping pid_getrlimit() check for RLIMIT_NOFILE, running in valgrind");
+ _exit(EXIT_SUCCESS);
+ }
+#endif
+
+ assert_se(direct.rlim_cur == indirect.rlim_cur);
+ assert_se(direct.rlim_max == indirect.rlim_max);
+
+ _exit(EXIT_SUCCESS);
+ }
+ }
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
log_notice("Not invoked with stdin == ctty, cutting get_ctty() test short");
}
+TEST(get_default_background_color) {
+ double red, green, blue;
+ int r;
+
+ r = get_default_background_color(&red, &green, &blue);
+ if (r < 0)
+ log_notice_errno(r, "Can't get terminal default background color: %m");
+ else
+ log_notice("R=%g G=%g B=%g", red, green, blue);
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
#include "varlink.h"
#include "varlink-idl.h"
#include "varlink-io.systemd.h"
+#include "varlink-io.systemd.Credentials.h"
#include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.ManagedOOM.h"
#include "varlink-io.systemd.PCRExtend.h"
print_separator();
test_parse_format_one(&vl_interface_io_systemd_sysext);
print_separator();
+ test_parse_format_one(&vl_interface_io_systemd_Credentials);
+ print_separator();
test_parse_format_one(&vl_interface_xyz_test);
}
if (getuid() != 0)
return log_tests_skipped("not root");
r = enter_cgroup_subroot(NULL);
- if (r == -ENOMEDIUM)
+ if (r < 0)
return log_tests_skipped("cgroupfs not available");
_cleanup_free_ char *unit_dir = NULL;
if (streq_ptr(z, c->zone))
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_TIME,
"org.freedesktop.timedate1.set-timezone",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (lrtc == c->local_rtc && !fix_system)
return sd_bus_reply_method_return(m, NULL);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_TIME,
"org.freedesktop.timedate1.set-local-rtc",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
} else
timespec_store(&ts, (usec_t) utc);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_TIME,
"org.freedesktop.timedate1.set-time",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
if (context_ntp_service_exists(c) <= 0)
return sd_bus_error_set(error, BUS_ERROR_NO_NTP_SUPPORT, "NTP not supported");
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
m,
- CAP_SYS_TIME,
"org.freedesktop.timedate1.set-ntp",
- NULL,
+ /* details= */ NULL,
interactive,
- UID_INVALID,
+ /* good_user= */ UID_INVALID,
&c->polkit_registry,
error);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server name or address, refusing: %s", *name);
}
- r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
- "org.freedesktop.timesync1.set-runtime-servers",
- NULL, true, UID_INVALID,
- &m->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ "org.freedesktop.timesync1.set-runtime-servers",
+ /* details= */ NULL,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "mountpoint-util.h"
#include "string-util.h"
#include "tests.h"
#include "udev-format.h"
test_udev_resolve_subsys_kernel_one("[net/lo]/address", true, 0, "00:00:00:00:00:00");
}
-DEFINE_TEST_MAIN(LOG_DEBUG);
+static int intro(void) {
+ if (path_is_mount_point("/sys", NULL, 0) <= 0)
+ return log_tests_skipped("/sys is not mounted");
+
+ return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "mountpoint-util.h"
#include "path-util.h"
#include "signal-util.h"
#include "strv.h"
int main(int argc, char *argv[]) {
_cleanup_free_ char *self = NULL;
+ if (path_is_mount_point("/sys", NULL, 0) <= 0)
+ return log_tests_skipped("/sys is not mounted");
+
if (argc > 1) {
if (streq(argv[1], "test1"))
test1();
/* Get kernel provided port index for the case when multiple ports on a single PCI function. */
- r = device_get_sysattr_unsigned(dev, "dev_port", &v);
+ r = device_get_sysattr_unsigned_filtered(dev, "dev_port", &v);
if (r < 0)
return r;
if (r > 0) {
if (fallback_to_dev_id) {
unsigned iftype;
- r = device_get_sysattr_unsigned(dev, "type", &iftype);
+ r = device_get_sysattr_unsigned_filtered(dev, "type", &iftype);
if (r < 0)
return r;
}
if (fallback_to_dev_id)
- return device_get_sysattr_unsigned(dev, "dev_id", ret);
+ return device_get_sysattr_unsigned_filtered(dev, "dev_id", ret);
/* Otherwise, return the original index 0. */
*ret = 0;
assert(ret);
/* First, try to use the kernel provided front panel port name for multiple port PCI device. */
- r = sd_device_get_sysattr_value(dev, "phys_port_name", &phys_port_name);
+ r = device_get_sysattr_value_filtered(dev, "phys_port_name", &phys_port_name);
if (r >= 0 && !isempty(phys_port_name)) {
if (naming_scheme_has(NAMING_SR_IOV_R)) {
int vf_id = -1;
assert(ret);
/* ACPI _DSM — device specific method for naming a PCI or PCI Express device */
- r = device_get_sysattr_unsigned(dev, "acpi_index", &idx);
+ r = device_get_sysattr_unsigned_filtered(dev, "acpi_index", &idx);
if (r < 0)
/* SMBIOS type 41 — Onboard Devices Extended Information */
- r = device_get_sysattr_unsigned(dev, "index", &idx);
+ r = device_get_sysattr_unsigned_filtered(dev, "index", &idx);
if (r < 0)
return log_device_debug_errno(dev, r, "Could not obtain onboard index: %m");
assert(prefix);
/* retrieve on-board label from firmware */
- r = sd_device_get_sysattr_value(pci_dev, "label", &label);
+ r = device_get_sysattr_value_filtered(pci_dev, "label", &label);
if (r < 0)
return log_device_debug_errno(pci_dev, r, "Failed to get PCI onboard label: %m");
static bool is_pci_ari_enabled(sd_device *dev) {
assert(dev);
- return device_get_sysattr_bool(dev, "ari_enabled") > 0;
+ return device_get_sysattr_bool_filtered(dev, "ari_enabled") > 0;
}
static bool is_pci_bridge(sd_device *dev) {
assert(dev);
- if (sd_device_get_sysattr_value(dev, "modalias", &v) < 0)
+ if (device_get_sysattr_value_filtered(dev, "modalias", &v) < 0)
return false;
if (!startswith(v, "pci:"))
return 0;
}
- if (sd_device_get_sysattr_value(dev, "function_id", &attr) < 0) {
+ if (device_get_sysattr_value_filtered(dev, "function_id", &attr) < 0) {
*ret = 0;
return 0;
}
if (!path)
return log_oom_debug();
- if (sd_device_get_sysattr_value(pci, path, &address) < 0)
+ if (device_get_sysattr_value_filtered(pci, path, &address) < 0)
continue;
/* match slot address with device by stripping the function */
if (!alias_index)
continue;
- if (sd_device_get_sysattr_value(aliases_dev, alias, &alias_path) < 0)
+ if (device_get_sysattr_value_filtered(aliases_dev, alias, &alias_path) < 0)
continue;
if (!path_equal(ofnode_path, alias_path))
}
/* ...but make sure we don't have an alias conflict */
- if (i == 0 && sd_device_get_sysattr_value(aliases_dev, conflict, NULL) >= 0)
+ if (i == 0 && device_get_sysattr_value_filtered(aliases_dev, conflict, NULL) >= 0)
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
"Ethernet alias conflict: ethernet and ethernet0 both exist");
assert(dev);
assert(prefix);
- r = device_get_sysattr_unsigned(dev, "type", &iftype);
+ r = device_get_sysattr_unsigned_filtered(dev, "type", &iftype);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to read 'type' attribute: %m");
"Not generating MAC name for infiniband device.");
/* check for NET_ADDR_PERM, skip random MAC addresses */
- r = device_get_sysattr_unsigned(dev, "addr_assign_type", &assign_type);
+ r = device_get_sysattr_unsigned_filtered(dev, "addr_assign_type", &assign_type);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to read/parse addr_assign_type: %m");
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL),
"addr_assign_type=%u, MAC address is not permanent.", assign_type);
- r = sd_device_get_sysattr_value(dev, "address", &s);
+ r = device_get_sysattr_value_filtered(dev, "address", &s);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to read 'address' attribute: %m");
if (r < 0)
return log_device_debug_errno(netdevsimdev, r, "Failed to parse device sysnum: %m");
- r = sd_device_get_sysattr_value(dev, "phys_port_name", &phys_port_name);
+ r = device_get_sysattr_value_filtered(dev, "phys_port_name", &phys_port_name);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to get 'phys_port_name' attribute: %m");
if (isempty(phys_port_name))
assert(dev);
assert(ret);
- r = device_get_sysattr_unsigned(dev, "type", &iftype);
+ r = device_get_sysattr_unsigned_filtered(dev, "type", &iftype);
if (r < 0)
return r;
if (r < 0)
return r;
- r = device_get_sysattr_int(dev, "iflink", &iflink);
+ r = device_get_sysattr_int_filtered(dev, "iflink", &iflink);
if (r < 0)
return r;
log_notice("udevadm hwdb is deprecated. Use systemd-hwdb instead.");
- if (arg_update) {
+ if (arg_update && !hwdb_bypass()) {
r = hwdb_update(arg_root, arg_hwdb_bin_dir, arg_strict, true);
if (r < 0)
return r;
if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
pcrpkey = opts.pcr_public_keys[0]
elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
- import cryptography.hazmat.primitives.serialization as serialization
+ from cryptography.hazmat.primitives import serialization
privkey = serialization.load_pem_private_key(opts.pcr_private_keys[0].read_bytes(), password=None)
pcrpkey = privkey.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
#include "signal-util.h"
#include "socket-util.h"
#include "stdio-util.h"
+#include "strv.h"
#include "umask-util.h"
#include "userdbd-manager.h"
m->deferred_start_worker_event_source = sd_event_source_unref(m->deferred_start_worker_event_source);
+ safe_close(m->listen_fd);
+
sd_event_unref(m->event);
return mfree(m);
_exit(EXIT_FAILURE);
}
-
if (setenv("USERDB_FIXED_WORKER", one_zero(fixed), 1) < 0) {
log_error_errno(errno, "Failed to set $USERDB_FIXED_WORKER: %m");
_exit(EXIT_FAILURE);
return 0;
}
-int manager_startup(Manager *m) {
- int n, r;
+static int manager_make_listen_socket(Manager *m) {
+ static const union sockaddr_union sockaddr = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/userdb/io.systemd.Multiplexer",
+ };
+ int r;
+
+ assert(m);
+
+ if (m->listen_fd >= 0)
+ return 0;
+
+ r = mkdir_p("/run/systemd/userdb", 0755);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /run/systemd/userdb: %m");
+
+ m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+ if (m->listen_fd < 0)
+ return log_error_errno(errno, "Failed to bind on socket: %m");
+
+ (void) sockaddr_un_unlink(&sockaddr.un);
+
+ WITH_UMASK(0000)
+ if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
+ return log_error_errno(errno, "Failed to bind socket: %m");
+
+ FOREACH_STRING(alias,
+ "/run/systemd/userdb/io.systemd.NameServiceSwitch",
+ "/run/systemd/userdb/io.systemd.DropIn") {
+
+ r = symlink_idempotent("io.systemd.Multiplexer", alias, /* make_relative= */ false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to symlink '%s': %m", alias);
+ }
+
+ if (listen(m->listen_fd, SOMAXCONN_DELUXE) < 0)
+ return log_error_errno(errno, "Failed to listen on socket: %m");
+
+ return 1;
+}
+
+static int manager_scan_listen_fds(Manager *m) {
+ int n;
assert(m);
- assert(m->listen_fd < 0);
- n = sd_listen_fds(false);
+ n = sd_listen_fds(/* unset_environment= */ true);
if (n < 0)
return log_error_errno(n, "Failed to determine number of passed file descriptors: %m");
if (n > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one listening fd, got %i.", n);
if (n == 1)
m->listen_fd = SD_LISTEN_FDS_START;
- else {
- static const union sockaddr_union sockaddr = {
- .un.sun_family = AF_UNIX,
- .un.sun_path = "/run/systemd/userdb/io.systemd.Multiplexer",
- };
-
- r = mkdir_p("/run/systemd/userdb", 0755);
- if (r < 0)
- return log_error_errno(r, "Failed to create /run/systemd/userdb: %m");
- m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
- if (m->listen_fd < 0)
- return log_error_errno(errno, "Failed to bind on socket: %m");
-
- (void) sockaddr_un_unlink(&sockaddr.un);
+ return 0;
+}
- WITH_UMASK(0000)
- if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
- return log_error_errno(errno, "Failed to bind socket: %m");
+int manager_startup(Manager *m) {
+ int r;
- r = symlink_idempotent("io.systemd.Multiplexer",
- "/run/systemd/userdb/io.systemd.NameServiceSwitch", false);
- if (r < 0)
- return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
+ assert(m);
+ assert(m->listen_fd < 0);
- r = symlink_idempotent("io.systemd.Multiplexer",
- "/run/systemd/userdb/io.systemd.DropIn", false);
- if (r < 0)
- return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
+ r = manager_scan_listen_fds(m);
+ if (r < 0)
+ return r;
- if (listen(m->listen_fd, SOMAXCONN_DELUXE) < 0)
- return log_error_errno(errno, "Failed to listen on socket: %m");
- }
+ r = manager_make_listen_socket(m);
+ if (r < 0)
+ return r;
/* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
* GC'ed on idle */
if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
- return start_workers(m, /* explicit_request= */ false);
+ r = start_workers(m, /* explicit_request= */ false);
+ if (r < 0)
+ return r;
+
+ return 0;
}
static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
static const JsonDispatch dispatch_table[] = {
- { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
+ { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
{ "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
- { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
+ { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
{}
};
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
}
-static int process_connection(VarlinkServer *server, int fd) {
+static int process_connection(VarlinkServer *server, int _fd) {
+ _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
_cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
int r;
r = varlink_server_add_connection(server, fd, &vl);
- if (r < 0) {
- fd = safe_close(fd);
+ if (r < 0)
return log_error_errno(r, "Failed to add connection: %m");
- }
+ TAKE_FD(fd);
vl = varlink_ref(vl);
for (;;) {
static int run(int argc, char *argv[]) {
usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
_cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
+ _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
unsigned n_iterations = 0;
int m, listen_fd, r;
if (r < 0)
return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
+ r = pidref_set_parent(&parent);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
+ if (parent.pid == 1) /* We got reparented away from userdbd? */
+ return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died, exiting.");
+
start_time = now(CLOCK_MONOTONIC);
for (;;) {
return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
if (FLAGS_SET(r, POLLIN)) {
- pid_t parent;
-
- parent = getppid();
- if (parent <= 1)
- return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died?");
-
- if (kill(parent, SIGUSR2) < 0)
- return log_error_errno(errno, "Failed to kill our own parent: %m");
+ r = pidref_kill(&parent, SIGUSR2);
+ if (r == -ESRCH)
+ return log_error_errno(r, "Parent already died?");
+ if (r < 0)
+ return log_error_errno(r, "Failed to send SIGUSR2 signal to parent: %m");
}
}
static int verify_vc_kbmode(int fd) {
int curr_mode;
+ assert(fd >= 0);
+
/*
* Make sure we only adjust consoles in K_XLATE or K_UNICODE mode.
* Otherwise we would (likely) interfere with X11's processing of the
return IN_SET(curr_mode, K_XLATE, K_UNICODE) ? 0 : -EBUSY;
}
+static int verify_vc_display_mode(int fd) {
+ int mode;
+
+ assert(fd >= 0);
+
+ /* Similarly the vc is likely busy if it is in KD_GRAPHICS mode. If it's not the case and it's been
+ * left in graphics mode, the kernel will refuse to operate on the font settings anyway. */
+
+ if (ioctl(fd, KDGETMODE, &mode) < 0)
+ return -errno;
+
+ return mode != KD_TEXT ? -EBUSY : 0;
+}
+
static int toggle_utf8_vc(const char *name, int fd, bool utf8) {
int r;
struct termios tc = {};
if (cfo.op != KD_FONT_OP_SET)
continue;
- r = ioctl(fd_d, KDFONTOP, &cfo);
+ r = verify_vc_display_mode(fd_d);
if (r < 0) {
- int last_errno, mode;
-
- /* The fonts couldn't have been copied. It might be due to the
- * terminal being in graphical mode. In this case the kernel
- * returns -EINVAL which is too generic for distinguishing this
- * specific case. So we need to retrieve the terminal mode and if
- * the graphical mode is in used, let's assume that something else
- * is using the terminal and the failure was expected as we
- * shouldn't have tried to copy the fonts. */
-
- last_errno = errno;
- if (ioctl(fd_d, KDGETMODE, &mode) >= 0 && mode != KD_TEXT)
- log_debug("KD_FONT_OP_SET skipped: tty%u is not in text mode", i);
- else
- log_warning_errno(last_errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i);
+ log_debug_errno(r, "KD_FONT_OP_SET skipped: tty%u is not in text mode", i);
+ continue;
+ }
+ if (ioctl(fd_d, KDFONTOP, &cfo) < 0) {
+ log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i);
continue;
}
fd = open_terminal(path, O_RDWR|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
- log_debug_errno(fd, "Failed to open terminal %s, ignoring: %m", path);
- RET_GATHER(err, r);
+ RET_GATHER(err, log_debug_errno(fd, "Failed to open terminal %s, ignoring: %m", path));
continue;
}
+
r = verify_vc_kbmode(fd);
if (r < 0) {
- log_debug_errno(r, "Failed to check VC %s keyboard mode: %m", path);
- RET_GATHER(err, r);
+ RET_GATHER(err, log_debug_errno(r, "Failed to check VC %s keyboard mode: %m", path));
+ continue;
+ }
+
+ r = verify_vc_display_mode(fd);
+ if (r < 0) {
+ RET_GATHER(err, log_debug_errno(r, "Failed to check VC %s display mode: %m", path));
continue;
}
if (r < 0)
return log_error_errno(r, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", src_vc);
+ /* setfont(8) silently ignores when the font can't be applied due to the vc being in
+ * KD_GRAPHICS. Hence we continue to accept this case however we now let the user know that the vc
+ * will be initialized only partially.*/
+ r = verify_vc_display_mode(fd);
+ if (r < 0)
+ log_notice_errno(r, "Virtual console %s is not in KD_TEXT, font settings likely won't be applied.", src_vc);
+
path = strdup(src_vc);
if (!path)
return log_oom();
static uint64_t arg_vsock_cid = UINT64_MAX;
static bool arg_qemu_gui = false;
static int arg_secure_boot = -1;
-static MachineCredential *arg_credentials = NULL;
-static size_t arg_n_credentials = 0;
+static MachineCredentialContext arg_credentials = {};
static SettingsMask arg_settings_mask = 0;
static char **arg_parameters = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_machine, freep);
STATIC_DESTRUCTOR_REGISTER(arg_qemu_smp, freep);
STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
static int help(void) {
_cleanup_free_ char *link = NULL;
break;
case ARG_SET_CREDENTIAL: {
- r = machine_credential_set(&arg_credentials, &arg_n_credentials, optarg);
+ r = machine_credential_set(&arg_credentials, optarg);
if (r < 0)
return r;
arg_settings_mask |= SETTING_CREDENTIALS;
}
case ARG_LOAD_CREDENTIAL: {
- r = machine_credential_load(&arg_credentials, &arg_n_credentials, optarg);
+ r = machine_credential_load(&arg_credentials, optarg);
if (r < 0)
return r;
return log_oom();
}
- if (ARCHITECTURE_SUPPORTS_SMBIOS) {
- ssize_t n;
- FOREACH_ARRAY(cred, arg_credentials, arg_n_credentials) {
+ if (ARCHITECTURE_SUPPORTS_SMBIOS)
+ FOREACH_ARRAY(cred, arg_credentials.credentials, arg_credentials.n_credentials) {
_cleanup_free_ char *cred_data_b64 = NULL;
+ ssize_t n;
n = base64mem(cred->data, cred->size, &cred_data_b64);
if (n < 0)
if (r < 0)
return log_oom();
}
- }
r = strv_extend(&cmdline, "-drive");
if (r < 0)
}
static int run(int argc, char *argv[]) {
- int r, ret = EXIT_SUCCESS;
+ int r;
log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
- goto finish;
+ return r;
r = determine_names();
if (r < 0)
- goto finish;
+ return r;
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18, -1) >= 0);
- r = run_virtual_machine();
- if (r > 0)
- ret = r;
-finish:
- machine_credential_free_all(arg_credentials, arg_n_credentials);
-
- if (r < 0)
- return r;
-
- return ret;
+ return run_virtual_machine();
}
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
}
check_result_qemu_hook() {
+ local workspace="${1:?}"
local console_log="${TESTDIR:?}/console.log"
+ if [[ -e "$workspace/skipped" ]]; then
+ return 0
+ fi
+
if [[ ! -e "$console_log" ]]; then
dfatal "Missing console log - this shouldn't happen"
return 1
--- /dev/null
+dn = "cn = systemd"
+expiration_days = 30
+
+signing_key
+encryption_key
+
+tls_www_client
+email_protection_key
mount_initdir
- cryptsetup luksOpen "${LOOPDEV:?}p2" "${DM_NAME:?}" <"$TESTDIR/keyfile"
+ cryptsetup luksOpen "${LOOPDEV:?}p4" "${DM_NAME:?}" <"$TESTDIR/keyfile"
mount "/dev/mapper/$DM_NAME" "$initdir/var"
check_result_common "${initdir:?}" && ret=0 || ret=$?
return $ret
}
+can_test_pkcs11() {
+ if ! command -v "softhsm2-util" >/dev/null; then
+ ddebug "softhsm2-util not available, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! command -v "pkcs11-tool" >/dev/null; then
+ ddebug "pkcs11-tool not available, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! command -v "certtool" >/dev/null; then
+ ddebug "certtool not available, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+P11KIT"; then
+ ddebug "Support for p11-kit is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP\b"; then
+ ddebug "Support for libcryptsetup is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+ if ! "${SYSTEMCTL:?}" --version | grep -q "+LIBCRYPTSETUP_PLUGINS"; then
+ ddebug "Support for libcryptsetup plugins is disabled, skipping the PKCS#11 test"
+ return 1
+ fi
+
+ return 0
+}
+
+setup_pkcs11_token() {
+ dinfo "Setup PKCS#11 token"
+ local P11_MODULE_CONFIGS_DIR P11_MODULE_DIR SOFTHSM_MODULE
+
+ export SOFTHSM2_CONF="/tmp/softhsm2.conf"
+ mkdir -p "$initdir/var/lib/softhsm/tokens/"
+ cat >${SOFTHSM2_CONF} <<EOF
+directories.tokendir = $initdir/var/lib/softhsm/tokens/
+objectstore.backend = file
+slots.removable = false
+slots.mechanisms = ALL
+EOF
+ export GNUTLS_PIN="1234"
+ export GNUTLS_SO_PIN="12345678"
+ softhsm2-util --init-token --free --label "TestToken" --pin ${GNUTLS_PIN} --so-pin ${GNUTLS_SO_PIN}
+
+ if ! P11_MODULE_CONFIGS_DIR=$(pkg-config --variable=p11_module_configs p11-kit-1); then
+ echo "WARNING! Cannot get p11_module_configs from p11-kit-1.pc, assuming /usr/share/p11-kit/modules" >&2
+ P11_MODULE_CONFIGS_DIR="/usr/share/p11-kit/modules"
+ fi
+
+ if ! P11_MODULE_DIR=$(pkg-config --variable=p11_module_path p11-kit-1); then
+ echo "WARNING! Cannot get p11_module_path from p11-kit-1.pc, assuming /usr/lib/pkcs11" >&2
+ P11_MODULE_DIR="/usr/lib/pkcs11"
+ fi
+
+ SOFTHSM_MODULE=$(grep -F 'module:' "$P11_MODULE_CONFIGS_DIR/softhsm2.module"| cut -d ':' -f 2| xargs)
+ if [[ "$SOFTHSM_MODULE" =~ ^[^/] ]]; then
+ SOFTHSM_MODULE="$P11_MODULE_DIR/$SOFTHSM_MODULE"
+ fi
+
+ # RSA #####################################################
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "RSA:2048" --label "RSATestKey" --usage-decrypt
+
+ certtool --generate-self-signed \
+ --load-privkey="pkcs11:token=TestToken;object=RSATestKey;type=private" \
+ --load-pubkey="pkcs11:token=TestToken;object=RSATestKey;type=public" \
+ --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \
+ --outder --outfile "/tmp/rsa_test.crt"
+
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/rsa_test.crt" --type cert --label "RSATestKey"
+ rm "/tmp/rsa_test.crt"
+
+ # prime256v1 ##############################################
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --keypairgen --key-type "EC:prime256v1" --label "ECTestKey" --usage-derive
+
+ certtool --generate-self-signed \
+ --load-privkey="pkcs11:token=TestToken;object=ECTestKey;type=private" \
+ --load-pubkey="pkcs11:token=TestToken;object=ECTestKey;type=public" \
+ --template "$TEST_BASE_DIR/$TESTNAME/template.cfg" \
+ --outder --outfile "/tmp/ec_test.crt"
+
+ pkcs11-tool --module "$SOFTHSM_MODULE" --token-label "TestToken" --pin "env:GNUTLS_PIN" --so-pin "env:GNUTLS_SO_PIN" --write-object "/tmp/ec_test.crt" --type cert --label "ECTestKey"
+ rm "/tmp/ec_test.crt"
+
+ ###########################################################
+ rm ${SOFTHSM2_CONF}
+ unset SOFTHSM2_CONF
+
+ inst_libs "$SOFTHSM_MODULE"
+ inst_library "$SOFTHSM_MODULE"
+ inst_simple "$P11_MODULE_CONFIGS_DIR/softhsm2.module"
+
+ cat >"$initdir/etc/softhsm2.conf" <<EOF
+directories.tokendir = /var/lib/softhsm/tokens/
+objectstore.backend = file
+slots.removable = false
+slots.mechanisms = ALL
+log.level = INFO
+EOF
+
+ mkdir -p "$initdir/etc/systemd/system/systemd-cryptsetup@.service.d"
+ cat >"$initdir/etc/systemd/system/systemd-cryptsetup@.service.d/PKCS11.conf" <<EOF
+[Service]
+Environment="SOFTHSM2_CONF=/etc/softhsm2.conf"
+Environment="PIN=$GNUTLS_PIN"
+EOF
+
+ unset GNUTLS_PIN
+ unset GNUTLS_SO_PIN
+}
+
test_create_image() {
create_empty_image_rootdir
echo -n test >"${TESTDIR:?}/keyfile"
- cryptsetup -q luksFormat --uuid="$PART_UUID" --pbkdf pbkdf2 --pbkdf-force-iterations 1000 "${LOOPDEV:?}p2" "$TESTDIR/keyfile"
- cryptsetup luksOpen "${LOOPDEV}p2" "${DM_NAME:?}" <"$TESTDIR/keyfile"
+ cryptsetup -q luksFormat --uuid="$PART_UUID" --pbkdf pbkdf2 --pbkdf-force-iterations 1000 "${LOOPDEV:?}p4" "$TESTDIR/keyfile"
+ cryptsetup luksOpen "${LOOPDEV}p4" "${DM_NAME:?}" <"$TESTDIR/keyfile"
mkfs.ext4 -L var "/dev/mapper/$DM_NAME"
mkdir -p "${initdir:?}/var"
mount "/dev/mapper/$DM_NAME" "$initdir/var"
install_dmevent
generate_module_dependencies
+ if can_test_pkcs11; then
+ setup_pkcs11_token
+ fi
+
# Create a keydev
dd if=/dev/zero of="${STATEDIR:?}/keydev.img" bs=1M count=16
mkfs.ext4 -L varcrypt_keydev "$STATEDIR/keydev.img"
if command -v dracut >/dev/null; then
dracut --force --verbose --add crypt "$INITRD"
elif command -v mkinitcpio >/dev/null; then
- mkinitcpio --addhooks sd-encrypt --generate "$INITRD"
+ mkinitcpio -S autodetect --addhooks sd-encrypt --generate "$INITRD"
elif command -v mkinitramfs >/dev/null; then
# The cryptroot hook is provided by the cryptsetup-initramfs package
if ! dpkg-query -s cryptsetup-initramfs; then
case "${1:?}" in
btrfs)
- modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $?
+ host_has_btrfs
;;
iscsi)
# Client/initiator (Open-iSCSI)
command -v lvm || return $?
;;
mdadm)
- command -v mdadm || return $?
+ host_has_mdadm
;;
multipath)
command -v multipath && command -v multipathd || return $?
# the QEMU test, as nspawn refuses the invalid machine ID with -EUCLEAN
printf "556f48e837bc4424a710fa2e2c9d3e3c\ne3d\n" >"$workspace/etc/machine-id"
fi
+
+ if host_has_btrfs && host_has_mdadm; then
+ install_btrfs
+ install_mdadm
+ generate_module_dependencies
+ fi
}
do_test "$@"
set -e
export SYSTEMD_LOG_LEVEL=info
+export SYSTEMD_HWDB_UPDATE_BYPASS=0
ROOTDIR="$(dirname "$(dirname "$(readlink -f "$0")")")"
SYSTEMD_HWDB="${1:?}"
}
install_lvm() {
+ local lvm_rules rule_prefix
+
image_install lvm
image_install "${ROOTLIBDIR:?}"/system/lvm2-lvmpolld.{service,socket}
image_install "${ROOTLIBDIR:?}"/system/{blk-availability,lvm2-monitor}.service
image_install -o "/lib/tmpfiles.d/lvm2.conf"
+
if get_bool "$LOOKS_LIKE_DEBIAN"; then
- inst_rules 56-lvm.rules 69-lvm-metad.rules
+ lvm_rules="56-lvm.rules"
+ rule_prefix=""
else
- # Support the new udev autoactivation introduced in lvm 2.03.14
- # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6
- # Static autoactivation (via lvm2-activation-generator) was dropped
- # in lvm 2.03.15
- # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c
- if [[ -f /lib/udev/rules.d/69-dm-lvm.rules ]]; then
- inst_rules 11-dm-lvm.rules 69-dm-lvm.rules
- else
- image_install "${ROOTLIBDIR:?}"/system-generators/lvm2-activation-generator
- image_install "${ROOTLIBDIR:?}"/system/lvm2-pvscan@.service
- inst_rules 11-dm-lvm.rules 69-dm-lvm-metad.rules
- fi
+ lvm_rules="11-dm-lvm.rules"
+ rule_prefix="dm-"
+ fi
+
+ # Support the new udev autoactivation introduced in lvm 2.03.14
+ # https://sourceware.org/git/?p=lvm2.git;a=commit;h=67722b312390cdab29c076c912e14bd739c5c0f6
+ # Static autoactivation (via lvm2-activation-generator) was dropped
+ # in lvm 2.03.15
+ # https://sourceware.org/git/?p=lvm2.git;a=commit;h=ee8fb0310c53ed003a43b324c99cdfd891dd1a7c
+ if [[ -f "/lib/udev/rules.d/69-${rule_prefix}lvm.rules" ]]; then
+ inst_rules "$lvm_rules" "69-${rule_prefix}lvm.rules"
+ else
+ image_install "${ROOTLIBDIR:?}"/system-generators/lvm2-activation-generator
+ image_install "${ROOTLIBDIR:?}"/system/lvm2-pvscan@.service
+ inst_rules "$lvm_rules" "69-${rule_prefix}lvm-metad.rules"
fi
+
mkdir -p "${initdir:?}/etc/lvm"
}
+host_has_btrfs() (
+ set -e
+ modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $?
+)
+
install_btrfs() {
instmods btrfs
# Not all utilities provided by btrfs-progs are listed here; extend the list
fi
}
+host_has_mdadm() (
+ set -e
+ command -v mdadm || return $?
+)
+
install_mdadm() {
local unit
local mdadm_units=(
system-shutdown/mdadm.shutdown
)
+ instmods "=md"
image_install mdadm mdmon
inst_rules 01-md-raid-creating.rules 63-md-raid-arrays.rules 64-md-raid-assembly.rules 69-md-clustered-confirm-device.rules
# Fedora/CentOS/RHEL ships this rule file
for unit in "${mdadm_units[@]}"; do
image_install "${ROOTLIBDIR:?}/$unit"
done
+
+ # Disable the mdmonitor service, since it fails if there's no valid email address
+ # configured in /etc/mdadm.conf, which just unnecessarily pollutes the logs
+ "${SYSTEMCTL:?}" mask --root "${initdir:?}" mdmonitor.service || :
}
install_compiled_systemd() {
fi
}
+install_package_file() {
+ local file="${1:?}"
+
+ # Skip missing files (like /etc/machine-info)
+ [[ ! -e "$file" ]] && return 0
+ # Skip python unit tests, since the image_install machinery will try to pull
+ # in the whole python stack in a very questionable state, making the tests fail.
+ # And given we're trying to transition to mkosi-based images anyway I'm not even
+ # going to bother
+ [[ "$file" =~ /tests/unit-tests/.*.py$ ]] && return 0
+ # If the current file is a directory, create it with the original
+ # mode; if it's a symlink to a directory, copy it as-is
+ if [[ -d "$file" ]]; then
+ inst_dir "$file"
+ else
+ inst "$file"
+ fi
+}
+
install_debian_systemd() {
dinfo "Install debian systemd"
- local files
+ local deb file
while read -r deb; do
- files="$(dpkg-query -L "$deb" 2>/dev/null)" || continue
ddebug "Install debian files from package $deb"
- for file in $files; do
- [ -e "$file" ] || continue
- [ ! -L "$file" ] && [ -d "$file" ] && continue
- inst "$file"
- done
+ while read -r file; do
+ install_package_file "$file"
+ done < <(dpkg-query -L "$deb" 2>/dev/null)
done < <(grep -E '^Package:' "${SOURCE_DIR}/debian/control" | cut -d ':' -f 2)
}
dinfo "Installing contents of RPM $rpm"
while read -r file; do
- # Skip missing files (like /etc/machine-info)
- [[ ! -e "$file" ]] && continue
- # Skip directories unless they are a symlink (both -L and -d pass in this case)
- [[ -d "$file" && ! -L "$file" ]] && continue
- # Skip python unit tests, since the image_install machinery will try to pull
- # in the whole python stack in a very questionable state, making the tests fail.
- # And given we're trying to transition to mkosi-based images anyway I'm not even
- # going to bother
- [[ "$file" =~ /tests/unit-tests/.*.py$ ]] && continue
-
- image_install "$file"
+ install_package_file "$file"
done < <(rpm -ql "$rpm")
}
# Partition sizes are in MiBs
local root_size=768
local data_size=100
+ local esp_size=128
+ local boot_size=128
+ local total=
if ! get_bool "$NO_BUILD"; then
if meson configure "${BUILD_DIR:?}" | grep 'static-lib\|standalone-binaries' | awk '{ print $2 }' | grep -q 'true'; then
root_size=$((root_size + 200))
data_size=$((data_size + IMAGE_ADDITIONAL_DATA_SIZE))
fi
- echo "Setting up ${IMAGE_PUBLIC:?} (${root_size} MB)"
+ total=$((root_size + data_size + esp_size + boot_size))
+
+ echo "Setting up ${IMAGE_PUBLIC:?} (${total} MB)"
rm -f "${IMAGE_PRIVATE:?}" "$IMAGE_PUBLIC"
# Create the blank file to use as a root filesystem
- truncate -s "${root_size}M" "$IMAGE_PUBLIC"
+ truncate -s "${total}M" "$IMAGE_PUBLIC"
LOOPDEV="$(losetup --show -P -f "$IMAGE_PUBLIC")"
[[ -b "$LOOPDEV" ]] || return 1
# Create two partitions - a root one and a data one (utilized by some tests)
sfdisk "$LOOPDEV" <<EOF
label: gpt
-type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=$((root_size - data_size))M bootable
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=${esp_size}M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=${root_size}M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot size=${boot_size}M
type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=data
EOF
udevadm settle
+ if ! mkfs -t vfat "${LOOPDEV}p1"; then
+ dfatal "Failed to mkfs -t vfat ${LOOPDEV}p1"
+ exit 1
+ fi
+
local label=(-L systemd_boot)
# mkfs.reiserfs doesn't know -L. so, use --label instead
[[ "$FSTYPE" == "reiserfs" ]] && label=(--label systemd_boot)
- if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p1" -q; then
- dfatal "Failed to mkfs -t ${FSTYPE}"
+ if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p2" -q; then
+ dfatal "Failed to mkfs -t ${FSTYPE} ${label[*]} ${LOOPDEV}p2 -q"
+ exit 1
+ fi
+
+ local label=(-L xbootldr)
+ [[ "$FSTYPE" == "reiserfs" ]] && label=(--label xbootldr)
+ if ! mkfs -t "${FSTYPE}" "${label[@]}" "${LOOPDEV}p3" -q; then
+ dfatal "Failed to mkfs -t ${FSTYPE} ${label[*]} ${LOOPDEV}p3 -q"
exit 1
fi
}
if ! mountpoint -q "${initdir:?}"; then
mkdir -p "$initdir"
- mount "${LOOPDEV}p1" "$initdir"
+ mount "${LOOPDEV}p2" "$initdir"
TEST_SETUP_CLEANUP_ROOTDIR=1
fi
}
}
test_setup() {
- if get_bool "${TEST_REQUIRE_INSTALL_TESTS:?}" && \
- command -v meson >/dev/null && \
- [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
+ if ! get_bool "$NO_BUILD" && \
+ get_bool "${TEST_REQUIRE_INSTALL_TESTS:?}" && \
+ command -v meson >/dev/null && \
+ [[ "$(meson configure "${BUILD_DIR:?}" | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
dfatal "$BUILD_DIR needs to be built with -Dinstall-tests=true"
exit 1
fi
"ip=10.0.0.1:::255.255.255::foo99:off"
"ip=10.0.0.1:::255.255.255.0:invalid_hostname:foo99:off"
"ip=10.0.0.1:::255.255.255.0::verylonginterfacename:off"
- "ip=:::::dhcp99:dhcp6:0"
+ "ip=:::::dhcp99:dhcp6:4294967296"
"ip=:::::dhcp99:dhcp6:-1"
"ip=:::::dhcp99:dhcp6:666:52:54:00"
"ip=fdef:c400:bd01:1096::2::[fdef:c400:bd01:1096::1]:64::ipv6:off:[fdef:c400:bd01:1096::aaaa]"
--- /dev/null
+# SPDX-License-Identifier: MIT-0
+#
+# This config file is installed as part of systemd.
+# It may be freely copied and edited (following the MIT No Attribution license).
+#
+# To make local modifications, one of the following methods may be used:
+# 1. add a drop-in file that extends this file by creating the
+# /etc/systemd/network/99-default.link.d/ directory and creating a
+# new .conf file there.
+# 2. copy this file into /etc/systemd/network or one of the other paths checked
+# by systemd-udevd and edit it there.
+# This file should not be edited in place, because it'll be overwritten on upgrades.
+
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+AlternativeNamesPolicy=database onboard slot path
+MACAddressPolicy=persistent
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Link]
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=test1
+Kind=dummy
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Network]
[NetDev]
Name=vcan99
Kind=vcan
+MTUBytes=16
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[NetDev]
+Name=vcan98
+Kind=vcan
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=vcan98
+
+[Link]
+MTUBytes=16
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Network]
+ManageForeignNextHops=no
# Get only the actual kernel version without any build/distro/arch stuff
# e.g. '5.18.5-200.fc36.x86_64' -> '5.18.5'
kver = platform.release().split('-')[0]
+ # Get also rid of '+'
+ kver = kver.split('+')[0]
return version.parse(kver) >= version.parse(min_kernel_version)
print(output)
self.assertRegex(output, 'Type: loopback')
- def test_udev_link_file(self):
- copy_network_unit('11-dummy.netdev', '11-dummy.network', '25-default.link')
+ def test_unit_file(self):
+ copy_network_unit('11-test-unit-file.netdev', '11-test-unit-file.network', '11-test-unit-file.link')
start_networkd()
self.wait_online(['test1:degraded'])
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
print(output)
- self.assertRegex(output, r'Link File: /run/systemd/network/25-default.link')
- self.assertRegex(output, r'Network File: /run/systemd/network/11-dummy.network')
+ self.assertIn('Link File: /run/systemd/network/11-test-unit-file.link', output)
+ self.assertIn('/run/systemd/network/11-test-unit-file.link.d/dropin.conf', output)
+ self.assertIn('Network File: /run/systemd/network/11-test-unit-file.network', output)
+ self.assertIn('/run/systemd/network/11-test-unit-file.network.d/dropin.conf', output)
+
+ output = read_networkd_log()
+ self.assertIn('test1: Configuring with /run/systemd/network/11-test-unit-file.network (dropins: /run/systemd/network/11-test-unit-file.network.d/dropin.conf).', output)
# This test may be run on the system that has older udevd than 70f32a260b5ebb68c19ecadf5d69b3844896ba55 (v249).
# In that case, the udev DB for the loopback network interface may already have ID_NET_LINK_FILE property.
check_output(*udevadm_cmd, 'trigger', '--settle', '--action=add', '/sys/class/net/lo')
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'lo', env=env)
print(output)
- self.assertRegex(output, r'Link File: n/a')
- self.assertRegex(output, r'Network File: n/a')
+ self.assertIn('Link File: n/a', output)
+ self.assertIn('Network File: n/a', output)
def test_delete_links(self):
copy_network_unit('11-dummy.netdev', '11-dummy.network',
@expectedFailureIfModuleIsNotAvailable('vcan')
def test_vcan(self):
- copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network')
+ copy_network_unit('25-vcan.netdev', '26-netdev-link-local-addressing-yes.network',
+ '25-vcan98.netdev', '25-vcan98.network')
start_networkd()
- self.wait_online(['vcan99:carrier'])
+ self.wait_online(['vcan99:carrier', 'vcan98:carrier'])
+
+ # https://github.com/systemd/systemd/issues/30140
+ output = check_output('ip -d link show vcan99')
+ print(output)
+ self.assertIn('mtu 16 ', output)
+
+ output = check_output('ip -d link show vcan98')
+ print(output)
+ self.assertIn('mtu 16 ', output)
@expectedFailureIfModuleIsNotAvailable('vxcan')
def test_vxcan(self):
def test_address_static(self):
copy_network_unit('25-address-static.network', '12-dummy.netdev', copy_dropins=False)
- start_networkd()
self.setup_nftset('addr4', 'ipv4_addr')
self.setup_nftset('network4', 'ipv4_addr', 'flags interval;')
self.setup_nftset('ifindex', 'iface_index')
+ start_networkd()
self.wait_online(['dummy98:routable'])
print(output)
self.assertEqual(output, '')
- self.tearDown()
-
def test_route_static(self):
first = True
for manage_foreign_routes in [True, False]:
print(output)
self.assertRegex(output, 'inet6 .* scope link')
+ @unittest.skip("Re-enable once https://github.com/systemd/systemd/issues/30056 is resolved")
def test_sysctl(self):
copy_networkd_conf_dropin('25-global-ipv6-privacy-extensions.conf')
copy_network_unit('25-sysctl.network', '12-dummy.netdev', copy_dropins=False)
self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
- @expectedFailureIfNexthopIsNotAvailable()
- def test_nexthop(self):
- def check_nexthop(self):
- self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
+ def check_nexthop(self, manage_foreign_nexthops):
+ self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable'])
- output = check_output('ip nexthop list dev veth99')
- print(output)
- self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
- self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
- self.assertIn('id 3 dev veth99', output)
- self.assertIn('id 4 dev veth99', output)
- self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
- self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
+ output = check_output('ip nexthop list dev veth99')
+ print(output)
+ self.assertIn('id 1 via 192.168.5.1 dev veth99', output)
+ self.assertIn('id 2 via 2001:1234:5:8f63::2 dev veth99', output)
+ self.assertIn('id 3 dev veth99', output)
+ self.assertIn('id 4 dev veth99', output)
+ self.assertRegex(output, 'id 5 via 192.168.10.1 dev veth99 .*onlink')
+ self.assertIn('id 8 via fe80:0:222:4dff:ff:ff:ff:ff dev veth99', output)
+ if manage_foreign_nexthops:
self.assertRegex(output, r'id [0-9]* via 192.168.5.2 dev veth99')
- output = check_output('ip nexthop list dev dummy98')
- print(output)
- self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
+ output = check_output('ip nexthop list dev dummy98')
+ print(output)
+ self.assertIn('id 20 via 192.168.20.1 dev dummy98', output)
+ if manage_foreign_nexthops:
+ self.assertNotIn('id 42 via 192.168.20.2 dev dummy98', output)
+ else:
+ self.assertIn('id 42 via 192.168.20.2 dev dummy98', output)
- # kernel manages blackhole nexthops on lo
- output = check_output('ip nexthop list dev lo')
- print(output)
- self.assertIn('id 6 blackhole', output)
- self.assertIn('id 7 blackhole', output)
+ # kernel manages blackhole nexthops on lo
+ output = check_output('ip nexthop list dev lo')
+ print(output)
+ self.assertIn('id 6 blackhole', output)
+ self.assertIn('id 7 blackhole', output)
- # group nexthops are shown with -0 option
- output = check_output('ip -0 nexthop list id 21')
- print(output)
- self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
+ # group nexthops are shown with -0 option
+ output = check_output('ip -0 nexthop list id 21')
+ print(output)
+ self.assertRegex(output, r'id 21 group (1,3/20|20/1,3)')
- output = check_output('ip route show dev veth99 10.10.10.10')
- print(output)
- self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
+ output = check_output('ip route show dev veth99 10.10.10.10')
+ print(output)
+ self.assertEqual('10.10.10.10 nhid 1 via 192.168.5.1 proto static', output)
- output = check_output('ip route show dev veth99 10.10.10.11')
- print(output)
- self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
+ output = check_output('ip route show dev veth99 10.10.10.11')
+ print(output)
+ self.assertEqual('10.10.10.11 nhid 2 via inet6 2001:1234:5:8f63::2 proto static', output)
- output = check_output('ip route show dev veth99 10.10.10.12')
- print(output)
- self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
+ output = check_output('ip route show dev veth99 10.10.10.12')
+ print(output)
+ self.assertEqual('10.10.10.12 nhid 5 via 192.168.10.1 proto static onlink', output)
- output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
- print(output)
- self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
+ output = check_output('ip -6 route show dev veth99 2001:1234:5:8f62::1')
+ print(output)
+ self.assertEqual('2001:1234:5:8f62::1 nhid 2 via 2001:1234:5:8f63::2 proto static metric 1024 pref medium', output)
- output = check_output('ip route show 10.10.10.13')
- print(output)
- self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
+ output = check_output('ip route show 10.10.10.13')
+ print(output)
+ self.assertEqual('blackhole 10.10.10.13 nhid 6 dev lo proto static', output)
- output = check_output('ip -6 route show 2001:1234:5:8f62::2')
- print(output)
- self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
+ output = check_output('ip -6 route show 2001:1234:5:8f62::2')
+ print(output)
+ self.assertEqual('blackhole 2001:1234:5:8f62::2 nhid 7 dev lo proto static metric 1024 pref medium', output)
- output = check_output('ip route show 10.10.10.14')
- print(output)
- self.assertIn('10.10.10.14 nhid 21 proto static', output)
- self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
- self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
+ output = check_output('ip route show 10.10.10.14')
+ print(output)
+ self.assertIn('10.10.10.14 nhid 21 proto static', output)
+ self.assertIn('nexthop via 192.168.20.1 dev dummy98 weight 1', output)
+ self.assertIn('nexthop via 192.168.5.1 dev veth99 weight 3', output)
- output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
- check_json(output)
+ output = check_output(*networkctl_cmd, '--json=short', 'status', env=env)
+ check_json(output)
+
+ def _test_nexthop(self, manage_foreign_nexthops):
+ if not manage_foreign_nexthops:
+ copy_networkd_conf_dropin('networkd-manage-foreign-nexthops-no.conf')
+
+ check_output('ip link add dummy98 type dummy')
+ check_output('ip link set dummy98 up')
+ check_output('ip address add 192.168.20.20/24 dev dummy98')
+ check_output('ip nexthop add id 42 via 192.168.20.2 dev dummy98')
copy_network_unit('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network',
'12-dummy.netdev', '25-nexthop-dummy.network')
start_networkd()
- check_nexthop(self)
+ self.check_nexthop(manage_foreign_nexthops)
remove_network_unit('25-nexthop.network')
copy_network_unit('25-nexthop-nothing.network')
networkctl_reconfigure('dummy98')
networkctl_reload()
- check_nexthop(self)
+ self.check_nexthop(manage_foreign_nexthops)
remove_link('veth99')
time.sleep(2)
print(output)
self.assertEqual(output, '')
+ @expectedFailureIfNexthopIsNotAvailable()
+ def test_nexthop(self):
+ first = True
+ for manage_foreign_nexthops in [True, False]:
+ if first:
+ first = False
+ else:
+ self.tearDown()
+
+ print(f'### test_nexthop(manage_foreign_nexthops={manage_foreign_nexthops})')
+ with self.subTest(manage_foreign_nexthops=manage_foreign_nexthops):
+ self._test_nexthop(manage_foreign_nexthops)
+
class NetworkdTCTests(unittest.TestCase, Utilities):
def setUp(self):
test ! -e "$root/etc/systemd/system/test1-badalias.socket"
test -h "$root/etc/systemd/system/test1-goodalias2.service"
-: '-------aliases in reeanable----------------------------------'
+: '-------aliases in reenable----------------------------------'
( ! "$systemctl" --root="$root" reenable test1 )
test -h "$root/etc/systemd/system/default.target.wants/test1.service"
test ! -e "$root/etc/systemd/system/test1-goodalias.service"
journalctl --flush
fi
+ rm -f /run/systemd/journald.conf.d/99-forward-to-console.conf
+ systemctl restart systemd-journald
+
return 0
}
# so try it a couple of times
for _ in {0..9}; do
setterm --term linux --dump --file /tmp/console.dump
- if grep -aq "Press any key to exit" /tmp/console.dump
- grep -aq "$expected_message" /tmp/console.dump
- grep -aq "The current boot has failed" /tmp/console.dump; then
+ if grep -aq "Press any key to exit" /tmp/console.dump &&
+ grep -aq "$expected_message" /tmp/console.dump &&
+ grep -aq "The current boot has failed" /tmp/console.dump; then
return 0
fi
# current boot, let's temporarily overmount /var/log/journal with a tmpfs,
# as we're going to wipe it multiple times, but we need to keep the original
# journal intact for the other tests to work correctly.
+#
+# Also, since we'll eventually lose the journal from this test, let's temporarily
+# forward everything to console, to make potential fails debug-able.
trap at_exit EXIT
+mkdir -p /run/systemd/journald.conf.d/
+echo -ne '[Journal]\nForwardToConsole=yes' >/run/systemd/journald.conf.d/99-forward-to-console.conf
mount -t tmpfs tmpfs /var/log/journal
systemctl restart systemd-journald
# the transient stuff from systemd-run. Let's just skip the following tests
# in that case instead of complicating the test setup even more */
if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then
+ if ! systemd-detect-virt -cq && command -v bootctl >/dev/null; then
+ boot_path="$(bootctl --print-boot-path)"
+ esp_path="$(bootctl --print-esp-path)"
+
+ # If the mount points are handled by automount units, make sure we trigger
+ # them before proceeding further
+ ls -l "$boot_path" "$esp_path"
+ fi
+
systemd-run --wait --pipe -p ProtectSystem=yes \
- bash -xec "test ! -w /usr; test ! -w /boot; test -w /etc; test -w /var"
+ bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test -w /etc; test -w /var"
systemd-run --wait --pipe -p ProtectSystem=full \
- bash -xec "test ! -w /usr; test ! -w /boot; test ! -w /etc; test -w /var"
+ bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test ! -w /etc; test -w /var"
systemd-run --wait --pipe -p ProtectSystem=strict \
bash -xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc"
systemd-run --wait --pipe -p ProtectSystem=no \
cat >/run/systemd/system/badbin_assert.service <<EOF
[Service]
ExecStart=/tmp/badbin
-Restart=never
+Restart=no
EOF
cat >/run/systemd/system/badbin_assert.socket <<EOF
exit 1
fi
+# This test requires systemd to run in the initrd as well, which is not the case
+# for mkinitrd-based initrd (Ubuntu/Debian)
+if [[ "$(systemctl show -P InitRDTimestampMonotonic)" -eq 0 ]]; then
+ echo "systemd didn't run in the initrd, skipping the test"
+ touch /skipped
+ exit 0
+fi
+
# We should've created a mount under /run in initrd (see the other half of the test)
# that should've survived the transition from initrd to the real system
test -d /run/initrd-mount-target
# shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/test-control.sh
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+maybe_mount_usr_overlay
+trap 'maybe_umount_usr_overlay' EXIT
clear_unit() {
local unit_name="${1:?}"
-w /sys/fs/cgroup/system.slice/test-0.service/cgroup.procs -a \
-w /sys/fs/cgroup/system.slice/test-0.service/cgroup.subtree_control
+# Test if this also works for some of the more recent attrs the kernel might or might not support
+for attr in cgroup.threads memory.oom.group memory.reclaim ; do
+
+ if grep -q "$attr" /sys/kernel/cgroup/delegate ; then
+ systemd-run --wait \
+ --unit=test-0.service \
+ --property="DynamicUser=1" \
+ --property="Delegate=" \
+ test -w /sys/fs/cgroup/system.slice/test-0.service/ -a \
+ -w /sys/fs/cgroup/system.slice/test-0.service/"$attr"
+ fi
+done
+
systemd-run --wait \
--unit=test-1.service \
--property="DynamicUser=1" \
# TODO:
# - /proc/cmdline parsing
-# - figure out token support (apart from TPM2, as that's covered by TEST-70-TPM2)
-# - this might help https://www.qemu.org/docs/master/system/devices/ccid.html
# - expect + interactive auth?
# We set up an encrypted /var partition which should get mounted automatically
empty1 $IMAGE_EMPTY - headless=1,try-empty-password=1
# This one expects the key to be under /{etc,run}/cryptsetup-keys.d/empty_nokey.key
empty_nokey $IMAGE_EMPTY - headless=1
+empty_pkcs11_auto $IMAGE_EMPTY - headless=1,pkcs11-uri=auto
detached $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=$IMAGE_DETACHED_HEADER,keyfile-offset=32,keyfile-size=16
detached_store0 $IMAGE_DETACHED $IMAGE_DETACHED_KEYFILE headless=1,header=/header:LABEL=header_store,keyfile-offset=32,keyfile-size=16
cp "$IMAGE_EMPTY_KEYFILE" /run/cryptsetup-keys.d/empty_nokey.key
cryptsetup_start_and_check empty_nokey
+if [[ -r /etc/softhsm2.conf ]]; then
+ # Test unlocking with a PKCS#11 token
+ export SOFTHSM2_CONF="/etc/softhsm2.conf"
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=RSATestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+ PIN="1234" systemd-cryptenroll --pkcs11-token-uri="pkcs11:token=TestToken;object=ECTestKey" --unlock-key-file="$IMAGE_EMPTY_KEYFILE" "$IMAGE_EMPTY"
+ cryptsetup_start_and_check empty_pkcs11_auto
+ cryptsetup luksKillSlot -q "$IMAGE_EMPTY" 2
+ cryptsetup token remove --token-id 0 "$IMAGE_EMPTY"
+fi
+
cryptsetup_start_and_check detached
cryptsetup_start_and_check detached_store{0..2}
cryptsetup_start_and_check -f detached_fail{0..4}
rm -fvr "/usr/lib/systemd/system/$UNIT_NAME" "/etc/systemd/system/$UNIT_NAME.d" "+4"
fi
+ maybe_umount_usr_overlay
+
rm -f /etc/init.d/issue-24990
return 0
}
+maybe_mount_usr_overlay
trap at_exit EXIT
# Create a simple unit file for testing
set -eux
set -o pipefail
-if journalctl -b -t systemd --grep '\.device: Changed plugged -> dead'; then
+if journalctl -b -t systemd --grep '(?<!loop.|diskseq-.)\.device: Changed plugged -> dead'; then
exit 1
fi
systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service
fi
+# Decrypt/encrypt via varlink
+
+echo -n '{"data":"Zm9vYmFyCg=="}' > /tmp/vlcredsdata
+
+varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Encrypt "$(cat /tmp/vlcredsdata)" | \
+ varlinkctl call /run/systemd/io.systemd.Credentials io.systemd.Credentials.Decrypt > /tmp/vlcredsdata2
+
+cmp /tmp/vlcredsdata /tmp/vlcredsdata2
+rm /tmp/vlcredsdata /tmp/vlcredsdata2
+
systemd-analyze log-level info
touch /testok
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
+maybe_mount_usr_overlay
+trap 'maybe_umount_usr_overlay' EXIT
+
teardown_test_dependencies() (
set +eux
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-62-RESTRICT-IFACES-altname
+[Service]
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
+ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
+ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
+RestrictNetworkInterfaces=veth0-altname-with-more-than-15-chars
+RestrictNetworkInterfaces=veth1-altname-with-more-than-15-chars
+Type=oneshot
ip -n "ns${i}" link set dev lo up
ip -n "ns${i}" addr add "192.168.113."$((4*i+1))/30 dev "veth${i}_"
ip link set dev "veth${i}" up
+ ip link property add dev "veth${i}" altname "veth${i}-altname-with-more-than-15-chars"
ip addr add "192.168.113."$((4*i+2))/30 dev "veth${i}"
done
}
# check that systemd-analyze cat-config paths work in a chroot
mkdir -p /tmp/root
mount --bind / /tmp/root
+ if mountpoint -q /usr; then
+ mount --bind /usr /tmp/root/usr
+ fi
systemd-analyze cat-config systemd/system-preset >/tmp/out1
chroot /tmp/root systemd-analyze cat-config systemd/system-preset >/tmp/out2
diff /tmp/out{1,2}
(! systemd-analyze verify /tmp/hoge@nonexist.service)
(! systemd-analyze verify /tmp/hoge@.service)
+# test that all commands are verified.
+cat <<EOF >/tmp/multi-exec-start.service
+[Service]
+Type=oneshot
+ExecStart=true
+ExecStart=ls
+EOF
+systemd-analyze verify /tmp/multi-exec-start.service
+echo 'ExecStart=command-should-not-exist' >>/tmp/multi-exec-start.service
+(! systemd-analyze verify /tmp/multi-exec-start.service)
+
# Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed
# The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in
# values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
+maybe_mount_usr_overlay
+trap 'maybe_umount_usr_overlay' EXIT
+
enable_debug() {
mkdir -p /run/systemd/system/systemd-localed.service.d
cat >>/run/systemd/system/systemd-localed.service.d/override.conf <<EOF
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+if systemd-detect-virt --quiet --container; then
+ echo "running on container, skipping."
+ exit 0
+fi
+
+if ! command -v bootctl >/dev/null; then
+ echo "bootctl not found, skipping."
+ exit 0
+fi
+
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
+# shellcheck source=test/units/test-control.sh
+. "$(dirname "$0")"/test-control.sh
+
+basic_tests() {
+ bootctl "$@" --help
+ bootctl "$@" --version
+
+ bootctl "$@" install --make-entry-directory=yes
+ bootctl "$@" remove --make-entry-directory=yes
+
+ bootctl "$@" install --all-architectures
+ bootctl "$@" remove --all-architectures
+
+ bootctl "$@" install --make-entry-directory=yes --all-architectures
+ bootctl "$@" remove --make-entry-directory=yes --all-architectures
+
+ bootctl "$@" install
+ (! bootctl "$@" update)
+ bootctl "$@" update --graceful
+
+ bootctl "$@" is-installed
+ bootctl "$@" is-installed --graceful
+ bootctl "$@" random-seed
+
+ bootctl "$@"
+ bootctl "$@" status
+ bootctl "$@" status --quiet
+ bootctl "$@" list
+ bootctl "$@" list --quiet
+ bootctl "$@" list --json=short
+ bootctl "$@" list --json=pretty
+
+ bootctl "$@" remove
+ (! bootctl "$@" is-installed)
+ (! bootctl "$@" is-installed --graceful)
+}
+
+testcase_bootctl_basic() {
+ assert_eq "$(bootctl --print-esp-path)" "/efi"
+ assert_eq "$(bootctl --print-boot-path)" "/boot"
+ bootctl --print-root-device
+
+ basic_tests
+}
+
+cleanup_image() (
+ set +e
+
+ if [[ -z "${IMAGE_DIR:-}" ]]; then
+ return 0
+ fi
+
+ umount "${IMAGE_DIR}/root"
+
+ if [[ -n "${LOOPDEV:-}" ]]; then
+ losetup -d "${LOOPDEV}"
+ unset LOOPDEV
+ fi
+
+ udevadm settle
+
+ rm -rf "${IMAGE_DIR}"
+ unset IMAGE_DIR
+
+ return 0
+)
+
+testcase_bootctl_image() {
+ IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
+ trap cleanup_image RETURN
+
+ truncate -s 256m "${IMAGE_DIR}/image"
+
+ cat >"${IMAGE_DIR}/partscript" <<EOF
+label: gpt
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=64M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
+EOF
+
+ LOOPDEV="$(losetup --show -P -f "${IMAGE_DIR}/image")"
+ sfdisk "$LOOPDEV" <"${IMAGE_DIR}/partscript"
+
+ udevadm settle
+
+ mkfs.vfat -n esp "${LOOPDEV}p1"
+ mkfs.ext4 -L root "${LOOPDEV}p2"
+ mkfs.ext4 -L boot "${LOOPDEV}p3"
+
+ mkdir -p "${IMAGE_DIR}/root"
+ mount -t ext4 "${LOOPDEV}p2" "${IMAGE_DIR}/root"
+
+ mkdir -p "${IMAGE_DIR}/root/efi"
+ mkdir -p "${IMAGE_DIR}/root/boot"
+ mkdir -p "${IMAGE_DIR}/root/etc"
+ mkdir -p "${IMAGE_DIR}/root/usr/lib"
+ if [[ -f /usr/lib/os-release ]]; then
+ cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
+ ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
+ else
+ cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
+ fi
+
+ umount "${IMAGE_DIR}/root"
+
+ assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path)" "/run/systemd/mount-rootfs/efi"
+ assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-esp-path --esp-path=/efi)" "/run/systemd/mount-rootfs/efi"
+ assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path)" "/run/systemd/mount-rootfs/boot"
+ assert_eq "$(bootctl --image "${IMAGE_DIR}/image" --print-boot-path --boot-path=/boot)" "/run/systemd/mount-rootfs/boot"
+
+ # FIXME: This provides spurious result.
+ bootctl --image "${IMAGE_DIR}/image" --print-root-device || :
+
+ basic_tests --image "${IMAGE_DIR}/image"
+}
+
+cleanup_raid() (
+ set +e
+
+ if [[ -z "${IMAGE_DIR:-}" ]]; then
+ return 0
+ fi
+
+ systemd-umount "${IMAGE_DIR}/root/efi"
+ systemd-umount "${IMAGE_DIR}/root/boot"
+ systemd-umount "${IMAGE_DIR}/root"
+
+ mdadm --misc --stop /dev/md/raid-esp
+ mdadm --misc --stop /dev/md/raid-root
+
+ if [[ -n "${LOOPDEV1:-}" ]]; then
+ mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
+ mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
+ fi
+
+ if [[ -n "${LOOPDEV2:-}" ]]; then
+ mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
+ mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
+ fi
+
+ udevadm settle
+
+ if [[ -n "${LOOPDEV1:-}" ]]; then
+ mdadm --misc --force --zero-superblock "${LOOPDEV1}p1"
+ mdadm --misc --force --zero-superblock "${LOOPDEV1}p2"
+ losetup -d "${LOOPDEV1}"
+ unset LOOPDEV1
+ fi
+
+ if [[ -n "${LOOPDEV2:-}" ]]; then
+ mdadm --misc --force --zero-superblock "${LOOPDEV2}p1"
+ mdadm --misc --force --zero-superblock "${LOOPDEV2}p2"
+ losetup -d "${LOOPDEV2}"
+ unset LOOPDEV2
+ fi
+
+ udevadm settle
+
+ rm -rf "${IMAGE_DIR}"
+
+ return 0
+)
+
+testcase_bootctl_raid() {
+ if ! command -v mdadm >/dev/null; then
+ echo "mdadm not found, skipping."
+ return 0
+ fi
+
+ if ! command -v mkfs.btrfs >/dev/null; then
+ echo "mkfs.btrfs not found, skipping."
+ return 0
+ fi
+
+ IMAGE_DIR="$(mktemp --directory /tmp/test-bootctl.XXXXXXXXXX)"
+ trap cleanup_raid RETURN
+
+ truncate -s 256m "${IMAGE_DIR}/image1"
+ truncate -s 256m "${IMAGE_DIR}/image2"
+
+ cat >"${IMAGE_DIR}/partscript" <<EOF
+label: gpt
+type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B name=esp size=64M
+type=0FC63DAF-8483-4772-8E79-3D69D8477DE4 name=root size=64M bootable
+type=BC13C2FF-59E6-4262-A352-B275FD6F7172 name=boot
+EOF
+
+ LOOPDEV1="$(losetup --show -P -f "${IMAGE_DIR}/image1")"
+ LOOPDEV2="$(losetup --show -P -f "${IMAGE_DIR}/image2")"
+ sfdisk "$LOOPDEV1" <"${IMAGE_DIR}/partscript"
+ sfdisk "$LOOPDEV2" <"${IMAGE_DIR}/partscript"
+
+ udevadm settle
+
+ echo y | mdadm --create /dev/md/raid-esp --name "raid-esp" "${LOOPDEV1}p1" "${LOOPDEV2}p1" -v -f --level=1 --raid-devices=2
+ mkfs.vfat /dev/md/raid-esp
+ echo y | mdadm --create /dev/md/raid-root --name "raid-root" "${LOOPDEV1}p2" "${LOOPDEV2}p2" -v -f --level=1 --raid-devices=2
+ mkfs.ext4 /dev/md/raid-root
+ mkfs.btrfs -f -M -d raid1 -m raid1 -L "raid-boot" "${LOOPDEV1}p3" "${LOOPDEV2}p3"
+
+ mkdir -p "${IMAGE_DIR}/root"
+ mount -t ext4 /dev/md/raid-root "${IMAGE_DIR}/root"
+ mkdir -p "${IMAGE_DIR}/root/efi"
+ mount -t vfat /dev/md/raid-esp "${IMAGE_DIR}/root/efi"
+ mkdir -p "${IMAGE_DIR}/root/boot"
+ mount -t btrfs "${LOOPDEV1}p3" "${IMAGE_DIR}/root/boot"
+
+ mkdir -p "${IMAGE_DIR}/root/etc"
+ mkdir -p "${IMAGE_DIR}/root/usr/lib"
+ if [[ -f /usr/lib/os-release ]]; then
+ cp /usr/lib/os-release "${IMAGE_DIR}/root/usr/lib/."
+ ln -s ../usr/lib/os-release "${IMAGE_DIR}/root/etc/os-release"
+ else
+ cp -a /etc/os-release "${IMAGE_DIR}/root/etc/."
+ fi
+
+ # find_esp() does not support md RAID partition.
+ (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path)
+ (! bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)
+
+ # If the verification is relaxed, it accepts md RAID partition.
+ assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path)" "${IMAGE_DIR}/root/efi"
+ assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-esp-path --esp-path=/efi)" "${IMAGE_DIR}/root/efi"
+
+ # find_xbootldr() does not support btrfs RAID, and bootctl tries to fall back to use ESP.
+ # (but as in the above, the ESP verification is also failed in this case).
+ (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path)
+ (! bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)
+
+ # If the verification for ESP is relaxed, bootctl falls back to use ESP.
+ assert_eq "$(SYSTEMD_RELAX_ESP_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/efi"
+
+ # If the verification is relaxed, it accepts the xbootldr partition.
+ assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path)" "${IMAGE_DIR}/root/boot"
+ assert_eq "$(SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes bootctl --root "${IMAGE_DIR}/root" --print-boot-path --boot-path=/boot)" "${IMAGE_DIR}/root/boot"
+
+ # FIXME: This provides spurious result.
+ bootctl --root "${IMAGE_DIR}/root" --print-root-device || :
+
+ SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes basic_tests --root "${IMAGE_DIR}/root"
+}
+
+run_testcases
EOF
# Test files
+
+networkctl mask --runtime "donotexist.network"
+assert_eq "$(readlink /run/systemd/network/donotexist.network)" "/dev/null"
+networkctl unmask "donotexist.network" # unmask should work even without --runtime
+[[ ! -e /run/systemd/network/donotexist.network ]]
+
+touch /usr/lib/systemd/network/donotexist.network
+(! networkctl unmask "donotexist.network")
+rm /usr/lib/systemd/network/donotexist.network
+
networkctl cat "$NETWORK_NAME" | tail -n +2 | cmp - "/usr/lib/systemd/network/$NETWORK_NAME"
cat >new <<EOF
EOF
EDITOR='mv new' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null
+(! networkctl mask --runtime "$NETWORK_NAME")
printf '%s\n' '[Match]' 'Name=test2' | cmp - "/run/systemd/network/$NETWORK_NAME"
+networkctl mask "$NETWORK_NAME"
+assert_eq "$(readlink "/etc/systemd/network/$NETWORK_NAME")" "/dev/null"
+(! networkctl edit "$NETWORK_NAME")
+(! networkctl edit --runtime "$NETWORK_NAME")
+(! networkctl cat "$NETWORK_NAME")
+networkctl unmask "$NETWORK_NAME"
+
EDITOR='true' script -ec 'networkctl edit "$NETWORK_NAME"' /dev/null
printf '%s\n' '[Match]' 'Name=test2' | cmp - "/etc/systemd/network/$NETWORK_NAME"
+(! networkctl mask "$NETWORK_NAME")
(! EDITOR='true' script -ec 'networkctl edit --runtime "$NETWORK_NAME"' /dev/null)
cat >"+4" <<EOF
assert_eq "$A" "$B"
+# Check that invoking the tool under the uid0 alias name works
+uid0 ls /
+echo "$(uid0 echo foo)" = "foo"
+
umount /proc/version
rm "$V"
"$@" |& tee "$RUN_OUT"
}
+run_delv() {
+ # Since [0] delv no longer loads /etc/(bind/)bind.keys by default, so we
+ # have to do that explicitly for each invocation
+ run delv -a /etc/bind.keys "$@"
+}
+
disable_ipv6() {
sysctl -w net.ipv6.conf.all.disable_ipv6=1
}
restart_resolved() {
systemctl stop systemd-resolved.service
(! systemctl is-failed systemd-resolved.service)
+ # Reset the restart counter since we call this method a bunch of times
+ # and can occasionally hit the default rate limit
+ systemctl reset-failed systemd-resolved.service
systemctl start systemd-resolved.service
systemctl service-log-level systemd-resolved.service debug
}
# Check the trust chain (with and without systemd-resolved in between
# Issue: https://github.com/systemd/systemd/issues/22002
# PR: https://github.com/systemd/systemd/pull/23289
-run delv @ns1.unsigned.test signed.test
+run_delv @ns1.unsigned.test signed.test
grep -qF "; fully validated" "$RUN_OUT"
-run delv signed.test
+run_delv signed.test
grep -qF "; fully validated" "$RUN_OUT"
for addr in "${DNS_ADDRESSES[@]}"; do
- run delv "@$addr" -t A mail.signed.test
+ run_delv "@$addr" -t A mail.signed.test
grep -qF "; fully validated" "$RUN_OUT"
- run delv "@$addr" -t AAAA mail.signed.test
+ run_delv "@$addr" -t AAAA mail.signed.test
grep -qF "; fully validated" "$RUN_OUT"
done
run resolvectl query mail.signed.test
grep -qF "fd00:dead:beef:cafe::123" "$RUN_OUT"
grep -qF "authenticated: yes" "$RUN_OUT"
# Check OPENPGPKEY support
-run delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test
+run_delv -t OPENPGPKEY 5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test
grep -qF "; fully validated" "$RUN_OUT"
run resolvectl openpgp mr.smith@signed.test
grep -qF "5a786cdc59c161cdafd818143705026636962198c66ed4c5b3da321e._openpgpkey.signed.test" "$RUN_OUT"
local addr
for addr in "${DNS_ADDRESSES[@]}"; do
- run delv "@$addr" -t "$record" "$domain"
+ run_delv "@$addr" -t "$record" "$domain"
grep -qF "$message" "$RUN_OUT"
done
- run delv -t "$record" "$domain"
+ run_delv -t "$record" "$domain"
grep -qF "$message" "$RUN_OUT"
run resolvectl query "$domain"
# Check the trust chain (with and without systemd-resolved in between
# Issue: https://github.com/systemd/systemd/issues/22002
# PR: https://github.com/systemd/systemd/pull/23289
-run delv @ns1.unsigned.test sub.onlinesign.test
+run_delv @ns1.unsigned.test sub.onlinesign.test
grep -qF "; fully validated" "$RUN_OUT"
-run delv sub.onlinesign.test
+run_delv sub.onlinesign.test
grep -qF "; fully validated" "$RUN_OUT"
run dig +short sub.onlinesign.test
grep -qF 'onlinesign.test IN TXT "hello from onlinesign"' "$RUN_OUT"
for addr in "${DNS_ADDRESSES[@]}"; do
- run delv "@$addr" -t A dual.onlinesign.test
+ run_delv "@$addr" -t A dual.onlinesign.test
grep -qF "10.0.0.135" "$RUN_OUT"
- run delv "@$addr" -t AAAA dual.onlinesign.test
+ run_delv "@$addr" -t AAAA dual.onlinesign.test
grep -qF "fd00:dead:beef:cafe::135" "$RUN_OUT"
- run delv "@$addr" -t ANY ipv6.onlinesign.test
+ run_delv "@$addr" -t ANY ipv6.onlinesign.test
grep -qF "fd00:dead:beef:cafe::136" "$RUN_OUT"
done
run resolvectl query dual.onlinesign.test
return 1
}
+
+MOUNTED_USR_OVERLAY=false
+
+maybe_mount_usr_overlay() {
+ if [[ ! -w /usr ]]; then
+ mkdir -p /tmp/usr-overlay/{upperdir,workdir}
+ mount -t overlay -o lowerdir=/usr,upperdir=/tmp/usr-overlay/upperdir,workdir=/tmp/usr-overlay/workdir overlay /usr
+ MOUNTED_USR_OVERLAY=true
+ fi
+}
+
+maybe_umount_usr_overlay() {
+ if "$MOUNTED_USR_OVERLAY"; then
+ umount -l /usr
+ fi
+}
'file' : 'systemd-coredump@.service.in',
'conditions' : ['ENABLE_COREDUMP'],
},
+ {
+ 'file' : 'systemd-creds.socket',
+ 'symlinks' : ['sockets.target.wants/'],
+ },
+ { 'file' : 'systemd-creds@.service' },
{ 'file' : 'systemd-exit.service' },
{
'file' : 'systemd-firstboot.service',
'file' : 'systemd-homed-activate.service',
'conditions' : ['ENABLE_HOMED'],
},
+ {
+ 'file' : 'systemd-homed-firstboot.service',
+ 'conditions' : ['ENABLE_HOMED'],
+ },
{
'file' : 'systemd-homed.service.in',
'conditions' : ['ENABLE_HOMED'],
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Credential Encryption/Decryption (Varlink)
+Documentation=man:systemd-creds(1)
+DefaultDependencies=no
+Before=sockets.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.Credentials
+FileDescriptorName=varlink
+SocketMode=0600
+Accept=yes
+
+[Install]
+WantedBy=sockets.target
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Credential Encryption/Decryption (Varlink)
+Documentation=man:systemd-creds(1)
+DefaultDependencies=no
+Conflicts=shutdown.target initrd-switch-root.target
+Before=shutdown.target initrd-switch-root.target
+
+[Service]
+Environment=LISTEN_FDNAMES=varlink
+ExecStart=-systemd-creds
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=First Boot Home Area Wizard
+Documentation=man:homectl(1)
+ConditionFirstBoot=yes
+After=home.mount systemd-homed.service
+Before=systemd-user-sessions.service first-boot-complete.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=homectl firstboot --prompt-new-user
+StandardOutput=tty
+StandardInput=tty
+StandardError=tty
+ImportCredential=home.*
+
+[Install]
+WantedBy=systemd-homed.service
+Also=systemd-homed.service
[Install]
WantedBy=multi-user.target
Alias=dbus-org.freedesktop.home1.service
-Also=systemd-homed-activate.service systemd-userdbd.service
+Also=systemd-homed-activate.service systemd-userdbd.service systemd-homed-firstboot.service