libxkbcommon-dev
libxtables-dev
libzstd-dev
+ mold
mount
net-tools
perl
for args in "${ARGS[@]}"; do
SECONDS=0
- # meson fails with
- # src/boot/efi/meson.build:52: WARNING: Not using lld as efi-ld, falling back to bfd
- # src/boot/efi/meson.build:52:16: ERROR: Fatal warnings enabled, aborting
- # when LINKER is set to lld so let's just not turn meson warnings into errors with lld
- # to make sure that the build systemd can pick up the correct efi-ld linker automatically.
-
# The install_tag feature introduced in 0.60 causes meson to fail with fatal-meson-warnings
# "Project targeting '>= 0.53.2' but tried to use feature introduced in '0.60.0': install_tag arg in custom_target"
# It can be safely removed from the CI since it isn't actually used anywhere to test anything.
find . -type f -name meson.build -exec sed -i '/install_tag/d' '{}' '+'
- if [[ "$LINKER" != lld ]]; then
- additional_meson_args="--fatal-meson-warnings"
+
+ # mold < 1.1 does not support LTO.
+ if dpkg --compare-versions "$(dpkg-query --showformat='${Version}' --show mold)" ge 1.1; then
+ fatal "Newer mold version detected, please remove this workaround."
+ elif [[ "$args" == *"-Db_lto=true"* ]]; then
+ LD="gold"
+ else
+ LD="$LINKER"
fi
+
info "Checking build with $args"
# shellcheck disable=SC2086
if ! AR="$AR" \
- CC="$CC" CC_LD="$LINKER" CFLAGS="-Werror" \
- CXX="$CXX" CXX_LD="$LINKER" CXXFLAGS="-Werror" \
+ CC="$CC" CC_LD="$LD" CFLAGS="-Werror" \
+ CXX="$CXX" CXX_LD="$LD" CXXFLAGS="-Werror" \
meson -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true --werror \
- -Dnobody-group=nogroup $additional_meson_args \
- -Dcryptolib="${CRYPTOLIB:?}" $args build; then
+ -Dnobody-group=nogroup -Dcryptolib="${CRYPTOLIB:?}" \
+ $args build; then
cat build/meson-logs/meson-log.txt
fatal "meson failed with $args"
env:
- { COMPILER: "gcc", COMPILER_VERSION: "11", LINKER: "bfd", CRYPTOLIB: "gcrypt" }
- { COMPILER: "gcc", COMPILER_VERSION: "12", LINKER: "gold", CRYPTOLIB: "openssl" }
- - { COMPILER: "clang", COMPILER_VERSION: "13", LINKER: "gold", CRYPTOLIB: "gcrypt" }
+ - { COMPILER: "clang", COMPILER_VERSION: "13", LINKER: "mold", CRYPTOLIB: "gcrypt" }
- { COMPILER: "clang", COMPILER_VERSION: "14", LINKER: "lld", CRYPTOLIB: "openssl" }
- { COMPILER: "clang", COMPILER_VERSION: "15", LINKER: "bfd", CRYPTOLIB: "auto" }
env: ${{ matrix.env }}
release: rawhide
- distro: opensuse
release: tumbleweed
- - distro: centos_epel
- release: 8-stream
- distro: centos_epel
release: 9-stream
) or (
f.getQualifiedName() = "accept" and
message = "Call to accept() is not O_CLOEXEC-safe. Use accept4() instead."
+ ) or (
+ f.getQualifiedName() = "dirname" and
+ message = "Call dirname() is icky. Use path_extract_directory() instead."
)
}
* rework mount.c and swap.c to follow proper state enumeration/deserialization
semantics, like we do for device.c now
+* get rid of prefix_roota() and similar, only use chase_symlinks() and related
+ calls instead.
+
+* get rid of basename() and replace by path_extract_filename()
+
Deprecations and removals:
* Remove any support for booting without /usr pre-mounted in the initrd entirely.
process, please use `_exit()` instead of `exit()`, so that the exit handlers
are not run.
-- We never use the POSIX version of `basename()` (which glibc defines in
- `libgen.h`), only the GNU version (which glibc defines in `string.h`). The
- only reason to include `libgen.h` is because `dirname()` is needed. Every
- time you need that please immediately undefine `basename()`, and add a
- comment about it, so that no code ever ends up using the POSIX version!
+- Do not use `basename()` or `dirname()`. The semantics in corner cases are
+ full of pitfalls, and the fact that there are two quite different versions of
+ `basename()` (one POSIX and one GNU, of which the latter is much more useful)
+ doesn't make it bette either. Use path_extract_filename() and
+ path_extract_directory() instead.
- Never use `FILENAME_MAX`. Use `PATH_MAX` instead (for checking maximum size
of paths) and `NAME_MAX` (for checking maximum size of filenames).
<varlistentry>
<term><option>--image=<replaceable>image</replaceable></option></term>
- <listitem><para>Takes a path to a disk image file or block device node. If specified all operations
- are applied to file system in the indicated disk image. This is similar to <option>--root=</option>
- but operates on file systems stored in disk images or block devices. The disk image should either
- contain just a file system or a set of file systems within a GPT partition table, following the
- <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
+ <listitem><para>Takes a path to a disk image file or block device node. If specified, all operations
+ are applied to file system in the indicated disk image. This option is similar to
+ <option>--root=</option>, but operates on file systems stored in disk images or block devices. The
+ disk image should either contain just a file system or a set of file systems within a GPT partition
+ table, following the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
Specification</ulink>. For further information on supported disk images, see
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
switch of the same name.</para></listitem>
Specification Type #2 entries should be placed in the directory <literal>$(bootctl
-x)/EFI/Linux/</literal>.</para>
- <para>Note that this option (similar to the <option>--print-booth-path</option> option mentioned
+ <para>Note that this option (similarly to the <option>--print-booth-path</option> option mentioned
above), is available independently from the boot loader used, i.e. also without
<command>systemd-boot</command> being installed.</para></listitem>
</varlistentry>
<para>If set to <option>os-id</option> the entries are named after the OS ID of the running system,
i.e. the <varname>ID=</varname> field of
- <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- (e.g. <literal>fedora</literal>). Similar, if set to <option>os-image-id</option> the entries are
- named after the OS image ID of the running system, i.e. the <varname>IMAGE_ID=</varname> field of
+ <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> (e.g.
+ <literal>fedora</literal>). Similarly, if set to <option>os-image-id</option> the entries are named
+ after the OS image ID of the running system, i.e. the <varname>IMAGE_ID=</varname> field of
<filename>os-release</filename> (e.g. <literal>vendorx-cashier-system</literal>).</para>
<para>If set to <option>auto</option> (the default), the <filename>/etc/kernel/entry-token</filename>
line = line.rstrip()
if not line or line.startswith('#'):
continue
- if m := re.match(r'([A-Z][A-Z_0-9]+)=(.*)', line):
+ m = re.match(r'([A-Z][A-Z_0-9]+)=(.*)', line)
+ if m:
name, val = m.groups()
if val and val[0] in '"\'':
val = ast.literal_eval(val)
then decrypted by the PKCS#11 token with an RSA key stored on it, and then used to unlock the encrypted
volume. Use the <option>pkcs11-uri=</option> option described below to use this mechanism.</para></listitem>
- <listitem><para>Similar, the key may be acquired via a FIDO2 compatible hardware security token (which
- must implement the "hmac-secret" extension). In this case a (during enrollment) randomly generated key
- is stored on disk/removable media, acquired via <constant>AF_UNIX</constant>, or stored in the LUKS2
- JSON token metadata header. The random key is hashed via a keyed hash function (HMAC) on the FIDO2
- token, using a secret key stored on the token that never leaves it. The resulting hash value is then
- used as key to unlock the encrypted volume. Use the <option>fido2-device=</option> option described
- below to use this mechanism.</para></listitem>
-
- <listitem><para>Similar, the key may be acquired via a TPM2 security chip. In this case a (during
+ <listitem><para>Similarly, the key may be acquired via a FIDO2 compatible hardware security token
+ (which must implement the "hmac-secret" extension). In this case a key generated randomly during
+ enrollment is stored on disk/removable media, acquired via <constant>AF_UNIX</constant>, or stored in
+ the LUKS2 JSON token metadata header. The random key is hashed via a keyed hash function (HMAC) on the
+ FIDO2 token, using a secret key stored on the token that never leaves it. The resulting hash value is
+ then used as key to unlock the encrypted volume. Use the <option>fido2-device=</option> option
+ described below to use this mechanism.</para></listitem>
+
+ <listitem><para>Similarly, the key may be acquired via a TPM2 security chip. In this case a (during
enrollment) randomly generated key — encrypted by an asymmetric key derived from the TPM2 chip's seed
key — is stored on disk/removable media, acquired via <constant>AF_UNIX</constant>, or stored in the
LUKS2 JSON token metadata header. Use the <option>tpm2-device=</option> option described below to use
project='man-pages'><refentrytitle>unix</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
details. The source socket name is chosen according the following format:</para>
- <programlisting><constant>NUL</constant> <replaceable>RANDOM</replaceable> <literal>/cryptsetup/</literal> <replaceable>VOLUME</replaceable></programlisting>
+ <programlisting><constant>NUL</constant> <replaceable>RANDOM</replaceable> /cryptsetup/ <replaceable>VOLUME</replaceable></programlisting>
<para>In other words: a <constant>NUL</constant> byte (as required for abstract namespace sockets),
followed by a random string (consisting of alphanumeric characters only), followed by the literal
string <literal>/cryptsetup/</literal>, followed by the name of the volume to acquire they key
- for. Example (for a volume <literal>myvol</literal>):</para>
+ for. For example, for the volume <literal>myvol</literal>:</para>
- <example><programlisting>\0d7067f78d9827418/cryptsetup/myvol</programlisting></example>
+ <programlisting>\0d7067f78d9827418/cryptsetup/myvol</programlisting>
<para>Services listening on the <constant>AF_UNIX</constant> stream socket may query the source socket
name with <citerefentry
project='man-pages'><refentrytitle>getpeername</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
- and use it to determine which key to send, allowing a single listening socket to serve keys for a
- multitude of volumes. If the PKCS#11 logic is used (see above) the socket source name is picked in
- identical fashion, except that the literal string <literal>/cryptsetup-pkcs11/</literal> is used (similar
- for FIDO2: <literal>/cryptsetup-fido2/</literal> and TPM2: <literal>/cryptsetup-tpm2/</literal>). This is
- done so that services providing key material know that not a secret key is requested but an encrypted key
- that will be decrypted via the PKCS#11/FIDO2/TPM2 logic to acquire the final secret key.</para>
+ and use this to determine which key to send, allowing a single listening socket to serve keys for
+ multiple volumes. If the PKCS#11 logic is used (see above), the socket source name is picked in similar
+ fashion, except that the literal string <literal>/cryptsetup-pkcs11/</literal> is used. And similarly for
+ FIDO2 (<literal>/cryptsetup-fido2/</literal>) and TPM2 (<literal>/cryptsetup-tpm2/</literal>). A diffent
+ path component is used so that services providing key material know that the secret key was not requested
+ directly, but instead an encrypted key that will be decrypted via the PKCS#11/FIDO2/TPM2 logic to acquire
+ the final secret key.</para>
</refsect1>
<refsect1>
<term><option>--image=<replaceable>IMAGE</replaceable></option></term>
<listitem><para>Takes a path to a disk image file or block device node. If specified,
- <command>journalctl</command> will operate on the file system in the indicated disk image. This is
- similar to <option>--root=</option> but operates on file systems stored in disk images or block
- devices, thus providing an easy way to extract log data from disk images. The disk image should
+ <command>journalctl</command> will operate on the file system in the indicated disk image. This
+ option is similar to <option>--root=</option>, but operates on file systems stored in disk images or
+ block devices, thus providing an easy way to extract log data from disk images. The disk image should
either contain just a file system or a set of file systems within a GPT partition table, following
the <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
Specification</ulink>. For further information on supported disk images, see
<varlistentry>
<term><option>with-unit</option></term>
- <listitem><para>similar to short-full, but prefixes the unit and user unit names instead of the
- traditional syslog identifier. Useful when using templated instances, as it will include the
- arguments in the unit names.</para></listitem>
+ <listitem><para>similar to <option>short-full</option>, but prefixes the unit and user unit names
+ instead of the traditional syslog identifier. Useful when using templated instances, as it will
+ include the arguments in the unit names.</para></listitem>
</varlistentry>
</variablelist></listitem>
</varlistentry>
<varlistentry>
<term><option>--smart-relinquish-var</option></term>
- <listitem><para>Similar to <option>--relinquish-var</option> but executes no operation if the root
- file system and <filename>/var/lib/journal/</filename> reside on the same mount point. This
- operation is used during system shutdown in order to make the journal daemon stop writing data to
- <filename>/var/log/journal/</filename> in case that directory is located on a mount point that
- needs to be unmounted.</para></listitem>
+ <listitem><para>Similar to <option>--relinquish-var</option>, but executes no operation if the root
+ file system and <filename>/var/lib/journal/</filename> reside on the same mount point. This operation
+ is used during system shutdown in order to make the journal daemon stop writing data to
+ <filename>/var/log/journal/</filename> in case that directory is located on a mount point that needs
+ to be unmounted.</para></listitem>
</varlistentry>
<varlistentry>
the machine name is specified as the empty string, or the
special machine name <literal>.host</literal> (see below) is
specified, the connection is made to the local host
- instead. This works similar to <command>login</command> but
+ instead. This works similarly to <command>login</command>, but
immediately invokes a user process. This command runs the
specified executable with the specified arguments, or the
default shell for the user if none is specified, or
<para>Note that <command>machinectl shell</command> does not propagate the exit code/status of the invoked
shell process. Use <command>systemd-run</command> instead if that information is required (see below).</para>
- <para>When using the <command>shell</command> command without
- arguments, (thus invoking the executed shell or command on the
- local host), it is in many ways similar to a <citerefentry
- project='die-net'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- session, but, unlike <command>su</command>, completely isolates
- the new session from the originating session, so that it
- shares no process or session properties, and is in a clean and
- well-defined state. It will be tracked in a new utmp, login,
- audit, security and keyring session, and will not inherit any
- environment variables or resource limits, among other
- properties.</para>
-
- <para>Note that <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- with its <option>--machine=</option> switch may be used in place of the <command>machinectl shell</command>
- command, and allows non-interactive operation, more detailed and low-level configuration of the invoked unit,
- as well as access to runtime and exit code/status information of the invoked shell process. In particular, use
- <command>systemd-run</command>'s <option>--wait</option> switch to propagate exit status information of the
- invoked process. Use <command>systemd-run</command>'s <option>--pty</option> switch for acquiring an
- interactive shell, similar to <command>machinectl shell</command>. In general, <command>systemd-run</command>
- is preferable for scripting purposes. However, note that <command>systemd-run</command> might require higher
- privileges than <command>machinectl shell</command>.</para></listitem>
+ <para>Using the <command>shell</command> command without arguments (thus invoking the executed shell
+ or command on the local host), is in many ways similar to a <citerefentry
+ project='die-net'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry> session,
+ but, unlike <command>su</command>, completely isolates the new session from the originating session,
+ so that it shares no process or session properties and is in a clean well-defined state. It will be
+ tracked in a new utmp, login, audit, security, and keyring sessions, and will not inherit any
+ environment variables or resource limits, among other properties.</para>
+
+ <para>Note that
+ <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry> with
+ its <option>--machine=</option> switch may be used in place of the <command>machinectl
+ shell</command> command, and allows non-interactive operation, more detailed and low-level
+ configuration of the invoked unit, as well as access to runtime and exit code/status information of
+ the invoked shell process. In particular, use <command>systemd-run</command>'s
+ <option>--wait</option> switch to propagate exit status information of the invoked process. Use
+ <command>systemd-run</command>'s <option>--pty</option> switch to acquire an interactive shell,
+ similarly to <command>machinectl shell</command>. In general, <command>systemd-run</command> is
+ preferable for scripting purposes. However, note that <command>systemd-run</command> might require
+ higher privileges than <command>machinectl shell</command>.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>enable</command> <replaceable>NAME</replaceable>…</term>
<term><command>disable</command> <replaceable>NAME</replaceable>…</term>
- <listitem><para>Enable or disable a container as a system
- service to start at system boot, using
+ <listitem><para>Enable or disable a container as a system service to start at system boot, using
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
- This enables or disables
- <filename>systemd-nspawn@.service</filename>, instantiated for
- the specified machine name, similar to the effect of
- <command>systemctl enable</command> or <command>systemctl
+ This enables or disables <filename>systemd-nspawn@.service</filename>, instantiated for the specified
+ machine name, similarly to the effect of <command>systemctl enable</command> or <command>systemctl
disable</command> on the service name.</para></listitem>
</varlistentry>
machine name. To omit creation of the local, writable copy
pass <literal>-</literal> as local machine name.</para>
- <para>Similar to the behavior of <command>pull-tar</command>,
- the read-only image is prefixed with
- <filename>.raw-</filename>, and thus not shown by
- <command>list-images</command>, unless <option>--all</option>
- is passed.</para>
+ <para>Similarly to the behavior of <command>pull-tar</command>, the read-only image is prefixed with
+ <filename>.raw-</filename>, and thus not shown by <command>list-images</command>, unless
+ <option>--all</option> is passed.</para>
<para>Note that pressing C-c during execution of this command
will not abort the download. Use
<term><command>import-fs</command> <replaceable>DIRECTORY</replaceable> [<replaceable>NAME</replaceable>]</term>
<listitem><para>Imports a container image stored in a local directory into
- <filename>/var/lib/machines/</filename>, operates similar to <command>import-tar</command> or
- <command>import-raw</command>, but the first argument is the source directory. If supported, this command will
- create btrfs snapshot or subvolume for the new image.</para></listitem>
+ <filename>/var/lib/machines/</filename>, operates similarly to <command>import-tar</command> or
+ <command>import-raw</command>, but the first argument is the source directory. If supported, this
+ command will create a btrfs snapshot or subvolume for the new image.</para></listitem>
</varlistentry>
<varlistentry>
specified file descriptor will be a tar file in case of <function>ExportTar()</function> or a raw disk
image in case of <function>ExportRaw()</function>. Note that currently raw disk images may not be
exported as tar files, and vice versa. This restriction might be lifted eventually. The method
- returns a transfer identifier and object path for cancelling or tracking the export operation, similar
+ returns a transfer identifier and object path for cancelling or tracking the export operation, similarly
to <function>ImportTar()</function> or <function>ImportRaw()</function> as described above.</para>
<para><function>PullTar()</function> and <function>PullRaw()</function> may be used to download, verify
and import a system image from a URL. They take an URL argument which should point to a tar or
raw file on the <literal>http://</literal> or <literal>https://</literal> protocols, possibly
compressed with xz, bzip2 or gzip. The second argument is a local name for the image. It should be
- suitable as a hostname, similar to the matching argument of the <function>ImportTar()</function> and
+ suitable as a hostname, similarly to the matching argument of the <function>ImportTar()</function> and
<function>ImportRaw()</function> methods above. The third argument indicates the verification mode for
the image. It may be one of <literal>no</literal>, <literal>checksum</literal>,
<literal>signature</literal>. <literal>no</literal> turns off any kind of verification of the image;
<refsect2>
<title>Methods</title>
- <para><function>Terminate()</function> and <function>ActivateSession()</function> work similar to
- TerminateSeat(), ActivationSessionOnSeat() on the Manager object.</para>
+ <para><function>Terminate()</function> and <function>ActivateSession()</function> work similarly to
+ <function>TerminateSeat()</function> and <function>ActivationSessionOnSeat()</function> on the Manager
+ object.</para>
<para><function>SwitchTo()</function> switches to the session on the virtual terminal
<varname>vtnr</varname>. <function>SwitchToNext()</function> and
encoded in a structure consisting of the ID and the object path.</para>
<para>The <varname>IdleHint</varname>, <varname>IdleSinceHint</varname>, and
- <varname>IdleSinceHintMonotonic</varname> properties encode the idle state, similar to the ones exposed
- on the <interfacename>Manager</interfacename> object, but specific for this seat.</para>
+ <varname>IdleSinceHintMonotonic</varname> properties encode the idle state, similarly to the ones
+ exposed on the <interfacename>Manager</interfacename> object, but specific for this seat.</para>
</refsect2>
</refsect1>
<refsect2>
<title>Methods</title>
- <para><function>Terminate()</function> and <function>Kill()</function> work similar to the
+ <para><function>Terminate()</function> and <function>Kill()</function> work similarly to the
<function>TerminateUser()</function> and <function>KillUser()</function> methods on the manager
object.</para>
</refsect2>
user. Each structure consists of the ID and object path.</para>
<para>The <varname>IdleHint</varname>, <varname>IdleSinceHint</varname>, and
- <varname>IdleSinceHintMonotonic</varname> properties encode the idle hint state of the user, similar to
- the <interfacename>Manager</interfacename>'s properties, but specific for this user.</para>
+ <varname>IdleSinceHintMonotonic</varname> properties encode the idle hint state of the user, similarly
+ to the <interfacename>Manager</interfacename>'s properties, but specific for this user.</para>
<para>The <varname>Linger</varname> property shows whether lingering is enabled for this user.</para>
</refsect2>
readonly s CollectMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly as Refs = ['...', ...];
+ readonly a(ss) ActivationDetails = [...];
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
<variablelist class="dbus-property" generated="True" extra-ref="Refs"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ActivationDetails"/>
+
<!--End of Autogenerated section-->
<refsect2>
<para><varname>Transient</varname> contains a boolean that indicates whether the unit was created as a
transient unit (i.e. via <function>CreateTransientUnit()</function> on the manager object).</para>
+
+ <para><varname>ActivationDetails</varname> contains a list of string pairs, key and value, that
+ describe the event that caused the unit to be activated, if any. The key describes the information
+ (e.g.: <varname>trigger_unit</varname>, with value <varname>foo.service</varname>). This is only filled
+ in if the unit was triggered by a <varname>Path</varname> or <varname>Timer</varname> unit, and it is
+ only provided in a best effort fashion: it is not guaranteed to be set, and it is not guaranteed to be
+ the only trigger. It is only guaranteed to be a valid trigger that caused the activation job to be
+ enqueued and complete successfully. The key value pairs correspond (in lowercase) to the environment
+ variables described in the <literal>Environment Variables Set on Triggered Units</literal> section in
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+ Note that new key value pair may be added at any time in future versions. Existing entries will not be
+ removed.</para>
</refsect2>
<refsect2>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s JobType = '...';
readonly s State = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly a(ss) ActivationDetails = [...];
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
<variablelist class="dbus-property" generated="True" extra-ref="State"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ActivationDetails"/>
+
<!--End of Autogenerated section-->
<refsect2>
<para><varname>State</varname> refers to the job's state and is one of <literal>waiting</literal> and
<literal>running</literal>. The former indicates that a job is currently queued but has not begun to
execute yet. The latter indicates that a job is currently being executed.</para>
+
+ <para><varname>ActivationDetails</varname> has the same content as the property of the same name under
+ the <varname>org.freedesktop.systemd1.Unit</varname> interface.</para>
</refsect2>
</refsect1>
<varlistentry>
<term><varname>PaddingWeight=</varname></term>
- <listitem><para>Similar to <varname>Weight=</varname> but sets a weight for the free space after the
+ <listitem><para>Similar to <varname>Weight=</varname>, but sets a weight for the free space after the
partition (the "padding"). When distributing available space the weights of all partitions and all
defined padding is summed, and then each partition and padding gets the fraction defined by its
weight. Defaults to 0, i.e. by default no padding is applied.</para>
<para>This option has no effect if the partition already exists.</para>
- <para>Similar to the behaviour of <varname>CopyBlocks=</varname> the file system is formatted before
- the partition is created, ensuring that the partition only ever exists with a fully initialized
- file system.</para>
+ <para>Similarly to the behaviour of <varname>CopyBlocks=</varname>, the file system is formatted
+ before the partition is created, ensuring that the partition only ever exists with a fully
+ initialized file system.</para>
<para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para></listitem>
</varlistentry>
synchronously when connected to a bus broker, i.e. the call sends a control message requested the match to be added
to the broker and waits until the broker confirms the match has been installed successfully.</para>
- <para><function>sd_bus_add_match_async()</function> operates very similar to
+ <para><function>sd_bus_add_match_async()</function> operates very similarly to
<function>sd_bus_add_match()</function>, however it installs the match asynchronously, in a non-blocking
fashion: a request is sent to the broker, but the call does not wait for a response. The
<parameter>install_callback</parameter> function is called when the response is later received, with the response
<title>Description</title>
<para><function>sd_bus_enqueue_for_read()</function> may be used to re-enqueue an incoming bus message on
- the local read queue, so that it is processed and dispatched locally again, similar to how an incoming
+ the local read queue, so that it is processed and dispatched locally again, similarly to how an incoming
message from the peer is processed. Takes a bus connection object and the message to enqueue. A reference
is taken of the message and the caller's reference thus remains in possession of the caller. The message
is enqueued at the end of the queue, thus will be dispatched after all other already queued messages are
<function>sd_peer_get_machine_name()</function>,
<function>sd_peer_get_slice()</function>,
<function>sd_peer_get_user_slice()</function> and
- <function>sd_peer_get_cgroup()</function> calls operate similar to
- their PID counterparts, but operate on a connected AF_UNIX socket
- and retrieve information about the connected peer process. Note
- that these fields are retrieved via <filename>/proc/</filename>,
- and hence are not suitable for authorization purposes, as they are
- subject to races.</para>
+ <function>sd_peer_get_cgroup()</function> calls operate similarly to their PID counterparts, but accept a
+ connected <constant>AF_UNIX</constant> socket and retrieve information about the connected peer process.
+ Note that these fields are retrieved via <filename>/proc/</filename>, and hence are not suitable for
+ authorization purposes, as they are subject to races.</para>
</refsect1>
<refsect1>
<replaceable>UNIT</replaceable>s or for the default target otherwise). The time after the unit is
active or started is printed after the "@" character. The time the unit takes to start is printed after
the "+" character. Note that the output might be misleading as the initialization of services might
- depend on socket activation and because of the parallel execution of units. Also, similar to the
+ depend on socket activation and because of the parallel execution of units. Also, similarly to the
<command>blame</command> command, this only takes into account the time units spent in
<literal>activating</literal> state, and hence does not cover units that never went through an
<literal>activating</literal> state (such as device units that transition directly from
<varlistentry>
<term><option>-n</option></term>
- <listitem><para>By default, when writing the acquired password to standard output it is suffixed by a
- newline character. This may be turned off with the <option>-n</option> switch, similar to the switch
- of the same name of the <citerefentry
+ <listitem><para>By default, when the acquired password is written to standard output it is suffixed
+ by a newline character. This may be turned off with the <option>-n</option> switch, similarly to the
+ switch of the same name of the <citerefentry
project='man-pages'><refentrytitle>echo</refentrytitle><manvolnum>1</manvolnum></citerefentry>
command.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--level-prefix=</option></term>
- <listitem><para>Controls whether lines read are parsed for
- syslog priority level prefixes. If enabled (the default), a
- line prefixed with a priority prefix such as
- <literal><5></literal> is logged at priority 5
- (<literal>notice</literal>), and similar for the other
- priority levels. Takes a boolean argument.</para></listitem>
+ <listitem><para>Controls whether lines read are parsed for syslog priority level prefixes. If enabled
+ (the default), a line prefixed with a priority prefix such as <literal><5></literal> is logged
+ at priority 5 (<literal>notice</literal>), and similarly for the other priority levels. Takes a
+ boolean argument.</para></listitem>
</varlistentry>
</variablelist>
<programlisting># ls | systemd-cat</programlisting>
</example>
- <para>Even though the two examples have very similar effects the
- first is preferable since only one process is running at a time,
- and both stdout and stderr are captured while in the second
- example, only stdout is captured.</para>
+ <para>Even though the two examples have very similar effects, the first is preferable, since only one
+ process is running at a time and both stdout and stderr are captured, while in the second example, only
+ stdout is captured.</para>
</refsect1>
<refsect1>
<para>If the <option>systemd.mask=</option> or <option>rd.systemd.mask=</option>
option is specified and followed by a unit name, this unit is
- masked for the runtime (i.e. for this session - from boot to shutdown), similar to the effect of
+ masked for the runtime (i.e. for this session — from boot to shutdown), similarly to the effect of
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>mask</command> command. This is useful to boot with
certain units removed from the initial boot transaction for
</thead>
<tbody>
<row>
- <entry valign="top" morerows="14">VM</entry>
+ <entry valign="top" morerows="15">VM</entry>
<entry><varname>qemu</varname></entry>
<entry>QEMU software virtualization, without KVM</entry>
</row>
<entry><ulink url="https://projectacrn.org">ACRN hypervisor</ulink></entry>
</row>
+ <row>
+ <entry><varname>apple</varname></entry>
+ <entry><ulink url="https://developer.apple.com/documentation/virtualization">Apple Virtualization.framework</ulink></entry>
+ </row>
+
<row>
<entry valign="top" morerows="9">Container</entry>
<entry><varname>openvz</varname></entry>
<listitem><para>Set a unit property on the scope unit to register for the machine. This applies only if the
machine is run in its own scope unit, i.e. if <option>--keep-unit</option> isn't used. Takes unit property
assignments in the same format as <command>systemctl set-property</command>. This is useful to set memory
- limits and similar for container.</para>
+ limits and similar for the container.</para>
</listitem>
</varlistentry>
the UID/GID range is automatically chosen. As first step, the file owner UID/GID of the root
directory of the container's directory tree is read, and it is checked that no other container is
currently using it. If this check is successful, the UID/GID range determined this way is used,
- similar to the behavior if <literal>yes</literal> is specified. If the check is not successful (and
- thus the UID/GID range indicated in the root directory's file owner is already used elsewhere) a
- new – currently unused – UID/GID range of 65536 UIDs/GIDs is randomly chosen between the host
+ similarly to the behavior if <literal>yes</literal> is specified. If the check is not successful
+ (and thus the UID/GID range indicated in the root directory's file owner is already used elsewhere)
+ a new – currently unused – UID/GID range of 65536 UIDs/GIDs is randomly chosen between the host
UID/GIDs of 524288 and 1878982656, always starting at a multiple of 65536, and, if possible,
consistently hashed from the machine name. This setting implies
<option>--private-users-ownership=auto</option> (see below), which possibly has the effect that the
<para>If set to <literal>copy-host</literal>, the <filename>/etc/resolv.conf</filename> file from the
host is copied into the container, unless the file exists already and is not a regular file (e.g. a
- symlink). Similar, if <literal>replace-host</literal> is used the file is copied, replacing any
- existing inode, including symlinks. Similar, if <literal>bind-host</literal> is used, the file is
+ symlink). Similarly, if <literal>replace-host</literal> is used the file is copied, replacing any
+ existing inode, including symlinks. Similarly, if <literal>bind-host</literal> is used, the file is
bind mounted from the host into the container.</para>
<para>If set to <literal>copy-static</literal>, <literal>replace-static</literal> or
command has begun execution (unless <option>--no-block</option> or <option>--wait</option> are specified, see
below).</para>
- <para>If a command is run as transient scope unit, it will be executed by <command>systemd-run</command> itself as
- parent process and will thus inherit the execution environment of the caller. However, the processes of the command
- are managed by the service manager similar to normal services, and will show up in the output of <command>systemctl
- list-units</command>. Execution in this case is synchronous, and will return only when the command finishes. This
- mode is enabled via the <option>--scope</option> switch (see below). </para>
+ <para>If a command is run as transient scope unit, it will be executed by <command>systemd-run</command>
+ itself as parent process and will thus inherit the execution environment of the caller. However, the
+ processes of the command are managed by the service manager similarly to normal services, and will show
+ up in the output of <command>systemctl list-units</command>. Execution in this case is synchronous, and
+ will return only when the command finishes. This mode is enabled via the <option>--scope</option> switch
+ (see below). </para>
<para>If a command is run with path, socket, or timer options such as <option>--on-calendar=</option> (see below),
a transient path, socket, or timer unit is created alongside the service unit for the specified command. Only the
<term><option>--same-dir</option></term>
<term><option>-d</option></term>
- <listitem><para>Similar to <option>--working-directory=</option> but uses the current working directory of the
- caller for the service to execute.</para></listitem>
+ <listitem><para>Similar to <option>--working-directory=</option>, but uses the current working
+ directory of the caller for the service to execute.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--socket-property=</option></term>
<term><option>--timer-property=</option></term>
- <listitem><para>Sets a property on the path, socket, or timer unit that is created. This option is similar to
- <option>--property=</option> but applies to the transient path, socket, or timer unit rather than the
- transient service unit created. This option takes an assignment in the same format as
+ <listitem><para>Sets a property on the path, socket, or timer unit that is created. This option is
+ similar to <option>--property=</option>, but applies to the transient path, socket, or timer unit
+ rather than the transient service unit created. This option takes an assignment in the same format as
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>set-property</command> command. These options may not be combined with
<option>--scope</option> or <option>--pty</option>.</para>
affecting the process' ability to operate. Note that many of these sandboxing features are gracefully turned off on
systems where the underlying security mechanism is not available. For example, <varname>ProtectSystem=</varname>
has no effect if the kernel is built without file system namespacing or if the service manager runs in a container
- manager that makes file system namespacing unavailable to its payload. Similar,
+ manager that makes file system namespacing unavailable to its payload. Similarly,
<varname>RestrictRealtime=</varname> has no effect on systems that lack support for SECCOMP system call filtering,
or in containers where support for this is turned off.</para>
<varlistentry>
<term><varname>EnvironmentFile=</varname></term>
- <listitem><para>Similar to <varname>Environment=</varname> but reads the environment variables from a text file.
- The text file should contain newline-separated variable assignments. Empty lines, lines without an
- <literal>=</literal> separator, or lines starting with <literal>;</literal> or <literal>#</literal> will be
- ignored, which may be used for commenting. The file must be UTF-8 encoded. Valid characters are <ulink
- url="https://www.unicode.org/glossary/#unicode_scalar_value">unicode scalar values</ulink> other than <ulink
- url="https://www.unicode.org/glossary/#noncharacter">noncharacters</ulink>, U+0000 NUL, and U+FEFF <ulink
- url="https://www.unicode.org/glossary/#byte_order_mark">byte order mark</ulink>. Control codes other than NUL
- are allowed.</para>
+ <listitem><para>Similar to <varname>Environment=</varname>, but reads the environment variables from
+ a text file. The text file should contain newline-separated variable assignments. Empty lines, lines
+ without an <literal>=</literal> separator, or lines starting with <literal>;</literal> or
+ <literal>#</literal> will be ignored, which may be used for commenting. The file must be UTF-8
+ encoded. Valid characters are <ulink
+ url="https://www.unicode.org/glossary/#unicode_scalar_value">unicode scalar values</ulink> other than
+ <ulink url="https://www.unicode.org/glossary/#noncharacter">noncharacters</ulink>, U+0000 NUL, and
+ U+FEFF <ulink url="https://www.unicode.org/glossary/#byte_order_mark">byte order mark</ulink>.
+ Control codes other than NUL are allowed.</para>
<para>In the file, an unquoted value after the <literal>=</literal> is parsed with the same backslash-escape
rules as <ulink
<para>Internally, journal namespaces are implemented through Linux mount namespacing and
over-mounting the directory that contains the relevant <constant>AF_UNIX</constant> sockets used for
logging in the unit's mount namespace. Since mount namespaces are used this setting disconnects
- propagation of mounts from the unit's processes to the host, similar to how
- <varname>ReadOnlyPaths=</varname> and similar settings (see above) work. Journal namespaces may hence
+ propagation of mounts from the unit's processes to the host, similarly to how
+ <varname>ReadOnlyPaths=</varname> and similar settings describe above work. Journal namespaces may hence
not be used for services that need to establish mount points on the host.</para>
<para>When this option is used the unit will automatically gain ordering and requirement dependencies
<listitem><para>The PID of the unit's main process if it is
known. This is only set for control processes as invoked by
- <varname>ExecReload=</varname> and similar. </para></listitem>
+ <varname>ExecReload=</varname> and similar.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>$MANAGERPID</varname></term>
<listitem><para>The PID of the user <command>systemd</command>
- instance, set for processes spawned by it. </para></listitem>
+ instance, set for processes spawned by it.</para></listitem>
</varlistentry>
<varlistentry>
<listitem><para>The PID of the unit process (e.g. process invoked by
<varname>ExecStart=</varname>). The child process can use this information to determine
whether the process is directly invoked by the service manager or indirectly as a child of
- another process by comparing this value with the current PID (as similar to the scheme used in
+ another process by comparing this value with the current PID (similarly to the scheme used in
<citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
with <varname>$LISTEN_PID</varname> and <varname>$LISTEN_FDS</varname>).</para></listitem>
</varlistentry>
system.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>$TRIGGER_UNIT</varname></term>
+ <term><varname>$TRIGGER_PATH</varname></term>
+ <term><varname>$TRIGGER_TIMER_REALTIME_USEC</varname></term>
+ <term><varname>$TRIGGER_TIMER_MONOTONIC_USEC</varname></term>
+
+ <listitem><para>If the unit was activated dynamically (e.g.: a corresponding path unit or timer unit), the
+ unit that triggered it and other type-dependent information will be passed via these variables. Note that
+ this information is provided in a best-effort way. For example, multiple triggers happening one after
+ another will be coalesced and only one will be reported, with no guarantee as to which one it will be.
+ Because of this, in most cases this variable will be primarily informational, i.e. useful for debugging
+ purposes, is lossy, and should not be relied upon to propagate a comprehensive reason for activation.
+ </para></listitem>
+ </varlistentry>
+
</variablelist>
<para>For system services, when <varname>PAMName=</varname> is enabled and <command>pam_systemd</command> is part
for details about journal namespaces.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>_SYSTEM_CONTEXT=</varname></term>
+
+ <listitem><para>A string field that specifies the context in which the message was logged. If
+ <literal>initrd</literal>, the log message was processed while systemd-journald
+ was running inside the initrd. If <literal>main</literal>, the log message was generated after
+ journald switched root to the root filesystem.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<varlistentry>
<term><varname>UseDomains=</varname></term>
<listitem>
- <para>Takes a boolean, or the special value <option>route</option>. When true, the domain
- name received from the DHCP server will be used as DNS search domain over this link, similar
- to the 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, similar to the effect of the <option>Domains=</option> setting when the
- argument is prefixed with <literal>~</literal>. Defaults to false.</para>
+ <para>Takes a boolean, or the special value <option>route</option>. When true, the domain name
+ received from the DHCP server will be used as DNS search domain over this link, similarly to the
+ 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>. Defaults to false.</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
<term><varname>UseDomains=</varname></term>
<listitem>
<para>Takes a boolean, or the special value <literal>route</literal>. When true, the domain name
- received via IPv6 Router Advertisement (RA) will be used as DNS search domain over this link, similar to
- the effect of the <option>Domains=</option> setting. If set to <literal>route</literal>, the domain name
- received via IPv6 RA will be used for routing DNS queries only, but not for searching, similar to the
- effect of the <option>Domains=</option> setting when the argument is prefixed with
- <literal>~</literal>. Defaults to false.</para>
+ received via IPv6 Router Advertisement (RA) will be used as DNS search domain over this link,
+ similarly to the effect of the <option>Domains=</option> setting. If set to
+ <literal>route</literal>, the domain name received via IPv6 RA 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>. Defaults to false.</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 safer to use the supplied domain
<varlistentry>
<term><varname>Prefix=</varname></term>
- <listitem><para>The IPv6 prefix that is to be distributed to hosts. Similarly to configuring static
+ <listitem><para>The IPv6 prefix that is to be distributed to hosts. Similarly to configuring static
IPv6 addresses, the setting is configured as an IPv6 prefix and its prefix length, separated by a
<literal>/</literal> character. Use multiple [IPv6Prefix] sections to configure multiple IPv6
prefixes since prefix lifetimes, address autoconfiguration and onlink status may differ from one
<varlistentry>
<term><varname>Route=</varname></term>
- <listitem><para>The IPv6 route that is to be distributed to hosts. Similarly to configuring static
+ <listitem><para>The IPv6 route that is to be distributed to hosts. Similarly to configuring static
IPv6 routes, the setting is configured as an IPv6 prefix routes and its prefix route length,
separated by a <literal>/</literal> character. Use multiple [IPv6RoutePrefix] sections to configure
multiple IPv6 prefix routes.</para></listitem>
<varname>PathExists=</varname> may be used to watch the mere
existence of a file or directory. If the file specified
exists, the configured unit is activated.
- <varname>PathExistsGlob=</varname> works similar, but checks
+ <varname>PathExistsGlob=</varname> works similarly, but checks
for the existence of at least one file matching the globbing
pattern specified. <varname>PathChanged=</varname> may be used
to watch a file or directory and activate the configured unit
<refsect1>
<title>See Also</title>
+ <para>Environment variables with details on the trigger will be set for triggered units. See the
+ <literal>Environment Variables Set on Triggered Units</literal> section in
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ for more details.</para>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details.</para>
- <para>This setting also applies to <command>systemd-oomd</command>, similar to the kernel OOM kills
- this setting determines the state of the service after <command>systemd-oomd</command> kills a cgroup
- associated with the service.</para></listitem>
+ <para>This setting also applies to <command>systemd-oomd</command>. Similarly to the kernel OOM
+ kills, this setting determines the state of the service after <command>systemd-oomd</command> kills a
+ cgroup associated with the service.</para></listitem>
</varlistentry>
</variablelist>
<refsect1>
<title>See Also</title>
+ <para>Environment variables with details on the trigger will be set for triggered units. See the
+ <literal>Environment Variables Set on Triggered Units</literal> section in
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ for more details.</para>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<literal>uml</literal>,
<literal>bhyve</literal>,
<literal>qnx</literal>,
+ <literal>apple</literal>,
<literal>openvz</literal>,
<literal>lxc</literal>,
<literal>lxc-libvirt</literal>,
fi
fi
+# The bpftool script shipped by Ubuntu tries to find the actual program to run via querying `uname -r` and
+# using the current kernel version. This obviously doesn't work in containers. As a workaround, we override
+# the ubuntu script with a symlink to the first bpftool program we can find.
+for bpftool in /usr/lib/linux-tools/*/bpftool; do
+ [ -x "$bpftool" ] || continue
+ ln -sf "$bpftool" /usr/sbin/bpftool
+ break
+done
+
if [ ! -f "$BUILDDIR"/build.ninja ] ; then
sysvinit_path=$(realpath /etc/init.d)
-D version-tag="${VERSION_TAG}" \
-D mode=developer \
-D b_sanitize="${SANITIZERS:-none}" \
- -D install-tests=true
+ -D install-tests=true \
+ -D tests=unsafe \
+ -D slow-tests=true \
+ -D utmp=true \
+ -D hibernate=true \
+ -D ldconfig=true \
+ -D resolve=true \
+ -D efi=true \
+ -D tpm=true \
+ -D environment-d=true \
+ -D binfmt=true \
+ -D repart=true \
+ -D sysupdate=true \
+ -D coredump=true \
+ -D pstore=true \
+ -D oomd=true \
+ -D logind=true \
+ -D hostnamed=true \
+ -D localed=true \
+ -D machined=true \
+ -D portabled=true \
+ -D sysext=true \
+ -D userdb=true \
+ -D homed=true \
+ -D networkd=true \
+ -D timedated=true \
+ -D timesyncd=true \
+ -D remote=true \
+ -D nss-myhostname=true \
+ -D nss-mymachines=true \
+ -D nss-resolve=true \
+ -D nss-systemd=true \
+ -D firstboot=true \
+ -D randomseed=true \
+ -D backlight=true \
+ -D vconsole=true \
+ -D quotacheck=true \
+ -D sysusers=true \
+ -D tmpfiles=true \
+ -D importd=true \
+ -D hwdb=true \
+ -D rfkill=true \
+ -D xdg-autostart=true \
+ -D translations=true \
+ -D polkit=true \
+ -D acl=true \
+ -D audit=true \
+ -D blkid=true \
+ -D fdisk=true \
+ -D kmod=true \
+ -D pam=true \
+ -D pwquality=true \
+ -D microhttpd=true \
+ -D libcryptsetup=true \
+ -D libcurl=true \
+ -D idn=true \
+ -D libidn2=true \
+ -D qrencode=true \
+ -D gcrypt=true \
+ -D gnutls=true \
+ -D openssl=true \
+ -D cryptolib=openssl \
+ -D p11kit=true \
+ -D libfido2=true \
+ -D tpm2=true \
+ -D elfutils=true \
+ -D zstd=true \
+ -D xkbcommon=true \
+ -D pcre2=true \
+ -D glib=true \
+ -D dbus=true \
+ -D gnu-efi=true \
+ -D kernel-install=true \
+ -D analyze=true \
+ -D bpf-framework=true
fi
cd "$BUILDDIR"
CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG
EOF
fi
+
+# Make sure services aren't enabled by default on Debian/Ubuntu.
+mkdir -p "$DESTDIR/etc/systemd/system-preset"
+echo "disable *" > "$DESTDIR/etc/systemd/system-preset/99-mkosi.preset"
BuildDirectory=mkosi.builddir
Cache=mkosi.cache
SourceFileTransferFinal=copy-git-others
+Packages=
+ acl
+ bash-completion
+ coreutils
+ diffutils
+ dnsmasq
+ findutils
+ gcc # For sanitizer libraries
+ gdb
+ grep
+ kbd
+ kexec-tools
+ kmod
+ less
+ nano
+ nftables
+ openssl
+ python3
+ qrencode
+ sed
+ strace
+ tree
+ util-linux
+ valgrind
+ wireguard-tools
+ zsh
+
+BuildPackages=
+ clang
+ gcc
+ gettext
+ git
+ gnu-efi
+ gperf
+ llvm
+ meson
+ pkgconf
+ rpm
+ rsync
+ zstd
[Host]
QemuHeadless=yes
Distribution=arch
[Content]
-BuildPackages=
- acl
- bzip2
- clang
- cryptsetup
- curl
- dbus
- diffutils
- docbook-xsl
- elfutils
- gcc
- git
- gnu-efi-libs
+Packages=
+ compsize
+ dhcp
gnutls
- gperf
- inetutils
- iptables
- kmod
+ iproute
libbpf
- libcap
- libgcrypt
- libidn2
+ libfido2
libmicrohttpd
- libseccomp
- libutil-linux
+ libpwquality
libxkbcommon
- libxslt
- llvm
- lz4
- meson
- pam
- pkgconfig
- python
- python-lxml
- python-jinja
- qrencode
- rsync
- xz
- zstd
-
-Packages=
- gdb
- libbpf
- libidn2
- nano
- qrencode
- strace
- # For testing "systemd-analyze verify".
man-db
- # For testing systemd's bash completion scripts.
- bash-completion
- # For testing systemd's zsh completion scripts
- # Run `autoload -Uz compinit; compinit` from a zsh shell in the booted image to enable completions.
- zsh
- # xxd is provided by the vim package
+ openbsd-netcat
+ polkit
+ quota-tools
+ tpm2-tss
vim
- # Required to run systemd-networkd-tests.py
- python
- iproute
- dnsmasq
- wireguard-tools
- dhcp
+
+BuildPackages=
+ bpf
+ docbook-xsl
+ libxslt
+ linux-api-headers
+ perl
+ python-jinja
+ python-lxml
HostonlyInitrd=no
[Content]
-BuildPackages=
- diffutils
- docbook-style-xsl
- findutils
- gcc
- gettext
- git
+Packages=
+ audit
+ cryptsetup
+ dhcp-server
+ glib2
glibc-minimal-langpack
- gnu-efi
+ gnutls
+ iproute
+ iproute-tc
+ kernel-modules-extra
+ libbpf
+ libfido2
+ libmicrohttpd
+ libxcrypt
+ libxkbcommon
+ netcat
+ p11-kit
+ pam
+ polkit
+ procps-ng
+ quota
+ tpm2-tss
+ vim-common
+
+BuildPackages=
+ bpftool
+ docbook-xsl
gnu-efi-devel
- gperf
- lz4
- meson
- ninja-build
+ libgcrypt-devel # CentOS Stream 8 libgcrypt-devel doesn't ship a pkg-config file.
+ libxslt
pam-devel
- # CentOS Stream 8 libgcrypt-devel doesn't ship a pkg-config file.
- libgcrypt-devel
- pkgconfig
+ perl-interpreter
pkgconfig(audit)
pkgconfig(blkid)
pkgconfig(bzip2)
pkgconfig(dbus-1)
pkgconfig(fdisk)
+ pkgconfig(glib-2.0)
pkgconfig(gnutls)
pkgconfig(libacl)
+ pkgconfig(libbpf)
pkgconfig(libcap)
pkgconfig(libcryptsetup)
pkgconfig(libcurl)
pkgconfig(libdw)
+ pkgconfig(libfido2)
pkgconfig(libidn2)
pkgconfig(libkmod)
- pkgconfig(liblz4)
- pkgconfig(liblzma)
pkgconfig(libmicrohttpd)
pkgconfig(libpcre2-8)
pkgconfig(libqrencode)
pkgconfig(xkbcommon)
python3dist(jinja2)
python3dist(lxml)
- rpm
- tree
- zstd
- /usr/bin/xsltproc
-
-Packages=
- gdb
- nano
- # procps-ng provides a set of useful utilities (ps, free, etc)
- procps-ng
- strace
- tpm2-tss
- less
- netcat
- e2fsprogs
- # xxd is provided by the vim-common package
- vim-common
- libasan
- libubsan
- # Required to run systemd-networkd-tests.py
- python3
- iproute
- iproute-tc
- dnsmasq
- wireguard-tools
- dhcp-server
- kernel-modules-extra
Release=testing
[Content]
+Packages=
+ cryptsetup-bin
+ iproute2
+ isc-dhcp-server
+ libbpf0
+ libfido2-1
+ libglib2.0-0
+ libgnutls30
+ libidn2-0
+ libmicrohttpd12
+ libp11-kit0
+ libpam0g
+ libpwquality1
+ libqrencode4
+ libtss2-dev # Use the -dev package to avoid churn in updating version numbers
+ netcat-openbsd
+ policykit-1
+ procps
+ quota
+ xxd
+
BuildPackages=
- acl
- clang
- docbook-xml
+ bpftool
docbook-xsl
- gcc
g++
- gettext
- git
- gnu-efi
- gperf
libacl1-dev
libaudit-dev
libblkid-dev
libbz2-dev
libcap-dev
libcryptsetup-dev
- libcurl4-gnutls-dev
+ libcurl4-openssl-dev
libdbus-1-dev
libdw-dev
libfdisk-dev
libfido2-dev
libgcrypt20-dev
+ libglib2.0-dev
libgnutls28-dev
- libidn2-0-dev
+ libidn2-dev
libiptc-dev
libkmod-dev
- liblz4-dev
- liblz4-tool
- liblzma-dev
libmicrohttpd-dev
libmount-dev
+ libp11-kit-dev
libpam0g-dev
+ libpwquality-dev
libqrencode-dev
libseccomp-dev
libsmartcols-dev
libssl-dev
- libtss2-dev
libxkbcommon-dev
libzstd-dev
- llvm
- meson
- pkg-config
- python3
- python3-lxml
python3-jinja2
- tree
- uuid-dev
+ python3-lxml
xsltproc
- xz-utils
- zstd
-
-Packages=
- gdb
- libbpf0
- libfdisk1
- libfido2-1
- libidn2-0
- libqrencode4
- # We pull in the -dev package here, since the binary ones appear to change names too often, and the -dev package pulls the right deps in automatically
- libtss2-dev
- locales
- nano
- strace
- xxd
- # Provides libasan/libubsan
- gcc
- # Required to run systemd-networkd-tests.py
- python3
- iproute2
- dnsmasq-base
- wireguard-tools
- isc-dhcp-server
Release=36
[Content]
-BuildPackages=
- diffutils
- docbook-style-xsl
- findutils
- gcc
- gettext
- git
+Packages=
+ compsize
+ cryptsetup
+ dhcp-server
+ glib2
glibc-minimal-langpack
- gnu-efi
+ gnutls
+ iproute
+ iproute-tc
+ kernel-modules-extra
+ libbpf
+ libfido2
+ libmicrohttpd
+ libxcrypt
+ libxkbcommon
+ netcat
+ pam
+ polkit
+ procps-ng
+ quota
+ tpm2-tss
+ vim-common
+
+BuildPackages=
+ bpftool
+ docbook-xsl
gnu-efi-devel
- gperf
- lz4
- meson
- ninja-build
pam-devel
- pkgconfig
+ pkgconfig # pkgconf shim to provide /usr/bin/pkg-config
pkgconfig(audit)
pkgconfig(blkid)
- pkgconfig(bzip2)
pkgconfig(dbus-1)
pkgconfig(fdisk)
- pkgconfig(gnutls)
+ pkgconfig(glib-2.0)
pkgconfig(libacl)
+ pkgconfig(libbpf)
pkgconfig(libcap)
pkgconfig(libcryptsetup)
pkgconfig(libcurl)
pkgconfig(libgcrypt)
pkgconfig(libidn2)
pkgconfig(libkmod)
- pkgconfig(liblz4)
- pkgconfig(liblzma)
pkgconfig(libmicrohttpd)
pkgconfig(libpcre2-8)
pkgconfig(libqrencode)
pkgconfig(xkbcommon)
python3dist(jinja2)
python3dist(lxml)
- rpm
- tree
- zstd
- /usr/bin/xsltproc
-
-Packages=
- acl
- gdb
- nano
- # procps-ng provides a set of useful utilities (ps, free, etc)
- procps-ng
- strace
- tpm2-tss
- less
- netcat
- e2fsprogs
- compsize
- # xxd is provided by the vim-common package
- vim-common
- # Sanitizers
- libasan
- libubsan
- # Required to run systemd-networkd-tests.py
- python
- iproute
- iproute-tc
- dnsmasq
- wireguard-tools
- dhcp-server
- kernel-modules-extra
Release=tumbleweed
[Content]
+Packages=
+ dbus-1
+ glibc-locale-base
+ libbpf0
+ libcrypt1
+ libcryptsetup12
+ libdw1
+ libelf1
+ libfido2
+ libgcrypt20
+ libglib-2_0-0
+ libkmod2
+ liblz4-1
+ libmount1
+ libp11-kit0
+ libqrencode4
+ libseccomp2
+ libxkbcommon0
+ pam
+ tpm2-0-tss
+ vim
+
BuildPackages=
+ audit-devel
+ bpftool
+ dbus-1-devel
docbook-xsl-stylesheets
- fdupes
- gcc
- gnu-efi
- gperf
- intltool
+ glib2-devel
+ glibc-locale
libacl-devel
- libapparmor-devel
libblkid-devel
- libbz2-devel
+ libbpf-devel
libcap-devel
libcryptsetup-devel
libcurl-devel
+ libdw-devel
+ libelf-devel
+ libfdisk-devel
+ libfido2-devel
libgcrypt-devel
libgnutls-devel
libkmod-devel
- liblz4-devel
libmicrohttpd-devel
libmount-devel
+ libpwquality-devel
libseccomp-devel
libselinux-devel
+ libxkbcommon-devel
libxslt-tools
- meson
+ openssl-devel
pam-devel
- pciutils-devel
pcre-devel
- python3
python3-Jinja2
python3-lxml
qrencode-devel
- shadow
- system-user-nobody
- systemd-sysvinit
- zlib-devel
-# to satisfy tests
- acl
- diffutils
- glibc-locale
- system-group-obsolete
- system-user-bin
- system-user-daemon
- system-user-root
- timezone
-
-Packages=
- gdb
- # brought in via meson->python3
- libp11-kit0
- # --bootable=no
- dbus-1
- libapparmor1
- libcrypt1
- libcryptsetup12
- libgcrypt20
- libgnutls30
- libkmod2
- liblz4-1
- libmount1
- libqrencode4
- libseccomp2
- pam
- nano
- strace
- util-linux
- # xxd is provided by the vim package
- vim
- # Provides libasan/libubsan
- gcc
+ tpm2-0-tss-devel
Repositories=main,universe
[Content]
+Packages=
+ cryptsetup-bin
+ iproute2
+ isc-dhcp-server
+ libbpf0
+ libfdisk1
+ libfido2-1
+ libglib2.0-0
+ libidn2-0
+ libmicrohttpd12
+ libp11-kit0
+ libpwquality1
+ libqrencode4
+ libtss2-dev # Use the -dev package to avoid churn in updating version numbers
+ linux-tools-common
+ linux-tools-generic
+ netcat-openbsd
+ policykit-1
+ procps
+ quota
+ xxd
+
BuildPackages=
- acl
- docbook-xml
docbook-xsl
- gcc
- gettext
- git
- gnu-efi
- gperf
+ g++
libacl1-dev
libaudit-dev
libblkid-dev
+ libbpf-dev
libbz2-dev
libcap-dev
libcryptsetup-dev
- libcurl4-gnutls-dev
+ libcurl4-openssl-dev
libdbus-1-dev
libdw-dev
libfdisk-dev
libfido2-dev
libgcrypt20-dev
+ libglib2.0-dev
libgnutls28-dev
- libidn2-0-dev
- libip4tc-dev
- libip6tc-dev
+ libidn2-dev
+ libiptc-dev
libkmod-dev
- liblz4-dev
- liblz4-tool
- liblzma-dev
libmicrohttpd-dev
libmount-dev
+ libp11-kit-dev
libpam0g-dev
+ libpwquality-dev
libqrencode-dev
libseccomp-dev
libsmartcols-dev
libssl-dev
- libtss2-dev
libxkbcommon-dev
- libxtables-dev
libzstd-dev
- meson
- pkg-config
- python3
- python3-lxml
python3-jinja2
- tree
- tzdata
- uuid-dev
+ python3-lxml
xsltproc
- xz-utils
- zstd
-
-Packages=
- gdb
- libfido2-1
- libidn2-0
- libqrencode4
- # We pull in the -dev package here, since the binary ones appear to change names too often, and the -dev package pulls the right deps in automatically
- libtss2-dev
- libfdisk1
- locales
- nano
- strace
- xxd
- # Provides libasan/libubsan
- gcc
- # Required to run systemd-networkd-tests.py
- python3
- iproute2
- dnsmasq-base
- wireguard-tools
- isc-dhcp-server
# `systemd-hwdb update` takes > 50s when built with sanitizers so let's not run it by default.
systemctl mask systemd-hwdb-update.service
fi
+
+ # Make sure dnsmasq.service doesn't start on boot on Debian/Ubuntu.
+ rm -f /etc/systemd/system/multi-user.target.wants/dnsmasq.service
fi
# Temporary workaround until https://github.com/openSUSE/suse-module-tools/commit/158643414ddb8d8208016a5f03a4484d58944d7a
return r;
}
- dir = dirname_malloc(abspath);
- if (!dir)
- return -ENOMEM;
+ r = path_extract_directory(abspath, &dir);
+ if (r < 0)
+ return r;
c = path_join(dir, with_instance ?: name);
if (!c)
return 0;
}
-int verify_generate_path(char **var, char **filenames) {
+int verify_generate_path(char **ret, char **filenames) {
_cleanup_strv_free_ char **ans = NULL;
+ _cleanup_free_ char *joined = NULL;
const char *old;
int r;
STRV_FOREACH(filename, filenames) {
+ _cleanup_free_ char *a = NULL;
char *t;
- t = dirname_malloc(*filename);
- if (!t)
- return -ENOMEM;
+ r = path_make_absolute_cwd(*filename, &a);
+ if (r < 0)
+ return r;
+
+ r = path_extract_directory(a, &t);
+ if (r < 0)
+ return r;
r = strv_consume(&ans, t);
if (r < 0)
return r;
}
- assert_se(strv_uniq(ans));
+ strv_uniq(ans);
/* First, prepend our directories. Second, if some path was specified, use that, and
* otherwise use the defaults. Any duplicates will be filtered out in path-lookup.c.
return r;
}
- *var = strv_join(ans, ":");
- if (!*var)
+ joined = strv_join(ans, ":");
+ if (!joined)
return -ENOMEM;
+ *ret = TAKE_PTR(joined);
return 0;
}
return 0;
}
-int glob_exists(const char *path) {
+int glob_first(const char *path, char **ret_first) {
_cleanup_globfree_ glob_t g = {};
int k;
assert(path);
k = safe_glob(path, GLOB_NOSORT|GLOB_BRACE, &g);
- if (k == -ENOENT)
+ if (k == -ENOENT) {
+ if (ret_first)
+ *ret_first = NULL;
return false;
+ }
if (k < 0)
return k;
+
+ if (ret_first) {
+ char *first = strdup(g.gl_pathv[0]);
+ if (!first)
+ return log_oom_debug();
+ *ret_first = first;
+ }
+
return true;
}
/* Note: this function modifies pglob to set various functions. */
int safe_glob(const char *path, int flags, glob_t *pglob);
-int glob_exists(const char *path);
+/* Note: which match is returned depends on the implementation/system and not guaranteed to be stable */
+int glob_first(const char *path, char **ret_first);
+#define glob_exists(path) glob_first(path, NULL)
int glob_extend(char ***strv, const char *path, int flags);
int glob_non_glob_prefix(const char *path, char **ret);
}
int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m) {
- _cleanup_free_ char *pp = NULL;
+ _cleanup_free_ char *pp = NULL, *bn = NULL;
_cleanup_close_ int dfd = -1;
- const char *bn;
int r;
- pp = dirname_malloc(p);
- if (!pp)
- return -ENOMEM;
-
- /* Not top-level? */
- if (!(path_equal(pp, "/") || isempty(pp) || path_equal(pp, "."))) {
-
- /* Recurse up */
+ r = path_extract_directory(p, &pp);
+ if (r == -EDESTADDRREQ) {
+ /* only fname is passed, no prefix to operate on */
+ dfd = open(".", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (dfd < 0)
+ return -errno;
+ } else if (r == -EADDRNOTAVAIL)
+ /* only root dir or "." was passed, i.e. there is no parent to extract, in that case there's nothing to do. */
+ return 0;
+ else if (r < 0)
+ return r;
+ else {
+ /* Extracting the parent dir worked, hence we aren't top-level? Recurse up first. */
r = mkdir_p_root(root, pp, uid, gid, m);
if (r < 0)
return r;
+
+ dfd = chase_symlinks_and_open(pp, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_DIRECTORY, NULL);
+ if (dfd < 0)
+ return dfd;
}
- bn = basename(p);
- if (path_equal(bn, "/") || isempty(bn) || path_equal(bn, "."))
+ r = path_extract_filename(p, &bn);
+ if (r == -EADDRNOTAVAIL) /* Already top-level */
return 0;
-
- if (!filename_is_valid(bn))
- return -EINVAL;
-
- dfd = chase_symlinks_and_open(pp, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_DIRECTORY, NULL);
- if (dfd < 0)
- return dfd;
+ if (r < 0)
+ return r;
if (mkdirat(dfd, bn, m) < 0) {
if (errno == EEXIST)
if (uid_is_valid(uid) || gid_is_valid(gid)) {
_cleanup_close_ int nfd = -1;
- nfd = openat(dfd, bn, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ nfd = openat(dfd, bn, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
if (nfd < 0)
return -errno;
#include <stdlib.h>
#include <unistd.h>
-/* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the
- * POSIX version which is really broken. We prefer GNU basename(). */
-#include <libgen.h>
-#undef basename
-
#include "alloc-util.h"
#include "chase-symlinks.h"
#include "extract-word.h"
return executable_is_good(checker);
}
-char* dirname_malloc(const char *path) {
- char *d, *dir, *dir2;
-
- assert(path);
-
- d = strdup(path);
- if (!d)
- return NULL;
-
- dir = dirname(d);
- assert(dir);
-
- if (dir == d)
- return d;
-
- dir2 = strdup(dir);
- free(d);
-
- return dir2;
-}
-
static const char *skip_slash_or_dot(const char *p) {
for (; !isempty(p); p++) {
if (*p == '/')
_ret; \
})
-char* dirname_malloc(const char *path);
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret);
int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret);
const char *last_path_component(const char *path);
const char *vendor;
Virtualization id;
} dmi_vendor_table[] = {
- { "KVM", VIRTUALIZATION_KVM },
- { "OpenStack", VIRTUALIZATION_KVM }, /* Detect OpenStack instance as KVM in non x86 architecture */
- { "KubeVirt", VIRTUALIZATION_KVM }, /* Detect KubeVirt instance as KVM in non x86 architecture */
- { "Amazon EC2", VIRTUALIZATION_AMAZON },
- { "QEMU", VIRTUALIZATION_QEMU },
- { "VMware", VIRTUALIZATION_VMWARE }, /* https://kb.vmware.com/s/article/1009458 */
- { "VMW", VIRTUALIZATION_VMWARE },
- { "innotek GmbH", VIRTUALIZATION_ORACLE },
- { "VirtualBox", VIRTUALIZATION_ORACLE },
- { "Xen", VIRTUALIZATION_XEN },
- { "Bochs", VIRTUALIZATION_BOCHS },
- { "Parallels", VIRTUALIZATION_PARALLELS },
+ { "KVM", VIRTUALIZATION_KVM },
+ { "OpenStack", VIRTUALIZATION_KVM }, /* Detect OpenStack instance as KVM in non x86 architecture */
+ { "KubeVirt", VIRTUALIZATION_KVM }, /* Detect KubeVirt instance as KVM in non x86 architecture */
+ { "Amazon EC2", VIRTUALIZATION_AMAZON },
+ { "QEMU", VIRTUALIZATION_QEMU },
+ { "VMware", VIRTUALIZATION_VMWARE }, /* https://kb.vmware.com/s/article/1009458 */
+ { "VMW", VIRTUALIZATION_VMWARE },
+ { "innotek GmbH", VIRTUALIZATION_ORACLE },
+ { "VirtualBox", VIRTUALIZATION_ORACLE },
+ { "Xen", VIRTUALIZATION_XEN },
+ { "Bochs", VIRTUALIZATION_BOCHS },
+ { "Parallels", VIRTUALIZATION_PARALLELS },
/* https://wiki.freebsd.org/bhyve */
- { "BHYVE", VIRTUALIZATION_BHYVE },
- { "Hyper-V", VIRTUALIZATION_MICROSOFT },
+ { "BHYVE", VIRTUALIZATION_BHYVE },
+ { "Hyper-V", VIRTUALIZATION_MICROSOFT },
+ { "Apple Virtualization", VIRTUALIZATION_APPLE },
};
int r;
[VIRTUALIZATION_QNX] = "qnx",
[VIRTUALIZATION_ACRN] = "acrn",
[VIRTUALIZATION_POWERVM] = "powervm",
+ [VIRTUALIZATION_APPLE] = "apple",
[VIRTUALIZATION_VM_OTHER] = "vm-other",
[VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
VIRTUALIZATION_QNX,
VIRTUALIZATION_ACRN,
VIRTUALIZATION_POWERVM,
+ VIRTUALIZATION_APPLE,
VIRTUALIZATION_VM_OTHER,
VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER,
}
static int verb_set(int argc, char *argv[], void *userdata) {
- _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL, *parent = NULL;
+ _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
const char *target, *source1, *source2;
uint64_t done;
int r;
r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target));
if (r == -EEXIST)
goto exists;
- else if (r == -ENOENT) {
+ if (r == -ENOENT) {
r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target));
if (r == -EEXIST)
goto exists;
- else if (r == -ENOENT) {
+ if (r == -ENOENT) {
if (faccessat(fd, skip_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
goto exists;
/* We found none of the snippets here, try the next directory */
continue;
- } else if (r < 0)
+ }
+ if (r < 0)
return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target);
- else
- log_debug("Successfully renamed '%s' to '%s'.", source2, target);
+ log_debug("Successfully renamed '%s' to '%s'.", source2, target);
} else if (r < 0)
return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target);
else
log_debug("Successfully renamed '%s' to '%s'.", source1, target);
/* First, fsync() the directory these files are located in */
- parent = dirname_malloc(target);
- if (!parent)
- return log_oom();
-
- r = fsync_path_at(fd, skip_slash(parent));
+ r = fsync_parent_at(fd, skip_slash(target));
if (r < 0)
log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
if efi_ld == 'auto'
efi_ld = cc.get_linker_id().split('.')[1]
if efi_ld not in ['bfd', 'gold']
- warning('Not using @0@ as efi-ld, falling back to bfd'.format(efi_ld))
+ message('Not using @0@ as efi-ld, falling back to bfd'.format(efi_ld))
efi_ld = 'bfd'
endif
endif
assert(n > 0);
assert(pcr_states);
- buffer = malloc(BUFFER_SIZE);
- if (!buffer)
- return log_oom();
-
if (arg_current) {
/* Shortcut things, if we should just use the current PCR value */
for (size_t i = 0; i < n; i++) {
_cleanup_free_ char *p = NULL, *s = NULL;
+ _cleanup_free_ void *v = NULL;
+ size_t sz;
if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32, pcr_states[i].bank, TPM_PCR_INDEX_KERNEL_IMAGE) < 0)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to read '%s': %m", p);
- r = unhexmem(strstrip(s), SIZE_MAX, &pcr_states[i].value, &pcr_states[i].value_size);
+ r = unhexmem(strstrip(s), SIZE_MAX, &v, &sz);
if (r < 0)
return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
+
+ assert(pcr_states[i].value_size == sz);
+ memcpy(pcr_states[i].value, v, sz);
}
return 0;
}
+ buffer = malloc(BUFFER_SIZE);
+ if (!buffer)
+ return log_oom();
+
for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++) {
_cleanup_(evp_md_ctx_free_all) EVP_MD_CTX **mdctx = NULL;
_cleanup_close_ int fd = -1;
static int automount_add_mount_dependencies(Automount *a) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(a);
- parent = dirname_malloc(a->where);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(a->where, &parent);
+ if (r < 0)
+ return r;
return unit_require_mounts_for(UNIT(a), parent, UNIT_DEPENDENCY_IMPLICIT);
}
#include "bus-util.h"
#include "dbus-job.h"
#include "dbus-unit.h"
+#include "dbus-util.h"
#include "dbus.h"
#include "job.h"
#include "log.h"
SD_BUS_PROPERTY("Unit", "(so)", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobType", "s", property_get_type, offsetof(Job, type), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("State", "s", property_get_state, offsetof(Job, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("ActivationDetails", "a(ss)", bus_property_get_activation_details, offsetof(Job, activation_details), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Refs", "as", property_get_refs, 0, 0),
+ SD_BUS_PROPERTY("ActivationDetails", "a(ss)", bus_property_get_activation_details, offsetof(Unit, activation_details), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_METHOD_WITH_ARGS("Start",
SD_BUS_ARGS("s", mode),
return 0;
}
+
+int bus_property_get_activation_details(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ActivationDetails **details = ASSERT_PTR(userdata);
+ _cleanup_strv_free_ char **pairs = NULL;
+ int r;
+
+ assert(reply);
+
+ r = activation_details_append_pair(*details, &pairs);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(ss)");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH_PAIR(key, value, pairs) {
+ r = sd_bus_message_append(reply, "(ss)", *key, *value);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
int bus_verify_manage_units_async_full(Unit *u, const char *verb, int capability, const char *polkit_message, bool interactive, sd_bus_message *call, sd_bus_error *error);
int bus_read_mount_options(sd_bus_message *message, sd_bus_error *error, MountOptions **ret_options, char **ret_format_str, const char *separator);
+
+int bus_property_get_activation_details(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
/* If we do not have our own mount put used the plain directory fallback, then we need to
* open access to the top-level credential directory and the per-service directory now */
- parent = dirname_malloc(final);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(final, &parent);
+ if (r < 0)
+ return r;
if (chmod(parent, 0755) < 0)
return -errno;
}
sd_bus_track_unref(j->bus_track);
strv_free(j->deserialized_clients);
+ activation_details_unref(j->activation_details);
+
return mfree(j);
}
assert(j->installed);
assert(j->unit == other->unit);
- if (j->type != JOB_NOP)
+ if (j->type != JOB_NOP) {
assert_se(job_type_merge_and_collapse(&j->type, other->type, j->unit) == 0);
- else
+
+ /* Keep the oldest ActivationDetails, if any */
+ if (!j->activation_details)
+ j->activation_details = TAKE_PTR(other->activation_details);
+ } else
assert(other->type == JOB_NOP);
j->irreversible = j->irreversible || other->irreversible;
}
static int job_perform_on_unit(Job **j) {
+ ActivationDetails *a;
uint32_t id;
Manager *m;
JobType t;
u = (*j)->unit;
t = (*j)->type;
id = (*j)->id;
+ a = (*j)->activation_details;
switch (t) {
case JOB_START:
- r = unit_start(u);
+ r = unit_start(u, a);
break;
case JOB_RESTART:
bus_track_serialize(j->bus_track, f, "subscribed");
+ activation_details_serialize(j->activation_details, f);
+
/* End marker */
fputc('\n', f);
return 0;
else if (streq(l, "subscribed")) {
if (strv_extend(&j->deserialized_clients, v) < 0)
return log_oom();
+
+ } else if (startswith(l, "activation-details")) {
+ if (activation_details_deserialize(l, v, &j->activation_details) < 0)
+ log_debug("Failed to parse job ActivationDetails element: %s", v);
+
} else
log_debug("Unknown job serialization key: %s", l);
}
else
return -1;
}
+
+void job_set_activation_details(Job *j, ActivationDetails *info) {
+ /* Existing (older) ActivationDetails win, newer ones are discarded. */
+ if (!j || j->activation_details || !info)
+ return; /* Nothing to do. */
+
+ j->activation_details = activation_details_ref(info);
+}
#include "unit-name.h"
#include "unit.h"
+typedef struct ActivationDetails ActivationDetails;
typedef struct Job Job;
typedef struct JobDependency JobDependency;
typedef enum JobType JobType;
unsigned run_queue_idx;
+ /* If the job had a specific trigger that needs to be advertised (eg: a path unit), store it. */
+ ActivationDetails *activation_details;
+
bool installed:1;
bool in_run_queue:1;
bool matters_to_anchor:1;
const char* job_type_to_access_method(JobType t);
int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep);
+
+void job_set_activation_details(Job *j, ActivationDetails *info);
/* Adds in links to other mount points that might lie further up in the hierarchy */
- parent = dirname_malloc(m->where);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(m->where, &parent);
+ if (r < 0)
+ return r;
r = unit_require_mounts_for(UNIT(m), parent, UNIT_DEPENDENCY_IMPLICIT);
if (r < 0)
return 0;
}
-static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) {
+static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify, char **ret_trigger_path) {
+ _cleanup_free_ char *trigger = NULL;
bool b, good = false;
+ assert(s);
+ assert(ret_trigger_path);
+
switch (s->type) {
case PATH_EXISTS:
break;
case PATH_EXISTS_GLOB:
- good = glob_exists(s->path) > 0;
+ good = glob_first(s->path, &trigger) > 0;
break;
case PATH_DIRECTORY_NOT_EMPTY: {
;
}
+ if (good) {
+ if (!trigger) {
+ trigger = strdup(s->path);
+ if (!trigger)
+ (void) log_oom_debug();
+ }
+ *ret_trigger_path = TAKE_PTR(trigger);
+ }
+
return good;
}
path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
}
-static void path_enter_running(Path *p) {
+static void path_enter_running(Path *p, char *trigger_path) {
+ _cleanup_(activation_details_unrefp) ActivationDetails *details = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Unit *trigger;
+ Job *job;
int r;
assert(p);
return;
}
- r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
+ details = activation_details_new(UNIT(p));
+ if (!details) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ r = free_and_strdup(&(ACTIVATION_DETAILS_PATH(details))->trigger_path_filename, trigger_path);
if (r < 0)
goto fail;
+ r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, &job);
+ if (r < 0)
+ goto fail;
+
+ job_set_activation_details(job, details);
+
path_set_state(p, PATH_RUNNING);
path_unwatch(p);
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
-static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) {
+static bool path_check_good(Path *p, bool initial, bool from_trigger_notify, char **ret_trigger_path) {
assert(p);
+ assert(ret_trigger_path);
LIST_FOREACH(spec, s, p->specs)
- if (path_spec_check_good(s, initial, from_trigger_notify))
+ if (path_spec_check_good(s, initial, from_trigger_notify, ret_trigger_path))
return true;
return false;
}
static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) {
+ _cleanup_free_ char *trigger_path = NULL;
Unit *trigger;
int r;
return;
}
- if (path_check_good(p, initial, from_trigger_notify)) {
+ if (path_check_good(p, initial, from_trigger_notify, &trigger_path)) {
log_unit_debug(UNIT(p), "Got triggered.");
- path_enter_running(p);
+ path_enter_running(p, trigger_path);
return;
}
* might have appeared/been removed by now, so we must
* recheck */
- if (path_check_good(p, false, from_trigger_notify)) {
+ if (path_check_good(p, false, from_trigger_notify, &trigger_path)) {
log_unit_debug(UNIT(p), "Got triggered.");
- path_enter_running(p);
+ path_enter_running(p, trigger_path);
return;
}
goto fail;
if (changed)
- path_enter_running(p);
+ path_enter_running(p, found->path);
else
path_enter_waiting(p, false, false);
return 1;
}
+static void activation_details_path_done(ActivationDetails *details) {
+ ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
+
+ p->trigger_path_filename = mfree(p->trigger_path_filename);
+}
+
+static void activation_details_path_serialize(ActivationDetails *details, FILE *f) {
+ ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
+
+ assert(f);
+
+ if (p->trigger_path_filename)
+ (void) serialize_item(f, "activation-details-path-filename", p->trigger_path_filename);
+}
+
+static int activation_details_path_deserialize(const char *key, const char *value, ActivationDetails **details) {
+ int r;
+
+ assert(key);
+ assert(value);
+
+ if (!details || !*details)
+ return -EINVAL;
+
+ ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(*details);
+ if (!p)
+ return -EINVAL;
+
+ if (!streq(key, "activation-details-path-filename"))
+ return -EINVAL;
+
+ r = free_and_strdup(&p->trigger_path_filename, value);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int activation_details_path_append_env(ActivationDetails *details, char ***strv) {
+ ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(details);
+ char *s;
+ int r;
+
+ assert(details);
+ assert(strv);
+ assert(p);
+
+ if (isempty(p->trigger_path_filename))
+ return 0;
+
+ s = strjoin("TRIGGER_PATH=", p->trigger_path_filename);
+ if (!s)
+ return -ENOMEM;
+
+ r = strv_consume(strv, TAKE_PTR(s));
+ if (r < 0)
+ return r;
+
+ return 1; /* Return the number of variables added to the env block */
+}
+
+static int activation_details_path_append_pair(ActivationDetails *details, char ***strv) {
+ ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(details);
+ int r;
+
+ assert(details);
+ assert(strv);
+ assert(p);
+
+ if (isempty(p->trigger_path_filename))
+ return 0;
+
+ r = strv_extend(strv, "trigger_path");
+ if (r < 0)
+ return r;
+
+ r = strv_extend(strv, p->trigger_path_filename);
+ if (r < 0)
+ return r;
+
+ return 1; /* Return the number of pairs added to the env block */
+}
+
static const char* const path_type_table[_PATH_TYPE_MAX] = {
[PATH_EXISTS] = "PathExists",
[PATH_EXISTS_GLOB] = "PathExistsGlob",
.can_start = path_can_start,
};
+
+const ActivationDetailsVTable activation_details_path_vtable = {
+ .object_size = sizeof(ActivationDetailsPath),
+
+ .done = activation_details_path_done,
+ .serialize = activation_details_path_serialize,
+ .deserialize = activation_details_path_deserialize,
+ .append_env = activation_details_path_append_env,
+ .append_pair = activation_details_path_append_pair,
+};
typedef struct Path Path;
typedef struct PathSpec PathSpec;
+typedef struct ActivationDetailsPath ActivationDetailsPath;
#include "unit.h"
RateLimit trigger_limit;
};
+struct ActivationDetailsPath {
+ ActivationDetails meta;
+ char *trigger_path_filename;
+};
+
void path_free_specs(Path *p);
extern const UnitVTable path_vtable;
+extern const ActivationDetailsVTable activation_details_path_vtable;
const char* path_type_to_string(PathType i) _const_;
PathType path_type_from_string(const char *s) _pure_;
PathResult path_result_from_string(const char *s) _pure_;
DEFINE_CAST(PATH, Path);
+DEFINE_ACTIVATION_DETAILS_CAST(ACTIVATION_DETAILS_PATH, ActivationDetailsPath, PATH);
}
}
+ if (UNIT(s)->activation_details) {
+ r = activation_details_append_env(UNIT(s)->activation_details, &our_env);
+ if (r < 0)
+ return r;
+ /* The number of env vars added here can vary, rather than keeping the allocation block in
+ * sync manually, these functions simply use the strv methods to append to it, so we need
+ * to update n_env when we are done in case of future usage. */
+ n_env += r;
+ }
+
r = unit_set_exec_params(UNIT(s), &exec_params);
if (r < 0)
return r;
}
static void timer_enter_running(Timer *t) {
+ _cleanup_(activation_details_unrefp) ActivationDetails *details = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Unit *trigger;
+ Job *job;
int r;
assert(t);
return;
}
- r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
+ details = activation_details_new(UNIT(t));
+ if (!details) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, &job);
if (r < 0)
goto fail;
dual_timestamp_get(&t->last_trigger);
+ ACTIVATION_DETAILS_TIMER(details)->last_trigger = t->last_trigger;
+
+ job_set_activation_details(job, details);
if (t->stamp_path)
touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID);
return 1;
}
+static void activation_details_timer_serialize(ActivationDetails *details, FILE *f) {
+ ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(details);
+
+ assert(details);
+ assert(f);
+ assert(t);
+
+ (void) serialize_dual_timestamp(f, "activation-details-timer-last-trigger", &t->last_trigger);
+}
+
+static int activation_details_timer_deserialize(const char *key, const char *value, ActivationDetails **details) {
+ int r;
+
+ assert(key);
+ assert(value);
+
+ if (!details || !*details)
+ return -EINVAL;
+
+ ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(*details);
+ if (!t)
+ return -EINVAL;
+
+ if (!streq(key, "activation-details-timer-last-trigger"))
+ return -EINVAL;
+
+ r = deserialize_dual_timestamp(value, &t->last_trigger);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int activation_details_timer_append_env(ActivationDetails *details, char ***strv) {
+ ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(details);
+ int r;
+
+ assert(details);
+ assert(strv);
+ assert(t);
+
+ if (!dual_timestamp_is_set(&t->last_trigger))
+ return 0;
+
+ r = strv_extendf(strv, "TRIGGER_TIMER_REALTIME_USEC=%" USEC_FMT, t->last_trigger.realtime);
+ if (r < 0)
+ return r;
+
+ r = strv_extendf(strv, "TRIGGER_TIMER_MONOTONIC_USEC=%" USEC_FMT, t->last_trigger.monotonic);
+ if (r < 0)
+ return r;
+
+ return 2; /* Return the number of variables added to the env block */
+}
+
+static int activation_details_timer_append_pair(ActivationDetails *details, char ***strv) {
+ ActivationDetailsTimer *t = ACTIVATION_DETAILS_TIMER(details);
+ int r;
+
+ assert(details);
+ assert(strv);
+ assert(t);
+
+ if (!dual_timestamp_is_set(&t->last_trigger))
+ return 0;
+
+ r = strv_extend(strv, "trigger_timer_realtime_usec");
+ if (r < 0)
+ return r;
+
+ r = strv_extendf(strv, "%" USEC_FMT, t->last_trigger.realtime);
+ if (r < 0)
+ return r;
+
+ r = strv_extend(strv, "trigger_timer_monotonic_usec");
+ if (r < 0)
+ return r;
+
+ r = strv_extendf(strv, "%" USEC_FMT, t->last_trigger.monotonic);
+ if (r < 0)
+ return r;
+
+ return 2; /* Return the number of pairs added to the env block */
+}
+
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
[TIMER_ACTIVE] = "OnActiveSec",
[TIMER_BOOT] = "OnBootSec",
.can_start = timer_can_start,
};
+
+const ActivationDetailsVTable activation_details_timer_vtable = {
+ .object_size = sizeof(ActivationDetailsTimer),
+
+ .serialize = activation_details_timer_serialize,
+ .deserialize = activation_details_timer_deserialize,
+ .append_env = activation_details_timer_append_env,
+ .append_pair = activation_details_timer_append_pair,
+};
#pragma once
typedef struct Timer Timer;
+typedef struct ActivationDetailsTimer ActivationDetailsTimer;
#include "calendarspec.h"
#include "unit.h"
char *stamp_path;
};
+struct ActivationDetailsTimer {
+ ActivationDetails meta;
+ dual_timestamp last_trigger;
+};
+
#define TIMER_MONOTONIC_CLOCK(t) ((t)->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC)
void timer_free_values(Timer *t);
extern const UnitVTable timer_vtable;
+extern const ActivationDetailsVTable activation_details_timer_vtable;
const char *timer_base_to_string(TimerBase i) _const_;
TimerBase timer_base_from_string(const char *s) _pure_;
TimerResult timer_result_from_string(const char *s) _pure_;
DEFINE_CAST(TIMER, Timer);
+DEFINE_ACTIVATION_DETAILS_CAST(ACTIVATION_DETAILS_TIMER, ActivationDetailsTimer, TIMER);
#include "path-util.h"
#include "process-util.h"
#include "rm-rf.h"
+#include "serialize.h"
#include "set.h"
#include "signal-util.h"
#include "sparse-endian.h"
STRV_FOREACH(i, u->dropin_paths) {
_cleanup_free_ char *p = NULL, *pp = NULL;
- p = dirname_malloc(*i); /* Get the drop-in directory from the drop-in file */
- if (!p)
+ if (path_extract_directory(*i, &p) < 0) /* Get the drop-in directory from the drop-in file */
continue;
- pp = dirname_malloc(p); /* Get the config directory from the drop-in directory */
- if (!pp)
+ if (path_extract_directory(p, &pp) < 0) /* Get the config directory from the drop-in directory */
continue;
/* Only drop transient drop-ins */
set_free_free(u->aliases);
free(u->id);
+ activation_details_unref(u->activation_details);
+
return mfree(u);
}
other->load_state = UNIT_MERGED;
other->merged_into = u;
+ if (!u->activation_details)
+ u->activation_details = activation_details_ref(other->activation_details);
+
/* If there is still some data attached to the other node, we
* don't need it anymore, and can free it. */
if (other->load_state != UNIT_STUB)
* -ESTALE: This unit has been started before and can't be started a second time
* -ENOENT: This is a triggering unit and unit to trigger is not loaded
*/
-int unit_start(Unit *u) {
+int unit_start(Unit *u, ActivationDetails *details) {
UnitActiveState state;
Unit *following;
int r;
following = unit_following(u);
if (following) {
log_unit_debug(u, "Redirecting start request from %s to %s.", u->id, following->id);
- return unit_start(following);
+ return unit_start(following, details);
}
/* Check our ability to start early so that failure conditions don't cause us to enter a busy loop. */
unit_add_to_dbus_queue(u);
unit_cgroup_freezer_action(u, FREEZER_THAW);
+ if (!u->activation_details) /* Older details object wins */
+ u->activation_details = activation_details_ref(details);
+
return UNIT_VTABLE(u)->start(u);
}
assert(n <= INT_MAX);
return (int) n;
}
+
+const ActivationDetailsVTable * const activation_details_vtable[_UNIT_TYPE_MAX] = {
+ [UNIT_PATH] = &activation_details_path_vtable,
+ [UNIT_TIMER] = &activation_details_timer_vtable,
+};
+
+ActivationDetails *activation_details_new(Unit *trigger_unit) {
+ _cleanup_free_ ActivationDetails *details = NULL;
+
+ assert(trigger_unit);
+ assert(trigger_unit->type != _UNIT_TYPE_INVALID);
+ assert(trigger_unit->id);
+
+ details = malloc0(activation_details_vtable[trigger_unit->type]->object_size);
+ if (!details)
+ return NULL;
+
+ *details = (ActivationDetails) {
+ .n_ref = 1,
+ .trigger_unit_type = trigger_unit->type,
+ };
+
+ details->trigger_unit_name = strdup(trigger_unit->id);
+ if (!details->trigger_unit_name)
+ return NULL;
+
+ if (ACTIVATION_DETAILS_VTABLE(details)->init)
+ ACTIVATION_DETAILS_VTABLE(details)->init(details, trigger_unit);
+
+ return TAKE_PTR(details);
+}
+
+static ActivationDetails *activation_details_free(ActivationDetails *details) {
+ if (!details)
+ return NULL;
+
+ if (ACTIVATION_DETAILS_VTABLE(details)->done)
+ ACTIVATION_DETAILS_VTABLE(details)->done(details);
+
+ free(details->trigger_unit_name);
+
+ return mfree(details);
+}
+
+void activation_details_serialize(ActivationDetails *details, FILE *f) {
+ if (!details || details->trigger_unit_type == _UNIT_TYPE_INVALID)
+ return;
+
+ (void) serialize_item(f, "activation-details-unit-type", unit_type_to_string(details->trigger_unit_type));
+ if (details->trigger_unit_name)
+ (void) serialize_item(f, "activation-details-unit-name", details->trigger_unit_name);
+ if (ACTIVATION_DETAILS_VTABLE(details)->serialize)
+ ACTIVATION_DETAILS_VTABLE(details)->serialize(details, f);
+}
+
+int activation_details_deserialize(const char *key, const char *value, ActivationDetails **details) {
+ assert(key);
+ assert(value);
+ assert(details);
+
+ if (!*details) {
+ UnitType t;
+
+ if (!streq(key, "activation-details-unit-type"))
+ return -EINVAL;
+
+ t = unit_type_from_string(value);
+ if (t == _UNIT_TYPE_INVALID)
+ return -EINVAL;
+
+ *details = malloc0(activation_details_vtable[t]->object_size);
+ if (!*details)
+ return -ENOMEM;
+
+ **details = (ActivationDetails) {
+ .n_ref = 1,
+ .trigger_unit_type = t,
+ };
+
+ return 0;
+ }
+
+ if (streq(key, "activation-details-unit-name")) {
+ (*details)->trigger_unit_name = strdup(value);
+ if (!(*details)->trigger_unit_name)
+ return -ENOMEM;
+
+ return 0;
+ }
+
+ if (ACTIVATION_DETAILS_VTABLE(*details)->deserialize)
+ return ACTIVATION_DETAILS_VTABLE(*details)->deserialize(key, value, details);
+
+ return -EINVAL;
+}
+
+int activation_details_append_env(ActivationDetails *details, char ***strv) {
+ int r = 0;
+
+ assert(strv);
+
+ if (!details)
+ return 0;
+
+ if (!isempty(details->trigger_unit_name)) {
+ char *s = strjoin("TRIGGER_UNIT=", details->trigger_unit_name);
+ if (!s)
+ return -ENOMEM;
+
+ r = strv_consume(strv, TAKE_PTR(s));
+ if (r < 0)
+ return r;
+ }
+
+ if (ACTIVATION_DETAILS_VTABLE(details)->append_env) {
+ r = ACTIVATION_DETAILS_VTABLE(details)->append_env(details, strv);
+ if (r < 0)
+ return r;
+ }
+
+ return r + !isempty(details->trigger_unit_name); /* Return the number of variables added to the env block */
+}
+
+int activation_details_append_pair(ActivationDetails *details, char ***strv) {
+ int r = 0;
+
+ assert(strv);
+
+ if (!details)
+ return 0;
+
+ if (!isempty(details->trigger_unit_name)) {
+ r = strv_extend(strv, "trigger_unit");
+ if (r < 0)
+ return r;
+
+ r = strv_extend(strv, details->trigger_unit_name);
+ if (r < 0)
+ return r;
+ }
+
+ if (ACTIVATION_DETAILS_VTABLE(details)->append_env) {
+ r = ACTIVATION_DETAILS_VTABLE(details)->append_pair(details, strv);
+ if (r < 0)
+ return r;
+ }
+
+ return r + !isempty(details->trigger_unit_name); /* Return the number of pairs added to the strv */
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(ActivationDetails, activation_details, activation_details_free);
} _packed_;
} UnitDependencyInfo;
+/* Store information about why a unit was activated.
+ * We start with trigger units (.path/.timer), eventually it will be expanded to include more metadata. */
+typedef struct ActivationDetails {
+ unsigned n_ref;
+ UnitType trigger_unit_type;
+ char *trigger_unit_name;
+} ActivationDetails;
+
+/* For casting an activation event into the various unit-specific types */
+#define DEFINE_ACTIVATION_DETAILS_CAST(UPPERCASE, MixedCase, UNIT_TYPE) \
+ static inline MixedCase* UPPERCASE(ActivationDetails *a) { \
+ if (_unlikely_(!a || a->trigger_unit_type != UNIT_##UNIT_TYPE)) \
+ return NULL; \
+ \
+ return (MixedCase*) a; \
+ }
+
+/* For casting the various unit types into a unit */
+#define ACTIVATION_DETAILS(u) \
+ ({ \
+ typeof(u) _u_ = (u); \
+ ActivationDetails *_w_ = _u_ ? &(_u_)->meta : NULL; \
+ _w_; \
+ })
+
+ActivationDetails *activation_details_new(Unit *trigger_unit);
+ActivationDetails *activation_details_ref(ActivationDetails *p);
+ActivationDetails *activation_details_unref(ActivationDetails *p);
+void activation_details_serialize(ActivationDetails *p, FILE *f);
+int activation_details_deserialize(const char *key, const char *value, ActivationDetails **info);
+int activation_details_append_env(ActivationDetails *info, char ***strv);
+int activation_details_append_pair(ActivationDetails *info, char ***strv);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ActivationDetails*, activation_details_unref);
+
+typedef struct ActivationDetailsVTable {
+ /* How much memory does an object of this activation type need */
+ size_t object_size;
+
+ /* This should reset all type-specific variables. This should not allocate memory, and is called
+ * with zero-initialized data. It should hence only initialize variables that need to be set != 0. */
+ void (*init)(ActivationDetails *info, Unit *trigger_unit);
+
+ /* This should free all type-specific variables. It should be idempotent. */
+ void (*done)(ActivationDetails *info);
+
+ /* This should serialize all type-specific variables. */
+ void (*serialize)(ActivationDetails *info, FILE *f);
+
+ /* This should deserialize all type-specific variables, one at a time. */
+ int (*deserialize)(const char *key, const char *value, ActivationDetails **info);
+
+ /* This should format the type-specific variables for the env block of the spawned service,
+ * and return the number of added items. */
+ int (*append_env)(ActivationDetails *info, char ***strv);
+
+ /* This should append type-specific variables as key/value pairs for the D-Bus property of the job,
+ * and return the number of added pairs. */
+ int (*append_pair)(ActivationDetails *info, char ***strv);
+} ActivationDetailsVTable;
+
+extern const ActivationDetailsVTable * const activation_details_vtable[_UNIT_TYPE_MAX];
+
+static inline const ActivationDetailsVTable* ACTIVATION_DETAILS_VTABLE(const ActivationDetails *a) {
+ assert(a);
+ assert(a->trigger_unit_type < _UNIT_TYPE_MAX);
+
+ return activation_details_vtable[a->trigger_unit_type];
+}
+
/* Newer LLVM versions don't like implicit casts from large pointer types to smaller enums, hence let's add
* explicit type-safe helpers for that. */
static inline UnitDependency UNIT_DEPENDENCY_FROM_PTR(const void *p) {
JobMode on_success_job_mode;
JobMode on_failure_job_mode;
+ /* If the job had a specific trigger that needs to be advertised (eg: a path unit), store it. */
+ ActivationDetails *activation_details;
+
/* Tweaking the GC logic */
CollectMode collect_mode;
bool unit_can_stop(Unit *u) _pure_;
bool unit_can_isolate(Unit *u) _pure_;
-int unit_start(Unit *u);
+int unit_start(Unit *u, ActivationDetails *details);
int unit_stop(Unit *u);
int unit_reload(Unit *u);
assert(arg_action == ACTION_COPY_TO);
- dn = dirname_malloc(arg_target);
- if (!dn)
- return log_oom();
+ r = path_extract_directory(arg_target, &dn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract directory name from target path '%s': %m", arg_target);
r = chase_symlinks(dn, mounted_dir, CHASE_PREFIX_ROOT|CHASE_WARN, NULL, &dfd);
if (r < 0)
disk_size = st.st_size;
stat_used = st.st_blocks * 512;
- parent = dirname_malloc(ip);
- if (!parent)
- return log_oom();
+ r = path_extract_directory(ip, &parent);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract parent directory from image path '%s': %m", ip);
if (statfs(parent, &sfs) < 0)
log_debug_errno(errno, "Failed to statfs() %s, ignoring: %m", parent);
return log_error_errno(errno, "Failed to allocate inotify fd: %m");
}
- dn = dirname_malloc(path);
+ r = path_extract_directory(path, &dn);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract directory from device node path '%s': %m", path);
for (;;) {
- if (!dn)
- return log_oom();
+ _cleanup_free_ char *ndn = NULL;
log_info("Watching %s", dn);
} else
break;
- if (empty_or_root(dn))
+ r = path_extract_directory(dn, &ndn);
+ if (r == -EADDRNOTAVAIL) /* Arrived at the top? */
break;
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract directory from device node path '%s': %m", dn);
- dn = dirname_malloc(dn);
+ free_and_replace(dn, ndn);
}
w = now(CLOCK_MONOTONIC);
#include <sys/sendfile.h>
-/* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the POSIX
- * version which is really broken. We prefer GNU basename(). */
-#include <libgen.h>
-#undef basename
-
#include "sd-daemon.h"
#include "alloc-util.h"
if (!isempty(s->namespace_field))
iovec[n++] = IOVEC_MAKE_STRING(s->namespace_field);
+ if (in_initrd())
+ iovec[n++] = IOVEC_MAKE_STRING("_SYSTEM_CONTEXT=initrd");
+ else
+ iovec[n++] = IOVEC_MAKE_STRING("_SYSTEM_CONTEXT=main");
+
assert(n <= m);
if (s->split_mode == SPLIT_UID && c && uid_is_valid(c->uid))
#define SERVER_MACHINE_ID(s) ((s)->machine_id_field + STRLEN("_MACHINE_ID="))
/* Extra fields for any log messages */
-#define N_IOVEC_META_FIELDS 23
+#define N_IOVEC_META_FIELDS 24
/* Extra fields for log messages that contain OBJECT_PID= (i.e. log about another process) */
#define N_IOVEC_OBJECT_FIELDS 18
#include <sys/mman.h>
#include <sys/prctl.h>
-/* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the POSIX
- * version which is really broken. We prefer GNU basename(). */
-#include <libgen.h>
-#undef basename
-
#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-kernel.h"
assert_se(mkdir_parents(path, 0755) >= 0);
(void) usleep(100 * USEC_PER_MSEC);
- d = dirname_malloc(path);
- assert_se(d);
+ assert_se(path_extract_directory(path, &d) >= 0);
assert_se(asprintf(&suffixed, "%s.%" PRIx64, d, random_u64()) >= 0);
assert_se(rename(d, suffixed) >= 0);
(void) usleep(100 * USEC_PER_MSEC);
_cleanup_free_ char *dir = NULL;
int r;
- dir = dirname_malloc(f->path);
- if (!dir)
- return -ENOMEM;
+ r = path_extract_directory(f->path, &dir);
+ if (r < 0)
+ return r;
r = add_directory(j, dir, NULL);
if (r < 0)
#include <sys/mount.h>
#include <sys/wait.h>
-/* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the POSIX
- * version which is really broken. We prefer GNU basename(). */
-#include <libgen.h>
-#undef basename
-
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-get-properties.h"
}
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- const char *src, *dest, *host_path, *container_path, *host_basename, *container_basename, *container_dirname;
+ _cleanup_free_ char *host_basename = NULL, *container_basename = NULL;
+ const char *src, *dest, *host_path, *container_path;
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS;
_cleanup_close_ int hostfd = -1;
bool copy_from;
pid_t child;
uid_t uid_shift;
- char *t;
int r;
assert(message);
container_path = dest;
}
- host_basename = basename(host_path);
+ r = path_extract_filename(host_path, &host_basename);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to extract file name of '%s' path: %m", host_path);
- container_basename = basename(container_path);
- t = strdupa_safe(container_path);
- container_dirname = dirname(t);
+ r = path_extract_filename(container_path, &container_basename);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to extract file name of '%s' path: %m", container_path);
hostfd = open_parent(host_path, O_CLOEXEC, 0);
if (hostfd < 0)
goto child_fail;
}
- containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
+ containerfd = open_parent(container_path, O_CLOEXEC, 0);
if (containerfd < 0) {
- r = log_error_errno(errno, "Failed to open destination directory: %m");
+ r = log_error_errno(containerfd, "Failed to open destination directory: %m");
goto child_fail;
}
* directly instead. It's not as good, due to symlinks and such, but we can't do
* anything better here. */
- parent = dirname_malloc(path);
- if (!parent)
- return log_oom();
+ r = path_extract_filename(path, &parent);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract parent path from '%s': %m", path);
r = RET_NERRNO(stat(parent, &st2));
}
static int in_search_path(const LookupPaths *lp, const char *path) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(path);
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
return path_strv_contains(lp->search_path, parent);
}
static int path_is_generator(const LookupPaths *lp, const char *path) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(lp);
assert(path);
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
return path_equal_ptr(parent, lp->generator) ||
path_equal_ptr(parent, lp->generator_early) ||
static int path_is_transient(const LookupPaths *lp, const char *path) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(lp);
assert(path);
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
return path_equal_ptr(parent, lp->transient);
}
static int path_is_control(const LookupPaths *lp, const char *path) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(lp);
assert(path);
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
return path_equal_ptr(parent, lp->persistent_control) ||
path_equal_ptr(parent, lp->runtime_control);
static int path_is_config(const LookupPaths *lp, const char *path, bool check_parent) {
_cleanup_free_ char *parent = NULL;
+ int r;
assert(lp);
assert(path);
* them we couldn't discern configuration from transient or generated units */
if (check_parent) {
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
path = parent;
}
static int path_is_runtime(const LookupPaths *lp, const char *path, bool check_parent) {
_cleanup_free_ char *parent = NULL;
const char *rpath;
+ int r;
assert(lp);
assert(path);
return true;
if (check_parent) {
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(path, &parent);
+ if (r < 0)
+ return r;
path = parent;
}
path_alias ++; /* skip over slash */
- dir = dirname_malloc(dst);
- if (!dir)
- return log_oom();
+ r = path_extract_directory(dst, &dir);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract parent directory from '%s': %m", dst);
p = endswith(dir, ".wants");
if (!p)
#define BATTERY_LOW_CAPACITY_LEVEL 5
#define DISCHARGE_RATE_FILEPATH "/var/lib/systemd/sleep/battery_discharge_percentage_rate_per_hour"
#define BATTERY_DISCHARGE_RATE_HASH_KEY SD_ID128_MAKE(5f,9a,20,18,38,76,46,07,8d,36,58,0b,bb,c4,e0,63)
+#define SYS_ENTRY_RAW_FILE_TYPE1 "/sys/firmware/dmi/entries/1-0/raw"
static void *CAPACITY_TO_PTR(int capacity) {
assert(capacity >= 0);
return 0;
}
+/* Return true if all batteries have acpi_btp support */
+int battery_trip_point_alarm_exists(void) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *dev;
+ int r;
+
+ r = battery_enumerator_new(&e);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to initialize battery enumerator: %m");
+
+ FOREACH_DEVICE(e, dev) {
+ int battery_alarm;
+ const char *s;
+
+ r = sd_device_get_sysattr_value(dev, "alarm", &s);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to read battery alarm: %m");
+
+ r = safe_atoi(s, &battery_alarm);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to parse battery alarm: %m");
+ if (battery_alarm <= 0)
+ return false;
+ }
+
+ return true;
+}
+
+/* Return true if wakeup type is APM timer */
+int check_wakeup_type(void) {
+ _cleanup_free_ char *s = NULL;
+ uint8_t wakeup_type_byte, tablesize;
+ size_t readsize;
+ int r;
+
+ /* implementation via dmi/entries */
+ r = read_full_virtual_file(SYS_ENTRY_RAW_FILE_TYPE1, &s, &readsize);
+ if (r < 0)
+ return log_debug_errno(r, "Unable to read %s: %m", SYS_ENTRY_RAW_FILE_TYPE1);
+
+ if (readsize < 25)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Only read %zu bytes from %s (expected 25)", readsize, SYS_ENTRY_RAW_FILE_TYPE1);
+
+ /* index 1 stores the size of table */
+ tablesize = (uint8_t) s[1];
+ if (tablesize < 25)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Table size lesser than the index[0x18] where waketype byte is available.");
+
+ wakeup_type_byte = (uint8_t) s[24];
+ /* 0 is Reserved and 8 is AC Power Restored. As per table 12 in
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.4.0.pdf */
+ if (wakeup_type_byte >= 128)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Expected value in range 0-127");
+
+ if (wakeup_type_byte == 3) {
+ log_debug("DMI BIOS System Information indicates wakeup type is APM Timer");
+ return true;
+ }
+
+ return false;
+}
+
int can_sleep_state(char **types) {
_cleanup_free_ char *text = NULL;
int r;
Hashmap *current_capacity,
usec_t before_timestamp,
usec_t after_timestamp);
+int check_wakeup_type(void);
+int battery_trip_point_alarm_exists(void);
const char* sleep_operation_to_string(SleepOperation s) _const_;
SleepOperation sleep_operation_from_string(const char *s) _pure_;
return r;
}
-static int execute_s2h(const SleepConfig *sleep_config) {
+static int custom_timer_suspend(const SleepConfig *sleep_config) {
_cleanup_hashmap_free_ Hashmap *last_capacity = NULL, *current_capacity = NULL;
int r;
if (!woken_by_timer)
/* Return as manual wakeup done. This also will return in case battery was charged during suspension */
return 0;
+
+ r = check_wakeup_type();
+ if (r < 0)
+ log_debug_errno(r, "Failed to check hardware wakeup type, ignoring: %m");
+ if (r > 0) {
+ log_debug("wakeup type is APM timer");
+ /* system should hibernate */
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static int execute_s2h(const SleepConfig *sleep_config) {
+ int r, k;
+
+ assert(sleep_config);
+
+ r = check_wakeup_type();
+ if (r < 0)
+ log_debug_errno(r, "Failed to check hardware wakeup type, ignoring: %m");
+
+ k = battery_trip_point_alarm_exists();
+ if (k < 0)
+ log_debug_errno(k, "Failed to check whether acpi_btp support is enabled or not, ignoring: %m");
+
+ if (r >= 0 && k > 0) {
+ log_debug("Attempting to suspend...");
+ r = execute(sleep_config, SLEEP_SUSPEND, NULL);
+ if (r < 0)
+ return r;
+
+ r = check_wakeup_type();
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check hardware wakeup type: %m");
+
+ if (r == 0)
+ /* For APM Timer wakeup, system should hibernate else wakeup */
+ return 0;
+ } else {
+ r = custom_timer_suspend(sleep_config);
+ if(r < 0)
+ return log_debug_errno(r, "Suspend cycle with manual battery discharge rate estimation failed: %m");
+ if(r == 0)
+ /* manual wakeup */
+ return 0;
}
+ /* For above custom timer, if 1 is returned, system will directly hibernate */
log_debug("Attempting to hibernate");
r = execute(sleep_config, SLEEP_HIBERNATE, NULL);
if (!arg_full) {
_cleanup_free_ char *dir = NULL;
- dir = dirname_malloc(*original);
- if (!dir)
- return log_oom();
+ r = path_extract_directory(*original, &dir);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract directory from '%s': %m", *original);
/* No need to check if the dir is empty, rmdir does nothing if it is not the case. */
(void) rmdir(dir);
dir = mfree(dir);
- dir = dirname_malloc(*dropin);
- if (!dir) {
- log_oom();
- return;
+ r = path_extract_directory(*dropin, &dir);
+ if (r < 0) {
+ log_error_errno(r, "Failed to extract directory of '%s': %m", *dropin);
+ break;
}
printf("%s\n"
test_policy_empty(false, cgroup, &prog);
test_policy_empty(true, cgroup, &prog);
- assert_se(parent = dirname_malloc(cgroup));
+ assert_se(path_extract_directory(cgroup, &parent) >= 0);
assert_se(cg_mask_supported(&supported) >= 0);
r = cg_attach_everywhere(supported, parent, 0, NULL, NULL);
assert_se(r >= 0);
- assert_se(unit_start(u) >= 0);
+ assert_se(unit_start(u, NULL) >= 0);
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED))
assert_se(sd_event_run(m->event, UINT64_MAX) >= 0);
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
- assert_se(unit_start(u) >= 0);
+ assert_se(unit_start(u, NULL) >= 0);
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED))
assert_se(sd_event_run(m->event, UINT64_MAX) >= 0);
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
- r = unit_start(u);
+ r = unit_start(u, NULL);
if (r < 0)
return log_error_errno(r, "Unit start failed %m");
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
- r = unit_start(u);
+ r = unit_start(u, NULL);
if (r < 0)
return log_error_errno(r, "Unit start failed %m");
assert_se(unit_name);
assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
- assert_se(unit_start(unit) >= 0);
+ assert_se(unit_start(unit, NULL) >= 0);
check_main_result(file, line, func, m, unit, status_expected, code_expected);
}
#define test(m, unit_name, status_expected, code_expected) \
assert_se(unit_name);
assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
- assert_se(unit_start(unit) >= 0);
+ assert_se(unit_start(unit, NULL) >= 0);
check_service_result(file, line, func, m, unit, result_expected);
}
#define test_service(m, unit_name, result_expected) \
#include "tests.h"
#include "tmpfile-util.h"
+TEST(glob_first) {
+ char *first, name[] = "/tmp/test-glob_first.XXXXXX";
+ int fd = -1;
+ int r;
+
+ fd = mkostemp_safe(name);
+ assert_se(fd >= 0);
+ close(fd);
+
+ r = glob_first("/tmp/test-glob_first*", &first);
+ assert_se(r == 1);
+ assert_se(streq(name, first));
+ first = mfree(first);
+
+ r = unlink(name);
+ assert_se(r == 0);
+ r = glob_first("/tmp/test-glob_first*", &first);
+ assert_se(r == 0);
+ assert_se(first == NULL);
+}
+
TEST(glob_exists) {
char name[] = "/tmp/test-glob_exists.XXXXXX";
int fd = -1;
path = PATH(unit);
service = service_for_path(m, path, NULL);
- assert_se(unit_start(unit) >= 0);
+ assert_se(unit_start(unit, NULL) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
path = PATH(unit);
service = service_for_path(m, path, NULL);
- assert_se(unit_start(unit) >= 0);
+ assert_se(unit_start(unit, NULL) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
path = PATH(unit);
service = service_for_path(m, path, NULL);
- assert_se(unit_start(unit) >= 0);
+ assert_se(unit_start(unit, NULL) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
path = PATH(unit);
service = service_for_path(m, path, NULL);
- assert_se(unit_start(unit) >= 0);
+ assert_se(unit_start(unit, NULL) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
path = PATH(unit);
service = service_for_path(m, path, "path-mycustomunit.service");
- assert_se(unit_start(unit) >= 0);
+ assert_se(unit_start(unit, NULL) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
assert_se(access(test_path, F_OK) < 0);
- assert_se(unit_start(unit) >= 0);
+ assert_se(unit_start(unit, NULL) >= 0);
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
return;
assert_se(access(test_path, F_OK) < 0);
- assert_se(unit_start(unit) >= 0);
+ assert_se(unit_start(unit, NULL) >= 0);
/* Check if the directory has been created */
assert_se(access(test_path, F_OK) >= 0);
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
- r = unit_start(u);
+ r = unit_start(u, NULL);
if (r < 0)
return log_error_errno(r, "Unit start failed %m");
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Path]
+PathExistsGlob=/tmp/test63-glob*
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63-glob-foo'
+ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63-glob.path'
+ExecStart=systemd-notify --ready
+RemainAfterExit=yes
+Type=notify
ConditionPathExists=/tmp/nonexistent
[Service]
+ExecStartPre=sh -c 'test "$TRIGGER_PATH" = /tmp/test63'
+ExecStartPre=sh -c 'test "$TRIGGER_UNIT" = test63.path'
ExecStart=true
cat >/lib/systemd/system/my.service <<EOF
[Service]
Type=oneshot
+ExecStartPre=sh -c 'test "\$TRIGGER_UNIT" = my.timer'
+ExecStartPre=sh -c 'test -n "\$TRIGGER_TIMER_REALTIME_USEC"'
+ExecStartPre=sh -c 'test -n "\$TRIGGER_TIMER_MONOTONIC_USEC"'
ExecStart=/bin/echo Timer runs me
EOF
sfdisk --part-label "${image}.gpt" 3 "Signature Partition"
fi
loop="$(losetup --show -P -f "${image}.gpt")"
+udevadm wait --timeout 60 --settle "${loop:?}"
dd if="${image}.raw" of="${loop}p1"
dd if="${image}.verity" of="${loop}p2"
if [ "${HAVE_OPENSSL}" -eq 1 ]; then
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
-Description=TEST-63-ISSUE-17433
+Description=TEST-63-PATH
[Service]
ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot
-
-# Test that a path unit continuously triggering a service that fails condition checks eventually fails with
-# the trigger-limit-hit error.
-ExecStart=rm -f /tmp/nonexistent
-ExecStart=systemctl start test63.path
-ExecStart=touch /tmp/test63
-# Make sure systemd has sufficient time to hit the trigger limit for test63.path.
-ExecStart=sleep 2
-ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = inactive'
-ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = success'
-ExecStart=sh -x -c 'test "$(systemctl show test63.path -P ActiveState)" = failed'
-ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = trigger-limit-hit'
-
-# Test that starting the service manually doesn't affect the path unit.
-ExecStart=rm -f /tmp/test63
-ExecStart=systemctl reset-failed
-ExecStart=systemctl start test63.path
-ExecStart=systemctl start test63.service
-ExecStart=sh -x -c 'test "$(systemctl show test63.service -P ActiveState)" = inactive'
-ExecStart=sh -x -c 'test "$(systemctl show test63.service -P Result)" = success'
-ExecStart=sh -x -c 'test "$(systemctl show test63.path -P ActiveState)" = active'
-ExecStart=sh -x -c 'test "$(systemctl show test63.path -P Result)" = success'
-ExecStart=sh -x -c 'echo OK >/testok'
--- /dev/null
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -ex
+set -o pipefail
+
+systemctl log-level debug
+
+# Test that a path unit continuously triggering a service that fails condition checks eventually fails with
+# the trigger-limit-hit error.
+rm -f /tmp/nonexistent
+systemctl start test63.path
+touch /tmp/test63
+
+# Make sure systemd has sufficient time to hit the trigger limit for test63.path.
+sleep 2
+test "$(systemctl show test63.service -P ActiveState)" = inactive
+test "$(systemctl show test63.service -P Result)" = success
+test "$(systemctl show test63.path -P ActiveState)" = failed
+test "$(systemctl show test63.path -P Result)" = trigger-limit-hit
+
+# Test that starting the service manually doesn't affect the path unit.
+rm -f /tmp/test63
+systemctl reset-failed
+systemctl start test63.path
+systemctl start test63.service
+test "$(systemctl show test63.service -P ActiveState)" = inactive
+test "$(systemctl show test63.service -P Result)" = success
+test "$(systemctl show test63.path -P ActiveState)" = active
+test "$(systemctl show test63.path -P Result)" = success
+
+# Test that glob matching works too, with $TRIGGER_PATH
+systemctl start test63-glob.path
+touch /tmp/test63-glob-foo
+timeout 60 bash -c 'while ! systemctl -q is-active test63-glob.service; do sleep .2; done'
+test "$(systemctl show test63-glob.service -P ActiveState)" = active
+test "$(systemctl show test63-glob.service -P Result)" = success
+
+test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[["trigger_unit","test63-glob.path"],["trigger_path","/tmp/test63-glob-foo"]]}'
+
+systemctl stop test63-glob.path test63-glob.service
+
+test "$(busctl --json=short get-property org.freedesktop.systemd1 /org/freedesktop/systemd1/unit/test63_2dglob_2eservice org.freedesktop.systemd1.Unit ActivationDetails)" = '{"type":"a(ss)","data":[]}'
+
+systemctl log-level info
+
+echo OK >/testok