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
steps:
- uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b
- - uses: systemd/mkosi@f37c19e7217a41c52d9a9a8769e98496255e2e2d
+ - uses: systemd/mkosi@d93967b94225ca63b3f5c18045db815439897b5c
- name: Install
run: sudo apt-get update && sudo apt-get install --no-install-recommends python3-pexpect python3-jinja2
KernelCommandLine=${{ env.KERNEL_CMDLINE }}
EOF
- echo systemd-stable/ >> .gitignore
-
- name: Build ${{ matrix.distro }}
- run: ./.github/workflows/run_mkosi.sh build
+ run: sudo python3 -m mkosi build
- name: Show ${{ matrix.distro }} image summary
- run: ./.github/workflows/run_mkosi.sh summary
+ run: sudo python3 -m mkosi summary
- name: Boot ${{ matrix.distro }} systemd-nspawn
- run: ./.github/workflows/run_mkosi.sh boot ${{ env.KERNEL_CMDLINE }}
+ run: sudo python3 -m mkosi boot ${{ env.KERNEL_CMDLINE }}
- name: Check ${{ matrix.distro }} systemd-nspawn
- run: ./.github/workflows/run_mkosi.sh shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }"
+ run: sudo python3 -m mkosi shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }"
- name: Boot ${{ matrix.distro }} QEMU
- run: ./.github/workflows/run_mkosi.sh qemu
+ run: sudo timeout -k 30 10m python3 -m mkosi qemu
- name: Check ${{ matrix.distro }} QEMU
- run: ./.github/workflows/run_mkosi.sh shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }"
+ run: sudo python3 -m mkosi shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }"
+++ /dev/null
-#!/usr/bin/env bash
-# SPDX-License-Identifier: LGPL-2.1-or-later
-# shellcheck disable=SC2064
-
-set -eu
-set -o pipefail
-
-EC=0
-TEMPFILE="$(mktemp)"
-TEMP_EXTRA_TREE="$(mktemp --directory)"
-trap "rm -rf '$TEMPFILE' '$TEMP_EXTRA_TREE'" EXIT
-
-# We need isc-dhcp-server to be installed for the networkd unit tests, but we don't want to
-# run it by default. mktemp creates the directory as 700, so change it, otherwise it will
-# affect the image's root folder permissions.
-chmod 755 "$TEMP_EXTRA_TREE"
-mkdir -p "$TEMP_EXTRA_TREE/etc/systemd/system/"
-ln -s /dev/null "$TEMP_EXTRA_TREE/etc/systemd/system/isc-dhcp-server.service"
-ln -s /dev/null "$TEMP_EXTRA_TREE/etc/systemd/system/isc-dhcp-server6.service"
-
-for ((i = 0; i < 5; i++)); do
- EC=0
- (sudo timeout -k 30 10m python3 -m mkosi --extra-tree="$TEMP_EXTRA_TREE" "$@") |& tee "$TEMPFILE" || EC=$?
- if [[ $EC -eq 0 ]]; then
- # The command passed — let's return immediately
- break
- fi
-
- if ! grep -E "Failed to dissect image .+: Connection timed out" "$TEMPFILE"; then
- # The command failed for other reason than the dissect-related timeout -
- # let's exit with the same EC
- exit $EC
- fi
-
- # The command failed due to the dissect-related timeout — let's try again
- sleep 1
-done
-
-exit $EC
) 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>
<term><varname>systemd.default_standard_error=</varname></term>
<term><varname>systemd.setenv=</varname></term>
<term><varname>systemd.machine_id=</varname></term>
- <term><varname>systemd.unified_cgroup_hierarchy</varname></term>
- <term><varname>systemd.legacy_systemd_cgroup_controller</varname></term>
<term><varname>systemd.set_credential=</varname></term>
<term><varname>systemd.import_credentials=</varname></term>
<listitem>
the hostname, it simply controls the initial hostname set during early boot.</para></listitem>
</varlistentry>
</variablelist>
+ </refsect1>
+ <refsect1>
+ <title>History</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>systemd 252</term>
+ <listitem><para>Kernel command-line arguments <varname>systemd.unified_cgroup_hierarchy</varname>
+ and <varname>systemd.legacy_systemd_cgroup_controller</varname> were deprecated. Please switch to
+ the unified cgroup hierarchy.</para></listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
<refsect1>
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
that kernel threads do not have a command line, in which case
-ENXIO is returned.</para>
- <para><function>sd_bus_creds_get_cgroup()</function> will retrieve
- the control group path. See <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v1/index.html">Control Groups version 1</ulink>.
+ <para><function>sd_bus_creds_get_cgroup()</function> will retrieve the control group path. See <ulink
+ url="https://docs.kernel.org/admin-guide/cgroup-v2.html">Control Groups v2</ulink>.
</para>
<para><function>sd_bus_creds_get_unit()</function> will retrieve
<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>
one iteration. The <option>--iterations=</option> argument, if
given, is honored. This mode is suitable for scripting.</para>
- <para>Resource usage is only accounted for control groups in the
- relevant hierarchy, i.e. CPU usage is only accounted for control
- groups in the <literal>cpuacct</literal> hierarchy, memory usage
- only for those in <literal>memory</literal> and disk I/O usage for
- those in <literal>blkio</literal>. If resource monitoring for
- these resources is required, it is recommended to add the
- <varname>CPUAccounting=1</varname>,
- <varname>MemoryAccounting=1</varname> and
- <varname>BlockIOAccounting=1</varname> settings in the unit files
- in question. See
+ <para>Resource usage is only accounted for control groups with the appropriate controllers turned on:
+ <literal>cpu</literal> controller for CPU usage, <literal>memory</literal> controller for memory usage,
+ and <literal>io</literal> contoller for disk I/O consumption. If resource monitoring for these resources
+ is required, it is recommended to add the <varname>CPUAccounting=1</varname>,
+ <varname>MemoryAccounting=1</varname> and <varname>IOAccounting=1</varname> settings in the unit files in
+ question. See
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para>
the CPU load value is going to be between 0% and 800%. The number of
processors can be found in <literal>/proc/cpuinfo</literal>.</para>
- <para>To emphasize this: unless
- <literal>CPUAccounting=1</literal>,
- <literal>MemoryAccounting=1</literal> and
- <literal>BlockIOAccounting=1</literal> are enabled for the
- services in question, no resource accounting will be available for
- system services and the data shown by
- <command>systemd-cgtop</command> will be incomplete.</para>
+ <para>To emphasize: unless <literal>CPUAccounting=1</literal>, <literal>MemoryAccounting=1</literal>, and
+ <literal>IOAccounting=1</literal> are enabled for the services in question, no resource accounting will
+ be available for system services and the data shown by <command>systemd-cgtop</command> will be
+ incomplete.</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>
<example>
<title>Limiting resources available to a command</title>
- <programlisting># systemd-run -p BlockIOWeight=10 updatedb</programlisting>
+ <programlisting># systemd-run -p IOWeight=10 updatedb</programlisting>
- <para>This command invokes the
- <citerefentry project='man-pages'><refentrytitle>updatedb</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ <para>This command invokes the <citerefentry
+ project='man-pages'><refentrytitle>updatedb</refentrytitle><manvolnum>8</manvolnum></citerefentry>
tool, but lowers the block I/O weight for it to 10. See
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for more information on the <varname>BlockIOWeight=</varname>
- property.</para>
+ for more information on the <varname>IOWeight=</varname> property.</para>
</example>
<example>
<varlistentry>
<term><varname>DefaultCPUAccounting=</varname></term>
- <term><varname>DefaultBlockIOAccounting=</varname></term>
<term><varname>DefaultMemoryAccounting=</varname></term>
<term><varname>DefaultTasksAccounting=</varname></term>
<term><varname>DefaultIOAccounting=</varname></term>
<term><varname>DefaultIPAccounting=</varname></term>
<listitem><para>Configure the default resource accounting settings, as configured per-unit by
- <varname>CPUAccounting=</varname>, <varname>BlockIOAccounting=</varname>, <varname>MemoryAccounting=</varname>,
- <varname>TasksAccounting=</varname>, <varname>IOAccounting=</varname> and <varname>IPAccounting=</varname>. See
+ <varname>CPUAccounting=</varname>, <varname>MemoryAccounting=</varname>,
+ <varname>TasksAccounting=</varname>, <varname>IOAccounting=</varname> and
+ <varname>IPAccounting=</varname>. See
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details on the per-unit settings. <varname>DefaultTasksAccounting=</varname> defaults to yes,
- <varname>DefaultMemoryAccounting=</varname> to &MEMORY_ACCOUNTING_DEFAULT;. <varname>DefaultCPUAccounting=</varname>
- defaults to yes if enabling CPU accounting doesn't require the CPU controller to be enabled (Linux 4.15+ using the
- unified hierarchy for resource control), otherwise it defaults to no. The other three settings default to no.</para></listitem>
+ <varname>DefaultMemoryAccounting=</varname> to
+ &MEMORY_ACCOUNTING_DEFAULT;. <varname>DefaultCPUAccounting=</varname> defaults to yes if enabling CPU
+ accounting doesn't require the CPU controller to be enabled (Linux 4.15+ using the unified hierarchy
+ for resource control), otherwise it defaults to no. The other three settings default to
+ no.</para></listitem>
</varlistentry>
<varlistentry>
</table>
</refsect1>
+ <refsect1>
+ <title>History</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>systemd 252</term>
+ <listitem><para>Option <varname>DefaultBlockIOAccounting=</varname> was deprecated. Please switch
+ to the unified cgroup hierarchy.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1>
<title>See Also</title>
<para>
<refsect1>
<title>Description</title>
- <para><command>systemd-sysusers</command> creates system users and
- groups, based on the file format and location specified in
+ <para><command>systemd-sysusers</command> creates system users and groups, based on files in the format
+ described in
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
- <para>If invoked with no arguments, it applies all directives from all files
- found in the directories specified by
- <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- When invoked with positional arguments, if option
- <option>--replace=<replaceable>PATH</replaceable></option> is specified, arguments
- specified on the command line are used instead of the configuration file
- <replaceable>PATH</replaceable>. Otherwise, just the configuration specified by
- the command line arguments is executed. The string <literal>-</literal> may be
- specified instead of a filename to instruct <command>systemd-sysusers</command>
- to read the configuration from standard input. If only the basename of a file is
- specified, all configuration directories are searched for a matching file and
- the file found that has the highest priority is executed.</para>
+ <para>If invoked with no arguments, it applies all directives from all files found in the directories
+ specified by
+ <citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>. When
+ invoked with positional arguments, if option <option>--replace=<replaceable>PATH</replaceable></option>
+ is specified, arguments specified on the command line are used instead of the configuration file
+ <replaceable>PATH</replaceable>. Otherwise, just the configuration specified by the command line
+ arguments is executed. The string <literal>-</literal> may be specified instead of a filename to instruct
+ <command>systemd-sysusers</command> to read the configuration from standard input. If the argument is a
+ relative path, all configuration directories are searched for a matching file and the file found that has
+ the highest priority is executed. If the argument is an absolute path, that file is used directly without
+ searching of the configuration directories.</para>
</refsect1>
<refsect1>
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>,
<!-- We don't have any default dependency here. -->
- <refsect1>
- <title>Unified and Legacy Control Group Hierarchies</title>
-
- <para>The unified control group hierarchy is the new version of kernel control group interface, see
- <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html">Control Groups v2</ulink>.
- Depending on the resource type, there are differences in resource control capabilities. Also, because of
- interface changes, some resource types have separate set of options on the unified hierarchy.</para>
-
- <para>
- <variablelist>
-
- <varlistentry>
- <term>CPU</term>
- <listitem>
- <para><varname>CPUWeight=</varname> and <varname>StartupCPUWeight=</varname> replace
- <varname>CPUShares=</varname> and <varname>StartupCPUShares=</varname>, respectively.</para>
-
- <para>The <literal>cpuacct</literal> controller does not exist separately on the unified hierarchy.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Memory</term>
- <listitem>
- <para><varname>MemoryMax=</varname> replaces <varname>MemoryLimit=</varname>. <varname>MemoryLow=</varname>
- and <varname>MemoryHigh=</varname> are effective only on unified hierarchy.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>IO</term>
- <listitem>
- <para><literal>IO</literal>-prefixed settings are a superset of and replace
- <literal>BlockIO</literal>-prefixed ones. On unified hierarchy, IO resource control also applies
- to buffered writes.</para>
- </listitem>
- </varlistentry>
-
- </variablelist>
- </para>
-
- <para>To ease the transition, there is best-effort translation between the two versions of settings. For each
- controller, if any of the settings for the unified hierarchy are present, all settings for the legacy hierarchy are
- ignored. If the resulting settings are for the other type of hierarchy, the configurations are translated before
- application.</para>
-
- <para>Legacy control group hierarchy (see <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v1/">Control Groups version 1</ulink>),
- also called cgroup-v1, doesn't allow safe delegation of controllers to unprivileged processes. If the
- system uses the legacy control group hierarchy, resource control is disabled for the systemd user
- instance, see
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
- </refsect1>
-
<refsect1>
<title>Options</title>
<varname>CPUWeight=</varname> applies to normal runtime of the system, and if the former is not set also to
the startup and shutdown phases. Using <varname>StartupCPUWeight=</varname> allows prioritizing specific services at
boot-up and shutdown differently than during normal runtime.</para>
-
- <para>These settings replace <varname>CPUShares=</varname> and <varname>StartupCPUShares=</varname>.</para>
</listitem>
</varlistentry>
For details about this control group attribute, see <ulink
url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
- <para>This setting is supported only if the unified control group hierarchy is used and disables
- <varname>MemoryLimit=</varname>.</para>
-
<para>Units may have their children use a default <literal>memory.min</literal> or
<literal>memory.low</literal> value by specifying <varname>DefaultMemoryMin=</varname> or
<varname>DefaultMemoryLow=</varname>, which has the same semantics as
special value <literal>infinity</literal>, no memory throttling is applied. This controls the
<literal>memory.high</literal> control group attribute. For details about this control group attribute, see
<ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
-
- <para>This setting is supported only if the unified control group hierarchy is used and disables
- <varname>MemoryLimit=</varname>.</para>
</listitem>
</varlistentry>
assigned the special value <literal>infinity</literal>, no memory limit is applied. This controls the
<literal>memory.max</literal> control group attribute. For details about this control group attribute, see
<ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
-
- <para>This setting replaces <varname>MemoryLimit=</varname>.</para>
</listitem>
</varlistentry>
special value <literal>infinity</literal>, no swap limit is applied. This controls the
<literal>memory.swap.max</literal> control group attribute. For details about this control group attribute,
see <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files">Memory Interface Files</ulink>.</para>
-
- <para>This setting is supported only if the unified control group hierarchy is used and disables
- <varname>MemoryLimit=</varname>.</para>
</listitem>
</varlistentry>
<term><varname>TasksMax=<replaceable>N</replaceable></varname></term>
<listitem>
- <para>Specify the maximum number of tasks that may be created in the unit. This ensures that the number of
- tasks accounted for the unit (see above) stays below a specific limit. This either takes an absolute number
- of tasks or a percentage value that is taken relative to the configured maximum number of tasks on the
- system. If assigned the special value <literal>infinity</literal>, no tasks limit is applied. This controls
- the <literal>pids.max</literal> control group attribute. For details about this control group attribute, see
- <ulink url="https://docs.kernel.org/admin-guide/cgroup-v1/pids.html">Process Number Controller</ulink>.
- </para>
+ <para>Specify the maximum number of tasks that may be created in the unit. This ensures that the
+ number of tasks accounted for the unit (see above) stays below a specific limit. This either takes
+ an absolute number of tasks or a percentage value that is taken relative to the configured maximum
+ number of tasks on the system. If assigned the special value <literal>infinity</literal>, no tasks
+ limit is applied. This controls the <literal>pids.max</literal> control group attribute. For
+ details about this control group attribute, the
+ <ulink url="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#pid">pids controller
+ </ulink>.</para>
<para>The system default for this setting may be controlled with
<varname>DefaultTasksMax=</varname> in
therein. The system default for this setting may be controlled with <varname>DefaultIOAccounting=</varname>
in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
-
- <para>This setting replaces <varname>BlockIOAccounting=</varname> and disables settings prefixed with
- <varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
</listitem>
</varlistentry>
the system, and if the former is not set also to the startup
and shutdown phases. This allows prioritizing specific services at boot-up
and shutdown differently than during runtime.</para>
-
- <para>These settings replace <varname>BlockIOWeight=</varname> and <varname>StartupBlockIOWeight=</varname>
- and disable settings prefixed with <varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
</listitem>
</varlistentry>
For details about this control group attribute, see <ulink
url="https://docs.kernel.org/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.</para>
- <para>This setting replaces <varname>BlockIODeviceWeight=</varname> and disables settings prefixed with
- <varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
-
<para>The specified device node should reference a block device that has an I/O scheduler
associated, i.e. should not refer to partition or loopback block devices, but to the originating,
physical device. When a path to a regular file or directory is specified it is attempted to
url="https://docs.kernel.org/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.
</para>
- <para>These settings replace <varname>BlockIOReadBandwidth=</varname> and
- <varname>BlockIOWriteBandwidth=</varname> and disable settings prefixed with <varname>BlockIO</varname> or
- <varname>StartupBlockIO</varname>.</para>
-
<para>Similar restrictions on block device discovery as for <varname>IODeviceWeight=</varname> apply, see above.</para>
</listitem>
</varlistentry>
url="https://docs.kernel.org/admin-guide/cgroup-v2.html#io-interface-files">IO Interface Files</ulink>.
</para>
- <para>These settings are supported only if the unified control group hierarchy is used and disable settings
- prefixed with <varname>BlockIO</varname> or <varname>StartupBlockIO</varname>.</para>
-
<para>Similar restrictions on block device discovery as for <varname>IODeviceWeight=</varname> apply, see above.</para>
</listitem>
</varlistentry>
strings: a device node specifier followed by a combination of <constant>r</constant>,
<constant>w</constant>, <constant>m</constant> to control <emphasis>r</emphasis>eading,
<emphasis>w</emphasis>riting, or creation of the specific device node(s) by the unit
- (<emphasis>m</emphasis>knod), respectively. On cgroup-v1 this controls the
- <literal>devices.allow</literal> control group attribute. For details about this control group
- attribute, see <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v1/devices.html">Device Whitelist Controller</ulink>.
- In the unified cgroup hierarchy this functionality is implemented using eBPF filtering.</para>
+ (<emphasis>m</emphasis>knod), respectively. This functionality is implemented using eBPF
+ filtering.</para>
<para>When access to <emphasis>all</emphasis> physical devices should be disallowed,
<varname>PrivateDevices=</varname> may be used instead. See
</refsect1>
<refsect1>
- <title>Deprecated Options</title>
-
- <para>The following options are deprecated. Use the indicated superseding options instead:</para>
-
- <variablelist class='unit-directives'>
-
- <varlistentry>
- <term><varname>CPUShares=<replaceable>weight</replaceable></varname></term>
- <term><varname>StartupCPUShares=<replaceable>weight</replaceable></varname></term>
-
- <listitem>
- <para>Assign the specified CPU time share weight to the processes executed. These options take an integer
- value and control the <literal>cpu.shares</literal> control group attribute. The allowed range is 2 to
- 262144. Defaults to 1024. For details about this control group attribute, see <ulink
- url="https://docs.kernel.org/scheduler/sched-design-CFS.html">CFS Scheduler</ulink>.
- The available CPU time is split up among all units within one slice relative to their CPU time share
- weight.</para>
-
- <para>While <varname>StartupCPUShares=</varname> applies to the startup and shutdown phases of the system,
- <varname>CPUShares=</varname> applies to normal runtime of the system, and if the former is not set also to
- the startup and shutdown phases. Using <varname>StartupCPUShares=</varname> allows prioritizing specific services at
- boot-up and shutdown differently than during normal runtime.</para>
-
- <para>Implies <literal>CPUAccounting=yes</literal>.</para>
-
- <para>These settings are deprecated. Use <varname>CPUWeight=</varname> and
- <varname>StartupCPUWeight=</varname> instead.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>MemoryLimit=<replaceable>bytes</replaceable></varname></term>
-
- <listitem>
- <para>Specify the limit on maximum memory usage of the executed processes. The limit specifies how much
- process and kernel memory can be used by tasks in this unit. Takes a memory size in bytes. If the value is
- suffixed with K, M, G or T, the specified memory size is parsed as Kilobytes, Megabytes, Gigabytes, or
- Terabytes (with the base 1024), respectively. Alternatively, a percentage value may be specified, which is
- taken relative to the installed physical memory on the system. If assigned the special value
- <literal>infinity</literal>, no memory limit is applied. This controls the
- <literal>memory.limit_in_bytes</literal> control group attribute. For details about this control group
- attribute, see <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v1/memory.html">Memory Resource Controller</ulink>.</para>
-
- <para>Implies <literal>MemoryAccounting=yes</literal>.</para>
-
- <para>This setting is deprecated. Use <varname>MemoryMax=</varname> instead.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>BlockIOAccounting=</varname></term>
-
- <listitem>
- <para>Turn on Block I/O accounting for this unit, if the legacy control group hierarchy is used on the
- system. Takes a boolean argument. Note that turning on block I/O accounting for one unit will also implicitly
- turn it on for all units contained in the same slice and all for its parent slices and the units contained
- therein. The system default for this setting may be controlled with
- <varname>DefaultBlockIOAccounting=</varname> in
- <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
-
- <para>This setting is deprecated. Use <varname>IOAccounting=</varname> instead.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>BlockIOWeight=<replaceable>weight</replaceable></varname></term>
- <term><varname>StartupBlockIOWeight=<replaceable>weight</replaceable></varname></term>
-
- <listitem><para>Set the default overall block I/O weight for the executed processes, if the legacy control
- group hierarchy is used on the system. Takes a single weight value (between 10 and 1000) to set the default
- block I/O weight. This controls the <literal>blkio.weight</literal> control group attribute, which defaults to
- 500. For details about this control group attribute, see <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v1/blkio-controller.html">Block IO Controller</ulink>.
- The available I/O bandwidth is split up among all units within one slice relative to their block I/O
- weight.</para>
-
- <para>While <varname>StartupBlockIOWeight=</varname> only
- applies to the startup and shutdown phases of the system,
- <varname>BlockIOWeight=</varname> applies to the later runtime
- of the system, and if the former is not set also to the
- startup and shutdown phases. This allows prioritizing specific services at
- boot-up and shutdown differently than during runtime.</para>
-
- <para>Implies
- <literal>BlockIOAccounting=yes</literal>.</para>
-
- <para>These settings are deprecated. Use <varname>IOWeight=</varname> and <varname>StartupIOWeight=</varname>
- instead.</para>
-
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>BlockIODeviceWeight=<replaceable>device</replaceable> <replaceable>weight</replaceable></varname></term>
+ <title>History</title>
- <listitem>
- <para>Set the per-device overall block I/O weight for the executed processes, if the legacy control group
- hierarchy is used on the system. Takes a space-separated pair of a file path and a weight value to specify
- the device specific weight value, between 10 and 1000. (Example: "/dev/sda 500"). The file path may be
- specified as path to a block device node or as any other file, in which case the backing block device of the
- file system of the file is determined. This controls the <literal>blkio.weight_device</literal> control group
- attribute, which defaults to 1000. Use this option multiple times to set weights for multiple devices. For
- details about this control group attribute, see <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v1/blkio-controller.html">Block IO Controller</ulink>.</para>
-
- <para>Implies
- <literal>BlockIOAccounting=yes</literal>.</para>
-
- <para>This setting is deprecated. Use <varname>IODeviceWeight=</varname> instead.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>BlockIOReadBandwidth=<replaceable>device</replaceable> <replaceable>bytes</replaceable></varname></term>
- <term><varname>BlockIOWriteBandwidth=<replaceable>device</replaceable> <replaceable>bytes</replaceable></varname></term>
-
- <listitem>
- <para>Set the per-device overall block I/O bandwidth limit for the executed processes, if the legacy control
- group hierarchy is used on the system. Takes a space-separated pair of a file path and a bandwidth value (in
- bytes per second) to specify the device specific bandwidth. The file path may be a path to a block device
- node, or as any other file in which case the backing block device of the file system of the file is used. If
- the bandwidth is suffixed with K, M, G, or T, the specified bandwidth is parsed as Kilobytes, Megabytes,
- Gigabytes, or Terabytes, respectively, to the base of 1000. (Example:
- "/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 5M"). This controls the
- <literal>blkio.throttle.read_bps_device</literal> and <literal>blkio.throttle.write_bps_device</literal>
- control group attributes. Use this option multiple times to set bandwidth limits for multiple devices. For
- details about these control group attributes, see <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v1/blkio-controller.html">Block IO Controller</ulink>.
- </para>
-
- <para>Implies
- <literal>BlockIOAccounting=yes</literal>.</para>
-
- <para>These settings are deprecated. Use <varname>IOReadBandwidthMax=</varname> and
- <varname>IOWriteBandwidthMax=</varname> instead.</para>
- </listitem>
- </varlistentry>
-
- </variablelist>
+ <variablelist>
+ <varlistentry>
+ <term>systemd 252</term>
+ <listitem><para> Options for controlling the Legacy Control Group Hierarchy (<ulink
+ url="https://docs.kernel.org/admin-guide/cgroup-v1/index.html">Control Groups version 1</ulink> are
+ now fully deprecated: <varname>CPUShares=<replaceable>weight</replaceable></varname>,
+ <varname>StartupCPUShares=<replaceable>weight</replaceable></varname>,
+ <varname>MemoryLimit=<replaceable>bytes</replaceable></varname>,
+ <varname>BlockIOAccounting=</varname>,
+ <varname>BlockIOWeight=<replaceable>weight</replaceable></varname>,
+ <varname>StartupBlockIOWeight=<replaceable>weight</replaceable></varname>,
+ <varname>BlockIODeviceWeight=<replaceable>device</replaceable>
+ <replaceable>weight</replaceable></varname>,
+ <varname>BlockIOReadBandwidth=<replaceable>device</replaceable>
+ <replaceable>bytes</replaceable></varname>,
+ <varname>BlockIOWriteBandwidth=<replaceable>device</replaceable>
+ <replaceable>bytes</replaceable></varname>.
+ Please switch to the unified cgroup hierarchy.</para></listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
<refsect1>
<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>,
<term><varname>ConditionControlGroupController=</varname></term>
<listitem><para>Check whether given cgroup controllers (e.g. <literal>cpu</literal>) are available
- for use on the system or whether the legacy v1 cgroup or the modern v2 cgroup hierarchy is used.
- </para>
+ for use on the system.</para>
<para>Multiple controllers may be passed with a space separating them; in this case the condition
will only pass if all listed controllers are available for use. Controllers unknown to systemd are
- ignored. Valid controllers are <literal>cpu</literal>, <literal>cpuacct</literal>,
- <literal>io</literal>, <literal>blkio</literal>, <literal>memory</literal>,
- <literal>devices</literal>, and <literal>pids</literal>. Even if available in the kernel, a
- particular controller may not be available if it was disabled on the kernel command line with
- <varname>cgroup_disable=controller</varname>.</para>
-
- <para>Alternatively, two special strings <literal>v1</literal> and <literal>v2</literal> may be
- specified (without any controller names). <literal>v2</literal> will pass if the unified v2 cgroup
- hierarchy is used, and <literal>v1</literal> will pass if the legacy v1 hierarchy or the hybrid
- hierarchy are used (see the discussion of <varname>systemd.unified_cgroup_hierarchy</varname> and
- <varname>systemd.legacy_systemd_cgroup_controller</varname> in
- <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for more information).</para>
- </listitem>
+ ignored. Valid controllers are <literal>cpu</literal>, <literal>cpuset</literal>,
+ <literal>io</literal>, <literal>memory</literal>, and <literal>pids</literal>. Even if available in
+ the kernel, a particular controller may not be available if it was disabled on the kernel command
+ line with <varname>cgroup_disable=controller</varname>.</para></listitem>
</varlistentry>
<varlistentry>
<row>
<entry><literal>%s</literal></entry>
<entry>User shell</entry>
- <entry>This is the shell of the user running the service manager instance. In case of the system manager this resolves to <literal>/bin/sh</literal>.</entry>
+ <entry>This is the shell of the user running the service manager instance.</entry>
</row>
<row>
<entry><literal>%S</literal></entry>
memory its accounting data is flushed out too. However, this data is generally not lost, as a journal log record
is generated declaring the consumed resources whenever a unit shuts down.</para>
- <para>Processes systemd spawns are placed in individual Linux
- control groups named after the unit which they belong to in the
- private systemd hierarchy. (see <ulink
- url="https://docs.kernel.org/admin-guide/cgroup-v1/index.html">Control Groups version 1</ulink>
- for more information about control groups, or short "cgroups").
- systemd uses this to effectively keep track of processes. Control
- group information is maintained in the kernel, and is accessible
- via the file system hierarchy (beneath
- <filename>/sys/fs/cgroup/systemd/</filename>), or in tools such as
- <citerefentry project='man-pages'><refentrytitle>systemd-cgls</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- or
- <citerefentry project='man-pages'><refentrytitle>ps</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- (<command>ps xawf -eo pid,user,cgroup,args</command> is
- particularly useful to list all processes and the systemd units
- they belong to.).</para>
+ <para>Processes systemd spawns are placed in individual Linux control groups named after the unit which
+ they belong to in the private systemd hierarchy. (see <ulink
+ url="https://docs.kernel.org/admin-guide/cgroup-v2.html">Control Groups v2</ulink> for more information
+ about control groups, or short "cgroups"). systemd uses this to effectively keep track of
+ processes. Control group information is maintained in the kernel, and is accessible via the file system
+ hierarchy (beneath <filename>/sys/fs/cgroup/</filename>), or in tools such as <citerefentry
+ project='man-pages'><refentrytitle>systemd-cgls</refentrytitle><manvolnum>1</manvolnum></citerefentry> or
+ <citerefentry
+ project='man-pages'><refentrytitle>ps</refentrytitle><manvolnum>1</manvolnum></citerefentry> (<command>ps
+ xawf -eo pid,user,cgroup,args</command> is particularly useful to list all processes and the systemd
+ units they belong to.).</para>
<para>systemd is compatible with the SysV init system to a large
degree: SysV init scripts are supported and simply read as an
for every boot.</para></listitem>
</varlistentry>
- <varlistentry>
- <term><varname>systemd.unified_cgroup_hierarchy</varname></term>
-
- <listitem><para>When specified without an argument or with a true argument,
- enables the usage of
- <ulink url="https://docs.kernel.org/admin-guide/cgroup-v2.html">unified cgroup hierarchy</ulink>
- (a.k.a. cgroups-v2). When specified with a false argument, fall back to
- hybrid or full legacy cgroup hierarchy.</para>
-
- <para>If this option is not specified, the default behaviour is determined
- during compilation (the <option>-Ddefault-hierarchy=</option> meson
- option). If the kernel does not support unified cgroup hierarchy, the legacy
- hierarchy will be used even if this option is specified.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>systemd.legacy_systemd_cgroup_controller</varname></term>
-
- <listitem><para>Takes effect if the full unified cgroup hierarchy is not used
- (see previous option). When specified without an argument or with a true
- argument, disables the use of "hybrid" cgroup hierarchy (i.e. a cgroups-v2
- tree used for systemd, and
- <ulink url="https://docs.kernel.org/admin-guide/cgroup-v1/index.html">legacy
- cgroup hierarchy</ulink>, a.k.a. cgroups-v1, for other controllers), and
- forces a full "legacy" mode. When specified with a false argument, enables
- the use of "hybrid" hierarchy.</para>
-
- <para>If this option is not specified, the default behaviour is determined
- during compilation (the <option>-Ddefault-hierarchy=</option> meson
- option). If the kernel does not support unified cgroup hierarchy, the legacy
- hierarchy will be used even if this option is specified.</para>
- </listitem>
- </varlistentry>
-
<varlistentry>
<term><varname>systemd.set_credential=</varname></term>
</variablelist>
</refsect1>
+ <refsect1>
+ <title>History</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>systemd 252</term>
+ <listitem><para>Kernel command-line arguments <varname>systemd.unified_cgroup_hierarchy</varname>
+ and <varname>systemd.legacy_systemd_cgroup_controller</varname> were deprecated. Please switch to
+ the unified cgroup hierarchy.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1>
<title>See Also</title>
<para>
option('time-epoch', type : 'integer', value : 0,
description : 'time epoch for time clients')
option('clock-valid-range-usec-max', type : 'integer', value : 473364000000000, # 15 years
- description : 'maximum value in microseconds for the difference between RTC and epoch, exceeding which is considered an RTC error')
+ description : 'maximum value in microseconds for the difference between RTC and epoch, exceeding which is considered an RTC error ["0" disables]')
option('default-user-shell', type : 'string', value : '/bin/bash',
description : 'default interactive shell')
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;
}
#include "chase-symlinks.h"
#include "devnum-util.h"
+#include "fs-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "string-util.h"
return 0;
}
+int device_path_make_major_minor_sysfs(mode_t mode, dev_t devnum, char **ret) {
+ _cleanup_free_ char *syspath = NULL, *link = NULL, *fname = NULL;
+ _cleanup_free_ char *devpath = NULL;
+ const char *t;
+ int r;
+
+ /* Generates the /dev/... path given a dev_t. What makes this different
+ * from device_path_make_major_minor is that it works even when udev
+ * hasn't yet run */
+
+ if (S_ISCHR(mode))
+ t = "char";
+ else if (S_ISBLK(mode))
+ t = "block";
+ else
+ return -ENODEV;
+
+ if (asprintf(&syspath, "/sys/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0)
+ return -ENOMEM;
+
+ r = readlink_malloc(syspath, &link);
+ if (r < 0)
+ return r;
+
+ r = path_extract_filename(link, &fname);
+ if (r < 0)
+ return r;
+
+ devpath = path_join("/dev", fname);
+ if (!devpath)
+ return -ENOMEM;
+
+ struct stat st;
+ if (stat(devpath, &st) < 0)
+ return -errno;
+
+ if (((st.st_mode ^ mode) & S_IFMT) != 0)
+ return S_ISBLK(mode) ? -ENOTBLK : -ENODEV;
+
+ if (st.st_rdev != devnum)
+ return -ENXIO;
+
+ *ret = TAKE_PTR(devpath);
+
+ return 0;
+}
+
int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) {
_cleanup_free_ char *p = NULL;
int r;
})
int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
+int device_path_make_major_minor_sysfs(mode_t mode, dev_t devnum, char **ret);
int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret);
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum);
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);
#include <syslog.h>
#include "macro.h"
+#include "ratelimit.h"
/* Some structures we reference but don't want to pull in headers for */
struct iovec;
#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
void log_setup(void);
+
+typedef struct LogRateLimit {
+ int error;
+ int level;
+ RateLimit ratelimit;
+} LogRateLimit;
+
+#define log_ratelimit_internal(_level, _error, _format, _file, _line, _func, ...) \
+({ \
+ int _log_ratelimit_error = (_error); \
+ int _log_ratelimit_level = (_level); \
+ static LogRateLimit _log_ratelimit = { \
+ .ratelimit = { \
+ .interval = 1 * USEC_PER_SEC, \
+ .burst = 1, \
+ }, \
+ }; \
+ unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \
+ if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \
+ ratelimit_reset(&_log_ratelimit.ratelimit); \
+ _log_ratelimit.error = _log_ratelimit_error; \
+ _log_ratelimit.level = _log_ratelimit_level; \
+ } \
+ if (ratelimit_below(&_log_ratelimit.ratelimit)) \
+ _log_ratelimit_error = _num_dropped_errors > 0 \
+ ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", __VA_ARGS__, _num_dropped_errors) \
+ : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, __VA_ARGS__); \
+ _log_ratelimit_error; \
+})
+
+#define log_ratelimit_full_errno(level, error, format, ...) \
+ ({ \
+ int _level = (level), _e = (error); \
+ _e = (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_ratelimit_internal(_level, _e, format, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
+ : -ERRNO_VALUE(_e); \
+ _e < 0 ? _e : -ESTRPIPE; \
+ })
}
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);
if (r->num < r->burst)
goto good;
+ r->num++;
return false;
good:
r->num++;
return true;
}
+
+unsigned ratelimit_num_dropped(RateLimit *r) {
+ assert(r);
+
+ return r->num > r->burst ? r->num - r->burst : 0;
+}
#include <stdbool.h>
#include "time-util.h"
-#include "util.h"
typedef struct RateLimit {
usec_t interval; /* Keep those two fields first so they can be initialized easily: */
}
bool ratelimit_below(RateLimit *r);
+
+unsigned ratelimit_num_dropped(RateLimit *r);
}
static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
+ if (delta == INT64_MIN) { /* prevent overflow */
+ assert_cc(-(INT64_MIN + 1) == INT64_MAX);
+ assert_cc(USEC_INFINITY > INT64_MAX);
+ return usec_add(timestamp, (usec_t) INT64_MAX + 1);
+ }
if (delta < 0)
return usec_add(timestamp, (usec_t) (-delta));
- else
- return usec_sub_unsigned(timestamp, (usec_t) delta);
+
+ return usec_sub_unsigned(timestamp, (usec_t) delta);
}
#if SIZEOF_TIME_T == 8
#include "sd-messages.h"
#include "alloc-util.h"
+#include "chase-symlinks.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
}
bool is_nologin_shell(const char *shell) {
-
return PATH_IN_SET(shell,
/* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
* message and exits. Different distributions place the binary at different places though,
"/usr/bin/true");
}
+const char* default_root_shell(const char *root) {
+ /* We want to use the preferred shell, i.e. DEFAULT_USER_SHELL, which usually
+ * will be /bin/bash. Fall back to /bin/sh if DEFAULT_USER_SHELL is not found,
+ * or any access errors. */
+
+ int r = chase_symlinks(DEFAULT_USER_SHELL, root, CHASE_PREFIX_ROOT, NULL, NULL);
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to look up shell '%s%s%s': %m",
+ strempty(root), root ? "/" : "", DEFAULT_USER_SHELL);
+ if (r > 0)
+ return DEFAULT_USER_SHELL;
+
+ return "/bin/sh";
+}
+
static int synthesize_user_creds(
const char **username,
uid_t *uid, gid_t *gid,
*home = "/root";
if (shell)
- *shell = "/bin/sh";
+ *shell = default_root_shell(NULL);
return 0;
}
- if (synthesize_nobody() &&
- STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
+ if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534") &&
+ synthesize_nobody()) {
*username = NOBODY_USER_NAME;
if (uid)
return 0;
}
- if (synthesize_nobody() &&
- STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
+ if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534") &&
+ synthesize_nobody()) {
*groupname = NOBODY_GROUP_NAME;
if (gid)
/* Shortcut things to avoid NSS lookups */
if (uid == 0)
return strdup("root");
- if (synthesize_nobody() &&
- uid == UID_NOBODY)
+ if (uid == UID_NOBODY && synthesize_nobody())
return strdup(NOBODY_USER_NAME);
if (uid_is_valid(uid)) {
if (gid == 0)
return strdup("root");
- if (synthesize_nobody() &&
- gid == GID_NOBODY)
+ if (gid == GID_NOBODY && synthesize_nobody())
return strdup(NOBODY_GROUP_NAME);
if (gid_is_valid(gid)) {
return ngroups;
}
-int get_home_dir(char **_h) {
+int get_home_dir(char **ret) {
struct passwd *p;
const char *e;
char *h;
uid_t u;
- assert(_h);
+ assert(ret);
/* Take the user specified one */
e = secure_getenv("HOME");
- if (e && path_is_valid(e) && path_is_absolute(e)) {
- h = strdup(e);
- if (!h)
- return -ENOMEM;
-
- *_h = path_simplify(h);
- return 0;
- }
+ if (e && path_is_valid(e) && path_is_absolute(e))
+ goto found;
/* Hardcode home directory for root and nobody to avoid NSS */
u = getuid();
if (u == 0) {
- h = strdup("/root");
- if (!h)
- return -ENOMEM;
-
- *_h = h;
- return 0;
+ e = "/root";
+ goto found;
}
- if (synthesize_nobody() &&
- u == UID_NOBODY) {
- h = strdup("/");
- if (!h)
- return -ENOMEM;
- *_h = h;
- return 0;
+ if (u == UID_NOBODY && synthesize_nobody()) {
+ e = "/";
+ goto found;
}
/* Check the database... */
p = getpwuid(u);
if (!p)
return errno_or_else(ESRCH);
+ e = p->pw_dir;
- if (!path_is_valid(p->pw_dir) ||
- !path_is_absolute(p->pw_dir))
+ if (!path_is_valid(e) || !path_is_absolute(e))
return -EINVAL;
- h = strdup(p->pw_dir);
+ found:
+ h = strdup(e);
if (!h)
return -ENOMEM;
- *_h = path_simplify(h);
+ *ret = path_simplify(h);
return 0;
}
-int get_shell(char **_s) {
+int get_shell(char **ret) {
struct passwd *p;
const char *e;
char *s;
uid_t u;
- assert(_s);
+ assert(ret);
/* Take the user specified one */
e = secure_getenv("SHELL");
- if (e && path_is_valid(e) && path_is_absolute(e)) {
- s = strdup(e);
- if (!s)
- return -ENOMEM;
-
- *_s = path_simplify(s);
- return 0;
- }
+ if (e && path_is_valid(e) && path_is_absolute(e))
+ goto found;
/* Hardcode shell for root and nobody to avoid NSS */
u = getuid();
if (u == 0) {
- s = strdup("/bin/sh");
- if (!s)
- return -ENOMEM;
-
- *_s = s;
- return 0;
+ e = default_root_shell(NULL);
+ goto found;
}
- if (synthesize_nobody() &&
- u == UID_NOBODY) {
- s = strdup(NOLOGIN);
- if (!s)
- return -ENOMEM;
-
- *_s = s;
- return 0;
+ if (u == UID_NOBODY && synthesize_nobody()) {
+ e = NOLOGIN;
+ goto found;
}
/* Check the database... */
p = getpwuid(u);
if (!p)
return errno_or_else(ESRCH);
+ e = p->pw_shell;
- if (!path_is_valid(p->pw_shell) ||
- !path_is_absolute(p->pw_shell))
+ if (!path_is_valid(e) || !path_is_absolute(e))
return -EINVAL;
- s = strdup(p->pw_shell);
+ found:
+ s = strdup(e);
if (!s)
return -ENOMEM;
- *_s = path_simplify(s);
+ *ret = path_simplify(s);
return 0;
}
int getgroups_alloc(gid_t** gids);
int get_home_dir(char **ret);
-int get_shell(char **_ret);
+int get_shell(char **ret);
int reset_uid_gid(void);
#endif
bool is_nologin_shell(const char *shell);
+const char* default_root_shell(const char *root);
int is_this_me(const char *username);
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);
DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint64_t, "Invalid block IO weight");
DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight");
DEFINE_CONFIG_PARSE_PTR(config_parse_cg_cpu_weight, cg_cpu_weight_parse, uint64_t, "Invalid CPU weight");
-DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
+static DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares_internal, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_status_unit_format, status_unit_format, StatusUnitFormat, "Failed to parse status unit format");
DEFINE_CONFIG_PARSE_ENUM_FULL(config_parse_socket_timestamping, socket_timestamping_from_string_harder, SocketTimestamping, "Failed to parse timestamping precision");
+int config_parse_cpu_shares(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Unit uses %s=; please use CPUWeight= instead. Support for %s= will be removed soon.",
+ lvalue, lvalue);
+
+ return config_parse_cpu_shares_internal(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
+}
+
bool contains_instance_specifier_superset(const char *s) {
const char *p, *q;
bool percent = false;
if (m == KILL_NONE)
log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Unit configured to use KillMode=none. "
+ "Unit uses KillMode=none. "
"This is unsafe, as it disables systemd's process lifecycle management for the service. "
- "Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. "
+ "Please update the service to use a safer KillMode=, such as 'mixed' or 'control-group'. "
"Support for KillMode=none is deprecated and will eventually be removed.");
*k = m;
c->memory_max = bytes;
else if (streq(lvalue, "MemorySwapMax"))
c->memory_swap_max = bytes;
- else if (streq(lvalue, "MemoryLimit"))
+ else if (streq(lvalue, "MemoryLimit")) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Unit uses MemoryLimit=; please use MemoryMax= instead. Support for MemoryLimit= will be removed soon.");
c->memory_limit = bytes;
- else
+ } else
return -EINVAL;
return 0;
assert(lvalue);
assert(rvalue);
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Unit uses %s=; please use IO*= settings instead. Support for %s= will be removed soon.",
+ lvalue, lvalue);
+
if (isempty(rvalue)) {
while (c->blockio_device_weights)
cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
assert(lvalue);
assert(rvalue);
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Unit uses %s=; please use IO*= settings instead. Support for %s= will be removed soon.",
+ lvalue, lvalue);
+
read = streq("BlockIOReadBandwidth", lvalue);
if (isempty(rvalue)) {
static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
+static void mount_enter_dead(Mount *m, MountResult f);
+static void mount_enter_mounted(Mount *m, MountResult f);
+static void mount_cycle_clear(Mount *m);
static int mount_process_proc_self_mountinfo(Manager *m);
static bool MOUNT_STATE_WITH_PROCESS(MountState state) {
/* 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)
static int mount_coldplug(Unit *u) {
Mount *m = MOUNT(u);
- MountState new_state = MOUNT_DEAD;
int r;
assert(m);
assert(m->state == MOUNT_DEAD);
- if (m->deserialized_state != m->state)
- new_state = m->deserialized_state;
- else if (m->from_proc_self_mountinfo)
- new_state = MOUNT_MOUNTED;
-
- if (new_state == m->state)
+ if (m->deserialized_state == m->state)
return 0;
if (m->control_pid > 0 &&
pid_is_unwaited(m->control_pid) &&
- MOUNT_STATE_WITH_PROCESS(new_state)) {
+ MOUNT_STATE_WITH_PROCESS(m->deserialized_state)) {
r = unit_watch_pid(UNIT(m), m->control_pid, false);
if (r < 0)
return r;
}
- if (!IN_SET(new_state, MOUNT_DEAD, MOUNT_FAILED)) {
+ if (!IN_SET(m->deserialized_state, MOUNT_DEAD, MOUNT_FAILED)) {
(void) unit_setup_dynamic_creds(u);
(void) unit_setup_exec_runtime(u);
}
- mount_set_state(m, new_state);
+ mount_set_state(m, m->deserialized_state);
return 0;
}
+static void mount_catchup(Unit *u) {
+ Mount *m = MOUNT(ASSERT_PTR(u));
+
+ assert(m);
+
+ /* Adjust the deserialized state. See comments in mount_process_proc_self_mountinfo(). */
+ if (m->from_proc_self_mountinfo)
+ switch (m->state) {
+ case MOUNT_DEAD:
+ case MOUNT_FAILED:
+ assert(m->control_pid == 0);
+ unit_acquire_invocation_id(u);
+ mount_cycle_clear(m);
+ mount_enter_mounted(m, MOUNT_SUCCESS);
+ break;
+ case MOUNT_MOUNTING:
+ assert(m->control_pid > 0);
+ mount_set_state(m, MOUNT_MOUNTING_DONE);
+ break;
+ default:
+ break;
+ }
+ else
+ switch (m->state) {
+ case MOUNT_MOUNTING_DONE:
+ assert(m->control_pid > 0);
+ mount_set_state(m, MOUNT_MOUNTING);
+ break;
+ case MOUNT_MOUNTED:
+ assert(m->control_pid == 0);
+ mount_enter_dead(m, MOUNT_SUCCESS);
+ break;
+ default:
+ break;
+ }
+}
+
static void mount_dump(Unit *u, FILE *f, const char *prefix) {
Mount *m = MOUNT(u);
MountParameters *p;
.done = mount_done,
.coldplug = mount_coldplug,
+ .catchup = mount_catchup,
.dump = mount_dump,
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;
#DefaultCPUAccounting=no
#DefaultIOAccounting=no
#DefaultIPAccounting=no
-#DefaultBlockIOAccounting=no
#DefaultMemoryAccounting={{ 'yes' if MEMORY_ACCOUNTING_DEFAULT else 'no' }}
#DefaultTasksAccounting=yes
#DefaultTasksMax=15%
}
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)
.pw_gid = 0,
.pw_gecos = (char *) "Super User",
.pw_dir = (char *) "/root",
- .pw_shell = (char *) (shell ?: "/bin/sh"),
+ .pw_shell = (char *) (shell ?: default_root_shell(arg_root)),
};
if (errno != ENOENT)
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 (vacuumed || !shall_try_append_again(f->file, r)) {
- log_error_errno(r, "Failed to write entry (%zu items, %zu bytes), ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n));
+ log_ratelimit_full_errno(LOG_ERR, r, "Failed to write entry (%zu items, %zu bytes), ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n));
return;
}
if (r == -E2BIG)
log_debug("Journal file %s is full, rotating to a new file", f->file->path);
else
- log_info_errno(r, "Failed to write entry to %s (%zu items, %zu bytes), rotating before retrying: %m", f->file->path, n, IOVEC_TOTAL_SIZE(iovec, n));
+ log_ratelimit_full_errno(LOG_INFO, r, "Failed to write entry to %s (%zu items, %zu bytes), rotating before retrying: %m", f->file->path, n, IOVEC_TOTAL_SIZE(iovec, n));
server_rotate(s);
server_vacuum(s, false);
log_debug("Retrying write.");
r = journal_file_append_entry(f->file, &ts, NULL, iovec, n, &s->seqnum, NULL, NULL);
if (r < 0)
- log_error_errno(r, "Failed to write entry to %s (%zu items, %zu bytes) despite vacuuming, ignoring: %m", f->file->path, n, IOVEC_TOTAL_SIZE(iovec, n));
+ log_ratelimit_full_errno(LOG_ERR, r, "Failed to write entry to %s (%zu items, %zu bytes) despite vacuuming, ignoring: %m", f->file->path, n, IOVEC_TOTAL_SIZE(iovec, n));
else
server_schedule_sync(s, priority);
}
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);
}
int device_get_property_bool(sd_device *device, const char *key);
+int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_device_id(sd_device *device, const char **ret);
int device_get_devlink_priority(sd_device *device, int *ret);
return 0;
}
+int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
+ const char *value;
+ int r;
+
+ r = sd_device_get_sysattr_value(device, sysattr, &value);
+ if (r < 0)
+ return r;
+
+ unsigned v;
+ r = safe_atou(value, &v);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
+
+ if (ret_value)
+ *ret_value = v;
+ /* We return "true" if the value is positive. */
+ return v > 0;
+}
+
int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
const char *value;
int r;
r = sd_device_get_sysattr_value(d, "name_assign_type", &val);
assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || IN_SET(r, -ENOENT, -EINVAL));
+
+ if (r > 0) {
+ unsigned x;
+
+ assert_se(device_get_sysattr_unsigned(d, "name_assign_type", NULL) >= 0);
+ assert_se(device_get_sysattr_unsigned(d, "name_assign_type", &x) >= 0);
+ }
}
TEST(sd_device_enumerator_devices) {
_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;
}
.pw_gid = 0,
.pw_gecos = (char*) "Super User",
.pw_dir = (char*) "/root",
- .pw_shell = (char*) "/bin/sh",
+ .pw_shell = NULL,
};
static const struct spwd root_spwd = {
static enum nss_status copy_synthesized_passwd(
struct passwd *dest,
const struct passwd *src,
+ const char *fallback_shell,
char *buffer, size_t buflen,
int *errnop) {
- size_t required;
-
assert(dest);
assert(src);
assert(src->pw_name);
assert(src->pw_passwd);
assert(src->pw_gecos);
assert(src->pw_dir);
- assert(src->pw_shell);
- required = strlen(src->pw_name) + 1;
- required += strlen(src->pw_passwd) + 1;
- required += strlen(src->pw_gecos) + 1;
- required += strlen(src->pw_dir) + 1;
- required += strlen(src->pw_shell) + 1;
+ const char *shell = ASSERT_PTR(src->pw_shell ?: fallback_shell);
+
+ size_t required =
+ strlen(src->pw_name) + 1 +
+ strlen(src->pw_passwd) + 1 +
+ strlen(src->pw_gecos) + 1 +
+ strlen(src->pw_dir) + 1 +
+ strlen(shell) + 1;
if (buflen < required) {
*errnop = ERANGE;
dest->pw_gecos = stpcpy(dest->pw_passwd, src->pw_passwd) + 1;
dest->pw_dir = stpcpy(dest->pw_gecos, src->pw_gecos) + 1;
dest->pw_shell = stpcpy(dest->pw_dir, src->pw_dir) + 1;
- strcpy(dest->pw_shell, src->pw_shell);
+ strcpy(dest->pw_shell, shell);
return NSS_STATUS_SUCCESS;
}
char *buffer, size_t buflen,
int *errnop) {
- size_t required;
-
assert(dest);
assert(src);
assert(src->sp_namp);
assert(src->sp_pwdp);
- required = strlen(src->sp_namp) + 1;
- required += strlen(src->sp_pwdp) + 1;
+ size_t required =
+ strlen(src->sp_namp) + 1 +
+ strlen(src->sp_pwdp) + 1;
if (buflen < required) {
*errnop = ERANGE;
char *buffer, size_t buflen,
int *errnop) {
- size_t required;
-
assert(dest);
assert(src);
assert(src->gr_name);
assert(src->gr_mem);
assert(!*src->gr_mem); /* Our synthesized records' gr_mem is always just NULL... */
- required = strlen(src->gr_name) + 1;
- required += strlen(src->gr_passwd) + 1;
- required += sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
+ size_t required =
+ strlen(src->gr_name) + 1 +
+ strlen(src->gr_passwd) + 1 +
+ sizeof(char*); /* ...but that NULL still needs to be stored into the buffer! */
if (buflen < ALIGN(required)) {
*errnop = ERANGE;
char *buffer, size_t buflen,
int *errnop) {
- size_t required;
-
assert(dest);
assert(src);
assert(src->sg_namp);
assert(src->sg_passwd);
- required = strlen(src->sg_namp) + 1;
- required += strlen(src->sg_passwd) + 1;
+ size_t required =
+ strlen(src->sg_namp) + 1 +
+ strlen(src->sg_passwd) + 1;
if (buflen < required) {
*errnop = ERANGE;
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
if (streq(name, root_passwd.pw_name))
- return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
+ return copy_synthesized_passwd(pwd, &root_passwd,
+ default_root_shell(NULL),
+ buffer, buflen, errnop);
if (streq(name, nobody_passwd.pw_name)) {
if (!synthesize_nobody())
return NSS_STATUS_NOTFOUND;
- return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
+ return copy_synthesized_passwd(pwd, &nobody_passwd,
+ NULL,
+ buffer, buflen, errnop);
}
} else if (STR_IN_SET(name, root_passwd.pw_name, nobody_passwd.pw_name))
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
if (uid == root_passwd.pw_uid)
- return copy_synthesized_passwd(pwd, &root_passwd, buffer, buflen, errnop);
+ return copy_synthesized_passwd(pwd, &root_passwd,
+ default_root_shell(NULL),
+ buffer, buflen, errnop);
if (uid == nobody_passwd.pw_uid) {
if (!synthesize_nobody())
return NSS_STATUS_NOTFOUND;
- return copy_synthesized_passwd(pwd, &nobody_passwd, buffer, buflen, errnop);
+ return copy_synthesized_passwd(pwd, &nobody_passwd,
+ NULL,
+ buffer, buflen, errnop);
}
} else if (uid == root_passwd.pw_uid || uid == nobody_passwd.pw_uid)
if (r < 0)
return log_error_errno(r, "Cannot resize LUKS device: %m");
- r = device_path_make_major_minor(S_IFBLK, main_devno, &main_devpath);
+ r = device_path_make_major_minor_sysfs(S_IFBLK, main_devno, &main_devpath);
if (r < 0)
return log_error_errno(r, "Failed to format device major/minor path: %m");
main_devpath);
log_debug("%s is %"PRIu64" bytes", main_devpath, size);
- r = device_path_make_major_minor(S_IFBLK, devno, &devpath);
+ r = device_path_make_major_minor_sysfs(S_IFBLK, devno, &devpath);
if (r < 0)
return log_error_errno(r, "Failed to format major/minor path: %m");
if (devno == main_devno)
return 0;
- r = device_path_make_major_minor(S_IFBLK, devno, &devpath);
+ r = device_path_make_major_minor_sysfs(S_IFBLK, devno, &devpath);
if (r < 0)
return log_error_errno(r, "Failed to format device major/minor path: %m");
if (r < 0)
log_warning_errno(r, "Unable to resize underlying device of \"%s\", proceeding anyway: %m", arg_target);
- r = device_path_make_major_minor(S_IFBLK, devno, &devpath);
+ r = device_path_make_major_minor_sysfs(S_IFBLK, devno, &devpath);
if (r < 0)
return log_error_errno(r, "Failed to format device major/minor path: %m");
now_usec = now(CLOCK_REALTIME);
if (now_usec < epoch_usec)
*ret_attempted_change = CLOCK_CHANGE_FORWARD;
- else if (now_usec > usec_add(epoch_usec, CLOCK_VALID_RANGE_USEC_MAX))
+ else if (CLOCK_VALID_RANGE_USEC_MAX > 0 && now_usec > usec_add(epoch_usec, CLOCK_VALID_RANGE_USEC_MAX))
*ret_attempted_change = CLOCK_CHANGE_BACKWARD;
else {
*ret_attempted_change = CLOCK_CHANGE_NOOP;
* 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)
#if HAVE_SELINUX
_cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
security_class_t sclass;
- int r;
assert(exe);
assert(label);
if (!mac_selinux_use())
return -EOPNOTSUPP;
- r = getcon_raw(&mycon);
- if (r < 0)
+ if (getcon_raw(&mycon) < 0)
return -errno;
- r = getfilecon_raw(exe, &fcon);
- if (r < 0)
+ if (getfilecon_raw(exe, &fcon) < 0)
return -errno;
sclass = string_to_security_class("process");
#endif
}
-int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
+int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **ret_label) {
#if HAVE_SELINUX
_cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
_cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
+ const char *range = NULL, *bcon_str = NULL;
security_class_t sclass;
- const char *range = NULL;
- int r;
assert(socket_fd >= 0);
assert(exe);
- assert(label);
+ assert(ret_label);
if (!mac_selinux_use())
return -EOPNOTSUPP;
- r = getcon_raw(&mycon);
- if (r < 0)
+ if (getcon_raw(&mycon) < 0)
return -errno;
- r = getpeercon_raw(socket_fd, &peercon);
- if (r < 0)
+ if (getpeercon_raw(socket_fd, &peercon) < 0)
return -errno;
- if (!exec_label) {
- /* If there is no context set for next exec let's use context
- of target executable */
- r = getfilecon_raw(exe, &fcon);
- if (r < 0)
+ if (!exec_label) /* If there is no context set for next exec let's use context of target executable */
+ if (getfilecon_raw(exe, &fcon) < 0)
return -errno;
- }
bcon = context_new(mycon);
if (!bcon)
if (!range)
return -errno;
- r = context_range_set(bcon, range);
- if (r)
+ if (context_range_set(bcon, range) != 0)
return -errno;
- freecon(mycon);
- mycon = strdup(context_str(bcon));
- if (!mycon)
+ bcon_str = context_str(bcon);
+ if (!bcon_str)
return -ENOMEM;
sclass = string_to_security_class("process");
if (sclass == 0)
return -ENOSYS;
- return RET_NERRNO(security_compute_create_raw(mycon, fcon, sclass, label));
+ return RET_NERRNO(security_compute_create_raw(bcon_str, fcon, sclass, ret_label));
#else
return -EOPNOTSUPP;
#endif
_cleanup_freecon_ char *fcon = NULL;
const struct sockaddr_un *un;
bool context_changed = false;
+ size_t sz;
char *path;
int r;
if (un->sun_path[0] == 0)
goto skipped;
- path = strndupa_safe(un->sun_path,
- addrlen - offsetof(struct sockaddr_un, sun_path));
+ sz = addrlen - offsetof(struct sockaddr_un, sun_path);
+ if (sz > PATH_MAX)
+ goto skipped;
+ path = strndupa_safe(un->sun_path, sz);
/* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
mac_selinux_maybe_reload();
#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_;
int on_ac_power(void) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- bool found_offline = false, found_online = false, found_battery = false;
+ bool found_ac_online = false, found_battery = false;
sd_device *d;
int r;
return r;
FOREACH_DEVICE(e, d) {
- const char *val;
- unsigned v;
-
- r = sd_device_get_sysattr_value(d, "type", &val);
- if (r < 0) {
- log_device_debug_errno(d, r, "Failed to read 'type' sysfs attribute, ignoring: %m");
- continue;
- }
-
- /* We assume every power source is AC, except for batteries. See
+ /* See
* https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176
* for defined power source types. Also see:
* https://docs.kernel.org/admin-guide/abi-testing.html#abi-file-testing-sysfs-class-power */
- if (streq(val, "Battery")) {
- found_battery = true;
- log_device_debug(d, "The power supply is battery, ignoring.");
+
+ const char *val;
+ r = sd_device_get_sysattr_value(d, "type", &val);
+ if (r < 0) {
+ log_device_debug_errno(d, r, "Failed to read 'type' sysfs attribute, ignoring device: %m");
continue;
}
r = device_is_power_sink(d);
if (r <= 0) {
if (r < 0)
- log_device_debug_errno(d, r, "Failed to determine the current power role, ignoring: %m");
+ log_device_debug_errno(d, r, "Failed to determine the current power role, ignoring device: %m");
else
- log_device_debug(d, "USB power supply is in source mode, ignoring.");
+ log_device_debug(d, "USB power supply is in source mode, ignoring device.");
continue;
}
}
- r = sd_device_get_sysattr_value(d, "online", &val);
- if (r < 0) {
- log_device_debug_errno(d, r, "Failed to read 'online' sysfs attribute, ignoring: %m");
+ bool is_battery = streq(val, "Battery");
+ if (is_battery) {
+ r = sd_device_get_sysattr_value(d, "scope", &val);
+ if (r < 0)
+ log_device_debug_errno(d, r, "Failed to read 'scope' sysfs attribute, ignoring: %m");
+ else if (streq(val, "Device")) {
+ log_device_debug(d, "The power supply is a device battery, ignoring.");
+ continue;
+ }
+
+ found_battery = true;
+ log_device_debug(d, "The power supply is battery.");
continue;
}
- r = safe_atou(val, &v);
+ r = device_get_sysattr_unsigned(d, "online", NULL);
if (r < 0) {
- log_device_debug_errno(d, r, "Failed to parse 'online' attribute, ignoring: %m");
+ log_device_debug_errno(d, r, "Failed to query 'online' sysfs attribute: %m");
continue;
- }
-
- if (v > 0) /* At least 1 and 2 are defined as different types of 'online' */
- found_online = true;
- else
- found_offline = true;
+ } else if (r > 0) /* At least 1 and 2 are defined as different types of 'online' */
+ found_ac_online = true;
- log_device_debug(d, "The power supply is currently %s.", v > 0 ? "online" : "offline");
+ log_device_debug(d, "The power supply is currently %s.", r > 0 ? "online" : "offline");
}
- if (found_online)
- log_debug("Found at least one online non-battery power supply, system is running on AC power.");
- else if (!found_offline)
- log_debug("Found no offline non-battery power supply, assuming system is running on AC power.");
- else if (!found_battery)
- log_debug("Found no battery, assuming system is running on AC power.");
- else
- log_debug("All non-battery power supplies are offline, assuming system is running with battery.");
-
- return found_online || !found_offline || !found_battery;
+ if (found_ac_online) {
+ log_debug("Found at least one online non-battery power supply, system is running on AC.");
+ return true;
+ } else if (found_battery) {
+ log_debug("Found battery and no online power sources, assuming system is running from battery.");
+ return false;
+ } else {
+ log_debug("No power supply reported online and no battery, assuming system is running on AC.");
+ return true;
+ }
}
bool udev_available(void) {
break;
}
- if (!changed && last_try && !can_initrd) {
+ if (!changed && !last_try && !can_initrd) {
/* There are things we cannot get rid of. Loop one more time in which we will log
* with higher priority to inform the user. Note that we don't need to do this if
* there is an initrd to switch to, because that one is likely to get rid of the
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"
#include <utmp.h>
#include "alloc-util.h"
+#include "chase-symlinks.h"
#include "conf-files.h"
#include "copy.h"
#include "creds-util.h"
#include "set.h"
#include "smack-util.h"
#include "specifier.h"
+#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "sync-util.h"
}
#endif
-static const char* default_shell(uid_t uid) {
- return uid == 0 ? "/bin/sh" : NOLOGIN;
+static const char* pick_shell(const Item *i) {
+ if (i->type != ADD_USER)
+ return NULL;
+ if (i->shell)
+ return i->shell;
+ if (i->uid_set && i->uid == 0)
+ return default_root_shell(arg_root);
+ return NOLOGIN;
}
static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
/* Initialize the shell to nologin, with one exception:
* for root we patch in something special */
- .pw_shell = i->shell ?: (char*) default_shell(i->uid),
+ .pw_shell = (char*) pick_shell(i),
};
/* Try to pick up the shell for this account via the credentials logic */
r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
if (r < 0)
- return log_debug_errno(r, "Failed to open temporary copy of %s: %m", group_path);
+ return log_error_errno(r, "Failed to open temporary copy of %s: %m", group_path);
original = fopen(group_path, "re");
if (original) {
r = copy_rights_with_fallback(fileno(original), fileno(group), group_tmp);
if (r < 0)
- return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
+ return log_error_errno(r, "Failed to copy permissions from %s to %s: %m",
group_path, group_tmp);
while ((r = fgetgrent_sane(original, &gr)) > 0) {
r = putgrent_with_members(gr, group);
if (r < 0)
- return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
+ return log_error_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
gr->gr_name);
if (r > 0)
group_changed = true;
}
if (r < 0)
- return log_debug_errno(r, "Failed to read %s: %m", group_path);
+ return log_error_errno(r, "Failed to read %s: %m", group_path);
} else {
if (errno != ENOENT)
- return log_debug_errno(errno, "Failed to open %s: %m", group_path);
+ return log_error_errno(errno, "Failed to open %s: %m", group_path);
if (fchmod(fileno(group), 0644) < 0)
- return log_debug_errno(errno, "Failed to fchmod %s: %m", group_tmp);
+ return log_error_errno(errno, "Failed to fchmod %s: %m", group_tmp);
}
ORDERED_HASHMAP_FOREACH(i, todo_gids) {
r = putgrent_with_members(&n, group);
if (r < 0)
- return log_debug_errno(r, "Failed to add new group \"%s\" to temporary group file: %m",
+ return log_error_errno(r, "Failed to add new group \"%s\" to temporary group file: %m",
gr->gr_name);
group_changed = true;
while (gr) {
r = putgrent_sane(gr, group);
if (r < 0)
- return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
+ return log_error_errno(r, "Failed to add existing group \"%s\" to temporary group file: %m",
gr->gr_name);
r = fgetgrent_sane(original, &gr);
if (r < 0)
- return log_debug_errno(r, "Failed to read %s: %m", group_path);
+ return log_error_errno(r, "Failed to read %s: %m", group_path);
if (r == 0)
break;
}
r = fflush_sync_and_check(group);
if (r < 0)
- return log_debug_errno(r, "Failed to flush %s: %m", group_tmp);
+ return log_error_errno(r, "Failed to flush %s: %m", group_tmp);
if (group_changed) {
*tmpfile = TAKE_PTR(group);
r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
if (r < 0)
- return log_debug_errno(r, "Failed to open temporary copy of %s: %m", gshadow_path);
+ return log_error_errno(r, "Failed to open temporary copy of %s: %m", gshadow_path);
original = fopen(gshadow_path, "re");
if (original) {
r = copy_rights_with_fallback(fileno(original), fileno(gshadow), gshadow_tmp);
if (r < 0)
- return log_debug_errno(r, "Failed to copy permissions from %s to %s: %m",
+ return log_error_errno(r, "Failed to copy permissions from %s to %s: %m",
gshadow_path, gshadow_tmp);
while ((r = fgetsgent_sane(original, &sg)) > 0) {
r = putsgent_with_members(sg, gshadow);
if (r < 0)
- return log_debug_errno(r, "Failed to add existing group \"%s\" to temporary gshadow file: %m",
+ return log_error_errno(r, "Failed to add existing group \"%s\" to temporary gshadow file: %m",
sg->sg_namp);
if (r > 0)
group_changed = true;
} else {
if (errno != ENOENT)
- return log_debug_errno(errno, "Failed to open %s: %m", gshadow_path);
+ return log_error_errno(errno, "Failed to open %s: %m", gshadow_path);
if (fchmod(fileno(gshadow), 0000) < 0)
- return log_debug_errno(errno, "Failed to fchmod %s: %m", gshadow_tmp);
+ return log_error_errno(errno, "Failed to fchmod %s: %m", gshadow_tmp);
}
ORDERED_HASHMAP_FOREACH(i, todo_gids) {
r = putsgent_with_members(&n, gshadow);
if (r < 0)
- return log_debug_errno(r, "Failed to add new group \"%s\" to temporary gshadow file: %m",
+ return log_error_errno(r, "Failed to add new group \"%s\" to temporary gshadow file: %m",
n.sg_namp);
group_changed = true;
r = fflush_sync_and_check(gshadow);
if (r < 0)
- return log_debug_errno(r, "Failed to flush %s: %m", gshadow_tmp);
+ return log_error_errno(r, "Failed to flush %s: %m", gshadow_tmp);
if (group_changed) {
*tmpfile = TAKE_PTR(gshadow);
if (group) {
r = make_backup("/etc/group", group_path);
if (r < 0)
- return log_debug_errno(r, "Failed to make backup %s: %m", group_path);
+ return log_error_errno(r, "Failed to make backup %s: %m", group_path);
}
if (gshadow) {
r = make_backup("/etc/gshadow", gshadow_path);
if (r < 0)
- return log_debug_errno(r, "Failed to make backup %s: %m", gshadow_path);
+ return log_error_errno(r, "Failed to make backup %s: %m", gshadow_path);
}
if (passwd) {
r = make_backup("/etc/passwd", passwd_path);
if (r < 0)
- return log_debug_errno(r, "Failed to make backup %s: %m", passwd_path);
+ return log_error_errno(r, "Failed to make backup %s: %m", passwd_path);
}
if (shadow) {
r = make_backup("/etc/shadow", shadow_path);
if (r < 0)
- return log_debug_errno(r, "Failed to make backup %s: %m", shadow_path);
+ return log_error_errno(r, "Failed to make backup %s: %m", shadow_path);
}
/* And make the new files count */
if (group) {
r = rename_and_apply_smack_floor_label(group_tmp, group_path);
if (r < 0)
- return log_debug_errno(r, "Failed to rename %s to %s: %m",
+ return log_error_errno(r, "Failed to rename %s to %s: %m",
group_tmp, group_path);
group_tmp = mfree(group_tmp);
if (gshadow) {
r = rename_and_apply_smack_floor_label(gshadow_tmp, gshadow_path);
if (r < 0)
- return log_debug_errno(r, "Failed to rename %s to %s: %m",
+ return log_error_errno(r, "Failed to rename %s to %s: %m",
gshadow_tmp, gshadow_path);
gshadow_tmp = mfree(gshadow_tmp);
if (passwd) {
r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path);
if (r < 0)
- return log_debug_errno(r, "Failed to rename %s to %s: %m",
+ return log_error_errno(r, "Failed to rename %s to %s: %m",
passwd_tmp, passwd_path);
passwd_tmp = mfree(passwd_tmp);
if (shadow) {
r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path);
if (r < 0)
- return log_debug_errno(r, "Failed to rename %s to %s: %m",
+ return log_error_errno(r, "Failed to rename %s to %s: %m",
shadow_tmp, shadow_path);
shadow_tmp = mfree(shadow_tmp);
return RET_NERRNO(stat(fix, st));
}
-static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
+static int read_id_from_file(Item *i, uid_t *ret_uid, gid_t *ret_gid) {
struct stat st;
bool found_uid = false, found_gid = false;
uid_t uid = 0;
assert(i);
/* First, try to get the GID directly */
- if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
+ if (ret_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
gid = st.st_gid;
found_gid = true;
}
/* Then, try to get the UID directly */
- if ((_uid || (_gid && !found_gid))
+ if ((ret_uid || (ret_gid && !found_gid))
&& i->uid_path
&& root_stat(i->uid_path, &st) >= 0) {
found_uid = true;
/* If we need the gid, but had no success yet, also derive it from the UID path */
- if (_gid && !found_gid) {
+ if (ret_gid && !found_gid) {
gid = st.st_gid;
found_gid = true;
}
}
/* If that didn't work yet, then let's reuse the GID as UID */
- if (_uid && !found_uid && i->gid_path) {
+ if (ret_uid && !found_uid && i->gid_path) {
if (found_gid) {
uid = (uid_t) gid;
}
}
- if (_uid) {
+ if (ret_uid) {
if (!found_uid)
return 0;
- *_uid = uid;
+ *ret_uid = uid;
}
- if (_gid) {
+ if (ret_gid) {
if (!found_gid)
return 0;
- *_gid = gid;
+ *ret_gid = gid;
}
return 1;
return 0;
}
-static bool item_equal(Item *a, Item *b) {
+static int item_equivalent(Item *a, Item *b) {
+ int r;
+
assert(a);
assert(b);
if (!streq_ptr(a->name, b->name))
return false;
+ /* Paths were simplified previously, so we can use streq. */
if (!streq_ptr(a->uid_path, b->uid_path))
return false;
if (!streq_ptr(a->home, b->home))
return false;
- if (!streq_ptr(a->shell, b->shell))
- return false;
+ /* Check if the two paths refer to the same file.
+ * If the paths are equal (after normalization), it's obviously the same file.
+ * If both paths specify a nologin shell, treat them as the same (e.g. /bin/true and /bin/false).
+ * Otherwise, try to resolve the paths, and see if we get the same result, (e.g. /sbin/nologin and
+ * /usr/sbin/nologin).
+ * If we can't resolve something, treat different paths as different. */
+
+ const char *a_shell = pick_shell(a),
+ *b_shell = pick_shell(b);
+ if (!path_equal_ptr(a_shell, b_shell) &&
+ !(is_nologin_shell(a_shell) && is_nologin_shell(b_shell))) {
+ _cleanup_free_ char *pa = NULL, *pb = NULL;
+
+ r = chase_symlinks(a_shell, arg_root, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &pa, NULL);
+ if (r < 0) {
+ log_full_errno(ERRNO_IS_RESOURCE(r) ? LOG_ERR : LOG_DEBUG,
+ r, "Failed to look up path '%s%s%s': %m",
+ strempty(arg_root), arg_root ? "/" : "", a_shell);
+ return ERRNO_IS_RESOURCE(r) ? r : false;
+ }
+
+ r = chase_symlinks(b_shell, arg_root, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &pb, NULL);
+ if (r < 0) {
+ log_full_errno(ERRNO_IS_RESOURCE(r) ? LOG_ERR : LOG_DEBUG,
+ r, "Failed to look up path '%s%s%s': %m",
+ strempty(arg_root), arg_root ? "/" : "", b_shell);
+ return ERRNO_IS_RESOURCE(r) ? r : false;
+ }
+
+ if (!path_equal(pa, pb))
+ return false;
+ }
return true;
}
r = extract_many_words(&p, NULL, EXTRACT_UNQUOTE,
&action, &name, &id, &description, &home, &shell, NULL);
if (r < 0)
- return log_error_errno(r, "[%s:%u] Syntax error.", fname, line);
+ return log_syntax(NULL, LOG_ERR, fname, line, r, "Syntax error.");
if (r < 2)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Missing action and name columns.", fname, line);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Missing action and name columns.");
if (!isempty(p))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Trailing garbage.", fname, line);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Trailing garbage.");
/* Verify action */
if (strlen(action) != 1)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Unknown modifier '%s'", fname, line, action);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Unknown modifier '%s'.", action);
if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE))
- return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
- "[%s:%u] Unknown command type '%c'.", fname, line, action[0]);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
+ "Unknown command type '%c'.", action[0]);
/* Verify name */
if (empty_or_dash(name))
if (name) {
r = specifier_printf(name, NAME_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_name);
if (r < 0)
- return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m", fname, line, name);
+ return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to replace specifiers in '%s': %m", name);
if (!valid_user_group_name(resolved_name, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] '%s' is not a valid user or group name.",
- fname, line, resolved_name);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "'%s' is not a valid user or group name.", resolved_name);
}
/* Verify id */
if (id) {
r = specifier_printf(id, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_id);
if (r < 0)
- return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
- fname, line, name);
+ return log_syntax(NULL, LOG_ERR, fname, line, r,
+ "Failed to replace specifiers in '%s': %m", name);
}
/* Verify description */
if (description) {
r = specifier_printf(description, LONG_LINE_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_description);
if (r < 0)
- return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
- fname, line, description);
+ return log_syntax(NULL, LOG_ERR, fname, line, r,
+ "Failed to replace specifiers in '%s': %m", description);
if (!valid_gecos(resolved_description))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] '%s' is not a valid GECOS field.",
- fname, line, resolved_description);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "'%s' is not a valid GECOS field.", resolved_description);
}
/* Verify home */
if (home) {
r = specifier_printf(home, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_home);
if (r < 0)
- return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
- fname, line, home);
+ return log_syntax(NULL, LOG_ERR, fname, line, r,
+ "Failed to replace specifiers in '%s': %m", home);
+
+ path_simplify(resolved_home);
if (!valid_home(resolved_home))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] '%s' is not a valid home directory field.",
- fname, line, resolved_home);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "'%s' is not a valid home directory field.", resolved_home);
}
/* Verify shell */
if (shell) {
r = specifier_printf(shell, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_shell);
if (r < 0)
- return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
- fname, line, shell);
+ return log_syntax(NULL, LOG_ERR, fname, line, r,
+ "Failed to replace specifiers in '%s': %m", shell);
+
+ path_simplify(resolved_shell);
if (!valid_shell(resolved_shell))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] '%s' is not a valid login shell field.",
- fname, line, resolved_shell);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "'%s' is not a valid login shell field.", resolved_shell);
}
switch (action[0]) {
case ADD_RANGE:
if (resolved_name)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Lines of type 'r' don't take a name field.",
- fname, line);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Lines of type 'r' don't take a name field.");
if (!resolved_id)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Lines of type 'r' require an ID range in the third field.",
- fname, line);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Lines of type 'r' require an ID range in the third field.");
if (description || home || shell)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Lines of type '%c' don't take a %s field.",
- fname, line, action[0],
- description ? "GECOS" : home ? "home directory" : "login shell");
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Lines of type '%c' don't take a %s field.",
+ action[0],
+ description ? "GECOS" : home ? "home directory" : "login shell");
r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id);
if (r < 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Invalid UID range %s.", fname, line, resolved_id);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Invalid UID range %s.", resolved_id);
return 0;
case ADD_MEMBER: {
/* Try to extend an existing member or group item */
if (!name)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Lines of type 'm' require a user name in the second field.",
- fname, line);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Lines of type 'm' require a user name in the second field.");
if (!resolved_id)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Lines of type 'm' require a group name in the third field.",
- fname, line);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Lines of type 'm' require a group name in the third field.");
if (!valid_user_group_name(resolved_id, 0))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] '%s' is not a valid user or group name.",
- fname, line, resolved_id);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "'%s' is not a valid user or group name.", resolved_id);
if (description || home || shell)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Lines of type '%c' don't take a %s field.",
- fname, line, action[0],
- description ? "GECOS" : home ? "home directory" : "login shell");
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Lines of type '%c' don't take a %s field.",
+ action[0],
+ description ? "GECOS" : home ? "home directory" : "login shell");
r = string_strv_ordered_hashmap_put(&members, resolved_id, resolved_name);
if (r < 0)
case ADD_USER:
if (!name)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Lines of type 'u' require a user name in the second field.",
- fname, line);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Lines of type 'u' require a user name in the second field.");
r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops);
if (r < 0)
if (valid_user_group_name(gid, 0))
i->group_name = TAKE_PTR(gid);
else
- return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
+ return log_syntax(NULL, LOG_ERR, fname, line, r,
+ "Failed to parse GID: '%s': %m", id);
} else {
i->gid_set = true;
i->id_set_strict = true;
if (!streq(resolved_id, "-")) {
r = parse_uid(resolved_id, &i->uid);
if (r < 0)
- return log_error_errno(r, "Failed to parse UID: '%s': %m", id);
+ return log_syntax(NULL, LOG_ERR, fname, line, r,
+ "Failed to parse UID: '%s': %m", id);
i->uid_set = true;
}
}
case ADD_GROUP:
if (!name)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Lines of type 'g' require a user name in the second field.",
- fname, line);
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Lines of type 'g' require a user name in the second field.");
if (description || home || shell)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "[%s:%u] Lines of type '%c' don't take a %s field.",
- fname, line, action[0],
- description ? "GECOS" : home ? "home directory" : "login shell");
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Lines of type '%c' don't take a %s field.",
+ action[0],
+ description ? "GECOS" : home ? "home directory" : "login shell");
r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops);
if (r < 0)
} else {
r = parse_gid(resolved_id, &i->gid);
if (r < 0)
- return log_error_errno(r, "Failed to parse GID: '%s': %m", id);
+ return log_syntax(NULL, LOG_ERR, fname, line, r,
+ "Failed to parse GID: '%s': %m", id);
i->gid_set = true;
}
existing = ordered_hashmap_get(h, i->name);
if (existing) {
- /* Two identical items are fine */
- if (!item_equal(existing, i))
- log_warning("%s:%u: conflict with earlier configuration for %s '%s', ignoring line.",
- fname, line,
- item_type_to_string(i->type), i->name);
+ /* Two functionally-equivalent items are fine */
+ r = item_equivalent(existing, i);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ log_syntax(NULL, LOG_WARNING, fname, line, SYNTHETIC_ERRNO(EUCLEAN),
+ "Conflict with earlier configuration for %s '%s', ignoring line.",
+ item_type_to_string(i->type), i->name);
return 0;
}
ORDERED_HASHMAP_FOREACH(i, users)
(void) process_item(i);
- r = write_files();
- if (r < 0)
- return log_error_errno(r, "Failed to write files: %m");
-
- return 0;
+ return write_files();
}
DEFINE_MAIN_FUNCTION(run);
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);
assert_se(ratelimit_below(&ratelimit));
}
+TEST(ratelimit_num_dropped) {
+ int i;
+ RateLimit ratelimit = { 1 * USEC_PER_SEC, 10 };
+
+ for (i = 0; i < 10; i++) {
+ assert_se(ratelimit_below(&ratelimit));
+ assert_se(ratelimit_num_dropped(&ratelimit) == 0);
+ }
+ assert_se(!ratelimit_below(&ratelimit));
+ assert_se(ratelimit_num_dropped(&ratelimit) == 1);
+ assert_se(!ratelimit_below(&ratelimit));
+ assert_se(ratelimit_num_dropped(&ratelimit) == 2);
+ sleep(1);
+ assert_se(ratelimit_below(&ratelimit));
+ assert_se(ratelimit_num_dropped(&ratelimit) == 0);
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
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(usec_sub_signed(4, 1) == 3);
assert_se(usec_sub_signed(4, 4) == 0);
assert_se(usec_sub_signed(4, 5) == 0);
+
assert_se(usec_sub_signed(USEC_INFINITY-3, -3) == USEC_INFINITY);
assert_se(usec_sub_signed(USEC_INFINITY-3, -4) == USEC_INFINITY);
assert_se(usec_sub_signed(USEC_INFINITY-3, -5) == USEC_INFINITY);
assert_se(usec_sub_signed(USEC_INFINITY, 5) == USEC_INFINITY);
+
+ assert_se(usec_sub_signed(0, INT64_MAX) == 0);
+ assert_se(usec_sub_signed(0, -INT64_MAX) == INT64_MAX);
+ assert_se(usec_sub_signed(0, INT64_MIN) == (usec_t) INT64_MAX + 1);
+ assert_se(usec_sub_signed(0, -(INT64_MIN+1)) == 0);
+
+ assert_se(usec_sub_signed(USEC_INFINITY, INT64_MAX) == USEC_INFINITY);
+ assert_se(usec_sub_signed(USEC_INFINITY, -INT64_MAX) == USEC_INFINITY);
+ assert_se(usec_sub_signed(USEC_INFINITY, INT64_MIN) == USEC_INFINITY);
+ assert_se(usec_sub_signed(USEC_INFINITY, -(INT64_MIN+1)) == USEC_INFINITY);
+
+ assert_se(usec_sub_signed(USEC_INFINITY-1, INT64_MAX) == USEC_INFINITY-1-INT64_MAX);
+ assert_se(usec_sub_signed(USEC_INFINITY-1, -INT64_MAX) == USEC_INFINITY);
+ assert_se(usec_sub_signed(USEC_INFINITY-1, INT64_MIN) == USEC_INFINITY);
+ assert_se(usec_sub_signed(USEC_INFINITY-1, -(INT64_MIN+1)) == USEC_INFINITY-1-((usec_t) (-(INT64_MIN+1))));
}
TEST(format_timestamp) {
}
TEST(get_user_creds) {
- test_get_user_creds_one("root", "root", 0, 0, "/root", "/bin/sh");
- test_get_user_creds_one("0", "root", 0, 0, "/root", "/bin/sh");
+ test_get_user_creds_one("root", "root", 0, 0, "/root", DEFAULT_USER_SHELL);
+ test_get_user_creds_one("0", "root", 0, 0, "/root", DEFAULT_USER_SHELL);
test_get_user_creds_one(NOBODY_USER_NAME, NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", NOLOGIN);
test_get_user_creds_one("65534", NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY, "/", NOLOGIN);
}
image_install /lib/tmpfiles.d/knot.conf
image_install "${ROOTLIBDIR:?}/system/knot.service"
image_install -o /etc/dbus-1/system.d/cz.nic.knotd.conf
+ image_install -o /etc/default/knot
# Copy over our configuration
mkdir -p "${workspace:?}/var/lib/knot/zones/" "${workspace:?}/etc/knot/"
ExecStart=sh -c 'test %g = $$(id -gn)'
ExecStart=sh -c 'test %G = $$(id -g)'
ExecStart=test %h = /root
-ExecStart=sh -c 'test %s = /bin/sh'
+ExecStart=sh -c 'test -x %s'
ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
ExecStart=sh -c 'test %H = $$(uname -n)'
ExecStart=sh -c 'test %g = $$(id -gn)'
ExecStart=sh -c 'test %G = $$(id -g)'
ExecStart=test %h = /root
-ExecStart=sh -c 'test %s = /bin/sh'
+ExecStart=sh -c 'test -x %s'
ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
ExecStart=sh -c 'test %H = $$(uname -n)'
echo "*** Running test $f"
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
- $SYSUSERS --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/err
+ $SYSUSERS --root=$TESTDIR 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >$TESTDIR/err
if ! diff -u $TESTDIR/err ${f%.*}.expected-err; then
echo "**** Unexpected error output for $f"
cat $TESTDIR/err
-Failed to parse UID: '9999999999': Numerical result out of range
+ Failed to parse UID: '9999999999': Numerical result out of range
--- /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
}
}
+test_issue_23796() {
+ local mount_path mount_mytmpfs
+
+ mount_path="$(command -v mount 2>/dev/null)"
+ mount_mytmpfs="${mount_path/\/bin/\/sbin}.mytmpfs"
+ cat >"$mount_mytmpfs" <<EOF
+#!/bin/bash
+sleep ".\$RANDOM"
+exec -- $mount_path -t tmpfs tmpfs "\$2"
+EOF
+ chmod +x "$mount_mytmpfs"
+
+ mkdir -p /run/systemd/system
+ cat >/run/systemd/system/tmp-hoge.mount <<EOF
+[Mount]
+What=mytmpfs
+Where=/tmp/hoge
+Type=mytmpfs
+EOF
+
+ # shellcheck disable=SC2064
+ trap "rm -f /run/systemd/system/tmp-hoge.mount '$mount_mytmpfs'" RETURN
+
+ for ((i = 0; i < 10; i++)); do
+ systemctl --no-block start tmp-hoge.mount
+ sleep ".$RANDOM"
+ systemctl daemon-reexec
+
+ sleep 1
+
+ if [[ "$(systemctl is-failed tmp-hoge.mount)" == "failed" ]] || \
+ journalctl -u tmp-hoge.mount -q --grep "but there is no mount"; then
+ exit 1
+ fi
+
+ systemctl stop tmp-hoge.mount
+ done
+}
+
: >/failed
systemd-analyze log-level debug
# test that handling of mount start jobs is delayed when /proc/self/mouninfo monitor is rate limited
test_issue_20329
+# test for reexecuting with background mount job
+test_issue_23796
+
systemd-analyze log-level info
touch /testok
# 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