- name: kernel-install
keys: ['kernel-install']
- - name: logind
+ - name: login
keys: ['systemd-logind', 'loginctl', 'pam_systemd']
- - name: machined
+ - name: machine
keys: ['systemd-machined', 'machinectl']
- name: modules-load
- any-glob-to-any-file: 'src/journal-remote/*'
login:
- changed-files:
- - any-glob-to-any-file: '**/sd-login*/**'
-logind:
- - changed-files:
- - any-glob-to-any-file: 'src/login/*'
+ - any-glob-to-any-file: ['src/login/*', '**/sd-login*/**']
meson:
- changed-files:
- any-glob-to-any-file: ['meson_options.txt', '**/meson.build']
steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633
- - uses: systemd/mkosi@236784dbb597321eedab50cef06cc49f92c57293
+ - uses: systemd/mkosi@6ab7d9f09f8f2633f4b7c777a04e62e109486e2f
# Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
# immediately, we remove the files in the background. However, we first move them to a different location
or /efi/ hierarchies in /etc/fstab. This is to prevent the generator
from interfering with systems where the ESP is explicitly configured
to be mounted at some path, for example /boot/efi/ (this type of
- setup is obsolete but still commonly found).
+ setup is obsolete, but still commonly found).
* The behavior of systemd-sleep and systemd-homed has been updated to
freeze user sessions when entering the various sleep modes or when
embedded in the file name. The files are ordered by version and
the newest one is selected.
- systemd-nspawn --image=/--directory=, systemd-dissect, and the
- RootDirectory=, RootImage=, ExtensionImages=, and
+ systemd-nspawn --image=/--directory=, systemd-dissect, systemd-portabled,
+ and the RootDirectory=, RootImage=, ExtensionImages=, and
ExtensionDirectories= settings for units now support the vpick
protocol and allow the latest version to be selected automatically if
a "*.v/" directory is specified as the source.
- * Encrypted service credentials may now be made accessible to
+ * Encrypted service credentials can now be made accessible to
unprivileged users. systemd-creds gained new options --user/--uid=
for encrypting/decrypting a credential for a specific user.
* New command-line tool 'importctl' to download, import, and export
disk images via systemd-importd is added with the following verbs:
pull-tar, pull-raw, import-tar, import-raw, import-fs, export-tar,
- export-raw, list-transfers, cancel-transfer. This functionality was
- previously available in "machinectl", where it was exclusively for
- machine image. The new "importctl" generalizes this for sysext,
- confext, portable service images, too.
+ export-raw, list-transfers, and cancel-transfer. This functionality
+ was previously available in "machinectl", where it was used
+ exclusively for machine images. The new "importctl" generalizes this
+ for sysext, confext, and portable service images.
Service Management:
enabled by default in the initrd.
* New unit setting WantsMountsFor= has been added. It is analogous to
- RequiresMountsFor=, but with a Wants= dependency instead of
- Requires=. This new logic is used in various places where mounts were
- added as dependencies for other settings (WorkingDirectory=-…,
+ RequiresMountsFor=, but creates a Wants= dependency instead of
+ Requires=. This new logic is now used in various places where mounts
+ were added as dependencies for other settings (WorkingDirectory=-…,
PrivateTmp=yes, cryptsetup lines with 'nofail').
* New unit setting MemoryZSwapWriteback= can be used to control the new
* The manager gained a org.freedesktop.systemd1.StartAuxiliaryScope()
D-Bus method to devolve some processes from a service into a new
- scope. This new scope will remain even if the original service unit
- is restarted. Control group properties of the new scope are copied
- from the originating unit, so various limits are retained.
+ scope. This new scope will remain running, even when the original
+ service unit is restarted or stopped. This allows a service unit to
+ split out some worker processes which need to continue running.
+ Control group properties of the new scope are copied from the
+ originating unit, so various limits are retained.
* Units now expose properties EffectiveMemoryMax=,
EffectiveMemoryHigh=, and EffectiveTasksMax=, which report the
system credential.
* The systemd binary will no longer chainload sysvinit's "telinit"
- binary when called under the init/telinit name on a system that
- isn't booted with systemd. This previously has been supported to make
- sure a distribution that has both init systems installed can be
- reasonably switched from one to the other via a simple
- reboot. Distributions apparently have lost interest in this, and the
- functionality has not been supported on the primary distribution this
- was still intended for for a longer time, and hence has been removed
- now.
-
- * A new concept called "capsules" has been introduced. "Capsules"
- encapsulate additional per-user service managers, whose users are
- transient and are only defined as long as the service manager
- is running (implemented via DynamicUser=1). These service managers run
- off home directories defined in /var/lib/capsules/<name>, where
- <name> is a the capsule's name. These home directories can contain
- regular per-user services and other units. A capsule is started via a
- simple "systemctl start capsule@<name>.service". See the
- capsule@.service(5) man page for further details. Various systemd
- tools (including, and most importantly, systemctl and systemd-run)
- have been updated to interact with capsules via the new
+ binary when called under the init/telinit name on a system that isn't
+ booted with systemd. This previously has been supported to make sure
+ a distribution that has both init systems installed can reasonably
+ switch from one to the other via a simple reboot. Distributions
+ apparently have lost interest in this, and the functionality has not
+ been supported on the primary distribution this was still intended
+ for for a long time, and hence has been removed now.
+
+ * A new concept called "capsules" has been introduced. "Capsules" wrap
+ additional per-user service managers, whose users are transient and
+ are only defined as long as the service manager is running. (This is
+ implemented via DynamicUser=1), allowing a user manager to be used to
+ manager a group of processes without needing to create an actual user
+ account. These service managers run with home directories of
+ /var/lib/capsules/<capsule-name> and can contain regular services and
+ other units. A capsule is started via a simple "systemctl start
+ capsule@<name>.service". See the capsule@.service(5) man page for
+ further details.
+
+ Various systemd tools (including, and most importantly, systemctl and
+ systemd-run) have been updated to interact with capsules via the new
"--capsule="/"-C" switch.
* .socket units gained a new setting PassFileDescriptorsToExec=, taking
encapsulates are passed to the ExecStartPost=, ExecStopPre=,
ExecStopPost= using the usual $LISTEN_FDS interface. This may be used
for doing additional initializations on the sockets once they are
- allocated (for example, install an additional eBPF program on them).
+ allocated. (For example, to install an additional eBPF program on
+ them).
* The .socket setting MaxConnectionsPerSource= (which so far put a
limit on concurrent connections per IP in Accept=yes socket units),
services in a simple Accept=yes mode.
* The service manager will now maintain a counter of soft reboot cycles
- the system went through so far. It may be queried via the D-Bus APIs.
+ the system went through. It may be queried via the D-Bus APIs.
* systemd's execution logic now supports the new pidfd_spawn() API
introduced by glibc 2.39, which allows us to invoke a subprocess in a
target cgroup and get a pidfd back in a single operation.
- * systemd/PID 1 will now send an additional sd_notify() message to its
+ * systemd/PIDÂ 1 will now send an additional sd_notify() message to its
supervising VMM or container manager reporting the selected hostname
("X_SYSTEMD_HOSTNAME=") and machine ID ("X_SYSTEMD_MACHINE_ID=") at
boot. Moreover, the service manager will send additional sd_notify()
reports "ssh-access.target" being reached a VMM/container manager
knows it can now connect to the system via SSH. Finally, a new
sd_notify() message ("X_SYSTEMD_SIGNALS_LEVEL=2") is sent the moment
- PID 1 successfully completed installation of its various UNIX process
- signal handlers (i.e. the moment where SIGRTMIN+4 sent to PID 1 will
- start to have the effect of shutting down the system cleanly).
+ PIDÂ 1 has successfully completed installation of its various UNIX
+ process signal handlers (i.e. the moment where SIGRTMIN+4 sent to
+ PIDÂ 1 will start to have the effect of shutting down the system
+ cleanly).
Journal:
--ssh-key-type= to optionally set up transient SSH keys to pass to the
invoked VMs in order to be able to SSH into them once booted.
+ * systemd-vmspawn will now enable various "HyperV enlightenments" and
+ the "VM Generation ID" on the VMs.
+
+ * A new environment variable $SYSTEMD_VMSPAWN_QEMU_EXTRA may carry
+ additional qemu command line options to pass to qemu.
+
systemd-repart:
* systemd-repart gained new options --generate-fstab= and
sd_journal_stream_fd() but creates a log stream targeted at a
specific log namespace.
+ * The sd-id128 API gained a new API call
+ sd_id128_get_invocation_app_specific() for acquiring an app-specific
+ ID that is derived from the service invocation ID.
+
systemd-cryptsetup/systemd-cryptenroll:
* systemd-cryptenroll can now enroll directly with a PKCS11 public key
Features:
+* systemd-repart should probably enable btrfs' "temp_fsid" feature for all file
+ systems it creates, as we have no interest in RAID for repart, and it should
+ make sure that we can mount them trivially everywhere.
+
* systemd-nspawn should get the same SSH key support that vmspawn now has.
* insert the new pidfs inode number as a third field into PidRef, so that
PCRs.
* vmspawn:
- - enable hyperv extension by default (https://www.qemu.org/docs/master/system/i386/hyperv.html)
- - register with machined
- run in scope unit when invoked from command line, and machined registration is off
- - support --directory= via virtiofs
- sd_notify support
- --ephemeral support
- --read-only support
expected format is six groups of two hexadecimal digits separated by colons,
e.g. `SYSTEMD_VMSPAWN_NETWORK_MAC=12:34:56:78:90:AB`
+* `$SYSTEMD_VMSPAWN_QEMU_EXTRA=…` – may contain additional command line
+ arguments to append the qemu command line.
+
`systemd-logind`:
* `$SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1` — if set, report that
<!ENTITY DEFAULT_USER_TIMEOUT "{{DEFAULT_USER_TIMEOUT_SEC}} s">
<!ENTITY DEFAULT_KEYMAP "{{SYSTEMD_DEFAULT_KEYMAP}}">
<!ENTITY fedora_latest_version "40">
+<!ENTITY fedora_cloud_release "1.10">
<xi:include href="version-info.xml" xpointer="v254"/>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>UseDomains=</varname></term>
+ <listitem>
+ <para>Specifies the network- and protocol-independent default value for the same settins in
+ [IPv6AcceptRA], [DHCPv4], and [DHCPv6] sections below. Takes a boolean, or the special value
+ <option>route</option>. See the same setting in
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Defaults to <literal>no</literal>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[IPv6AcceptRA] Section Options</title>
+
+ <para>This section configures the default setting of the Neighbor Discovery. The following options are
+ available in the [IPv6AcceptRA] section:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>UseDomains=</varname></term>
+ <listitem>
+ <para>Specifies the network-independent default value for the same setting in the [IPv6AcceptRA]
+ section in
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ Takes a boolean, or the special value <option>route</option>. When unspecified, the value specified
+ in the [Network] section in
+ <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ which defaults to <literal>no</literal>, will be used.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<varlistentry>
<term><varname>UseDomains=</varname></term>
- <listitem><para>Specifies the default value for per-network <varname>UseDomains=</varname>.
- Takes a boolean. See for details in
- <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- Defaults to <literal>no</literal>.</para>
-
+ <listitem>
+ <para>Same as the one in the [IPv6AcceptRA] section, but applied for DHCPv4 protocol.</para>
+
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<variablelist class='network-directives'>
<varlistentry>
- <term><varname>PersistLeases=</varname></term>
+ <term><varname>UseDomains=</varname></term>
<listitem>
- <para>Specifies the default value for per-network <varname>PersistLeases=</varname>.
- Takes a boolean. See for details in
- <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- Defaults to <literal>yes</literal>.</para>
+ <para>Same as the one in the [IPv6AcceptRA] section, but applied for DHCPv4 protocol.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
};
</programlisting>
- <!--method GetHardwareSerial is not documented!-->
-
- <!--property OperatingSystemSupportEnd is not documented!-->
-
- <!--property HardwareVendor is not documented!-->
-
- <!--property HardwareModel is not documented!-->
-
- <!--property FirmwareVersion is not documented!-->
-
- <!--property FirmwareVendor is not documented!-->
-
- <!--property FirmwareDate is not documented!-->
-
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.hostname1"/>
<constant>UINT32_MAX</constant> otherwise. See <citerefentry project="man-pages"><refentrytitle>vsock</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
details.</para>
+ <para><varname>OperatingSystemSupportEnd</varname> exposes when the OS' vendor support ends, if this
+ information is known. It's an unsigned 64bit value, in µs since the UNIX epoch, UTC. If this information
+ is not known carries the value 2^64-1, i.e. <constant>UINT64_MAX</constant>.</para>
+
+ <para><varname>HardwareVendor</varname> and <varname>HardwareModel</varname> expose information about the
+ vendor of the hardware of the system. If no such information can be determined these properties are set
+ to empty strings.</para>
+
+ <para><varname>FirmwareVersion</varname> and <varname>FirmwareVendor</varname> expose information about
+ the system's firmware, i.e. a version string and a vendor name. If no such information can be determined
+ these properties are set to empty strings.</para>
+
+ <para><varname>FirmwareDate</varname> exposes the firmware build date, if that information is known. It's
+ an unsigned 64bit value, in µs since the UNIX epoch, UTC. If not known
+ <constant>UNIT64_MAX</constant>.</para>
+
<refsect2>
<title>Methods</title>
requires root privileges, and this method allows access to unprivileged clients through the polkit
framework.</para>
+ <para><function>GetHardwareSerial()</function> returns the "hardware serial" as exposed by the kernel
+ based on DMI information. Reading the file directly requires root privileges, and this method allows
+ access to unprivileged clients through the polkit framework.</para>
+
<para><function>Describe()</function> returns a JSON representation of all properties in one.</para>
</refsect2>
on the system. Note that this method returns only after all the listed operations are completed,
and due to the I/O involved it might take some time.</para>
+ <xi:include href="vpick.xml" xpointer="image"/>
+ <xi:include href="vpick.xml" xpointer="directory"/>
+
<para><function>AttachImageWithExtensions()</function> attaches a portable image to the system.
This method is a superset of <function>AttachImage()</function> with the addition of
a list of extensions as input parameter, which will be overlaid on top of the main
immediately started (blocking operation unless <option>--no-block</option> is passed) and/or enabled after
attaching the image.</para>
+ <xi:include href="vpick.xml" xpointer="image"/>
+ <xi:include href="vpick.xml" xpointer="directory"/>
<xi:include href="version-info.xml" xpointer="v239"/>
</listitem>
</varlistentry>
<para>Note that the same extensions have to be specified, in the same order, when attaching
and detaching.</para>
+ <xi:include href="vpick.xml" xpointer="image"/>
+ <xi:include href="vpick.xml" xpointer="directory"/>
<xi:include href="version-info.xml" xpointer="v249"/></listitem>
</varlistentry>
<refname>sd_event_add_inotify</refname>
<refname>sd_event_add_inotify_fd</refname>
<refname>sd_event_source_get_inotify_mask</refname>
+ <refname>sd_event_source_get_inotify_path</refname>
<refname>sd_event_inotify_handler_t</refname>
<refpurpose>Add an "inotify" file system inode event source to an event loop</refpurpose>
<funcprototype>
<funcdef>int <function>sd_event_source_get_inotify_mask</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
- <paramdef>uint32_t *<parameter>mask</parameter></paramdef>
+ <paramdef>uint32_t *<parameter>ret</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_event_source_get_inotify_path</function></funcdef>
+ <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
+ <paramdef>const char **<parameter>ret</parameter></paramdef>
</funcprototype>
</funcsynopsis>
event source created previously with <function>sd_event_add_inotify()</function>. It takes the event source object
as the <parameter>source</parameter> parameter and a pointer to a <type>uint32_t</type> variable to return the mask
in.</para>
+
+ <para><function>sd_event_source_get_inotify_path()</function> retrieves the target path of the configured
+ inotify watch of an event source created previously with <function>sd_event_add_inotify()</function>. It
+ takes the event source object as the <parameter>source</parameter> parameter and a pointer to a
+ <type>const char **</type> variable to return the path in. The caller must not free the returned path.
+ </para>
</refsect1>
<refsect1>
<varlistentry>
<term><constant>-ESTALE</constant></term>
- <listitem><para>The event loop is already terminated.</para></listitem>
+ <listitem>
+ <para>Returned by <function>sd_event_source_add_inotify()</function> or
+ <function>sd_event_source_add_inotify_fd()</function> when the event loop is already terminated.
+ Returned by <function>sd_event_source_get_inotify_path()</function> when no active inode data is
+ assigned to the event source, e.g. when the event source is disabled.</para>
+ </listitem>
</varlistentry>
<varlistentry>
<term><constant>-EBADF</constant></term>
- <listitem><para>The passed file descriptor is not valid.</para>
+ <listitem><para>The passed file descriptor is not valid.</para></listitem>
- <xi:include href="version-info.xml" xpointer="v250"/></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOSYS</constant></term>
- <listitem><para><function>sd_event_add_inotify_fd()</function> was called without
- <filename>/proc/</filename> mounted.</para>
+ <listitem>
+ <para><function>sd_event_add_inotify_fd()</function> or
+ <function>sd_event_source_get_inotify_path()</function> was called without
+ <filename>/proc/</filename> mounted.</para>
+ </listitem>
- <xi:include href="version-info.xml" xpointer="v250"/></listitem>
</varlistentry>
</variablelist>
<function>sd_event_add_inotify()</function>, and
<function>sd_event_source_get_inotify_mask()</function> were added in version 239.</para>
<para><function>sd_event_add_inotify_fd()</function> was added in version 250.</para>
+ <para><function>sd_event_source_get_inotify_path()</function> was added in version 256.</para>
</refsect1>
<refsect1>
<paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
</funcprototype>
+ <funcprototype>
+ <funcdef>int <function>sd_id128_get_invocation_app_specific</function></funcdef>
+ <paramdef>sd_id128_t <parameter>app_id</parameter></paramdef>
+ <paramdef>sd_id128_t *<parameter>ret</parameter></paramdef>
+ </funcprototype>
+
</funcsynopsis>
</refsynopsisdiv>
for details. The ID is cached internally. In future a different mechanism to determine the invocation ID
may be added.</para>
+ <para><function>sd_id128_get_invocation_app_specific()</function> derives an application-specific ID from
+ the invocation ID.</para>
+
<para>Note that <function>sd_id128_get_machine_app_specific()</function>,
- <function>sd_id128_get_boot()</function>, <function>sd_id128_get_boot_app_specific()</function>, and
- <function>sd_id128_get_invocation()</function> always return UUID Variant 1 Version 4 compatible IDs.
- <function>sd_id128_get_machine()</function> will also return a UUID Variant 1 Version 4 compatible ID on
- new installations but might not on older. It is possible to convert the machine ID non-reversibly into a
- UUID Variant 1 Version 4 compatible one. For more information, see
+ <function>sd_id128_get_boot()</function>, <function>sd_id128_get_boot_app_specific()</function>,
+ <function>sd_id128_get_invocation()</function> and
+ <function>sd_id128_get_invocation_app_specific</function> always return UUID Variant 1 Version 4
+ compatible IDs. <function>sd_id128_get_machine()</function> will also return a UUID Variant 1 Version 4
+ compatible ID on new installations but might not on older. It is possible to convert the machine ID
+ non-reversibly into a UUID Variant 1 Version 4 compatible one. For more information, see
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>. It is
hence guaranteed that these functions will never return the ID consisting of all zero or all one bits
(<constant>SD_ID128_NULL</constant>, <constant>SD_ID128_ALLF</constant>) — with the possible exception of
<para><function>sd_id128_get_machine_app_specific()</function> was added in version 233.</para>
<para><function>sd_id128_get_boot_app_specific()</function> was added in version 240.</para>
<para><function>sd_id128_get_app_specific()</function> was added in version 255.</para>
+ <para><function>sd_id128_get_invocation_app_specific()</function> was added in version 256.</para>
</refsect1>
<refsect1>
<table>
<title>Partition Type GUIDs</title>
- <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+ <tgroup cols='5' align='left' colsep='1' rowsep='1'>
+ <colspec colname="type" />
<colspec colname="guid" />
<colspec colname="name" />
<colspec colname="where" />
<colspec colname="explanation" />
<thead>
<row>
- <entry>Partition Type GUID</entry>
+ <entry>Partition Type</entry>
+ <entry>GUID</entry>
<entry>Name</entry>
<entry>Mount Point</entry>
<entry>Explanation</entry>
</thead>
<tbody>
<row>
- <entry><constant>SD_GPT_ROOT_X86_64</constant> <constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant></entry>
+ <entry><constant>SD_GPT_ROOT_X86_64</constant></entry>
+ <entry><constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant></entry>
<entry><filename>Root Partition (x86-64)</filename></entry>
<entry><filename>/</filename></entry>
- <entry>The first partition with this type UUID, located on the same disk as the ESP, is used as the root file system <filename>/</filename> on AMD64 / 64-bit x86 systems.</entry>
+ <entry>The first partition with this type UUID, located on the same disk as the ESP used for booting, is used as the root file system <filename>/</filename> on AMD64 / 64-bit x86 systems.</entry>
</row>
<row>
- <entry><constant>SD_GPT_ROOT_ARM64</constant> <constant>b921b045-1df0-41c3-af44-4c6f280d3fae</constant></entry>
+ <entry><constant>SD_GPT_ROOT_ARM64</constant></entry>
+ <entry><constant>b921b045-1df0-41c3-af44-4c6f280d3fae</constant></entry>
<entry><filename>Root Partition (64-bit ARM)</filename></entry>
<entry><filename>/</filename></entry>
- <entry>The first partition with this type UUID, located on the same disk as the ESP, is used as the root file system <filename>/</filename> on AArch64 / 64-bit ARM systems.</entry>
+ <entry>The first partition with this type UUID, located on the same disk as the ESP used for booting, is used as the root file system <filename>/</filename> on AArch64 / 64-bit ARM systems.</entry>
</row>
<row>
- <entry>
- <constant>SD_GPT_ROOT_ALPHA</constant> <constant>SD_GPT_ROOT_ARC</constant> <constant>SD_GPT_ROOT_ARM</constant> <constant>SD_GPT_ROOT_ARM64</constant> <constant>SD_GPT_ROOT_IA64</constant> <constant>SD_GPT_ROOT_LOONGARCH64</constant> <constant>SD_GPT_ROOT_MIPS</constant> <constant>SD_GPT_ROOT_MIPS64</constant> <constant>SD_GPT_ROOT_MIPS_LE</constant> <constant>SD_GPT_ROOT_MIPS64_LE</constant> <constant>SD_GPT_ROOT_PARISC</constant> <constant>SD_GPT_ROOT_PPC</constant> <constant>SD_GPT_ROOT_PPC64</constant> <constant>SD_GPT_ROOT_PPC64_LE</constant> <constant>SD_GPT_ROOT_RISCV32</constant> <constant>SD_GPT_ROOT_RISCV64</constant> <constant>SD_GPT_ROOT_S390</constant> <constant>SD_GPT_ROOT_S390X</constant> <constant>SD_GPT_ROOT_TILEGX</constant> <constant>SD_GPT_ROOT_X86</constant> <constant>SD_GPT_ROOT_X86_64</constant> <constant>SD_GPT_USR_ALPHA</constant> <constant>SD_GPT_USR_ARC</constant> <constant>SD_GPT_USR_ARM</constant> <constant>SD_GPT_USR_IA64</constant> <constant>SD_GPT_USR_LOONGARCH64</constant> <constant>SD_GPT_USR_MIPS_LE</constant> <constant>SD_GPT_USR_MIPS64_LE</constant> <constant>SD_GPT_USR_PARISC</constant> <constant>SD_GPT_USR_PPC</constant> <constant>SD_GPT_USR_PPC64</constant> <constant>SD_GPT_USR_PPC64_LE</constant> <constant>SD_GPT_USR_RISCV32</constant> <constant>SD_GPT_USR_RISCV64</constant> <constant>SD_GPT_USR_S390</constant> <constant>SD_GPT_USR_S390X</constant> <constant>SD_GPT_USR_TILEGX</constant> <constant>SD_GPT_USR_X86</constant>
- </entry>
- <entry>root partitions for other architectures</entry>
+ <entry><constant>SD_GPT_ROOT_ALPHA</constant> <constant>SD_GPT_ROOT_ARC</constant> <constant>SD_GPT_ROOT_ARM</constant> <constant>SD_GPT_ROOT_ARM64</constant> <constant>SD_GPT_ROOT_IA64</constant> <constant>SD_GPT_ROOT_LOONGARCH64</constant> <constant>SD_GPT_ROOT_MIPS</constant> <constant>SD_GPT_ROOT_MIPS64</constant> <constant>SD_GPT_ROOT_MIPS_LE</constant> <constant>SD_GPT_ROOT_MIPS64_LE</constant> <constant>SD_GPT_ROOT_PARISC</constant> <constant>SD_GPT_ROOT_PPC</constant> <constant>SD_GPT_ROOT_PPC64</constant> <constant>SD_GPT_ROOT_PPC64_LE</constant> <constant>SD_GPT_ROOT_RISCV32</constant> <constant>SD_GPT_ROOT_RISCV64</constant> <constant>SD_GPT_ROOT_S390</constant> <constant>SD_GPT_ROOT_S390X</constant> <constant>SD_GPT_ROOT_TILEGX</constant> <constant>SD_GPT_ROOT_X86</constant> <constant>SD_GPT_ROOT_X86_64</constant> <constant>SD_GPT_USR_ALPHA</constant> <constant>SD_GPT_USR_ARC</constant> <constant>SD_GPT_USR_ARM</constant> <constant>SD_GPT_USR_IA64</constant> <constant>SD_GPT_USR_LOONGARCH64</constant> <constant>SD_GPT_USR_MIPS_LE</constant> <constant>SD_GPT_USR_MIPS64_LE</constant> <constant>SD_GPT_USR_PARISC</constant> <constant>SD_GPT_USR_PPC</constant> <constant>SD_GPT_USR_PPC64</constant> <constant>SD_GPT_USR_PPC64_LE</constant> <constant>SD_GPT_USR_RISCV32</constant> <constant>SD_GPT_USR_RISCV64</constant> <constant>SD_GPT_USR_S390</constant> <constant>SD_GPT_USR_S390X</constant> <constant>SD_GPT_USR_TILEGX</constant> <constant>SD_GPT_USR_X86</constant></entry>
+ <entry>…</entry>
+ <entry>Root partitions for other architectures</entry>
<entry><filename>/</filename></entry>
- <entry>The first partition with the type UUID matching the architecture, located on the same disk as the ESP, is used as the root file system <filename>/</filename>. For the full list and constant values, see <ulink url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions Specification</ulink>.</entry>
+ <entry>The first partition with the type UUID matching the architecture, located on the same disk as the ESP used for booting, is used as the root file system <filename>/</filename>. For the full list and constant values, see <ulink url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions Specification</ulink>.</entry>
</row>
<row>
- <entry><constant>SD_GPT_HOME</constant> <constant>933ac7e1-2eb4-4f13-b844-0e14e2aef915</constant></entry>
+ <entry><constant>SD_GPT_HOME</constant></entry>
+ <entry><constant>933ac7e1-2eb4-4f13-b844-0e14e2aef915</constant></entry>
<entry>Home Partition</entry>
<entry><filename>/home/</filename></entry>
- <entry>The first partition with this type UUID on the same disk as the ESP is mounted to <filename>/home/</filename>.</entry>
+ <entry>The first partition with this type UUID on the same disk as the root partition is mounted to <filename>/home/</filename>.</entry>
</row>
<row>
- <entry><constant>SD_GPT_SRV</constant> <constant>3b8f8425-20e0-4f3b-907f-1a25a76f98e8</constant></entry>
+ <entry><constant>SD_GPT_SRV</constant></entry>
+ <entry><constant>3b8f8425-20e0-4f3b-907f-1a25a76f98e8</constant></entry>
<entry>Server Data Partition</entry>
<entry><filename>/srv/</filename></entry>
- <entry>The first partition with this type UUID on the same disk as the ESP is mounted to <filename>/srv/</filename>.</entry>
+ <entry>The first partition with this type UUID on the same disk as the root partition is mounted to <filename>/srv/</filename>.</entry>
</row>
<row>
- <entry><constant>SD_GPT_VAR</constant> <constant>4d21b016-b534-45c2-a9fb-5c16e091fd2d</constant></entry>
+ <entry><constant>SD_GPT_VAR</constant></entry>
+ <entry><constant>4d21b016-b534-45c2-a9fb-5c16e091fd2d</constant></entry>
<entry>Variable Data Partition</entry>
<entry><filename>/var/</filename></entry>
- <entry>The first partition with this type UUID on the same disk as the ESP is mounted to <filename>/var/</filename> — under the condition its partition UUID matches the first 128 bit of the HMAC-SHA256 of the GPT type uuid of this partition keyed by the machine ID of the installation stored in <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
+ <entry>The first partition with this type UUID on the same disk as the root partition is mounted to <filename>/var/</filename> — under the condition its partition UUID matches the first 128 bit of the HMAC-SHA256 of the GPT type uuid of this partition keyed by the machine ID of the installation stored in <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</entry>
</row>
<row>
- <entry><constant>SD_GPT_TMP</constant> <constant>7ec6f557-3bc5-4aca-b293-16ef5df639d1</constant></entry>
+ <entry><constant>SD_GPT_TMP</constant></entry>
+ <entry><constant>7ec6f557-3bc5-4aca-b293-16ef5df639d1</constant></entry>
<entry>Temporary Data Partition</entry>
<entry><filename>/var/tmp/</filename></entry>
- <entry>The first partition with this type UUID on the same disk as the ESP is mounted to <filename>/var/tmp/</filename>.</entry>
+ <entry>The first partition with this type UUID on the same disk as the root partition is mounted to <filename>/var/tmp/</filename>.</entry>
</row>
<row>
- <entry><constant>SD_GPT_SWAP</constant> <constant>0657fd6d-a4ab-43c4-84e5-0933c84b4f4f</constant></entry>
+ <entry><constant>SD_GPT_SWAP</constant></entry>
+ <entry><constant>0657fd6d-a4ab-43c4-84e5-0933c84b4f4f</constant></entry>
<entry>Swap</entry>
<entry>n/a</entry>
- <entry>All partitions with this type UUID on the same disk as the ESP are used as swap.</entry>
+ <entry>All partitions with this type UUID on the same disk as the root partition are used as swap.</entry>
</row>
<row>
- <entry><constant>SD_GPT_ESP</constant> <constant>c12a7328-f81f-11d2-ba4b-00a0c93ec93b</constant></entry>
+ <entry><constant>SD_GPT_ESP</constant></entry>
+ <entry><constant>c12a7328-f81f-11d2-ba4b-00a0c93ec93b</constant></entry>
<entry>EFI System Partition (ESP)</entry>
<entry><filename>/efi/</filename> or <filename>/boot/</filename></entry>
<entry>The first partition with this type UUID located on the same disk as the root partition is mounted to <filename>/boot/</filename> or <filename>/efi/</filename>, see below.</entry>
</row>
<row>
- <entry><constant>SD_GPT_XBOOTLDR</constant> <constant>bc13c2ff-59e6-4262-a352-b275fd6f7172</constant></entry>
+ <entry><constant>SD_GPT_XBOOTLDR</constant></entry>
+ <entry><constant>bc13c2ff-59e6-4262-a352-b275fd6f7172</constant></entry>
<entry>Extended Boot Loader Partition</entry>
<entry><filename>/boot/</filename></entry>
<entry>The first partition with this type UUID located on the same disk as the root partition is mounted to <filename>/boot/</filename>, see below.</entry>
<table>
<title>Partition Attribute Flags</title>
- <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+ <tgroup cols='4' align='left' colsep='1' rowsep='1'>
<colspec colname="flag" />
+ <colspec colname="value" />
<colspec colname="where" />
<colspec colname="explanation" />
<thead>
<row>
<entry>Flag</entry>
+ <entry>Value</entry>
<entry>Applicable to</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
- <entry><constant>SD_GPT_FLAG_READ_ONLY</constant> <constant>0x1000000000000000</constant></entry>
+ <entry><constant>SD_GPT_FLAG_READ_ONLY</constant></entry>
+ <entry><constant>0x1000000000000000</constant></entry>
<entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>, <filename>/var/tmp/</filename>, Extended Boot Loader Partition</entry>
<entry>Partition is mounted read-only</entry>
</row>
<row>
- <entry><constant>SD_GPT_FLAG_NO_AUTO</constant> <constant>0x8000000000000000</constant></entry>
+ <entry><constant>SD_GPT_FLAG_NO_AUTO</constant></entry>
+ <entry><constant>0x8000000000000000</constant></entry>
<entry><filename>/</filename>, <filename>/home/</filename>, <filename>/srv/</filename>, <filename>/var/</filename>, <filename>/var/tmp/</filename>, Extended Boot Loader Partition</entry>
<entry>Partition is not mounted automatically</entry>
</row>
<row>
- <entry><constant>SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL</constant> <constant>0x0000000000000002</constant></entry>
+ <entry><constant>SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL</constant></entry>
+ <entry><constant>0x0000000000000002</constant></entry>
<entry>EFI System Partition (ESP)</entry>
<entry>Partition is not mounted automatically</entry>
</row>
<listitem><para>Pre-calculate the expected values seen in PCR register 11 after boot-up of a unified
kernel image consisting of the components specified with <option>--linux=</option>,
<option>--osrel=</option>, <option>--cmdline=</option>, <option>--initrd=</option>,
- <option>--splash=</option>, <option>--dtb=</option>, <option>--uname=</option>,
- <option>--sbat=</option>, <option>--pcrpkey=</option> see below. Only <option>--linux=</option> is
- mandatory. (Alternatively, specify <option>--current</option> to use the current values of PCR
+ <option>--ucode=</option>, <option>--splash=</option>, <option>--dtb=</option>,
+ <option>--uname=</option>, <option>--sbat=</option>, <option>--pcrpkey=</option> see below.
+ Only <option>--linux=</option> is mandatory. (Alternatively, specify <option>--current</option> to use the current values of PCR
register 11 instead.)</para>
<xi:include href="version-info.xml" xpointer="v252"/>
<term><option>--osrel=<replaceable>PATH</replaceable></option></term>
<term><option>--cmdline=<replaceable>PATH</replaceable></option></term>
<term><option>--initrd=<replaceable>PATH</replaceable></option></term>
+ <term><option>--ucode=<replaceable>PATH</replaceable></option></term>
<term><option>--splash=<replaceable>PATH</replaceable></option></term>
<term><option>--dtb=<replaceable>PATH</replaceable></option></term>
<term><option>--uname=<replaceable>PATH</replaceable></option></term>
<listitem><para>An <literal>.initrd</literal> section with the initrd.</para></listitem>
+ <listitem><para>A <literal>.ucode</literal> section with an initrd containing microcode, to be handed
+ to the kernel before any other initrd. This initrd must not be compressed.</para></listitem>
+
<listitem><para>A <literal>.splash</literal> section with an image (in the Windows
<filename>.BMP</filename> format) to show on screen before invoking the kernel.</para></listitem>
core kernel, the embedded initrd and kernel command line (see above for a full list).</para>
<para>Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means
- every type of initrd will be measured two or three times: the initrd embedded in the kernel image will be
+ every type of initrd will be measured two or three times: the initrds embedded in the kernel image will be
measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials (and the one synthesized
from configuration extensions) will be measured to both PCR 9 and PCR 12; the initrd synthesized from
system extensions will be measured to both PCR 4 and PCR 9. Let's summarize the OS resources and the PCRs
<entry>4 + 9 + 11</entry>
</row>
+ <row>
+ <entry>Microcode initrd (embedded in unified PE binary)</entry>
+ <entry>4 + 9 + 11</entry>
+ </row>
+
<row>
<entry>Default kernel command line (embedded in unified PE binary)</entry>
<entry>4 + 11</entry>
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
- "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-vmspawn" conditional="ENABLE_VMSPAWN"
<listitem><para>Directory to use as file system root for the virtual machine.</para>
- <para>One of either <option>--directory=</option> or <option>--image=</option> must be specified.</para>
+ <para>One of either <option>--directory=</option> or <option>--image=</option> must be specified.
+ If neither are specified <option>--directory=.</option> is assumed.</para>
<para>Note: If mounting a non-root owned directory you may require <option>--private-users=</option>
- to map into the user's subuid namespace.</para>
+ to map into the user's subuid namespace. An example of how to use <constant>/etc/subuid</constant>
+ for this is given later.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
<varlistentry>
<term><option>--cpus=<replaceable>CPUS</replaceable></option></term>
- <listitem><para>Configures the number of CPUs to start the virtual machine with.
+ <listitem><para>The number of CPUs to start the virtual machine with.
Defaults to 1.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
<varlistentry>
<term><option>--ram=<replaceable>BYTES</replaceable></option></term>
- <listitem><para>Configures the amount of memory to start the virtual machine with.
+ <listitem><para>The amount of memory to start the virtual machine with.
Defaults to 2G.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
<varlistentry>
<term><option>--kvm=<replaceable>BOOL</replaceable></option></term>
- <listitem><para>Configures whether to use KVM. If the option is not specified KVM support will be
+ <listitem><para>If <option>--kvm=</option> is not specified KVM support will be
detected automatically. If true, KVM is always used, and if false, KVM is never used.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
<varlistentry>
<term><option>--vsock=<replaceable>BOOL</replaceable></option></term>
- <listitem>
- <para>Configure whether to use VSOCK networking.</para>
+ <listitem><para>If <option>--vsock=</option> is not specified VSOCK networking support will be
+ detected automatically. If true, VSOCK networking is always used, and if false, VSOCK networking is never used.</para>
- <para>If the option is not specified VSOCK support will be detected automatically. If yes is
- specified VSOCK is always used, and vice versa if no is set VSOCK are never used.</para>
- <xi:include href="version-info.xml" xpointer="v255"/>
- </listitem>
+ <xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--vsock-cid=<replaceable>CID</replaceable></option></term>
<listitem>
- <para>Configure vmspawn to use a specific CID for the guest.</para>
-
- <para>If the option is not specified or an empty argument is supplied the guest will be assigned a random CID.</para>
-
- <para>Valid CIDs are in the range <constant>3</constant> to <constant>4294967294</constant> (<constant>0xFFFF_FFFE</constant>).
- CIDs outside of this range are reserved.</para>
+ <para>Sets the specific CID to use for the guest.
+ Valid CIDs are in the range <constant>3</constant> to <constant>4294967294</constant> (<constant>0xFFFF_FFFE</constant>).
+ CIDs outside of this range are reserved. By default vmspawn will attempt to derive a CID for the guest derived from the machine name,
+ falling back to a random CID if this CID is taken.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
<term><option>--tpm=<replaceable>BOOL</replaceable></option></term>
<listitem>
- <para>Configure whether to use VM with a virtual TPM or not.</para>
-
- <para>If the option is not specified vmspawn will detect the presence of <citerefentry project='debian'>
+ <para>If <option>--tpm=</option> is not specified vmspawn will detect the presence of <citerefentry project='debian'>
<refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry> and use it if available.
If yes is specified <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- is always used, and vice versa if no is set <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle>
+ is always used, and if no is set <citerefentry project='debian'><refentrytitle>swtpm</refentrytitle>
<manvolnum>8</manvolnum></citerefentry> is never used.</para>
<para>Note: the virtual TPM used may change in future.</para>
<term><option>--linux=<replaceable>PATH</replaceable></option></term>
<listitem>
- <para>Set the linux kernel image to use for direct kernel boot.</para>
-
- <para>If no kernel was installed into the image then the image will fail to boot.</para>
+ <para>Set the linux kernel image to use for direct kernel boot.
+ If a directory type image is used and <option>--linux=</option> was omitted, vmspawn will search for boot loader entries
+ according to the
+ <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink> assuming
+ XBOOTLDR to be located at /boot and ESP to be /efi respectively.
+ If no kernel was installed into the image then the image will fail to boot.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
<term><option>--initrd=<replaceable>PATH</replaceable></option></term>
<listitem>
- <para>Set the initrd to use for direct kernel boot.</para>
+ <para>Set the initrd to use for direct kernel boot.
+ If the <option>--linux=</option> supplied is a
+ <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink>
+ Type #2 entry, then this argument is not required.
+ If no initrd was installed into the image then the image will fail to boot.</para>
- <para>If the linux kernel supplied is a UKI then this argument is not required.</para>
-
- <para>If the option is specified multiple times vmspawn will merge the initrds together.</para>
-
- <para>If no initrd was installed into the image then the image will fail to boot.</para>
+ <para><option>--initrd=</option> can be specified multiple times and vmspawn will merge them together.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
<term><option>--private-users=<replaceable>UID_SHIFT[:UID_RANGE]</replaceable></option></term>
<listitem><para>Controls user namespacing under <option>--directory=</option>.
- If enabled, <command>virtiofsd</command> is instructed to map user and group ids (UIDs and GIDs).
+ If enabled,
+ <citerefentry><refentrytitle>virtiofsd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ is instructed to map user and group ids (UIDs and GIDs).
This involves mapping the private UIDs/GIDs used in the virtual machine (starting with the virtual machine's
root user 0 and up) to a range of UIDs/GIDs on the host that are not used for other purposes (usually in the
range beyond the host's UID/GID 65536).</para>
for more information.</para>
<para>By default <literal>ed25519</literal> keys are generated, however <literal>rsa</literal> keys
- may also be useful if the VM has a particularly old version of <command>sshd</command></para>.
+ may also be useful if the VM has a particularly old version of <command>sshd</command>.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
$ systemd-vmspawn --image=image.raw
</programlisting>
</example>
+
+ <example>
+ <title>Import and run a Fedora 39 Cloud image using machinectl</title>
+
+ <programlisting>
+$ curl -L \
+ -O https://download.fedoraproject.org/pub/fedora/linux/releases/&fedora_latest_version;/Cloud/x86_64/images/Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86_64.raw.xz \
+ -O https://download.fedoraproject.org/pub/fedora/linux/releases/&fedora_latest_version;/Cloud/x86_64/images/Fedora-Cloud-&fedora_latest_version;-&fedora_cloud_release;-x86_64-CHECKSUM \
+ -O https://fedoraproject.org/fedora.gpg
+$ gpgv --keyring ./fedora.gpg Fedora-Cloud-&fedora_latest_version;-&fedora_cloud_release;-x86_64-CHECKSUM
+$ sha256sum -c Fedora-Cloud-&fedora_latest_version;-&fedora_cloud_release;-x86_64-CHECKSUM
+# machinectl import-raw Fedora-Cloud-Base-&fedora_latest_version;-&fedora_cloud_release;.x86_64.raw.xz fedora-&fedora_latest_version;-cloud
+# systemd-vmspawn -M fedora-&fedora_latest_version;-cloud
+ </programlisting>
+ </example>
+
+ <example>
+ <title>Build and run systemd's system image and forward the VM's journal to a local file</title>
+
+ <programlisting>
+$ mkosi build
+$ systemd-vmspawn \
+ -D mkosi.output/system \
+ --private-users $(grep $(whoami) /etc/subuid | cut -d: -f2) \
+ --linux mkosi.output/system.efi \
+ --forward-journal=vm.journal \
+ enforcing=0
+ </programlisting>
+
+ <para>Note: this example also uses a kernel command line argument to ensure SELinux isn't started in enforcing mode.</para>
+ </example>
+
+ <example>
+ <title>SSH into a running VM using <command>systemd-ssh-proxy</command></title>
+
+ <programlisting>
+$ mkosi build
+$ my_vsock_cid=3735928559
+$ systemd-vmspawn \
+ -D mkosi.output/system \
+ --private-users $(grep $(whoami) /etc/subuid | cut -d: -f2) \
+ --linux mkosi.output/system.efi \
+ --vsock-cid $my_vsock_cid \
+ enforcing=0
+$ ssh root@vsock/$my_vsock_cid -i /run/user/$UID/systemd/vmspawn/machine-*-system-ed25519
+ </programlisting>
+ </example>
</refsect1>
<refsect1>
<member><citerefentry project='debian'><refentrytitle>mkosi</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification">Boot Loader Specification</ulink></member>
</simplelist></para>
</refsect1>
</refentry>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>UseDomains=</varname></term>
+ <listitem>
+ <para>Specifies the protocol-independent default value for the same settins in
+ [IPv6AcceptRA], [DHCPv4], and [DHCPv6] sections below. Takes a boolean, or the special value
+ <option>route</option>. See also the same setting in [DHCPv4] below. Defaults to unset.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>Domains=</varname></term>
<listitem>
effect of the <option>Domains=</option> setting. If set to <option>route</option>, the domain name
received from the DHCP server will be used for routing DNS queries only, but not for searching,
similarly to the effect of the <option>Domains=</option> setting when the argument is prefixed with
- <literal>~</literal>. When unspecified, the value specified in the same setting in
- <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
- which defaults to <literal>no</literal>, will be used.</para>
+ <literal>~</literal>.</para>
+
+ <para>When unspecified, the value specified in the same setting in the [Network] section will be
+ used. When it is unspecified, the value specified in the same setting in the [DHCPv4] section in
+ <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ will be used. When it is unspecified, the value specified in the same setting in the [Network]
+ section in
+ <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ will be used. When none of them are specified, defaults to <literal>no</literal>.</para>
<para>It is recommended to enable this option only on trusted networks, as setting this
affects resolution of all hostnames, in particular of single-label names. It is generally
<para>Additional sections will be inserted into the UKI, either automatically or only if a specific
option is provided. See the discussions of
+ <varname>Microcode=</varname>/<option>--microcode=</option>,
<varname>Cmdline=</varname>/<option>--cmdline=</option>,
<varname>OSRelease=</varname>/<option>--os-release=</option>,
<varname>DeviceTree=</varname>/<option>--devicetree=</option>,
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Microcode=<replaceable>UCODE</replaceable></varname></term>
+ <term><option>--microcode=<replaceable>UCODE</replaceable></option></term>
+
+ <listitem><para>Path to initrd containing microcode updates. If not specified, the section
+ will not be present.</para>
+
+ <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>Cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></varname></term>
<term><option>--cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
@SELinuxRelabel=no
BuildSourcesEphemeral=yes
+KernelCommandLine=systemd.crash_shell
+ systemd.log_level=debug,console:info
+ systemd.log_ratelimit_kmsg=0
+ systemd.journald.forward_to_console
+ systemd.journald.max_level_console=warning
+ # Disable the kernel's ratelimiting on userspace logging to kmsg.
+ printk.devkmsg=on
+ # Make sure /sysroot is mounted rw in the initrd.
+ rw
+ # Lower the default device timeout so we get a shell earlier if the root device does
+ # not appear for some reason.
+ systemd.default_device_timeout_sec=20
+ # Make sure no LSMs are enabled by default.
+ apparmor=0
+ selinux=0
+ enforcing=0
+ systemd.early_core_pattern=/core
+ systemd.firstboot=no
+ systemd.setenv=SYSTEMD_ENABLE_LOG_CONTEXT=yes
+ SYSTEMD_ENABLE_LOG_CONTEXT=yes
+
[Host]
@Incremental=yes
@RuntimeSize=8G
@RuntimeBuildSources=yes
@QemuSmp=2
ToolsTreePackages=virtiofsd
-KernelCommandLineExtra=systemd.crash_shell
- systemd.log_level=debug,console:info
- systemd.log_ratelimit_kmsg=0
- systemd.journald.forward_to_console
- systemd.journald.max_level_console=warning
- # Disable the kernel's ratelimiting on userspace logging to kmsg.
- printk.devkmsg=on
- # Make sure /sysroot is mounted rw in the initrd.
- rw
- # Lower the default device timeout so we get a shell earlier if the root device does
- # not appear for some reason.
- systemd.default_device_timeout_sec=20
- # Make sure no LSMs are enabled by default.
- apparmor=0
- selinux=0
- enforcing=0
- systemd.early_core_pattern=/core
- systemd.firstboot=no
- systemd.setenv=SYSTEMD_ENABLE_LOG_CONTEXT=yes
- SYSTEMD_ENABLE_LOG_CONTEXT=yes
[Content]
VolatilePackages=
systemd
- systemd-ukify
- systemd-sysvcompat
+ systemd-libs
systemd-resolvconf
+ systemd-sysvcompat
systemd-tests
+ systemd-ukify
Packages=
bpf
git
gnutls
iproute
+ iputils
linux
man-db
openbsd-netcat
pacman
pkgconf
polkit
+ procps-ng
quota-tools
sbsigntools
shadow
InitrdVolatilePackages=
systemd
+ systemd-libs
systemd-sysvcompat
SRCDEST="/usr/src/debug/systemd-$VERSION-${RELEASE}${DIST}.$ARCH"
# TODO: Drop -U_FORTIFY_SOURCE when we switch to CentOS Stream 10.
-EXTRA_CFLAGS="-O0 -Wp,-U_FORTIFY_SOURCE"
+CFLAGS="$(rpm --define "_fortify_level 0" --undefine _lto_cflags --eval %build_cflags) -O0 -Wp,-U_FORTIFY_SOURCE"
if ((WITH_DEBUG)); then
- EXTRA_CFLAGS="$EXTRA_CFLAGS -ffile-prefix-map=../src=$SRCDEST"
+ CFLAGS="$CFLAGS -ffile-prefix-map=../src=$SRCDEST"
fi
IFS=
# TODO: Replace meson_build and meson_install overrides with "--undefine __meson_verbose" once
# https://github.com/mesonbuild/meson/pull/12835 is available.
# shellcheck disable=SC2046
-rpmbuild \
+ANNOBIN="no-active-checks" rpmbuild \
-bb \
--build-in-place \
--with upstream \
$( ((WITH_DEBUG)) || echo "debug_package %{nil}") \
--define "version_override $VERSION" \
--define "release_override $RELEASE" \
- --define "build_cflags $(rpm --eval %build_cflags) $EXTRA_CFLAGS" \
+ --define "build_cflags $CFLAGS" \
--define "meson_build %{shrink:%{__meson} compile -C %{_vpath_builddir} -j %{_smp_build_ncpus} %{nil}}" \
--define "meson_install %{shrink:DESTDIR=%{buildroot} %{__meson} install -C %{_vpath_builddir} --no-rebuild --quiet %{nil}}" \
--define "meson_extra_configure_options -D mode=developer -D b_sanitize=${SANITIZERS:-none}" \
--define "_find_debuginfo_dwz_opts %{nil}" \
--define "_fortify_level 0" \
--undefine _lto_cflags \
+ --undefine _package_note_flags \
--noclean \
"pkg/$ID/systemd.spec"
integritysetup
iproute
iproute-tc
+ iputils
kernel-core
libasan
libcap-ng-utils
p11-kit
pam
passwd
+ policycoreutils
polkit
procps-ng
quota
selinux-policy
selinux-policy-targeted
setools-console
- policycoreutils
util-linux
vim-common
--- /dev/null
+g root 0
+g bin 1
+g daemon 2
+g sys 3
+g adm 4
+g tty 5
+g disk 6
+g lp 7
+g mem 8
+g kmem 9
+g wheel 10
+g cdrom 11
+g mail 12
+g man 15
+g dialout 18
+g floppy 19
+g games 20
+g tape 33
+g video 39
+g ftp 50
+g lock 54
+g audio 63
+g users 100
+g nobody 65534
--- /dev/null
+u root 0:0 "Super User" /root /bin/bash
+u bin 1:1 "bin" /bin -
+u daemon 2:2 "daemon" /sbin -
+u adm 3:4 "adm" /var/adm -
+u lp 4:7 "lp" /var/spool/lpd -
+u sync 5:0 "sync" /sbin /bin/sync
+u shutdown 6:0 "shutdown" /sbin /sbin/shutdown
+u halt 7:0 "halt" /sbin /sbin/halt
+u mail 8:12 "mail" /var/spool/mail -
+u operator 11:0 "operator" /root -
+u games 12:100 "games" /usr/games -
+u ftp 14:50 "FTP User" /var/ftp -
+u nobody 65534:65534 "Kernel Overflow User" - -
fdisk
git-core
iproute2
+ iputils-ping
isc-dhcp-server
libcap-ng-utils
libtss2-rc0
git-core
glibc-locale-base
grep
+ group(bin)
+ group(daemon)
+ group(games)
+ group(nobody)
+ group(root)
gzip
+ iputils
kernel-kvmsmall
kmod
libasan8
openssh-server
pam
patterns-base-minimal_base
+ procps4
python3-pefile
quota
rpm-build
sed
shadow
timezone
+ user(bin)
+ user(daemon)
+ user(games)
+ user(nobody)
+ user(root)
vim
xz
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
+# Make sure serial console line wrapping is re-enabled as qemu's seabios firmware disables serial console
+# line-wrapping on boot.
+echo "tput smam || :" >>/etc/profile
+
if [ -n "$SANITIZERS" ]; then
LD_PRELOAD=$(ldd /usr/lib/systemd/systemd | grep libasan.so | awk '{print $3}')
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-03-04 10:09+0100\n"
-"PO-Revision-Date: 2024-01-21 10:36+0000\n"
+"PO-Revision-Date: 2024-04-20 07:36+0000\n"
"Last-Translator: Andika Triwidada <andika@gmail.com>\n"
"Language-Team: Indonesian <https://translate.fedoraproject.org/projects/"
-"systemd/master/id/>\n"
+"systemd/main/id/>\n"
"Language: id\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 5.3.1\n"
+"X-Generator: Weblate 5.4\n"
#: src/core/org.freedesktop.systemd1.policy.in:22
msgid "Send passphrase back to system"
#: src/home/org.freedesktop.home1.policy:73
msgid "Inhibit automatic lock of a home area"
-msgstr ""
+msgstr "Cegah penguncian otomatis dari area home"
#: src/home/org.freedesktop.home1.policy:74
-#, fuzzy
msgid ""
"Authentication is required to inhibit automatic lock of a user's home area."
-msgstr "Otentikasi diperlukan untuk memperbarui suatu area rumah pengguna."
+msgstr ""
+"Otentikasi diperlukan untuk mencegah penguncian otomatis dari area home "
+"pengguna."
#: src/home/org.freedesktop.home1.policy:83
-#, fuzzy
msgid "Activate a home area"
-msgstr "Menciptakan suatu area rumah"
+msgstr "Aktifkan suatu area home"
#: src/home/org.freedesktop.home1.policy:84
-#, fuzzy
msgid "Authentication is required to activate a user's home area."
-msgstr "Otentikasi diperlukan untuk mencipta suatu area rumah pengguna."
+msgstr "Otentikasi diperlukan untuk mengaktifkan suatu area home pengguna."
#: src/home/pam_systemd_home.c:293
#, c-format
msgstr "Otentikasi diperlukan untuk mendapatkan deskripsi sistem."
#: src/import/org.freedesktop.import1.policy:22
-#, fuzzy
msgid "Import a disk image"
-msgstr "Impor sebuah image kontainer atau VM"
+msgstr "Impor sebuah image disk"
#: src/import/org.freedesktop.import1.policy:23
-#, fuzzy
msgid "Authentication is required to import an image"
-msgstr "Otentikasi diperlukan untuk mengimpor suatu image kontainer atau VM"
+msgstr "Otentikasi diperlukan untuk mengimpor suatu image"
#: src/import/org.freedesktop.import1.policy:32
-#, fuzzy
msgid "Export a disk image"
-msgstr "Ekspor sebuah image kontainer atau VM"
+msgstr "Ekspor sebuah image disk"
#: src/import/org.freedesktop.import1.policy:33
-#, fuzzy
msgid "Authentication is required to export disk image"
-msgstr "Otentikasi diperlukan untuk mengekspor suatu image kontainer atau VM"
+msgstr "Otentikasi diperlukan untuk mengekspor suatu image disk"
#: src/import/org.freedesktop.import1.policy:42
-#, fuzzy
msgid "Download a disk image"
-msgstr "Unduh sebuah image kontainer atau VM"
+msgstr "Unduh sebuah image disk"
#: src/import/org.freedesktop.import1.policy:43
-#, fuzzy
msgid "Authentication is required to download a disk image"
-msgstr "Otentikasi diperlukan untuk mengunduh suatu image kontainer atau VM"
+msgstr "Otentikasi diperlukan untuk mengunduh suatu image disk"
#: src/import/org.freedesktop.import1.policy:52
msgid "Cancel transfer of a disk image"
-msgstr ""
+msgstr "Batalkan transfer suatu image disk"
#: src/import/org.freedesktop.import1.policy:53
-#, fuzzy
msgid ""
"Authentication is required to cancel the ongoing transfer of a disk image"
msgstr ""
-"Otentikasi diperlukan untuk mengubah kata sandi dari suatu area rumah "
-"pengguna."
+"Otentikasi diperlukan untuk membatalkan transfer suatu image disk yang "
+"sedang berjalan"
#: src/locale/org.freedesktop.locale1.policy:22
msgid "Set system locale"
return r;
}
-static bool stderr_is_journal(void) {
+bool stderr_is_journal(void) {
_cleanup_free_ char *w = NULL;
const char *e;
uint64_t dev, ino;
assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
+bool stderr_is_journal(void);
int log_open(void);
void log_close(void);
void log_forget_fds(void);
/* Returns a short identifier for the various VM implementations */
Virtualization detect_vm(void) {
static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID;
- bool other = false;
+ bool other = false, hyperv = false;
int xen_dom0 = 0;
Virtualization v, dmi;
v = detect_vm_cpuid();
if (v < 0)
return v;
- if (v == VIRTUALIZATION_VM_OTHER)
+ if (v == VIRTUALIZATION_MICROSOFT)
+ /* QEMU sets the CPUID string to hyperv's, in case it provides hyperv enlightenments. Let's
+ * hence not return Microsoft here but just use the other mechanisms first to make a better
+ * decision. */
+ hyperv = true;
+ else if (v == VIRTUALIZATION_VM_OTHER)
other = true;
else if (v != VIRTUALIZATION_NONE)
goto finish;
return v;
finish:
- if (v == VIRTUALIZATION_NONE && other)
- v = VIRTUALIZATION_VM_OTHER;
+ /* None of the checks above gave us a clear answer, hence let's now use fallback logic: if hyperv
+ * enlightenments are available but the VMM wasn't recognized as anything yet, it's probably
+ * Microsoft. */
+ if (v == VIRTUALIZATION_NONE) {
+ if (hyperv)
+ v = VIRTUALIZATION_MICROSOFT;
+ else if (other)
+ v = VIRTUALIZATION_VM_OTHER;
+ }
cached_found = v;
log_debug("Found VM virtualization %s", virtualization_to_string(v));
char *a;
assert(fname);
- assert(contents_size || contents_size == 0);
+ assert(contents || contents_size == 0);
assert(target_dir_prefix);
assert(inode_counter);
assert(cpio_buffer);
DECLARE_SBAT(SBAT_STUB_SECTION_TEXT);
-static EFI_STATUS combine_initrd(
- EFI_PHYSICAL_ADDRESS initrd_base, size_t initrd_size,
- const void * const extra_initrds[], const size_t extra_initrd_sizes[], size_t n_extra_initrds,
+/* Combine initrds by concatenation in memory */
+static EFI_STATUS combine_initrds(
+ const void * const initrds[], const size_t initrd_sizes[], size_t n_initrds,
Pages *ret_initr_pages, size_t *ret_initrd_size) {
- size_t n;
+ size_t n = 0;
assert(ret_initr_pages);
assert(ret_initrd_size);
- /* Combines four initrds into one, by simple concatenation in memory */
-
- n = ALIGN4(initrd_size); /* main initrd might not be padded yet */
-
- for (size_t i = 0; i < n_extra_initrds; i++) {
- if (!extra_initrds[i])
+ for (size_t i = 0; i < n_initrds; i++) {
+ if (!initrds[i])
continue;
- if (n > SIZE_MAX - extra_initrd_sizes[i])
+ /* some initrds (the ones from UKI sections) need padding,
+ * pad all to be safe */
+ size_t initrd_size = ALIGN4(initrd_sizes[i]);
+ if (n > SIZE_MAX - initrd_size)
return EFI_OUT_OF_RESOURCES;
- n += extra_initrd_sizes[i];
+ n += initrd_size;
}
_cleanup_pages_ Pages pages = xmalloc_pages(
EFI_SIZE_TO_PAGES(n),
UINT32_MAX /* Below 4G boundary. */);
uint8_t *p = PHYSICAL_ADDRESS_TO_POINTER(pages.addr);
- if (initrd_base != 0) {
+ for (size_t i = 0; i < n_initrds; i++) {
+ if (!initrds[i])
+ continue;
+
size_t pad;
- /* Order matters, the real initrd must come first, since it might include microcode updates
- * which the kernel only looks for in the first cpio archive */
- p = mempcpy(p, PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
+ p = mempcpy(p, initrds[i], initrd_sizes[i]);
- pad = ALIGN4(initrd_size) - initrd_size;
+ pad = ALIGN4(initrd_sizes[i]) - initrd_sizes[i];
if (pad > 0) {
memzero(p, pad);
p += pad;
}
}
- for (size_t i = 0; i < n_extra_initrds; i++) {
- if (!extra_initrds[i])
- continue;
-
- p = mempcpy(p, extra_initrds[i], extra_initrd_sizes[i]);
- }
-
assert(PHYSICAL_ADDRESS_TO_POINTER(pages.addr + n) == p);
*ret_initr_pages = pages;
void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL;
char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL;
_cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL;
- size_t linux_size, initrd_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0;
- EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base;
+ size_t linux_size, initrd_size, ucode_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0;
+ EFI_PHYSICAL_ADDRESS linux_base, initrd_base, ucode_base, dt_base;
_cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {};
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
CLEANUP_ARRAY(dt_filenames_addons_global, n_dts_addons_global, dt_filenames_free);
CLEANUP_ARRAY(dt_filenames_addons_uki, n_dts_addons_uki, dt_filenames_free);
+ if (szs[UNIFIED_SECTION_UNAME] > 0)
+ uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME],
+ szs[UNIFIED_SECTION_UNAME]);
+
/* Now that we have the UKI sections loaded, also load global first and then local (per-UKI)
* addons. The data is loaded at once, and then used later. */
err = load_addons(
/* Show splash screen as early as possible */
graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]);
- if (szs[UNIFIED_SECTION_UNAME] > 0)
- uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME],
- szs[UNIFIED_SECTION_UNAME]);
-
if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) {
/* Let's measure the passed kernel command line into the TPM. Note that this possibly
* duplicates what we already did in the boot menu, if that was already used. However, since
initrd_size = szs[UNIFIED_SECTION_INITRD];
initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0;
+ ucode_size = szs[UNIFIED_SECTION_UCODE];
+ ucode_base = ucode_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_UCODE] : 0;
+
_cleanup_pages_ Pages initrd_pages = {};
- if (credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
- /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */
- err = combine_initrd(
- initrd_base, initrd_size,
+ if (ucode_base || credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) {
+ /* If we have generated initrds dynamically or there is a microcode initrd, combine them with the built-in initrd. */
+ err = combine_initrds(
(const void*const[]) {
+ /* Microcode must always be first as kernel only scans uncompressed cpios
+ * and later initrds might be compressed. */
+ PHYSICAL_ADDRESS_TO_POINTER(ucode_base),
+ PHYSICAL_ADDRESS_TO_POINTER(initrd_base),
credential_initrd,
global_credential_initrd,
sysext_initrd,
pcrpkey_initrd,
},
(const size_t[]) {
+ ucode_size,
+ initrd_size,
credential_initrd_size,
global_credential_initrd_size,
sysext_initrd_size,
pcrsig_initrd_size,
pcrpkey_initrd_size,
},
- 6,
+ 8,
&initrd_pages, &initrd_size);
if (err != EFI_SUCCESS)
return err;
" --osrel=PATH Path to os-release file %7$s .osrel\n"
" --cmdline=PATH Path to file with kernel command line %7$s .cmdline\n"
" --initrd=PATH Path to initrd image file %7$s .initrd\n"
+ " --ucode=PATH Path to microcode image file %7$s .ucode\n"
" --splash=PATH Path to splash bitmap file %7$s .splash\n"
" --dtb=PATH Path to Devicetree file %7$s .dtb\n"
" --uname=PATH Path to 'uname -r' file %7$s .uname\n"
ARG_OSREL,
ARG_CMDLINE,
ARG_INITRD,
+ ARG_UCODE,
ARG_SPLASH,
ARG_DTB,
ARG_UNAME,
{ "osrel", required_argument, NULL, ARG_OSREL },
{ "cmdline", required_argument, NULL, ARG_CMDLINE },
{ "initrd", required_argument, NULL, ARG_INITRD },
+ { "ucode", required_argument, NULL, ARG_UCODE },
{ "splash", required_argument, NULL, ARG_SPLASH },
{ "dtb", required_argument, NULL, ARG_DTB },
{ "uname", required_argument, NULL, ARG_UNAME },
[UNIFIED_SECTION_OSREL] = ".osrel",
[UNIFIED_SECTION_CMDLINE] = ".cmdline",
[UNIFIED_SECTION_INITRD] = ".initrd",
+ [UNIFIED_SECTION_UCODE] = ".ucode",
[UNIFIED_SECTION_SPLASH] = ".splash",
[UNIFIED_SECTION_DTB] = ".dtb",
[UNIFIED_SECTION_UNAME] = ".uname",
UNIFIED_SECTION_OSREL,
UNIFIED_SECTION_CMDLINE,
UNIFIED_SECTION_INITRD,
+ UNIFIED_SECTION_UCODE,
UNIFIED_SECTION_SPLASH,
UNIFIED_SECTION_DTB,
UNIFIED_SECTION_UNAME,
/* Pref64 option type (RFC8781, section 4) */
#define RADV_OPT_PREF64 38
-enum RAdvState {
+typedef enum RAdvState {
RADV_STATE_IDLE = 0,
RADV_STATE_ADVERTISING = 1,
-};
-typedef enum RAdvState RAdvState;
+} RAdvState;
struct sd_radv_opt_dns {
uint8_t type;
struct ether_addr mac_addr;
uint8_t hop_limit;
uint8_t flags;
+ uint8_t preference;
uint32_t mtu;
usec_t retransmit_usec;
usec_t lifetime_usec; /* timespan */
/* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
* simultaneously in the structured initializer in the above. */
adv.nd_ra_curhoplimit = ra->hop_limit;
- adv.nd_ra_flags_reserved = ra->flags;
+ /* RFC 4191, Section 2.2,
+ * "...If the Router Lifetime is zero, the preference value MUST be set to (00) by the sender..." */
+ adv.nd_ra_flags_reserved = ra->flags | (ra->lifetime_usec > 0 ? (ra->preference << 3) : 0);
iov[msg.msg_iovlen++] = IOVEC_MAKE(&adv, sizeof(adv));
/* MAC address is optional, either because the link does not use L2 addresses or load sharing is
if (r < 0)
return r;
- struct in6_addr src = {};
+ struct in6_addr src;
r = sd_ndisc_router_solicit_get_sender_address(rs, &src);
- if (r < 0 && r != -ENODATA) /* null address is allowed */
+ if (r == -ENODATA) /* null address is allowed */
+ return sd_radv_send(ra); /* When an unsolicited RA, we need to also update timer. */
+ if (r < 0)
return log_radv_errno(ra, r, "Failed to get sender address of RS, ignoring: %m");
- if (r >= 0 && in6_addr_equal(&src, &ra->ipv6ll))
+ if (in6_addr_equal(&src, &ra->ipv6ll))
/* This should be definitely caused by a misconfiguration. If we send RA to ourself, the
* kernel complains about that. Let's ignore the packet. */
return log_radv_errno(ra, SYNTHETIC_ERRNO(EADDRINUSE), "Received RS from the same interface, ignoring.");
}
static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
- usec_t min_timeout, max_timeout, time_now, timeout;
sd_radv *ra = ASSERT_PTR(userdata);
+
+ if (sd_radv_send(ra) < 0)
+ (void) sd_radv_stop(ra);
+
+ return 0;
+}
+
+int sd_radv_send(sd_radv *ra) {
+ usec_t min_timeout, max_timeout, time_now, timeout;
int r;
- assert(s);
- assert(ra->event);
+ assert_return(ra, -EINVAL);
+ assert_return(ra->event, -EINVAL);
+ assert_return(sd_radv_is_running(ra), -EINVAL);
assert(router_lifetime_is_valid(ra->lifetime_usec));
r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
if (r < 0)
- goto fail;
+ return r;
r = radv_send_router(ra, NULL);
if (r < 0)
- log_radv_errno(ra, r, "Unable to send Router Advertisement, ignoring: %m");
+ return log_radv_errno(ra, r, "Unable to send Router Advertisement: %m");
+
+ ra->ra_sent++;
/* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
- if (ra->ra_sent < RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
+ if (ra->ra_sent <= RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
max_timeout = RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
else
max_timeout = RADV_DEFAULT_MAX_TIMEOUT_USEC;
assert(min_timeout <= max_timeout * 3 / 4);
timeout = min_timeout + random_u64_range(max_timeout - min_timeout);
- log_radv(ra, "Next Router Advertisement in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
-
- r = event_reset_time(ra->event, &ra->timeout_event_source,
- CLOCK_BOOTTIME,
- usec_add(time_now, timeout), MSEC_PER_SEC,
- radv_timeout, ra,
- ra->event_priority, "radv-timeout", true);
- if (r < 0)
- goto fail;
-
- ra->ra_sent++;
-
- return 0;
-
-fail:
- sd_radv_stop(ra);
-
- return 0;
+ log_radv(ra, "Sent unsolicited Router Advertisement. Next advertisement will be in %s.",
+ FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
+
+ return event_reset_time(
+ ra->event, &ra->timeout_event_source,
+ CLOCK_BOOTTIME,
+ usec_add(time_now, timeout), MSEC_PER_SEC,
+ radv_timeout, ra,
+ ra->event_priority, "radv-timeout", true);
}
int sd_radv_stop(sd_radv *ra) {
int r;
- if (!ra)
- return 0;
-
- if (ra->state == RADV_STATE_IDLE)
- return 0;
+ if (!sd_radv_is_running(ra))
+ return 0; /* Already stopped. */
log_radv(ra, "Stopping IPv6 Router Advertisement daemon");
assert_return(ra->event, -EINVAL);
assert_return(ra->ifindex > 0, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return 0;
+ if (sd_radv_is_running(ra))
+ return 0; /* Already started. */
r = radv_setup_recv_event(ra);
if (r < 0)
int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
assert_return(ra, -EINVAL);
+ assert_return(!sd_radv_is_running(ra), -EBUSY);
assert_return(ifindex > 0, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
ra->ifindex = ifindex;
-
return 0;
}
int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
if (mac_addr)
ra->mac_addr = *mac_addr;
else
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
ra->hop_limit = hop_limit;
-
return 0;
}
int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
- if (usec > RADV_MAX_RETRANSMIT_USEC)
- return -EINVAL;
-
ra->retransmit_usec = usec;
return 0;
}
int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
if (!router_lifetime_is_valid(usec))
return -EINVAL;
- /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
- * to (00) by the sender..." */
- if (usec == 0 &&
- (ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3))
- return -EINVAL;
-
ra->lifetime_usec = usec;
return 0;
}
-int sd_radv_set_managed_information(sd_radv *ra, int managed) {
+int sd_radv_set_managed_information(sd_radv *ra, int b) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
- SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed);
-
+ SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, b);
return 0;
}
-int sd_radv_set_other_information(sd_radv *ra, int other) {
+int sd_radv_set_other_information(sd_radv *ra, int b) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
- SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other);
-
+ SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, b);
return 0;
}
-int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
+int sd_radv_set_preference(sd_radv *ra, uint8_t preference) {
assert_return(ra, -EINVAL);
assert_return(IN_SET(preference,
SD_NDISC_PREFERENCE_LOW,
SD_NDISC_PREFERENCE_MEDIUM,
SD_NDISC_PREFERENCE_HIGH), -EINVAL);
- /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
- * to (00) by the sender..." */
- if (ra->lifetime_usec == 0 && preference != SD_NDISC_PREFERENCE_MEDIUM)
- return -EINVAL;
-
- ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3);
-
+ ra->preference = preference;
return 0;
}
int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
SET_FLAG(ra->flags, ND_RA_FLAG_HOME_AGENT, home_agent);
-
return 0;
}
int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
ra->home_agent.nd_opt_home_agent_info_preference = htobe16(preference);
-
return 0;
}
int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
if (lifetime_usec > RADV_HOME_AGENT_MAX_LIFETIME_USEC)
return -EINVAL;
log_radv(ra, "Added prefix %s", addr_p);
}
- if (ra->state == RADV_STATE_IDLE)
+ if (!sd_radv_is_running(ra))
return 0;
if (ra->ra_sent == 0)
log_radv(ra, "Added route prefix %s", strna(addr_p));
}
- if (ra->state == RADV_STATE_IDLE)
+ if (!sd_radv_is_running(ra))
return 0;
if (ra->ra_sent == 0)
log_radv(ra, "Added PREF64 prefix %s", strna(addr_p));
}
- if (ra->state == RADV_STATE_IDLE)
+ if (!sd_radv_is_running(ra))
return 0;
if (ra->ra_sent == 0)
assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0);
ASSERT_RETURN_EXPECTED_SE(sd_radv_set_preference(ra, ~0) < 0);
- assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0);
- assert_se(sd_radv_set_router_lifetime(ra, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_set_router_lifetime(ra, 0) < 0);
- assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_MEDIUM) >= 0);
- assert_se(sd_radv_set_router_lifetime(ra, 0) >= 0);
-
ASSERT_RETURN_EXPECTED_SE(sd_radv_set_managed_information(NULL, true) < 0);
assert_se(sd_radv_set_managed_information(ra, true) >= 0);
assert_se(sd_radv_set_managed_information(ra, false) >= 0);
ASSERT_RETURN_EXPECTED_SE(sd_radv_set_retransmit(NULL, 10 * USEC_PER_MSEC) < 0);
assert_se(sd_radv_set_retransmit(ra, 10 * USEC_PER_MSEC) >= 0);
assert_se(sd_radv_set_retransmit(ra, 0) >= 0);
- assert_se(sd_radv_set_retransmit(ra, usec_add(UINT32_MAX * USEC_PER_MSEC, USEC_PER_MSEC)) < 0);
+ assert_se(sd_radv_set_retransmit(ra, USEC_INFINITY) >= 0);
ASSERT_RETURN_EXPECTED_SE(sd_radv_set_rdnss(NULL, 0, NULL, 0) < 0);
assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0);
global:
sd_bus_creds_get_pidfd_dup;
sd_bus_creds_new_from_pidfd;
+ sd_id128_get_invocation_app_specific;
sd_journal_stream_fd_with_namespace;
+ sd_event_source_get_inotify_path;
} LIBSYSTEMD_255;
* iteration. */
int fd;
+ /* The path that the fd points to. The field is optional. */
+ char *path;
+
/* The inotify "watch descriptor" */
int wd;
assert_se(hashmap_remove(d->inotify_data->inodes, d) == d);
}
+ free(d->path);
free(d);
}
wd = inotify_add_watch_fd(d->inotify_data->fd, d->fd, combined_mask);
if (wd < 0)
- return -errno;
+ return wd;
if (d->wd < 0) {
r = hashmap_put(d->inotify_data->wd, INT_TO_PTR(wd), d);
}
LIST_PREPEND(to_close, e->inode_data_to_close_list, inode_data);
+
+ _cleanup_free_ char *path = NULL;
+ r = fd_get_path(inode_data->fd, &path);
+ if (r < 0 && r != -ENOSYS) { /* The path is optional, hence ignore -ENOSYS. */
+ event_gc_inode_data(e, inode_data);
+ return r;
+ }
+
+ free_and_replace(inode_data->path, path);
}
/* Link our event source to the inode data object */
}
LIST_PREPEND(to_close, s->event->inode_data_to_close_list, new_inode_data);
+
+ _cleanup_free_ char *path = NULL;
+ r = fd_get_path(new_inode_data->fd, &path);
+ if (r < 0 && r != -ENOSYS)
+ goto fail;
+
+ free_and_replace(new_inode_data->path, path);
}
/* Move the event source to the new inode data structure */
return 0;
}
-_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask) {
+_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret) {
assert_return(s, -EINVAL);
- assert_return(mask, -EINVAL);
+ assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_INOTIFY, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
- *mask = s->inotify.mask;
+ *ret = s->inotify.mask;
+ return 0;
+}
+
+_public_ int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret) {
+ assert_return(s, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(s->type == SOURCE_INOTIFY, -EDOM);
+ assert_return(!event_origin_changed(s->event), -ECHILD);
+
+ if (!s->inotify.inode_data)
+ return -ESTALE; /* already disconnected. */
+
+ if (!s->inotify.inode_data->path)
+ return -ENOSYS; /* /proc was not mounted? */
+
+ *ret = s->inotify.inode_data->path;
return 0;
}
unsigned create_called[CREATE_EVENTS_MAX];
unsigned create_overflow;
unsigned n_create_events;
+ const char *path;
};
static void maybe_exit(sd_event_source *s, struct inotify_context *c) {
}
static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
- struct inotify_context *c = userdata;
- const char *description;
+ struct inotify_context *c = ASSERT_PTR(userdata);
+ const char *path, *description;
unsigned bit, n;
+ assert_se(sd_event_source_get_inotify_path(s, &path) >= 0);
+
assert_se(sd_event_source_get_description(s, &description) >= 0);
assert_se(safe_atou(description, &n) >= 0);
bit = 1U << n;
if (ev->mask & IN_Q_OVERFLOW) {
- log_info("inotify-handler <%s>: overflow", description);
+ log_info("inotify-handler for %s <%s>: overflow", path, description);
c->create_overflow |= bit;
} else if (ev->mask & IN_CREATE) {
+ assert_se(path_equal_or_inode_same(path, c->path, 0));
if (streq(ev->name, "sub"))
- log_debug("inotify-handler <%s>: create on %s", description, ev->name);
+ log_debug("inotify-handler for %s <%s>: create on %s", path, description, ev->name);
else {
unsigned i;
c->create_called[i] |= bit;
}
} else if (ev->mask & IN_DELETE) {
- log_info("inotify-handler <%s>: delete of %s", description, ev->name);
+ log_info("inotify-handler for %s <%s>: delete of %s", path, description, ev->name);
assert_se(streq(ev->name, "sub"));
} else
assert_not_reached();
}
static int delete_self_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
- struct inotify_context *c = userdata;
+ struct inotify_context *c = ASSERT_PTR(userdata);
+ const char *path;
+
+ assert_se(sd_event_source_get_inotify_path(s, &path) >= 0);
if (ev->mask & IN_Q_OVERFLOW) {
- log_info("delete-self-handler: overflow");
+ log_info("delete-self-handler for %s: overflow", path);
c->delete_self_handler_called = true;
} else if (ev->mask & IN_DELETE_SELF) {
- log_info("delete-self-handler: delete-self");
+ log_info("delete-self-handler for %s: delete-self", path);
c->delete_self_handler_called = true;
} else if (ev->mask & IN_IGNORED) {
- log_info("delete-self-handler: ignore");
+ log_info("delete-self-handler for %s: ignore", path);
} else
assert_not_reached();
.n_create_events = n_create_events,
};
sd_event *e = NULL;
- const char *q;
+ const char *q, *pp;
unsigned i;
log_info("/* %s(%u) */", __func__, n_create_events);
assert_se(sd_event_default(&e) >= 0);
assert_se(mkdtemp_malloc("/tmp/test-inotify-XXXXXX", &p) >= 0);
+ context.path = p;
assert_se(sd_event_add_inotify(e, &a, p, IN_CREATE|IN_ONLYDIR, inotify_handler, &context) >= 0);
assert_se(sd_event_add_inotify(e, &b, p, IN_CREATE|IN_DELETE|IN_DONT_FOLLOW, inotify_handler, &context) >= 0);
assert_se(sd_event_source_set_description(b, "1") >= 0);
assert_se(sd_event_source_set_description(c, "2") >= 0);
+ assert_se(sd_event_source_get_inotify_path(a, &pp) >= 0);
+ assert_se(path_equal_or_inode_same(pp, p, 0));
+ assert_se(sd_event_source_get_inotify_path(b, &pp) >= 0);
+ assert_se(path_equal_or_inode_same(pp, p, 0));
+ assert_se(sd_event_source_get_inotify_path(b, &pp) >= 0);
+ assert_se(path_equal_or_inode_same(pp, p, 0));
+
q = strjoina(p, "/sub");
assert_se(touch(q) >= 0);
assert_se(sd_event_add_inotify(e, &d, q, IN_DELETE_SELF, delete_self_handler, &context) >= 0);
return sd_id128_get_app_specific(id, app_id, ret);
}
+
+_public_ int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+ sd_id128_t id;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_id128_get_invocation(&id);
+ if (r < 0)
+ return r;
+
+ return sd_id128_get_app_specific(id, app_id, ret);
+}
int r;
if (le64toh(o->data.entry_offset) == 0)
- warning(offset, "Unused data (entry_offset==0)");
+ debug(offset, "Unused data (entry_offset==0)");
if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
error(offset, "Bad n_entries: %"PRIu64, le64toh(o->data.n_entries));
m->wd = inotify_add_watch_fd(j->inotify_fd, fd, mask);
if (m->wd < 0) {
- log_debug_errno(errno, "Failed to watch journal directory '%s', ignoring: %m", m->path);
+ log_debug_errno(m->wd, "Failed to watch journal directory '%s', ignoring: %m", m->path);
return;
}
}
static void append_number(JournalFile *f, int n, const sd_id128_t *boot_id, uint64_t *seqnum, uint64_t *ret_offset) {
- _cleanup_free_ char *p = NULL, *q = NULL;
+ _cleanup_free_ char *p = NULL, *q = NULL, *s = NULL;
dual_timestamp ts;
- struct iovec iovec[2];
+ struct iovec iovec[3];
size_t n_iov = 0;
dual_timestamp_now(&ts);
assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
iovec[n_iov++] = IOVEC_MAKE_STRING(p);
+ assert_se(s = strjoin("LESS_THAN_FIVE=%d", yes_no(n < 5)));
+ iovec[n_iov++] = IOVEC_MAKE_STRING(s);
+
if (boot_id) {
assert_se(q = strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id)));
iovec[n_iov++] = IOVEC_MAKE_STRING(q);
(void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
}
+static void test_cursor(sd_journal *j) {
+ _cleanup_strv_free_ char **cursors = NULL;
+ int r;
+
+ assert_se(sd_journal_seek_head(j) >= 0);
+
+ for (;;) {
+ r = sd_journal_next(j);
+ assert_se(r >= 0);
+ if (r == 0)
+ break;
+
+ _cleanup_free_ char *cursor = NULL;
+ assert_se(sd_journal_get_cursor(j, &cursor) >= 0);
+ assert_se(sd_journal_test_cursor(j, cursor) > 0);
+ assert_se(strv_consume(&cursors, TAKE_PTR(cursor)) >= 0);
+ }
+
+ STRV_FOREACH(c, cursors) {
+ assert_se(sd_journal_seek_cursor(j, *c) >= 0);
+ assert_se(sd_journal_next(j) >= 0);
+ assert_se(sd_journal_test_cursor(j, *c) > 0);
+ }
+
+ assert_se(sd_journal_seek_head(j) >= 0);
+ STRV_FOREACH(c, cursors) {
+ assert_se(sd_journal_next(j) >= 0);
+ assert_se(sd_journal_test_cursor(j, *c) > 0);
+ }
+}
+
static void test_skip_one(void (*setup)(void)) {
char t[] = "/var/tmp/journal-skip-XXXXXX";
sd_journal *j;
test_check_numbers_up(j, 9);
sd_journal_close(j);
+ /* For issue #31516. */
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=yes", SIZE_MAX) >= 0);
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=no", SIZE_MAX) >= 0);
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=hoge", SIZE_MAX) >= 0);
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=3", SIZE_MAX) >= 0);
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=3", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=4", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=5", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=6", SIZE_MAX) >= 0);
+ test_cursor(j);
+
test_done(t);
}
case NETWORK_CONFIG_SOURCE_NDISC:
n = link->network->ndisc_use_domains;
c = _USE_DOMAINS_INVALID;
- m = _USE_DOMAINS_INVALID;
+ m = link->manager->ndisc_use_domains;
break;
default:
assert_not_reached();
if (c >= 0)
return c;
+ /* If per-network but protocol-independent setting is specified, use it. */
+ if (link->network->use_domains >= 0)
+ return link->network->use_domains;
+
/* If global per-protocol setting is specified, use it. */
if (m >= 0)
return m;
- /* Otherwise, defaults to no. */
- return USE_DOMAINS_NO;
+ /* If none of them are specified, use the global protocol-independent value. */
+ return link->manager->use_domains;
}
bool link_get_use_dns(Link *link, NetworkConfigSource proto) {
Network.IPv4Forwarding, config_parse_tristate, 0, offsetof(Manager, ip_forwarding[0])
Network.IPv6Forwarding, config_parse_tristate, 0, offsetof(Manager, ip_forwarding[1])
Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 0, offsetof(Manager, ipv6_privacy_extensions)
+Network.UseDomains, config_parse_use_domains, 0, offsetof(Manager, use_domains)
+IPv6AcceptRA.UseDomains, config_parse_use_domains, 0, offsetof(Manager, ndisc_use_domains)
DHCPv4.UseDomains, config_parse_use_domains, 0, offsetof(Manager, dhcp_use_domains)
DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Manager, dhcp_duid)
DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, dhcp_duid)
.persistent_storage_fd = persistent_storage_open(),
.dhcp_use_domains = _USE_DOMAINS_INVALID,
.dhcp6_use_domains = _USE_DOMAINS_INVALID,
+ .ndisc_use_domains = _USE_DOMAINS_INVALID,
.dhcp_duid.type = DUID_TYPE_EN,
.dhcp6_duid.type = DUID_TYPE_EN,
.duid_product_uuid.type = DUID_TYPE_UUID,
OrderedSet *address_pools;
Set *dhcp_pd_subnet_ids;
+ UseDomains use_domains; /* default for all protocols */
UseDomains dhcp_use_domains;
UseDomains dhcp6_use_domains;
+ UseDomains ndisc_use_domains;
DUID dhcp_duid;
DUID dhcp6_duid;
Network.Address, config_parse_address, 0, 0
Network.Gateway, config_parse_gateway, 0, 0
Network.Domains, config_parse_domains, 0, 0
+Network.UseDomains, config_parse_use_domains, 0, offsetof(Network, use_domains)
Network.DNS, config_parse_dns, 0, 0
Network.DNSDefaultRoute, config_parse_tristate, 0, offsetof(Network, dns_default_route)
Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
.keep_configuration = manager->keep_configuration,
+ .use_domains = _USE_DOMAINS_INVALID,
+
.compat_dhcp_use_domains = _USE_DOMAINS_INVALID,
.compat_dhcp_use_dns = -1,
.compat_dhcp_use_ntp = -1,
.ndisc_use_hop_limit = true,
.ndisc_use_reachable_time = true,
.ndisc_use_retransmission_time = true,
+ .ndisc_use_domains = _USE_DOMAINS_INVALID,
.ndisc_route_table = RT_TABLE_MAIN,
.ndisc_route_metric_high = IPV6RA_ROUTE_METRIC_HIGH,
.ndisc_route_metric_medium = IPV6RA_ROUTE_METRIC_MEDIUM,
bool default_route_on_device;
AddressFamily ip_masquerade;
+ /* Protocol independent settings */
+ UseDomains use_domains;
+
/* For backward compatibility, only applied to DHCPv4 and DHCPv6. */
UseDomains compat_dhcp_use_domains;
int compat_dhcp_use_dns;
}
int radv_update_mac(Link *link) {
- bool restart;
- int r;
-
assert(link);
if (!link->radv)
if (link->hw_addr.length != ETH_ALEN)
return 0;
- restart = sd_radv_is_running(link->radv);
-
- r = sd_radv_stop(link->radv);
- if (r < 0)
- return r;
-
- r = sd_radv_set_mac(link->radv, &link->hw_addr.ether);
- if (r < 0)
- return r;
-
- if (restart) {
- r = sd_radv_start(link->radv);
- if (r < 0)
- return r;
- }
-
- return 0;
+ return sd_radv_set_mac(link->radv, &link->hw_addr.ether);
}
static int radv_is_ready_to_configure(Link *link) {
#ManageForeignNextHops=yes
#RouteTable=
#IPv6PrivacyExtensions=no
+#UseDomains=no
+
+[IPv6AcceptRA]
+#UseDomains=
[DHCPv4]
#DUIDType=vendor
#DUIDRawData=
-#UseDomains=no
+#UseDomains=
[DHCPv6]
#DUIDType=vendor
#DUIDRawData=
-#UseDomains=no
+#UseDomains=
[DHCPServer]
#PersistLeases=yes
assert_se(a = tpm2_hash_alg_to_string(*pa));
assert_se(md = EVP_get_digestbyname(a));
hash_ssize = EVP_MD_size(md);
- assert_se(hash_ssize > 0);
+ assert(hash_ssize > 0);
hash_usize = hash_ssize;
hash = malloc(hash_usize);
return 0;
}
+static void evp_md_ctx_free_all(EVP_MD_CTX *(*md)[TPM2_N_HASH_ALGORITHMS]) {
+ assert(md);
+ FOREACH_ARRAY(alg, *md, TPM2_N_HASH_ALGORITHMS)
+ if (*alg)
+ EVP_MD_CTX_free(*alg);
+}
+
+static int make_pcrlock_record_from_stream(
+ uint32_t pcr_mask,
+ FILE *f,
+ JsonVariant **ret_records) {
+
+ _cleanup_(json_variant_unrefp) JsonVariant *digests = NULL;
+ _cleanup_(evp_md_ctx_free_all) EVP_MD_CTX *mdctx[TPM2_N_HASH_ALGORITHMS] = {};
+ int r;
+
+ assert(f);
+ assert(ret_records);
+
+ for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) {
+ const char *a;
+ const EVP_MD *md;
+
+ assert_se(a = tpm2_hash_alg_to_string(tpm2_hash_algorithms[i]));
+ assert_se(md = EVP_get_digestbyname(a));
+
+ mdctx[i] = EVP_MD_CTX_new();
+ if (!mdctx[i])
+ return log_oom();
+
+ if (EVP_DigestInit_ex(mdctx[i], md, NULL) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to initialize message digest for %s.", a);
+ }
+
+ for (;;) {
+ uint8_t buffer[64*1024];
+ size_t n;
+
+ n = fread(buffer, 1, sizeof(buffer), f);
+ if (ferror(f))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to read file: %m");
+ if (n == 0 && feof(f))
+ break;
+
+ for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++)
+ if (EVP_DigestUpdate(mdctx[i], buffer, n) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to hash data.");
+ }
+
+ for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) {
+ const char *a;
+ int hash_ssize;
+ unsigned hash_usize;
+
+ assert_se(a = tpm2_hash_alg_to_string(tpm2_hash_algorithms[i]));
+ hash_ssize = EVP_MD_CTX_size(mdctx[i]);
+ assert(hash_ssize > 0 && hash_ssize <= EVP_MAX_MD_SIZE);
+ hash_usize = hash_ssize;
+ unsigned char hash[hash_usize];
+
+ if (EVP_DigestFinal_ex(mdctx[i], hash, &hash_usize) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to finalize hash context for algorithn '%s'.", a);
+
+ r = json_variant_append_arrayb(
+ &digests,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("hashAlg", JSON_BUILD_STRING(a)),
+ JSON_BUILD_PAIR("digest", JSON_BUILD_HEX(hash, hash_usize))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to build JSON digest object: %m");
+ }
+
+ for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) {
+ _cleanup_(json_variant_unrefp) JsonVariant *record = NULL;
+
+ if (!FLAGS_SET(pcr_mask, UINT32_C(1) << i))
+ continue;
+
+ r = json_build(&record,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR("pcr", JSON_BUILD_UNSIGNED(i)),
+ JSON_BUILD_PAIR("digests", JSON_BUILD_VARIANT(digests))));
+ if (r < 0)
+ return log_error_errno(r, "Failed to build record object: %m");
+
+ r = json_variant_append_array(ret_records, record);
+ if (r < 0)
+ return log_error_errno(r, "Failed to append to JSON array: %m");
+ }
+
+ return 0;
+}
+
static const char *pcrlock_path(const char *default_pcrlock_path) {
return arg_pcrlock_path ?: arg_pcrlock_auto ? default_pcrlock_path : NULL;
}
}
static int verb_lock_raw(int argc, char *argv[], void *userdata) {
- _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
- _cleanup_free_ char *data = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *records = NULL;
_cleanup_fclose_ FILE *f = NULL;
- size_t size;
int r;
if (arg_pcr_mask == 0)
return log_error_errno(errno, "Failed to open '%s': %m", argv[1]);
}
- r = read_full_stream(f ?: stdin, &data, &size);
+ r = make_pcrlock_record_from_stream(arg_pcr_mask, f ?: stdin, &records);
if (r < 0)
- return log_error_errno(r, "Failed to read data from stdin: %m");
-
- for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) {
- _cleanup_(json_variant_unrefp) JsonVariant *record = NULL;
-
- if (!FLAGS_SET(arg_pcr_mask, UINT32_C(1) << i))
- continue;
-
- r = make_pcrlock_record(i, data, size, &record);
- if (r < 0)
- return r;
-
- r = json_variant_append_array(&array, record);
- if (r < 0)
- return log_error_errno(r, "Failed to append to JSON array: %m");
- }
+ return r;
- return write_pcrlock(array, NULL);
+ return write_pcrlock(records, NULL);
}
static int verb_unlock_simple(int argc, char *argv[], void *userdata) {
}
static int verb_lock_kernel_initrd(int argc, char *argv[], void *userdata) {
- _cleanup_(json_variant_unrefp) JsonVariant *record = NULL, *array = NULL;
- _cleanup_free_ void *data = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *records = NULL;
_cleanup_fclose_ FILE *f = NULL;
- size_t size;
+ uint32_t pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_INITRD;
int r;
if (argc >= 2) {
return log_error_errno(errno, "Failed to open '%s': %m", argv[1]);
}
- r = read_full_stream(f ?: stdin, (char**) &data, &size);
- if (r < 0)
- return log_error_errno(r, "Failed to read data from stdin: %m");
-
- r = make_pcrlock_record(TPM2_PCR_KERNEL_INITRD /* = 9 */, data, size, &record);
+ r = make_pcrlock_record_from_stream(pcr_mask, f ?: stdin, &records);
if (r < 0)
return r;
- r = json_variant_new_array(&array, &record, 1);
- if (r < 0)
- return log_error_errno(r, "Failed to create record array: %m");
-
- r = write_pcrlock(array, PCRLOCK_KERNEL_INITRD_PATH);
+ r = write_pcrlock(records, PCRLOCK_KERNEL_INITRD_PATH);
if (r < 0)
return r;
#include "strv.h"
#include "tmpfile-util.h"
#include "user-util.h"
+#include "vpick.h"
/* Markers used in the first line of our 20-portable.conf unit file drop-in to determine, that a) the unit file was
* dropped there by the portable service logic and b) for which image it was dropped there. */
_cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL, *confext_level = NULL;
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL;
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
_cleanup_strv_free_ char **valid_prefixes = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
assert(name_or_path);
- r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
+ /* If we get a path, then check if it can be resolved with vpick. We need this as we might just
+ * get a simple image name, which would make vpick error out. */
+ if (path_is_absolute(name_or_path)) {
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ name_or_path,
+ &pick_filter_image_any,
+ PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+ &result);
+ if (r < 0)
+ return r;
+ if (!result.path)
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(ENOENT),
+ "No matching entry in .v/ directory %s found.",
+ name_or_path);
+
+ name_or_path = result.path;
+ }
+
+ r = image_find_harder(IMAGE_PORTABLE, name_or_path, /* root= */ NULL, &image);
if (r < 0)
return r;
}
STRV_FOREACH(p, extension_image_paths) {
+ _cleanup_(pick_result_done) PickResult ext_result = PICK_RESULT_NULL;
_cleanup_(image_unrefp) Image *new = NULL;
+ const char *path = *p;
+
+ if (path_is_absolute(*p)) {
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ *p,
+ &pick_filter_image_any,
+ PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+ &ext_result);
+ if (r < 0)
+ return r;
+ if (!ext_result.path)
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(ENOENT),
+ "No matching entry in .v/ directory %s found.",
+ *p);
+
+ path = ext_result.path;
+ }
- r = image_find_harder(IMAGE_PORTABLE, *p, NULL, &new);
+ r = image_find_harder(IMAGE_PORTABLE, path, NULL, &new);
if (r < 0)
return r;
while (!isempty(marker))
STRV_FOREACH(image_name_or_path, root_and_extensions) {
_cleanup_free_ char *image = NULL, *base_image = NULL, *base_image_name_or_path = NULL;
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
if (r < 0)
return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", image);
- r = path_extract_image_name(*image_name_or_path, &base_image_name_or_path);
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ *image_name_or_path,
+ &pick_filter_image_any,
+ PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
+ &result);
+ if (r < 0)
+ return r;
+ if (!result.path)
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(ENOENT),
+ "No matching entry in .v/ directory %s found.",
+ *image_name_or_path);
+
+ r = path_extract_image_name(result.path, &base_image_name_or_path);
if (r < 0)
- return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", *image_name_or_path);
+ return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", result.path);
if (!streq(base_image, base_image_name_or_path)) {
if (match_all)
assert(manager);
- ERR_load_crypto_strings();
- SSL_load_error_strings();
-
manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
if (!manager->dnstls_data.ctx)
return -ENOMEM;
if (fd < 0)
return -errno;
- /* This leaves the timezone fields of struct tm
- * uninitialized! */
+ /* This leaves the timezone fields of struct tm uninitialized! */
if (ioctl(fd, RTC_RD_TIME, tm) < 0)
- return -errno;
+ /* Some drivers return -EINVAL in case the time could not be kept, i.e. power loss
+ * happened. Let's turn that into a clearly recognizable error */
+ return errno == EINVAL ? -ENODATA : -errno;
/* We don't know daylight saving, so we reset this in order not
* to confuse mktime(). */
.type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
.basename = name,
.architecture = _ARCHITECTURE_INVALID,
- .suffix = suffix,
+ .suffix = STRV_MAKE(suffix),
};
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
.type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
.basename = pretty,
.architecture = _ARCHITECTURE_INVALID,
- .suffix = suffix,
+ .suffix = STRV_MAKE(suffix),
};
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
#include "string-util.h"
#if HAVE_OPENSSL
+# include <openssl/rsa.h>
+# include <openssl/ec.h>
+
+# if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+# include <openssl/engine.h>
+DISABLE_WARNING_DEPRECATED_DECLARATIONS;
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ENGINE*, ENGINE_free, NULL);
+REENABLE_WARNING;
+# endif
+
/* 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). */
assert(private_key_uri);
assert(ret);
+#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
DISABLE_WARNING_DEPRECATED_DECLARATIONS;
_cleanup_(ENGINE_freep) ENGINE *e = ENGINE_by_id(engine);
if (!e)
*ret = TAKE_PTR(private_key);
return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
}
int openssl_load_key_from_token(
# include <openssl/bio.h>
# include <openssl/bn.h>
# include <openssl/crypto.h>
-# include <openssl/engine.h>
# include <openssl/err.h>
# include <openssl/evp.h>
# include <openssl/opensslv.h>
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MD_CTX*, EVP_MD_CTX_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ASN1_OCTET_STRING*, ASN1_OCTET_STRING_free, NULL);
-DISABLE_WARNING_DEPRECATED_DECLARATIONS;
-DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ENGINE*, ENGINE_free, NULL);
-REENABLE_WARNING;
+
#if OPENSSL_VERSION_MAJOR >= 3
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER*, EVP_CIPHER_free, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_KDF*, EVP_KDF_free, NULL);
void test_setup_logging(int level) {
log_set_assert_return_is_critical(true);
log_set_max_level(level);
- log_parse_environment();
- log_open();
+ log_setup();
}
int write_tmpfile(char *pattern, const char *contents) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
- _cleanup_free_ char *scope = NULL;
+ _cleanup_free_ char *scope = NULL, *cgroup_root = NULL;
const char *object;
int r;
/* Let's try to run this test in a scope of its own, with delegation turned on, so that PID 1 doesn't
* interfere with our cgroup management. */
+ if (cg_pid_get_path(NULL, 0, &cgroup_root) >= 0 && cg_is_delegated(cgroup_root) && stderr_is_journal()) {
+ log_debug("Already running as a unit with delegated cgroup, not allocating a cgroup subroot.");
+ return 0;
+ }
r = sd_bus_default_system(&bus);
if (r < 0)
#include "sd-daemon.h"
#include "argv-util.h"
+#include "errno-util.h"
#include "macro.h"
#include "process-util.h"
#include "rlimit-util.h"
} \
})
+#define ASSERT_ERROR(expr1, expr2) \
+ ({ \
+ int _expr1 = (expr1); \
+ int _expr2 = (expr2); \
+ if (_expr1 >= 0) { \
+ log_error("%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but it succeeded", \
+ PROJECT_FILE, __LINE__, #expr1, STRERROR(_expr2)); \
+ abort(); \
+ } else if (-_expr1 != _expr2) { \
+ log_error_errno(_expr1, "%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but got the following error: %m", \
+ PROJECT_FILE, __LINE__, #expr1, STRERROR(_expr2)); \
+ abort(); \
+ } \
+ })
+
+#define ASSERT_ERROR_ERRNO(expr1, expr2) \
+ ({ \
+ int _expr1 = (expr1); \
+ int _expr2 = (expr2); \
+ if (_expr1 >= 0) { \
+ log_error("%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but it succeeded", \
+ PROJECT_FILE, __LINE__, #expr1, STRERROR(_expr2)); \
+ abort(); \
+ } else if (errno != _expr2) { \
+ log_error_errno(errno, "%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but got the following error: %m", \
+ PROJECT_FILE, __LINE__, #expr1, STRERROR(errno)); \
+ abort(); \
+ } \
+ })
+
#define ASSERT_TRUE(expr) \
({ \
if (!(expr)) { \
#include "tpm2-util.h"
#include "virt.h"
+#if HAVE_OPENSSL
+# include <openssl/hmac.h>
+#endif
+
#if HAVE_TPM2
static void *libtss2_esys_dl = NULL;
static void *libtss2_rc_dl = NULL;
if (FLAGS_SET(flags, PICK_TRIES) || !filter->version) /* Underspecified? */
return -ENOEXEC;
+ if (strv_length(filter->suffix) > 1) /* suffix is not deterministic? */
+ return -ENOEXEC;
/* The format for names we match goes like this:
*
return -ENOMEM;
}
- if (filter->suffix && !strextend(&fn, filter->suffix))
- return -ENOMEM;
+ if (!strv_isempty(filter->suffix))
+ if (!strextend(&fn, filter->suffix[0]))
+ return -ENOMEM;
if (!filename_is_valid(fn))
return -EINVAL;
if (!result.path)
return log_oom_debug();
- if (filter->version) {
- result.version = strdup(filter->version);
- if (!result.version)
- return log_oom_debug();
- }
+ r = strdup_to(&result.version, filter->version);
+ if (r < 0)
+ return r;
*ret = TAKE_PICK_RESULT(result);
return 1;
return log_oom_debug();
r = chaseat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, &object_path, &object_fd);
- if (r < 0) {
- if (r != -ENOENT)
- return log_debug_errno(r, "Failed to open '%s': %m", prefix_roota(toplevel_path, p));
-
+ if (r == -ENOENT) {
*ret = PICK_RESULT_NULL;
return 0;
}
+ if (r < 0)
+ return log_debug_errno(r, "Failed to open '%s': %m", prefix_roota(toplevel_path, p));
return pin_choice(
toplevel_path,
} else
e = dname;
- if (!isempty(filter->suffix)) {
- char *sfx = endswith(e, filter->suffix);
+ if (!strv_isempty(filter->suffix)) {
+ char *sfx = endswith_strv(e, filter->suffix);
if (!sfx)
continue;
PickResult *ret) {
_cleanup_free_ char *filter_bname = NULL, *dir = NULL, *parent = NULL, *fname = NULL;
- const char *filter_suffix, *enumeration_path;
+ char * const *filter_suffix_strv = NULL;
+ const char *filter_suffix = NULL, *enumeration_path;
uint32_t filter_type_mask;
int r;
if (!filter_bname)
return -ENOMEM;
- if (filter->suffix) {
- /* Chop off suffix, if specified */
- char *f = endswith(filter_bname, filter->suffix);
- if (f)
- *f = 0;
- }
+ /* Chop off suffix, if specified */
+ char *f = endswith_strv(filter_bname, filter->suffix);
+ if (f)
+ *f = 0;
- filter_suffix = filter->suffix;
+ filter_suffix_strv = filter->suffix;
filter_type_mask = filter->type_mask;
enumeration_path = path;
.basename = filter_bname,
.version = filter->version,
.architecture = filter->architecture,
- .suffix = filter_suffix,
+ .suffix = filter_suffix_strv ?: STRV_MAKE(filter_suffix),
},
flags,
ret);
const PickFilter pick_filter_image_raw = {
.type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK),
.architecture = _ARCHITECTURE_INVALID,
- .suffix = ".raw",
+ .suffix = STRV_MAKE(".raw"),
};
const PickFilter pick_filter_image_dir = {
.type_mask = UINT32_C(1) << DT_DIR,
.architecture = _ARCHITECTURE_INVALID,
};
+
+const PickFilter pick_filter_image_any = {
+ .type_mask = (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) | (UINT32_C(1) << DT_DIR),
+ .architecture = _ARCHITECTURE_INVALID,
+ .suffix = STRV_MAKE(".raw", ""),
+};
const char *basename; /* Can be overridden by search pattern */
const char *version;
Architecture architecture;
- const char *suffix; /* Can be overridden by search pattern */
+ char * const *suffix; /* Can be overridden by search pattern */
} PickFilter;
typedef struct PickResult {
extern const PickFilter pick_filter_image_raw;
extern const PickFilter pick_filter_image_dir;
+extern const PickFilter pick_filter_image_any;
int sd_event_source_send_child_signal(sd_event_source *s, int sig, const void *si, unsigned flags);
#endif
int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret);
+int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret);
int sd_event_source_set_memory_pressure_type(sd_event_source *e, const char *ty);
int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec);
int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback);
int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
+int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret);
#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
{ .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
int sd_radv_start(sd_radv *ra);
int sd_radv_stop(sd_radv *ra);
int sd_radv_is_running(sd_radv *ra);
+int sd_radv_send(sd_radv *ra);
int sd_radv_set_ifindex(sd_radv *ra, int interface_index);
int sd_radv_set_ifname(sd_radv *ra, const char *interface_name);
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit);
int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec);
int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec);
-int sd_radv_set_managed_information(sd_radv *ra, int managed);
-int sd_radv_set_other_information(sd_radv *ra, int other);
-int sd_radv_set_preference(sd_radv *ra, unsigned preference);
+int sd_radv_set_managed_information(sd_radv *ra, int b);
+int sd_radv_set_other_information(sd_radv *ra, int b);
+int sd_radv_set_preference(sd_radv *ra, uint8_t preference);
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p);
int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p);
if (r == -ENOMEDIUM)
return log_tests_skipped("cgroupfs not available");
+ r = find_executable("ping", NULL);
+ if (r < 0)
+ return log_tests_skipped_errno(r, "Can't find ping binary: %m");
+
_cleanup_free_ char *unit_dir = NULL;
ASSERT_OK(get_testdata_dir("units", &unit_dir));
ASSERT_OK(set_unit_path(unit_dir));
ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
/* We need to enable access to the filesystem where the binary is so we
- * add @common-block */
- ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("@common-block")), 0);
- ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block")));
- ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "~tracefs")), 0);
- ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("@common-block")), 0);
- ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("debugfs", "@common-block")));
+ * add @common-block and @application */
+ ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("@common-block", "@application")), 0);
+ ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "@application")));
+ ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/tracing/printk_formats", STRV_MAKE("tracefs", "@common-block", "@application", "~tracefs")), 0);
+ ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("@common-block", "@application")), 0);
+ ASSERT_OK(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("debugfs", "@common-block", "@application")));
ASSERT_LT(test_restrict_filesystems(m, "restrict_filesystems_test.service", "/sys/kernel/debug/sleep_time", STRV_MAKE("~debugfs")), 0);
return 0;
usec_t ts;
usec_t timeout = 2 * USEC_PER_MINUTE;
- assert_se(m);
- assert_se(unit);
+ ASSERT_NOT_NULL(m);
+ ASSERT_NOT_NULL(unit);
/* Bump the timeout when running in plain QEMU, as some more involved tests might start hitting the
* default 2m timeout (like exec-dynamicuser-statedir.service) */
usec_t n;
r = sd_event_run(m->event, 100 * USEC_PER_MSEC);
- assert_se(r >= 0);
+ ASSERT_OK(r);
n = now(CLOCK_MONOTONIC);
if (ts + timeout < n) {
Manager *m, Unit *unit, int status_expected, int code_expected) {
Service *service = NULL;
- assert_se(m);
- assert_se(unit);
+ ASSERT_NOT_NULL(m);
+ ASSERT_NOT_NULL(unit);
wait_for_service_finish(m, unit);
Manager *m, Unit *unit, ServiceResult result_expected) {
Service *service = NULL;
- assert_se(m);
- assert_se(unit);
+ ASSERT_NOT_NULL(m);
+ ASSERT_NOT_NULL(unit);
wait_for_service_finish(m, unit);
struct passwd *p;
struct group *g;
- assert_se(name);
+ ASSERT_NOT_NULL(name);
p = getpwnam(name);
if (!p ||
if (slice) {
start_parent_slices(slice);
int r = unit_start(slice, NULL);
- assert_se(r >= 0 || r == -EALREADY);
+ if (r != -EALREADY)
+ ASSERT_OK(r);
}
}
FORK_CLOSE_ALL_FDS |
FORK_DEATHSIG_SIGKILL,
&pid);
- assert(r >= 0);
+ ASSERT_OK(r);
if (r == 0) {
/* Keep CAP_SYS_ADMIN if we have it to ensure we give an
* accurate result to the caller. Some kernels have a
Manager *m, const char *unit_name, int status_expected, int code_expected) {
Unit *unit;
- assert_se(unit_name);
+ ASSERT_NOT_NULL(unit_name);
- assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit));
/* We need to start the slices as well otherwise the slice cgroups might be pruned
* in on_cgroup_empty_event. */
start_parent_slices(unit);
- assert_se(unit_start(unit, NULL) >= 0);
+ ASSERT_OK(unit_start(unit, NULL));
check_main_result(file, line, func, m, unit, status_expected, code_expected);
++n_ran_tests;
Manager *m, const char *unit_name, ServiceResult result_expected) {
Unit *unit;
- assert_se(unit_name);
+ ASSERT_NOT_NULL(unit_name);
- assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
- assert_se(unit_start(unit, NULL) >= 0);
+ ASSERT_OK(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit));
+ ASSERT_OK(unit_start(unit, NULL));
check_service_result(file, line, func, m, unit, result_expected);
}
#define test_service(m, unit_name, result_expected) \
_test_service(PROJECT_FILE, __LINE__, __func__, m, unit_name, result_expected)
static void test_exec_bindpaths(Manager *m) {
- assert_se(mkdir_p("/tmp/test-exec-bindpaths", 0755) >= 0);
- assert_se(mkdir_p("/tmp/test-exec-bindreadonlypaths", 0755) >= 0);
+ ASSERT_OK(mkdir_p("/tmp/test-exec-bindpaths", 0755));
+ ASSERT_OK(mkdir_p("/tmp/test-exec-bindreadonlypaths", 0755));
test(m, "exec-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
static void test_exec_cpuaffinity(Manager *m) {
_cleanup_(cpu_set_reset) CPUSet c = {};
- assert_se(cpu_set_realloc(&c, 8192) >= 0); /* just allocate the maximum possible size */
- assert_se(sched_getaffinity(0, c.allocated, c.set) >= 0);
+ ASSERT_OK(cpu_set_realloc(&c, 8192)); /* just allocate the maximum possible size */
+ ASSERT_OK_ERRNO(sched_getaffinity(0, c.allocated, c.set));
if (!CPU_ISSET_S(0, c.allocated, c.set)) {
log_notice("Cannot use CPU 0, skipping %s", __func__);
}
static void test_exec_workingdirectory(Manager *m) {
- assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0);
+ ASSERT_OK(mkdir_p("/tmp/test-exec_workingdirectory", 0755));
test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
test(m, "exec-workingdirectory-trailing-dot.service", 0, CLD_EXITED);
}
static void test_exec_execsearchpath(Manager *m) {
- assert_se(mkdir_p("/tmp/test-exec_execsearchpath", 0755) >= 0);
+ ASSERT_OK(mkdir_p("/tmp/test-exec_execsearchpath", 0755));
- assert_se(copy_file("/bin/ls", "/tmp/test-exec_execsearchpath/ls_temp", 0, 0777, COPY_REPLACE) >= 0);
+ ASSERT_OK(copy_file("/bin/ls", "/tmp/test-exec_execsearchpath/ls_temp", 0, 0777, COPY_REPLACE));
test(m, "exec-execsearchpath.service", 0, CLD_EXITED);
- assert_se(rm_rf("/tmp/test-exec_execsearchpath", REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+ ASSERT_OK(rm_rf("/tmp/test-exec_execsearchpath", REMOVE_ROOT|REMOVE_PHYSICAL));
test(m, "exec-execsearchpath.service", EXIT_EXEC, CLD_EXITED);
}
int r;
r = write_string_file("/tmp/test-exec_execsearchpath_environmentfile.conf", path_not_set, WRITE_STRING_FILE_CREATE);
-
- assert_se(r == 0);
+ ASSERT_OK(r);
test(m, "exec-execsearchpath-environmentfile.service", 0, CLD_EXITED);
r = write_string_file("/tmp/test-exec_execsearchpath_environmentfile-set.conf", path_set, WRITE_STRING_FILE_CREATE);
-
- assert_se(r == 0);
+ ASSERT_OK(r);
test(m, "exec-execsearchpath-environmentfile-set.service", 0, CLD_EXITED);
}
static void test_exec_execsearchpath_passenvironment(Manager *m) {
- assert_se(setenv("VAR1", "word1 word2", 1) == 0);
- assert_se(setenv("VAR2", "word3", 1) == 0);
- assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
- assert_se(setenv("VAR4", "new\nline", 1) == 0);
- assert_se(setenv("VAR5", "passwordwithbackslashes", 1) == 0);
+ ASSERT_OK_ERRNO(setenv("VAR1", "word1 word2", 1));
+ ASSERT_OK_ERRNO(setenv("VAR2", "word3", 1));
+ ASSERT_OK_ERRNO(setenv("VAR3", "$word 5 6", 1));
+ ASSERT_OK_ERRNO(setenv("VAR4", "new\nline", 1));
+ ASSERT_OK_ERRNO(setenv("VAR5", "passwordwithbackslashes", 1));
test(m, "exec-execsearchpath-passenvironment.service", 0, CLD_EXITED);
- assert_se(setenv("PATH", "/usr", 1) == 0);
+ ASSERT_OK_ERRNO(setenv("PATH", "/usr", 1));
test(m, "exec-execsearchpath-passenvironment-set.service", 0, CLD_EXITED);
- assert_se(unsetenv("VAR1") == 0);
- assert_se(unsetenv("VAR2") == 0);
- assert_se(unsetenv("VAR3") == 0);
- assert_se(unsetenv("VAR4") == 0);
- assert_se(unsetenv("VAR5") == 0);
- assert_se(unsetenv("PATH") == 0);
+ ASSERT_OK_ERRNO(unsetenv("VAR1"));
+ ASSERT_OK_ERRNO(unsetenv("VAR2"));
+ ASSERT_OK_ERRNO(unsetenv("VAR3"));
+ ASSERT_OK_ERRNO(unsetenv("VAR4"));
+ ASSERT_OK_ERRNO(unsetenv("VAR5"));
+ ASSERT_OK_ERRNO(unsetenv("PATH"));
}
static void test_exec_personality(Manager *m) {
}
static void test_exec_privatetmp(Manager *m) {
- assert_se(touch("/tmp/test-exec_privatetmp") >= 0);
+ ASSERT_OK(touch("/tmp/test-exec_privatetmp"));
if (MANAGER_IS_SYSTEM(m) || have_userns_privileges()) {
test(m, "exec-privatetmp-yes.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
char buf[4096];
ssize_t l;
- assert_se(s);
- assert_se(fd >= 0);
+ ASSERT_NOT_NULL(s);
+ ASSERT_GT(fd, 0);
l = read(fd, buf, sizeof(buf) - 1);
if (l < 0) {
buf[l] = '\0';
if (result)
- assert_se(strextend(result, buf));
+ ASSERT_NOT_NULL(strextend(result, buf));
else
log_error("ldd: %s", buf);
reenable:
/* Re-enable the event source if we did not encounter EOF */
- assert_se(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT) >= 0);
+ ASSERT_OK(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT));
return 0;
}
static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
pid_t *pid = userdata;
- assert_se(pid);
+ ASSERT_NOT_NULL(pid);
(void) kill(*pid, SIGKILL);
static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
int ret = -EIO;
- assert_se(si);
+ ASSERT_NOT_NULL(si);
if (si->si_code == CLD_EXITED)
ret = si->si_status;
pid_t pid;
int r;
- assert_se(exec);
- assert_se(ret);
+ ASSERT_NOT_NULL(exec);
+ ASSERT_NOT_NULL(ret);
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
+ ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
- assert_se(pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) == 0);
- assert_se(pipe2(errpipe, O_NONBLOCK|O_CLOEXEC) == 0);
+ ASSERT_OK_ERRNO(pipe2(outpipe, O_NONBLOCK|O_CLOEXEC));
+ ASSERT_OK_ERRNO(pipe2(errpipe, O_NONBLOCK|O_CLOEXEC));
r = safe_fork_full("(spawn-ldd)",
(int[]) { -EBADF, outpipe[1], errpipe[1] },
NULL, 0,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG, &pid);
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (r == 0) {
execlp("ldd", "ldd", exec, NULL);
_exit(EXIT_FAILURE);
outpipe[1] = safe_close(outpipe[1]);
errpipe[1] = safe_close(errpipe[1]);
- assert_se(sd_event_new(&e) >= 0);
+ ASSERT_OK(sd_event_new(&e));
- assert_se(sd_event_add_time_relative(e, NULL, CLOCK_MONOTONIC,
- 10 * USEC_PER_SEC, USEC_PER_SEC, on_spawn_timeout, &pid) >= 0);
- assert_se(sd_event_add_io(e, &stdout_source, outpipe[0], EPOLLIN, on_spawn_io, &result) >= 0);
- assert_se(sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT) >= 0);
- assert_se(sd_event_add_io(e, &stderr_source, errpipe[0], EPOLLIN, on_spawn_io, NULL) >= 0);
- assert_se(sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT) >= 0);
- assert_se(sd_event_add_child(e, &sigchld_source, pid, WEXITED, on_spawn_sigchld, NULL) >= 0);
+ ASSERT_OK(sd_event_add_time_relative(e, NULL, CLOCK_MONOTONIC,
+ 10 * USEC_PER_SEC, USEC_PER_SEC, on_spawn_timeout, &pid));
+ ASSERT_OK(sd_event_add_io(e, &stdout_source, outpipe[0], EPOLLIN, on_spawn_io, &result));
+ ASSERT_OK(sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT));
+ ASSERT_OK(sd_event_add_io(e, &stderr_source, errpipe[0], EPOLLIN, on_spawn_io, NULL));
+ ASSERT_OK(sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT));
+ ASSERT_OK(sd_event_add_child(e, &sigchld_source, pid, WEXITED, on_spawn_sigchld, NULL));
/* SIGCHLD should be processed after IO is complete */
- assert_se(sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1) >= 0);
+ ASSERT_OK(sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1));
- assert_se(sd_event_loop(e) >= 0);
+ ASSERT_OK(sd_event_loop(e));
_cleanup_strv_free_ char **v = NULL;
- assert_se(strv_split_newlines_full(&v, result, 0) >= 0);
+ ASSERT_OK(strv_split_newlines_full(&v, result, 0));
STRV_FOREACH(q, v) {
_cleanup_free_ char *word = NULL;
const char *p = *q;
r = extract_first_word(&p, &word, NULL, 0);
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (r == 0)
continue;
if (path_is_absolute(word)) {
- assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
+ ASSERT_OK(strv_consume(&libraries, TAKE_PTR(word)));
continue;
}
word = mfree(word);
r = extract_first_word(&p, &word, NULL, 0);
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (r == 0)
continue;
word = mfree(word);
r = extract_first_word(&p, &word, NULL, 0);
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (r == 0)
continue;
if (path_is_absolute(word)) {
- assert_se(strv_consume(&libraries, TAKE_PTR(word)) >= 0);
+ ASSERT_OK(strv_consume(&libraries, TAKE_PTR(word)));
continue;
}
}
_cleanup_strv_free_ char **libraries = NULL, **libraries_test = NULL;
int r;
- assert_se(user_runtime_unit_dir);
+ ASSERT_NOT_NULL(user_runtime_unit_dir);
r = find_executable("ldd", NULL);
if (r < 0) {
if (MANAGER_IS_USER(m) && !have_userns_privileges())
return (void)log_notice("Skipping %s, do not have user namespace privileges", __func__);
- assert_se(find_libraries(fullpath_touch, &libraries) >= 0);
- assert_se(find_libraries(fullpath_test, &libraries_test) >= 0);
- assert_se(strv_extend_strv(&libraries, libraries_test, true) >= 0);
+ ASSERT_OK(find_libraries(fullpath_touch, &libraries));
+ ASSERT_OK(find_libraries(fullpath_test, &libraries_test));
+ ASSERT_OK(strv_extend_strv(&libraries, libraries_test, true));
- assert_se(strextend(&data, "[Service]\n"));
- assert_se(strextend(&data, "ExecStart=", fullpath_touch, " /aaa\n"));
- assert_se(strextend(&data, "ExecStart=", fullpath_test, " -f /aaa\n"));
- assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n"));
- assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n"));
+ ASSERT_NOT_NULL(strextend(&data, "[Service]\n"));
+ ASSERT_NOT_NULL(strextend(&data, "ExecStart=", fullpath_touch, " /aaa\n"));
+ ASSERT_NOT_NULL(strextend(&data, "ExecStart=", fullpath_test, " -f /aaa\n"));
+ ASSERT_NOT_NULL(strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n"));
+ ASSERT_NOT_NULL(strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n"));
STRV_FOREACH(p, libraries)
- assert_se(strextend(&data, "BindReadOnlyPaths=", *p, "\n"));
+ ASSERT_NOT_NULL(strextend(&data, "BindReadOnlyPaths=", *p, "\n"));
- assert_se(write_drop_in(user_runtime_unit_dir, "exec-mount-apivfs-no.service", 10, "bind-mount", data) >= 0);
+ ASSERT_OK(write_drop_in(user_runtime_unit_dir, "exec-mount-apivfs-no.service", 10, "bind-mount", data));
- assert_se(mkdir_p("/tmp/test-exec-mount-apivfs-no/root", 0755) >= 0);
+ ASSERT_OK(mkdir_p("/tmp/test-exec-mount-apivfs-no/root", 0755));
test(m, "exec-mount-apivfs-no.service", can_unshare || !MANAGER_IS_SYSTEM(m) ? 0 : EXIT_NAMESPACE, CLD_EXITED);
_cleanup_free_ char *p = NULL;
struct stat st;
- assert_se(p = path_join(m->prefix[dt], "private"));
+ ASSERT_NOT_NULL(p = path_join(m->prefix[dt], "private"));
if (stat(p, &st) >= 0 &&
(st.st_mode & (S_IRWXG|S_IRWXO)))
int r;
r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE);
- assert_se(r == 0);
+ ASSERT_OK(r);
test(m, "exec-environmentfile.service", 0, CLD_EXITED);
* This is still a good approximation of how a test for MANAGER_SYSTEM
* would work.
*/
- assert_se(setenv("VAR1", "word1 word2", 1) == 0);
- assert_se(setenv("VAR2", "word3", 1) == 0);
- assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
- assert_se(setenv("VAR4", "new\nline", 1) == 0);
- assert_se(setenv("VAR5", "passwordwithbackslashes", 1) == 0);
+ ASSERT_OK_ERRNO(setenv("VAR1", "word1 word2", 1));
+ ASSERT_OK_ERRNO(setenv("VAR2", "word3", 1));
+ ASSERT_OK_ERRNO(setenv("VAR3", "$word 5 6", 1));
+ ASSERT_OK_ERRNO(setenv("VAR4", "new\nline", 1));
+ ASSERT_OK_ERRNO(setenv("VAR5", "passwordwithbackslashes", 1));
test(m, "exec-passenvironment.service", 0, CLD_EXITED);
test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED);
test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED);
- assert_se(unsetenv("VAR1") == 0);
- assert_se(unsetenv("VAR2") == 0);
- assert_se(unsetenv("VAR3") == 0);
- assert_se(unsetenv("VAR4") == 0);
- assert_se(unsetenv("VAR5") == 0);
+ ASSERT_OK_ERRNO(unsetenv("VAR1"));
+ ASSERT_OK_ERRNO(unsetenv("VAR2"));
+ ASSERT_OK_ERRNO(unsetenv("VAR3"));
+ ASSERT_OK_ERRNO(unsetenv("VAR4"));
+ ASSERT_OK_ERRNO(unsetenv("VAR5"));
test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
}
{},
};
- assert_se(unsetenv("USER") == 0);
- assert_se(unsetenv("LOGNAME") == 0);
- assert_se(unsetenv("SHELL") == 0);
- assert_se(unsetenv("HOME") == 0);
- assert_se(unsetenv("TMPDIR") == 0);
+ ASSERT_OK_ERRNO(unsetenv("USER"));
+ ASSERT_OK_ERRNO(unsetenv("LOGNAME"));
+ ASSERT_OK_ERRNO(unsetenv("SHELL"));
+ ASSERT_OK_ERRNO(unsetenv("HOME"));
+ ASSERT_OK_ERRNO(unsetenv("TMPDIR"));
/* Unset VARx, especially, VAR1, VAR2 and VAR3, which are used in the PassEnvironment test cases,
* otherwise (and if they are present in the environment), `manager_default_environment` will copy
* them into the default environment which is passed to each created job, which will make the tests
* that expect those not to be present to fail. */
- assert_se(unsetenv("VAR1") == 0);
- assert_se(unsetenv("VAR2") == 0);
- assert_se(unsetenv("VAR3") == 0);
- assert_se(unsetenv("VAR4") == 0);
- assert_se(unsetenv("VAR5") == 0);
+ ASSERT_OK_ERRNO(unsetenv("VAR1"));
+ ASSERT_OK_ERRNO(unsetenv("VAR2"));
+ ASSERT_OK_ERRNO(unsetenv("VAR3"));
+ ASSERT_OK_ERRNO(unsetenv("VAR4"));
+ ASSERT_OK_ERRNO(unsetenv("VAR5"));
- assert_se(runtime_dir = setup_fake_runtime_dir());
- assert_se(user_runtime_unit_dir = path_join(runtime_dir, "systemd/user"));
- assert_se(unit_paths = strjoin(PRIVATE_UNIT_DIR, ":", user_runtime_unit_dir));
- assert_se(set_unit_path(unit_paths) >= 0);
+ ASSERT_NOT_NULL(runtime_dir = setup_fake_runtime_dir());
+ ASSERT_NOT_NULL(user_runtime_unit_dir = path_join(runtime_dir, "systemd/user"));
+ ASSERT_NOT_NULL(unit_paths = strjoin(PRIVATE_UNIT_DIR, ":", user_runtime_unit_dir));
+ ASSERT_OK(set_unit_path(unit_paths));
r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
if (manager_errno_skip_test(r))
return (void) log_tests_skipped_errno(r, "manager_new");
- assert_se(r >= 0);
+ ASSERT_OK(r);
m->defaults.std_output = EXEC_OUTPUT_INHERIT; /* don't rely on host journald */
- assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
+ ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
/* Uncomment below if you want to make debugging logs stored to journal. */
//manager_override_log_target(m, LOG_TARGET_AUTO);
FORK_NEW_MOUNTNS |
FORK_MOUNTNS_SLAVE,
NULL);
- assert_se(r >= 0);
+ ASSERT_OK(r);
if (r == 0) {
_cleanup_free_ char *unit_dir = NULL, *build_dir = NULL, *build_dir_mount = NULL;
int ret;
/* Make "/" read-only. */
- assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) >= 0);
+ ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL));
/* Creating a new user namespace in the above means all MS_SHARED mounts become MS_SLAVE.
* Let's put them back to MS_SHARED here, since that's what we want as defaults. (This will
* not reconnect propagation, but simply create new peer groups for all our mounts). */
- assert_se(mount_follow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_SHARED|MS_REC, NULL) >= 0);
+ ASSERT_OK(mount_follow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_SHARED|MS_REC, NULL));
- assert_se(mkdir_p(PRIVATE_UNIT_DIR, 0755) >= 0);
- assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", PRIVATE_UNIT_DIR, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
+ ASSERT_OK(mkdir_p(PRIVATE_UNIT_DIR, 0755));
+ ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", PRIVATE_UNIT_DIR, "tmpfs", MS_NOSUID|MS_NODEV, NULL));
/* Mark our test "playground" as MS_SLAVE, so we can MS_MOVE mounts underneath it. */
- assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, PRIVATE_UNIT_DIR, NULL, MS_SLAVE, NULL) >= 0);
+ ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, NULL, PRIVATE_UNIT_DIR, NULL, MS_SLAVE, NULL));
/* Copy unit files to make them accessible even when unprivileged. */
- assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
- assert_se(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0);
+ ASSERT_OK(get_testdata_dir("test-execute/", &unit_dir));
+ ASSERT_OK(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY));
/* Mount tmpfs on the following directories to make not StateDirectory= or friends disturb the host. */
ret = get_build_exec_dir(&build_dir);
- assert_se(ret >= 0 || ret == -ENOEXEC);
+ if (ret != -ENOEXEC)
+ ASSERT_OK(ret);
if (build_dir) {
/* Account for a build directory being in one of the soon-to-be-tmpfs directories. If we
* overmount it with an empty tmpfs, manager_new() will pin the wrong systemd-executor binary,
* which can then lead to unexpected (and painful to debug) test fails. */
- assert_se(access(build_dir, F_OK) >= 0);
- assert_se(build_dir_mount = path_join(PRIVATE_UNIT_DIR, "build_dir"));
- assert_se(mkdir_p(build_dir_mount, 0755) >= 0);
- assert_se(mount_nofollow_verbose(LOG_DEBUG, build_dir, build_dir_mount, NULL, MS_BIND, NULL) >= 0);
+ ASSERT_OK_ERRNO(access(build_dir, F_OK));
+ ASSERT_NOT_NULL(build_dir_mount = path_join(PRIVATE_UNIT_DIR, "build_dir"));
+ ASSERT_OK(mkdir_p(build_dir_mount, 0755));
+ ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, build_dir, build_dir_mount, NULL, MS_BIND, NULL));
}
FOREACH_STRING(p, "/dev/shm", "/root", "/tmp", "/var/tmp", "/var/lib")
- assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0);
+ ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, NULL));
if (build_dir_mount) {
ret = RET_NERRNO(access(build_dir, F_OK));
- assert_se(ret >= 0 || ret == -ENOENT);
+ if (ret != -ENOENT)
+ ASSERT_OK(ret);
if (ret == -ENOENT) {
/* The build directory got overmounted by tmpfs, so let's use the "backup" bind mount to
* bring it back. */
- assert_se(mkdir_p(build_dir, 0755) >= 0);
- assert_se(mount_nofollow_verbose(LOG_DEBUG, build_dir_mount, build_dir, NULL, MS_MOVE, NULL) >= 0);
+ ASSERT_OK(mkdir_p(build_dir, 0755));
+ ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, build_dir_mount, build_dir, NULL, MS_MOVE, NULL));
}
}
/* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */
FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") {
- assert_se(mkdir_p(p, 0) >= 0);
- assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, "mode=0000") >= 0);
+ ASSERT_OK(mkdir_p(p, 0));
+ ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, "mode=0000"));
}
- assert_se(write_string_file("/run/credstore/test-execute.load-credential", "foo", WRITE_STRING_FILE_CREATE) >= 0);
+ ASSERT_OK(write_string_file("/run/credstore/test-execute.load-credential", "foo", WRITE_STRING_FILE_CREATE));
}
return r;
return (void) log_tests_skipped("unshare() is disabled");
/* safe_fork() clears saved_argv in the child process. Let's copy it. */
- assert_se(filters = strv_copy(strv_skip(saved_argv, 1)));
+ ASSERT_NOT_NULL(filters = strv_copy(strv_skip(saved_argv, 1)));
if (prepare_ns("(test-execute-root)") == 0) {
can_unshare = true;
return (void) log_tests_skipped("Seccomp not available, cannot run unshare() filtered tests");
/* safe_fork() clears saved_argv in the child process. Let's copy it. */
- assert_se(filters = strv_copy(strv_skip(saved_argv, 1)));
+ ASSERT_NOT_NULL(filters = strv_copy(strv_skip(saved_argv, 1)));
if (prepare_ns("(test-execute-without-unshare)") == 0) {
_cleanup_hashmap_free_ Hashmap *s = NULL;
r = seccomp_syscall_resolve_name("unshare");
- assert_se(r != __NR_SCMP_ERROR);
- assert_se(hashmap_ensure_put(&s, NULL, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)) >= 0);
- assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true) >= 0);
+ ASSERT_NE(r, __NR_SCMP_ERROR);
+ ASSERT_OK(hashmap_ensure_put(&s, NULL, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)));
+ ASSERT_OK(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true));
/* Check unshare() is actually filtered. */
- assert_se(unshare(CLONE_NEWNS) < 0);
- assert_se(errno == EOPNOTSUPP);
+ ASSERT_ERROR_ERRNO(unshare(CLONE_NEWNS), EOPNOTSUPP);
can_unshare = false;
run_tests(RUNTIME_SCOPE_SYSTEM, filters);
return (void) log_tests_skipped("unshare() is disabled");
/* safe_fork() clears saved_argv in the child process. Let's copy it. */
- assert_se(filters = strv_copy(strv_skip(saved_argv, 1)));
+ ASSERT_NOT_NULL(filters = strv_copy(strv_skip(saved_argv, 1)));
if (prepare_ns("(test-execute-unprivileged)") == 0) {
- assert_se(capability_bounding_set_drop(0, /* right_now = */ true) >= 0);
+ ASSERT_OK(capability_bounding_set_drop(0, /* right_now = */ true));
can_unshare = false;
run_tests(RUNTIME_SCOPE_USER, filters);
copyfd = fdset_put_dup(fdset, fd);
assert_se(copyfd >= 0);
+ /* fdset_close_others() will close any logging file descriptors as well, so close them beforehand
+ * and reopen them again afterwards. */
+ log_close();
assert_se(fdset_close_others(fdset) >= 0);
+
flags = fcntl(fd, F_GETFD);
assert_se(flags < 0);
+
+ /* Open log again after checking that fd is invalid, since reopening the log might make fd a valid
+ * file descriptor again. */
+ (void) log_open();
+
flags = fcntl(copyfd, F_GETFD);
assert_se(flags >= 0);
}
}
TEST(sd_id128_get_invocation) {
- sd_id128_t id;
+ sd_id128_t id = SD_ID128_NULL;
int r;
/* Query the invocation ID */
log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
else
log_info("Invocation ID: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
+
+ sd_id128_t appid = SD_ID128_NULL;
+ r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(59,36,e9,92,fd,11,42,fe,87,c9,e9,b5,6c,9e,4f,04), &appid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+ else {
+ assert(!sd_id128_equal(id, appid));
+ log_info("Per-App Invocation ID: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid));
+ }
+
+ sd_id128_t appid2 = SD_ID128_NULL;
+ r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(59,36,e9,92,fd,11,42,fe,87,c9,e9,b5,6c,9e,4f,05), &appid2); /* slightly different appid */
+ if (r < 0)
+ log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+ else {
+ assert(!sd_id128_equal(id, appid2));
+ assert(!sd_id128_equal(appid, appid2));
+ log_info("Per-App Invocation ID 2: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid2));
+ }
+
+ sd_id128_t appid3 = SD_ID128_NULL;
+ r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(59,36,e9,92,fd,11,42,fe,87,c9,e9,b5,6c,9e,4f,04), &appid3); /* same appid as before */
+ if (r < 0)
+ log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+ else {
+ assert(!sd_id128_equal(id, appid3));
+ assert(sd_id128_equal(appid, appid3));
+ assert(!sd_id128_equal(appid2, appid3));
+ log_info("Per-App Invocation ID 3: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid3));
+ }
}
TEST(benchmark_sd_id128_get_machine_app_specific) {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stddef.h>
+#include <sys/stat.h>
+#include "errno-util.h"
#include "log.h"
#include "macro.h"
#include "tests.h"
ASSERT_SIGNAL(ASSERT_OK_ERRNO(-1), SIGABRT);
ASSERT_SIGNAL(ASSERT_OK_ERRNO(-ENOANO), SIGABRT);
+ ASSERT_ERROR(-ENOENT, ENOENT);
+ ASSERT_ERROR(RET_NERRNO(mkdir("/i/will/fail/with/enoent", 666)), ENOENT);
+ ASSERT_SIGNAL(ASSERT_ERROR(0, ENOENT), SIGABRT);
+ ASSERT_SIGNAL(ASSERT_ERROR(RET_NERRNO(mkdir("/i/will/fail/with/enoent", 666)), ENOANO), SIGABRT);
+
+ errno = ENOENT;
+ ASSERT_ERROR_ERRNO(-1, ENOENT);
+ errno = 0;
+ ASSERT_ERROR_ERRNO(mkdir("/i/will/fail/with/enoent", 666), ENOENT);
+ ASSERT_SIGNAL(ASSERT_ERROR_ERRNO(0, ENOENT), SIGABRT);
+ errno = 0;
+ ASSERT_SIGNAL(ASSERT_ERROR_ERRNO(mkdir("/i/will/fail/with/enoent", 666), ENOANO), SIGABRT);
+
ASSERT_TRUE(true);
ASSERT_TRUE(255);
ASSERT_TRUE(getpid());
PickFilter filter = {
.architecture = _ARCHITECTURE_INVALID,
- .suffix = ".raw",
+ .suffix = STRV_MAKE(".raw"),
};
if (IN_SET(native_architecture(), ARCHITECTURE_X86, ARCHITECTURE_X86_64)) {
log_warning("/dev/rtc is busy. Is somebody keeping it open continuously? That's not a good idea... Returning a bogus RTC timestamp.");
else if (r == -ENOENT)
log_debug("/dev/rtc not found.");
+ else if (r == -ENODATA)
+ log_debug("/dev/rtc has no valid time, power loss probably occurred?");
else if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to read RTC: %m");
else
DEFAULT_SECTIONS_TO_SHOW = {
'.linux' : 'binary',
'.initrd' : 'binary',
+ '.ucode' : 'binary',
'.splash' : 'binary',
'.dtb' : 'binary',
'.cmdline' : 'text',
('.splash', opts.splash, True ),
('.pcrpkey', pcrpkey, True ),
('.initrd', initrd, True ),
+ ('.ucode', opts.microcode, True ),
# linux shall be last to leave breathing room for decompression.
# We'll add it later.
config_push = ConfigItem.config_list_prepend,
),
+ ConfigItem(
+ '--microcode',
+ metavar = 'UCODE',
+ type = pathlib.Path,
+ help = 'microcode file [.ucode section]',
+ config_key = 'UKI/Microcode',
+ ),
+
ConfigItem(
('--config', '-c'),
metavar = 'PATH',
log_warning("Couldn't find OVMF firmware blob with Secure Boot support, "
"falling back to OVMF firmware blobs without Secure Boot support.");
- shm = arg_directory ? ",memory-backend=mem" : "";
+ shm = arg_directory || arg_runtime_mounts.n_mounts != 0 ? ",memory-backend=mem" : "";
if (ARCHITECTURE_SUPPORTS_SMM)
machine = strjoin("type=" QEMU_MACHINE_TYPE ",smm=", on_off(ovmf_config->supports_sb), shm);
else
if (strv_extend_many(&cmdline, "-uuid", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0)
return log_oom();
+ /* Derive a vmgenid automatically from the invocation ID, in a deterministic way. */
+ sd_id128_t vmgenid;
+ r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(bd,84,6d,e3,e4,7d,4b,6c,a6,85,4a,87,0f,3c,a3,a0), &vmgenid);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to get invocation ID, making up randomized vmgenid: %m");
+
+ r = sd_id128_randomize(&vmgenid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make up randomized vmgenid: %m");
+ }
+
+ _cleanup_free_ char *vmgenid_device = NULL;
+ if (asprintf(&vmgenid_device, "vmgenid,guid=" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(vmgenid)) < 0)
+ return log_oom();
+
+ if (strv_extend_many(&cmdline, "-device", vmgenid_device) < 0)
+ return log_oom();
+
/* if we are going to be starting any units with state then create our runtime dir */
if (arg_tpm != 0 || arg_directory || arg_runtime_mounts.n_mounts != 0) {
r = runtime_directory(&arg_runtime_directory, arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER, "systemd/vmspawn");
pass_fds[n_pass_fds++] = device_fd;
}
- r = strv_extend_many(&cmdline, "-cpu", "max");
+ r = strv_extend_many(&cmdline, "-cpu",
+#ifdef __x86_64__
+ "max,hv_relaxed,hv-vapic,hv-time"
+#else
+ "max"
+#endif
+ );
if (r < 0)
return log_oom();
return log_error_errno(r, "Failed to call getsockname on VSOCK: %m");
}
+ const char *e = secure_getenv("SYSTEMD_VMSPAWN_QEMU_EXTRA");
+ if (e) {
+ _cleanup_strv_free_ char **extra = NULL;
+
+ r = strv_split_full(&extra, e, /* separator= */ NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to split $SYSTEMD_VMSPAWN_QEMU_EXTRA environment variable: %m");
+
+ if (strv_extend_strv(&cmdline, extra, /* filter_duplicates= */ false) < 0)
+ return log_oom();
+ }
+
if (DEBUG_LOGGING) {
_cleanup_free_ char *joined = quote_command_line(cmdline, SHELL_ESCAPE_EMPTY);
if (!joined)
.basename = arg_filter_basename,
.version = arg_filter_version,
.architecture = arg_filter_architecture,
- .suffix = arg_filter_suffix,
+ .suffix = STRV_MAKE(arg_filter_suffix),
.type_mask = arg_filter_type_mask,
},
arg_flags,
check(self, False, False)
def test_dhcp_client_default_use_domains(self):
- def check(self, ipv4, ipv6):
+ def check(self, common, ipv4, ipv6):
mkdir_p(networkd_conf_dropin_dir)
with open(os.path.join(networkd_conf_dropin_dir, 'default_use_domains.conf'), mode='w', encoding='utf-8') as f:
+ f.write('[Network]\nUseDomains=')
+ f.write('yes\n' if common else 'no\n')
f.write('[DHCPv4]\nUseDomains=')
f.write('yes\n' if ipv4 else 'no\n')
f.write('[DHCPv6]\nUseDomains=')
for _ in range(20):
output = resolvectl('domain', 'veth99')
- if ipv4 or ipv6:
+ if common or ipv4 or ipv6:
if 'example.com' in output:
break
else:
time.sleep(0.5)
else:
print(output)
+ print(read_link_state_file('veth99'))
self.fail('unexpected domain setting in resolved...')
stop_dnsmasq()
remove_networkd_conf_dropin('default_use_domains.conf')
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
- check(self, True, True)
- check(self, True, False)
- check(self, False, True)
- check(self, False, False)
+ check(self, True, False, False)
+ check(self, False, True, True)
+ check(self, False, True, False)
+ check(self, False, False, True)
+ check(self, False, False, False)
def test_dhcp_client_use_captive_portal(self):
def check(self, ipv4, ipv6):
portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1
+# Ensure vpick works, including reattaching to a new image
+mkdir -p /tmp/app1.v/
+cp /usr/share/app1.raw /tmp/app1.v/app1_1.0.raw
+cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw
+portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+rm -f /tmp/app1.v/app1_2.0.raw
+portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
+
+systemctl is-active app1.service
+status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)"
+[[ "${status}" == "running-runtime" ]]
+
+portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1
+rm -f /tmp/app1.v/app1_1.0.raw
+
# Ensure that the combination of read-only images, state directory and dynamic user works, and that
# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
# after the service is attached before the file appears.